cloud-initでAmazon Linuxの初期設定を制御する

本当は書きたくない低レイヤーネタ。が、知ったことは記録しておきたいので。

表題の件、今さら? なんだけどね…、実は最近までcloud-initの存在すら知りませんでした。AWSは個人的に利用しているとはいえ、検証目的なんで低レイヤーな部分は放置だし、一旦作ったAMIを使いまわしているだけなので、知らないことも沢山ある。

cloud-initは一言でいうと、Amazon Linux(それ以外もあると思うがよくわからない)インスタンス起動時にガリガリ、ゴリゴリと勝手にカスタマイズしてくれるヤツ。便利といえば便利で、おせっかいといえばおせっかい。

本題。Amazon Linuxにおける以下のような一般的な対処があるとする。このようなcloud-initへの対応を、cloud-initで制御したい。最後のパスワードログイン以外はありがちな対応と想定する。

  • ストレージサイズをデフォルトの8GBから大きいサイズに指定してインスタンスを起動した場合、インスタンスログイン後にresize2fsしてやる必要がある。
  • タイムゾーンはデフォルトでUTCだがJSTに変えたい。
  • ロケールをja_JP.UTF-8としたい。
  • 自動アップデートを抑止したい。
  • パスワードログインはデフォルトで無効になる。これを有効にして、かつ複製したAMIから起動したインスタンスでもcloud-initによる上書きを抑止したい。

これを、起動時に渡すUserDataに記述するとこうなる。すまんが早く寝たいので細かい説明は…

#cloud-config
ssh_pwauth: true
repo_upgrade: none
locale: ja_JP.UTF-8

runcmd:
 - [ cp, /usr/share/zoneinfo/Asia/Tokyo, /etc/localtime]
 - [ resize2fs, /dev/xvda1]

マネジメントコンソールでUserDataを記載するところは、「ステップ3: インスタンスの詳細の設定」。「高度な詳細」に隠れているので、広げる。UserDataはaws cliやAPIから直接起動するときにもセットできるはずだが、記述方法はすまんが早く…

ec2_userdata

試しにストレージサイズを9GBに指定して起動してみる。起動後、ログインして確認した結果。

指定した通りになってる。よしよし。

$ df -h
ファイルシス サイズ 使用 残り 使用% マウント位置
/dev/xvda1 8.8G 1.5G 7.2G 17% /
devtmpfs 490M 56K 490M 1% /dev
tmpfs 499M 0 499M 0% /dev/shm

ロケールも指定した通り。

$ sudo cat /etc/sysconfig/i18n
LANG=ja_JP.UTF-8

タイムゾーンは確かにJSTになっているが…
$ date
2015年 7月 15日 水曜日 21:08:04 JST

/etc/sysconfig/clockはUTCのまま。

$ cat /etc/sysconfig/clock
ZONE="UTC"
UTC=true

“cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime”は実行された様子だが、/etc/sysconfig/clockはいじってないよということか。確かに。

なので以下、別途やっとおく必要がある。

$ sudo vim /etc/sysconfig/clock
ZONE="Asia/Tokyo"
UTC=False

パスワード認証。有効になってる。

$ grep Pass /etc/ssh/sshd_config
PasswordAuthentication yes
#PermitEmptyPasswords no
# PasswordAuthentication no
(略)

うむ、これは楽だ。ついでにSwap領域作成もcloud-initでできないものかと軽く調べてみると、こんな風に書いてできる様子。これはストレージの追加時にInstance Storeを/dev/sdbとして割り当てて、Instance Store(ephemeralディスク)をSwap領域にするという前提。しかしすべてのインスタンスタイプにephemeralディスクがアタッチ可能ではないので、適用できないケースもある。

mounts:
 - [ ephemeral0, swap, swap ,"defaults", "0", "0" ]

bootcmd:
 - mkswap /dev/xvdb
 - swapon /dev/xvdb

