はじめに
今回はCircleCIとAmazon ECRを利用したCI/CDパイプラインを用意し、コンテナイメージのビルドからKubernetesクラスタへのデプロイまで実行するサンプルを作成しました。
構成
今回は以下のようなCI/CDパイプラインを作成しました。
- 管理対象はGoのソース・テストコード、Kubernetesマニフェストファイル・テスト用スクリプト
- リポジトリ上のいずれかのブランチに対するPushを受けたら、GoコードのテストとKubernetesマニフェストファイルのValidationを並列に実行する
main
ブランチへのMergeが行われたら、Dockerイメージのビルド・プッシュ、Kubernetesリソースの更新を行う
構成図は以下のようになります。CI/CDパイプラインはCircleCI、コンテナレジストリはAmazon ECR、Kubernets環境はAmazon EKSを選択しています。
CircleCI
今回利用したリポジトリのディレクトリ構造はこちら。.circleci/config.yml
以外は前回のGitHub Actionsの例と同じファイルを使用しています。
. ├── .circleci │ └── config.yml ├── README.md ├── app │ ├── dockerfile │ ├── main.go │ └── main_test.go └── manifest ├── deployment.yaml └── test.sh
CI/CDパイプラインは以下のようなファイルで構成しています。CircleCIを利用するには .circleci/config.yml
ファイル上にワークフローを定義する必要があります。
今回はAmazon ECRへコンテナイメージをPushするために circleci/aws-ecr
というOrbを利用しています。このOrbは、サンプルなどを見る限りWorkflow上で定義することが多いのですが、今回処理結果をSlackに通知したかったためecr-push
というJob上で定義しています。本当はJobの実行基盤をDockerにしたかったのですが上手くいかなかったため、ecr-push
JobはVM上で実行しています。
./circleci/config.yml
version: 2.1 orbs: slack: circleci/slack@4.4.2 aws-ecr: circleci/aws-ecr@7.0.0 aws-eks: circleci/aws-eks@1.1.0 kubernetes: circleci/kubernetes@0.12.0 commands: slack_fail: steps: - slack/notify: event: fail template: basic_fail_1 slack_success: steps: - slack/notify: event: pass template: basic_success_1 jobs: go-test: docker: - image: circleci/golang:1.15.6 working_directory: ~/repo steps: - checkout - run: name: Run tests command: go test -v ./app - slack_fail ecr-push: machine: image: ubuntu-2004:202010-01 steps: - aws-ecr/build-and-push-image: account-url: AWS_ECR_ACCOUNT_URL aws-access-key-id: AWS_ACCESS_KEY_ID aws-secret-access-key: AWS_SECRET_ACCESS_KEY dockerfile: dockerfile path: ./app region: AWS_REGION repo: <repository-name> tag: latest - slack_fail - slack_success k8s-validate: docker: - image: circleci/golang:1.15.6 working_directory: ~/repo steps: - checkout - run: name: install command: | wget https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz tar xf kubeval-linux-amd64.tar.gz sudo cp kubeval /usr/local/bin - run: name: validation command: kubeval ./manifest/*.yaml - slack_fail k8s-deployment: executor: aws-eks/python3 parameters: cluster-name: default: <cluster-name> type: string aws-region: default: <aws-region> type: string steps: - checkout - aws-eks/update-kubeconfig-with-authenticator: cluster-name: << parameters.cluster-name >> aws-region: << parameters.aws-region >> install-kubectl: true - kubernetes/create-or-update-resource: resource-file-path: manifest/deployment.yaml - run: name: check resource command: kubectl get pods - run: name: test apps command: | chmod +x ./manifest/test.sh sh ./manifest/test.sh - slack_fail - slack_success workflows: docker-build-and-deploy: jobs: - go-test - k8s-validate - ecr-push: requires: - go-test filters: branches: only: - main - k8s-deployment: requires: - k8s-validate - ecr-push filters: branches: only: - main
利用したOrbsはこちら。
参考ドキュメントはこちら。
- CircleCI Blog - Go アプリケーションの継続的インテグレーション
- CircleCI Blog - Amazon ECR へのプライベート Docker イメージの自動デプロイ
- Classmethod blog - CircleCI OrbsでSlackにFailedとSuccessを通知する
- GitHub - CircleCI-Public/slack-orb
- Kubeval Docs - Installation
- Qiita - CircleCIでmasterにmergeされた場合のみビルドしたバイナリをCloud Source Repositoresへpushする
利用時の前提
パイプラインを利用するうえで必要な準備なども記載します。
CircleCIの登録
CircleCIに登録していない場合は登録と利用するリポジトリの選択を済ませておきます。
※参考ドキュメント:
Slackへの通知用設定
Jobの実行結果をSlackに通知するため、Slackの設定を行います。Slack AppにてTokenの取得と投稿用チャンネルの作成をしておきます。Slackでの設定はこちらのドキュメントに書かれた通り実行すれば完了するかと思います。
CircleCIでの変数の設定
CircleCIの Environment Variables
には以下の情報を登録します。
AWS_ACCESS_KEY_ID
: AWSへのアクセスに利用するアカウントIDAWS_SECRET_ACCESS_KEY
: AWSへのアクセスに利用するシークレットアクセスキーAWS_ECR_ACCOUNT_URL
: Amazon ECRへのアクセスに利用するURLAWS_REGION
: AWSで利用するリージョンSLACK_ACCESS_TOKEN
: Slack Appで取得したアクセスTokenSLACK_DEFAULT_CHANNEL
: Slack通知に利用するチャンネル名
その他
CircleCIでは、リポジトリ上のディレクトリやファイル毎にJobを起動することができない、と認識しています。そのため、以前GitHub Actionsでつくったパイプラインと異なり、今回はひとつのワークフローの中に、イメージのビルドとクラスターへのデプロイのJobを入れています。
CIとCDのプロセスを分けようと思った場合、いくつか方法はあると考えられます。
アプリ・インフラそれぞれ専用のリポジトリを用意する
各リポジトリごとに.circleci/config.yml
を作成し、ワークフローを設定すれば処理を分けることが可能になり、CIとCDを分離することができます。アプリとインフラのコードを別のリポジトリに管理する場合、こちらの方法を採用することができるでしょう。
シェルスクリプト上で処理する
こちらの記事などで紹介されていますが、シェルスクリプト上でCircleCIを実行するタイミングをコントロールする、という方法もあるようです。プロジェクトで利用するコードをmonorepoで管理する場合、こちらの方法を採用することになるでしょう。