※本記事は、技術評論社「Software Design」(2023年5月号)に寄稿した連載記事「Google Cloudで実践するSREプラクティス」からの転載です。発行元からの許可を得て掲載しております。
はじめに
第1回(本誌2023年4月号)では、キャディにおけるGoogle Cloudを中心としたサービス基盤の全体像を紹介し、信頼性向上のために筆者らが心掛けている技術選定基準について触れました。
今回からは数回にわたって、Terraformを中心としたIaC(Infrastructure as Code)の実践例を紹介予定です(図1)。
▼図1 CADDiスタックにおけるTerraformの位置付け
今回は「インフラの信頼性」という側面で考えるIaCの重要性と、Google Cloudを中心としたクラウドベースのシステムのIaCにおいて、なぜTerraformとの相性が良いのかを説明します。
クラウドネイティブにおけるIaCの必要性
システムのインフラをコードとして記述すること、すなわちInfrastructure as Code(IaC)は、すでに当たり前のプラクティスとなりつつあります。たとえば、オンプレミス上のシステムや、パブリッククラウド上でAmazon Web Services(AWS)のEC2など仮想マシン(VM)ベースのシステムを構築する際は、Ansibleを使ってIaCを実現する事例が増えてきました。また、AWS CloudFormationを利用してAWS上のリソースを作成している方も多いでしょう。
手作業によるインフラ管理の課題とIaCによる解決
AWS、Google Cloud、Microsoft Azureなどのパブリッククラウドは、いずれもWeb上のUIからVMインスタンスを起動するなどの操作が可能で、手軽なことがメリットの1つです。その一方、クラウドネイティブなアーキテクチャで実運用可能なシステムを構築するには、さまざまなサービスを組み合わせる必要があり、膨大な設定が必要となります。 これらをWebUIからいわゆる「ポチポチ」で設定するのは非現実的で、言うまでもなく次のような問題が生じます。
- 構築作業に時間がかかる
- 設定内容を記録しにくい
- 全体の見通しが悪く、レビューしにくい
WebUIによるインフラ構築は手軽であるため、学習やコンセプト検証など、試行錯誤が主体の作業とは相性が抜群です。しかし、実運用するシステムを構築・維持するうえでWebUIだけが設定手段となっている場合は、従来のサーバ構築よりも非効率になってしまいます。
また、WebUIでは設定内容の記録が困難なことも大きな問題です。ここから、さまざまな問題が派生します。 通常、システム運用時には、開発環境、テスト環境、本番環境など、複数の環境が必要になります。これらを手作業で同じように構築するのは手間がかかりますし、ミスも発生します。 記録が困難であることからノウハウを残しにくく、インフラの品質も属人的スキルに依存しがちになります。変更管理ができないことから、インフラの変更意図がわかりにくく、障害発生時に元の構成に戻すことも困難でしょう。
設定内容のレビューをするにしても、Web上の管理画面から確認が必要で、大規模なシステムでは確認箇所が広範囲にわたります。このように見通しが悪い状況ではレビュー効率が悪く、問題を適切に見つけることも困難です。
IaCによる解決
そこで必要となるのが、IaCの考え方です。 旧来なら、シェルスクリプトなどでOSの状態を変更したり、設定ファイルを書き換えたりといった手続き的な処理の自動化も、広義のIaCと捉えることもできました。すなわち、「サーバ構築手順書」をそのままプログラム化するような考え方です。
また、主要なクラウドサービスでは、インフラ操作のためのCLIコマンドが提供されています。
AWSであればaws
、Google Cloudではgcloud
、Microsoft Azureならaz
といったコマンドです。
WebUIの代わりにこれらのCLIを利用することで、効率や記録の問題はある程度解決できるでしょう。
しかし、CLIコマンドによる構築は手続き的です。このため、見通しの悪さやレビューのしにくさの改善に対しては、あまり寄与しません。
現代の理想的なIaCとしては、インフラの「状態」を宣言的に記述することが考えられます。ツールによって、記述されたコードの状態と等しくなるように、インフラを自動設定するのです。
インフラの信頼性に寄与するIaC
さて、IaCというと「自動化による効率改善」というイメージが強いかもしれません。もちろんこれは大きなメリットであり、「開発」「テスト」「本番」など、複数環境の構築も容易になります。 しかし、筆者らがそれ以上に大きなメリットと捉えているのが、次に挙げるような信頼性向上への寄与です。
- レビューしやすくなる
- ツールによるチェックができる
- 変更管理ができる
- 再現性・自動デプロイに繋げられる
- 再利用化で知見が共有できる
プルリクエストによる相互レビュー
IaC化されたコードはGitHubなどのソースコードリポジトリで管理でき、アプリケーションコードと同じようにプルリクエストによるレビューが可能になります。これによって、構築や変更前にチェックができ、品質向上につながります。
ツールによる自動チェック
さらに、チェックツールを導入することでレビュー自体を半自動化できます。 たとえば、筆者らがIaCツールとして採用しているTerraformでは、tflintやtfsecといったチェックツールがあります。tflintでは、設定内容の妥当性、命名規則、ベストプラクティスに従っているかなど、プログラミング言語の静的チェックツールと同じようなチェックが可能です。tfsecでは、セキュリティ上問題となる設定がないかの確認ができます。 このような自動チェックを事前に実施することで、人間によるレビューはより本質的な点に絞ることができ、作業効率と精度の両面に寄与できます。
変更管理が可能
コードリポジトリで変更管理が可能になることで、構成変更に起因する障害が発生したとき、ロールバックもしやすくなります。 クラウドネイティブなシステムでは、アプリケーションとインフラ構成が密接に絡みます。たとえば、アプリケーションがパブリッククラウドの提供するキューイングサービス(AWSならSQS、Google CloudならCloud Pub/Subなど)を利用するとします。その場合、アプリケーションコードと、キューを構成するインフラコードのバージョンには整合性が求められるでしょう。 インフラがIaC化されてリポジトリ上で管理されていれば、アプリケーションとインフラの整合性を取るのが容易になります。
再現性
IaC化の大きなメリットは再現性が得られることですが、これには信頼性向上という側面もあります。たとえば、誤って環境を壊してしまっても元に戻すことができるので、ダウンタイムを最小化できます。 筆者らは過去に、あるプロダクトの開発環境において不注意でKubernetes(K8s)クラスタを削除してしまったことがありました。幸いにもこれらはIaC化されていたため、比較的短時間で復旧できました。 また、ディザスタリカバリ観点でも事業の継続性に寄与します。
再利用化による知見の共有
手作業によるインフラ構築は、ノウハウが担当者の中に閉じがちで暗黙知となりやすいです。 TerraformやAnsibleなど、たいていの構成管理ツールでは、コードをモジュール化して再利用する機能が提供されています。ノウハウをモジュール化して再利用することで、インフラの品質向上が望めます。
たとえば、TerraformのGoogle Cloud向けモジュールでは、管理者がシステムにアクセスするための「踏み台サーバ(Bastion server)」を構築するためのモジュールbastion-hostが公開されています。 このモジュールでは、ネットワークや認証のしくみもセットで構築してくれるので、踏み台サーバ構築のノウハウが再利用できる状態と言えます。
IaCのデメリット
一方で、次のような点はIaCのデメリットと捉えられることもあります。
- 初期構築が大変
- 変更コストがかかる
初期構築の大変さ
初期構築について、とくに慣れない段階では時間がかかります。筆者らも開発チームからIaCについて相談を受ける際は、開発初期段階では無理にIaC化しなくても良いとアドバイスしています。 IaC化を進めるには、手作業で構築してから、ツールでコード生成するのが1つの方法です。 たとえばTerraformの場合、主要なパブリッククラウドに対応しているTerraformerや、Google Cloudであれば、gcloud resource-configコマンドといった選択肢があります。 Terrformerは、利用するクラウドサービスに対応するTerraformプロバイダの手動インストールが必要という手間はありますが、さまざまなクラウドからコード生成できる点が魅力的です。 gcloud resource-configは、普段からgcloudコマンドを利用していれば、追加インストールが不要なので、手軽に利用できます。 筆者も現段階では両者を軽く試用した程度ですが、Terraformerの方がTerraformコードをきちんとファイルに分けて出力してくれるため、見通しが良さそうと感じています。 いずれにせよ、自動生成されたコードは土台と割り切ってしまい、それらを参考にしながらコードをきれいにしていく必要はあるでしょう。 また、組織として知見が溜まってくれば、ベースとなるコードを共有することで、イニシャルコストを下げられそうです。筆者らもこの点に関してはまだ手探りの段階です。
変更コスト
IaC化されたインフラの変更コストは、信頼性向上との裏返しではあります。インフラのコードをGitリポジトリで管理し、プルリクエストをレビューしてから反映するというプロセスは、WebUIから変更するのに比べると手間がかかる印象を持たれるでしょう。 実際のところ、次回以降で説明するようにCI/CDパイプラインを構築することで多くの作業は自動化でき、それほどの手間はかからなくなります。
しかし、とくに慣れないうちは心理的ハードルが高くなるのは否めません。たとえば「パフォーマンス調整のためにK8sクラスタ上でノードプールのインスタンス数を変更する」といったケースでも、プルリクエストとレビューが必要になります。 一方で、プルリクエストに変更理由を記載して妥当性をレビューできるのは、インフラの信頼性を保つうえで大きなメリットです。このため、信頼性とアジリティ(機敏性)のバランスが重要です。 キャディのプロダクトにおけるインフラについては、IaCによる管理を基本としつつも、「検証のための一時的な変更などは、開発環境に限ってWebUIからの変更をOKとする」というルールを導入しました。このようにして、信頼性と変更コストのバランスをとっています。
Terraformを採用する理由
筆者らがIaCツールとしてTerraformを採用する大きな理由は、次の2点です。
クラウドサービス上のリソースが扱える
IaCツールというとAnsibleが有名であり、本誌でもたびたび取り上げられています。しかし、AnsibleとTerraformでは、図2のように位置付けが少し異なります。
▼図2 AnsibleとTerraformの想定ターゲット
Ansibleの想定している主な役割はサーバ設定であり、OSよりも上のレイヤがターゲットです。一方Terraformは、クラウド上のリソース作成や設定変更を主な役割と想定しており、Ansibleがカバーするレイヤをメインターゲットとしていません。
AnsibleとTerraformはそれぞれ基本的なしくみも異なります。Ansibleは設定対象のサーバ(マネージドノードと呼ばれる)にSSHでログインして「モジュール」と呼ばれる小さなプログラムを送り込み、そのモジュールがマネージドノードを設定します(図3上)。 一方、TerraformではターゲットとなるクラウドサービスのWeb APIを呼び出すことで、リソース作成や変更を実現します(図3下)。
▼図3 Ansible(上)とTerraform(下)のしくみの違い
このような設計思想の違いはありますが、互いの領域が重なっていないわけではありません。 Ansibleでは、AWSやGoogle Cloudのリソースを構築するためのモジュールが提供されており、クラウドサービスのリソース管理もできます。 一方、TerraformでもProvisionersという機能でAnsibleのようなサーバの内部の設定変更も実現できます。 ただし、Terraformに関してはProvisioners のドキュメントによると、あくまでも「最後の手段」と位置付けられていることから、主要な使い方でないことがわかります。
なお、各クラウドサービスへの対応状況については注意が必要です。 筆者らが利用しているGoogle Cloudについては、TerraformではGoogleとHashiCorpのTerraformチームがメンテナンスする公式のProvider が提供されており、頻繁にメンテナンスされています。 一方AnsibleのGoogle Cloud向けコレクションの開発状況は、GitHubの履歴を見る限りそれほど開発が活発ではなかったものの、2022年12月からリリース頻度が早くなっており、今後に注目です。
なお、Ansibleにおける「コレクション」とは、一連の機能に関するモジュールの集まりのことです。
幅広いクラウドサービスへの対応
IaCツールを選定する際、まずクラウドサービス固有のツールが選択肢に挙がるでしょう。 「AWS CloudFormation」 や、「Google Cloud Deployment Manager」などです。 これらのツールは、当該クラウドサービスとの高い親和性が利点である一方、幅広いクラウドサービスには対応できないという弱点があります。
Terraformには「Provider」というプラグイン機構があり、クラウドサービスの対応はそれぞれの Provider によって実現されています。 Providerは、Terraform Registryで公開されており、コード内で宣言するだけでTerraformが自動的にProviderをダウンロードしてくれます。このようなしくみから、Providerがあれば、さまざまなクラウドサービスに対応できます。 もちろん、必要に応じてProviderの自作もできます。
また、AWS、Google Cloud、Azureなど主要なパブリッククラウドについては、HashiCorp社によるオフィシャルProviderが提供されているため、安心して活用できます。Google Cloudでは、Terraformの利用に関する公式ドキュメントも充実しています。
キャディのインフラでは、Google Cloudを中心としつつも、さまざまなXaaSを組み合わせているため、インフラをコード化するツールとしてTerraformが最適といえるのです。
[Column] Ansibleと比較してTerraformが良い点
筆者自身、キャディへ入社する前はAnsibleを長く利用していました。いささか主観的ですが、双方を使った経験から、Ansibleと比べてTerraformのほうが良いと感じる点を紹介します。なお、筆者が最後にAnsibleを触ったのは2021年秋ごろなので、その後改善されている点があればご了承ください。
純粋に宣言的な書き方ができる
Ansibleでは手続き的な書き方ができてしまうので、YAMLでプログラミングをするようなイメージになりがちです。また、コードを書くときにも冪等性を意識した書き方をする必要があり、習熟に時間がかかりました。コードを読み解くのにも慣れが必要です。 一方、Terraformのコードは純粋に宣言的なので、「あるべき姿を記述する」ことに集中でき、コードの可読性も高いです。冪等性は、クラウドサービスまたは TerraformのProvider内部で担保されるためです。
エラーメッセージが読みやすい
Ansibleのエラーは、メッセージを含むJSONが改行なしに出力されるため、読み解くのが非常に困難です。Terraform のエラーは整形されてわかりやすく表示されます(図A)。これはTerraformに限らず、HashiCorp製品全般に言えます。
▼図A Terraformのエラー表示例
環境の影響を受けない
設計上仕方のないことですが、Ansible自体がPythonで動作するため、ホストで実行されるPythonの影響を受けます。 古いホストでAnsibleを動かす際、Ansibleが要求するバージョンのPythonがインストールされていないことがありました。 このため、venv1でAnsible用のPython環境を隔離するといった対応が必要になりました。 TerraformはGo言語で実装されており、ランタイムが不要なバイナリとして提供されるため、このような影響を受けることがありません。
特性の差に対する理解が必要
一方で、TerraformにはState2というAnsibleには無い概念があるため、その考え方を理解しないと混乱してしまいます。Stateとは管理対象リソースの状態を保存したファイルのことで、TerraformはStateを中心にコードと実環境の差分をチェックする考え方です。AnsibleはPlaybookの記述内容を正とすることで、冪等性を実現します。 また前述のように、TerraformはOSよりも上のレイヤに対しては使えません。たとえば、クラウド上に構築した仮想マシンの内部を設定したいといった場合は、依然としてAnsibleが有力な選択肢です。このようなケースでクラウド自体のリソース管理もしたい場合は、すべてをAnsibleで統一するというのも良いかもしれません。
キャディではすべてのアプリケーションをDockerコンテナ化しており、仮想マシンを使用するケースは踏み台サーバなど限られています。 AnsibleがカバーしているOSよりも上のレイヤはDockerfile として記述できるため、すべてをTerraformでカバーできています。
まとめ
今回はまず、信頼性の高いインフラを運用する基盤となるIaCの考え方を紹介しました。そして、Google Cloudを中心としたクラウドネイティブなインフラを構築している弊社にとって、IaCツールとしてTerraformが最適であると判断した理由を解説しました。 今後はGitHub ActionsとTerraformの組合せでインフラのCI/CD実践例を紹介する予定です。次回はまず、Terraformに触れたことがない方向けにTerraformの基本を解説します。お楽しみに。