はじめに
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管理下に残り続けます。
そこら辺はオートスケールのライフサイクルフックをうまく駆使し、本番では必ずお掃除を心掛けましょう。