User Data でインスタンスを初期化する

AWSBeginner
オンラインで実践に進む

はじめに

チームは、新しいレポートサーバーが毎回正しい挨拶メッセージで起動することを求めています。Bash の User Data スクリプトを作成し、EC2 インスタンスの起動時に渡して、アプリケーションの結果と起動ログを確認します。

EC2 インスタンスの起動と SSH 接続の方法は学習済みであることを前提とします。この新しい環境には独自のイメージ、ネットワーク、キーペアが用意されています。サーバーと起動設定は自分で作成します。

認定試験との関連

このラボでは、AWS Certified Cloud Practitioner CLF-C02 の分野 3 の試験目標のタスク 3.1 に関連する、プログラムによるリソース設定と起動の自動化を練習します。

起動時にアプリケーションを設定する

このステップでは起動スクリプトを作成し、そのスクリプトでインスタンスを起動して、アプリケーションの応答を確認します。

User Data は、起動時にインスタンスへ渡す情報です。Linux の起動スクリプトは、この情報を使ってソフトウェアを自動設定できます。デフォルトでは初回起動時に root として実行されるため、スクリプト内のコマンドに sudo は不要です。EC2 User Data の公式ガイドに、この起動動作とログの場所が説明されています。

作業ディレクトリから始めます。

cd /home/labex/project

用意されたイメージにはレポートアプリケーションが含まれています。イメージとネットワークの ID を現在のシェルに読み込みます。

source launch.env

ヒアドキュメントを使って user-data.sh を作成します。外側の SCRIPT マーカーはスクリプト全体を、内側の JSON マーカーはアプリケーション設定を区切ります。引用符で囲んだマーカーはテキストをそのまま保持します。先頭の #!/bin/bash は Bash を選択し、set -euo pipefail はコマンドの失敗や未設定変数の使用時にスクリプトを停止します。最後の echo は確認に役立つ起動ログを出力します。

cat > user-data.sh <<'SCRIPT'
#!/bin/bash
set -euo pipefail
cat > /etc/report-app/config.json <<'JSON'
{
  "message": "Started with User Data"
}
JSON
echo 'Report application configuration applied.'
SCRIPT

これでローカルファイルが作成されますが、インスタンスはまだ変更されません。--user-data file://user-data.sh を使って、このファイルを run-instances に渡します。AWS CLI がファイルを読み込んで API 用にエンコードするため、自分でエンコードする必要はありません。サーバーの名前を bootstrap-server にします。

aws ec2 \
  run-instances \
  --image-id "$AMI_ID" \
  --instance-type t3.micro \
  --subnet-id "$SUBNET_ID" \
  --security-group-ids "$SECURITY_GROUP_ID" \
  --key-name report-key \
  --count 1 \
  --user-data file://user-data.sh \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=bootstrap-server}]'

新しいインスタンス ID を保存します。フィルターは名前タグを選択し、$(...) はプレーンテキストの結果を取得します。

INSTANCE_ID=$(aws ec2 \
  describe-instances \
  --filters Name=tag:Name,Values=bootstrap-server \
  --query 'Reservations[0].Instances[0].InstanceId' \
  --output text)

状態とパブリックアドレスを確認します。

aws ec2 \
  describe-instances \
  --instance-ids "$INSTANCE_ID" \
  --query 'Reservations[].Instances[].{Instance:InstanceId,State:State.Name,PublicIPv4:PublicIpAddress}'

running を確認します。まだ pending の場合は、少し待って照会を繰り返します。実行中であることだけでは起動設定の成功を証明できません。次にアプリケーションを確認します。

AWS View を開き、Refresh resources をクリックして、Application requests で bootstrap-server を選択します。Check application をクリックし、HTTP 200 と Started with User Data を確認します。起動後に編集することなく、起動スクリプトでサーバーを設定できました。

AWS View で起動時に設定されたアプリケーションが HTTP 200 を返している

この例は期待される応答を示します。インスタンスとネットワークの ID は異なります。

SSH 接続用のパブリックアドレスを取得します。

PUBLIC_IP=$(aws ec2 \
  describe-instances \
  --instance-ids "$INSTANCE_ID" \
  --query 'Reservations[0].Instances[0].PublicIpAddress' \
  --output text)

提供された SSH 設定により、秘密鍵とラボのアクセス経路が選択されます。

ssh -F ssh_config ubuntu@"$PUBLIC_IP"

現在、アプリケーションのインスタンス内にいます。管理者権限で起動出力ログを読みます。

sudo cat /var/log/cloud-init-output.log

Report application configuration applied. を探します。これは渡したスクリプトの出力です。起動失敗を調査する際は、まずこのログのコマンドエラーを確認し、次にアプリケーション自体をテストします。インスタンスの状態だけで判断しないでください。

LabEx ターミナルに戻ります。

exit

インスタンスに保存された User Data も取得できます。この照会は Base64 値を選択し、パイプで base64 --decode に渡して元のスクリプトを表示します。

aws ec2 \
  describe-instance-attribute \
  --instance-id "$INSTANCE_ID" \
  --attribute userData \
  --query 'UserData.Value' \
  --output text | base64 --decode

Bash スクリプトと挨拶メッセージがファイルと一致することを確認します。User Data は通常、初回起動時にのみ実行されます。インスタンスを停止して再起動しても、この設定は自動的には繰り返されません。再現可能な初期設定に起動自動化を使い、スクリプトには認証情報を含めないでください。

初期化したサーバーを終了する

このステップでは、作成したインスタンスを削除して最終状態を確認します。

保存した変数で識別されるインスタンスだけを終了します。

aws ec2 \
  terminate-instances \
  --instance-ids "$INSTANCE_ID"

インスタンスが terminated になるまで待ちます。waiter は状態を繰り返し照会し、成功すると出力なしで終了します。

aws ec2 \
  wait instance-terminated \
  --instance-ids "$INSTANCE_ID"

最終状態を確認します。

aws ec2 \
  describe-instances \
  --instance-ids "$INSTANCE_ID" \
  --query 'Reservations[].Instances[].{Instance:InstanceId,State:State.Name}'

結果に terminated が表示されるはずです。AWS View を更新し、bootstrap-server が実行中のアプリケーションのリクエスト先ではなくなったことを確認します。用意されたネットワークとキーペアは保持します。

まとめ

Bash の User Data スクリプトを作成して EC2 の起動時に渡しました。AWS View でアプリケーションの自動設定を確認し、起動出力ログを調べ、保存されたスクリプトを取得しました。最後にサーバーを終了して最終状態を確認しました。