CodeDeploy pushコマンドは裏で何をやっている?

2016.04.09

AWSのデプロイ自動化サービスCodeDeployのチュートリアルでは、デプロイするコードを $ aws deploy pushコマンドでS3にアップロードし、(アップロードしたコードベースは「リビジョン」と呼ばれます)、管理画面からリビジョンを指定してデプロイを実行するのが一般的です。

では、この$ aws deploy pushコマンドは裏で何をやっているのでしょうか?

deploy pushを実行してみる

$ aws deploy pushを実際に実行してみましょう。

$ aws deploy push --application-name foo --description "test" --s3-location
s3://YOUR-BUCKET/cd/test.zip
To deploy with this revision, run:
aws deploy create-deployment --application-name foo --s3-location bucket=YOUR-BUCKET,key=cd/test.zip,bundleType=zip,eTag="21e51f92a0c85eca8a1cc1d514e85061" --deployment-group-name <deployment-group-name> --deployment-config-name <deployment-config-name> --description <description>

リビジョンをアップロードするpushコマンドを実行すると、デプロイ実行するコマンドがレスポンスとして表示されました。

aws cliのコマンド実行時に--debugオプションをつけると、AWS に

という2つのAPIを投げていることがわかります。

さらにAWS CLIのコードを眺めてみると、

  • ローカルファイルシステムのソースコードを ZIP 形式でバンドル
  • S3 に ZIP ファイルを PutObject
  • PutObject したS3オブジェクトを アプリケーションと紐付け

していることがわかります。

deploy pushの処理を個別実行してみる

deploy pushコマンド実行時に、背後で実行されている処理を個別に実行してみましょう。

ローカルファイルシステムのソースコードを ZIP 形式でバンドル

カレントディレクトリ以下を ZIP アーカイブ化します。

$ zip -r test.zip .
updating: .test (stored 0%)
updating: appspec.yml (deflated 54%)
updating: index.html (deflated 47%)
updating: LICENSE.txt (deflated 64%)
updating: scripts/ (stored 0%)
updating: scripts/install_dependencies (stored 0%)
updating: scripts/start_server (stored 0%)
updating: scripts/stop_server (deflated 17%)

S3 に ZIP ファイルを PutObject

S3 にアップロードします。

$ aws s3api put-object --bucket YOUR-BUCKET --key cd/test.zip --body test.zip
{
    "ETag": "\"240d16519925272a9bd463b8af5ee1ba\""
}

PutObject したS3オブジェクトをアプリケーションと紐付け

deploy register-application-revisionコマンドで CodeDeployのアプリケーションとリビジョンを紐付けます。

$ aws deploy register-application-revision \
  --application-name foo \
  --description "register zip revision" \
  --s3-location bucket=YOUR-BUCKET,key=cd/test.zip,bundleType=zip

紐付け時にはオプションでS3オブジェクトのETagやバージョンを渡す事もできます。

ETagを渡すと、オブジェクトのETagバリデーションが行われます。 バージョンを指定しないと、最新のバージョンが利用されます。

紐付けられていることを確認します。

$ aws deploy list-application-revisions --application-name foo
{
    "revisions": [
        {
            "revisionType": "S3",
            "s3Location": {
                "bundleType": "zip",
                "bucket": "YOUR-BUCKET",
                "key": "cd/test.zip"
            }
        }
    ]
}

デプロイを実行

デプロイメントグループとリビジョンを指定してデプロイします。

$ aws deploy create-deployment \
    --application-name foo \
    --deployment-config-name CodeDeployDefault.OneAtATime \
    --deployment-group-name foo \
    --description "zip" \
    --s3-location bucket=YOUR-BUCKET,bundleType=zip,key=cd/test.zip
{
    "deploymentId": "d-GLEG3CLBE"
}

デプロイステータスを確認します

