EC2 インスタンスメタデータで 色々やってみた!

EC2 インスタンスメタデータで 色々やってみた!

実行中のEC2インスタンスに関する情報を取得できる、EC2 インスタンスメタデータ。実際に使ってみました!

こんにちは!Koty-Mousa 矢坂幸太郎です!
今回は、EC2 の機能である「インスタンスメタデータ」を使ってみます!

EC2 インスタンスメタデータ

EC2のインスタンスメタデータとは、実行中のEC2インスタンスに関する情報を取得できるサービスです。ホスト名、インスタンスID、セキュリティグループ、IAMロール情報など、実行中のインスタンス自身に関する設定情報やその他の詳細情報が含まれます。
当該インスタンスから 「http://169.254.169.254/latest/meta-data/」 エンドポイントにアクセスすることで、情報を取得できます。

取得可能な情報など、詳細は公式ドキュメントをご参照ください。
インスタンスメタデータを使用して EC2 インスタンスを管理する - Amazon Elastic Compute Cloud

まずは設定から

インスタンスメタデータを利用するには、機能を有効化しておく必要があります。

インスタンス起動時の設定

(デフォルトでは有効化されています)
EC2インスタンス 起動設定の画面『高度な設定』内に"メタデータ"の設定があります。

  • 「アクセス可能なメタデータ」を有効にします。
    デフォルトで有効です
  • 「メタデータのバージョン」は、基本的には V2 を利用します。
    何らかの理由で V1 が必要でない限り、V2 を推奨します。
  • 「メタデータレスポンスのホップ制限」は、パケットが経由できるルーター等の数を制限します。
    デフォルトでは 1 に設定されています。必要なければそのままにしてください。
  • 「メタデータのタグを許可」では、本サービスで EC2インスタンス のタグ(例えば インスタンス名)を取得できるかどうかを設定します。
    デフォルトでは無効化されています。必要なければそのままにしてください。

画像11

起動済みのインスタンスの設定

すでにインスタンスが起動済みの場合は、『アクション』→『インスタンスの設定』→『インスタンスメタデータオプションの変更』から、現在の設定を確認できます。

画像12

画像13

やってみよう!

まずは、単に情報を取得してみるところから始めましょう。
今回は、Amazon Linux 2023 を使用します。

今回は、バージョン V2 を使用するため、トークンを取得します。

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

続いて、データを取得します。
今回は、インスタンスID を取得します。

curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id

次のような返答がありました

i-03ed97279ff6ea96a

インスタンスID が返されましたね!


取得可能なデータ

取得可能な情報一覧は次のコマンドで取得できます

curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
managed-ssh-keys/
metrics/
network/
placement/
profile
public-hostname
public-ipv4
reservation-id
security-groups
services/
system

詳細は下記ドキュメントをご参照ください
インスタンスメタデータを使用して EC2 インスタンスを管理する - Amazon Elastic Compute Cloud


もちろん、直接参照する方法でも十分有用かと思いますが、コードの中に組み込んで、より実践的にやってみます。

HTML に組み込んでみよう!

まずは、取得した情報を表示するWebサイトを作成してみます。

今回の検証では、EC2 のパブリックIPを有効化しておきます。
セキュリティグループで、8080ポートのインバウンド接続を手元PC等 必要最低限のIPアドレスからのアクセスを許可します。

Claude 3.5 Sonnet v2 (via AI-Starter)を用いて、プログラムを書きました!

HTML CSS Python ファイルはここをクリックして表示
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>EC2メタデータ情報</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
    <div class="container">
        <h1>EC2メタデータ情報</h1>
        {% for key, value in metadata.items() %}
        <div class="info-item">
            <span class="label">{{ key }}:</span>
            <span class="value">{{ value }}</span>
        </div>
        {% endfor %}
        <button class="refresh-button" onclick="location.reload()">更新</button>
    </div>
</body>
</html>
style.css
body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
    background-color: #f5f5f5;
}

