TL;DR
CodePipelineでCodeCommitからECR、ECSへデプロイするために必要な
設計ポイントが書いてあります。
実際の構築手順を期待している方には残念な記事です。
事のはじまり
よくあるオーダーなんですが
「今まで手作業だった業務をシステム化したい」
に追加して
「コンテナ化してCICDで自動的にデプロイしたい」
というオーダーになり
突貫工事で作ることになったので やっつけてきました。
やったこと
- CodeCommitをリポジトリにしたよ
- CodeBuildでテストコードを実行させたよ
- CodeBuildでdocker buildしてECRにPUSHしたよ
- CodeCommitでマージしたら自動でFargateまでローリングデプロイする仕組みにしたよ
- 本番環境が別AWSアカウントでも自動デプロイ対応だよ
- SSMパラメータストアを活用することでマルチアカウント間でもパイプラインが動作するよ
用意するもの
- AWSアカウント
- dockerfile
- イメージ定義ファイル
- buildspec.yml
詳しく掘らないもの
- webコンソールの操作方法
- pytestの使い方
- コンパイル言語でビルドした出力アーティファクトをパイプラインに乗せてデプロイする方法
- オシャンなコンテナの実装
- オシャンなパイプラインの構成
- オシャンなbuildspec.ymlの実装
- イケてるNginxの設定
- イケてるuWSGIの設定
- ABデプロイ
- デプロイ種別で変わるイメージ定義ファイル
- 1Container1process
構成
使用するアカウント種別
- 開発環境AWSアカウント
- 本番環境AWSアカウント
※開発と本番のアーティファクトのデータ移動は手動でやるものとします。
※ステージング環境が増えても、やることは変わらんです。
使用するAWSのサービス
- VPC
- ECS/Fargate
- ECR
- SystemsManager
- ELB
- CodeCommit
- CodeBuild
- CodePipeline
- S3
- IAM
1.VPC
イイカンジに作ってください。
Public Subnet、Private Subnetがあるなら
Private SubnetはNATゲートウェイもしくはVPCエンドポイントが必須です
2.ECS/Fargate
常時稼働するか?バッチ的にタスクが起動するだけか?で設定が変わります。
①常時起動が必要なコンテナ
- クラスター
- サービス
- タスク定義
いわゆるWEBサービス的に起動するサービス。
このあたりの設定が必要です。
タスク定義を作ってクラスター作ってサービスを作ります。
※先に下記ELBから作っておくと面倒が少なくてよいかも。
②バッチ的に起動するコンテナ
- クラスター
- タスク定義
バッチ処理の場合EventBridgeから起動することが多いとおもいます。
その場合
イベントソース→EventBridge→ECS/Fargate
という経路になるため、ECSサービスを定義する必要はないです。
3.ECR
ECRへのPUSHをトリガーにFargateへデプロイします。
ECRリポジトリを作っといてください。
4.SystemsManager
パラメータストアに辞書形式で色々と保存しておくと、
開発/商用間の環境差分を吸収するのがシンプルに楽です。
アプリ側もパラメータストアを参照してDB等のエンドポイントを参照するようにしてください。
DBへのアクセスをIAMで認証する場合、ユーザ情報の保存は不要です。
5.ELB
FargateがローリングデプロイするためのALBとターゲットグループを作成しておいてください。
コンテナがデプロイされるまで、ターゲットは空っぽでよいです。
6.CodeCommit
リポジトリ作っておいてください。
パイプラインが発火するブランチ名を決めておくとイイカンジです。
- 特定ブランチは消させない
- 特定ブランチへのPUSHを拒否する
といった運用はIAMロールで制御してください。
7.CodeBuild
テスト実行用のコンテナを用意しておくと色々と使い勝手が良いですが、
このあたりは設計に合わせてください。
また、CodeBuildに渡される入力アーティファクトはenvに渡されます。
何度かecho $env
などを繰り返して、入念に期待する動作するか?を試してください。
- webコンテナ
- APPコンテナ
- testexeコンテナ
こんなような構成が好きです。
Buildspec.ymlは別個に書くのはあんまり好きくなくて
CodeBuildには軽く書いといて、実際の処理はbashなどに書くのが好きです。
Dockerのデーモン化に結構苦しめられたので、UPしておきます。
version: 0.2
phases:
install:
commands:
- nohup /usr/bin/dockerd -H tcp://127.0.0.1:2375 -H unix:///var/run/docker.sock &
- timeout 15 sh -c "until docker info; do echo .; sleep 1; done"
pre_build:
commands:
- git config --global credential.helper '!aws codecommit credential-helper $@' &&git config --global credential.UseHttpPath true &&git config --global user.email "example@example.com" &&git config --global user.name "UNKO"
build:
commands:
- bash boot-ctrbld.sh
ちなみにgit
8.CodePipeline
ざっくりですが、下記ステージの構成を取るとイイカンジになります。
アプリソースとコンテナdockerfileを1まとめのブランチに置いている前提で進めています。
①開発環境
パイプラインA
- Source:CodeCommit
アクション名:source
変数名:source
出力アーティファクト:source
CodeCommitの発火したいブランチ名をターゲットにしてください。
- Build:CodeBuild
アクション名:build
変数名:build
入力アーティファクト:source
出力アーティファクト:build
codebuildで起動したコンテナを使って
pytestを実行したあと、dockerfileにdocker build
とdocker push
します。
ここでテストしてビルドが通る事を確認しておきましょう。
- Deploy:S3
アクション名:deploy
変数名:deploy
入力アーティファクト:source
S3へのデプロイソースをSourceアーティファクトにしているにはワケがあります。
- 商用環境パイプラインにアーティファクトを送るとき、開発で取得した構成やら何やらを変えたくない。
- 別にビルドしたりコンパイルしているワケではない。
- どうせDockerfileも送るんだし、素の状態で送っとけ
- テストが通ったアーティファクトさえ送れるならヨシ!
パイプラインB
- Source:ECR、S3
アクション名:source01
変数名:source01
出力アーティファクト:source01
アクション名:source02
変数名:source02
出力アーティファクト:source02
このsource02と呼んでいる箇所がイメージ定義ファイルになります。
SystemsManagerやCodeCommit内に置いても読み込むんちゃうかと思いましたが、
S3に置いてるパターンが多いのでS3に置いてます。
https://docs.aws.amazon.com/ja_jp/codepipeline/latest/userguide/file-reference.html
- Deploy:ECS
アクション名:deploy
変数名deploy
入力アーティファクト:source01、source02
これでサービス登録されたECSクラスターにデプロイが可能となります。
サービス登録していない場合へのデプロイは、パイプラインAでECRにPushしたと思いますが
その時点で作業終了です。
ローリングデプロイしたい場合はサービス化しておく必要があるって感じですね。
②本番環境
パイプライン
- Source01:S3
どうにかして開発パイプラインAでS3にデプロイした成果物を
本番S3バケットに置いてください。
- Build:CodeBuild
CodeBuildで入力アーティファクトに対して下記の処理を行ってください。
- DockerfileをbuildしてECRにPUSHしてください。
- Source02:ECR、S3
開発環境と同じく、ECRとイメージ定義ファイルを指定してください。
- Deploy:ECS
ECSにデプロイして終了です。
反省点
時間が無くて、コンテナのブランチとAPPのブランチを分けちゃったのが結構面倒でした。1
次作るときは下記気を付けたいです。
- CodeCommitでマージするときブランチ消させたくない。
- デフォルトブランチにPushすんな権限付けたい。
- イメージ定義ファイルをS3じゃなくて、パラメータストアとかリポジトリに格納する方法を調べる。
- CodeBuildは本当に色々やれそう
こうして書き起こすとクッソ面倒ですけど
CodeCommitからターゲットまで「えい!」でデプロイできるのは本当に強いので
インフラメンバーとアプリ共通メンバーに置かれましては
ぜひ導入をご検討ください。