AWS Lambdaを新機能バージョニングとエイリアスでBlue-Green Deploymentする #reinvent

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

これまで AWS Lambda をデプロイするときは、新しいコードをアップロードし、バグっていた時は、もともと動いていたコードをアップロードし直すというカウボーイ運用でした。

先日の re:invent 2015 では AWS Lambda にバージョニング機能とエイリアス機能が追加されました。 この2つの機能を組み合わせることで Lambda 関数のリリースやロールバックを簡単にする Blue-Green Deployment が可能になりました。

デプロイサイクル

本番向け Lambda 関数にエイリアス(PROD)を設定し、他のアプリケーションは常にこのエイリアスを参照するようにしておくと Lambda 関数のコード(=バージョン)を気にすることなく、常に本番向け Lambda 関数を利用できます。

Lambda 関数の運用チームは本番向けコードの用意ができ次第、新しいコードをアップロードしてバージョン番号を取得し、本番切り替えのタイミングでエイリアスの向き先を新しいコードのバージョンに向き変えればリリース完了です。

一旦リリースした関数にバグがありロールバックする場合は、エイリアスの向き先をリリース前のバージョン番号に向き変えるだけです。 コードをアップロードをし直す必要はありません。

lambda blue-green deployment : aliase change

バージョニング機能について

バージョニング機能を利用すると、コードをアップロードするたびにそのコードベースに対して番号が振られ、コードを世代管理できるようになります。

同じバージョン番号に対して、コードだけを変更することはできません。

バージョニング機能を有効にする

バージョニングはデフォルトでは無効化されています。 バージョニング機能を有効化するには、コードアップロード時に publish フラグを有効にします。

バージョン管理されるのはコードだけで、タイムアウト時間など、Lambda 関数の設定変更はバージョニングに影響しません。

Lambda 関数の ARN

Lambda 関数の ARN は以下の 2 種類が存在します。

  • Qualified ARN
  • Unqualified ARN

Qualified ARN について

  • arn:aws:lambda:aws-region:acct-id:function:helloworld:$LATEST
  • arn:aws:lambda:aws-region:acct-id:function:helloworld:versioni-number

というように、バージョンに関連する suffix がついています。

バージョニングを有効にしなければ suffix は $LATEST 一つだけが存在します。 バージョニングを有効にしてコードをアップロードすると、 1, 2, 3, ... といったバージョン番号が suffix する ARN が作成されていきます。

Unqualified ARN について

  • arn:aws:lambda:aws-region:acct-id:function:helloworld 

というように、先ほどの例とことなりバージョンに関する suffix が存在しません。

バージョン番号が $LATEST のものを参照します。

エイリアス機能について

バージョン管理されている Lambda 関数に対して、(関数名, バージョン番号) をキーにしてエイリアスを 設定することができます。

役割別にエイリアスを設定しておけば、バージョン番号を気にすることなくエイリアス経由で対応する Lambda 関数を呼び出せます。

AWS CLI を使って AWS Lambda を Blue-Green Deployment する

マネージメントコンソールからバージョニング・エイリアスを利用する方法は次のドキュメントで解説されています。

Managing Versioning Using the AWS Management Console, the AWS CLI, or Lambda APIs - AWS Lambda

以下では、AWS CLI を使って Blue-Green Deployment を試してみましょう。

  • Lambda 関数名 : helloworld
  • エイリアス名 : PROD

とし、PROD の指すバージョンを切り替えて Blue-Green Deployment します。

Lambda 関数の登録

API aws lambda create-function で Lambda 関数を登録します。 --publish オプションを有効にして、バージョニング機能を有効にします。

$ aws lambda create-function \
  --function-name helloworld \
  --zip-file fileb://helloworld-initial.zip \
  --role arn:aws:iam::1234567890:role/lambda_basic_execution \
  --handler helloworld.lambda_handler \
  --runtime python2.7 \
  --memory-size 128 \
  --timeout 60 \
  --publish
{
    "CodeSha256": "oTP+y1ctPr+lS14HjeBBLkjWdnEUZpgCyVFeW2RnsTM=",
    "FunctionName": "helloworld",
    "CodeSize": 245,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld",
    "Version": "1",
    "Role": "arn:aws:iam::1234567890:role/lambda_basic_execution",
    "Timeout": 60,
    "LastModified": "2015-10-12T10:58:05.379+0000",
    "Handler": "helloworld.lambda_handler",
    "Runtime": "python2.7",
    "Description": ""
}

