APIGateway+Lambdaによるアプリサーバをバージョン管理する

APIGatewayの統合リクエストとLambdaの組み合わせでバックエンドを構成することが多いのですが、最近になってバージョニングが出来ることを知り、いろいろと調べてみた結果を書き連ねていきたいと思います!

やりたいこと

今回書く記事は以下のようなことをやりたい場合に有効です。

  • APIGateway+Lambdaで構成されたサーバをバージョニングしたい
  • バージョニングされたサーバにはそれぞれ異なるエンドポイントを持たせたい

構成

f:id:tansantktk:20210119201801p:plain

Browser(Chrome)

APIGatewayを直接呼ぶために使います。 実運用では巷に溢れるフロントエンドフレームワークがこの役割を担当するかと思います。 今回はフロントは主題ではないので、ブラウザで流用します。

APIGateway

フロントからのリクエストを受けたら、それをLambdaへ横流しするプロキシサーバとして使います。

APIGatewayは2つのステージを用意します。

  • Devステージ:最新バージョンのLambdaを参照します。
  • Prodステージ:旧バージョンのLambdaを参照します。

Lambda

ビジネスロジックを担当します。 今回は単純な文字列をレスポンスするだけのサーバとして作ります。

Lambdaは2つのバージョンを用意します。

  • バージョン$LATEST: 最新バージョン。呼び出すと「"See you, world!"」とレスポンスしてくれます。APIGatewayのDevステージと紐づけます。
  • バージョン1: 旧バージョン。呼び出すと「"Hello, world!"」とレスポンスしてくれます。APIGatewayのProdステージと紐づけます。

下準備

Lambdaを作成

まずはざくざくとLambda関数を新規作成していきます。

  • 関数名: HelloWorld
  • ランタイム: Node.js 12.x
exports.handler = async (event, context, callback) => {
    const responseBody = "Hello, world!";
    
    var response = {
        "statusCode": 200,
        "body": JSON.stringify(responseBody),
        "isBase64Encoded": false
    };
    callback(null, response);
};

「Hello, world!」を返すだけの単純なLambda関数を作ります。 この関数をAPIGatewayごしに実行できるようにしていきます。

APIGatewayの準備

続いてAPIGatewayを作成します。

  • API名: HelloWorld
  • 統合タイプ: Lambda関数
  • Lambda プロキシ統合の使用: 有効
  • デプロイするステージ: Prod, Dev

APIGatewayが作成できたら、Lambdaプロキシ統合の設定をしたAPIGatewayをステージProdとステージDevに対してデプロイします。 デプロイができたらAPIのエンドポイントがステージエディター上に公開されますので、そのURLをコピペしてブラウザで開いてみます。 f:id:tansantktk:20210119184029p:plain

f:id:tansantktk:20210119183954p:plain

ブラウザの画面に"Hello, world!"が表示されたら正しく通信できています。

バージョン管理をする

では、ここからバージョニングの為の設定をしていきましょう。

Lambdaのバージョンを分ける

f:id:tansantktk:20210119184720p:plain

Lambdaの管理ページから「アクション」→「新しいバージョンを発行」を選択します。

f:id:tansantktk:20210119184859p:plain

既に説明文に書かれている通り、現在のLambdaの最新バージョン($LATEST)のスナップショットを作成しようとしているところです。 $LATESTというバージョンは最初からデフォルトで用意されており、直近にデプロイしたLambdaのソースコードは自動的に$LATESTというバージョンに適用されます。

「バージョンの説明」にはLambdaのバージョンを明確に区別する為に、開発者が分かりやすいようなラベルを付けておくことをお勧めします。 今回はAPIGatewayのProdステージのバージョンであることを示す為、Prodと入力しました。

f:id:tansantktk:20210119185751p:plain

バージョンが作成されると自動的に作成されたバージョンの情報が表示されます。 ARNの末尾に:1が付きます。

1という数字はLambdaが自動的に連番で割り振られるバージョンの番号です。 次回発行時は2、その次は3…といった具合に発行されます。

バージョン:1というボタンを押すと、現在のバージョン一覧が確認できます。 各バージョンをクリックするとそのバージョンの詳細ページへ遷移します。 先ほど付けた「バージョンの説明」もセットで表示されていますね。

なお、発行したバージョンのソースコードは一切編集ができません。 編集できるのは最新バージョンである$LATESTのソースコードのみです。

Lambdaのエイリアスを作成する

APIGatewayが特定バージョンを呼び出す為には、Lambdaのエイリアスを作成する必要があります。

f:id:tansantktk:20210119191048p:plain

