メインコンテンツまでスキップ
最新4mo ago

AWS を使用した Grafana のデプロイ

実践例: Docker Hub から公式 Grafana イメージをプル し、ECS Fargate 上で実行し、ブラウザからアクセスできる完全に機能する監視ダッシュボードを取得します — ネットワーク、IAM、およびサービス設定を最初から説明します。


📌 何を構築しているのか?

                    ┌─────────────────────────────────────────────────┐
│ AWS Cloud (your account) │
│ │
You (browser) │ ┌──────────────┐ │
http://<IP>:3000 ──┼───►│ Security │ │
│ │ Group :3000 │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────▼───────────────────────────────┐ │
│ │ ECS Cluster (Fargate) │ │
│ │ │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ ECS Task (awsvpc mode) │ │ │
│ │ │ │ │ │
│ │ │ ┌───────────────────────────┐ │ │ │
│ │ │ │ grafana/grafana:latest │ │ │ │
│ │ │ │ (from Docker Hub) │ │ │ │
│ │ │ │ Port 3000 │ │ │ │
│ │ │ └───────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ Task Execution Role │ │ │
│ │ │ → pulls image │ │ │
│ │ │ → writes CloudWatch logs │ │ │
│ │ └─────────────────────────────────┘ │ │
│ └──────────────────────────────────────┘ │
│ │
│ VPC → Public Subnet → Internet Gateway │
└─────────────────────────────────────────────────┘

このガイドで説明する 2 つのアプローチ:

  1. パス A — Docker Hub から公開イメージを直接プル(例:grafana/grafana)— 最もシンプル、ECR は不要

  2. パス B — カスタムイメージを ECR(プライベートレジストリ)にプッシュしてから、ECR からプル — カスタム/プライベートバックエンドの場合


🧱 コア ECS コンセプト

手順に進む前に、各コンポーネントが何をするかを説明します:

コンポーネント説明類似例
ECS Clusterタスクとサービスの論理的なグループコンテナ用の「ワークスペース」
Task Definitionコンテナのブループリント — イメージ URI、CPU、メモリ、ポート、環境変数、IAM ロールAWS 用の docker-compose.yml
Taskタスク定義の実行中のインスタンス実行中の docker run コマンド
Serviceタスクの必要な数を管理し、失敗時に再起動し、ロードバランサーと統合プロセス監視
Fargateサーバーレスコンピュートエンジン — EC2 インスタンスの管理が不要AWS がコンテナを実行
Task Execution RoleECS エージェントがイメージをプルしてログを書き込むための IAM ロールインフラストラクチャの権限
Task Roleアプリケーションコードが AWS API を呼び出すときに使用する IAM ロールアプリケーションの権限

