share facebook facebook2 twitter menu hatena pocket slack

2016.01.13 WED

いまさらで恐縮ですが cloud-init について勉強する(1)

川原 洋平

WRITTEN BY川原 洋平

追記

各モジュールの動作確認には以下のようなシェルスクリプトを作って行うと捗る。こちら参考になりました。ありがとうございます。

#!/bin/sh

rm -rf /var/lib/cloud/*
  cloud-init init --local
  cloud-init init
  cloud-init modules --mode config
  cloud-init modules --mode final

まず、/var/lib/cloud/ 以下をバサッと消す。その後で…

  • cloud-init init --local
  • cloud-init init
  • cloud-init modules --mode config
  • cloud-init modules --mode final

を実行する。これは /etc/rc3.d/ 以下の起動スクリプトの順番に準拠している。

[root@centos-01 cloud]# ls -l /etc/rc3.d/*cloud*
lrwxrwxrwx 1 root root 20 Nov 13 21:38 /etc/rc3.d/K50cloud-init -> ../init.d/cloud-init
lrwxrwxrwx 1 root root 26 Nov 13 21:38 /etc/rc3.d/K50cloud-init-local -> ../init.d/cloud-init-local
lrwxrwxrwx 1 root root 22 Nov 13 21:38 /etc/rc3.d/S50cloud-config -> ../init.d/cloud-config
lrwxrwxrwx 1 root root 21 Nov 13 21:38 /etc/rc3.d/S51cloud-final -> ../init.d/cloud-final

tl;dr

いまさらで恐縮ですが cloud-init に迫ってみる

普段、何気なく EC2 を起動していてもあまり意識していなかった cloud-init について、面と向かわないといけないことがあったので、これを機会に cloud-init について勉強してみたいと思う。既に cloud-init については数多くの知見が出回っているが、実際に手を動かして cloud-init に迫ってみたい。

参考

cloud-init を自分なりに紐解いていく

cloud-init とは

cloud-init とは」について詳しく書かれている資料を見つけることが出来なかったが、こちらのページの冒頭に…

Package provides configuration and customization of cloud instance.

とあるので、(ディストリビュージョンを超えた)クラウドインスタンスの構成やカスタマイズを行うことが出来るパッケージという認識ですすめる。

また、ドキュメントには以下のように書かれている。

  • Setting a default locale(デフォルトロケールの設定)
  • Setting a instance hostname(インスタンスのホスト名の設定)
  • Generating instance ssh private keys(SSH プライベートキーの生成)
  • Adding ssh keys to a users .ssh/authorized_keys so they can log in(ユーザーの SSH キーを authorized_keys に追加)
  • Setting up ephemeral mount points(マウントポイントの設定)

また、

Cloud-init’s behavior can be configured via user-data.

cloud-init の振る舞いは user-data で設定することが出来るとのことで、ドキュメントには EC2 インスタンスを起動する際に利用する例として以下のように記載されている。

This is done via the –user-data or –user-data-file argument to ec2-run-instances for example.

うんちくは程々に

cloud-init の設定ファイルである cloud-init について自分なりに紐解いていく。紐解くにあたっては予算が少ないので Ubuntu 14.04 上に LXC で起動した CentOS 6.7 コンテナを利用する。

$ sudo apt-get install lxc yum
$ sudo lxc-create -n centos-01 -t centos
$ sudo lxc-start -d -n centos-01

CentOS release 6.7 (Final)
Kernel 3.13.0-55-generic on an x86_64

centos-01 login:

尚、LXC についてはこちらの特集記事が大変参考になる。

CentOS コンテナが起動したらログインして cloud-init をインストールする。

# yum install cloud-init

これで準備完了。

cloud-init のディレクトリ構成

前述の設定ファイルではなく、cloud-init によって実行されるスクリプト等が展開されるディレクトリは以下のようになっている。

# tree --charset=x /var/lib/cloud/ -L 2
/var/lib/cloud/
|-- data
|   |-- instance-id
|   |-- previous-datasource
|   |-- previous-hostname
|   |-- previous-instance-id
|   |-- result.json
|   `-- status.json
|-- handlers
|-- instance -> /var/lib/cloud/instances/iid-datasource-none
|-- instances
|   `-- iid-datasource-none
|-- scripts
|   |-- per-boot
|   |-- per-instance
|   |-- per-once
|   `-- vendor
|-- seed
`-- sem
    `-- config_scripts_per_once.once

このディレクトリについてはドキュメントにて言及されているので、ざっくりと拝借させて頂く。

ディレクトリ 詳細
/var/lib/cloud cloud-init 固有のサブディレクトリを含むメインディレクトリで変更することも可能
data/ インスタンス ID やデータソース、ホスト名等の情報が含まれている(以下で全てのファイルを展開してみる)
handlers/ part-handler コードを置いておく、ファイル名は part-handler-${ハンドラ番号}(ドキュメントでは XYZ で表現)とする
instance/ instances のサブディレクトリのアクティブなインスタンスディレクトリのシンボリックリンクになっている
instances/ 同一のインスタンスイメージから作成されたインスタンスの識別子ディレクトリが作成され、アクティブなインスタンスが instance にリンクしている
scripts/ ダウンロードしたり作成したスクリプトを配置する
seed/ TBD = 未確定
sem/ このディレクトリにはインスタンス ID に関連付けられていないセマフォファイルが含まれており、これらのファイルは各モジュールが確実に実行する為に使用される

data/ 以下に保存されているファイルを全て展開すると以下のような内容となっている。

# for i in `ls /var/lib/cloud/data/*`; do echo "====== $i ======"; cat $i; echo ""; done
====== /var/lib/cloud/data/instance-id ======
iid-datasource-none

====== /var/lib/cloud/data/previous-datasource ======
DataSourceNone: DataSourceNone

====== /var/lib/cloud/data/previous-hostname ======
# Created by cloud-init v. 0.7.5 on Thu, 12 Nov 2015 14:30:25 +0000
HOSTNAME=centos01.localdomain

====== /var/lib/cloud/data/previous-instance-id ======
iid-datasource-none

====== /var/lib/cloud/data/result.json ======
{
 "v1": {
  "errors": [
   "'NoneType' object is not iterable",
   "'NoneType' object is not iterable"
  ],
  "datasource": null
 }
}

====== /var/lib/cloud/data/status.json ======
{
 "v1": {
  "init": {
   "start": 1447339866.7182679,
   "finished": 1447340129.1867039,
   "errors": [
    "'NoneType' object is not iterable"
   ],
   "end": null
  },
  "datasource": null,
  "modules-config": {
   "start": 1447340129.37832,
   "finished": 1447340129.4275589,
   "errors": [
    "'NoneType' object is not iterable"
   ],
   "end": null
  },
  "modules-final": {
   "start": 1447368909.2942469,
   "finished": 1447368909.3693061,
   "errors": [],
   "end": null
  },
  "init-local": {
   "start": 1447339866.4216521,
   "finished": 1447339866.5258591,
   "errors": [],
   "end": null
  },
  "stage": null
 }
}

instance ディレクトリには以下のようなファイルが展開されている。

# tree --charset=x /var/lib/cloud/instance -L 1
/var/lib/cloud/instance
|-- boot-finished
|-- cloud-config.txt
|-- datasource
|-- handlers
|-- obj.pkl
|-- scripts
|-- sem
|-- user-data.txt
|-- user-data.txt.i
|-- vendor-data.txt
`-- vendor-data.txt.i

sem ディレクトリには以下のようにファイルが展開されている。

# for i in `ls /var/lib/cloud/sem/*`; do echo "====== $i ======"; cat $i; echo ""; done
====== /var/lib/cloud/sem/config_scripts_per_once.once ======
375: 1447338625.91

cloud-init 設定ファイルのディレクトリ構成

cloud-init をインストールすると以下のように設定ファイルが展開される。

# tree --charset=x /etc/cloud/
/etc/cloud/
|-- cloud.cfg
|-- cloud.cfg.d
|   |-- 05_logging.cfg
|   `-- README
`-- templates
    |-- chef_client.rb.tmpl
    |-- hosts.debian.tmpl
    |-- hosts.redhat.tmpl
    |-- hosts.suse.tmpl
    |-- resolv.conf.tmpl
    |-- sources.list.debian.tmpl
    `-- sources.list.ubuntu.tmpl

2 directories, 10 files

cloud-init の挙動を制御するのは cloud.cfg となるが、cloud.cfg.d 以下に切り出すことも可能。但し、切り出しについては行わず cloud.cfg のみを見てみる。また、templates ディレクトリには hosts ファイルや resolv.conf のテンプレートとなるファイルが保存されている。例えば、hosts.redhat.tmpl は以下のような内容になっている。

# cat hosts.redhat.tmpl
#*
    This file /etc/cloud/templates/hosts.redhat.tmpl is only utilized
    if enabled in cloud-config.  Specifically, in order to enable it
    you need to add the following to config:
      manage_etc_hosts: True
*#
# Your system has configured 'manage_etc_hosts' as True.
# As a result, if you wish for changes to this file to persist
# then you will need to either
# a.) make changes to the master file in /etc/cloud/templates/hosts.redhat.tmpl
# b.) change or remove the value of 'manage_etc_hosts' in
#     /etc/cloud/cloud.cfg or cloud-config from user-data
#
# The following lines are desirable for IPv4 capable hosts
127.0.0.1 ${fqdn} ${hostname}
127.0.0.1 localhost.localdomain localhost
127.0.0.1 localhost4.localdomain4 localhost4

# The following lines are desirable for IPv6 capable hosts
::1 ${fqdn} ${hostname}
::1 localhost.localdomain localhost
::1 localhost6.localdomain6 localhost6

また、興味深いのが chef_client.rb.tmpl で展開すると以下のようになっている。

# cat chef_client.rb.tmpl
#*
     This file is only utilized if the module 'cc_chef' is enabled in
     cloud-config. Specifically, in order to enable it
     you need to add the following to config:
       chef:
         validation_key: XYZ
         validation_cert: XYZ
         validation_name: XYZ
         server_url: XYZ
*#
log_level              :info
log_location           "/var/log/chef/client.log"
ssl_verify_mode        :verify_none
validation_client_name "$validation_name"
validation_key         "/etc/chef/validation.pem"
client_key             "/etc/chef/client.pem"
chef_server_url        "$server_url"
environment            "$environment"
node_name              "$node_name"
json_attribs           "/etc/chef/firstboot.json"
file_cache_path        "/var/cache/chef"
file_backup_path       "/var/backups/chef"
pid_file               "/var/run/chef/client.pid"
Chef::Log::Formatter.show_time = true

このテンプレートを展開して OS 起動時に Chef を使ってプロビジョニングも出来たりするのだろう。(今回は試さないけど)

cloud.cfg について

cloud-init インストール直後の cloud.cfg は以下のようになっている。(長いので gist に…)

cloud.cfg のフォーマットは YAML となっており、各キーに関しての解説がこれまた見つからない…ので以下のファイルのコメントが参考になった。

github.com

上記のファイルを参考にさせて頂いて以下のようにコメントを付けてみた。

# インスタンス上に作成するユーザーを定義する。以下の場合には default_user を参照する。
users:
 - default

# root でのログインを無効にする定義。以下の場合には root でのログインを有効にしている。
disable_root: 1
# SSH のパスワードログインを許可する、許可しないの定義。以下の場合には SSH のパスワードログインを有効にしている。
ssh_pwauth:   0

# locale の設定ファイルのパスを定義
locale_configfile: /etc/sysconfig/i18n
# fstab の各フィールドを定義
mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
# ???
resize_rootfs_tmp: /dev
# SSH キーを削除する定義。パブリックなイメージについては有効にしておく必要がある。以下の例では有効にしている。
ssh_deletekeys:   0
# 生成する SSH キーのタイプを定義する
ssh_genkeytypes:  ~
# syslog のパラメータを定義する
syslog_fix_perms: ~

# 起動時に実行されるモジュールを定義する(※S50cloud-init-local/S51cloud-init)
cloud_init_modules:
 - migrator
(snip)

# 起動時に実行されるモジュールを定義する(※S52cloud-config なので S50cloud-init-local/S51cloud-init の後に実行される)
cloud_config_modules:
 - mounts
(snip)

# 起動時に実行されるモジュールを定義する(※S53cloud-final なので S50cloud-init-local => S51cloud-init => S52cloud-config => S53cloud-final)
cloud_final_modules:
 - rightscale_userdata
(snip)

# システム固有の設定等を定義する(???)
system_info:
  # 冒頭の users > default から参照されるユーザー定義
  default_user:
    name: centos
    lock_passwd: true
    gecos: Cloud User
    groups: [wheel, adm]
    sudo: ["ALL=(ALL) NOPASSWD:ALL"]
    shell: /bin/bash
  distro: rhel
  paths:
    cloud_dir: /var/lib/cloud
    templates_dir: /etc/cloud/templates
  ssh_svcname: sshd

# vim:syntax=yaml

ひとまず今日はここまで。

おわり

cloud-init

  • 奥深い
  • EC2 使っていれば漏れ無く使っているはずなんだけど…情報が少ない気がするのは自分だけかな…

まだまだ

  • 全然使いこなせていないよ…(汗

(おまけ)cloud-init ヴィデオ

元記事はこちら

いまさらで恐縮ですが cloud-init について勉強する(1)