こんいす〜!ISUCON楽しかったですね。GROOVE X クラウドチームの mineo です。
この記事はGROOVE Xアドベントカレンダーの13日目の記事です。
クラウドチームでは、LOVOTと通信するクラウドプラットフォームの開発・運用をしています。今回は、インフラ管理にTerraformを導入したので、その話をさせてください。
導入した理由
Terraformを導入する以前はインフラを作成したコマンドをMakefileに残すことで管理されていました。Google Cloudをメインのクラウドとして使っているので、gcloudコマンドがMakefileに多く記載されていました。(記録が残っているだけでも素晴らしい...!)
作成記録を残すことで、どのようなリソースがあるか把握し、再度リソースを作成する必要が出来たとき、再利用することは可能です。ただし、
- あくまで、実行記録のため、実体と乖離していってしまう可能性があり、もし、そうなっても気付けない
- 各環境(staging/productionなど)に適用するオペレーションが難しい
- Makefileが肥大化していく
といった課題がありました。そこで、宣言的にIaCを導入することで、解決したいと考えました。
Google Cloudでは、Deployment ManagerというIaCのサービスもありますが、現時点ではTerraformがデファクトスタンダードであるため、Terraformを導入しました。
方針
弊社では、Terraformを導入した実績がなかったため、なるべく認知負荷が低い方針を採用しました。
シンプルなディレクトリ構成
ディレクトリ構成はGoogleのこのセクションを参考に以下のような構成にしました。環境ごとに、ディレクトリを分ける構成としています。シンプルでわかりやすく気に入っています。
-- envs/ -- dev/ -- main.tf -- locals.tf -- xxx.tf -- stg/ -- main.tf -- locals.tf -- xxx.tf -- prod/ -- main.tf -- locals.tf -- xxx.tf
moduleを使わない
moduleはTerraformのリソースを共通化できる強力なツールです。
しかし、moduleは設計が難しく、使い方もとっつきにくいです。時期尚早と考えて、導入しませんでした。
そのため、環境ごとにほぼ同じコードをコピペで運用する管理となり、その点は少し手間ではあります。今後はなんらかの工夫をしていく必要があるかもしれませんが、ベタで管理するようにしています。
変数を一つのファイルでのみ管理する
moduleを使わず、環境をディレクトリごとに管理するため、変更をコピペで適用することが多いです。 このときに、環境ごとの固有の値が散らばっていると大変なため、locals.tfでlocal変数を宣言して、ここでのみ変数を扱うようにしています。そのため、locals.tfとbackend等の設定を記載したmain.tfのみ、環境個別のファイルとすることが出来ました。
また、外部から値を注入することがないので、variableは使わず、localのみ変数として定義しています。
例↓
locals { cloudenv = "dev" region = "asia-northeast1" }
これもGoogleのベストプラクティスを参考にしました。
Terraform を使用するためのベスト プラクティス | Google Cloud
master mergeでの自動デプロイはしない
Terraformの事例では、GitHubでTerraformのコードをレビューしたのちに、masterにmergeして自動でterraform applyするという運用がみられます。しかし、我々の場合はインフラ専門のチームはなく、小規模なチームのため、以下のような理由で、自動terraform applyは不要ではないか、と考えました。
- terraform applyを実行しないと成功することがわからないときがある
- planでは問題なかったのに発生することがあり、手戻りが大きい
- 自動デプロイの場合は、実際にリソースを作成してから、terraform importをするという、運用ができない
- ペアワークなどで、その場でterraform applyをする運用が多い
- masterとの乖離が発生しにくい
可能な限り記述を短くする
先述しましたが、弊社ではTerraformの導入実績がないため、コードリーディングするときの負荷をなるべく減らすべく、コード行数をなるべく短く、わかりやすくするように心がけています。そのため、以下のような工夫をしています。
- デフォルトの値と同じ場合は、なるべくコードに記述しない
- 同じようなリソースが複数必要な場合は、for_eachを利用する
for_eachは最近初めて使ったのですが、とても便利な機能でした。for_eachの個別のリソースをterraform importするときに少し悩んだのですが、ドキュメントに記載されている通り、以下のような書式で実行することが出来ました。
terraform import 'aws_instance.baz["example"]' i-abcd1234
導入してみて
まだ道半ばですが、Terraformで宣言的にインフラの状態を管理することで、
- 実体と乖離がないこと
- 環境ごとの差異がないこと
といった安心感を持てるようになりました。また、コピペはやや面倒ではあるものの、一つの環境を他の環境に適用するオペレーションも簡単で、信頼できるものとなりました。
また、クラウドチームでは、Google Cloudだけではなく、中国のサービス展開用にTencent Cloudも使っており、両方ともTerraformでコード化できてよかったです。
ただ、TerraformがOSSでなくなってしまったことによりforkされたOpenTofuの動向も気になっており、状況次第で乗り換える必要があるかもしれない、とも考えています。
まだ道半ばではありますが、IaCは今後も進めていきたいと思います。
おわりに
GROOVE Xでは一緒にLOVOTを成長させていくメンバーを絶賛募集中です!少しでも、興味を持って頂けたら、お気軽にお話しましょう〜