$ aws deploy get-deployment --deployment-id d-GLEG3CLBE
{
    "deploymentInfo": {
        "applicationName": "foo",
        "status": "Succeeded",
        "deploymentOverview": {
            "Failed": 0,
            "InProgress": 0,
            "Skipped": 0,
            "Succeeded": 1,
            "Pending": 0
        },
        "description": "zip",
        "deploymentConfigName": "CodeDeployDefault.OneAtATime",
        "creator": "user",
        "deploymentId": "d-GLEG3CLBE",
        "ignoreApplicationStopFailures": false,
        "deploymentGroupName": "foo",
        "createTime": 1460183482.584,
        "completeTime": 1460183499.275,
        "revision": {
            "revisionType": "S3",
            "s3Location": {
                "bundleType": "zip",
                "bucket": "YOUR-BUCKET",
                "key": "cd/test.zip"
            }
        }
    }
}

ステータスは正常系であればInProgress -> Succeededと遷移します。

ZIP形式以外でリビジョン登録する

RegisterApplicationRevision APIのbundleTypeを見るとわかるように、リビジョンのファイル形式は

  • tar
  • tgz(tar+gzip)
  • zip

に対応しています。

tgz形式でリビジョン登録してみましょう。

ローカルファイルシステムのソースコードを tgz 形式でバンドル

$ tar cvzf foo.tgz * .??*
a LICENSE.txt
a appspec.yml
a index.html
a scripts
a scripts/install_dependencies
a scripts/start_server
a scripts/stop_server
a .test

S3 に tgz ファイルを PutObject

$ aws s3api put-object --bucket YOUR-BUCKET --key cd/foo.tgz --body foo.tgz
{
    "ETag": "\"18fb40b3f744bf1b551e33204a1a7a59\""
}

PutObject したS3オブジェクトをアプリケーションと紐付け

$ aws deploy register-application-revision \
  --application-name foo \
  --description "register tgz bundle" \
  --s3-location bucket=YOUR-BUCKET,key=cd/foo.tgz,bundleType=tgz
$ aws deploy list-application-revisions --application-name foo
{
    "revisions": [
        {
            "revisionType": "S3",
            "s3Location": {
                "bundleType": "tgz",
                "bucket": "YOUR-BUCKET",
                "key": "cd/foo.tgz"
            }
        },
        {
            "revisionType": "S3",
            "s3Location": {
                "bundleType": "zip",
                "bucket": "YOUR-BUCKET",
                "key": "cd/test.zip"
            }
        }
    ]
}

デプロイを実行

$ aws deploy create-deployment \
    --application-name foo \
    --deployment-config-name CodeDeployDefault.OneAtATime \
    --deployment-group-name foo \
    --description "tgz" \
    --s3-location bucket=YOUR-BUCKET,bundleType=tgz,key=cd/foo.tgz
{
    "deploymentId": "d-XB5OOPKBE"
}
$ aws deploy get-deployment --deployment-id d-XB5OOPKBE
{
    "deploymentInfo": {
        "applicationName": "foo",
        "status": "Succeeded",
        "deploymentOverview": {
            "Failed": 0,
            "InProgress": 0,
            "Skipped": 0,
            "Succeeded": 1,
            "Pending": 0
        },
        "description": "tgz",
        "deploymentConfigName": "CodeDeployDefault.OneAtATime",
        "creator": "user",
        "deploymentId": "d-XB5OOPKBE",
        "ignoreApplicationStopFailures": false,
        "deploymentGroupName": "foo",
        "createTime": 1460185839.647,
        "completeTime": 1460185857.264,
        "revision": {
            "revisionType": "S3",
            "s3Location": {
                "bundleType": "tgz",
                "bucket": "YOUR-BUCKET",
                "key": "cd/foo.tgz"
            }
        }
    }
}

bundleTypeをzipなどと間違えると、CodeDeploy エージェントはリビジョンがZIPバンドルであると判断するため "Zip end of central directory signature not found" というようなエラーが発生します。

まとめ

CodeDeployのpushコマンドは

  • コードベースをバンドル
  • バンドルをS3にアップロード
  • アプリケーションとS3オブジェクトの紐付け

を行っており、各ステップで分解できることがわかりました。

例えばsbtなどのビルドツールでバンドルする仕組みがあるアプリケーションは、既存の仕組みを活かしたまま AWS との連携処理を追加すれば CodeDeploy対応できます。

またCIツールを使っている場合、各ステップにブレイクダウンしたタスクを作成しておくと、デプロイパイプラインの変更やトラブルシュートがしやすくなります。