一部のサブシステムの構築で、プロビジョニングツールを捨ててみた。じゃあどうするのかというとシェルスクリプトでやる。今回はこのやりかたが一番楽できるような気がしたので試している。
具体的にはPackerからシェルスクリプトとServerspecを実行してAMIを煮込む。おいしくできあがったらそいつから構築。もしミドルウェアより下の層のコンフィグ類に変更があったらまた煮込む。構築する。新しい方に切り替える。つまり”捨てるインフラ”にする。
プラットフォームはAWS。
(追記)ちなみにchefなどのプロビジョニングツールがめんどくさいからシェルスクリプトにしたというよりは、捨てる前提のサーバだからシェルスクリプトでの構築も選択肢として出てきたということです。ただ自分個人の嗜好としてchefはもう飽きたというのも事実です。なお、オンプレだと同じサーバで継続してプロビジョニングすることになるのでchefのような寡等性を担保するものの恩恵を十二分に受けることができる。クラウドの場合、「捨てる」という選択肢があるのでプロビジョニングは最初の一回だけ成功すればよくてその場合シェルスクリプトで十分な場合「も」ある。というエントリ(追記ここまで)



Packer + シェルスクリプト + Serverspec

今回は以下の要件, 方針&ぼやき。

  • 要件:このサブシステムはトラフィックが少なく、機能も複雑ではないのでミドルウェアより下の層でやることはほとんどない。なのでシェルスクリプトでやっても大して面倒ではないはず。やることはnginxとnodejsと監視エージェントのパッケージをインストール -> コンフィグを撒く -> 必要なプロセスを起動…ってだけだ。
  • 方針:冒頭で述べた通りコンフィグ類の変更時は捨てる。横文字で言うとDisposable Infrastructureってやつ?
  • ぼやき:プロビジョニングツールに疲れた。
  • たぶん:chefわからん人は結構いるかもしれないけどシェルスクリプトだったら誰でもわかるだろう。もしかしたらこっちのやりかたの方が引き継ぎもしやすいかもしれないし、保守もしやすいかもしれない。
ちなみにpacker buildには15分くらいかかる。できればデプロイするときもインスタンスごと作りなおしてしまいたいんだが、毎デプロイごとに15分かかるってのはちょっと気分的にもやもやする。なのでデプロイについては同じインスタンスで継続的に行っている。

こんな感じ

以下のようにして作っている。

1
2
3
4
5
export AWS_ACCESS_KEY_ID=XXXXXX

export AWS_SECRET_ACCESS_KEY=XXXXXX

packer build template.json

template.jsonは次の通り。手を加えたOSのコンフィグ(assets/os_settings)、sshログインのための公開鍵とgithubのプライベートリポジトリからcloneするための鍵(assets/keys)、ミドルウェアのコンフィグ類(assets/XXXX/production)、パッケージ類(nodejsのrpm)、シェルスクリプト内で使う環境変数を記述したファイル(base.env)、serverspecをアップロードする。そしてシェルスクリプト(base.sh, production.sh, production_spec.sh)を実行する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
{
  "variables": {
    "aws_access_key": "{{env `AWS_ACCESS_KEY_ID`}}",
    "aws_secret_key": "{{env `AWS_SECRET_ACCESS_KEY`}}"
  },
  "builders": [{
    "access_key": "{{user `aws_access_key`}}",
    "secret_key": "{{user `aws_secret_key`}}",
    "region": "XXXX",
    "source_ami": "ami-XXXX",
    "vpc_id": "vpc-XXXX",
    "subnet_id": "subnet-XXXX",
    "type": "amazon-ebs",
    "instance_type": "XXXX",
    "ssh_username": "XXXX",
    "ami_users": ["XXXX"],
    "ami_name": "XXXX"
  }],
  "provisioners": [{
    "type": "file",
    "source": "assets/os_settings",
    "destination": "/tmp/os_settings"
  },
  {
    "type": "file",
    "source": "assets/keys",
    "destination": "/tmp/keys"
  },
  {
    "type": "file",
    "source": "assets/XXXX/production",
    "destination": "/tmp/production"
  },
  {
    "type": "file",
    "source": "assets/rpms/node-XXXX.x86_64.rpm",
    "destination": "/tmp/node-XXXX.x86_64.rpm"
  },
  {
    "type": "file",
    "source": "scripts/base.env",
    "destination": "/tmp/base.env"
  },
  {
    "type": "file",
    "source": "serverspec",
    "destination": "/tmp/serverspec"
  },
  {
    "type": "shell",
    "scripts": ["scripts/base.sh", "scripts/XXXX/production.sh", "scripts/XXXX/production_spec.sh"],
    "execute_command": "{{ .Vars }} sudo -E sh '{{ .Path }}'"
  }]
}