📋 前提条件

  • 管理者権限またはそれ以上の IAM 権限を持つ AWS アカウント

  • AWS CLI がインストールされて設定されている(aws configure

  • Docker がローカルにインストールされている(パス B でのみ必要 — ECR へのプッシュ)

  • インターネットゲートウェイを備えた 1 つ以上のパブリックサブネットを持つ VPC(デフォルト VPC で動作)


パス A — Docker Hub から公開イメージをプル(Grafana)

最もシンプルなアプローチです。ECS Fargate は Docker Hub から公開イメージを直接プルできます — タスク定義でイメージ名を指定するだけです。ECR リポジトリは不要です。


ステップ 1 — VPC を作成(またはデフォルト VPC を使用)

すべての AWS アカウントは各リージョンにデフォルト VPC を備えています。クイックセットアップには、デフォルト VPC で十分です。 カスタム VPC を作成する場合:

  1. VPC コンソールVPC を作成

  2. VPC とその他のもの を選択(ウィザードがサブネット、ルートテーブル、IGW を自動作成します)

  3. 次のように設定します:

    • 名前:grafana-vpc
    • IPv4 CIDR:10.0.0.0/16
    • AZ の数:2
    • パブリックサブネット:2
    • プライベートサブネット:0(このシンプルなセットアップの場合)
    • NAT ゲートウェイ:なし
    • VPC エンドポイント:なし

Fargate タスクがインターネットからイメージをプルするために満たす必要があること:

  • タスクがパブリックサブネットで実行される

  • サブネットのルートテーブルに 0.0.0.0/0 → Internet Gateway のルートがある

  • タスク起動時にパブリック IP の自動割り当てENABLED に設定されている

  • または、タスクがプライベートサブネットで実行され、NAT ゲートウェイへのルートがある


ステップ 2 — セキュリティグループを作成

セキュリティグループは、Grafana コンテナに到達できるトラフィックを制御します。

  1. EC2 コンソールセキュリティグループセキュリティグループを作成

  2. 次のように設定します:

フィールド
名前grafana-sg
説明Grafana UI アクセスを許可
VPCVPC を選択
  1. インバウンドルール:
タイププロトコルポートソース説明
カスタム TCPTCP30000.0.0.0/0Grafana ウェブ UI
  1. アウトバウンドルール: デフォルトのままにします(すべてのトラフィックを送信許可 — イメージプルとインターネットアクセスに必要)

AWS CLI:


# セキュリティグループを作成
SG_ID=$(aws ec2 create-security-group \
--group-name grafana-sg \
--description "Allow Grafana UI on port 3000" \
--vpc-id vpc-0abc123 \
--query 'GroupId' --output text)

# ポート 3000 のインバウンドルールを追加
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp \
--port 3000 \
--cidr 0.0.0.0/0


ステップ 3 — ECS クラスターを作成

  1. ECS コンソールクラスタークラスターを作成

  2. 次のように設定します:

    • クラスター名:grafana-cluster
    • インフラストラクチャ:AWS Fargate を選択(デフォルト)
  3. 作成 をクリック それだけです。クラスターは単なる論理コンテナ — サーバーはプロビジョニングされません。

AWS CLI:

aws ecs create-cluster --cluster-name grafana-cluster


ステップ 4 — Task Execution IAM ロールを作成

Task Execution ロールにより、ECS エージェント(アプリケーションではなく)がイメージをプルしてログを書き込むことができます。ECS を以前に使用した場合は、ecsTaskExecutionRole が既にある可能性があります。

存在するかどうかを確認:

aws iam get-role --role-name ecsTaskExecutionRole

存在しない場合は作成:

  1. IAM コンソールロールロールを作成

  2. 信頼できるエンティティ:AWS サービスElastic Container Service → ユースケース:Elastic Container Service Task

  3. マネージドポリシーをアタッチ:AmazonECSTaskExecutionRolePolicy

  4. ロール名:ecsTaskExecutionRole このポリシーは以下の権限を付与します:

  • ecr:GetAuthorizationToken — ECR に認証

  • ecr:BatchGetImage, ecr:GetDownloadUrlForLayer — ECR からイメージをプル

  • logs:CreateLogStream, logs:PutLogEvents — CloudWatch にコンテナログを書き込む

AWS CLI:


# 信頼ポリシードキュメントを作成
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
EOF

# ロールを作成
aws iam create-role \
--role-name ecsTaskExecutionRole \
--assume-role-policy-document file://trust-policy.json

# マネージドポリシーをアタッチ
aws iam attach-role-policy \
--role-name ecsTaskExecutionRole \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy


ステップ 5 — タスク定義を作成

これがセットアップの心臓部です — ECS に実行するイメージ、割り当てる CPU/メモリ、公開するポート、環境変数、および使用するロールを指定するブループリントです。

  1. ECS コンソールタスク定義新しいタスク定義を作成

  2. 次のように設定します:

設定
タスク定義ファミリーgrafana-task
起動タイプAWS Fargate
OS/アーキテクチャLinux/X86_64
タスクサイズ — CPU0.5 vCPU(512)
タスクサイズ — メモリ1 GB(1024)
タスク実行ロールecsTaskExecutionRole
タスクロールなし(Grafana は AWS API を呼び出す必要がありません)
  1. コンテナ定義:
設定
コンテナ名grafana
イメージ URIgrafana/grafana:latest
必須はい
ポートマッピングコンテナポート:3000、プロトコル:TCP
  1. (オプション)環境変数:
キー目的
GF_SECURITY_ADMIN_USERadminデフォルト管理者ユーザー名
GF_SECURITY_ADMIN_PASSWORDYourStrongPassword123!デフォルトパスワードをオーバーライド
  1. (オプション)ログ — CloudWatch:
設定
ログドライバーawslogs
ログループ/ecs/grafana
リージョンお使いのリージョン(例:ap-northeast-1
ストリームプレフィックスgrafana
  1. 作成 をクリック

同等の JSON タスク定義:

{
"family": "grafana-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"executionRoleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "grafana",
"image": "grafana/grafana:latest",
"essential": true,
"portMappings": [
{
"containerPort": 3000,
"protocol": "tcp"
}
],
"environment": [
{ "name": "GF_SECURITY_ADMIN_USER", "value": "admin" },
{ "name": "GF_SECURITY_ADMIN_PASSWORD", "value": "YourStrongPassword123!" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/grafana",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "grafana",
"awslogs-create-group": "true"
}
}
}
]
}

