Auto ScalingさせたEC2インスタンスのOSホスト名を連番にしてみた

ユーザーデータで頑張ればOSホスト名を連番にできる
2023.09.21

のっぴきならない事情でOSのホスト名を連番にする必要がある

こんにちは、のんピ(@non____97)です。

皆さんはAuto ScalingさせたEC2インスタンスのOSホスト名を連番にしたいなと思ったことはありますか? 私はちょっとだけあります。

Auto ScalingさせるEC2インスタンスにOSホスト名を連番にする必要あるの?」や「一意にしたいのであれば末尾にIPアドレスやインスタンスIDを付与すれば良いんじゃない?」、「そもそもOSのホスト名なんて使わないし、デフォルトで良くない?」など色々ご意見あると思いますが、のっぴきならない事情でOSのホスト名を連番にする必要があります。

今回はユーザーデータで頑張ります。

EC2インスタンスが追加されたことをトリガーにStep FunctionsとSSM Run Commandでも対応できそうでしたが、ユーザーデータで頑張るのとあまり労力が変わらない & 関連リソースが増えるので、ユーザーデータで対応しました。

いきなりまとめ

  • ユーザーデータで頑張ればAuto Scalingで同時に起動したEC2インスタンスのOSホスト名を連番にすることができる
  • リトライ間隔やリトライ回数は起動させたいEC2インスタンスの台数によって調整

使用するユーザーデータ

ユーザーデータは各EC2インスタンスで実行するため、各EC2インスタンスがホスト名が重複しないように処理してあげる必要があります。Auto Scalingなので、同時に複数台のEC2インスタンスが起動することもあるでしょう。

排他処理のためにDynamoDBか何かでロックの取得/解放/更新を実装するのも大袈裟な気するので、ホスト名が一意である確証が得られるまでホスト名を計算し直す方式で頑張ります。

使用したユーザーデータは以下のとおりです。

#!/bin/bash

set -ue

# Redirect /var/log/user-data.log and /dev/console
exec > >(tee /var/log/user-data.log | logger -t user-data -s 2>/dev/console) 2>&1

declare -r max_retry_interval=8                       # リトライ間隔の最大値 ループする度に0から指定した値までランダムな値でsleepする
declare -r max_retries=16                             # リトライ回数の上限
declare -r hostname_prefix=web-                       # ホスト名のプレフィックス
declare -r hostname_domain=corp.non-97.net            # ホスト名のドメイン
declare -r filter_tag_key=aws:autoscaling:groupName   # ホスト名を連番にしたいEC2インスタンスをフィルターする際に使用するタグのキー
declare -r filter_tag_value=asg                       # ホスト名を連番にしたいEC2インスタンスをフィルターする際に使用するタグの値

# インスタンスIDの取得
token=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
instance_id=$(curl -s -H "X-aws-ec2-metadata-token: $token" "http://169.254.169.254/latest/meta-data/instance-id")