base.sh, production.sh, production_spec.sh

base.shはアプリケーション用のユーザの作成、タイムゾーン、ulimit、鍵、sudoersなど主にOSの設定を整えている。

production.shはnginx入れてコンフィグ撒く、nodejs入れてボワー、datadog入れてコンフィグ撒く、sensu入れてコンフィグ撒くという処理をする(下にコードを晒す)。少しトリッキーなのは、rc.localにヒアドキュメントを突っ込んでいる。これはインスタンス起動時にそのインスタンスのホスト名でSensuのコンフィグを生成したいため。

production_spec.shはuploadしたserverspecのディレクトリ(/tmp/serverspec)に移動してbundle installしてserverspecを実行する。

例になるが、production.shは次のような形になっている。やっていることはパッケージを入れて、アップロードしたコンフィグを撒いているだけであることがわかると思う。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
source /tmp/base.env

echo '### install nginx ###'
yum install -y nginx
install -o root -g root -m 0644 /tmp/production/nginx/nginx.conf /etc/nginx/
/etc/init.d/nginx start
chkconfig nginx on

echo '### delete default configs ###'
rm -f /etc/nginx/conf.d/default.conf
rm -f /etc/nginx/conf.d/example_ssl.conf

echo '### install nodejs ###'
yum install -y /tmp/node-XXXX.x86_64.rpm
/opt/node/bin/npm install -g bower
/opt/node/bin/npm install -g grunt-cli
/opt/node/bin/npm install -g forever

echo "### install datadog agent ###"
DD_API_KEY=XXXXX bash -c "$(curl -L https://raw.githubusercontent.com/DataDog/dd-agent/master/packaging/datadog-agent/source/install_agent.sh)"
install -o dd-agent -g root -m 0644 /tmp/production/datadog/nginx.yaml /etc/dd-agent/conf.d/
chkconfig datadog-agent on

echo '### install sensu-client ###'
install -o root -g root -m 0644 /tmp/production/sensu/sensu.repo /etc/yum.repos.d/
yum clean all
yum install -y sensu nmap
rm -f /etc/sensu/client.json
install -o root -g root -m 0644 /tmp/production/sensu/sensu /etc/default/
install -o root -g root -m 0644 /tmp/production/sensu/config.json /etc/sensu/
install -o root -g root -m 0755 /tmp/production/sensu/plugins/* /etc/sensu/plugins/
install -o root -g root -m 0755 -d /etc/sensu/ssl
install -o root -g root -m 0644 /tmp/production/sensu/ssl/* /etc/sensu/ssl/
/etc/init.d/sensu-client stop
chkconfig sensu-client off

echo '### edit rc.local to make the sensu client config after creating instance ###'
cat << "EOS" >> /etc/rc.local
$(cat << EOT > /etc/sensu/conf.d/client.json
{
  "client": {
    "name": "`hostname`",
    "address": "`hostname`",
    "subscriptions": ["common","http", "node"]
  },
  "keepalive": {
    "thresholds": {
      "critical": 60,
      "occurrence": 3
    },
  "refresh": 300
  }
}
EOT)

/etc/init.d/sensu-client start
chkconfig sensu-client on
EOS

rc.localのところがちょっと痒いが、この程度だったらシェルスクリプトで十分じゃないかな。運用が辛くなったらchefなりansibleなりitamaeなりをやっぱ導入すればいいし。まあそれで解決されるかはわからんけど。

コンテナは使わないのか?

コンテナも動向を追ったり検証してはいる。ちょっと今は他にやることがあって手が出しにくい。

そういえば

fluentd入れ忘れた。作りなおさなきゃ。

おわり

 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

Set your Twitter account name in your settings to use the TwitterBar Section.