CLI 経由で登録:

aws ecs register-task-definition --cli-input-json file://grafana-task-def.json

重要な点: image フィールドは grafana/grafana:latest のみです — Docker Hub パブリックイメージです。ECS は Docker Hub から直接プルします。公開イメージの場合、ECR リポジトリは不要です。


ステップ 6 — ECS サービスを作成(またはスタンドアロンタスクを実行)

オプション A — サービスとして実行(推奨)

サービスは Grafana が実行され続けることを保証します。タスクがクラッシュした場合、ECS は自動的に新しいタスクを起動します。

  1. ECS コンソールクラスターgrafana-clusterサービス作成

  2. 次のように設定します:

設定
起動タイプFargate
タスク定義grafana-task(最新リビジョン)
サービス名grafana-service
必要なタスク1
  1. ネットワーク:
設定
VPCVPC を選択
サブネットパブリックサブネットを選択
セキュリティグループgrafana-sg(ポート 3000 を許可するもの)
パブリック IP有効(これが重要です!)
  1. 作成 をクリック

AWS CLI:

aws ecs create-service \
--cluster grafana-cluster \
--service-name grafana-service \
--task-definition grafana-task \
--desired-count 1 \
--launch-type FARGATE \
--network-configuration '{
"awsvpcConfiguration": {
"subnets": ["subnet-0abc123"],
"securityGroups": ["sg-0def456"],
"assignPublicIp": "ENABLED"
}
}'

オプション B — スタンドアロンタスクを実行(クイックテスト)

aws ecs run-task \
--cluster grafana-cluster \
--task-definition grafana-task \
--launch-type FARGATE \
--network-configuration '{
"awsvpcConfiguration": {
"subnets": ["subnet-0abc123"],
"securityGroups": ["sg-0def456"],
"assignPublicIp": "ENABLED"
}
}'


