既にServerless Frameworkを使ってAWSにデプロイしているリソースに対してアップデートが必要となり、修正版を本番環境にデプロイする機会が巡ってきました。
検証環境で動作に問題が無いことは確認済ですが、万が一を想定して修正したリソースが正常に動作しなかった時の対処を考えていました。
何かあれば修正前の状態に戻すのが良いと思い調べたところ、Serverless Frameworkにはロールバック用のコマンドが用意されており、これを使って対処できそうだったので使ってみた結果を残しておきたいと思います。
ロールバックの方法
ロールバックに使用するコマンドはsls rollback --timestamp
というコマンドです。sls
はserverless
のaliasです。
以下が公式のドキュメントのコマンドの説明になります。
timestampの値はsls deploy list
を使うことで今までのデプロイ実行毎の状態とそれに対応するtimestamp
が表示されるので、その中から戻したいと状態のtimestampを使います。
sls rollback
はロールバックするリソースを個別に指定してロールバックするのではなく、ある時点すべてのリソースをその時の状態に戻すという動きをします。
サンプルで試してみる
サンプルとして用意したリソースとアップデートによる変更内容は以下の通りとなります。
AWSリソース
簡単なREST APIで Lambddaからはステータスコード200とHollo Worldという文字列のレスポンスボディが返るのみです。
- API Gateway
- Lambda
アップデートの変更内容
- Lambdaからレスポンスボディとして返す文字列の内容を変更
- 'Hello World' --> 'Updated!'
- Lambdaのランタイムのバージョン
- Node.js14 --> Node.js18
- タイムアウト秒数の変更
- 3 --> 25
現行のLambdaの設定を確認
アップデート前のLambda関数の設定を取得しておきます。
AWS CLIのコマンドで取得します。(長いので折りたたんだ中に入れました。)
今回更新しようとしているRuntime
やTimeout
も取得できています。
これらの設定がロールバック後に同様になっているか確認したいと思います。
確認は以下のコマンドでできます。
$ aws lambda get-function --function-name <Lambda関数名>
取得結果
$ aws lambda get-function --function-name serverless-rollback-trial
{
"Configuration": {
"FunctionName": "serverless-rollback-trial",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:************:function:serverless-rollback-trial",
"Runtime": "nodejs14.x", <--ここを変更します
"Role": "arn:aws:iam::************:role/serverless-rollback-trial-dev-ap-northeast-1-lambdaRole",
"Handler": "src/handlers/sample/index.handler",
"CodeSize": 293,
"Description": "",
"Timeout": 3, <--ここを変更します
"MemorySize": 128,
"LastModified": "2023-04-27T12:25:20.000+0000",
"CodeSha256": "urACWtzfnxSt894CGNHGeAx9IaPmaegTD5UuNDYUGdY=",
"Version": "$LATEST",
"VpcConfig": {
"SubnetIds": [],
"SecurityGroupIds": [],
"VpcId": ""
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "d6c1ef1e-d97b-468e-857e-ada13d5feb0c",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:cbf1e4541be1b7368dfdccdd8dd4be4f1de07dfdf6da3cab893d919f6b99e9a9"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "https://awslambda-ap-ne-1-tasks.s3.ap-northeast-1.amazonaws.com/snapshots/************/serverless-rollback-trial-3272321a-f631-4ff0-8114-1179fe85e064
},
"Tags": {
"aws:cloudformation:stack-name": "serverless-rollback-trial-dev",
"aws:cloudformation:stack-id": "arn:aws:cloudformation:ap-northeast-1:************:stack/serverless-rollback-trial-dev/4a1a57f0-e4d9-11ed-a25e-0a12404786f5",
"STAGE": "dev",
"aws:cloudformation:logical-id": "SampleLambdaFunction"
}
}
変更後の serverless.yml と lambda関数
serverless.yml
更新後のserverless.ymlは以下のような感じになります。
使用するServerless Frameworkのバージョンはv3
となります。
これでAPI Gatewayまで作れてしまうのでなかなかに便利ですね。
serverless.yml
service: serverless-rollback-trial
frameworkVersion: "3"
provider:
name: aws
runtime: nodejs18.x <-- ここを変更しました
stage: dev
region: ap-northeast-1
package:
individually: true
exclude:
- "**"
functions:
sample:
name: serverless-rollback-trial
description:
handler: src/handlers/sample/index.handler
memorySize: 128
timeout: 25 <-- ここを変更しました
package:
include:
- src/handlers/sample/*.js
events:
- http:
path: /sample
method: get
integration: lambda
変更後のlambda関数
src/handlers/sample/index.js
exports.handler = async () => {
const response = {
statusCode: 200,
body: JSON.stringify('Updated!'), <-- ここを変更しました
};
return response;
};
変更したリソースをデプロイ
それでは変更したリソースをデプロイしてみます。
$ sls deploy --verbose
無事にデプロイできましたので設定を確認してみます。 ランタイムとタイムアウト時間が変更されていることが確認できます。
取得結果
$ aws lambda get-function --function-name serverless-rollback-trial
{
"Configuration": {
"FunctionName": "serverless-rollback-trial",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:************:function:serverless-rollback-trial",
"Runtime": "nodejs18.x",
"Role": "arn:aws:iam::************:role/serverless-rollback-trial-dev-ap-northeast-1-lambdaRole",
"Handler": "src/handlers/sample/index.handler",
"CodeSize": 292,
"Description": "",
"Timeout": 25,
"MemorySize": 128,
"LastModified": "2023-04-27T13:25:29.000+0000",
"CodeSha256": "2uxHvOBNgeuVweVDbG8d5u0Q2+FaTy5O0yCBuISXqcY=",
"Version": "$LATEST",
"VpcConfig": {
"SubnetIds": [],
"SecurityGroupIds": [],
"VpcId": ""
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "f2fd2513-1c4a-4907-a1fd-a8f9220f214b",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:a19cefba18c0aa228bfe71e81d95cc5aab18e4570e899b3cfd9f759f022c7f8c"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "https://awslambda-ap-ne-1-tasks.s3.ap-northeast-1.amazonaws.com/snapshots/************/serverless-rollback-trial-0746451f-8ea8-495d-af91-b53b3081e3e8
},
"Tags": {
"aws:cloudformation:stack-name": "serverless-rollback-trial-dev",
"aws:cloudformation:stack-id": "arn:aws:cloudformation:ap-northeast-1:************:stack/serverless-rollback-trial-dev/4a1a57f0-e4d9-11ed-a25e-0a12404786f5",
"STAGE": "dev",
"aws:cloudformation:logical-id": "SampleLambdaFunction"
}
}
API Gatewayのエンドポイントも叩いてみます。
$ curl 'https://**********.execute-api.ap-northeast-1.amazonaws.com/dev/sample'
{"statusCode":200,"body":"\"Updated!\""}
変更したレスポンスボディの内容が返ってきました。
ロールバック
それではロールバックしてみます。 まずはタイムスタンプを確認します。
$ sls deploy list
2023-04-27 12:21:22 UTC
Timestamp: 1682598082581
Files:
- compiled-cloudformation-template.json
- sample.zip
- serverless-state.json
2023-04-27 13:23:52 UTC
Timestamp: 1682601832416
Files:
- compiled-cloudformation-template.json
- sample.zip
- serverless-state.json
Timestampが違う2つのバージョンがあることがわかります。
日付情報が新しいほうが後からデプロイした更新バージョンですので、その上のtimestampが1682598082581
のほうが更新前のバージョンとなります。
実際にロールバックしてみます。
$ sls deploy rollback --timestamp 1682598082581
Running "serverless" from node_modules
Rolling back serverless-rollback-trial to timestamp "1682598082581"
✔ Service rolled back to timestamp "1682598082581"
ロールバックが完了しました。
再度、API Gatewayのエンドポイントを叩いてみます。
$ curl 'https://**********.execute-api.ap-northeast-1.amazonaws.com/dev/sample'
{"statusCode":200,"body":"\"Hello World!\""}
変更前のレスポンスに変わっています。
Lambdaの設定を確認してみます。
取得結果
$ aws lambda get-function --function-name serverless-rollback-trial
{
"Configuration": {
"FunctionName": "serverless-rollback-trial",
"FunctionArn": "arn:aws:lambda:ap-northeast-1:************:function:serverless-rollback-trial",
"Runtime": "nodejs14.x", <--変更前に戻ってます
"Role": "arn:aws:iam::************:role/serverless-rollback-trial-dev-ap-northeast-1-lambdaRole",
"Handler": "src/handlers/sample/index.handler",
"CodeSize": 293,
"Description": "",
"Timeout": 3, <--変更前に戻ってます
"MemorySize": 128,
"LastModified": "2023-04-27T13:48:42.000+0000",
"CodeSha256": "urACWtzfnxSt894CGNHGeAx9IaPmaegTD5UuNDYUGdY=",
"Version": "$LATEST",
"VpcConfig": {
"SubnetIds": [],
"SecurityGroupIds": [],
"VpcId": ""
},
"TracingConfig": {
"Mode": "PassThrough"
},
"RevisionId": "fb659749-989f-4125-8031-0b551bf7351a",
"State": "Active",
"LastUpdateStatus": "Successful",
"PackageType": "Zip",
"Architectures": [
"x86_64"
],
"EphemeralStorage": {
"Size": 512
},
"SnapStart": {
"ApplyOn": "None",
"OptimizationStatus": "Off"
},
"RuntimeVersionConfig": {
"RuntimeVersionArn": "arn:aws:lambda:ap-northeast-1::runtime:cbf1e4541be1b7368dfdccdd8dd4be4f1de07dfdf6da3cab893d919f6b99e9a9"
}
},
"Code": {
"RepositoryType": "S3",
"Location": "https://awslambda-ap-ne-1-tasks.s3.ap-northeast-1.amazonaws.com/snapshots/************/serverless-rollback-trial-b0fd73e6-bce0-41e5-85aa-083496e4b2f7
},
"Tags": {
"aws:cloudformation:stack-name": "serverless-rollback-trial-dev",
"aws:cloudformation:stack-id": "arn:aws:cloudformation:ap-northeast-1:************:stack/serverless-rollback-trial-dev/4a1a57f0-e4d9-11ed-a25e-0a12404786f5",
"STAGE": "dev",
"aws:cloudformation:logical-id": "SampleLambdaFunction"
}
}
"Runtime": "nodejs14.x","Timeout": 3,が変更前の設定の戻っていますので、問題なく元の状態にロールバックされてます。
ということで、もともとの目的には使用できそうです。
使用するような事にはならないようにアップデートできるのが一番ですが、万が一の時の手段ができて良かったです。
以上。