CircleCIを使ってリリースタグを作ったらKubernetesにアプリケーションをデプロイする仕組みを作る

今回はプライベートで作ってるサービスのデプロイで抱えていた問題を解決した話

デプロイで抱えていた問題

今運用している2つのサービスはk8s上で動いているのですが、現状こんな感じのサイクルでデプロイしています。

  1. masterからブランチきって作業
  2. PR出してセルフレビューしてマージ
  3. いくつかマージした後によきタイミングでリリースタグをきる
  4. デプロイ用のシェルスクリプトを実行
  5. 待つ

というのを繰り返している。
普段は仕事しているので、だいたい帰ってリリースできそうな単位で開発してその日にデプロイしているのでほぼ毎日デプロイしている。

このサイクルの課題としては デプロイを考えないといけないこと なんですよね。
短い時間しか作業できないからこそ考えることは最小限にしたくて、リリースタグきったらあのスクリプトを実行して数分まってとかやりたくないです。

候補

世の中には Pipeline を組めるのをいくつかあるので、そこからどれかを選択します。
選ぶ軸としては2つで

  • リリースタグをきったらデプロイ開始してほしい
  • できるだけコストを抑えたい

個人的には最近 Google Cloud が正式にサポートした した Spinnaker を使おうとおもったのですが、新しくインスタンスが立てる必要があったりでコストが増えそうでした。
同様に Tekton も気になってはいたのですが現状の k8s のリソースが結構かつかつで新しく node を追加する必要があるので、こっちも断念。

codefresh は無料で利用できそうでよかったのですが、初期登録でバグを踏んだのかうまく利用できず断念。 ※後に運営の方から修正したと連絡がきて使えるようにはなりましたがその頃には別の案を採用していました。

AWS で CodePipelineを使う案ですが、 AWS のブログでこんな感じで紹介されてました。

aws.amazon.com

CodeBuildでイメージを作って Lambda で kubernetes API を実行している感じですが、現状 Kustomize を利用しているので、できればそれを使用したいです。

そして色々検討した結果 CircleCI を使うことにしました。
もともとCIとしてCircleCIは使っていたので、それの延長としてデプロイをすることにしました。

CircleCI でデプロイする

k8s API をリクエストするための認証情報の作成

まず今回外部のサービスから k8s API をリクエストするために認証情報を作成します。
認証は前回記事にした ServiceAccount を作成しました。
blog.hatappi.me

kubectl applyした後に完了するまで待つようにする

例えばRailsのアプリケーションのデプロイだと rails db:migrate しますが自分の場合は k8sjob を使っています。

localからデプロイしていた時は watch しつつデプロイが完了するまで見届けていましたが、 CirlceCI上で実行するにあたって良い感じに待ってくれるようにする必要があります。
その仕組みとしては現在 kubectl v1.15.1 では 実験的な機能 となっている kubectl wait を使用しました。

kubectl apply -f db-migrate.yaml
# timeout 600秒で完了になるまで待つ
kubectl wait --for=condition=complete --timeout=600s -f db-migrate.yaml

こうすることで完了するまで待ってくれるようになりました。

CircleCI にデプロイJOBを構築する

まずは Docker Image の build と ECR への push ですが Orb を使うことにしました。 Orb は CircleCI が提供する機能ですがジョブ、コマンド、Executor のような設定要素を共有可能なパッケージにしたものです。

例えば今までだと build と ecr に push といえば build して ECR へログインして docker pushしてなどいくつか step を追加する必要があります。

- run: |
     docker build . -t hogehoge
- run: | # ECR にログイン
     $(aws ecr get-login --region ap-northeast-1)
- run: |
     docker push hogehoge

例えば CircleCI が提供している circleci/aws-ecr Orb を使うとこんな感じで定義できます。

      - aws-ecr/build-and-push-image:
          account-url: AWS_ACCOUNT_URL
          repo: "${AWS_ECR_REPO_NAME}"
          region: AWS_REGION
          tag: "${CIRCLE_TAG}"
          extra-build-args: "--build-arg env=${RAILS_ENV}"

後は k8sAPI を使うために作成した認証情報を前回のブログの最後のほうに紹介した kubectl でサービスアカウントを使う方法を job に組み混んだら後はいつもローカルでたたいているデプロイスクリプトを実行したら終わりです。

blog.hatappi.me

デプロイの過程は通知してほしい

今までデプロイは手元でやっていたのでどこまで終わったとか失敗したなどは手元を見れば把握できました。
ただ今回CircleCI上で実行するようにしたことで今デプロイがどこまでいったかとか失敗したかなどの進捗を把握しずらくなりました。

そこでデプロイの過程を Slack に通知するようにしました。

こちらも CircleCI が提供する Orb を使用しました。
https://circleci.com/orbs/registry/orb/circleci/slack

各 job の最後に↓こんな感じで定義します。

      - slack/status:
          failure_message: ":red_circle: $CIRCLE_JOB が失敗しました"
          success_message: ":tada: $CIRCLE_JOB が成功しました"

実際に JOB が実行されると↓のような通知が届きます。

f:id:hatappi1225:20190802233714p:plain

最後に

今回はデプロイを自動化しました。
今のところは順調に動いているので良い感じです。
ただCircleCI上で docker build しているのですがキャッシュがきいてないので、毎回デプロイごとに build 時間がかかってしまいます。
これを有効にする方法も CircleCI では Docker Layer Caching が提供されているのですが、これは有料なんですよね。

しばらく運用してみて良い方法を考えたい。