Infrastructure as Code (IaC) とは、コンピューターが解読可能な定義ファイルを介してクラウドと IT のリソースを管理およびプロビジョニングするプロセスであり、最新の継続的インテグレーションのパイプラインの一部として利用されています。IaC は最新型の DevOps ツールを通じ、コンピューティング リソースをプロビジョニング、管理、破棄する機能を提供します。IaC を使用すると、目的のリソースを宣言型の静的なコードとして定義し、リソースのデプロイと動的な管理を、コードを介して実行できるようになります。

私は最近、開発者の皆さんとやり取りする中で、ある事実に気付きました。多くの開発者が IaC の有用性を認識していない、または認識してはいるものの、準備が煩雑であるという理由で、初めから導入をあきらめてしまっているようなのです。この記事では皆さんの IaC の導入が少しでも楽になるよう、IaC の利点を説明すると共に、現在利用可能な IaC ツールについて、具体的なツールの使用例を交えながら簡単に紹介したいと思います。今回取り上げるのは、業界内でも特に人気が高い、HashiCorp の TerraformPulumi という 2 つの IaC ツールです。

前提条件

事前に以下の手順を完了しておく必要があります。

Google Cloud プロジェクト認証情報の作成

IaC ツールを使用して管理操作を行うには、Google Cloud の認証情報を作成する必要があります。

  • サービス アカウント キーの作成ページを開きます。
  • 既定のサービス アカウントを選択するか、新たにアカウントを作成したら、キー タイプとして JSON を選択し、[作成] をクリックします。
  • この JSON ファイルを ~/.config/gcloud/ ディレクトリに保存し、ファイル名を cicd_demo_gcp_creds.json に変更します。このファイルは、後の行程で Google Cloud CLI をコンテナ内で有効化するときに非常に重要になります。

Pulumi API トークンの作成

プロジェクトのステートを Pulumi のバックエンド クラウドに格納するために、Pulumi API トークンの取得が必要です。

  • app.pulumi.com/ にアクセスします。
  • ページ左上の [Select an Organization (組織を選択する)] をクリックし、使用するアカウントを選択します。
  • [Settings (設定)] をクリックします。
  • ページ左側の [Access Tokens (アクセス トークン)] をクリックします。
  • ページ右側の [New Access Token (新規アクセス トークン)] をクリックします。
  • [Create (作成)] をクリックし、作成した API トークンを保存します。このトークンは、後で Pulumi プロジェクトの初期化に使用します。

IaC101 Docker イメージの取得

これですべての前提条件が満たされたので、次はこのワークショップで使用する Docker イメージを取得しましょう。ターミナルに次のコマンドを入力します。

docker pull ariv3ra/iac101

これで、ariv3ra/iac101 Docker イメージがローカルに取得できたはずです。docker images を実行して、結果の一覧にこのイメージが含まれていれば、正常に取得できています。

IaC101 Docker コンテナの起動

上記の ariv3ra/iac101 イメージを基に、新たに Docker コンテナを作成します。ここには、あらかじめ定義された IaC ツールおよびコードがすべて含まれています。

/.config/gcloud/ ディレクトリのマウント

新たなコンテナを実行するには、~/.config/gcloud/ への絶対パスが必要になります。たとえば、私の MacOS マシンの場合なら、絶対パスは /Users/angel/.config/gcloud/ です。ご自身のローカル マシン上で、gcloud ディレクトリの絶対パスを忘れずに確認しておいてください。

マウントによる IaC101 コンテナの起動

ターミナルで次のコマンドを実行します。<your absolute path here> の部分は、ローカル マシン上の実際の glocud/ ディレクトリの絶対パスと置き換えてください。

docker run -it --name iactest --mount type=bind,source=<your absolute path here>.config/gcloud/,target=/root/.config/gcloud/ ariv3ra/iac101

IaC101 コンテナの起動後

前述の docker run コマンドを実行すると、IaC101 コンテナが作成および起動され、ターミナル シェルは実行中のコンテナにログインした状態になります。これ以降、exit コマンドによって手動でコンテナを停止するまで、ターミナル シェルで実行するコマンドはすべて、この Docker コンテナ内で実行されます。このコンテナには Terraform と Pulumi の CLI ツールがどちらもインストールされており、それぞれのツールを使用してインフラストラクチャを構築するためのサンプル コードも付属しています。このコンテナのプロジェクト ファイルは次のとおりマッピングされます。