「アクション」→「エイリアスを作成」を選択します。

f:id:tansantktk:20210119191326p:plain

「名前」には先ほどのバージョン同様に分かりやすい名前を付けてあげましょう。 今回はLambdaのバージョンの説明と同様にProdと名付けました。

「バージョン」にはエイリアスと紐づけたいLambdaバージョンを指定します。 先ほど作成した1を指定したら、「保存」を押しエイリアスを作成します。

f:id:tansantktk:20210119191930p:plain

エイリアスを作成すると作成したエイリアスの詳細ページが表示されます。 「エイリアス」ボタンからエイリアスとバージョンが紐づいたことが確認できます。

また、ARNは末尾に:Prodが付与されたものが発行されました。 このARNを利用し、APIGatewayから特定のLambdaのバージョンを呼び出すように設定していきます。

最新バージョンのLambdaのソースを更新

バージョン:$LATESTの詳細ページへ移動し、関数コードを以下のように修正してデプロイしておきます。

console.log('Loading function');

exports.handler = async (event, context, callback) => {
    const responseBody = "See you, world!";
    
    var response = {
        "statusCode": 200,
        "body": JSON.stringify(responseBody),
        "isBase64Encoded": false
    };
    callback(null, response);
};

レスポンスする文字列がSee you, world!に変わっただけです😑

今回はこのレスポンスを返すLambdaが最新バージョンということになります。

APIGatewayからLambdaのエイリアスを呼び出す

f:id:tansantktk:20210119193455p:plain

APIGatewayの「リソース」→「ANY」→「統合リクエスト」の画面へ移動し Lambda関数の末尾に${stageVariables.alias}と追記してチェックボタンを押します。

この${stageVariables.alias}は各ステージに定義されたaliasという名前のステージ変数の値を指します。

f:id:tansantktk:20210119193717p:plain

設定が保存されるとウィンドウが表示され、Lambdaに権限を付与するAWS CLIコマンドを実行しろと指示されますので表示されているコマンドをコピーします。 ただし、--function-nameで指定した値の末尾の${stageVariables.alias}は紐づけたいLambdaのエイリアスを直接記述する必要があります。

今回は${stageVariables.alias}をLambdaエイリアスのProdに置き換えておきましょう。

AWS CLIを使う場合、ローカル環境から利用するのも良いですが、最近AWS CloudShellというブラウザ上からAWS CLIを実行できる環境が用意されましたので、せっかくなので使っていきます。 f:id:tansantktk:20210119194112p:plain

AWSコンソールの右上のターミナルのアイコンをクリックするとAWS CloudShellが起動します。

f:id:tansantktk:20210119194258p:plain

先ほどコピーしたコマンドをペーストして実行すればLambdaに権限が付与されます。 なお、この権限付与はLambdaのエイリアス毎に毎回指定が必要です。ちょっと面倒ですね…🤤

APIGatewayのProdステージにデプロイ

Lambdaエイリアスを参照するようリソースを修正しましたので、Prodステージにデプロイします。

デプロイできたら、Prodステージの「ステージ変数」に aliasという名前のステージ変数を追加し、値をProdと指定します。

f:id:tansantktk:20210119200216p:plain

このaliasは統合リクエストのLambda関数で指定した${stageVariables.alias}の参照先です。 Prodを指定しましたので統合リクエストのLambda関数はHelloWorld:Prodを指定したことになり、バージョン:1のLambda関数が呼び出されるようになります。

動作確認してみる

では、APIGatewayのProdステージのURL(https://***.execute-api.***.amazonaws.com/Prod)をブラウザから叩いてみます。

f:id:tansantktk:20210119183954p:plain

Hello, world!が出ました。つまりLambdaのバージョン:1(エイリアス: Prod)が呼ばれています。

一方で、APIGatewayのDevステージのURL(https://***.execute-api.***.amazonaws.com/Dev)をブラウザから叩いてみるとどうでしょうか。

f:id:tansantktk:20210119200547p:plain

See you, world!が出ました。ということはLambdaのバージョン: $LATESTが呼ばれています。 DevステージはLambdaのエイリアスを指定していないので、自動的に$LATESTが呼ばれました。

長かったですが、これでLambdaのバージョンごとにAPIGatewayのエンドポイントを分けることができました。

最後に改めて構成図を置いておきます。

f:id:tansantktk:20210119201801p:plain

あとがき

最初はエイリアス、バージョンの概念や関係性で混乱しましたが、手を動かして構成するうちに理解が深まってきました。 やはりまずは手を動かすのが理解の早道ですね!😆