chroju.dev/blog

the world as code

Terraform で疲れないために

追記(2019-02-27) : 一部表現等見直しました。論旨は変わっていないです。

Infrastructure-as-Code-is-very-tired - Speaker Deck

こちらの記事を読みました。スライド内で触れられている Terraform Best Practices in 2017 - Qiita については私も読んだことがあり、以下の記事で言及させてもらっていたり。悩みますよね、 Terraform 運用。

私もある程度ポリシーめいたものをもって、これはコード化するべき、これは手で作ってもいい、みたいな線を引いてはいるものの、あまりそれを言語化してみたことがなかったので、いい機会だしまとめてみます。疲れないようにするべき、コード化がすべてではない、という視点は先のスライドと共有できると思っています。なお、私の場合はほぼ AWS にしか Terraform を使っていません。

柔軟性、再利用性を過度に求めない

端的に言うとこれがすべてで、私の場合は module をそんなに使ってはいないです。スライド内では DynamoDB のコードを書こうとしたときに、どう再利用しようかと考えて module 実装に向かうところが描かれていましたが、基本的には必要性がない限り module 化はせず、再利用のこともあまり考えないというのが個人的なスタンス。

というのも、 module の維持管理がやっぱり辛いから。そもそものところでAWS リソースの設定項目は常に変化していきますので、一度 module を作っても、それが新しい設定項目に対応していないので更新が必要、なんてことはザラにあります。例えば最近だと IAM ユーザーやロールにタグが付けられるようになる、という変更が2018年11月に入っていますが、 Terraform はすでにこれに対応済みです。

となると、 IAM ユーザー用の module を作っていた場合、このタグに関する新たな設定箇所に対応するべきかどうかを判断して、必要に応じて module の更新を行うことになります。このようなことが、年間に何回かは発生します。

またこのような AWS 側の更新以外でも、 module を再利用していくなかで、やっぱりこういう使い方をしたい、変数をなにか付け足したい、という内部的な要因で更新を迫られるケースは往々にしてあります。最初から将来的な利用ケースをすべて見越して、完璧に再利用可能な module を作ることは難しいです。おそらくスライド中ではそれをやられようとされていたのかなと見受けます。 Terraform resource に設定可能なすべての変数を module から設定できるようにし、設定しない場合は default 値を埋め込むようなコードを見かけたので。個人的には、そこまでいってしまうとやっぱり管理コストが大きくなりすぎるように思える。

あと、同様の理由で Terraform module registry も使っていません。これは先に上げた『Terraform module は何が嬉しいのか』という過去記事の中でも言及していますが、 module registry にあるのは多くが「設定可能なすべての変数を設定できるようにした module」のようで、使う上でコストが高いというか面倒だなと思ってしまうためです。また Terraform resource 自体の更新に追随するのは、イコールで AWS のアップデートに追随することなので受け入れていますが、 module の更新についていくコストはちょっと余計に感じるので割きたくない、という感じ。

module は抽象化のために使う

では module をまったく使っていないのかと言うとそんなことはないです。使っているのは基本的に抽象化のためです。

先のスライドでも言及がある、複数サービスを繋いで使うような場合がそれに当たります。 ELB と EC2 と RDS と、それらで使うセキュリティグループをワンセットで立ち上げることがよくある、みたいな。そういうときは、そのワンセットをポンと立ち上げられる module を作り、いくつか可変な値を変数で設定できるようにしてあげるととても楽です。

また、例えば先の RDS と EC2 とセキュリティグループをセットで作る、という場合、その時点で EC2 の IP から RDS の 3306 への通信をセキュリティグループで開ける必要がある、みたいな自明な設定が出てきます。こういうものも抽象化が可能なので、 module 内で自動的に処理しています。 Route53 で正引きと逆引きを両方設定するのは面倒なので、IP とホスト名を渡すと両方自動的に作ってくれる module を作ったりとかしています。

なんでも module にするというのは旨味がやっぱり少ないので、人間がやらなくてはならない具体的作業を抽象化できそうな場合に限り module 化をします。ただ、あくまで何回かその操作を行う可能性がある場合です。マネコンや CLI をいじっていて、この操作って何度もやってるし面倒だな、と感じたときに module 作成のトリガーが引かれるイメージです。

何のために Infrastructure as Code するのか

Infrastructure as Code を進める目的は、抽象化以外だと、自分の場合は以下の2点が大きいです。

  • 設定作業、設定変更作業のトレーサビリティ確保
  • 設定の標準化による運用コストの抑制

前者は AutoScaling の台数設定とか頻繁に変えると思いますが、コードで commit 履歴残しておくと、いつ何のためにこの設定にしたのか、という妥当性を後追いできるので精神衛生に良いです。正直現状の設定値がどうなっているか、というのは畢竟現物を見てしまえばわかるわけで、設定のバックアップの意味というよりは、「なぜ」その設定にしたのかという根拠が残っていることの効果を強く感じています。ちなみに単に設定変更履歴を追いたいだけであれば AWS Config でも十分だったりするわけで、 Git を使う意味というのは commit message だとか Pull Request を通したやり取りのような、人間の思想が残る点にあると思っています。スライドの p.64 では「何回も作らない」 CDN などの設定はコード化しないとありましたが、自分の場合は「なぜ」を残す意味があると判断したら、設定変更が頻繁ではないだろうものでもコード化します。このように、ツールの使い所は目的に応じて変化するものであり、万人に適用可能な最適解は無いと考えています。

2点目の「設定の標準化」に関しては、社内に複数存在する AWS アカウントで、それぞれ CloudTrail の設定が異なっていたりしたら困るので、一律に揃えたいときにコード化してそれを全部に対して当てていく、というような使い方をします。設定差異が存在する、というのは平常時であればそんなに気にしませんが、いざ問題が起きたときはやはり「なぜ」というのが問われる部分になりますので、必要性がなければ設定を標準化しておくことは運用コストの低減に繋がります。その設定がレポジトリを通じて、メンバー間に可視化されているというのも意味が大きいです。

Conclusion

つらつらと書きましたが、スライドの内容には基本的に同意で、何のために IaC したいのか、という ROI の観点は常に考慮が必要だと感じています。 Ansible でサーバーの設定作業をコード化して、その後の設定変更も全部 Playbook の変更で頑張っていたけど、徐々に無理になってきたので設定変更手順を Wiki に書いて Playbook は破棄した、その方が「管理コスト」という面では低くなった、なんて経験もあります。手作業は残ってても構わないと思います。ただし、代替として手順を書くとか、設定をエクスポートするなりして残す、ということは必要に応じてやらなくてはならないですが。

Terraform というツールは大好きなので、自分の目的にどう合わせていけるか、今後も試行錯誤を重ねていきます。