VPC LambdaでNAT Gatewayを使わずに外部通信させる with terraform
VPC内にAWS Lambdaを置きたいと思ったことはありませんか?
私はあります
お金ありますか?
私はありません
今回はTerraformを使ってVPC内に(お高い)NAT Gatewayを使わずにAWS Lambdaを設置したので、その備忘録です。
結論
- AWS LambdaにもNICがあり、EIP(パブリックIP)をアタッチしてあげることでNAT GatewayなしでAWS Lambdaを外部APIと通信させることができる
- この記事の執筆時点でTerraformにLambdaのNICを特定してとってくる機能はないため、AWS CLIを使うしかない
- AWS CLIと
terraform data
を使うことによっていい感じにIaC化することができる
脱NAT Gateway
AWS LambdaをVPC内に置くとき、一番問題になるのが外部との通信です。
VPC内に設置されたLambdaはデフォルトでパブリックIPを持たないために、外部との通信ができません。
この問題に対する最もポピュラーな解決法はNAT Gatewayを使うことです。
NAT Gatewayを作成し、パブリックIPを付与します。AWS Lambdaはそこを経由してIGWから外に出ていくように設定してあげれば、簡単に外部と通信できるようになります。
しかし、このNAT Gatewayがお財布に優しくない。
そこで脱NAT Gatewayを掲げて情報を漁っていたところ、とてもいい記事を見つけました。
どうやらAWS LambdaをVPC内のサブネットに設置するとき、NICが自動で生成されるようです(VPC Lambdaの裏側が知りたい方はコチラの公式ブログが参考になります)。このNICに対して、パブリックIPをアタッチしてあげたら解決です。
IaCで管理したい
ここからが本題です。
可能ならば、このリソースをterraformで作成・管理したいです。
しかし、残念ながらこの記事を執筆している時点で、terraformからLambdaのNICを探してくることはできないようです。ドキュメントを読み漁りましたが、見当たりませんでした。(あったら教えてください)
aws_lambda_function.sample_lambda.nic_id
みたいな形でとってこられたらうれしかったんですけど、ないものは仕方ないです。
代替手段として、AWS CLIとterraform data
というterraformのresourceを組み合わせることで既存のterraformリソースと共存させながらいい感じにIaCすることを試みました。
以下がそのコードです。
resource "terraform_data" "eip_for_lambda_attachment" {
triggers_replace = [
aws_eip.eip_for_lambda.id,
var.profile,
aws_lambda_function.sample_lambda.id,
]
provisioner "local-exec" {
command = <<EOF
nic_id=`aws ec2 describe-network-interfaces \
--profile ${var.profile} \
--filters "Name=subnet-id,Values=${aws_subnet.sample_subnet_public.id}" \
"Name=interface-type,Values=lambda" \
"Name=group-id,Values=${aws_security_group.sample_security_group.id}" \
--query 'NetworkInterfaces[].NetworkInterfaceId' \
--output text`
aws ec2 associate-address \
--profile ${var.profile} \
--allocation-id ${aws_eip.eip_for_lambda.id} \
--network-interface-id $${nic_id}
EOF
}
provisioner "local-exec" {
when = destroy
command = <<EOF
aws ec2 disable-address-transfer \
--profile ${self.triggers_replace[1]} \
--allocation-id ${self.triggers_replace[0]}
EOF
}
}
こちらのコードではサブネット、セキュリティグループから知りたいLambdaのNICを探し出して、パブリックIPをアタッチしています。サブネットやセキュリティグループ等はterraformで定義しているものをそのまま入れてあげれば大丈夫です。
これでterraform apply
するだけでパブリックIPをLambdaのNICにアタッチしてくれるようになりました。
ついでにterraform destory
時にもパブリックIPとNICの関連付けを解除してくれるようになっていますので、消すときもちゃんと消えてくれます。
ついにNAT Gatewayを使わずに外部と通信できるようになりました。
IaC管理としてもなかなか綺麗になったように思います。
まとめ
今回はterraformでNAT Gatewayを使わずにVPC Lambdaを立てる方法をまとめました。
VPC Lambdaは同じAZ内の同じsgである限り、複数のLambda関数で一つのNICを共有して使う挙動があるようです(参考)。そのため今回のようにサブネットとセキュリティグループを指定してNICをとって来る記述が有用です。
この記事が少しでも誰かのお役に立てば幸いです。
ほな。
Discussion
Lambda関数のIPアドレスを固定化する機会があり、こちらの記事が大変参考になりました。ありがとうございます。
aws_lambda_function
の属性としては取得できませんが、AWS CLIとterraform dataの代わりに aws_network_interface data source の
filter
引数でNetwork Interfaceを探して、 aws_eip resource でElastic IPと紐づけできるようです。参考になれば幸いです。
ハッとしました。
思い返すとたしかにマネジメントコンソールから探すときもLambdaのページじゃなくて、EC2ページのNICからなんですよね。
もっときれいに書けそうです。
教えていただきありがとうございます。