AWS PrivateLink 経由で ALB 配下 の Lambda 関数を起動する 

## はじめに
2021/9/28 のアップデートで NLB のターゲットグループに ALB を指定できるようになりました :tada: 

https://aws.amazon.com/jp/about-aws/whats-new/2021/09/application-load-balancer-aws-privatelink-static-ip-addresses-network-load-balancer/

これにより AWS が提供する機能だけを使用して、ALB 配下にある Lambda 関数を PrivateLink 経由で呼び出すこともできるはずです。ALB ターゲットタイプの使い方や注意点等は他の記事に譲るとして、本記事では以下の構成を試してみます。

<!-- ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/ebd14a8e-d55a-58e1-29c3-87fd67f9a5d9.png) -->

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/bff5d67f-f3b0-da58-af6e-3b91bb533006.png)


## ALB ターゲットタイプの何が嬉しいのか
そもそも NLB のターゲットに ALB を指定できると何が嬉しいのでしょうか。

### ALB の IP アドレスを固定したい
ALB の仕様として IP アドレスが動的に変化します。ALB を使用しつつ、静的IP を設定したい場合は ALB の前段に AWS Global Accelerator を配置するか、NLB を配置する必要がありました。

どちらの方式も制限事項があり、Global Accelerator の場合はサービス特性上、Public Facing であることが前提となり、Internal Facing では利用できません。また NLB を利用する場合は ALB の IP アドレス変動に対応するため、AWS Lambda を使用して IP アドレスの変更をユーザーの責任で管理する必要がありました。実装例は下記ブログを参照ください。

https://aws.amazon.com/jp/blogs/networking-and-content-delivery/using-aws-lambda-to-enable-static-ip-addresses-for-application-load-balancers/

### PrivateLink 経由で ALB 配下のリソースに接続したい

PrivateLink 接続で使用する VPC エンドポイントサービスの作成には NLB が必要です。NLB はL4 ロードバランサーであるため、リダイレクトや固定レスポンス、HTTPヘッダーやパスベースのルーティングなど ALB が提供する L7 ロードバランサーの機能は利用できません。NLB で受けたトラフィックを ALB にルーティングすることで、PrivateLink 接続でも L7 ロードバランサーのメリットを享受することができます。

どちらのユースケースでも NLB 用の ALB ターゲットタイプを使用すると ALB の IP アドレスの変動をユーザー側で管理する必要がなくなります！

## やってみる
前置きが長くなってしまいましたがやってみます。サービスプロバイダー側の設定として、VPC エンドポイントサービスの作成までを行います。

ALB と AWS Lambda の設定は以下のブログの内容をベースにします。

https://aws.amazon.com/jp/blogs/news/lambda-functions-as-targets-for-application-load-balancers/



### Lambda 関数の準備
Lambda 関数は以下の内容で作成します。今回は完全に Internal な構成とするために、Lambda 関数を VPC に配置します。実行ロールには ENI の作成および管理を行う権限を付与するため、AWS 管理ポリシーの `AWSLambdaVPCAccessExecutionRole` を付与したロールを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/a5c1d773-c8b6-4cef-9ec1-79c0703634dd.png)


```lambda_function.py
def lambda_handler(event, context):
    response = {
       'statusCode': 200,
       'statusDescription': '200 OK',
       'isBase64Encoded': False,
       'headers': {
           'Content-Type': 'text/html; charset=utf-8'
        }
    }

    response['body'] = """<html>
    <head>
    <title>Hello World!</title>
    <style>
    html, body {
       margin: 0; padding: 0;
       font-family: arial; font-weight: 700; font-size: 3em;
       text-align: center;
    }
    </style>
    </head>
       <body>
          <p>Hello World!</p>
      </body>
    </html>"""
    return response
```

### ALB の準備
ターゲットタイプに Lambda 関数を指定してターゲットグループを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/58dcb383-3e75-b119-158a-57b365715913.png)

先ほど作成した Lambda 関数をターゲットに登録します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/17e74f15-a544-fef2-3938-5a7de12e2bda.png)

次に Internal Facing な ALB を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/5c27c4bc-88cd-644b-3db9-3044bd48d175.png)

リスナーとルーティングの設定で、デフォルトアクションに作成したターゲットグループを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/a81876d0-bb98-2abb-133a-c3e7c154c5e8.png)

作成された ALB にリクエストを投げて、Lambda 関数から応答が返ることを確認しておきます。

```shell-session
$ curl http://internal-lambda-alb-123456789.ap-northeast-1.elb.amazonaws.com
<html>
    <head>
    <title>Hello World!</title>
    <style>
    html, body {
       margin: 0; padding: 0;
       font-family: arial; font-weight: 700; font-size: 3em;
       text-align: center;
    }
    </style>
    </head>
       <body>
          <p>Hello World!</p>
      </body>
    </html>
```

### NLB の準備
ターゲットタイプに Application Load Balancer を指定してターゲットグループを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/aa742c07-585f-922e-11b0-9f8f0714eff0.png)

ターゲットの登録で作成した ALB を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/01a4dc2d-fe7a-beb8-7777-686ca98e1485.png)

次に Internal Facing な NLB を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/781045ac-a1ef-0ff3-e38a-6b29e3ae4fa2.png)

リスナーとルーティングの設定で、デフォルトアクションに作成したターゲットグループを指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/495d8d13-da61-a6de-2c57-7672047a73df.png)

作成された NLB にリクエストを投げて、ALB 経由で Lambda 関数から応答が返ることを確認しておきます。

```shell-session
$ curl http://lambda-nlb-xxxxxxxxxxxxxxxx.elb.ap-northeast-1.amazonaws.com
<html>
    <head>
    <title>Hello World!</title>
    <style>
    html, body {
       margin: 0; padding: 0;
       font-family: arial; font-weight: 700; font-size: 3em;
       text-align: center;
    }
    </style>
    </head>
       <body>
          <p>Hello World!</p>
      </body>
    </html>
```

### VPC エンドポイントサービスの作成
作成した NLB を指定して VPC エンドポイントサービスを作成します。検証目的のためエンドポイントの承諾は不要にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/8fd0e191-9737-97b3-e7e7-6b853f24c748.png)

このエンドポイントサービスを別の AWS アカウントから接続できるようにするにはプリンシパルに対象のアカウント ID または IAM ユーザ、IAM ロール を指定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/2c0c5a03-c7c3-7976-0a43-b0630d5d6ac3.png)

以上でサービスプロバイダー側の設定が完了しました。

## 動作確認
サービスコンシューマー側アカウントの設定として、インターフェイス VPC エンドポイントを作成して動作確認をおこないます。

サービスカテゴリで「サービスを名前で検索」を選択し、作成したエンドポイントサービス名を検証します。VPC とサブネット、セキュリティグループを指定してエンドポイントの作成を完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/1b8255d8-446c-a7e6-ff20-7e362245e111.png)

今回は Direct Connect で接続している VPC にインターフェース VPC エンドポイントを作成したので、クライアント PC からブラウザでアクセスします。冒頭の図のとおり、PrivateLink → NLB → ALB → Lambda function の呼び出しに成功しました！！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/125105/a63953a2-0a1c-bcf0-41f9-b7b3c30af68e.png)


## 参考

https://aws.amazon.com/jp/blogs/news/application-load-balancer-aws-privatelink-static-ip-addresses-network-load-balancer/

https://docs.aws.amazon.com/elasticloadbalancing/latest/network/application-load-balancer-target.html


以上です。
参考になれば幸いです。