愚直:正直なばかりで臨機応変の行動をとれないこと。また、そのさま。ばか正直。
はじめに
こんにちは、とまだです。
CircleCI Advent Calendar 2020 の 20 日目を担当させていただきます。
今回、自分が個人開発として作った Web アプリケーションで使っている設定ファイル config.yml を解説する記事を書こうとしたのですが、
16日目の方の記事と内容が丸かぶりでした。
そこで、今回は私が CircleCI を使うにあたり初めて書いた設定ファイルを解説してみたいと思います。
先に申し上げておくとそのコードには スタイリッシュな様相や、実運用を重視した工夫 はございません。
あえて 初めて書いた設定ファイル をテーマとすることで、
CircleCI を使ったことが無い人でも「これなら分かるかも」と思ってもらえることを目的としています。
きっかけ
何かツールを使うにあたり、例えそれが便利そうだとしても事前の準備が複雑だったり
やっていることがよく分からなかったりすると、つい敬遠してしまいませんか?
また、例え解説があったとしてもパッと見で複雑そうなだけで「もういいや」となったりしませんか?
例えば何らかの目的でコードを書こうとする時、Google で検索して参考になりそうなコードを探すことがあるかと思います。
その時に コード量は極限まで減らしていてスッキリしているコード よりも
愚直なコードでも意図が分かるコード の方が、最初の一歩としてはとっつきやすいですよね。
CircleCI を使うにあたり必要な設定ファイルも多種多様な書き方があり、
工夫を凝らすことでコードもすっきりし、結果的に利便性は向上するはずなのですが
初心者目線でも直感的に理解できる設定ファイルの書き方から触れてもらうことで、
最初の一歩を踏み出すきっかけになればいいなと思いました。
解説パート
システム構成 (CI/CD pipeline)
今回のシステム構成と CI/CD pipeline の流れは以下の通りです。
- 修正したコードを GitHub に push し、
- 指定のリポジトリに push されたことを CircleCI が検知して、
- CircleCI 上で立ち上がったマシン上でビルド&テストを行い、
- テストが通ったらまた別なマシンから EC2 に SSH でログインして、
- GitHub 上のコードを EC2(本番環境) に反映し、
- コンテナを再起動して本番環境に修正内容を反映させる
という流れになってます。
初めて書いた愚直な設定ファイル
いよいよ本題です。
最初に設定ファイルの全貌をご紹介します。
# circleCIのバージョン指定
version: 2
jobs:
build:
machine:
image: circleci/classic:edge
steps:
# Djangoのテストを実行
- checkout
- run:
name: make env file
command: |
echo DEBUG=${DEBUG} > src/.env
echo SECRET_KEY=${SECRET_KEY} >> src/.env
echo APIKEY=${APIKEY} >> src/.env
echo MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} >> src/.env
echo MYSQL_DATABASE=${MYSQL_DATABASE} >> src/.env
echo MYSQL_USER=${MYSQL_USER} >> src/.env
echo MYSQL_PASSWORD=${MYSQL_PASSWORD} >> src/.env
echo MYSQL_HOST=${MYSQL_HOST} >> src/.env
- run:
name: docker-compose up
command: docker-compose up -d
- run:
name: docker ps
command: docker ps
- run:
name: collectstatic
command: docker-compose run python ./manage.py collectstatic
- run:
name: migrate
command: docker-compose run python ./manage.py migrate
- run:
name: test
command: docker-compose run python ./manage.py test
- run:
name: docker-compose down
command: docker-compose down
# EC2にSSH接続し、デプロイを実行
deploy:
machine:
image: circleci/classic:edge
steps:
- checkout
# CircleCIに登録した秘密鍵を呼び出す。
- add_ssh_keys
- run: ssh ${USER_NAME}@${HOST_NAME} -p ${SSH_PORT} 'cd xxxxx && git pull origin main && docker-compose restart && docker-compose run python ./manage.py migrate'
# テストが成功した場合のみ、deployを実行するようにします。
workflows:
version: 2
build_and_deploy:
jobs:
- build
- deploy:
requires:
- build
# mainブランチにpushされた場合のみdeployするようにする。
filters:
branches:
only: main
この設定ファイルについて、いくつか抜粋して解説していきます。
version
version: 2
指定するバージョンによって使用するキーが異なるのですが、当時はバージョン「2」を使用していました。
jobs - build - machine
jobs:
build:
machine:
image: circleci/classic:edge
image: で、ビルドに使用する公式のマシンイメージを指定しています。
ここで指定しているマシンイメージでは下記が実装されています。
- Ubuntu バージョン 14.04 イメージ
- Docker バージョン 17.10.0-ce
- docker-compose バージョン 1.16.1
まっさらの Linux からビルドしなくていいのはありがたいですね。
jobs - build - steps
build の後、steps というところで Docker コンテナの構築からテストまでを行っていきます。
ここでお伝えしたいことは 実行したいコマンドを記述していくだけ ということです。
普通ならば人間の手でコマンドを叩いてコンテナを構築し、テスト用のコマンドまで実行していくんですが
そういった一つひとつの作業もすべて自動化してやってくれるのがメリットですね。
こういった自動化は大好物でございます。
このパートはどうしても一番長くなるので、コメントアウトで解説をしていきます。
なお、Django という Python 製のフレームワークを使っているゆえのコマンドを含みますが、あまり気にしなくて大丈夫です。
steps:
- checkout
- run:
# .env ファイルからセキュアな情報を読み出す設計のため、CircleCI に設定した環境変数を .env ファイルに書き出す処理
# 他に良いやり方もありそうですが一つひとつ愚直に書き出していました
name: make env file
command: |
echo DEBUG=${DEBUG} > src/.env
echo SECRET_KEY=${SECRET_KEY} >> src/.env
echo APIKEY=${APIKEY} >> src/.env
echo MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} >> src/.env
echo MYSQL_DATABASE=${MYSQL_DATABASE} >> src/.env
echo MYSQL_USER=${MYSQL_USER} >> src/.env
echo MYSQL_PASSWORD=${MYSQL_PASSWORD} >> src/.env
echo MYSQL_HOST=${MYSQL_HOST} >> src/.env
- run:
# テストに必要なコンテナを立ち上げ
name: docker-compose up
command: docker-compose up -d
- run:
# トラブルシューティング時、CircleCI の UI 上でコンテナの起動状況を確認できるようにコンテナ一覧を表示させる(趣味)
name: docker ps
command: docker ps
- run:
# Django で必要な処理
name: collectstatic
command: docker-compose run python ./manage.py collectstatic
- run:
# Django で必要な処理
name: migrate
command: docker-compose run python ./manage.py migrate
- run:
# Django の自動テスト
# ここで失敗すると次の処理には進まないようになっているため、問題があるコードは本番環境に反映されないようになっている
name: test
command: docker-compose run python ./manage.py test
- run:
# コンテナを停止 (今思えばいらないかも?)
name: docker-compose down
command: docker-compose down
なお、name: を書いておくと一つひとつのジョブを CircleCI の UI 上で確認する時に成功したジョブ、失敗したジョブがひと目で分かるので便利です。
UI 上ではこんな感じで表示されますので一目瞭然ですね。
緑のマークで埋め尽くされていくのが気持ちいい。
deploy - machine
前項の build でテストに成功した時、はじめて deploy のジョブが走ります。
CircleCI の UI 上では、build のジョブと deploy のジョブがこのように繋がれて表示されます(スクショではデプロイまで成功してますが)。
この環境では AWS の EC2 上でコンテナを起動させているので、EC2 に SSH ログインをして git pull を叩くことを目的としています。
そのために、EC2 に SSH ログインをするためのマシンを立ち上げます。
ここでは build の時と同じマシンイメージを使用していますが、SSH さえできる Linux であれば大丈夫なので
もっと軽量のマシンイメージを使ってもよかったかもしれません。
deploy:
machine:
image: circleci/classic:edge
deploy - steps - add_ssh_keys (秘密鍵の登録)
EC2 にログインするためには、事前に作成しておいた秘密鍵を使います。
通常であれば特定のフォルダとかに秘密鍵を格納しておいて、SSH コマンドを叩く時に呼び出すことになります。
そのため、環境変数とは少し扱いが異なるため注意が必要です。
steps:
- checkout
# CircleCIに登録した秘密鍵を呼び出す
- add_ssh_keys
事前に UI 上の操作で登録しておくことになるのですが add_ssh_keys というコマンドで呼び出せます。
※以下は 公式ドキュメントの引用です。
すべての CircleCI ジョブは、ssh-agent を使用して登録済みのすべての SSH 鍵に自動的に署名します。
ただし、コンテナに実際に鍵を登録するには、add_ssh_keys キーを使用する必要があります。
deploy - steps - SSH & git pull
おまたせしました。
ここで一気に ECS に SSH でログインし、git pull してからコンテナの立ち上げ、およびアプリケーションで必要なタスクを実行しています。
- run: ssh ${USER_NAME}@${HOST_NAME} -p ${SSH_PORT} &&
'cd xxxxx &&
git pull origin main &&
docker-compose restart &&
docker-compose run python ./manage.py migrate'
当然ながらユーザー名やホスト名、SSH ポートの情報も必要になるのですが
こういった情報は CircleCI に環境変数を事前に登録しておくほうがセキュアなので、ベタ書きはしないようにしましょう。
(config.yml に直接書くとリポジトリごと公開されてしまうので。)
最後に
いかがでしたでしょうか。
スタイリッシュではなく、一つひとつの処理を愚直に記述しているからこそ
やろうとしている処理のイメージが付きやすかったのではないかと思います。
今回はデプロイの部分まで紹介しましたが、ビルド&テストの自動化だけでも十分便利ですので
普段、コマンドを手入力でやっているようなテストがあったら CircleCI を試してみてはいかがでしょうか。
Django 以外に、Ruby on Rails で自動ビルド&テストについて環境構築から解説している記事も書いてますので、是非ご覧ください。
→Docker環境のRuby on RailsアプリをCircleCIで自動ビルド&テスト(RSpec)する方法