projects/
|_ terraform/gcp/compute/   # Contains the Terraform code files
|_ pulumi/gcp/compute/      # Contains the Pulumi code files

HashiCorp Terraform

HashiCorp Terraform は、インフラストラクチャの作成、変更、バージョン管理を安全かつ効果的に行うためのオープンソース ツールです。Terraform を使用すると、既存のサービスプロバイダーに加え、カスタムの自社ソリューションも管理できます。

Terraform では、単一のアプリケーションまたはデータセンター全体を運用するために必要なコンポーネントを設定ファイルに定義します。また、目的のステートに達するために実行する内容が記載された実行プランを生成し、これを実行して、記述どおりのインフラストラクチャを構築します。設定が変更されると、Terraform は変更内容を特定し、適用すべき増分の実行プランを生成します。

Terraform で管理できるインフラストラクチャには、コンピューティング インスタンス、ストレージ、ネットワーキングのような低レベルのコンポーネントだけでなく、DNS エントリや SaaS 機能のような高レベルのコンポーネントも含まれます。

Terraform によるインフラストラクチャのプロビジョニング

Terraform コードを使用して、GCP にリソースをプロビジョニングしてみましょう。terraform/gcp/compute/ 内にある main.tf は、この例で使用するインフラストラクチャについて定義したコードです。これからこのファイルを使用して、Docker コンテナにパッケージ化された、Python Flask アプリケーションをインストールして実行するための新しいコンピューティング インスタンスを作成します。この Terraform コードではさらに、ポート 5000 を介したアプリケーションへのパブリック アクセスを許可するファイアウォール ルールも作成されます。

前述の内容でプロビジョニングを行うために、ターミナルで以下のコマンドを実行します。

cd ~/project/terraform/gcp/compute/

Terraform の初期化

前述のコマンドで ~/project/terraform/gcp/compute/ ディレクトリに移動してから、以下のコマンドを実行します。

terrform init

以下のような結果が返されるはずです。

root@d9ce721293e2:~/project/terraform/gcp/compute# terraform init

Initializing the backend...

Initializing provider plugins...
- Checking for available provider plugins...
- Downloading plugin for provider "google" (hashicorp/google) 3.10.0...

* provider.google: version = "~> 3.10"

Terraform has been successfully initialized!

Terraform でのプレビュー

Terraform には、Terraform コードをドライ実行して、実際には何も実行することなくバリデーションできる terraform plan というコマンドがあります。このコマンドでは、Terraform が既存のインフラストラクチャに対して実行するすべてのアクションと変更をグラフ化することも可能です。ターミナルで以下を実行します。

terraform plan

以下のような結果が返されるはずです。

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_compute_firewall.http-5000 will be created
  + resource "google_compute_firewall" "http-5000" {
      + creation_timestamp = (known after apply)
      + destination_ranges = (known after apply)
  }

    # google_compute_instance.default will be created
  + resource "google_compute_instance" "default" {
      + can_ip_forward       = false
      + cpu_platform         = (known after apply)
      + deletion_protection  = false
      + guest_accelerator    = (known after apply)
      + id                   = (known after apply)
      + instance_id          = (known after apply)
      + label_fingerprint    = (known after apply)
      + labels               = {
          + "container-vm" = "cos-stable-69-10895-62-0"
        }
      + machine_type         = "g1-small"
  }
  Plan: 2 to add, 0 to change, 0 to destroy.

このように、Terraform は main.tf ファイル内のコードに基づいて GCP リソースを新規作成します。

Terraform の適用

インフラストラクチャを新規作成してアプリケーションをデプロイする準備が整ったので、ターミナルで以下のコマンドを実行します。

terraform apply

コマンドを確認するメッセージが表示されたら、yes と入力して Enter キーを押します。

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Terraform によって GCP 上でインフラストラクチャが作成され、その後少ししてから、アプリケーションがセットアップされて起動されます。なお、Terraform による処理の完了後、アプリケーションがオンラインになるまでには 3 ~ 5 分かかります。バックエンド システムがプロビジョニングを行ってからクラスタをオンラインにするため、瞬時には処理されません。

