はじめに
Go言語で書かれたプログラムをFargateでサービス実行していて、そこにCI/CDを設定する
手順の紹介記事です。
構成は以下の通り
- GitHub
- CircleCI
- CodePipeline(CodeCommit/CodeBuild/CodeDeploy)
- ECS(Fargate)
CircleCIを経由しないフローでもいいですが、CircleCI 2.1での新しい書き方を試したかった
のもあり、こんな構成にしました。 参考:CircleCI2.1
Qiita検索結果
なお、手順の紹介に重点を置いたので、各種設定ファイルについては概要説明に止めます。
前提
環境
- QA/Production環境それぞれ別のAWSアカウントで利用
- 東京リージョン(ap-northeast-1)を利用
- FargateでサービスとしてGoプログラムを実行している
- CirceCIのconfig.ymlはversion2.1を使用
- executors/commands/parametersを使いたいので
- Golangのバージョンは1.13~
- Go Modulesを使っているので
デプロイフロー
GitHubでは3種類のブランチを使います。
それを、以下のように動作するよう設定します。
- masterブランチ
- developブランチのマージ先
- PRマージされるとProduction環境のCodeCommitへPushされる
- CodeCommitへのPushをトリガにCodePipelineによりECSへデプロイされる
- developブランチ
- 開発用ブランチのマージ先
- 開発を始める場合はここから新しい開発用ブランチを作成
- PRマージされるとQA環境のCodeCommitへPushされる
- CodeCommitへのPushをトリガにCodePipelineによりECSへデプロイされる
- 開発用ブランチ(ここではfeature_hogeブランチ)
- GitHubにPushされるとLint/Testが実行される
なので、マージの流れは、 feature_hoge -> develop -> master の順
イメージ図
今回利用するGoプログラム
sample-go
プログラムの内容は適当(個人的な検証プログラム)です。
ECSにサービス登録して実行する前提なので、デーモンとして動く実装であればどんなコードでもOK。
ファイル構成
├── .circleci
│ └── config.yml # CircleCI設定ファイル
├── Dockerfile # Goプログラム実行用コンテナ作るやつ(マルチステージビルド!!)
├── Makefile # Goプログラムのビルドに使う
├── bin # リポジトリには含まれません
│ └── sample-go # make bin/sample-go を実行して生成されるバイナリ
├── buildspec.yml # CodeBuildが実行するコマンドを記述したファイル
├── main.go
├── main_test.go
├── go.mod
└── go.sum
CI設定に関わる下記ファイルの概要説明
- .circleci/config.yml
- GitHubへのPushをトリガに動くCircleCIワークフローの設定ファイル
- buildspec.yml
- CodeBuildが実行するコマンドなどを指定したファイル
- docker build してイメージをECRへPushするという内容
- Dockerfile
- Goプログラムのビルド、及び実行バイナリを格納したコンテナイメージを作成するDockerfile
- buildspec.yml内で指定されている
手順
GitHub - CircleCI 連携設定
https://circleci.com/ にアクセス
まだGitHub - CircleCI 連携設定していない場合は下記手順を実施
GitHubアカウントでログイン
CircleCIからGitHubへのアクセスを許可
下記のような確認画面が表示されるので画面下の Authorize circleci
ボタンをクリック
CIをセットアップするリポジトリを指定
https://circleci.com/add-projects/gh/{GitHubアカウント名} にアクセス
Set Up Project 画面からビルドを一度実行する
初回はエラーになるがこのまま進めます
CI(Lint/Test)動作確認
feature_hoge
ブランチを作成してPush
git checkout -b feature_hoge
git push origin feature_hoge
https://circleci.com/gh/{GitHubアカウント名}
にアクセスして上記のPushをトリガに最新Job(一番上)が動いていることを確認します。
SUCCESSであれば、Lint/Testにパスしたということになります。
もし、FAILEDであれば、対象のJobをクリックして、Lint/Test結果を確認し、必要な修正を行ってください。
以降の設定は、QA/Productionの両方のAWSアカウントで実施する必要があります。
QA用AWSアカウントの手順のみ記載しますが、Productionにも同様に設定する必要があります
Fargate設定
ECRにリポジトリを作成してコンテナイメージをPush
# awsクレデンシャルのプロファイルを指定
export AWS_PROFILE={プロファイル名}
# ECRにリポジトリを作成
aws ecr create-repository --region ap-northeast-1 --repository-name sample-go
# コンテナイメージをECRにPush
ACCOUNT_ID=$(aws sts get-caller-identity | jq -r .Account)
$(aws ecr get-login --no-include-email --region ap-northeast-1)
docker build -t sample-go .
docker tag sample-go:latest $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/sample-go:latest
docker push $ACCOUNT_ID.dkr.ecr.ap-northeast-1.amazonaws.com/sample-go:latest
Fargate設定(詳細割愛)
ここでは下記を作成した前提で進めます
- クラスタ名:sample-go
- サービス名:sample-go
- タスク定義名:sample-go
- コンテナ定義のイメージURL:123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/sample-go:latest
CodeCommit設定
CodePipelineのトリガとなる、CodeCommitの設定を行います。
CI設定時に、事前にCodeCommitにブランチを作成する必要があるのでそのセットアップも行います。
CodeCommitにPushするためのIAMユーザ作成
IAMユーザ作成
ユーザ名:codecommit-pusher(任意)
アクセスの種類:プログラムによるアクセス
にチェック
アタッチポリシー:AWSCodeCommitPowerUser
※ここでは権限広めですが、適切に絞ってくださいー
後は、デフォルト値で進めてユーザ作成まで実行
CodeCommitのSSHキーを作成
注)ここで発行するSSHキーは、CodeCommitブランチ作成、及びCircleCIのSSH Permissionsでも使用します
ローカル端末でSSHキーペアを作成します
(後述するCircleCIへの鍵アップロードではPEM形式にしか対応していないので -m pem
を忘れずに!)
cd ~/.ssh/
ssh-keygen -t rsa -b 2048 -f codecommit-pusher-key -m pem
ls codecommit-pusher-key*
→ codecommit-pusher-key, codecommit-pusher-key.pubが存在することを確認
(鍵ファイル名を変えて、QA/Production用に別々に発行してください)
IAMユーザ画面の認証情報
タブを開き、SSHパブリックキーのアップロード
ボタンをクリックし
codecommit-pusher-key.pubの内容を貼り付けてアップロードする
表示されたSSHキーIDを控えておいてください
CodeCommitリポジトリ作成
- AWSマネジメントコンソールからCodeCommit画面を開き、
リポジトリを作成
ボタンをクリック - リポジトリ名に
sample-go
を入力して作成
ボタンをクリック
developブランチを作成する
GitHubとCodeCommitでコミット履歴が同じ状態である必要があるので、git remoteにCodeCommitリポジトリを追加
する形でブランチを追加します。
# CodeCommitにgitでアクセスできるようにssh_config設定
IAM_SSH_KEY_ID={SSHキーIDを指定}
echo -e "Host git-codecommit.*.amazonaws.com\n User ${IAM_SSH_KEY_ID}\n StrictHostKeyChecking no\n UserKnownHostsFile=/dev/null\n IdentityFile ~/.ssh/codecommit-pusher-key" >> ${HOME}/.ssh/config
# githubからcloneしたsample-goローカルリポジトリへ移動
cd sample-go
# CodeCommitのリポジトリをremote追加
git remote add codecommit ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample-go
# developブランチをPush
git checkout develop
git pull codecommit develop
git push codecommit develop
git remote remove codecommit
(Productionの場合はmasterブランチに読み替えてください)
CodeCommitにPushするためのCircleCI設定
.circleci/config.ymlを見るとわかりますが、今回QA/ProductionでAWSアカウントが別れている関係で
CircleCIからPushするCodeCommitは2アカウント分となるため、SSHキー&秘密鍵が2つ必要になります。
今回は、環境ごとにdeploy_qaとdeploy_prodの2つのジョブに分けて、IAM_SSH_KEY_ID_QA / IAM_SSH_KEY_IDという
環境変数で使用するSSHキーを使い分ける形にしました。
deploy_qa:
〜略〜
steps:
- checkout
- run:
name: deploy for qa
command: |
echo -e "Host git-codecommit.*.amazonaws.com\n User ${IAM_SSH_KEY_ID_QA}\n StrictHostKeyChecking no\n UserKnownHostsFile=/dev/null" >> ${HOME}/.ssh/config
git push ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample-go develop
deploy_prod:
〜略〜
steps:
- checkout
- run:
name: deploy for production
command: |
echo -e "Host git-codecommit.*.amazonaws.com\n User ${IAM_SSH_KEY_ID}\n StrictHostKeyChecking no\n UserKnownHostsFile=/dev/null" >> ${HOME}/.ssh/config
git push ssh://git-codecommit.ap-northeast-1.amazonaws.com/v1/repos/sample-go master
CircleCIの環境変数追加
Project Settings
画面(https://circleci.com/gh/{GitHubアカウント名}/sample-go/edit) にアクセス
Environment Variables
をクリック
Add Variable
ボタンをクリック
IAM_SSH_KEY_ID/IAM_SSH_KEY_ID_QAという環境変数として、QA/Production環境で発行したSSHキーIDを追加します
CircleCIのSSH鍵追加
Project Settings
画面(https://circleci.com/gh/{GitHubアカウント名}/sample-go/edit) にアクセス
SSH Permissions
をクリック
Add SSH Key
ボタンをクリック
Private Key
欄に codecommit-pusher-keyの内容を貼り付けます(Hostnameは空欄でOK)
Add SSH Key
ボタンをクリック
QA/Production環境用に発行した2つのキーを登録します
コードパイプライン設定
コードパイプライン作成
AWSマネジメントコンソールからコードパイプライン画面を開き、パイプラインを作成する
ボタンをクリック
パイプラインの設定を選択する
パイプライン名を入力して、次に
をクリック
ロール名は自動的に入力されます(手入力は不要です)
ソースステージを追加する
ソースプロバイダーにAWS CodeCommit
を選択
リポジトリ名にsample-go
を指定し、ブランチ名にdevelop
を指定して、次に
をクリック
ビルドステージを追加する
プロバイダー:AWS CodeBuild
リージョン:アジアパシフィック(東京)
を選択し、プロジェクトを作成する
ボタンをクリック
ビルドプロジェクトを作成する
プロジェクト名:sample-go
環境イメージ:マネージド型イメージ
オペレーティングシステム:Ubuntu
ランタイム:Standard
イメージ:aws/codebuild/standard:2.0
特権付与:チェックOn
サービスロール:新しいサービスロール
ロール名:codebuild-sample-go-service-role
※あとで権限変更するため、ロール名を控えておくこと
<追加設定>を開いて環境変数として下記入力
名前 | 値 | 入力 |
---|---|---|
AWS_DEFAULT_REGION | ap-northeast-1 | プレーンテキスト |
AWS_ACCOUNT_ID | 123456789012 | プレーンテキスト |
以降、Buildspec、ログ設定はデフォルトのまま CodePipelineに進む
をクリック
ビルドステージを追加する
画面に戻るので、次に
ボタンをクリック
この画像には、特権付与にチェック入っていませんが必要です m(_ _)m
デプロイステージを追加する
デプロイプロバイダーにAmazon ECS
を選択
リージョン:アジアパシフィック(東京)
クラスター名:sample-go
サービス名:sample-go
次に
ボタンをクリック
確認画面が表示されるので、パイプラインを作成する
ボタンをクリック
パイプラインが作成されると、最初のパイプライン処理が実行されます。
まだ設定完了ではないのでエラーになりますが、気にせずに。
ECRアクセス権限付与
CodeBuildがビルドしたイメージをECRにPushするための権限を付与します。
IAMロール[codebuild-sample-go-service-role]に権限追加
※デフォルトでは、codebuild-{プロジェクト名}-service-roleというロール名になります
IAMコンソールで、上記ロールを開きます
アクセス権限
タブを開きます
AmazonEC2ContainerRegistryPowerUser
ポリシーをアタッチします
※ここでは権限広めですが、適切に絞ってくださいー
CI/CD動作確認
feature_hogeブランチをPushした際に、golangci-lintやgo testの結果がNGであれば、その旨GitHubのプルリク上
で表示されます。CircleCIのジョブ画面でエラー詳細が確認できます。
developブランチにMergeした時には、自動的にQA環境のECS(Fargate)のサービスが更新(デプロイ)されます。
また、手順省略しましたが、Production環境用の設定も行えば、masterにMergeした時に、自動的にProductionの
ECS(Fargate)のサービスが更新(デプロイ)されるようになります。
まとめ
ビルドが早い、マルチステージビルドでコンテナイメージがコンパクトにできる、ワンバイナリで動く、ということで
もともとGoとコンテナ実行の相性はいいですが、これにCI/CDができるとさらにデリバリスピードが上がるので最高ですねー。
さらに、サーバの存在を意識しなくてよいECS(Fargate)へのデプロイも、Code4兄弟が面倒をみてくれるので、
開発者だけでなく、SREにもメリットのあるかなりよい構成なので、今後もこの構成推しでいこうと思っています。
あと、今回は長々とコンソールを操作する手順を紹介しましたが、本来こういう設定はCloudFormationでテンプレートを
作って自動でセットアップするのがいいと思います(実際に、弊社でもテンプレート化してそれを使って構築しています)
ですが、コンソールで操作してみるとAWS/GitHub/CircleCIの連携部分の理解が進むので、一度やってみるのを
オススメします!