IaC
Infrastructure as Code ・ あいえーしー
インフラのあるべき構成をコードで宣言し、ツールに現実を合わせさせて、再現と変更の管理を可能にする実践。
✦ 30秒で試す — 航海サーバを手で直すのが、いちばん危ない修理になる翌週の apply の後、web-1 はどうなる?予想してみる概要
IaC(Infrastructure as Code)は、サーバ・ネットワーク・データベースといったインフラの構成を、管理画面での手作業ではなくコードとして書き、ツールに適用させる実践です。「Web サーバを2台、この設定のロードバランサの後ろに置き、RDBMS は外から見えないようにする」といった構成をテキストファイルに書くと、ツールがクラウドの API を呼んで、そのとおりの環境を作り上げます。
代表的なツールは、クラウドをまたいで使える Terraform(と、そのオープンソースのフォークである OpenTofu)、AWS の CloudFormation、汎用のプログラミング言語で書ける Pulumi や AWS CDK などです。構成がただのテキストになるので、Git での履歴管理、プルリクエストでのコードレビュー、同じ環境の複製といった、アプリケーション開発で培われた規律をそのままインフラに持ち込めます。
IaC を理解する鍵は、3つのものを区別することです。「こうあるべき」と書いたコード(宣言)、ツールが前回どう作ったかを覚えている記録(Terraform では state)、そしてクラウドの上に実際にある資源(現実)です。IaC のツールの仕事は、この3つを突き合わせて差分を見つけ、現実を宣言に合わせることです。
なぜ生まれたか
手作業のインフラ管理には宿命的な問題がありました。管理画面で設定したサーバは、時間とともに「誰が・いつ・なぜその設定にしたのか」が失われ、触るのが怖い「秘伝のタレ」になります。本番とステージングの微妙な違いが原因の「本番でだけ起きるバグ」、担当者の退職とともに消える構築の知識、障害でサーバを失ったときに元どおりに作り直せない恐怖。どれも「構成が人の記憶と手順書にしかない」ことに根があります。1台ずつ手で育てたサーバは、名前を付けて世話をするペットにたとえられました。
この問題への答えは段階的に形になりました。1993年に Mark Burgess が作った CFEngine は、「あるべき状態」を書いておき、ずれていたら直すという収束の考え方を構成管理に持ち込みました。2000年代には Puppet や Chef、2012年には Ansible が、サーバの中身(パッケージや設定ファイル)をコードで管理する道具として広まります。そしてクラウドコンピューティングの普及で、サーバそのもの、ネットワーク、データベースまでが API で作れるようになりました。2011年の CloudFormation、2014年の Terraform は、インフラ全体を「作る」ところからコードに書けるようにしました。構成の唯一の正解をコードに置き、人はコードだけを編集する。これが IaC の中心の思想です。サーバは手で世話をするペットではなく、同じ型からいつでも作り直せる家畜として扱えるようになりました。
詳細
宣言・記録・現実 — 3つを突き合わせる
Terraform を例に、IaC のツールが何をしているかを追ってみます。コードには「web サーバが2台、サイズは small」とだけ書きます。ツールは前回の適用結果を state(状態ファイル)に記録していて、「コードの web-1 は、クラウドの i-0abc という資源に対応する」という対応表を持っています。変更を適用するとき、ツールはまず state に載っている資源について、クラウドの API に現在の姿を問い合わせて記録を更新します(refresh)。そのうえでコードと記録を比べ、差分を「作る・変える・消す」の計画にします。
この仕組みから、2つの大切な性質が出てきます。1つは冪等性です。「サーバを作れ」という手順ではなく「サーバが2台ある状態」という結果を書くので、すでに望む状態なら計画は空になり、何度適用しても同じ状態に落ち着きます(冪等性)。手順を書いたスクリプトを2回実行するとサーバが4台になりかねないのと対照的で、安心して繰り返し実行できることが自動化の土台になります。もう1つは、ツールが知っているのは state に載っている資源だけだということです。誰かが管理画面から作った資源は state にないので、計画にも現れず、ツールからは存在しないのと同じです。
plan → apply のワークフロー
Terraform に代表される流れの核心は、適用の前に変更の中身を確かめられることです。コードを書き換えたら、まず plan で「何が作られ、何が変わり、何が消えるか」の計画を確認します。消すつもりのないデータベースが「削除して作り直す」になっていれば、ここで止められます。問題がなければ apply で実際にクラウドへ反映します。この二段構えが、インフラの変更に「実行前のレビュー」という安全装置を与えます。
state はチームで共有する場所(クラウドのストレージなど)に置き、同時に2人が適用して記録を壊さないようにロックをかけるのが必須です。state には資源の属性がそのまま入るので、データベースのパスワードのような秘密の値が平文で残ることがあります。state を置く場所の読み取り権限は、本番の認証情報と同じ重さで守ります。
ドリフト — コードの外で変わった現実
IaC の最大の敵は「ドリフト」、つまりコードの外で加えられた手作業の変更です。障害対応の最中に管理画面から直接サーバのサイズを上げると、コードと現実がずれます。次に誰かが別の目的で apply すると、ツールはそのずれを「コードに合っていない」と判断し、何の警告もなく元に戻してしまいます。手で消した資源は作り直され、手で作った資源は誰にも管理されないまま残って費用だけがかかり続けます。
下の部品で、クラウドの列のセルを押して手作業の変更を加え、plan と apply を押してみてください。同じ変更がどう扱われるかが3つの列の線で見えます。最後に手順型のスクリプトに切り替えて、2回実行したときに何が起きるかも比べてください。
コード(宣言)・state(記録)・クラウド(現実)がそろっています。右のクラウドの列の web-1 を押して、管理画面から手作業で変えてみてください。
対策は規律と仕組みの両方です。「緊急時も含め、変更は必ずコードを通す」をチームで守り、緊急で手を入れたら直後にコードへ書き戻します。定期的に plan を実行して差分が出ていないかを確かめ、ドリフトを早く見つける運用も広く行われています。そもそも本番の管理画面での書き込み権限を人に与えない(最小権限)ことも、ドリフトを減らします。
コードになると何が変わるか
構成がコードになると、Git のプルリクエストでインフラの変更をレビューできます。「なぜこの変更をしたか」がコミットの履歴に残り、問題があれば前の版に戻せます。さらに強力なのが環境の複製で、同じコードに変数だけ変えて適用すれば、本番とほぼ同じステージング環境が数分で立ち上がります。災害からの復旧でも、「コードさえあれば環境全体を作り直せる」状態は、手作業の運用とは比べものにならない安心感があります。
CI/CD のパイプラインに組み込むと、プルリクエストで plan の結果をレビューし、マージされたら CI が apply する、という流れになります。これはパイプラインが変更を本番へ押し込む「プッシュ型」の運用です。これと区別すべきなのが GitOps で、Git を唯一の正解として、クラスタの中で動くエージェント(Kubernetes の Argo CD や Flux)が Git を定期的に見に行き(プル型)、現実との差分を見つけたら自動で直し続けます。プッシュ型はマージの瞬間にだけ現実を合わせるので、その後のドリフトには次の実行まで気づきません。GitOps はエージェントが差分を収束させ続けるので、手作業の変更もすぐ Git の状態に戻されます。本番への書き込み権限を CI に渡さず、クラスタの中のエージェントだけが持てばよい点も違いです。
手順型の道具との関係
Ansible のような構成管理の道具は、「このパッケージを入れる」「この設定ファイルを置く」というタスクを上から順に実行する、手順に近い書き方をします。それでも多くのモジュールは「すでに入っていれば何もしない」ように作られていて、繰り返し実行しても同じ結果になる冪等な手順を組み立てられます。ただし、手順そのものが「いまどうなっているか」の全体を覚えているわけではないので、コードから削除した資源を現実から消してくれることはありません。Terraform のような宣言型の道具は資源を作って消すところ、Ansible はサーバの中身を整えるところ、と組み合わせて使われることもよくあります。
落とし穴と設計の粒度
すべてを1つの巨大な構成にまとめると、plan に時間がかかり、変更の影響範囲も読みにくくなります。ネットワーク・データベース・アプリの基盤といった、変更の頻度と壊れたときの影響が違う単位に分けて、state も分けるのが定石です。消したら取り返しのつかないデータベースには、誤って削除されないよう保護の設定を付けておきます。コードの書き換えがリソースの「作り直し」を引き起こす(名前を変えただけで、中身ごと消して作り直す計画になる)ことは IaC の典型的な事故で、plan を読む習慣がそれを防ぎます。
秘密の値の扱いにも注意が要ります。パスワードや API キーをコードに直書きせず、シークレット管理の仕組みから実行時に読み込むようにします。前に書いたとおり、state にも値が残り得るので、state の保管場所の暗号化と権限の管理はセットです。導入の手間は小さくありませんが、「そのインフラを2年後も安心して変更できるか」という問いへの、いま最良の答えが IaC です。
試問
左からコード(こうあるべきという宣言)・state(前回の適用の記録)・クラウド(現実)の3列です。障害の夜、担当者が管理画面から web-1 を large に上げたところで、コードは small のままです。コードは下のボタンで書き換えられ、plan で差分を見て、apply で現実をコードにそろえます。
障害の夜、担当者が管理画面から web-1 を large に上げました(朱の文字)。コードと state は small のままです。