.container {
    max-width: 800px;
    margin: 0 auto;
    background-color: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

h1 {
    color: #333;
    text-align: center;
    margin-bottom: 30px;
}

.info-item {
    margin: 10px 0;
    padding: 10px;
    border-bottom: 1px solid #eee;
}

.label {
    font-weight: bold;
    color: #333;
}

.value {
    color: #666;
    margin-left: 10px;
}

.refresh-button {
    background-color: #4CAF50;
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    margin-top: 20px;
    display: block;
    width: 100%;
    max-width: 200px;
    margin: 20px auto 0;
}

.refresh-button:hover {
    background-color: #45a049;
}

app.py
from flask import Flask, render_template
import requests

app = Flask(__name__)

def get_metadata_token():
    try:
        token_url = "http://169.254.169.254/latest/api/token"
        headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"}
        response = requests.put(token_url, headers=headers, timeout=2)
        return response.text
    except:
        return None

def get_metadata(path):
    try:
        # トークンの取得
        token = get_metadata_token()
        if not token:
            return "トークン取得エラー"

        # メタデータの取得
        url = f"http://169.254.169.254/latest/meta-data/{path}"
        headers = {"X-aws-ec2-metadata-token": token}
        response = requests.get(url, headers=headers, timeout=2)
        return response.text if response.status_code == 200 else "取得エラー"
    except Exception as e:
        return f"エラー: {str(e)}"

@app.route('/')
def index():
    metadata = {
        "インスタンスタイプ": get_metadata("instance-type"),
        "パブリックIPアドレス": get_metadata("public-ipv4"),
        "アベイラビリティゾーン": get_metadata("placement/availability-zone"),
        "パブリックホスト名": get_metadata("public-hostname")
    }
    return render_template('index.html', metadata=metadata)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

下記のフォルダ階層に保存してあります

/home/ec2-user/ec2-metadata-web/
├── app.py
├── templates/
│   └── index.html
└── static/
    └── css/
        └── style.css

続いて、下記コマンドでテストサーバーを実行します

cd ~/ec2-metadata-web
python3 app.py

ブラウザで、 XX.XX.XX.XX:8080 (パブリックIP)にアクセスします。

画像14

インスタンスの情報が正しく取得できていることが確認できました!

ログを保存してみよう!

続いては、インスタンスメタデータを保存するログシステムを作ってみましょう!

起動時にグローバルIPアドレスを保存するシステムです。
今回は、ユーザデータに下記のコードを入力します。
EC2 インスタンス起動時に「高度な設定」の中に「ユーザデータ」項目があります。

ユーザデータのサンプルはこちら
#!/bin/bash

# メタデータ収集用のPythonスクリプト
cat <<'EOF' > /opt/collect_metadata.py
import requests
import json
from datetime import datetime
import os

def get_metadata():
    token_url = "http://169.254.169.254/latest/api/token"
    token_headers = {"X-aws-ec2-metadata-token-ttl-seconds": "21600"}
    token = requests.put(token_url, headers=token_headers).text

    metadata_url = "http://169.254.169.254/latest/meta-data/"
    headers = {"X-aws-ec2-metadata-token": token}

    metadata_items = {
        "instance-id": "instance-id",
        "public-ipv4": "public-ipv4",
        "local-ipv4": "local-ipv4",
        "instance-type": "instance-type",
        "availability-zone": "placement/availability-zone"
    }

    metadata = {
        "timestamp": datetime.utcnow().isoformat()
    }

    for key, path in metadata_items.items():
        try:
            response = requests.get(f"{metadata_url}{path}", headers=headers)
            metadata[key] = response.text
        except:
            metadata[key] = "N/A"

    return metadata

# メイン処理
metadata = get_metadata()

# 履歴形式でJSONファイルに保存
history_file = '/var/log/instance-metadata-history.json'
history = []

if os.path.exists(history_file):
    with open(history_file, 'r') as f:
        try:
            history = json.load(f)
        except:
            history = []

history.append(metadata)

with open(history_file, 'w') as f:
    json.dump(history, f, indent=2)

# 最新のメタデータも別ファイルに保存
with open('/var/log/instance-metadata.json', 'w') as f:
    json.dump(metadata, f, indent=2)

print(f"Collected metadata at: {metadata['timestamp']}")
EOF

# スクリプトの実行権限を設定
chmod +x /opt/collect_metadata.py

# systemdサービスの作成
cat <<'EOF' > /etc/systemd/system/metadata-collector.service
[Unit]
Description=EC2 Metadata Collector
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/bin/python3 /opt/collect_metadata.py
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
EOF

# systemdの設定
systemctl daemon-reload
systemctl enable metadata-collector
systemctl start metadata-collector

インスタンスを起動し、下記コマンドを実行するとログが取得できます

cat /var/log/instance-metadata.json
[ec2-user@ip-10-0-8-169 ~]$cat /var/log/instance-metadata.json
{
  "timestamp": "2024-12-03T07:52:13.616215",
  "instance-id": "i-05d4d5da9f3ae44c6",
  "public-ipv4": "54.168.108.185",
  "local-ipv4": "10.0.8.169",
  "instance-type": "t3.micro",
  "availability-zone": "ap-northeast-1a"
}

また、下記コマンドで過去のログ履歴が確認できます

cat /var/log/instance-metadata-history.json
ログ履歴実行結果はこちらをクリックして表示

3回 Start / Stop を行った後です

[ec2-user@ip-10-0-8-169 ~]$ cat /var/log/instance-metadata-history.json
[
  {
    "timestamp": "2024-12-03T07:48:19.876235",
    "instance-id": "i-05d4d5da9f3ae44c6",
    "public-ipv4": "52.199.13.91",
    "local-ipv4": "10.0.8.169",
    "instance-type": "t3.micro",
    "availability-zone": "ap-northeast-1a"
  },
  {
    "timestamp": "2024-12-03T07:52:13.616215",
    "instance-id": "i-05d4d5da9f3ae44c6",
    "public-ipv4": "54.168.108.185",
    "local-ipv4": "10.0.8.169",
    "instance-type": "t3.micro",
    "availability-zone": "ap-northeast-1a"
  },
  {
    "timestamp": "2024-12-03T07:58:37.705748",
    "instance-id": "i-05d4d5da9f3ae44c6",
    "public-ipv4": "54.199.235.6",
    "local-ipv4": "10.0.8.169",
    "instance-type": "t3.micro",
    "availability-zone": "ap-northeast-1a"
  }
]

この中から、パブリックIPアドレスだけを抽出してみましょう

cat /var/log/instance-metadata-history.json | jq -r '.[]["public-ipv4"]'
[ec2-user@ip-10-0-8-169 ~]$cat /var/log/instance-metadata-history.json | jq -r '.[]["public-ipv4"]''
52.199.13.91
54.168.108.185
54.199.235.6

再起動のたびにパブリックIPアドレスが変わっていることが確認できました!

Elastic IP をつけてみよう

Elactic IP をアタッチすれば、IPアドレスは毎回変わらないはずです。
Elastic IP をアタッチして、同じようにログを取得してみましょう!

ログの完全版はこちら
[ec2-user@ip-10-0-4-242 ~]$ cat /var/log/instance-metadata-history.json
[
  {
    "timestamp": "2024-12-06T00:08:27.663941",
    "instance-id": "i-056740a9ec49a7dc9",
    "public-ipv4": "54.178.111.12",
    "local-ipv4": "10.0.4.242",
    "instance-type": "t3.micro",
    "availability-zone": "ap-northeast-1a"
  },
  {
    "timestamp": "2024-12-06T01:30:13.785983",
    "instance-id": "i-056740a9ec49a7dc9",
    "public-ipv4": "13.112.137.36",
    "local-ipv4": "10.0.4.242",
    "instance-type": "t3.micro",
    "availability-zone": "ap-northeast-1a"
  },
  {
    "timestamp": "2024-12-06T01:34:30.171394",
    "instance-id": "i-056740a9ec49a7dc9",
    "public-ipv4": "13.112.137.36",
    "local-ipv4": "10.0.4.242",
    "instance-type": "t3.micro",
    "availability-zone": "ap-northeast-1a"
  },
  {
    "timestamp": "2024-12-06T01:43:49.628157",
    "instance-id": "i-056740a9ec49a7dc9",
    "public-ipv4": "13.112.137.36",
    "local-ipv4": "10.0.4.242",
    "instance-type": "t3.micro",
    "availability-zone": "ap-northeast-1a"
  }
]
[ec2-user@ip-10-0-4-242 ~]$cat /var/log/instance-metadata-history.json | jq -r '.[]["public-ipv4"]''
54.178.111.12
13.112.137.36
13.112.137.36
13.112.137.36

(初回起動の後に Elastic IP を設定したため、最初に Elastic IP ではない 54.178.111.12 が記録されています。)

再起動を繰り返しても、グローバルIPアドレスが変化していないことが確認できました!
状況によっては、使えるシステムだなと感じました!

つづく!

インスタンスメタデータを取得して、利用してみました。
取り扱いに注意が必要なデータもありますが、うまく使えば便利なシステムが構築できると考えています。
皆様のお役に立てれば幸いです。

Koty-Mousa 矢坂幸太郎がお伝えしました!


The avatar, logo and name of "Koty-Mousa 矢坂幸太郎" and "Clamerus, dev. by Koty-Mousa" (include inconsistent/abbreviation spelling) are owned and copyrighted by Koty-Mousa (individually).

アノテーション株式会社について

アノテーション株式会社はクラスメソッドグループのオペレーション専門特化企業です。サポート・運用・開発保守・情シス・バックオフィスの専門チームが、最新 IT テクノロジー、高い技術力、蓄積されたノウハウをフル活用し、お客様の課題解決を行っています。当社は様々な職種でメンバーを募集しています。「オペレーション・エクセレンス」と「らしく働く、らしく生きる」を共に実現するカルチャー・しくみ・働き方にご興味がある方は、アノテーション株式会社 採用サイトをぜひご覧ください。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.