完了すると、以下のような内容が出力されます。

Outputs:

Public_IP_Address = 34.74.75.17

この Public_IP_Address の値は、アプリケーションを実行中の IP アドレスを表します。そのため、ブラウザーを開き、この値とポート アドレスの :5000 を入力 (34.74.75.17:5000 のように入力) すると、GCP で実行中のアプリケーションが表示されます。

Terraform のリソースの破棄

作成した Google のコンピューティング インスタンスとアプリケーションが正常に稼働していることを確認できたところで、今度は terraform destroy コマンドを実行して、このチュートリアルで作成したアセットを破棄してみましょう。アセットはそのまま稼働させても構いません。しかし、Google Cloud Platform 上でアセットを実行するにはコストが発生し、そのコストに対する責任も負わなくてはなりません。Google から無料トライアルの登録ユーザーに提供されている 300 ドル分のクレジットは、アセットを実行し続ければあっという間に尽きてしまいます。もちろん、ご事情に合わせて判断していただければと思いますが、terraform destroy を実行すれば実行中のすべてのアセットを破棄できます。

次は、Pulumi を使用したインフラストラクチャのプロビジョニング方法を見ていきましょう。

Pulumi SDK

Pulumi SDK はクラウド アプリケーションと IaC の定義およびデプロイに対応したオープン ソース フレームワークです。TypeScript や JavaScript、Python、Go など、開発者が自分で選んだ言語でコードを記述できるという特長があります。YAML や DSL といった固有の言語を新たに習得しなくても、クラウドベースのアプリケーションやインフラストラクチャを運用できるという点で、Pulumi のアプローチはきわめて画期的です。Pulumi を使用すれば、抽象化やコードの再利用のほか、使い慣れた IDE、リファクタリング ツールやテスト ツールを利用することができます。1 つのツールチェーンおよび一連のフレームワークを熟知していれば、任意のクラウド (AWS、Azure、GCP、または Kubernetes) に簡単に移行できるのです。

Pulumi によるインフラストラクチャのプロビジョニング

記事の前半では、Terraform を使用してインフラストラクチャを構築したので、今度は Pulumi を使用して、新たなインフラストラクチャをプロビジョニングおよびデプロイする方法を学習しましょう。Pulumi のコードとツールを使用して、先ほどと同じ GCP リソースを作成する例をお見せします。Pulumi では、柔軟性の高い汎用的なプログラミング言語でインフラストラクチャを定義できます。今回の例では Python を使用します。インフラストラクチャの定義は、~/project/pulumi/gcp/compute/ ディレクトリ内の __main.py__ ファイルで行います。

なお、Pulumi API トークンを忘れずに準備しておいてください。以降のセクションで必要になります。

Pulumi のサンプルは ~/project/pulumi/gcp/compute/ ディレクトリ内にあります。次のコマンドを実行し、Pulumi のサンプル ディレクトリに移動します。

cd ~/project/pulumi/gcp/compute/

Pulumi の依存関係のインストール

この例では IaC の仕様を Python で定義するため、最初に Pulumi Python SDK の依存関係をインストールする必要があります。ターミナルで以下のコマンドを実行します。

pip install -r ../requirements.txt

Pulumi によるプレビュー

Pulumi には、pulumi preview というドライ実行のコマンドが用意されています。このコマンドを使用すると、既存のステート ファイルに記載されているステートに基づいて、既存スタックに対する更新内容をプレビューすることが可能です。目的としている最新のステートは、Pulumi プログラムを実行し、オブジェクト グラフの作成結果から全リソースの割り当て情報を抽出することで算定されます。こうした割り当ての情報を既存のステートと比較することにより、目的のステートを実現するためにどのような処理を実行する必要があるかが判断されます。スタックへの実際の変更処理は一切行われません。

ターミナルで以下のコマンドを実行します。

pulumi preview

上記のコマンドを実行すると、以下の結果が表示され、事前に作成しておいた Pulumi API トークンの入力を求められるので、トークンをターミナルにペーストして Enter キーを押します。

Manage your Pulumi stacks by logging in.
Run `pulumi login --help` for alternative login options.
Enter your access token from https://app.pulumi.com/account/tokens
    or hit <ENTER> to log in using your browser                   :

