はじめに
Rancher環境にAWSのスポットインスタンスでホストを追加したいときってありますよね。
でもスポットインスタンスはいつ消されるかわからない環境なので、単体でRancherのホストとして使っていてもいつかきっと意図しないタイミング消されます。
そんなことが無いように今回はスポットインスタンスをAuto Scaling Groupに関連付け、かつRancher環境に自動でホストとして登録してみましょう。(しかもTerraformで!)
構成
構成としては以下のようになります。
TerraformでAuto Scaling Groupを作成しつつ、User-Dataを使ってRancher Server(今回はtry.rancher.com)に自動登録します。

コードについて
今回使用するコードの完成形はこちらに置いています。
また、このterraform内で使用しているmoduleもこちらに置いています。
これらのコードはほぼほぼgreensheep/terraform-aws-rancher-hostsから作っています。
変更している点としては主に、
- terraformで自前で作ったrancher-serverがあることを前提にしないようにした
- ↑の関係でホスト削除の際にSQSでrancher-serverにホスト自動削除依頼がされない
- ↑の関係でSQSが必要なくなるのでIAMリソースの作成もしない
- 立ち上げるAMIはRancherOSにしている
ことです。
AWSにTerraformで作るリソース
セキュリティグループ(aws_security_group)
Rancherに追加するホストたちが使用するセキュリティグループを作ります。IPSecのためにUDPの500と4500を開けておきます。
オートスケールのLaunch Configuration(aws_launch_configuration)
オートスケールに使用するLaunch Configurationを作ります。
主にEC2の情報になります。ポイントとしては、先に作っておいたセキュリティグループのIDを使うようにすることと、スポットインスタンスであればspot_priceで希望する価格を記載することです。
AMIの最新に関してはこちらで確認が可能なので、適宜見ておきましょう。
Auto Scaling Group(aws_autoscaling_group)
オートスケールに使うAuto Scaling Groupです。
ここでどれだけのインスタンス数にするかを決めます。
User Dataを使ったホスト(EC2)のRancherへの自動登録
Terraformのaws_launch_configurationにはuser_dataを渡すことができます。
EC2の初期化時にこれが発火するのですが、RancherOSであればここにcloud-configを渡すことができます。
公式ドキュメントの「Running RancherOS on AWS」にも3番で述べられています(ファイルでも良いしテキスト渡してもいい)。
さて、自動登録するためにやりたいことは以下のとおりです。
- rancher-serverの対象Environmentから
registrationTokenを取得 - EC2のメタ情報を取得(IPアドレスなど)
- 登録コマンド発火して
rancher-agentを起動
これをやるためにはcurlが欲しくなりますが、残念ながらRancherOSにはcurlがありません。
そこでcurlだけが入ったコンテナ(supersoftware/curl)を使ってコマンド発火させることにしました。
注意点として、これにはコンテナを使うため、dockerが使えないといけません。
cloud-configにはruncmdという場所でスクリプトを実行できるのですが、ここで書くコマンドはdockerが使えるようになる前に実行されますので、この中でdockerを使うと必ず失敗します。
そういうときの為に/etc/rc.localにスクリプトを書いておく手法があります。
これはdockerの起動状態を気にせずに発火するスクリプトなので、ここでwait-for-dockerという特別なコマンドを使うことで、必ずdockerが使える状態を保証することができます。
cloud-configを以下のように記述することで/etc/rc.localを作成することができます。
#cloud-config
write_files:
- path: /etc/rc.local
permissions: "0755"
owner: root
content: |
#!/bin/bash
wait-for-docker
# Setup initial vars
serverUrl=https://${environment_access_key}:${environment_secret_key}@${server_hostname}
projectId=${environment_id}
curlCmd="docker run --rm supersoftware/curl"
# Make initial POST request for a registration token and record the id
response=$($curlCmd -s -X POST $serverUrl/v1/registrationtokens?projectId=$projectId)
requestId=$(echo $response | jq -r '.id')
requestState=$(echo $response | jq -r '.state')
# The registration token request is async so keep checking until it's complete
while [[ "$requestState" != "active" ]]; do
sleep 2
response=$($curlCmd -s $serverUrl/v1/registrationtokens/$requestId)
requestState=$(echo $response | jq -r '.state')
done
# Get the instance id from metadata
instanceId=$($curlCmd -s http://169.254.169.254/latest/meta-data/instance-id)
instancePrivateIp=$($curlCmd -s http://169.254.169.254/latest/meta-data/local-ipv4)
# Labels
instanceLabels="HOSTID=$instanceId&CLOUD=aws&CLUSTER=${cluster_name}"
customLabels="${cluster_instance_labels}"
if [ -n "$customLabels" ]; then
instanceLabels="$instanceLabels&$customLabels"
fi
# Add external DNS label if there's a public IP address
instancePublicIp=$($curlCmd -f -s http://169.254.169.254/latest/meta-data/public-ipv4)
if [ -n "$instancePublicIp" ]; then
instanceLabels="$instanceLabels&io.rancher.host.external_dns_ip=$instancePublicIp"
fi
# Use the command in the response to start the rancher agent
cmd=$(echo $response | jq -r '.command')
# add environment variables when launching the agent
eval $${cmd/sudo docker run /sudo docker run -e CATTLE_AGENT_IP=$instancePrivateIp -e CATTLE_HOST_LABELS=\"$instanceLabels\" }
YAMLに慣れてる人はすぐ気付くと思いますが、公式ドキュメント通りに記述すると…
#cloud-config
rancher:
write_files:
- path: /etc/rc.local
permissions: "0755"
owner: root
content: |
#!/bin/bash
wait-for-docker
このように、write_filesの下の- path:はインデントしています。
これは不正なので、インデントを無くしてください(これのせいでかなり悩まされました…)。
もう一つ、上のデータはTerraformのtemplate(userdata.template)として用意しています。
なので、terraformが埋める変数がそのままの記述(e.g. ${environment_key})になっています。
これはterraform applyする際にrenderedされ、自分で設定した値に置換されるようになっています。
デプロイ!
supersoftware/tf-aws-try-rancher-hostsからterraformのコードcloneし、必要な情報を埋めてterraform plan → terraform applyしてみましょう。try.rancher.comの指定したEnvironmentにホストが自動で登録されるはずです。
おわりに
Rancherにホストをオートスケールで自動登録する方法についてご紹介しました。
実際はスポットインスタンスが停止するときにRancher Serverに連絡してホストを削除するようにしないと、お亡くなりになったホストがRancher管理下に残り続けます。
そこら辺はオートスケールのライフサイクルフックをうまく駆使し、本番では必ずお掃除を心掛けましょう。