for i in $(seq 1 $max_retries); do
  echo "======================================================="

  # ホスト名を連番にしたいEC2インスタンスに設定されているホスト名の配列
  hostname_list=($(aws ec2 describe-instances \
    --filters Name=tag:$filter_tag_key,Values=$filter_tag_value \
      Name=instance-state-code,Values=0,16 \
    --query "Reservations[].Instances[].[Tags[?Key=='HostName'].Value][]" \
    --output text \
    | grep $hostname_prefix \
    | sort -n
  ))

  # 設定するホスト名の連番の候補
  candidate_hostname_number="1"
  
  for hostname in "${hostname_list[@]}"; do
    # ホスト名の末尾の数字を取得
    hostname_number=$(echo $hostname | grep -o -E '[0-9]+$')
    
    echo "--------------------------------------------------"
    echo candidate_hostname_number : $candidate_hostname_number
    echo hostname : $hostname
    echo hostname_number : $hostname_number

    # ホスト名の末尾の数値 と 設定するホスト名の連番の候補 が異なるか
    if [[ $((10#$hostname_number)) -ne $candidate_hostname_number ]]; then
      # 異なる場合は空いているホスト名と判断
      break
    fi
    # 同じである場合は、設定するホスト名の連番の候補を変更する
    candidate_hostname_number=$(($candidate_hostname_number+1))
  done
  
  # 設定するホスト名の候補
  candidate_hostname=$(printf "%s%02d" $hostname_prefix $candidate_hostname_number)
  
  echo "--------------------------------------------------"
  echo candidate_hostname : $candidate_hostname

  # ホスト名の候補をHostNameタグに割り当て
  aws ec2 create-tags \
    --resources $instance_id \
    --tags Key=HostName,Value=$candidate_hostname

  # 同じHostNameタグを割り当てたEC2インスタンスのIDを取得
  instance_ids=($(aws ec2 describe-instances \
    --filters Name=tag:$filter_tag_key,Values=$filter_tag_value \
      Name=tag:HostName,Values=$candidate_hostname \
      Name=instance-state-code,Values=0,16 \
    --query "Reservations[].Instances[].[InstanceId]" \
    --output text
  ))
  
  echo "--------------------------------------------------"

  # 自身が設定したホスト名が一意であるか
  # = 取得したEC2インスタンスIDが自身のEC2インスタンスIDである かつ EC2インスタンスIDの配列の長さが1
  if [[ "${instance_ids[0]}" == "$instance_id" && "${#instance_ids[@]}" == 1 ]]; then
    # OSのホスト名を設定し、ループを抜ける
    echo "Set HostName ${candidate_hostname}.${hostname_domain}"
    hostnamectl set-hostname "${candidate_hostname}.${hostname_domain}"
    
    echo "hostnamectl :
      $(hostnamectl)"
    break
  else
    # ホスト名が重複していると判断
    # HostNameタグを削除
    aws ec2 delete-tags \
      --resources $instance_id \
      --tags Key=HostName

    # リトライ間隔を計算し、計算した秒数sleep
    retry_interval=$(($RANDOM % $max_retry_interval))
    echo "Failed to allocate hostname $candidate_hostname, retrying in $retry_interval seconds..."
    sleep $retry_interval
  fi
done

# 最大リトライ回数に達してもホスト名が決まらない場合
if [[ $i == $max_retries ]]; then
  echo "Failed to allocate a unique hostname after $max_retries retries. Please manually assign a hostname."
fi

今回はAuto ScalingするEC2インスタンスということで、起動中(pending)もしくは起動済み(running)のEC2インスタンス間で連番となるようにしました。

実行状態のステータスコードは以下AWS公式ドキュメントに記載があります。

The valid values for instance-state-code will all be in the range of the low byte and they are:

  • 0 : pending
  • 16 : running
  • 32 : shutting-down
  • 48 : terminated
  • 64 : stopping
  • 80 : stopped

InstanceState - Amazon Elastic Compute Cloud

EC2インスタンスのライフサイクルは以下のとおりです。

instance_lifecycle

抜粋 : インスタンスのライフサイクル - Amazon Elastic Compute Cloud

停止済みのEC2インスタンスも考慮してOSホスト名を連番にしたい場合は、6480も追加してフィルターすると良いでしょう。

動作確認

同時に10台起動する場合

実際に動作確認をしてみます。

動作確認を行う検証環境は全てAWS CDKでデプロイしました。使用したコードは以下リポジトリに保存しています。

デプロイ後、EC2インスタンスを確認します。

Auto Scaling Groupに属するEC2インスタンスのHostName

10台立ち上がっていますね。web-02というHostNameタグが4台重複しています。もう少し見守ってあげます。

Auto Scaling Groupに属するEC2インスタンスのHostName2

リロードすると、一部EC2インスタンスのHostNameタグはweb-01web-06と連番になっていますね。

HostNameが連番になっていることを確認

また、リロードするとEC2インスタンスのHostNameタグがweb-01web-10と連番になりました。やったぜ。

各EC2インスタンスに接続してログを確認してみます。

まず、web-01のEC2インスタンスです。

$ cat /var/log/user-data.log
=======================================================
--------------------------------------------------
candidate_hostname : web-01
--------------------------------------------------
Failed to allocate hostname web-01, retrying in 3 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname : web-01
--------------------------------------------------
Set HostName web-01.corp.non-97.net
hostnamectl :
       Static hostname: web-01.corp.non-97.net
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: ec2340b8d1c240c93d7f87fd8e188ee9
         Boot ID: 71d79e831671491b931021d2f2353cd0
  Virtualization: amazon
Operating System: Amazon Linux 2023
     CPE OS Name: cpe:2.3:o:amazon:amazon_linux:2023
          Kernel: Linux 6.1.49-70.116.amzn2023.x86_64
    Architecture: x86-64
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.nano
Firmware Version: 1.0

一度web-01を設定しようとしたところ、重複したようですね。3秒sleepした後、再度計算した結果web-01が空いていたので、最終的にweb-01を割り当てたことが分かります。

web-02のEC2インスタンスのログも確認します。

$ cat /var/log/user-data.log
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname : web-02
--------------------------------------------------
Failed to allocate hostname web-02, retrying in 4 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname : web-02
--------------------------------------------------
Failed to allocate hostname web-02, retrying in 5 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname : web-05
--------------------------------------------------
Failed to allocate hostname web-05, retrying in 7 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-05
hostname_number : 05
--------------------------------------------------
candidate_hostname_number : 6
hostname : web-07
hostname_number : 07
--------------------------------------------------
candidate_hostname : web-06
--------------------------------------------------
Failed to allocate hostname web-06, retrying in 1 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-05
hostname_number : 05
--------------------------------------------------
candidate_hostname_number : 6
hostname : web-07
hostname_number : 07
--------------------------------------------------
candidate_hostname : web-06
--------------------------------------------------
Failed to allocate hostname web-06, retrying in 2 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-05
hostname_number : 05
--------------------------------------------------
candidate_hostname_number : 6
hostname : web-07
hostname_number : 07
--------------------------------------------------
candidate_hostname : web-06
--------------------------------------------------
Failed to allocate hostname web-06, retrying in 4 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname : web-02
--------------------------------------------------
Set HostName web-02.corp.non-97.net
hostnamectl :
       Static hostname: web-02.corp.non-97.net
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: ec28dd999e2c6c0b08e8a2848f2a592a
         Boot ID: 9f624d17e6294699b319ab1035249f10
  Virtualization: amazon
Operating System: Amazon Linux 2023
     CPE OS Name: cpe:2.3:o:amazon:amazon_linux:2023
          Kernel: Linux 6.1.49-70.116.amzn2023.x86_64
    Architecture: x86-64
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.nano
Firmware Version: 1.0

こちらは6回りトライしていました。EC2インスタンスの同時起動台数が多い場合はmax_retry_intervalmax_retriesの値を大きくする必要がありそうです。

web-10のログも確認します。

$ cat /var/log/user-data.log
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname : web-02
--------------------------------------------------
Failed to allocate hostname web-02, retrying in 7 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-05
hostname_number : 05
--------------------------------------------------
candidate_hostname_number : 6
hostname : web-06
hostname_number : 06
--------------------------------------------------
candidate_hostname_number : 7
hostname : web-07
hostname_number : 07
--------------------------------------------------
candidate_hostname_number : 8
hostname : web-08
hostname_number : 08
--------------------------------------------------
candidate_hostname_number : 9
hostname : web-09
hostname_number : 09
--------------------------------------------------
candidate_hostname : web-10
--------------------------------------------------
Set HostName web-10.corp.non-97.net
hostnamectl :
       Static hostname: web-10.corp.non-97.net
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: ec2adc0def4d78c7982af3b6edc1de50
         Boot ID: 88e4579bb7b3400caae66a206e269caa
  Virtualization: amazon
Operating System: Amazon Linux 2023
     CPE OS Name: cpe:2.3:o:amazon:amazon_linux:2023
          Kernel: Linux 6.1.49-70.116.amzn2023.x86_64
    Architecture: x86-64
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.nano
Firmware Version: 1.0

こちらは1回のリトライで確定できたようです。

複数台のEC2インスタンスを削除した場合

次に、EC2インスタンスを数台削除してみて、削除して抜けた穴のHostNameが埋められるかどうか確認します。

web-01web-3web-06web-07のEC2インスタンスを削除します。

いくつかのEC2インスタンスを削除する

シャットダウン中になりました。

いくつかのEC2インスタンスを削除する2

少し待つと、EC2インスタンスが2台立ち上がってきました。

EC2インスタンスが起動してきたことを確認

何回かリロードすると、追加したEC2インスタンスにHostNameタグが割当てられました。web-01web-02が割り当てられていますね。

追加されたEC2インスタンスにHostNameが設定されたことを確認

また、もう少し待つと、残りの2台のEC2インスタンスも立ち上がり、web-06web-07が割り当てられました。問題なく動作していますね。

残りの追加されたEC2インスタンスにHostNameが設定されたことを確認

Auto Scaling Groupのアクティビティ履歴は以下のとおりです。

AutoScaling Groupのアクティビティ履歴

ログを確認してみましょう。

web-02のログです。

$ cat /var/log/user-data.log
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname : web-02
--------------------------------------------------
Failed to allocate hostname web-02, retrying in 4 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname : web-02
--------------------------------------------------
Failed to allocate hostname web-02, retrying in 5 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname : web-05
--------------------------------------------------
Failed to allocate hostname web-05, retrying in 7 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-05
hostname_number : 05
--------------------------------------------------
candidate_hostname_number : 6
hostname : web-07
hostname_number : 07
--------------------------------------------------
candidate_hostname : web-06
--------------------------------------------------
Failed to allocate hostname web-06, retrying in 1 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-05
hostname_number : 05
--------------------------------------------------
candidate_hostname_number : 6
hostname : web-07
hostname_number : 07
--------------------------------------------------
candidate_hostname : web-06
--------------------------------------------------
Failed to allocate hostname web-06, retrying in 2 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-02
hostname_number : 02
--------------------------------------------------
candidate_hostname_number : 3
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname_number : 4
hostname : web-04
hostname_number : 04
--------------------------------------------------
candidate_hostname_number : 5
hostname : web-05
hostname_number : 05
--------------------------------------------------
candidate_hostname_number : 6
hostname : web-07
hostname_number : 07
--------------------------------------------------
candidate_hostname : web-06
--------------------------------------------------
Failed to allocate hostname web-06, retrying in 4 seconds...
=======================================================
--------------------------------------------------
candidate_hostname_number : 1
hostname : web-01
hostname_number : 01
--------------------------------------------------
candidate_hostname_number : 2
hostname : web-03
hostname_number : 03
--------------------------------------------------
candidate_hostname : web-02
--------------------------------------------------
Set HostName web-02.corp.non-97.net
hostnamectl :
       Static hostname: web-02.corp.non-97.net
       Icon name: computer-vm
         Chassis: vm 🖴
      Machine ID: ec28dd999e2c6c0b08e8a2848f2a592a
         Boot ID: 9f624d17e6294699b319ab1035249f10
  Virtualization: amazon
Operating System: Amazon Linux 2023
     CPE OS Name: cpe:2.3:o:amazon:amazon_linux:2023
          Kernel: Linux 6.1.49-70.116.amzn2023.x86_64
    Architecture: x86-64
 Hardware Vendor: Amazon EC2
  Hardware Model: t3.nano
Firmware Version: 1.0

譲り合いをした結果、6回リトライしていますね。

ユーザーデータで頑張ればOSホスト名を連番にできる

Auto ScalingさせたEC2インスタンスのOSホスト名を連番にしてみました。

5回ぐらい動作確認をしましたが、いずれも最終的にOSホスト名の重複したり、OSホスト名が歯抜けになったりということは発生しませんでした。リトライ間隔やリトライ回数は起動させたいEC2インスタンスの台数によって調整すると良いと思います。

他にも「こんな実装方法があるよ」など情報あれば共有お待ちしています。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!