「Assured」でバックエンド、インフラと英訳周りを担当しているオリバーです。趣味のボルタリングを再開するため体重を減らそうとしてますが、なかなか落ちてくれないこの頃です。今回は、プロダクトのアーリーステージによく後回しにされるインフラ管理について、「Assured」では開発初期フェーズでどう管理したのかを振り返りながらお伝えしたいと思います。
前提となるインフラの技術スタックの紹介は以下の記事に書いてありますのでぜひお読みください。
Assuredの技術スタック紹介(インフラ編) - Assured Tech Blog
現状
「Assured」では、監視、アラートなどのツールを除き、メインのアプリケーション基盤およびデータ基盤はすべて Google Cloud Platform(GCP)上で稼働しています。
これらのインフラ構成とIAM周りを合わせて、全体の7割を Terraform で管理しており、Infrastructure as Code(IaC) を実践しています。
開発初期フェーズ
一般的に、アーリーステージにおいてスピードはもっとも優先されることの一つです。また、製品についてのフィードバックサイクルを素早く回すことで、できる限り早く製品と市場の適合性を見つけることが良いとされています。スピードが重視される段階では、価値提供の競争やドメイン的優位性を作るために一時的に再現性のないインフラを構築してでも、お客様へ価値を提供するアプリケーションの開発を最優先とするケースが多いと思います。
「Assured」でもビジネスを推進するための開発スピードは常に重視しています。ただし、セキュリティに携わるサービスとして、一定のサービス品質を保ちつつアプリケーションを何度も再構築しながらビジネスの検証を進めていける環境を整備するため、初期フェーズであってもインフラの状態を常に把握しサービスの安全性を担保する必要があると判断しました。
そこで「Assured」では、開発スピードと安全性の双方を落とさないということを念頭に置き、以下の二点に注意しながら Terraform による構成管理を初期フェーズから導入しています。
- コンフィギュレーション・ドリフト:小さい変更が重なり、当初の想定と大きな乖離ができるリスク
- 属人化リスク:作った人のみしか構成を知らない状態、IaCしても一人運用になるリスク
コンフィギュレーション・ドリフト
アーリーステージでは、素早いビジネス変化がシステムの設定や規則、思想といった部分に多くの変化をもたらします。こういった変化に対して構成管理を伴わず手動で対応し続けると、意図しないセキュリティホールや権限管理上のミスを発生させてしまうリスクがあります。
こういった「コンフィギュレーション・ドリフト」を避けるためには IaC による構成管理が有効です。「Assured」では、必要な構成管理を行いつつ、開発スピードを損なわないようその対象は最低限のものに絞ることにしました。
必要最低限の構成管理を実現するため、以下の観点から管理するリソースを選択しました。
- 管理ができる対象か
- 変更内容を残すべき対象か
- レビューが必要な対象か
- 再現性が重視される対象か
- 依存関係が複雑な対象か
管理するリソースと管理しないリソースの例は以下となります。
管理するリソースの例
リソース | 理由 |
---|---|
GKE | Private Clusterを誤って公開してしまわないようにするため。また、費用に影響のある設定を記録するため。 |
CloudSQL | 重要なデータが保存されるDBへのアクセス元の管理のため。 |
Service Account(SA) | 用途不明なSAが出ないようにするため。GCP側でSAが変更された際に気づけるようにするため。 |
Security Policy | セキュリティに関するWAFポリシーの変更記録を残すため。 |
Cloud Build | 環境ごとの変数値を記録・管理するため。ビルド構成の変更失敗はリリース時の事故に直結し、レビューと状態管理が重要となるため。 |
管理しないリソースの例
リソース | 理由 |
---|---|
IAM Role | 各SAに紐づけられたRoleは頻繁に変更があり管理コストが高いため。 |
Big Query Scheduling Job | Terraform経由ではなくCloud Consoleから変更することが多いため。 |
DNS Managed Zone | 一度決めてしまえば、変更されることは少ないため。 |
Ingress(Load Balancer) | Kubernetesの設定ファイルで表現されており、変更や反映をGKEで管理しているため。 |
インフラの構成管理に含めないリソースに対しても、必要に応じて異なるアプローチでコンフィギュレーション・ドリフトのリスクを下げる試みも行っています。
例えばDatadogでは、すぐに使える検知ルール (OOTB rules)を設定することでIAM Roleの作成や変更、削除といったイベントをモニタリングできます。「Assured」では設定した検知ルールのセキュリティシグナルをSlackに連携し、各環境の Service Account や IAM の新規作成、変更および削除を通知しています。そうすることで、過剰な権限の付与や意図しない変更にチームが気づけるようになっています。
振り返りサマリー
👍 やりたいこと: コンフィギュレーション・ドリフトを減らしたい
⚠️ 問題点: すべてを Terraform で管理できるわけではない。GCPでは開発者以外が管理、変更しているリソースもある
✅ 解決策: なぜ管理したいかを整理し、管理すべきリソースに絞って管理する
💡学んだこと: IaCによる構成管理のコストが高い場合は、重要な変更の通知など工数が低い対応策でもリスクを軽減できる
また、最初の構成を作る際に全員でレビューすることで、構造の理解や設定の理由、値をすり合わせられたのも良かった点の一つでした。
属人化リスク
インフラ構成の管理は属人化しやすく、作った人にしかわからなくなりがちです。構成管理も重要ですが、仮にそれが導入できていたとしても、管理が属人化していると担当者の不在によって管理ができなくなったり、構成を変更可能な人が限られることによって開発スピードの低下を招いたりするリスクがあります。
「Assured」でも、属人化リスクは今後起こり得る問題だと事前に認識していたので、構成に関する意思決定はチーム全体で議論し、前提知識をドキュメント化して共有しながら管理を行っています。また、インフラに不慣れなメンバーでも管理がしやすいように、なるべくシンプルな管理を心がけています。
例えばTerraformの構成について、最初は共通部分をModule化し、環境ごとに同じ構成が再利用できることで再現しやすいような以下の構成を考えました。
_modules
:共通コンポーネントのTerraformファイルenv
:環境別の変数
├── README.md ├── _modules │ └── gcp │ ├── data │ ├── db │ │ ├── main.tf │ │ ├── output.tf │ │ └── variables.tf │ ├── gke │ │ ├── main.tf │ │ ├── output.tf │ │ └── variables.tf │ ├── network │ │ ├── dns │ │ │ └── main.tf │ │ ├── firewall │ │ │ ├── main.tf │ │ │ └── variables.tf │ │ ├── subnet │ │ │ ├── main.tf │ │ │ ├── output.tf │ │ │ └── variables.tf │ │ └── vpc │ │ ├── main.tf │ │ ├── output.tf │ │ └── variables.tf │ └── storage │ ├── main.tf │ └── variables.tf ├── _role │ └── terraform_account_role.yaml └── env ├── dev │ ├── backend.tf │ ├── main.tf │ ├── output.tf │ ├── provider.tf │ ├── terraform.tfvars │ ├── variables.tf │ └── versions.tf └── prd
上記は一見すると構成を再利用できる Modules を活用した”良い感じ”のディレクトリ構成ですが、Module の共通化の方法や変数のルールなどを覚えるコストの割に、共通化したModuleが再利用される場面が少なく、当時の「Assured」の構成管理には過剰なものになってしまっていました。
チームでも会話し、最終的には共通化を意識しすぎず各環境ごとのシンプルなファイル構成とすることにしました。ファイル名もGCPのマネージドサービスごとに作っています。
また以下を固定することで、各メンバー間でTerraformの設定を統一させています。
backend.tf
:stateファイルを保存しているGCS bucketの情報provider.tf
:Terraformで利用しているGCPプラグインなどproviderのバージョン管理output.tf
:Terraformの出力情報の形式と表示したくない情報のマスキング設定
├── envs │ ├── dev │ │ ├── backend.tf │ │ ├── bigquery.tf │ │ ├── cloudbuild.tf │ │ ├── function.tf │ │ ├── gke.tf │ │ ├── iam.tf │ │ ├── logging.tf │ │ ├── maintenance.tf │ │ ├── network.tf │ │ ├── output.tf │ │ ├── postgres.tf │ │ ├── provider.tf │ │ ├── pubsub.tf │ │ ├── security.tf │ │ ├── storage.tf │ │ ├── variables.tf │ │ └── versions.tf │ ├── prd │ │ ├── backend.tf │ │ ├── bigquery.tf │ │ ├── cloudbuild.tf │ │ ├── function.tf │ │ ├── gke.tf │ │ ├── iam.tf │ │ ├── logging.tf │ │ ├── maintenance.tf │ │ ├── network.tf │ │ ├── output.tf │ │ ├── postgres.tf │ │ ├── provider.tf │ │ ├── pubsub.tf │ │ ├── security.tf │ │ ├── storage.tf │ │ ├── variables.tf │ │ └── versions.tf │ └── stg │ ├── backend.tf │ ├── bigquery.tf │ ├── cloudbuild.tf │ ├── gke.tf │ ├── iam.tf │ ├── logging.tf │ ├── network.tf │ ├── output.tf │ ├── postgres.tf │ ├── provider.tf │ ├── pubsub.tf │ ├── security.tf │ ├── storage.tf │ ├── variables.tf
こうしたファイル構成のメリットとしては、
- 独自の事前知識を減らす事でチーム全員が管理しやすくなる
- 各環境ごとにファイルをまとめるため、環境の増減が容易になる
デメリットとしては、
- 環境間の差分が出やすい
- 環境が増えると編集コストが上がる(コピペが増えがち)
ということが言えそうです。「Assured」では、環境ごとに融通が効きやすくなることで新機能や実験的な試みを気軽に早く試せるため、よりメリットが大きくなると判断しました。また、デメリットに関しては、アーリーステージの規模であればレビューなど人による確認で十分補えると考えました。
この構成は Terraform の機能を全て活用した”理想的”なファイル構成ではありませんが、全員が管理しやすく IaC の利点と開発スピードを両立できる一つの解ではないかと思います。
振り返りサマリー
👍 やりたいこと: 全員で Terraform を管理できる状態にしたい
⚠️ 問題点: ファイル構成や Module の活用方法によっては管理コストが高くなってしまった
✅ 解決策: 環境ごとにディレクトリを分け、わかりやすいファイル構成にする
💡 学んだこと: Terraformの導入に際して、フェーズとチームに合わせたファイル構成を意識する
まとめ
アーリーステージにおいては、必ずしも IaC による構成管理をする必要はありません。しかし、インフラの構成管理をせずに手動でインフラを更新し続けると「コンフィグレーション・ドリフト」が発生しやすくなり、結果として大きな事故につながる可能性があります。また、構成管理を導入しても「属人化リスク」によって開発スピードの低下を招いてしまうこともあります。「Assured」では必要最低限の構成管理を心がけてわかりやすいリソース管理を意識した結果、こういった問題をある程度回避できたと考えています。インフラの状態を管理し変更記録を残すことで、チーム内に一定の安心感を生み出し、開発スピードも向上できていると感じます。
現在、「Assured」は一緒に技術で事業を推進していくコアメンバーを募集しております。事業やプロダクトの成長、変化に対して常にチームで最適解を考えながら物事を進めていくような環境を楽しめる方を募集しております。Meetyもあるので気軽にお声掛けください。
エンジニアチーム紹介ページ
カジュアル面談フォーム