cloud-init を使って自動的に hostname の変更と、Private Route53 への登録を行う

やりたいこと: EC2 インスタンスの Name タグの値を取得し、hostname の変更と、Route53 への登録を cloud-init を使って起動時に自動的に行いたい。

## 前提条件

1. AWS CLI がセットアップされていること。See http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html
2. cloud init がインストールされていること (Amazon Linux ならデフォルト。CentOS の場合は yum install -y epel-release && yum install -y cloud-init)
3. [jq](http://stedolan.github.io/jq/) コマンドをインストールしていること。/usr/local/bin/jq においたものとする。

## 方針

AWS の User-Data を毎回設定するのがダルいので、使わない。スクリプトを仕込んだ状態でイメージを作っておき、以降自動で反映されるようにする

## 設定

/etc/cloud/cloud.cfg の hostname を更新する行をコメントアウトする。かき消されてしまうため。

```
cloud_init_modules:
  - migrator
  - bootcmd
  - write-files
  - growpart
  - resizefs
#  - set_hostname
#  - update_hostname
#  - update_etc_hosts
  - rsyslog
  - users-groups
  - ssh
```

bootcmd にスクリプトを仕込む。cfg に直接コマンドを書いても良いが、シェルスクリプトに分離したほうが開発が楽。

```yaml:/etc/cloud/cloud.cfg.d/set_hostname.cfg
bootcmd:
  - /etc/cloud/cloud.cfg.d/set_hostname.sh
```

本体のシェルスクリプト。

```bash:/etc/cloud/cloud.cfg.d/set_hostname.sh
#!/bin/bash
jq=/usr/local/bin/jq
aws=/usr/local/bin/aws
hosted_zone_id=XXXXXXXX
domain=xxxx.internal

# /root/.aws/credentials および /root/.aws/config があれば不要
# export AWS_ACCESS_KEY_ID=xxxxx
# export AWS_SECRET_ACCESS_KEY=xxxx
# export AWS_DEFAULT_REGION=xxxxx

instance_id=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
json=$($aws ec2 describe-instances --instance-ids $instance_id)
private_ip_address=$(echo $json | $jq -r '.Reservations[0].Instances[0].PrivateIpAddress')
hostname=$(echo $json | $jq -r '.Reservations[].Instances[].Tags[] | select(.Key=="Name").Value')

if [ -z "$hostname" ]; then
  echo "Name tag is not set" 1>&2
  exit 1
fi

# set hostname
if [ -z $(grep ^HOSTNAME /etc/sysconfig/network) ]; then
  echo "HOSTNAME=$hostname" >> /etc/sysconfig/network
else
  sed -i "s/^HOSTNAME.*$/HOSTNAME=$hostname/" /etc/sysconfig/network
fi
hostname $hostname
echo "\$ cat /etc/sysconfig/network"
cat /etc/sysconfig/network

# register to Route53
cat > /etc/cloud/cloud.cfg.d/set_hostname.json <<EOF
{
  "Comment": "${hostname}",
  "Changes": [
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "${hostname}.${domain}",
        "Type": "A",
        "TTL" : 300 ,
        "ResourceRecords": [
         {
           "Value": "${private_ip_address}"
         }
        ]
      }
    }
  ]
}
EOF
echo "\$ cat /etc/cloud/cloud.cfg.d/set_hostname.json"
cat /etc/cloud/cloud.cfg.d/set_hostname.json
echo "\$ $aws route53 change-resource-record-sets --hosted-zone-id $hosted_zone_id --change-batch file:////etc/cloud/cloud.cfg.d/set_hostname.json"
$aws route53 change-resource-record-sets --hosted-zone-id $hosted_zone_id --change-batch file:////etc/cloud/cloud.cfg.d/set_hostname.json
```

## ハマったメモ

ログインしてシェルからスクリプトを実行すると成功するのに、cloud-init で boot プロセス時に処理させると、aws ec2 describe-instances で

```
A client error (AuthFailure) occurred when calling the DescribeInstances operation: AWS was not able to validate the provided access credentials
Name tag is not set
2015-08-10 15:58:41,538 - util.py[WARNING]: Failed to run bootcmd module bootcmd
2015-08-10 15:58:41,540 - util.py[WARNING]: Running bootcmd (<module 'cloudinit.config.cc_bootcmd' from '/usr/lib/python2.6/site-packages/cloudinit/config/cc_bootcmd.pyc'>) failed
```

とエラーが出ていた。cf. https://gist.github.com/sonots/5d7b6ff1bff5b78cb2bb

これは時刻設定が間違っていて、hwclock がズレているせいだった。aws cli は時刻があっていないと認証に失敗する。エラーメッセージから全くわからなくてハマってた。

自分の場合は、/etc/sysconfig/clock と /etc/localtime は設定していたが、/etc/adjtime (勝手に設定されると書いている文献もある)の設定がズレており、時刻がズレてしまっていた。

参考

* 再起動で時刻がズレる場合の対処法 http://www.server-memo.net/server-setting/ntp/ntp-time-shift.html
* パソコンの時計　ハードウェア クロックとシステム クロック http://park12.wakwak.com/~eslab/pcmemo/clock/clock3.html
* システム言語の設定ファイル http://webbox0120.com/wb-os/linux-conf.html
* Linuxの時間管理の仕組み http://itblog.posifeel.com/2013/09/26/linux-date-system/