AmazonLinuxのcloud-initについての調査メモ

EC2の起動処理で色々やりたいのでUserDataにシェルスクリプトかcloud-configを書いてやってみようと思ったのですが、そもそもどうやって動いているのか起動タイミングや初回起動のみ実行されるのかなど分からなかったので調べてみたり、動作確認してみたメモ

# 参考

+ [Linux インスタンスでの起動時のコマンドの実行](http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html)
+ [Amazon Linuxのcloud-initの動きについて調べてみた](http://dev.classmethod.jp/cloud/aws/probe-cloud-init/)
+ [Amazon Linuxのcloud-initの実行順番を確認する](http://dev.classmethod.jp/cloud/aws/probe-cloud-init-exec-timing/)
+ [cloud-init を None データソースで動作確認する](http://qiita.com/ngyuki/items/e2d0c13f54054e4f35e4)
+ [cloud-initでスクリプトが実行されるタイミングを調べてみた](http://blog.cloudpack.jp/2013/01/11/aws-news-cloud-init-scripts/)
+ [AWS EC2インスタンス起動時 cloud-initによりホスト名をNameタグ_インスタンスID_IPアドレスに設定する](http://qiita.com/na0AaooQ/items/d18a0df8d147e51a04a2)
+ [cloud-initのper-xxxまとめ](http://diary.shu-cream.net/blog/2015/06/01/cloud-init-per-xxx.html)

# 結論

色々確認しましたが、結論としては以下のようでした。


| 方法 | 実行タイミング | 起動時の実行有無 |ファイルの場所 |
|:-----------|:------------|:------------|:------------|
| シェルスクリプト | /etc/rc3.d/S98cloud-final | インスタンスIDごとの初回起動 | /var/lib/cloud/instances/インスタンスID/scripts/ |
| runcmd(cloud-init) | /etc/rc3.d/S98cloud-final | インスタンスIDごとの初回起動 | /var/lib/cloud/instances/インスタンスID/scripts/ |
| /var/lib/cloud/scripts/per-once/* | /etc/rc3.d/S98cloud-final | インスタンスIDに限らず1回のみ | - |
| /var/lib/cloud/scripts/per-instances/* | /etc/rc3.d/S98cloud-final | インスタンスIDごとの初回起動 | - |
| bootcmd(cloud-init) | /etc/rc3.d/S51cloud-init | 起動都度 | ???(どこにファイルが作成されているのかわからない。。。) |
| /var/lib/cloud/scripts/per-boot/* | /etc/rc3.d/S98cloud-final | 起動都度 | - |

なお、/var/lib/cloud/scripts/per-once/配下などへのスクリプトの配置は以下のようにcloud-initのwrite-files（/etc/rc3.d/S51cloud-init）によって行うことでS98cloud-finalでの実行前となり、初回起動時でも実行が可能となります。

```
#cloud-config
write_files:
 - content: |
      #!/bin/sh
      echo "Per-Once:" `date` >> /var/tmp/test.txt
      sleep 3s
   owner: root:root
   path: /var/lib/cloud/scripts/per-once/once.sh
   permissions: '0755'
```
# シェルスクリプト

EC2起動時のUserDataに記述が可能です。
先頭に「#!/bin/bash」を記載し、以降にbashスクリプトを書くことで指定が可能です。

例えば

```lang:user-data.sh
#!/bin/bash

touch /home/ec2-user/cloud-init
```

というのを作り、

```
$aws ec2 run-instances --image-id  ami-hogefuga --instance-type t2.micro --subnet-id subnet-xxxx --security-group-ids sg-xxxxx --key-name hogefuga --user-data file://user-data.sh
```

と実行すると起動時後に`/home/ec2-user/cloud-init`というファイルが出来ている事が確認できます。

なお、実行時のログは`/var/log/cloud-init.log`に出来ています。

# [シェルスクリプト]ファイルの場所

以下に存在しています。なお、i-c834fd3aとなっている部分はインスタンスIDなので起動したものによって異なります。

```
$sudo cat /var/lib/cloud/instances/i-c834fd3a/scripts/part-001
#!/bin/bash

touch /home/ec2-user/cloud-init
```

# [シェルスクリプト]実行タイミング

一体このスクリプトはいつ実行されるのかという疑問が出ます。
というかそもそもこれってどうやって実行されているのかという話がありますが、**cloud-init**の中で実行されています。

[Amazon Linuxのcloud-initの実行順番を確認する](http://dev.classmethod.jp/cloud/aws/probe-cloud-init-exec-timing/)

上記のリンクの下の方に記載されているように**/var/lib/cloud/instances/インスタンスID/scripts/**はcloud-initのscripts-userモジュールによって実行されるようです。

そのscript-userモジュールはいつ実行されるかというのは以下を確認すれば分かります。

```lang:/etc/cloud/cloud.cfg.d/00_defaults.cfg
# ### DO NOT MODIFY THIS FILE! ###
# This file will be replaced if cloud-init is upgraded.
# Please put your modifications in other files under /etc/cloud/cloud.cfg.d/
#
# Note that cloud-init uses flexible merge strategies for config options
# http://cloudinit.readthedocs.org/en/latest/topics/merging.html

# The top level settings are used as module
# and system configuration.

# A set of users which may be applied and/or used by various modules
# when a 'default' entry is found it will reference the 'default_user'
# from the distro configuration specified below
users:
  - default

ssh_pwauth: false

# Example datasource config
# datasource:
#   Ec2:
#     metadata_urls: [ 'blah.com' ]
#     timeout: 5 # (defaults to 50 seconds)
#     max_wait: 10 # (defaults to 120 seconds)

locale_configfile: /etc/sysconfig/i18n
mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
resize_rootfs: noblock
resize_rootfs_tmp: /dev
ssh_deletekeys: true
ssh_genkeytypes: [ 'rsa', 'dsa', 'ecdsa' ]
syslog_fix_perms: ~

# The modules that run in the 'init' stage
cloud_init_modules:
  - migrator
  - rsyslog
  - bootcmd
  - write-files
  - write-metadata
  - growpart
  - resizefs
  - set-hostname
  - update-hostname
  - update-etc-hosts
  - users-groups
  - ssh

# The modules that run in the 'config' stage
cloud_config_modules:
  - mounts
  - locale
  - set-passwords
  - yum-configure
  - yum-add-repo
  - package-update-upgrade-install
  - timezone
  - puppet
  - disable-ec2-metadata
  - runcmd

# The modules that run in the 'final' stage
cloud_final_modules:
  - scripts-per-once
  - scripts-per-boot
  - scripts-per-instance
  - scripts-user
  - ssh-authkey-fingerprints
  - keys-to-console
  - phone-home
  - final-message
  - power-state-change

# System and/or distro specific settings
# (not accessible to handlers/transforms)
system_info:
  # This will affect which distro class gets used
  distro: amazon
  distro_short: amzn
  # Default user name + that default users groups (if added/used)
  default_user:
    name: ec2-user
    lock_passwd: true
    gecos: EC2 Default User
    groups: [ wheel ]
    sudo: [ "ALL=(ALL) NOPASSWD:ALL" ]
    shell: /bin/bash
  # Other config here will be given to the distro class and/or path classes
  paths:
    cloud_dir: /var/lib/cloud/
    templates_dir: /etc/cloud/templates/
    upstart_dir: /etc/init/
  package_mirrors:
    - arches: [ i386, x86_64 ]
      search:
        regional:
          - repo.%(ec2_region)s.%(services_domain)s
          - repo.%(ec2_region)s.amazonaws.com
  ssh_svcname: sshd

# vim:syntax=yaml expandtab
```

上記では**cloud_final_modules**という記載の下にscript-userモジュールが記載されているのが確認できます。また、

```
# The modules that run in the 'final' stage
```

というコメントが有り、cloud init のfinalステージのタイミングで実行される事が分かります。

cloud init のfinalステージはいつ実行されるのかというと

```
$ll /etc/rc3.d/ |grep cloud
lrwxrwxrwx 1 root root 26 Mar 18 15:30 S50cloud-init-local -> ../init.d/cloud-init-local
lrwxrwxrwx 1 root root 20 Mar 18 15:30 S51cloud-init -> ../init.d/cloud-init
lrwxrwxrwx 1 root root 22 Mar 18 15:30 S52cloud-config -> ../init.d/cloud-config
lrwxrwxrwx 1 root root 21 Mar 18 15:30 S98cloud-final -> ../init.d/cloud-final
```

となっており、S98のタイミングで実行される事がわかります。
上記よりdeamon起動後に実行されることが分かります。

# [シェルスクリプト]注意点

+ rootユーザーで実行される
+ 実行はdeamon起動後(S98cloud-finalで実行される)
+ 上記はインスタンスIDごとの初回起動時のみ実行される

# cloud-init

[Cloud-init](http://cloudinit.readthedocs.org/en/latest/index.html)

+ EC2起動時のUserDataに記述が可能
+ 先頭に「#cloud-config」と記載し、以降に処理を記載
+ シェルスクリプトではなく、独自のYAMLで記載

以下にnginxをインストールして、その後、サービスを起動させる処理を記載します。

```
#cloud-config

packages:
 - nginx

runcmd:
 - service nginx start
 - chkconfig nginx on
```

実行時のログは`/var/log/cloud-init.log`に出来ています。なお、cloud-initの記述エラーの場合でもインスタンスの起動は実行されますが、処理が実行されず、上記ログにエラーの記載があります。

# [cloud-init]再起動時に実行されるもの、されないものを確認する

以下で試されているスクリプトを実行し、初回起動時と2回目の起動のタイミングでどのような差分が出るか確認します。

[Amazon Linuxのcloud-initの実行順番を確認する](http://dev.classmethod.jp/cloud/aws/probe-cloud-init-exec-timing/)

初回起動時
```
BootCmd: Mon Jul 6 07:46:55 UTC 2015
Per-Once: Mon Jul 6 07:48:06 UTC 2015
Per-Boot: Mon Jul 6 07:48:09 UTC 2015
Per-Instance: Mon Jul 6 07:48:12 UTC 2015
User-Data: Mon Jul 6 07:48:15 UTC 2015
RunCmd: Mon Jul 6 07:48:18 UTC 2015
```

再起動後
```
$cat /var/tmp/test.txt
BootCmd: Mon Jul 6 07:56:19 UTC 2015
Per-Boot: Mon Jul 6 07:57:07 UTC 2015
```

bootcmdで指定したものと/var/lib/cloud/scripts/per-boot/配下に配置したスクリプト以外は再起動時には実行されないことが分かりました。