VPC内のLambdaからインターネットにアクセスする

VPC内にLambdaを配置すると、VPC内のサービスへの接続はとても簡単だけど、インターネットに疎通させようとした時に一手間必要だったので、そのメモ。

# 手順
以下の手順を踏むことになります。

1. Lambdaに「AWSLambdaVPCAccessExecutionRole」の権限をアタッチしたIAMロールを割り当てる
2. プライベートサブネットとパブリックサブネットを作成する
3. パブリックサブネット内でNATゲートウェイを作成する
4. NAT／インターネットゲートウェイを各サブネットのルートテーブルで割り当てる
5. Lambdaをプライベートサブネットに配置する

出来上がる構成はこんな感じになります。（あんまり自信ないけどこうなるはず）
インターネットアクセスの先として、DynamoDBをとりあえず考えた場合の構成になってます。

![スクリーンショット 2017-02-12 14.03.15.png](https://qiita-image-store.s3.amazonaws.com/0/102769/0e39849a-5456-73b4-7339-364352d11c48.png)

一つずつ内容を残しておきます。

## Lambdaに「AWSLambdaVPCAccessExecutionRole」の権限をアタッチしたIAMロールを割り当てる
書いてある通りです。Lambdaを実施する際のロールは、「AWSLambdaVPCAccessExecutionRole」を割り当てられている必要があります。正確には以下の3つ。

```
        "ec2:CreateNetworkInterface",
        "ec2:DescribeNetworkInterfaces",
        "ec2:DeleteNetworkInterface"
```
カスタムポリシーとして割り当ててもいいですが、「AWSLambdaVPCAccessExecutionRole」を割り当てておけば大丈夫です。

## プライベートサブネットとパブリックサブネットを作成する
操作自体単純です。まだインターネットゲートウェイなど割り当ててませんが、とりあえずサブネットを2つ作っておきます。

![スクリーンショット 2017-02-12 13.39.15.png](https://qiita-image-store.s3.amazonaws.com/0/102769/4947889d-3c0b-c683-f759-544b2aa0a8ef.png)

## パブリックサブネット内でNATゲートウェイを作成する
作成する際のサブネット選択を間違えないように注意。パブリックサブネット内で作成することがポイントになります。

![スクリーンショット 2017-02-12 13.41.52.png](https://qiita-image-store.s3.amazonaws.com/0/102769/2e675040-573c-a8b3-2779-bb597a231389.png)

## NAT／インターネットゲートウェイを各サブネットのルートテーブルで割り当てる
プライベートとして扱うサブネットにはNATゲートウェイを、パブリックとして扱うサブネットにはインターネットゲートウェイを割り当てます。（インターネットゲートウェイの作成方法は省略）

パブリック側はインターネットゲートウェイと繋げる
![スクリーンショット 2017-02-12 13.55.49_02.png](https://qiita-image-store.s3.amazonaws.com/0/102769/77a707dd-2f40-4b46-a571-6842aea92a3c.png)

プライベート側はNATゲートウェイと繋げる
![スクリーンショット 2017-02-12 13.56.17_02.png](https://qiita-image-store.s3.amazonaws.com/0/102769/63cc2cbb-df87-1ef1-b405-9ee65e91c35c.png)

## Lambdaをプライベートサブネットに配置する
Lambdaの設定で設置先をプライベートサブネットにします。パブリック側に置いてしまうとLambdaの仕様上インターネットに接続できません。

![スクリーンショット 2017-02-12 13.59.46_02.png](https://qiita-image-store.s3.amazonaws.com/0/102769/43d8e1b1-db32-e915-825b-e22fbea1511f.png)

# Lambdaを実行してみる
一通り設定したのでLambdaを実行してみます。テスト用のコードは単純にGoogleにアクセスするものになってます。

```
var http = require ('http');
exports.handler = function(event, context) {
    http.get("http://www.google.com/index.html", function(res) {
        console.log("Got response: " + res.statusCode);
    }).on('error', function(e) {
        context.done('error', e);
    });
};
```

とりあえずつながったようです。

```
START RequestId: d7a05c09-f0e0-11e6-xxxx-000000000000 Version: $LATEST
2017-02-12T05:05:25.896Z	d7a05c09-f0e0-11e6-xxxx-000000000000	Got response: 200
END RequestId: d7a05c09-f0e0-11e6-xxxx-000000000000
REPORT RequestId: d7a05c09-f0e0-11e6-xxxx-000000000000	Duration: 575.61 ms	Billed Duration: 600 ms 	Memory Size: 128 MB	Max Memory Used: 9 MB	
```

ちなみにVPC内のLambdaからインターネット初回アクセス時には、ENIを自動で作成してからのアクセスになるため、実行時間が2回目以降に比べて長くなります。（今回も2回目の実行時間は200ms程度になりました。）

自動で生成されるENIですが、ある程度の時間アクセスがないと自動的に削除される仕様のようなので、その辺は気にしなくてもよいらしいです。

# 料金とかは？
NATゲートウェイはマネージド型のNATインスタンスですので、自分でNATインスタンスを立てて運用するよりも堅牢である反面、コストはかかるようになります。
本記事執筆時が最安値のリージョンを利用して1時間0.045ドル。1ヶ月使い続けると30ドル程度かかる計算になります。（例えばt2.microで同様のインスタンスを組んだ場合、もう少し安くできる）

この辺は求められる性能や用途などを考慮しながらどちらがいいかを選択する必要があります。もっとも、すぐにでもVPC内のLambdaをインターネットに接続させたい、というのであればNATゲートウェイを作成した方が早いですがね。