レスポンスから "Version": "1" とバージョン番号が振られていることを確認できます。

この関数に別名 PROD を設定します。

API aws lambda create-alias を利用し

  • 関数名は --function-name
  • 関数のバージョン番号は --function-Version
  • エイリアスは --name

で指定します。

$ aws lambda create-alias --function-name helloworld --name PROD --function-version 1 --description "function for production"
{
    "AliasArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD",
    "FunctionVersion": "1",
    "Name": "PROD",
    "Description": "function for production"
}

レスポンスからエイリアスが接尾した ARN(AliasArn) を確認できます。

バージョン一覧とエイリアスを確認

API aws lambda list-versions-by-function でバージョン一覧を確認します

$ aws lambda list-versions-by-function --function-name helloworld
{
    "Versions": [
        {
            "Version": "$LATEST",
            "CodeSha256": "oTP+y1ctPr+lS14HjeBBLkjWdnEUZpgCyVFeW2RnsTM=",
            "FunctionName": "helloworld",
            "MemorySize": 128,
            "CodeSize": 245,
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:$LATEST",
            "Handler": "helloworld.lambda_handler",
            "Role": "arn:aws:iam::1234567890:role/lambda_basic_execution",
            "Timeout": 60,
            "LastModified": "2015-10-12T10:58:05.379+0000",
            "Runtime": "python2.7",
            "Description": ""
        },
        {
            "Version": "1",
            "CodeSha256": "oTP+y1ctPr+lS14HjeBBLkjWdnEUZpgCyVFeW2RnsTM=",
            "FunctionName": "helloworld",
            "MemorySize": 128,
            "CodeSize": 245,
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:1",
            "Handler": "helloworld.lambda_handler",
            "Role": "arn:aws:iam::1234567890:role/lambda_basic_execution",
            "Timeout": 60,
            "LastModified": "2015-10-12T10:58:05.379+0000",
            "Runtime": "python2.7",
            "Description": ""
        }
    ]
}

API aws lambda list-aliases でエイリアスを確認します

$ aws lambda list-aliases --function-name helloworld
{
    "Aliases": [
        {
            "AliasArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD",
            "FunctionVersion": "1",
            "Name": "PROD",
            "Description": "function for production"
        }
    ]
}

lambda-blue-green-initial-state

登録した関数を呼び出す

登録した Lambda 関数を aws lambda invoke API で呼び出してみましょう。

関数名で invoke

--function-name に素直に関数名を指定すると、バージョンが $LATEST なコードが実行されます。

$ aws lambda invoke \
   --invocation-type RequestResponse \
   --function-name helloworld \
   --payload '{}' \
   outputfile.txt
{
    "StatusCode": 200
}
$ cat outputfile.txt
"initial lambda function"

エイリアスで invoke

--function-name にエイリアス ARN を指定することもできます。

$ aws lambda invoke \
  --invocation-type RequestResponse \
  --function-name "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD" \
  --payload '{}' \
  outputfile-prod.txt
{
    "StatusCode": 200
}
$ cat outputfile-prod.txt
"initial lambda function"

バージョン番号で invoke

--function-name にバージョンが接尾した FunctionArn を指定することもできます。

$ aws lambda invoke \
  --invocation-type RequestResponse \
  --function-name "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:1" \
  --payload '{}' \
  outputfile-version.txt
{
    "StatusCode": 200
}
$ cat outputfile-version.txt
"initial lambda function"

Lambda 関数のコードを更新

次に Lambda 関数のコードを更新します。

API aws lambda update-function-code で Lambda 関数のコードを更新します。 --publish オプションを有効にして、バージョニング機能を有効にします。

