突然ですが、以下のタスクを与えられた場合、みなさんはどのように実装しますか。

「今回我々はアプリケーションのOSにUbuntuを採用することにしました。アプリケーションはDockerコンテナでホストするため、VM内ではdocker及びdocker-composeが利用できる必要があります。
また、Cloud LoggingとCloud Monitoringを利用したいので、それぞれのagentをインストールして有効化する必要もあります。
必要なパッケージをインストールして有効化する一連の作業は人が行わなくても良いように、何らかのソフトウェアを利用してInfrastructure as Codeを推進してください。」

最近のdockerはdocker composeというサブコマンドがついてきますが、一旦忘れます。

今回はこれにcloud-initを利用するとどれくらい簡単に実現できるのかを紹介するのが目的です。
最初に書いておくと、めちゃくちゃ簡単なのでansibleとかpuppetを思い浮かべた人は是非読んでください。

cloud-configを書く

cloud-initで構成を定義する際にはcloud-configと呼ばれる定義が必要です。みんな大好きyamlファイルで記述をします。
今回の要件に必要なものはざっとこんな感じでしょうか。

#cloud-config
timezone: Asia/Tokyo
locale: en_US.UTF-8
packages:
  - ["docker.io",      "20.10.2-0ubuntu1~20.04.2"]
  - ["docker-compose", "1.25.0-1"                ]
package_update: true
package_upgrade: true

# agent install
runcmd:
  - curl -sSO https://dl.google.com/cloudagents/add-logging-agent-repo.sh
  - curl -sSO https://dl.google.com/cloudagents/add-monitoring-agent-repo.sh
  - bash add-monitoring-agent-repo.sh
  - bash add-logging-agent-repo.sh
  - apt-get update
  - apt-get install -y google-fluentd stackdriver-agent google-fluentd-catch-all-config
  - systemctl enable google-fluentd
  - systemctl enable stackdriver-agent
  - gcloud auth configure-docker --quiet

write_files:
  - path: /etc/docker/daemon.json
    content: |
      {
        "userns-remap": "default"
      }
    permissions: '0644'
    owner: root:root


# reboot
power_state:
  delay: now
  mode: reboot

これをファイルとして用意しておく必要は必須ではありません、試したい人はクリップボードにコピーしていただければ良いです。

あとはUbuntu20.04LTSを指定してVMインスタンスを作成する際に、下画像のようにuser-data metadataを定義するだけです。

これで完了です。
筆者はcloud-initを使って作成したディスクをイメージとして保存し、bootイメージとして利用しています。
さすがにインスタンステンプレートに指定をして、スケールアウトの度にパッケージがインストールされるような運用はしていません。

terraformから使う

terraformから使う場合はファイルとして保存をします。
ここでは、templatesというディレクトリに cloud-init.yaml というファイル名で上記のcloud-configを保存しているものとします。
このとき、terraform resourceの google_compute_instanceに以下のようにmetadataを記述すれば良いです。

metadata = {
    user-data = file("./templates/cloud-init.yaml"),
}