いまさらで物凄く恐縮ですが、cloud-initについて勉強してみた


はじめに

本稿は、cloud-init に関して、ふわ っとしたイメージしか持っていなかった私が、自身の記録用にまとめたノートを投稿しているだけですので、エキスパートの方は参考にしないでください。初学者の方にとっては、比較的わかりやすい内容にまとめているつもりです。

cloud-initとは

cloud-initは、cloud instanceの初期化メソッドで、様々なクラウドベンダーをサポートしています。
元々、EC2用に作成されたもので、ec2-init というパッケージ名だったそうです。
ドキュメントは、こちらです。

インスタンスの初期設定に必要な、アカウント、リポジトリ設定、パッケージのインストールなど様々な機能もありますし、任意のスクリプトを実行する事も可能です。(CA証明書インストール作業とか、結構楽かも。)
cloud-initは #cloud-configで始まる、ユーザーデータ などを受け取り、初期セットアップを行います。

cloud-configは、YAML形式で記述します。
例えば、以下でユーザーを作成します。

#cloud-config
users:
  - default
  - name: foobar
    gecos: Foo B. Bar
    primary_group: foobar
    groups: users
    selinux_user: staff_u
    expiredate: 2012-09-01
    ssh_import_id: foobar
    lock_passwd: false
    passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYC

使い分け

構成管理ツールとして、ChefやAnsible、puppetなどがありますが、それらの使い分けに関してまとめてみました。
以下の画像がわかりやすいと思います。
AnsibleやChefなどと被るところがありますが、初期セットアップ という観点で使う場合は、 cloud-init を利用すればいいのではないかと考えています。

terraformで、新たにインスタンスをプロビジョニング、cloud-initで初期セットアップ、Ansibleで構成管理といった連携など考えられます。

Boot Stage

cloud-initのboot stageは以下、5つに分かれています。

  • Generator
  • Local
  • Network
  • Config
  • Final

Generator では、cloud-initを実行するか否かの決定をします。実行する場合、Localではメタデータに格納されているネットワーク情報を適用します。Networkではdisk_setupを実行しmodulesをマウントします。Configでは、config_moduleのみをrunします。Finalは一番最後に実行されます。ユーザーログイン後にRunする必要のあるスクリプトはここに記述する必要があります。

それぞれのステージは、以下のコマンドで確認できます。

$ cloud-init status --long
status: running
time: Fri, 26 Jan 2018 21:39:43 +0000
detail:
Running in stage: init-local

モジュール郡はそれぞれが上記のStageに紐づいています。例えば、Bootcmd は、cloud_init_modulesに属しており、boot毎に実行されます。
scripts-per-bootでは、cloud_final_modulesに属しており、こちらもboot毎に実行されます。これらは実行のタイミングが異なるので、それを意識した上での記述が必要です。
ドキュメントには、

ブートプロセスの後半で実行できない処理にのみ使用してください。

と書いてあったので、上記でない限りは、 cloud_init_modules 内で処理をした方が良いみたいです。

注意としては、再起動プロセスは runcmd で記述の記述がおすすめ。(runcmdは、config_modulesですが、Final Stageで実行されます。)

デバッグ

まず、cloud-initのディレクトリ情報は以下の通りになっていました。

$ tree --charset=x /var/lib/cloud/ -L 2
/var/lib/cloud/
|-- data
|   |-- instance-id
|   |-- previous-datasource
|   |-- previous-instance-id
|   |-- result.json
|   |-- set-hostname
|   `-- status.json
|-- handlers
|-- instance -> /var/lib/cloud/instances/ocid1.instance.oc1.iad.***********
|-- instances
|   `-- ocid1.instance.oc1.iad.***********
|-- scripts
|   |-- per-boot
|   |-- per-instance
|   |-- per-once
|   `-- vendor
|-- seed
`-- sem

instanceのcloud-config.txtには、cloud-configの情報が格納されていました。
(これってプロビジョニング後からruncmdなどの記述を変更をしていいのかな。)

$ cat cloud-config.txt 

#cloud-config

# from 1 files
# part-001

---
cloud_final_modules:
-   - scripts-user
    - always
locale: ja_JP.utf8
package_upgrade: true
packages:
- man-pages-ja
- multitail
- nmap
- nc
- tmux
- tree
runcmd:
-   - sh
    - -c
    - echo $(date) >> /tmp/testfile.txt
timezone: Asia/Tokyo

また実行ログは、/var/log/cloud-init.log に格納されます。基本的に実行確認などは、ここを見ればOKだと思います。

$ cat /var/log/cloud-init.log | grep config-timezone
2020-05-17 16:28:37,630 - handlers.py[DEBUG]: start: modules-config/config-timezone: running config-timezone with frequency once-per-instance
2020-05-17 16:28:37,635 - helpers.py[DEBUG]: Running config-timezone using lock (<FileLock using file '/var/lib/cloud/instances/ocid1.instance.oc1.iad.anuwcljrejk3llyct26xrjijj74qh5i6cgtd33i7575zupmk5g74hk4o7cqa/sem/config_timezone'>)
2020-05-17 16:28:37,636 - handlers.py[DEBUG]: finish: modules-config/config-timezone: SUCCESS: config-timezone ran successfully

もしくは以下のような形で、ログ情報を出力する事も可能です。

runcmd:
-   - sh
    - -c
    - echo $(date) >> /tmp/testfile.txt
$ cat /tmp/testfile.txt
Mon May 18 01:28:38 JST 2020
2020年 5月 18日 月曜日 01:36:08 JST

最後に

初学者としてのcloud-initの私なりの理解をまとめました。
windowsOSをサポートしているcloudbase-initに関してのドキュメントは充実していないようなので、今後そのあたりのまとめ記事など投稿したいなと考えています。

参考情報

Amazon Linuxのcloud-initの実行順番を確認する
cloud-initを使ったLinux OSの初期設定
cloud-initをデバッグする Part 1