$ aws lambda update-function-code \
  --function-name helloworld \
  --zip-file fileb://helloworld-update.zip \
  --publish
{
    "CodeSha256": "2zVUqCqwfzFpt+vgSrs7lxeERMgUCulyu6m2rDYv/EA=",
    "FunctionName": "helloworld",
    "CodeSize": 241,
    "MemorySize": 128,
    "FunctionArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:2",
    "Version": "2",
    "Role": "arn:aws:iam::1234567890:role/lambda_basic_execution",
    "Timeout": 60,
    "LastModified": "2015-10-12T11:06:42.217+0000",
    "Handler": "helloworld.lambda_handler",
    "Runtime": "python2.7",
    "Description": ""
}

API aws lambda list-versions-by-function でバージョン一覧を確認すると、バージョン番号2の FunctionArn を確認できます。 また、CodeSha256 の値から $LATEST は バージョン番号2と同じであることがわかります。

$ aws lambda list-versions-by-function --function-name helloworld
{
    "Versions": [
        {
            "Version": "$LATEST",
            "CodeSha256": "2zVUqCqwfzFpt+vgSrs7lxeERMgUCulyu6m2rDYv/EA=",
            "FunctionName": "helloworld",
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:$LATEST",
            ...
        },
        {
            "Version": "1",
            "CodeSha256": "oTP+y1ctPr+lS14HjeBBLkjWdnEUZpgCyVFeW2RnsTM=",
            "FunctionName": "helloworld",
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:1",
            ...
        },
        {
            "Version": "2",
            "CodeSha256": "2zVUqCqwfzFpt+vgSrs7lxeERMgUCulyu6m2rDYv/EA=",
            "FunctionName": "helloworld",
            "FunctionArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:2",
            ...
        }
    ]
}

lambda blue-gree deployment two versions

エイリアスPRODの向き先を変える

API aws lambda update-alias を利用し、「エイリアス PROD」 の向き先を「バージョン2」に変更します。

lambda blue-green deployment : aliase change

  • 関数名は --function-name
  • 関数のバージョン番号は --function-Version
  • エイリアスは --name

で指定します。

$ aws lambda update-alias \
  --function-name helloworld \
  --name PROD \
  --function-version 2
{
    "AliasArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD",
    "FunctionVersion": "2",
    "Name": "PROD",
    "Description": "function for production"
}
$ aws lambda list-aliases --function-name helloworld
{
    "Aliases": [
        {
            "AliasArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD",
            "FunctionVersion": "2",
            "Name": "PROD",
            "Description": "function for production"
        }
    ]
}

AliasArn を指定して PROD 関数を invoke してみましょう。

$ aws lambda invoke \
  --invocation-type RequestResponse \
  --function-name "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD" \
  --payload '{}' \
  outputfile-prod.txt
{
    "StatusCode": 200
}
$ cat outputfile-prod.txt
"new lambda function"

実行結果が "initial lambda function" から "new lambda function" に変わりましたね。

デプロイ完了です!

lambda blue-green deployed

デプロイした関数をロールバックする

Lambda 関数がバージョン管理されていなかったころは、ロールバックするために以前のコードを再アップロードしていました。 バージョニング&エイリアス運用では、エイリアスの指すバージョンを昔の番号に戻すだけでロールバックが完了します。

$ aws lambda update-alias --function-name helloworld --name PROD --function-version 1
{
    "AliasArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD",
    "FunctionVersion": "1",
    "Name": "PROD",
    "Description": "function for production"
}
$ aws lambda list-aliases --function-name helloworld
{
    "Aliases": [
        {
            "AliasArn": "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD",
            "FunctionVersion": "1",
            "Name": "PROD",
            "Description": "function for production"
        }
    ]
}

念の為に AliasArn を指定して PROD 関数を invoke してロールバックしていることを確認しましょう。

$ aws lambda invoke \
   --invocation-type RequestResponse \
   --function-name "arn:aws:lambda:ap-northeast-1:1234567890:function:helloworld:PROD" \
   --payload '{}' \
   outputfile-prod.txt
{
    "StatusCode": 200
}
$ cat outputfile-prod.txt
"initial lambda function"

実行結果が "initial lambda function" に戻っていますね。

ロールバック完了です!

参考リンク