注: API トークンをターミナルにペーストしても、実際の値は表示されません。セキュリティの観点から、非表示に設定されています。

場合によっては、ターミナル内でスタックの選択を求められることがあります。その場合は dev オプションを選択し、Enter キーを押します。以下のような結果が返されるはずです。

Previewing update (dev):
     Type                     Name                        Plan
 +   pulumi:pulumi:Stack      compute-dev                 create
 +   ├─ gcp:compute:Network   network                     create
 +   ├─ gcp:compute:Address   workshops-infra-as-code101  create
 +   ├─ gcp:compute:Instance  workshops-infra-as-code101  create
 +   └─ gcp:compute:Firewall  firewall                    create

Resources:
    + 5 to create

これで、インフラストラクチャのステートが追跡管理されている、Pulumi のバックエンド クラウドにアクセスできるようになりました。続いて、コードを実行してみましょう。

Pulumi コードの実行

Pulumi コードを実行するには、pulumi up コマンドを使用します。これは、スタック内のリソースを作成または更新するコマンドです。ターゲット スタックで目的とされている最新のステートは、現在の Pulumi プログラムを実行し、リソース グラフの作成に使用される全リソースの割り当て情報を確認することで算定されます。さらに、この目的のステートを既存のステートと比較することにより、運用停止を最小限に抑えつつ目的のステートを実現するためにはどのような作成、読み取り、更新、削除の処理を実行する必要があるかが判断されます。その後、このコマンドによって、スタックの新しいステートに関するすべてのトランザクションのスナップショットが記録されるため、以降も引き続き増分のみの変更でスタックの更新を行えます。

ターミナルで以下を実行します。

pulumi up

yes オプションを選択し、Enter キーを押します。Pulumi アプリケーションが実行され、少し経つと GCP 上にアプリケーションを実行するサーバーが完成します。以下のような結果が返されるはずです。

Updating (dev):
     Type                     Name                        Status
 +   pulumi:pulumi:Stack      compute-dev                 created
 +   ├─ gcp:compute:Network   network                     created
 +   ├─ gcp:compute:Address   workshops-infra-as-code101  created
 +   ├─ gcp:compute:Firewall  firewall                    created
 +   └─ gcp:compute:Instance  workshops-infra-as-code101  created

Outputs:
    external_ip       : "34.74.75.17"
    instance_meta_data: {
        gce-container-declaration: "spec:\n  containers:\n    - name: workshops-infra-as-code101\n      image: ariv3ra/workshops-infra-as-code101:latest\n      stdin: false\n      tty: false\n  restartPolicy: Always\n"
    }
    instance_name     : "workshops-infra-as-code101"
    instance_network  : [
        [0]: {
            accessConfigs    : [
                [0]: {
                    natIp              : "34.74.75.17"
                    network_tier       : "PREMIUM"
                }
            ]
            name             : "nic0"
        }
    ]

Resources:
    + 5 created

Duration: 58s

表示された結果の中に、external_ip キーが含まれていることにお気付きかと思います。この値はポート 5000 経由でアクセスできる、アプリケーションのパブリック向けの IP アドレスです。前半の Terraform の例とまったく同様に、ブラウザーを使用してこのアプリケーションにアクセスできます。バックエンド システムの処理が完了し、スタック全体がオンラインになるまでに、数分かかるのでご注意ください。

Pulumi のリソースの破棄

Pulumi には、すべてのクラウド リソースの運用を終了できる pulumi destroy というコマンドがあります。このコマンドでは、名前を指定することで、既存のスタックを削除できます。現在のスタックのステートは、ワークスペース内の関連付けられたステート ファイルから読み込まれています。終了コマンドを実行すると、こうしたスタックのリソースの割り当ておよび関連付けられているステートがすべて解除されます。

ターミナルで pulumi destroy を実行し、オプションの入力を求められたら yes を選択します。これで、作成した GCP リソースが永久に破棄されます。

まとめ

お疲れさまでした。TerraformPulumi という最新の IaC ツールを使用し、GCP でのアプリケーションのプロビジョニングとデプロイを経験したことで、皆さんは開発者としてさらにレベルアップしました。さらに詳しく知りたい方は以下の資料を参照してください。