ステップ 7 — Grafana にアクセス

  1. ECS コンソールクラスターgrafana-clusterタスクタブ

  2. 実行中のタスク ID をクリック

  3. 設定 の下で、パブリック IP を見つけます(例:3.112.45.67

  4. ブラウザを開いて次のにアクセス:http://3.112.45.67:3000

  5. 次のようにログイン:

    • ユーザー名:admin
    • パスワード:admin(または GF_SECURITY_ADMIN_PASSWORD で設定したもの)
  6. Grafana は初回ログイン時にパスワード変更を求めます これで、Docker Hub から直接プルされた ECS Fargate 上で Grafana が実行されています。


パス B — カスタムイメージを ECR にプッシュし、ECS にデプロイ

カスタムバックエンド(独自の Dockerfile)がある場合、またはプライベートレジストリを使用したい場合はこのパスを使用します。ワークフローは:ローカルでビルド → ECR にプッシュ → ECS が ECR からプル。


ステップ B1 — ECR リポジトリを作成

  1. ECR コンソールリポジトリリポジトリを作成

  2. 次のように設定します:

    • 表示:プライベート
    • リポジトリ名:my-backend

AWS CLI:

aws ecr create-repository \
--repository-name my-backend \
--region ap-northeast-1

これにより、リポジトリ URI が得られます:
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-backend


ステップ B2 — Docker イメージをビルド、タグ付け、プッシュ


# 1. Docker を ECR に認証
aws ecr get-login-password --region ap-northeast-1 | \
docker login --username AWS --password-stdin \
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com

# 2. Docker イメージをビルド
docker build -t my-backend .

# 3. ECR 用にイメージをタグ付け
docker tag my-backend:latest \
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-backend:latest

# 4. ECR にプッシュ
docker push \
123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-backend:latest

ECR コンソールで、イメージがリポジトリの下に表示されることを確認します。


ステップ B3 — タスク定義を作成(ECR イメージを使用)

パス A との唯一の違いは image フィールドです — Docker Hub の代わりに ECR URI を指します:

{
"family": "my-backend-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::123456789012:role/ecsTaskExecutionRole",
"containerDefinitions": [
{
"name": "my-backend",
"image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-backend:latest",
"essential": true,
"portMappings": [
{
"containerPort": 8080,
"protocol": "tcp"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/my-backend",
"awslogs-region": "ap-northeast-1",
"awslogs-stream-prefix": "backend",
"awslogs-create-group": "true"
}
}
}
]
}

ecsTaskExecutionRole には既に ECR からプルする権限があります(AmazonECSTaskExecutionRolePolicy を通じて)。追加の設定は不要です。 その後、クラスター、サービス、セキュリティグループを作成 — パス A のステップ 1~7 と同じです。


🔄 イメージを更新(CI/CD サイクル)

ECR に新しいイメージをプッシュする(または Docker Hub が更新される)と、実行中のタスク用に ECS は自動的にそれをピックアップしません。新しいイメージをデプロイするには:


# 新しいデプロイを強制 — ECS は最新イメージをプル
aws ecs update-service \
--cluster grafana-cluster \
--service grafana-service \
--force-new-deployment

これはローリング更新をトリガーします:ECS は最新イメージで新しいタスクを起動し、古いタスクをドレインします。


🔒 本番環境のハードニング

上記のセットアップは学習とテストに最適です。本番環境の場合、これらの改善を行います:

1. タスクをプライベートサブネットに移動 + ALB を追加

タスクの公開 IP を直接公開する代わりに:

Internet → ALB (public subnet) → ECS Task (private subnet) → NAT GW (outbound only)

これにより、Grafana はロードバランサーを通じてのみ到達でき、コンテナ自体には公開 IP がありません。

2. Application Load Balancer(ALB)を使用

  • パブリックサブネットに ALB を作成

  • ターゲットグループを作成(タイプ:ip、ポート 3000、ヘルスチェックパス /api/health

  • ターゲットグループを ECS サービスにアタッチ

  • HTTPS リスナー(443)を ACM 証明書で設定

  • HTTP(80)→ HTTPS(443)にリダイレクト これで、raw IP の代わりに https://grafana.yourdomain.com 経由で Grafana にアクセスします。

3. パスワードに Secrets Manager を使用

GF_SECURITY_ADMIN_PASSWORD をタスク定義にハードコーディングしないでください。代わりに:


# シークレットを保存
aws secretsmanager create-secret \
--name grafana/admin-password \
--secret-string "YourStrongPassword123!"

タスク定義で参照:

"secrets": [
{
"name": "GF_SECURITY_ADMIN_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:grafana/admin-password-AbCdEf"
}
]

タスク実行ロールには追加の権限が必要:

{
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue"],
"Resource": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:grafana/*"
}

4. EFS での永続ストレージ

デフォルトでは、Grafana データ(ダッシュボード、データソース)はタスクが再起動されると失われます。EFS ボリュームをマウント:

"volumes": [{
"name": "grafana-data",
"efsVolumeConfiguration": {
"fileSystemId": "fs-0abc123",
"rootDirectory": "/grafana"
}
}]

コンテナ定義で:

"mountPoints": [{
"sourceVolume": "grafana-data",
"containerPath": "/var/lib/grafana"
}]

5. 自動スケーリング


# スケーラブルターゲットを登録
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/grafana-cluster/grafana-service \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 1 \
--max-capacity 3

# CPU に基づくスケール
aws application-autoscaling put-scaling-policy \
--service-namespace ecs \
--resource-id service/grafana-cluster/grafana-service \
--scalable-dimension ecs:service:DesiredCount \
--policy-name cpu-scaling \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration '{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
}
}'


🔍 トラブルシューティング

問題考えられる原因解決策
タスクが PROVISIONING のままスタックサブネットがインターネットに到達できませんパブリックサブネットに IGW ルートがあり、パブリック IP が有効であることを確認
CannotPullContainerErrorインターネットアクセスがないか、イメージ名が間違っていますサブネットルートテーブルを確認し、イメージ名が完全に正しいこと(大文字と小文字を区別)を確認
ECR イメージの CannotPullContainerErrorタスク実行ロールに ECR 権限がありませんAmazonECSTaskExecutionRolePolicyecsTaskExecutionRole にアタッチ
タスクが起動してからすぐに停止コンテナがクラッシュしています/ecs/grafana の CloudWatch ログを確認
ブラウザで Grafana にアクセスできませんセキュリティグループがポート 3000 を許可していませんポート 3000 のインバウンド TCP ルールを追加
ResourceInitializationError実行ロールが不足しているか、権限がありませんタスク定義で executionRoleArn が設定されていることを確認
Docker Hub から toomanyrequestsDocker Hub レート制限に達しました代わりに ECR Public イメージを使用:public.ecr.aws/grafana/grafana:latest
タスクが実行されていますがパブリック IP がありませんパブリック IP が割り当てられていませんネットワーク設定で assignPublicIp: ENABLED を設定
ECS サービスがタスクを継続的に再起動ヘルスチェックが失敗していますコンテナの正常性を確認し、ヘルスチェック猶予期間内にアプリが起動することを確認

コンテナログを確認


# タスク ID を見つけます
aws ecs list-tasks --cluster grafana-cluster --service-name grafana-service

# タスク詳細を取得(パブリック IP を含む)
aws ecs describe-tasks --cluster grafana-cluster --tasks <TASK_ID>

# CloudWatch でログを表示
aws logs tail /ecs/grafana --follow


📊 Docker Hub と ECR — どちらを使用するか

シナリオDocker Hub を直接使用ECR(プライベート)を使用
パブリック/公式イメージ(Grafana、Nginx、Redis)✅ 最もシンプル — イメージ名を使用するだけ❌ 不要なオーバーヘッド
カスタムアプリケーションイメージ❌ Docker Hub アカウント + プッシュが必要✅ AWS IAM と統合、レート制限なし
本番環境ワークロード⚠️ Docker Hub レート制限の対象✅ プルレート制限なし、AWS 内での高速プル
エアギャップ/プライベート環境❌ インターネットアクセスが必要✅ VPC エンドポイントで機能(インターネット不要)
CI/CD パイプライン⚠️ Docker Hub 認証情報が必要aws ecr get-login-password がネイティブで機能

プロのヒント: 本番環境の公開イメージの場合、Docker Hub ではなく ECR Public を使用してレート制限を回避します:public.ecr.aws/grafana/grafana:latest


🏗️ Terraform の例(完全)


# ─── Cluster ───
resource "aws_ecs_cluster" "grafana" {
name = "grafana-cluster"
}

# ─── CloudWatch Log Group ───
resource "aws_cloudwatch_log_group" "grafana" {
name = "/ecs/grafana"
retention_in_days = 7
}

# ─── Task Definition ───
resource "aws_ecs_task_definition" "grafana" {
family = "grafana-task"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "512"
memory = "1024"
execution_role_arn = aws_iam_role.ecs_task_execution.arn

container_definitions = jsonencode([{
name = "grafana"
image = "grafana/grafana:latest"
essential = true

portMappings = [{
containerPort = 3000
protocol = "tcp"
}]

environment = [
{ name = "GF_SECURITY_ADMIN_USER", value = "admin" }
]

logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.grafana.name
"awslogs-region" = var.region
"awslogs-stream-prefix" = "grafana"
}
}
}])
}

# ─── Security Group ───
resource "aws_security_group" "grafana" {
name = "grafana-sg"
description = "Allow Grafana UI access"
vpc_id = var.vpc_id

ingress {
from_port = 3000
to_port = 3000
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}

# ─── ECS Service ───
resource "aws_ecs_service" "grafana" {
name = "grafana-service"
cluster = aws_ecs_cluster.grafana.id
task_definition = aws_ecs_task_definition.grafana.arn
desired_count = 1
launch_type = "FARGATE"

network_configuration {
subnets = var.public_subnet_ids
security_groups = [aws_security_group.grafana.id]
assign_public_ip = true
}
}

# ─── IAM: Task Execution Role ───
resource "aws_iam_role" "ecs_task_execution" {
name = "ecsTaskExecutionRole"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = { Service = "ecs-tasks.amazonaws.com" }
}]
})
}

resource "aws_iam_role_policy_attachment" "ecs_task_execution" {
role = aws_iam_role.ecs_task_execution.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}


📚 参考資料


最終更新:2026 年 3 月

Related Articles