こんにちは、OTTサービス技術部開発第1Gの大元です。
みなさんはどのようにデプロイを行っていますか?
私の担当している案件ではビルド、ECSタスクのエラーの確認、切り替えなど手動で行っていました。
手動で行うとミスも発生しやすいですし、デプロイに毎回時間を取られて大変ですよね。
この手動で行っていたデプロイ方法を CodePipeline / CodeBuild / CodeDeployを使用して自動化しました。
今回は、このデプロイ自動化について紹介していきます。
従来のデプロイ方法と課題
従来の手動デプロイは、以下のような流れで行っていました。
- デプロイサーバにアクセスする
- ブランチの最新の状態を取得する
- ビルドコマンドを実行し、Dockerのimageを作成する
- AWSのECSタスクが起動されるのを待ち、エラーの確認を行う
- 環境によってコンテナの台数を変更する
- ELBのリスナールールを変更してターゲットグループを切り替える
- 裏に回ったコンテナの台数を減らす
手動デプロイの課題点は、以下の通りです。
- ビルドコマンドを実行するためにデプロイサーバを用意する必要がある
- 手動のため、見落としや操作ミスなどが考えられる
- ECSのスケール台数なども手動で設定する必要があり、手間がかかる
いくつも手順を踏まないといけないので手間もかかりますし、手動で行う工程が多い分ミスが起こりやすい状況でした。
自動デプロイのメリット
自動化することによるメリットは以下の通りです。
- トリガーとなる動作を実行するだけで自動でビルドを行うため、コマンドを実行する手間が省ける
- エラーの確認も自動のため、見落としがなくなる
- 自動でスケールの変更をしてくれる
自動化することによって、見落としやミスもなくなりますし、時間の節約にもなります。
何より「コードをpushしただけ」や「タグをつけただけ」でデプロイを行ってくれるという手順がとても簡単なので導入してとても良かったと感じました。
また、私たちの事例としてCode兄弟を導入したことにより結果的に以下の様なメリットもありました。
- デプロイサーバが必要なくなった
- 従来のBlue/Greenでは一つ前の状態にのみ戻すことができたが、過去の任意のデプロイ状態に戻すことが可能になった
- Blue/Greenの切り替えは環境によって自動 or 手動に設定することができる (本番は手動、開発環境は自動など)
デプロイの流れ
前提条件
今回紹介するデプロイでは、以下の条件で行っています。
- ECS を使用したアプリケーションの実行
- デプロイ戦略に Blue/Green デプロイを使用
デプロイの構成
今回の自動デプロイの流れは以下の通りとなっています。
- gitにcodeをpushする
- pushがトリガーとなってCodePipelineが起動する
- CodePipelineがgitのソースを使用してCodeBuildを起動する
- CodeBuildがbuildspec.ymlの手順書にそって実行する
- CodePipelineがCodeDeployを起動する
- appspec.ymlの設定に従ってデプロイを行う
- エラーがなければ構築設定によってコンテナの切り替えを行う (自動 or 手動)
ファイルの配置
自動デプロイを行うにあたり、以下のようにファイルを配置しました。
STG, 本番など環境ごとにファイルが必要な場合は新たにそれぞれのディレクトリを作成すると良いです。
├── Dockerfile ├── buildspec.yml └── appspec.yml
今回は、環境ごとにファイルを作成したので構成を以下のようにしました。
├── Dockerfile └── pipelineconfig ├── staging │ └── appspec.yml ├── development │ └── appspec.yml ├── production │ └── appspec.yml └── buildspec.yml
Code兄弟による自動デプロイの導入
今回は、設定の詳細は割愛します。
詳細に関しては、脚注にURLを記載しますのでそちらから確認してください。
今回は、設定時にポイントとなる箇所や手順・設定ファイルをメインに記載していきます。
CodePipelineの設定
CodePipelineでは、CodeBuild, CodeDeployを順次起動していきます。
CodePipelineの設定は公式ドキュメント*1を参考にしてください。
ソースステージの追加
ソースプロバイダーには、以下の画像の項目が選択できます。*2
パイプライントリガー設定
CodePipelineの実行トリガーの設定として以下の2つから選択できます。
1. ブランチにプッシュイン
コードをpushした時にCodePipelineが動く
例) Staging, Developなど頻繁にデプロイを行う環境
2. Gitタグ
タグを紐づけをトリガーとしてCodePipelineが動く
例) 本番環境など複数の変更内容を1回でデプロイする環境
⚠️注意点⚠️
ブランチにプッシュインだと、ブランチにプッシュされるたびに都度CodePipelineが動いてしまいます。
本番環境の場合は、上記のようにGitタグを設定することをオススメします。
CodeBuildの設定
GitHubのコードをビルド実行手順ファイル(buildspec.yml)に沿ってビルドします。
ビルドプロジェクトの作成
以下の設定で行いました。
- マネージド型イメージ : コンピューティング(EC2)
- オペレーティングシステム:Amazon Linux
- ランタイム: Standard
- イメージ:Amazon Linux 2 x86_64 standard:4.0
- 特権付与にチェック(今回はdockerを使用するため)
⚠️注意点⚠️
イメージは、使用するランタイム名とバージョンによって対象のイメージが違うため確認が必要です。
以下の公式ドキュメント*3*4を参考にしてください。
buildspec.yml
version: 0.2 env: secrets-manager: GITLAB_ACCESS_TOKEN: gitlab/hoge:GITLAB_ACCESS_TOKEN phases: install: runtime-versions: nodejs: 16 pre_build: commands: ## ECRにログイン - echo "Logging in to Amazon ECR" - REPOSITORY_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $REPOSITORY_URI build: commands: ## ここからは各デプロイ方法に合わせて変更してください ## Dockerイメージのビルド - echo "Docker Build" - docker build --progress=plain -t $REPOSITORY_URI:LATEST ## DockerイメージのTAGづけ - docker tag $REPOSITORY_URI:LATEST $REPOSITORY_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION post_build: commands: ## DockerイメージのECRへのプッシュ - echo "Pushing the Docker image..." - docker push $REPOSITORY_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION - printf '{"ImageURI":"%s"}' $REPOSITORY_URI:$CODEBUILD_RESOLVED_SOURCE_VERSION > imageDetail.json ## 最新のタスク定義を取得して整形 - aws ecs describe-task-definition --task-definition ${TASK_DEFINITION} | jq '.taskDefinition | del(.taskDefinitionArn, .status, .requiresAttributes, .compatibilities, .revision, .registeredAt, .registeredBy)' | jq '(.containerDefinitions[] | select(.name == "${CONTAINER_NAME}")).image = "<IMAGE1_NAME>"' > taskdef.json ## 各環境下にappspec.ymlをおいている場合、root直下に持ってこないとエラーになるので対応 - cp ${CODEBUILD_SRC_DIR}/pipelineconfig/${ENV_NAME}/appspec.yml ./ artifacts: files: - appspec.yml - taskdef.json - imageDetail.json
buildspec.ymlに使用している環境変数は以下のとおりです。
AWS_ACCOUNT_ID
: CodePipelineに使用するAWSのアカウントIDAWS_DEFAULT_REGION
: CodePipelineに使用するAWSのデフォルトリージョンIMAGE_REPO_NAME
: CodeBuildで使用するECSのクラスター名TASK_DEFINITION
: CodeBuildで使用するタスク定義名
CodeDeployの設定
CodeBuildでビルドしたイメージを使用してデプロイ設定ファイル(appspec.yml)の設定にそってデプロイします。
CodeDeployのデプロイステージに入る前に事前準備が2つ必要です。
上記に関しては、詳しくは公式ドキュメントを参考にしてください。
今回は、2のデプロイグループの作成にて重要な設定に関してピックアップします。
手動 or 自動切り替え
Amazon ECS 用のデプロイグループ作成にて、以下の箇所を変更します。
- 自動:トラフィックをすぐに再ルーティングするを選択
- 手動:トラフィックを再ルーティングするタイミングを指定するを選択
手動の場合は、タスクセットの切り替え準備が完了してから待機する時間を設定します。
期限内に手動で更新を行わないと再ルーティングがされる前にデプロイが終了します。
元のリビジョンの終了
Blue/Greenの切り替えが完了してからタスクセットを終了するまでの時間を指定します。 こちらは自動で終了してくれます。(最短5分)
appspec.yml
各環境によってコンテナ名、サブネット、セキュリティグループなどの設定が違う場合は、環境毎にappspec.ymlを作成することをオススメします。
version: 0.0 Resources: - TargetService: Type: AWS::ECS::Service Properties: TaskDefinition: <TASK_DEFINITION> LoadBalancerInfo: ContainerName: "dummy-green" ContainerPort: 8080 PlatformVersion: "LATEST" NetworkConfiguration: AwsvpcConfiguration: Subnets: ["subnet-1234abcd","subnet-5678abcd"] SecurityGroups: ["sg-12345678"] AssignPublicIp: "DISABLED" # Optional properties CapacityProviderStrategy: - Base: 1 CapacityProvider: "FARGATE_SPOT" Weight: 2 - Base: 0 CapacityProvider: "FARGATE" Weight: 1
まとめ
デプロイを自動化してみて、デプロイの手順が簡単になり、デプロイを行う手順にかかる時間も格段に減りました。
また、今回紹介できなかったのですが、Slackへの通知も対応しました。
AWSへアクセスしなくてもデプロイの状況をSlackから把握できたり、Blue/Greenの変更もボタン一つで変えられるようになりました。
この通知機能がとても有能だったので次回機会があれば紹介したいと思います。
*1:https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/pipelines-create.html
*2:https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/pipelines-connections.html
*3:https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-env-ref-available.html
*4:https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/available-runtimes.html
*5:https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/getting-started-create-service-role.html#getting-started-create-service-role-console
*6:https://docs.aws.amazon.com/ja_jp/codedeploy/latest/userguide/deployment-groups-create-ecs.html