ちなみに自動アップデート抑止の対応はOS側(yumの設定だったか)でも別途必要だと思うが、ここでは割愛。
先ほどのUserDataに書いた設定を/etc/cloud/cloud.cfgに書いておけば、このインスタンスを元にしたAMIを起動するときにはUserDataは必要ない、と考えていいのかな。cloud.cfgの中身はこんな風。

$ cat /etc/cloud/cloud.cfg

# WARNING: Modifications to this file may be overridden by files in
# /etc/cloud/cloud.cfg.d

# If this is set, 'root' will not be able to ssh in and they 
# will get a message to login instead as the default user (ec2-user)
disable_root: true

# This will cause the set+update hostname module to not operate (if true)
preserve_hostname: true

datasource_list: [ Ec2, None ]

repo_upgrade: security
repo_upgrade_exclude:
 - kernel
 - nvidia*
 - cudatoolkit

mounts:
 - [ ephemeral0, /media/ephemeral0 ]
 - [ swap, none, swap, sw, "0", "0" ]
# vim:syntax=yaml

以下ディレクトリにカスタムcfgを放り込んでおいても、よしなにやってくれるらしい。

$ ls -l /etc/cloud/cloud.cfg.d
合計 16
-rw-r–r– 1 root root 2594 3月 5 05:39 00_defaults.cfg
-rw-r–r– 1 root root 1963 3月 5 05:40 05_logging.cfg
-rw-r–r– 1 root root 586 2月 12 08:32 10_aws_yumvars.cfg
-rw-r–r– 1 root root 141 10月 11 2014 README

READMEにはこう書いてあるので、実行順序はファイル先頭の数値で制御されるっぽい。

# All files in this directory will be read by cloud-init
# They are read in lexical order.  Later files overwrite values in
# earlier files.

ではやってみるか、と/etc/cloud/cloud.cfg.d/06_custom.cfgファイルを作成して、先ほどのUsarDataを貼り付けておく。ログに記録されるようにコメントを追加。

/etc/cloud/cloud.cfg.d/06_custom.cfg

ssh_pwauth: true
repo_upgrade: none
locale: ja_JP.UTF-8

runcmd:
 - [ echo, 'Set time zone' ]
 - [ cp, /usr/share/zoneinfo/Asia/Tokyo, /etc/localtime]
 - [ echo, 'Extend disk size' ]
 - [ resize2fs, /dev/xvda1]

この状態でAMIを取得。そのAMIを元に、新たにインスタンスを起動してみる。今度はUserDataには何も入れない。ストレージサイズを11GBに指定してみた。結果、期待値通りだった。

$ df -h
ファイルシス サイズ 使用 残り 使用% マウント位置
/dev/xvda1 11G 2.5G 8.2G 24% /
devtmpfs 490M 56K 490M 1% /dev
tmpfs 499M 0 499M 0% /dev/shm

パスワード認証も上書きされることがなく、他の要素もOK。

ログをみてみると、コメントが記録されているので06_custom.cfgが実行されたはず。最後になんか言われているが気にしない…

/var/log/cloud-init-output.log

:
:
Set time zone
Extend disk size
resize2fs 1.42.12 (29-Aug-2014)
The filesystem is already 2883067 (4k) blocks long.  Nothing to do!

cloud-init、使いこなせれば結構便利そう。
AWSの場合、低レイヤーな部分はAnsible等ではなくcloud-initにやらせた方がよさげである。というか、そうするべきなんだろう。

参考
Amazon EC2(Linux)システム管理で知らないとハマる5つの環境設定
AmazonLinux起動時にタイムゾーン、ホスト名、ディスク拡張、アップデートを実施する方法(cloud init、user data)

追記:2014/07/19
CentOSのイメージでやってみたら、こっちも同様にできた。/etc/cloud/cloud.cfgの中身はAmazon Linuxとは異なっていた。プラットフォームによってどのように適用されるのか、詳しいことはわからない。

Pocket