我々はどこから来たのか?
我々はどこから来たのか?サーバーサイドの管理とプロビジョニング・サーバーの進化を見てみよう。最初は、もちろんハードウェアがあった。それをラックに積み、データセンターに移動させなければいけなかった。しかし仮想化によって、以前より非常に簡単になった。サーバーを手に入れれば、それはステートフルなマシンだからだ。依存関係や、その上で実行の必要があるソフトウェアである場合、どのようにインストールするのか?最初のステップは、手動でインストールする必要があった。マシンにSSHでログインする。ソフトウェアは時間とともにアップデートする必要があるので、手動でパッチを当てる。最近はポインティングやクリックが多い。クラウド・プロバイダーにはコンソールがあるし、オンプレミスでもvSphereの管理コンソールがある。これらすべてが問題を抱えている。私たちが歩んできた過程に、再現性がなかった。インフラストラクチャ・アズ・コードを設定し、パッケージをインストールする。同じように見える2台目のサーバーが必要になったら?何かに失敗したらどうする?コマンドラインやUIを使って手作業で行う方法では、再現性が課題だった。また、拡張性もない。サーバーを1台から2台にすると言った。しかし1台から10台、100台、1000台と増やしていったらどうだろう?1つの環境から複数の環境へ移行する場合はどうするのか?間違えることは簡単だ。手作業でコマンドを実行している時に、誤操作をした場合、今度はその失敗から手作業でリカバリしなければならない状況に陥る。こうした失敗はよく耳にする。信頼性の問題や停止、セキュリティの問題につながる。もっといい方法があるはずだ。少し伏線を張っておくと、インフラストラクチャ・アズ・コードとはそういうものだ。
懐かしい話になるが、1970年代を振り返ってみると、当時はソフトウェア・パッケージを構築していた。どうやってソフトウェア・パッケージを構築していたのか?繰り返しになるが、手動でコマンドラインを実行していた。ソフトウェアをコンパイルし、パッケージ化し、リンクさせる。そこでMakeというツールを作った。Make は、インフラストラクチャ・アズ・コードの観点から、私たちがどこへ向かうのかについての興味深いインスピレーションを与えた。Makeは、ある一連の入力を受け取り、コマンドを実行し、その結果、ソースコードが入力され、コンパイルされたプログラムが出力される。Makeの仕組みは、ドメイン固有の言語を使っているという点で、実に興味深い。インフラストラクチャ・アズ・コードは、多くのツールはドメイン固有の言語を持っている。どちらの場合でも、Makeはこれらすべてのターゲット間の依存関係を理解し、ビルドプロセスの実行をオーケストレーションする必要がある。
このようなコンセプトの多くは、インフラストラクチャーの管理方法やインフラストラクチャーの自動化方法へのアプローチに表れている。この進化の中で、私が最初に挙げるツールはCFEngineだ。CFEngineは、依存関係を記述できるMakeと同じようなアプローチをとった最初のツールである。インフラストラクチャーを自動化するプロセスは、機械可読テキストによる記述によってエンコードされる。インフラストラクチャ・アズ・コードによって、手作業のプロセスを自動化が可能だ。サーバーにSSHで入り、パッケージをアップデートするために手動でコマンドを実行するようなことはしない。ソフトウェアのニーズが進化しても、それを繰り返し、再利用し、拡張できるようにするためである。また、スケーラビリティや要件が変化しても、失敗する可能性のあるコマンドの発行やそのリカバリーを心配し手動で行う必要もなく、インフラストラクチャ・アズ・コードを簡単にスケールできる。
インフラストラクチャーをソフトウェアのように扱えるようになったことで、複雑なインフラストラクチャーを手なずけるための多くのプラクティスやプロセスが解放された。手作業の手間を省き、コラボレーション能力を高め、より少ない労力で効率的に成果を上げる。これを見てほしい。これは、インフラストラクチャ・アズ・コードの系統図だ。Makeが登場したのは70年代。CFEngineは90年代初頭だ。しかし、CFEngine 1、2、3を見てみると、メジャーリビジョンごとにかなりの変更が加えられている。それから、このロードマップに載っている他のツールもすべて見ていくと、この中に興味深い分岐がいくつかあるので、それらを辿っていく。
命令型と宣言型
まず基本的なコンセプトを紹介し、それから具体的な使い方を説明しよう。最初の大きなコンセプトは、命令型と宣言型だ。どういう意味だろうか?わかりやすいように例を挙げよう。80番ポートをリッスンするPython Webサーバーを作り、インターネット経由での利用が目的だとしよう。命令型では、Python Webサーバーを作る方法を説明する。以下はその手順だ。新しいAWS EC2インスタンスを作成する。そのインスタンスにSSHでログインし、Pythonをインストール、GitリポジトリなどのWebサーバーのファイルをコピーする。PythonのWebサーバープロセスを開始、ポート80のファイアウォールルールを追加して、トラフィックを許可する。だが、これは先程説明した手作業のステップによく似ている。もしも、ステップ2とステップ3の間で不具合が発生し、手動でリカバリーする必要があったらどうするか?宣言型のアプローチとは、文字通りOracleに、80番ポートをリッスンするPython Webサーバーを作ってくれと言うものだ。そのOracle、つまり我々が使っているインフラストラクチャ・アズ・コードエンジン、インフラストラクチャ・アズ・コードツールが、これらのステップの実行方法を決定する。私たちは、実際にOracleがプロビジョニングするために煩わしい業務から切り離し、正確なステップを理解し処理できるようにした。もし失敗したらどうするのか?どのように具体的なステップに落とし込んでいくのか。これから紹介するインフラストラクチャ・アズ・コードツールのほとんどに、宣言型アプローチという考え方がある。これはツールによって大きく異なる。
デモンストレーション
もう少し具体的に説明しよう。これから実際にインフラストラクチャ・アズ・コードツールで、ポート80をリッスンしている特定のPython Webサーバーのデモをお見せする。私が働いているPulumiを例として使うが、これは一般的なコンセプトで、どのツールも凌駕している。ここではエディタだけを使用する。インフラストラクチャ・アズ・コードの特徴のひとつは、多くのツールが汎用的な言語を使っていることだ。VS Codeのようなツールも使えるし、エディタも使用できる。パッケージ・マネージャーや、使い慣れたすべての言語を使用可能だ。YAMLを使うこともあれば、ドメイン固有の言語を使う場合もある。今回はPulumiを使っている。どんな言語でも構わないが、Pythonを使用する。ここで何が起こっているかというと、オブジェクトを設定しているだけだ。繰り返しになるが、これはインフラストラクチャ・アズ・コードの望ましい状態を宣言しているのだ。望ましい状態というコンセプトは重要で、この状態を辿っていくと、80番ポートのインターネットトラフィックを許可する必要がある、ということだ。AWSでそれを行うには、IngressArgsを持つセキュリティ・グループを使い、ポート80のインターネット経由でトラフィックを許可する。EC2では、LinuxのAmazon Machine Image(AMI)を使う必要がある。これは事実上、サーバーを動作させるためのイメージだ。そしてサーバーを設定し、先ほど調べたAMIを使うことを宣言し、作成したセキュリティ・グループに入れて、Python Webサーバーを実行する。そして最後に、自動的に割り当てられたアドレスを吐き出す。
これは状態の宣言していて、Webサーバーをどうやって作るかについては正確に話していない。宣言型エンジンへ委ねられている点に注意してほしい。Pulumiでは、私はただ、"pulumi up"と述べるだけだ。たいていのインフラストラクチャ・アズ・コードツールは、実際にそれを実行する前に、何が起こるかのプランを示してくれる。ここでは、インスタンスにセキュリティ・グループを作成すると表示されている。詳細を見たければ、確認もできる。インスタンス・タイプやその他の設定が表示されるので、この場合は「はい」と答える。これで前に進み、順調に進行する。このステップで、最初に何が起こるか示したことに注目してほしい。クラウド・プロバイダーに対してスクリプトを書いたり、クラウド・プロバイダーの提供するSDKを使ったりもできるが、スクリプトを実行する前に何が行われるのか、失敗したらどうなるのかを知る術はない、という問題が残るからだ。インフラストラクチャ・アズ・コードツールの利点は、そうした問題から解放される。したがって、デプロイ実行による意図しない影響が発生しないことを、常に確認できる。インフラストラクチャ・アズ・コードは、スクリプトが何をしようとしているのかを事前に示してくれるのだ。もしここで処理を失敗しても、中断したところからすぐに再開できる。ここでサーバーを作成する。Amazonでサーバーを作成するには少し時間がかかる。そして自動生成されたIDが表示される。素晴らしいのは、これを2回目にデプロイする場合だ。ここはテスト環境なので、本番環境にデプロイしたい。これはとても簡単なことだ。本番環境が東海岸と西海岸、あるいはEUの本番環境で、それぞれ設定が異なるかもしれないがそれが些細なことだ。共通のコードベースから始めて、それを繰り返しスケールさせる。繰り返しになるが、セカンドサーバーやサードサーバーがほしい場合、このデモではゼロから何かを作っていたが、インフラストラクチャ・アズ・コードツールは、基本的に現在の状態と将来の望ましい状態を差分することで、既存の環境を時間をかけて進化させることが可能だ。このHTTPアドレスをcurlしてみると、Hello, Worldと表示される。このままサーバーを放置しておくとコストがかかるので、破棄する。
表現言語と評価エンジン
インフラストラクチャ・アズ・コードの基本的なコンセプトを見ただろう。私たちはWebサーバーを宣言することでプロビジョニングした。宣言型と命令型の2つの重要なコンセプトがあると思う。もうひとつの重要なコンセプトは、表現言語と評価エンジンの違いだ。先ほどの例は宣言的な例だと主張したが、私はPythonという命令型言語を使って望ましい状態を表現していたことがわかるだろう。多くのインフラストラクチャ・アズ・コードツールは、さまざまな選択肢を提供している。YAMLでもいいし、JSONでもいい。HashiCorpのConfiguration LanguageやPuppetスクリプトのようなドメイン固有の言語も使える。ChefのRubyやPulumiのJavaScript、Python、Goのような汎用言語を使うこともできる。ここでの重要なコンセプトは、1つの言語で望ましい状態を表現し、評価エンジンが実際にデプロイ活動をするということだ。デプロイ・エンジンと評価エンジンは宣言的であり、ゴールとなる状態を設定しておけば、処理が失敗しても、中断したところから再開できる。そう、この表現言語では、命令構文を使えることがメリットになる。Pythonでは、さきほどの例をクラスに入れて、パッケージ化したWebサーバー・クラスを作ることができ、forループを実装できる。もし3つのサーバーを作りたかったら、for i in range, (0,3), provision.とすれば良い。とはいえ、結局のところ、評価エンジンは宣言的なものである、という事実から目をそらすことはできない。インフラストラクチャ・アズ・コードについて考えるときに、これらのコンセプトを頭の中で分けておくこと、利用可能な選択肢を理解し、自分のプロジェクトでどれを選択するべきかを考える上で非常に役立つ。
DAGの重要性について
もうひとつの重要なコンセプトは、Makeの例で示唆した通りだ。結局のところ、先ほどの例を見ると、WebサーバーのEC2インスタンスがあった。EC2インスタンスが実際に消費したのは、宣言したセキュリティ・グループだったことを覚えているだろうか。これはDAG(有向非巡回グラフ)を形成するもので、CFEngineに限らず多くのインフラストラクチャ・アズ・コードツールに浸透しているコンセプトだ。確か2つだったと思うが、3つだったかもしれない。これは、エンジンがオペレーションを並列化し、正しい順序でオペレーションの実行を可能にする重要なコンセプトだ。先ほどの話を思い出してほしい。セキュリティ・グループを作成した後に、Webサーバーを作る。Webサーバーはセキュリティ・グループに依存しているので、サーバーを作成する前に、セキュリティ・グループが完成し、プロビジョニングが完了するまで待たなければならないのは明らかだ。同様に、スタックを破棄した。つまり、逆の順序でやらなければならない。もし最初にセキュリティ・グループを破壊しようとすれば、Webサーバーがそのセキュリティ・グループに依存していることに気づくだろう。インフラストラクチャ・アズ・コードエンジンは、この宣言的な計画を作成するだけでなく、スタック内のすべてのインフラストラクチャー・コンポーネント間の参照も理解する必要がある。これはMakeにも似ている。Makeは物事を正しい順序で構築する必要があり、何かが前のビルドステップの出力を消費する場合、正しい順序でそれらを実行しなければならない。DAGは非常に重要だ。ツールを使うだけなら内部的な実装の詳細だが、インフラストラクチャ・アズ・コードのこの側面を理解するのにも間違いなく役立つ。
望ましい状態と収束
この「望ましい状態」という概念については何度も触れてきた。多くのインフラストラクチャ・アズ・コードツールに共通するコンセプトでもあり、事実上、インフラストラクチャ・アズ・コードエンジンは、最終的にあなたの望む状態に収束しなければならない。Webサーバーのセキュリティ・グループを考えてみよう。さて、インフラストラクチャ・アズ・コードツールの仕事は、現在地はどこなのかを把握することだ。空の環境からスタートするのか?部分的に構築された環境からスタートしているのか?完全に構築された環境で、WebサーバーサイズをT2マイクロからT3ラージにするといった、わずかな環境の変更なのか?それなら、EC2 VMのプロパティを1つ更新するだけでいい。この望ましい状態をシステムが知っていて、システムがそれに向かって収束できるという考え方は、インフラストラクチャ・アズ・コードだけでなく、Kubernetesのような最新のテクノロジーにとっても重要だ。Kubernetesのコンフィギュレーションを宣言する場合、APIサーバーとコントロールプレーンは事実上同じことをしていることになる。これは重要なコンセプトだ。インフラストラクチャ・アズ・コードツールが障害に直面しても再開できる理由もここにある。望ましい状態がわかっているため、Oracleは常に収束を試みることができる。途中で何かが失敗しても、中断したところから再開し、望ましい状態を達成しようとし続けることができる。これも、インフラストラクチャ・アズ・コードツールで遭遇する重要なコンセプトだ。
2000年代:データセンターの近代化を開始
では、具体的なツールについて話をしよう。2000年代にデータセンターの近代化から始まった、コンピューティングとクラウド・コンピューティングの進化を見ていくことにしよう。そこでは、ラックやスタック、物理サーバー、データセンターの手動設定から、ソフトウェアで管理されたVMやVMwareによる自動化へと、全面的に移行した。すごいのは、KVMやスイッチで実際に処理を行うには、物理的にサーバーの近くにいる必要があり、TelnetやSSHでアクセスする必要があったのが、今では実際にサーバーを管理するソフトウェア・コントロール・プレーンがある。これは極めて重要なイノベーションで、これによりインフラストラクチャ・アズ・コードは効果的にソフトウェア化され、プログラマブルになった。例えばvSphereにはソフトウェア定義のコントロール・プレーンがあった。AWSを見ると、とても信じられないことだが、REST API を curlで実行して、どこかのデータセンターのどこかのサーバーを取得できる。Pulumiでは、クラウドをプログラムしよう、と言っている。これは、私たちがますます強力なインフラストラクチャ・アズ・コードやその他のツールで機能を構築する過程で起こった重要なイノベーションだった。
コンフィギュレーション
とても大切ななコンセプトについて話そう。コンフィギュレーション・ベースのインフラストラクチャ・アズ・コードとプロビジョニング・ベースのインフラストラクチャ・アズ・コードについてだ。この2つは、IaCツールの主要な異なるカテゴリーだ。コンフィギュレーションでは、サーバー、仮想マシン、物理サーバーを作成したら、そこにソフトウェアをインストールしてコンフィギュレーションする必要がある。ファイルをコピーして編集し、コマンドやプロセスを実行したら、デーモンやサービスを起動する必要がある。コンフィギュレーション・ベースの "インフラストラクチャ・アズ・コード"は、ステートフルな世界、つまり仮想マシンの世界で生まれた。初期の時代には、主に仮想マシンを使ってインフラストラクチャ・アズ・コード化が行われていた。CFEngineはこれらに先行していたが、Chef、Puppet、SaltStack、Ansibleを見ると、これらのツールは、仮想マシンにソフトウェアをインストール・アップグレードし、パッチを当てる方法について書かれていた。それが当初の一番の目標であり、現在はさらに進化している。当時は、いかに自動化するか、いかに再現性を高めるかが大きな課題だった。
Puppetは、CFEngineの勢いを受け継いだ初期のアプローチだが、少し異なる手法を採用している。Puppetはドメイン固有の言語(DSL, domain specific language)を提供する。非常にRubyに似ているが、DSLであり、このケースでは基本的に、達成したいファイアウォールのルールを宣言している。繰り返しになるが、これがいかに宣言的であるかを見てほしい。これはBashスクリプトのようにコマンドを実行するのではなく、ICMPプロトコルのファイアウォール、acceptに設定されていることを確認する、といった具合だ。もちろん、これはSSHコマンドやマシン上の設定ファイルの編集に変換され、より宣言的でレベルの高い、反復可能な抽象化レベルで操作できる。
Chefはこれをベースにして、少し違った方向に物事を進める。ファイアウォールルールを宣言していることに変わりはないが、Ruby言語の完全な表現力を持っている。繰り返しになるが、表現力豊かな言語とインフラストラクチャ・アズ・コードエンジンが分離されているため、Rubyの豊富な機能をすべて利用できるとはいえ、インフラストラクチャ・アズ・コードのベルトとサスペンダーを手に入れたに過ぎない。おかげでChefは、Chefの共有と再利用の仕組みであるクックブックのような、多くの素晴らしい機能を可能にし、解放してくれた。プログラミング言語では、パッケージやパターンを共有・再利用することに慣れているので、あちこちにスクリプトをコピー&ペーストする必要はない。共通のコンフィギュレーションを一度定義して、それを何度も使える。これは非常に強力な機能だ。それに加えて、言語の表現力、テストや実際にコードをテストする能力も手に入れた。これは、本格的な汎用言語を使いながら、それをコードエンジンとしての強固なインフラストラクチャ・アズ・コードと融合させた最初の例だ。ここからは、他の多くのツールとは少し異なる方向に進むが、このパターンが繰り返されることがわかるだろう。
Ansibleは異なる角度からこれにアプローチし、DSLの代わりに、汎用言語の代わりに、設定の望ましい状態を効果的に表現するためのマークアップ言語であるYAMLを実際に使用した。この分野におけるAnsibleの大きなイノベーションの1つとして、サーバーレス設計を採用したことだ。今日我々がサーバーレスやLambdasについて語るような方法ではないが、エージェントを必要としないエージェントレスだ。事実上、Pythonがインストールされているマシンであれば、どんなマシンでも管理できる。そのため、エージェントを実行したり、管理しようとしているマシンで重いプロセスを実行したり心配はない。そのため、組織内でAnsibleを大規模に採用することは、非常にシームレスになった。ほとんどすべてのマシンにすでにPythonが入っている。Pythonがインストールされていなくても、議論の余地はなく、簡単にインストールできる。ここでも、我々はDSL、汎用言語、YAMLを見てきたが、基本的に、インフラストラクチャ・アズ・コードで見られる表現言語への3つのアプローチについて理解いただけただろうか。
DSLとGPとMLの比較
DSLの利点は、目的に合わせて構築され、簡単に始められる。DSLは、基本的に状況に応じた使用が簡単にできるように合理化を目的とした言語設計の余裕がある。しかし、DSLには短所もある。DSLは本当に、その1つのためだけに使用し、他の用途には応用できない。長所であると同時に短所でもある。Pythonと比較した時、Pythonを学べば、他のツールがたくさんあり様々な機能が使えるようになる。しかし、DSLはシンプルなことが多いので、学習コストは低いかもしれない。しかし大学を卒業したばかりで、Pythonを知っていても、特定のDSLを知る人は少ないだろう。多くのDSLは汎用的な言語(GP, general-purpose language)に成長する運命にある。ただ、最初からそのように設計されているわけではないので、おかしなforループや意図的に設計されたものとは異なるようなif文が出てくることが多い。汎用言語は、やはりマルチユースだ。この文脈ではインフラストラクチャ・アズ・コード、つまり宣言型エンジンが核となり、表現力を与えてくれる。しかしそれは自らできる範囲を限定してしまうといった課題がある。
短所は、汎用言語を知らなければ、始めたばかりのときに学ぶとより複雑になることだ。率直に言って、それをインフラストラクチャ・アズ・コードと結びつけるのは容易ではない。Chefはそれを可能にし、Pulumiは実現できた。オブジェクトモデルを絞り込んで、両方の長所を生かせるようなシステムを最初から設計する必要があるからだ。最後に、マークアップ言語(ML, markup language)は極めてシンプルだ。JSONやYAMLは事実上、普遍的なデータ・フォーマットであり、最近のコンピューター・ソフトウェアではどこでも使われている。それはまた、宣言的なアプローチともうまく調和する。データだから宣言する。そこに計算はない。ただし、マークアップ言語には表現力がない。forループが必要だったり、ある程度の抽象化やテンプレート化が必要だったり、Webサーバーがセキュリティ・グループを参照する必要があることを宣言する場合、それを可能にするための構文を考えなければならない。実際、HelmやKubernetesエコシステムのように、YAMLを生成するためにGoテンプレートを追加しなければならないシステムもある。なぜなら、多くの複雑な状況では、複雑さの壁にぶつかり、多くのコピー&ペーストや、このシンプルなはずのフォーマットの周りに構築された多くのカスタムツールにつながるからだ。このようなことを大規模に管理するための優れたツールはない。これらはすべて長所であり短所でもある。私はただ、全体像を知ってもらいたかっただけだ。繰り返しになるが、適切な問題領域に対する適切なソリューションが、どちらを選ぶべきかについての私の指針である。
DevOps
DevOpsとは、ソフトウェア開発と運用を組み合わせた一連の手法のことである。開発者がコードを書き運用チームに投げる。運用チームはそれからコードを実行する、つまりインフラストラクチャ・アズ・コードを最前線に据えてソフトウェア開発のライフサイクルを再構築し、開発者、運用、インフラストラクチャ・アズ・コードという立場の両者が協力し合えるようにする考え方だ。これにより、四半期ごとや20年前のようなやり方ではなく、毎日・毎時間、絶え間なく出荷する継続的ソフトウェア・デリバリーを実現できる。私がここでDevOpsについて触れたのは、インフラストラクチャ・アズ・コードがまずDevOpsを促進するために不可欠なテクノロジーだったからだ。そしてDevOpsは、インフラストラクチャ・アズ・コードを主流にする助けにもなった。
家畜とペット
ここで旅を続けよう。私は、DevOpsはクラウドよりも先行していたと考えている。私はこのプレスリリースが大好きだ。AWSは2006年にS3で最初のサービスを開始した。これは本当にゲームを完全に変えた。インフラはAPIを呼び出すだけで利用できる。我々にはマネージド・サービスがある。事実上、インフラの運用タスクや管理の多くを、AWSのコントロールプレーン、つまりAWSのサービス自体にオフロードできる。この変曲点を迎えるにあたり、もう1つ触れておきたい概念がある。それは、構成ベースのインフラストラクチャ・アズ・コードからプロビジョニングベースのインフラストラクチャ・アズ・コードにつながるものだ。これはひどい例えかもしれないが、よく知られている。VMの世界では、VMはペットのようなものだった。どのマシンも、特に物理的にラックに積み上げられたデータセンター・コンピュータの世界では、1台1台が特別な状態の存在だ。pussinboots.cern.chのような特定の名前を持っている。彼らは唯一無二の存在であり、愛情を持って育てられ、大切にされている。彼らに何か問題が起きたら、あなたはそれを健全な状態に戻そうとする。ハードウェアが故障したら、SIMカードを交換して新しいものをインストールする。ソフトウェアが壊れた場合や、セキュリティの脆弱性があれば、パッチを当てる。
一方、クラウドへの移行では、家畜という概念に向かう。つまり、たくさんのマシンがあり、それらはほとんど同じもの同士である。1台が故障したら、新しいマシンに入れ替える。それがハードウェアの故障であろうと、新しいマシンを買ってきて接続するだけだ。あるいは、頻繁にアップグレードが必要な場合は、パッチを当てに行ったり、ステートフルであることを気にしたりする代わりに、新しいマシンを作る。そしてロードバランサーのようなものを使って、トラフィックを古いものから新しいものにリダイレクトできる。このスライドは、2012年にこのコンセプトを最初に紹介したときのものだ。
プロビジョニング
これがプロビジョニングにつながる。ペットの代わりに家畜がいる世界では、仮想マシンにパッチを当てることを深く考える意味はない。コンテナやDocker、Kubernetesを使えば、新しいやり方が見えてくる。最新のソフトウェアが入ったイメージをベイクして、新しいバージョンを提供し、古いリファレンスをすべて新しいものにアップデートする必要がある。つまり、AからB、CからDへの状態遷移について考える必要がないということだ。もし途中で処理が失敗しても、「望ましい状態」宣言すればいい。つまり、望ましいコンフィギュレーションというコンセプトに戻り、インフラストラクチャ・アズ・コードツールは、今日いる場所から明日行きたい場所までの道のり見つけ出すことができる。
その結果、インフラストラクチャ・アズ・コードツールの中間の時代になった。AWS CloudFormation、Azure Resource Manager、Google Deployment Manager、そしてCloud-AgnosticなTerraformのようなクラウド固有のツールがある。CloudFormationはJSONやYAMLを使用できる。先ほど見たAnsibleの例とよく似ているが、ここではAWSのインフラストラクチャ・アズ・コードを宣言しているだけだ。これは基本的に、先ほどのデモで見たのと同じことをやっている。事実上、EC2インスタンスを立ち上げ、それが立ち上がるときにいくつかのコマンドを実行するだけだ。ただし、いくつかの点に注意する必要がある。FN::Base64の中で!Subが使用されていることだ。これは、マークアップ言語の制約のために、CloudFormationが埋め込まれたミニDSLを導入していることを意味する。ここでは、他のオブジェクトを参照したり、計算を実行したりするためにコマンドを実際に代替できる場所である。この場合、私たちはいくつかの文字列をBase64エンコードして置き換えている。
Azure Resource Managerも非常に似たコンセプトを持っている。YAMLの代わりにJSONで書かれているだけだ。しかしTerraformは、ドメイン固有の言語というアプローチを取っている。繰り返しになるが、Puppetがマークアップ言語の制限に縛られることなく、かつ本格的な言語をサポートする必要もない、という点で非常によく似ている。Terraformでは、DSLであるHashiCorp config languageで自分自身を表現できる。ここでは、var.instance_type、var.instance_keyを見てみよう。aws_security_group.sg.idというセキュリティグループを参照できる。プログラミングのための機能はあるが、よりシンプルな宣言型DSLだ。Terraformはマルチクラウドなので、AWS、Azure、Google Cloud、そしてvSphereのようなオンプレにも対応している。
コンテナの登場
これらはすべて2010年代初頭の話だ。その後、DockerとKubernetesが登場し、アプリケーション・アーキテクチャに対する考え方を大きく変え、リフト・アンド・シフトを超えることを促してくれた。それ以前の多くのテクノロジーは、VMをどのようにプロビジョニングし、どのように構成するかということに重点を置いていたと思う。Kubernetesとクラウド・ネイティブは、実際、宣言的インフラストラクチャ・アズ・コードというコンセプトとうまく調和している。なぜなら、Kubernetesのオブジェクト・モデルと物事の進め方は、最終的な一貫性、物事が失敗することを想定し、望ましい状態、疎結合の依存関係という観点で作業することに尽きるからだ。残念ながら、そこにはYAMLがたくさんあるが、Kubernetesの仕組みの核となるコンセプトは、すでに話したインフラストラクチャ・アズ・コードの核となるコンセプトと非常にうまく合致している。この時点で、汎用言語はどこに行ったのだろうか?我々はChefでそれを見た。つまり、forループやif文の機能を失い、関数クラスやモジュールのような抽象化機能を失った。エディタのサポートの多くが失われた。ほとんどのエディタは、CloudFormationのDSLによる参照方法を理解できないだろう。ステートメントを間違えても補完されないし、赤いスクイグルも表示されない。誰かが特定のプラグインを実装しない限り、TerraformのDSLのようなインタラクティブなドキュメントは得られない。そのため、コピー&ペーストが多くなり、車輪を作り直すことが多くなった。
現代では、2つほど復活しつつある。ひとつは、クラウド・ネイティブ・テクノロジーだ。一般的にクラウド・ネイティブと言われるKubernetesやHelmだけでなく、AWS LambdaやAzure Cosmos DBのようなマネージド・サービスを採用することで、クラウド・プロバイダー自身にサービス管理の負担を軽減している。また、汎用言語を使おうというルネッサンスも見られる。汎用言語と宣言型インフラストラクチャ・アズ・コードを結びつけよう。少数のサービスから多数のサービス、多数の環境へと複雑さを調整し、その複雑さを調整するためにソフトウェアが設計されたように使用できるようにしよう。これが、2017年に私が設立したPulumiの導入につながった。このアイデアは、JavaScript、TypeScript、Go、Python、C#、Java、そしてマークアップ言語のアプローチを好むならYAMLさえもサポートしている。ここでの考え方は、どのような表現言語を選んでも、依然として素晴らしい宣言型プロビジョニングベースのインフラストラクチャ・アズ・コードと、("ベルトとサスペンダー"のように、重ねて)プレビュー、およびその他のすべてを得られることだ。
まとめ - インフラストラクチャ・アズ・コードソリューションを選ぶ
もしあなたが今日、インフラストラクチャ・アズ・コードツールを選ぶなら、PulumiとAnsible、あるいはTerraformとChef、あるいはそれらの組み合わせを使うのが一般的だろう。ステートフルなワークロードやステートフルな仮想マシンを使っているのであれば、それらを設定し、パッチを当てなければならない。これらのツールは非常に活気があり、今日のエコシステムで広く使われている。最新のクラウドアーキテクチャに本格的に取り組むのであれば、プロビジョニングベースのインフラストラクチャ・アズ・コードを提供することは、基本的に当たり前のものだ。PulumiやTerraformなど、そのようなカテゴリーのものを選びたくなるだろう。単純なユースケースの場合は、DSLやマークアップ言語から始めても構わない。Pulumiを使えば、どちらを選んでもPulumiのエコシステムにとどまることができる。表現力が豊かで、馴染みがあり、非常に複雑な環境でも本当に機能し、開発者に馴染みのあるものを求めるのであれば、汎用言語が適している傾向がある。そしてもちろん、Kubernetesに限っては、クラウド・ネイティブのエコシステムで動作するものを選ぶ必要がある。いくつかの例を見た。Pulumiはそこで素晴らしい働きをする。Crossplaneもその一つだ。あるいは、Kubernetesのネイティブ・ツール自体が、非常にインフラストラクチャ・アズ・コード的だ。
未来
最後に、今後数年間にインフラストラクチャ・アズ・コードの領域で見られるであろう、私にとってエキサイティングないくつかのトレンドについて話して終わろうと思う。まず1つ目は、開発者のエンパワーメントだ。時計の針を20年前に戻すと、開発者はインフラストラクチャ・アズ・コードについてあまり考えていなかった。開発者は3層のアプリケーションを書き、2つの仮想マシンとデータベースを構築し、インフラチームはそれを簡単に取り込むことができた。我々は四半期ごとにアプリケーションを更新した。生活は順調だった。最近では、インフラストラクチャ・アズ・コードとアプリケーションコードの境界線が曖昧になってきている。AWS Lambdaは、アプリケーションのコンセプトなのかインフラストラクチャ・アズ・コードのコンセプトなのか?その中間だ。Dockerコンテナをビルドしてプライベート・コンテナ・レジストリに公開するのはどうだろう?それもその中間だ。開発者がビジネス価値を創造しているため、開発者を可能な限り迅速に動かし、可能な限りクラウドを利用させることが、革新的な企業、特にクラウドが出荷の迅速化に貢献している企業にとって重要である。最近では、すべてのソフトウェアがクラウド・ソフトウェアである。これはまったく理にかなっている。しかし、ネットワークのプロビジョニング方法、費用対効果の高いKubernetesクラスタ、信頼性、セキュリティなど、ガードレールを整備する必要がある。インフラストラクチャー・チームがベストプラクティスを定義し、ガードレールを設定しても、開発者はインフラストラクチャ・アズ・コードのサブセットをセルフサービスできる。
それが、プラットフォーム・チームという考え方を生み出した。この考え方はますます増えている。プラットフォーム・チームとは、運用・IT組織と開発者の間に位置するチームだ。多くの場合、インフラ・プラットフォーム・チームとしてのゴールは、ガードレールを整備した上で開発者が業務をこなせるようにサポートし、開発者とオペレーション・チームを繋ぐ役割を果たす。その多くは、ソフトウェア・エンジニアリング的な考え方をする。彼らはシステムを構築し、Kubernetesを導入してスケールアップを図っている。彼らは、他の場所でも使われる共通のインフラストラクチャ・アズ・コードコンポーネントや、ポリシーコンポーネントを定義している。このようなソフトウェア・エンジニアリングの考え方は、プラットフォーム・チーム内に多く見られる。このグループでは、ソフトウェアエンジニアリングの専門家とインフラストラクチャ・アズ・コードの専門家が一体となっていることがわかる。これは間違いなく、近代的な組織に見られるパターンだ。
複雑さを和らげることは、誰もが考え続けていることだ。その複雑さには、本質的なものと偶発的なものがあると考えている。今日のクラウドでは、偶発的な複雑さがたくさんある。もしあなたが開発者で、マイクロサービスを立ち上げ、コンテナ、Pub/Subトピック、キュー、サーバーレス機能を定義したい場合、その空間には偶発的な複雑さがたくさんある。Pulumiがそうであるように、インフラストラクチャ・アズ・コードを語るツールの台頭や、市場への新規参入がすでに見られる。この抽象化レベルは、時間の経過とともにますます高まっていくだろう。
今日においても、残念ながらセキュリティは後回しにされている。もう1つのトレンドは、最小権限の原則、コードとしてのポリシー、早期かつ定期的なスキャン、ソフトウェアにおける構造上の安全性の確保だと思う。インフラストラクチャ・アズ・コードは、それを保証する上で大きな役割を担っている。私たちはここで、ChefのInSpecという素晴らしい技術をたくさん見てきた。Chefがコードをテストする機能については触れた。その作業の多くがInSpecに現れ、デフォルトでセキュアであることを実際に保証できるようになった。OPA(オープン・ポリシー・エージェント)のようなものがあり、クラウド・ネイティブの領域でそれを実現している。HashiCorpのSentinelはTerraformツールを提供している。Pulumi CrossGuardは、Pulumiでポリシーをコードとして実行できる。Snykは、多くの異なるテクノロジーや真の分散アプリケーション・アーキテクチャにまたがり、より汎用的なソリューションだ。クラウドについてもっともエキサイティングなこととして、クラウドによって無限のスケール、無限のデータを持つソフトウェアを簡単に作れるようになったことだ。1台のコンピューターから、HTTPで接続された複数のコンピューター、マルチコア、そして今では、コンテナやサーバーレス、マネージド・サービスを活用した、本当の意味で分散型のアプリケーション・アーキテクチャへと移行しており、これは業界全体にとって非常に素晴らしい変化である。これにより、まったく新しい企業やビジネスモデル、さらには新しいアーキテクチャーを生み出す驚くべき技術が開花した。
そして最後に、人工知能について。GitHub Copilotが初めて登場したとき、Pulumiは単なるコードなので、私たちはそれを解読し、インフラストラクチャ・アズ・コードとして書き始めた。GitHub Copilotは、インフラストラクチャ・アズ・コードの一部を実際にコードとしてオーサリングし、完成させてくれた。最近OpenAPIからChatGPTが導入されたことで、文字通りECS Fargateマイクロサービスを作成するためにPulumiプログラムを書くことができるようになった。
まとめ
我々は豊かな歴史から、どのようにして歩んできたか見てきた。Makeから始まり、CFEngine、Puppet、Chefなど、我々は数多くの偉大なシステムのおかげで今がある。そして今日、オンプレミス、ハイブリッド、パブリック・クラウド、あるいはその中間であろうと、あらゆるクラウド・インフラにとって、インフラストラクチャ・アズ・コードが本当に当たり前になっている。コンテナ、クラウド・ネイティブ、そして開発者中心の環境によって、私たちは多くの素晴らしいツールから選択し、多くの素晴らしいイノベーションを手に入れた。そしてここから先は、単なるビルディング・ブロックからベスト・プラクティス、分散型アプリケーション・アーキテクチャへと移行し、セキュリティのようなものを最初から組み込んでいくことになる。幸運なことに、我々は本当にしっかりとした基盤の上からスタートしている、ということだ。
質疑応答
Andoh氏:宣言的な実装とは、誰かが行った非常に徹底した命令的な実装を再利用することだと定義できるだろうか?ある意味、キュレーションの必要性を示した特定のケースのために、上のレイヤーとして宣言型にするまでは、すべてが命令型だ。
Duffy氏:Pulumiのエンジン自体は宣言型だが、実際にはGoで書かれている。私はプログラミング言語を、命令型から宣言型までのスペクトラムで構成されていると考えている。Haskellのような言語を見てみると、Haskellは間違いなく宣言型だ。副作用が明示的にモデルに組み込まれているからだ。F#のような言語には宣言的な機能がたくさんあり、F#をPulumiやCDKと一緒に使うことができる。F#は宣言的なモデルでありながら、命令的な構造も持っているので、うまくフィットするんだ。私は、こういったものをスペクトラムのように考えている。それは良い考え方だと思う。プルーフ・コードやTLA+のような難解なソリューションもあるし、より研究的でアカデミックな言語もある。ほとんどの場合、私たちは命令型言語を使って宣言型に近づこうとしている。
Andoh氏:特にPulumiでは、インフラストラクチャ・アズ・コードに対するソリューションの一環として、宣言型エンジンを採用している。また、インフラストラクチャ・アズ・コードは宣言的であるべきだとも話していたと思う。評価エンジンに独自の言語構造を持ち込む過程で、エンジンやソリューションが宣言型ネイティブでないことにデメリットはあるのか?
Duffy氏:どちらにも言えると思う。基本的に宣言的でないものを、宣言的なものへマッピングする場合、インピーダンスのミスマッチが生じる。しかし同時に、多くのプログラマーは命令型言語に慣れている。forループや、共有されたミュータブルな状態が好きなのだ。私たちがとったアプローチは、エンドユーザーによって異なる嗜好に対応することだ。彼らはすでに知っている言語を持っているかもしれないが、それでもなお、それを宣言型コアにマッピングする方法を提供する。YAMLはかなり主流で人気がある。YAMLはどちらかというとデータフォーマットだ。Qを見ると、型チェックや、純粋に宣言的なデータモデルの中でforループのようなことができるなど、とてもエキサイティングな機能が追加されている。残念なのは、本当に人気のある宣言型プログラミング言語がないことだ。Haskellがもっとも近いかもしれないが、人々はHaskellをいじるのが好きだ。確かにフィンテック業界では、ウォールストリートの量的アナリストのように、狂ったようにHaskellを使っている者もいる。しかし、業界全般では少し控えめな傾向がある。
Andoh氏:インフラストラクチャ・アズ・コードの将来について言及していたが、またポリシーとPolicy of Codeの台頭について話しました。そしてインフラストラクチャ・アズ・コードがどのように大きな役割を果たすかについて、もう少し説明をいただけるだろうか?
Duffy氏:私はTrustworthy Computing(信頼できるコンピューティング)時代、マイクロソフトで働いていたときに、ウィンドウズでウイルスに関する問題がたくさんあった。私たちが発見したのは、静的解析やコード解析、環境解析によって多くのウイルスを検出できるということだった。そもそもそのような状況に陥らない方がずっといい。当時、私たちがやったことは、プログラミング言語により多くのことを取り入れ、より静的な型チェックを行うことだった。例えば、Rustは堅牢な型システムを持つことで、一般的なセキュリティ問題のクラス全体を排除している。Policy as Codeは静的な型チェック、そして時には動的な型チェックの領域にあると思う。「このデータベースはインターネットに公開するつもりはなかった。」「私のマイクロサービス全体が、やはりイントラネットからの経路になっている。」あるいは、「ファイルを暗号化するのを忘れていた。」あるいは、「MySQLの旧バージョンを使っている。」デプロイの時に発見することが理想的だが、最悪の場合は、すでにデプロイされてしまった後にこれらを発見する。我々は多くの素晴らしいツールを見てきた。HashiCorpにはSentinelがあり、ChefにはInSpecがあった。PulumiではPolicy as Codeを作成した。それはまだ後付けだからだ。自分のプログラムを作成する際にチェックされ、デフォルトでセキュアであるところまでは至っていない。デフォルトでセキュアであることは、私たち全員が望んでいることだと思う。チェインガードのような企業は、サプライチェーンのセキュリティーを構築によってセキュアにしようとしている。業界として、そのような方向への動きはさらに活発化すると思う。
Andoh氏:Pulumiでパイプラインを実装する場合、単体テストと統合テストは推奨されるのか?
Duffy氏:もちろんだ。汎用言語を使うメリットのひとつは、その言語を取り巻くエコシステム全体を手に入れられることだと思う。Pythonを使うなら、組み込みの単体テストが使える。どの言語にも単体テストのフレームワークには様々な種類がある。デプロイ前のテストもある。実際、よくあるのは、GitHubのプルリクエストを開き、そのインフラストラクチャ・アズ・コードのまったく新しいコピーを立上げ、、それに対して一連のテストを実行し、それを破棄することだ。その一連のテストがうまくいった場合のみ、プルリクエストを通す。これもテストの一種だ。そして実際にマージしたら、ポストデプロイメント・テストや、A/Bテスト、カナリア、ブルーグリーンデプロイメントのような様々なテクニックがある。私たちは、このような様々なことを行っている人々を目にする。
Andoh氏:あなたは、望ましい状態や状態を見ることについては言及したが、ドリフトという言葉については触れなかった。インフラストラクチャ・アズ・コード、そしてコンフィギュレーションの部分とインスタンス化の部分の両方について、ドリフトとそこで見られる複雑さ、そして解決策について伺うことは可能か?
Duffy氏:re:Inventから戻ってすぐ、多くの人と話をしたがそのほとんどは、インフラへの変更はインフラストラクチャ・アズ・コードを通じて行う必要があると言っていた。セキュリティ上の問題が発生した場合、誰かが中に入って手動で変更を加えるという素晴らしいクラスのシナリオがある。多くの人は、変更を加える能力をロックダウンしている。標準的なやり方である場合もあれば、誰かがログインして手動で変更する場合もある。手動で変更すると、インフラの実際の状態は、デプロイしたと思っていたものと一致しなくなる。PulumiやTerraformのようなインフラストラクチャ・アズ・コードツールは、それを検知して、デプロイしたと思っていたものが実は違っていた、と言うことを防げる。例えば、SSHでデバッグをするために22番ポートを開いたのに、その後閉じるのを忘れてしまったとする。ドリフト検知は、「22番ポートを開けたままにしておくつもりはなかった」と教えてくれる。その後、状態を調整できる。変更を適用し直すこともできるし、サーバーにタグを追加した場合は、それをインフラストラクチャ・アズ・コードに取り込むこともできる。それがドリフト検知だ。間違いなく、とても重要なことだ。まずはパイプラインから始め、その後でドリフト検知を行う。
講演録をもっと見る