CircleCI で Terraform リリースのサーバレス化 〜インフラの独自リリースを継続的リリースへ
クラウドソーシングサービス ランサーズ や自分に合ったメンターとのマッチングサービス MENTA といったさまざまなサービスを提供しているランサーズ株式会社。AWS上で動作しているサービスのインフラリソースの管理には、HashiCorp社のTerraformを使用しており、CircleCIを使ってリリースされています。本稿では、2021年11月24日に開催された CircleCI ユーザコミュニティミートアップ にて、同社SRE 安達 涼(@adachin0817)氏に紹介いただいた CircleCI を使ったリリースフローを紹介します。
これまでの Terraform 管理方法
安達 涼氏によると、ランサーズ社では開発者一人一人が使用するローカルのPCにTerraformのクレデンシャル情報を持たせたくないということで、共用のDeployサーバーを用意して、そのサーバー上からランサーズに関してはTerraform CIを使ってAWS上にデプロイしてきていました。
一方、ランサーズ以降に開発、提供されたサービスは AWS Fargate 上で動作しており、そのAWS Fargate がコストパフォーマンスの高いGraviton2(ARMコア)サポートを表明したこともあり、ランサーズについても、性能評価が終わり次第、AWS Fargateへの移行を予定しています。移行が完了すれば、これまでもAWS Fargateへのデプロイ等はCircleCIでやってきているので、Deployサーバーは廃止する予定です。
共用環境でビルド/デプロイする上での課題
さて、ランサーズ社では、クレデンシャル情報を開発者一人一人のPCが持つのではなく、一か所で保持・管理したいというニーズのもと、Deployサーバーという開発者間で共用するビルドやデプロイのためのサーバーを利用してきました。
共用環境を利用することで、どのような課題があったのか、安達氏は、次の5点を挙げています。
- ブランチの切り替えを忘れて、他のリリースに影響が出る (割と起こりがち)
- 一台のサーバーを共有する事で、複数人でのTerraform開発スピードが遅れる
- 一台のサーバーを共有することで、サービスごとにTerraformのバージョンの切り替えが必要になることがある
- Terraformの各種設定の変更が発生するたびに、SREがプルリクをレビューした上でDeployサーバー上の設定を行うため、工数の増大と作業漏れを招いている
- そうはいっても、各エンジニアにTerraformのクレデンシャルを渡して、ローカルで実行してほしくはない
これらの課題を解決するため、Terraform専用の開発環境を(共用サーバーによってではなく)Dockerコンテナとして構築、提供し、CircleCIで継続的にリリースする方法を考えました。
Terraform開発環境のコンテナ化
では、課題の中でも言及されていたクレデンシャルの管理も含め、コンテナベースのTerraform開発環境をランサーズ社ではどのように実現していたのでしょうか? 安達氏は次のように説明しています。
各サービスに対応するプロジェクトがリポジトリにあり、それぞれのプロジェクトのリポジトリにTerraformディレクトリを用意しておき、そのディレクトリにコンテナからマウントしています。
クレデンシャルは、ランサーズ社ではAWS Key Management Service (KMS) を使って管理しており、1) KMS専用のクレデンシャルから 2) 暗号化されたkeyをコピーして渡し、3) コンテナ内でクレデンシャルを上書きして復号しています。したがって、
- 暗号化を二重で行うことにより、セキュリティが向上
- 上書きしたクレデンシャルはdocker-compose down時(コンテナ終了時)に自動的に削除される ため、漏洩リスクは下がるようになっています。 (具体的なコードは、発表スライドの16ページ目を参照)
CircleCI を使った当初のリリースフロー
さて、開発環境にて開発が進んだ後、どのようにして本番環境にリリースしていたのでしょうか。安達氏は次のように説明しています。
「これまでは、masterブランチにマージされると、terraform applyを実施していたものの、開発チームだけでなく、SREチームもTerraformでデバッグしてそのままapply、開発チームの別のメンバーも他のプルリクをマージ、といった具合でAWS側でのデプロイ内容が元に戻ってしまい、危うくインシデントになりそうでした。さすがにこれでは問題だろう、ということで masterブランチへのマージで terraform applyを自動実行するのはやめにしました。」
「CircleCIのAPIを使って、リリース用のシェルを実装したものの、権限設定上、開発メンバー全員がリリース可能になってしまうという課題が残ってしまいます。CircleCIのApproval(承認)フローを試してみたものの、作業フローが増えてしまい、SRE側の作業負荷が増す、という問題がありました。」
CircleCI を使ったリリースフローの改善
単に本番環境へのリリースを自動化するだけでは、あるべきリリースフローの姿が実現できない状況において、どのような改善が図られたのでしょうか。安達氏は次のように説明しています。
2021年4月にリリースされたセットアップ ワークフローによるダイナミック コンフィグを利用しています。まずは、大元のコンフィグ(config.yml)では、path-filtering Orbを使って、masterマージ(base-revision: origin/master)の際にmappingでステージング(stg)環境なりプロダクション(prd)環境に対しての変更かどうかを検出します。該当する場合は、デプロイ用のコンフィグ(deploy.yml)でワークフローを続けます。
一方、処理を引き継いだデプロイ用コンフィグの中では、 check_terraform_difference というジョブを実行しています。これは、CircleCIでは差分があることは検出できるものの、差分がないことは検出できないために、結果として終了ステータスがfalseになってしまい、結果として後続のジョブが実行できなくなってしまうので、echo でメッセージ出力して終了ステータスをtrueにするようにしています(ここはCircleCIに改善してもらいたいポイントです)。
最終的にterraformディレクトリに差分がなければ何もしない、masterブランチとの間で差分があればterraform applyを実行するところまで、望んでいるセキュリティレベルを担保しつつ、自動化することができました。
最後に安達氏は、ここでの取り組みがTerraformリリースのサーバレス化に止まらず、AWS Lambdaを使ったサーバレスの実装においても、個別のプロジェクトとして大量のレポジトリに分割しなくても、例えば、AWS Lambda専用リポジトリの中で、ディレクトリごとにサーバレスアプリケーションのプロジェクトを分け、個別のプロジェクトをpath-filteringした上で、差分があればリリースするような応用もできること、すでにランサーズ社で取り組みを進めていることを合わせて共有いただきました。
モノリポを活用されている開発現場においても、サーバレスアプリケーションを開発されている開発現場においても、CircleCIのセットアップ ワークフロー機能で開発やデプロイの効率化、自動化をより一歩、先に進めることができます。安達様、ミートアップでのご登壇、ありがとうございました。
追加お役立ち情報一覧
- 発表スライド: インフラの独自リリースを継続的リリースへ
- 関連ブログ: CircleCIでTerraformのCI/CD環境を実装してみた
- 関連ブログ: 特定ディレクトリに差分が出たら terraform apply を適用するように実装してみた
- CircleCIブログ: ダイナミック コンフィグを使用してCI/CDパイプラインをビルドする方法