【AWSハンズオン】第8回 VPCエンドポイントでプライベート通信

【AWSハンズオン】第8回 VPCエンドポイントでプライベート通信

VPCエンドポイントを設定し、NAT Gatewayを使用せずにAWSサービスへの完全なプライベート通信を実現します。ECR、CloudWatch Logs、S3へのアクセスをVPC内で完結させ、セキュリティとコスト削減方法。

AWS #AWS#初学者向け#ハンズオン

【AWSハンズオン】第8回 VPCエンドポイントでプライベート通信

サムネイル

VPCエンドポイントを設定し、NAT Gatewayを使用せずにAWSサービスへの完全なプライベート通信を実現します。ECR、CloudWatch Logs、S3へのアクセスをVPC内で完結させ、セキュリティとコスト削減方法。

更新日: 8/18/2025

今回作業対象のブランチ

前提条件

  • 第1回から第7回までの構築が完了していること
  • これまでの作業をterraform destroyでリソースを削除していること(NAT Gatewayを使用しない方法で検証するため)
  • こちらからDockerコンテナでの実行環境が準備できていること

Terraform実行環境の起動

前回と同様に、Dockerコンテナ内で作業を進めます。

# コンテナを起動してbashに入る
docker-compose run --rm terraform

これ以降のコマンドは、すべてこのコンテナ内で実行します。

基礎知識

VPCエンドポイントは、VPCとAWSサービス間のプライベート接続を提供する機能です。インターネットゲートウェイやNAT Gatewayを経由せずに、AWSサービスにアクセスできます。

WEBアプリケーション開発者の視点から見ると

ローカル開発では、すべてのサービスがlocalhost内で通信しますが、クラウドでは通常インターネット経由でサービス間通信が発生します。VPCエンドポイントは、AWSサービスへのアクセスをlocalhost通信のように内部ネットワークで完結させる仕組みです。

エンドポイントの種類

VPCエンドポイントには2つのタイプがあります。

タイプ 対象サービス 料金体系 ネットワーク構成
Gateway Endpoint S3、DynamoDB 無料(データ転送料のみ) ルートテーブルで制御
Interface Endpoint ECR、CloudWatch等 時間課金+データ転送料 ENI(Elastic Network Interface)を作成

なぜVPCエンドポイントが必要か

現在の構成では、ECSタスクがAWSサービスにアクセスする際の通信フローは以下のとおりです。

通信元 通信先 現在の経路 課題
ECSタスク ECR(イメージ取得) NAT Gateway → Internet 通信料金、レイテンシ
ECSタスク CloudWatch Logs NAT Gateway → Internet セキュリティリスク
ECSタスク S3(ログ保存等) NAT Gateway → Internet 帯域幅の制限

VPCエンドポイントを使用することで、これらの通信をVPC内で完結させることができます。

PrivateLinkは、VPCエンドポイントの基盤技術です。AWSサービスやサードパーティのサービスをプライベートIPアドレスでアクセス可能にします。

メリット

項目 説明
セキュリティ向上 インターネットを経由しないため、攻撃対象面が減少
パフォーマンス向上 AWS内部ネットワークを使用するため低レイテンシ
コスト削減 NAT Gatewayのデータ処理料金が不要
簡単な設定 Security GroupとRoute Tableで制御可能

今回作成するネットワーク構成

リソース 説明 数量
S3 Gateway Endpoint S3へのプライベートアクセス 1個
ECR API Interface Endpoint ECR APIへのプライベートアクセス 1個
ECR DKR Interface Endpoint Dockerイメージ取得用 1個
CloudWatch Logs Interface Endpoint ログ送信用 1個
Secrets Manager Interface Endpoint シークレット取得用 1個
VPC Flow Logs ネットワークトラフィックの可視化 1個
Security Group (VPC Endpoints) エンドポイント用セキュリティグループ 1個

Terraformコードの実装

Nat Gatewayの無効化

今回はNAT Gatewayを使用せずにプライベート通信を行うため、学習においてコスト面で厄介なNat Gatewayを無効化します。

# terraform/terraform.tfvars

~~~
enable_nat_gateway = false

変数の追加

VPCエンドポイント用の変数を追加します。

# terraform/variables.tf に追加

variable "enable_vpc_endpoints" {
  description = "VPCエンドポイントを有効にするかどうか"
  type        = bool
  default     = true
}

variable "enable_vpc_flow_logs" {
  description = "VPC Flow Logsを有効にするかどうか"
  type        = bool
  default     = true
}

variable "flow_logs_retention_days" {
  description = "VPC Flow Logsの保持期間(日)"
  type        = number
  default     = 7
}

Security Group(VPCエンドポイント用)

Interface Endpointへのアクセスを制御するSecurity Groupを作成します。

# terraform/vpc_endpoints.tf

# VPC Endpoints用Security Group
resource "aws_security_group" "vpc_endpoints" {
  name        = "${var.project_name}-${var.environment}-vpc-endpoints-sg"
  description = "Security group for VPC Endpoints"
  vpc_id      = aws_vpc.main.id

  ingress {
    description = "HTTPS from VPC"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [var.vpc_cidr]
  }

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

  tags = {
    Name = "${var.project_name}-${var.environment}-vpc-endpoints-sg"
  }
}

Security Group設定の説明

パラメータ 設定値 設定理由
ingress from_port/to_port 443 Interface EndpointはHTTPS(443)で通信するため
ingress cidr_blocks VPC CIDR VPC内からのみアクセスを許可してセキュリティを確保
egress すべて許可 エンドポイントからの戻り通信を許可

S3 Gateway Endpoint

S3へのプライベートアクセスを提供するGateway Endpointを作成します。

# terraform/vpc_endpoints.tf

# S3 Gateway Endpoint
resource "aws_vpc_endpoint" "s3" {
  count = var.enable_vpc_endpoints ? 1 : 0

  vpc_id       = aws_vpc.main.id
  service_name = "com.amazonaws.${var.aws_region}.s3"

  route_table_ids = concat(
    aws_route_table.private[*].id,
    [aws_route_table.public.id]
  )

  tags = {
    Name = "${var.project_name}-${var.environment}-s3-endpoint"
  }
}

S3 Gateway Endpoint設定の説明

パラメータ 設定値 設定理由
service_name com.amazonaws.{region}.s3 S3サービスを指定
route_table_ids Private + Public 両方のサブネットからS3にアクセス可能にする
vpc_endpoint_type Gateway(暗黙的) S3はGateway型のみサポート

ECR Interface Endpoints

ECRからDockerイメージを取得するための2つのエンドポイントを作成します。

# terraform/vpc_endpoints.tf

# ECR API Endpoint
resource "aws_vpc_endpoint" "ecr_api" {
  count = var.enable_vpc_endpoints ? 1 : 0

  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.aws_region}.ecr.api"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoints.id]
  private_dns_enabled = true

  tags = {
    Name = "${var.project_name}-${var.environment}-ecr-api-endpoint"
  }
}

# ECR DKR Endpoint (Docker Registry)
resource "aws_vpc_endpoint" "ecr_dkr" {
  count = var.enable_vpc_endpoints ? 1 : 0

  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.aws_region}.ecr.dkr"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoints.id]
  private_dns_enabled = true

  tags = {
    Name = "${var.project_name}-${var.environment}-ecr-dkr-endpoint"
  }
}

ECR Endpoints設定の説明

パラメータ 設定値 設定理由
service_name (API) ecr.api ECRのAPI操作(認証、メタデータ取得)用
service_name (DKR) ecr.dkr Dockerイメージのpull/push用
subnet_ids Private Subnets ECSタスクが配置されているサブネットに設置
private_dns_enabled true ECRのパブリックDNS名をプライベートIPに解決
security_group_ids VPC Endpoints SG HTTPS通信のみ許可するSGを適用

CloudWatch Logs Interface Endpoint

ログ送信のためのエンドポイントを作成します。

# terraform/vpc_endpoints.tf

# CloudWatch Logs Endpoint
resource "aws_vpc_endpoint" "logs" {
  count = var.enable_vpc_endpoints ? 1 : 0

  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.aws_region}.logs"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoints.id]
  private_dns_enabled = true

  tags = {
    Name = "${var.project_name}-${var.environment}-logs-endpoint"
  }
}

CloudWatch Logs Endpoint設定の説明

パラメータ 設定値 設定理由
service_name logs CloudWatch Logsサービスを指定
vpc_endpoint_type Interface CloudWatch LogsはInterface型のみサポート
private_dns_enabled true logs.{region}.amazonaws.comをプライベート解決

Secrets Manager Interface Endpoint

シークレット取得のためのエンドポイントを作成します。

# terraform/vpc_endpoints.tf

# Secrets Manager Endpoint
resource "aws_vpc_endpoint" "secrets_manager" {
  count = var.enable_vpc_endpoints ? 1 : 0

  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.aws_region}.secretsmanager"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = aws_subnet.private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoints.id]
  private_dns_enabled = true

  tags = {
    Name = "${var.project_name}-${var.environment}-secrets-manager-endpoint"
  }
}

VPC Flow Logs

ネットワークトラフィックを可視化するためのFlow Logsを設定します。

# terraform/vpc_endpoints.tf

# VPC Flow Logs用のCloudWatch Log Group
resource "aws_cloudwatch_log_group" "vpc_flow_logs" {
  count = var.enable_vpc_flow_logs ? 1 : 0

  # 削除時にログデータも強制削除
  skip_destroy = false

  name              = "/aws/vpc/${var.project_name}-${var.environment}"
  retention_in_days = var.flow_logs_retention_days

  lifecycle {
    ignore_changes = [name]
  }

  tags = {
    Name = "${var.project_name}-${var.environment}-vpc-flow-logs"
  }
}

# VPC Flow Logs用のIAM Role
resource "aws_iam_role" "vpc_flow_logs" {
  count = var.enable_vpc_flow_logs ? 1 : 0

  name = "${var.project_name}-${var.environment}-vpc-flow-logs-role"

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

  tags = {
    Name = "${var.project_name}-${var.environment}-vpc-flow-logs-role"
  }
}

# VPC Flow Logs用のIAM Policy
resource "aws_iam_role_policy" "vpc_flow_logs" {
  count = var.enable_vpc_flow_logs ? 1 : 0

  name = "${var.project_name}-${var.environment}-vpc-flow-logs-policy"
  role = aws_iam_role.vpc_flow_logs[0].id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents",
          "logs:DescribeLogGroups",
          "logs:DescribeLogStreams"
        ]
        Resource = "*"
      }
    ]
  })
}

# VPC Flow Logs
resource "aws_flow_log" "main" {
  count = var.enable_vpc_flow_logs ? 1 : 0

  iam_role_arn    = aws_iam_role.vpc_flow_logs[0].arn
  log_destination = aws_cloudwatch_log_group.vpc_flow_logs[0].arn
  traffic_type    = "ALL"
  vpc_id          = aws_vpc.main.id

  tags = {
    Name = "${var.project_name}-${var.environment}-vpc-flow-logs"
  }
}

VPC Flow Logs設定の説明

パラメータ 設定値 設定理由
traffic_type ALL ACCEPT/REJECT両方のトラフィックを記録して完全な可視化
log_destination CloudWatch Logs ログの検索・分析が容易なため
retention_in_days 7 開発環境ではコスト削減のため短期間に設定

出力の追加

作成したリソースの情報を出力に追加します。

# terraform/outputs.tf に追加

# VPC Endpoints関連の出力
output "vpc_endpoint_s3_id" {
  description = "S3 VPC Endpoint ID"
  value       = try(aws_vpc_endpoint.s3[0].id, "")
}

output "vpc_endpoint_ecr_api_id" {
  description = "ECR API VPC Endpoint ID"
  value       = try(aws_vpc_endpoint.ecr_api[0].id, "")
}

output "vpc_endpoint_ecr_dkr_id" {
  description = "ECR DKR VPC Endpoint ID"
  value       = try(aws_vpc_endpoint.ecr_dkr[0].id, "")
}

output "vpc_endpoint_logs_id" {
  description = "CloudWatch Logs VPC Endpoint ID"
  value       = try(aws_vpc_endpoint.logs[0].id, "")
}

output "vpc_endpoint_secrets_manager_id" {
  description = "Secrets Manager VPC Endpoint ID"
  value       = try(aws_vpc_endpoint.secrets_manager[0].id, "")
}

output "vpc_flow_logs_id" {
  description = "VPC Flow Logs ID"
  value       = try(aws_flow_log.main[0].id, "")
}

output "vpc_flow_logs_group_name" {
  description = "VPC Flow Logs CloudWatch Log Group名"
  value       = try(aws_cloudwatch_log_group.vpc_flow_logs[0].name, "")
}

リソースを作成

Terraformコンテナ内で以下のコマンドを実行します。

# terraformディレクトリへ移動
cd terraform

# 実行計画の確認
terraform plan

# リソースの作成
terraform apply

VPCエンドポイントの作成には2〜3分程度かかります。

注意: ECSタスクを動かすには、事前にECRにDockerイメージをプッシュしておく必要があります。詳細は第7回の記事の「ECRへのイメージプッシュ」セクションを参照してください。

作成結果の確認

# VPCエンドポイントの確認
terraform output vpc_endpoint_s3_id
terraform output vpc_endpoint_ecr_api_id
terraform output vpc_endpoint_ecr_dkr_id
terraform output vpc_endpoint_logs_id

動作確認

VPCエンドポイントの状態確認

AWS Management ConsoleでVPCエンドポイントが正しく作成されていることを確認します。

VPC > エンドポイント

エンドポイント エンドポイントタイプ ステータス
S3 Gateway 使用可能
ECR API Interface 使用可能
ECR DKR Interface 使用可能
CloudWatch Logs Interface 使用可能
Secrets Manager Interface 使用可能

VPC Flow Logsの確認

ネットワークトラフィックが正しくログに記録されていることを確認します。

# Flow Logsの確認
aws logs tail $(terraform output -raw vpc_flow_logs_group_name) \
  --follow \
  --format short

正常なFlow Logsのフォーマット例

# 送信方向
2025-07-30T15:10:35 2 123456789012 eni-xxxxxxxxxxxxxxxxx 10.0.1.8 54.xxx.xxx.10 443 51804 6 1 40 1753888235 1753888263 ACCEPT OK
# 受信方向  
2025-07-30T15:10:35 2 123456789012 eni-xxxxxxxxxxxxxxxxx 54.xxx.xxx.10 10.0.1.8 51804 443 6 1 60 1753888235 1753888263 ACCEPT OK

通信経路の確認

VPCエンドポイント経由で通信していることを確認します。

# ECSタスクのENIを特定
TASK_ARN=$(aws ecs list-tasks \
  --cluster $(terraform output -raw ecs_cluster_id) \
  --service-name $(terraform output -raw ecs_service_name) \
  --query 'taskArns[0]' \
  --output text)

# タスクの詳細からENIを取得(ECRにイメージをプッシュしていること前提)
ENI_ID=$(aws ecs describe-tasks \
  --cluster $(terraform output -raw ecs_cluster_id) \
  --tasks $TASK_ARN \
  --query 'tasks[0].attachments[0].details[?name==`networkInterfaceId`].value' \
  --output text)

# ENIのプライベートIPを確認
aws ec2 describe-network-interfaces \
  --network-interface-ids $ENI_ID \
  --query 'NetworkInterfaces[0].PrivateIpAddress' \
  --output text

コスト管理(料金比較)

項目 NAT Gateway利用時 VPCエンドポイント利用時
NAT Gateway(2AZ) $0.045 × 2 × 24時間 × 30日 = 約$65/月 $0
NAT Gatewayデータ処理 $0.045/GB $0
Interface Endpoint(4個) $0 $0.01 × 4 × 24時間 × 30日 = 約$29/月
データ転送料金 標準料金 同一AZ内は$0.01/GB

月間コスト削減額の例(100GB/月の通信量の場合)

コスト項目 削減額
NAT Gateway固定費 $65
データ処理料金(100GB) $4.5
VPCエンドポイント費用 -$29
実質削減額 約$40.5/月

次のステップ

次回は、フロントエンド用のS3とCloudFront構築を行います。

  • S3バケットによる静的ウェブサイトホスティング
  • CloudFrontディストリビューションの設定
  • Origin Access Control(OAC)によるセキュリティ設定
  • HTTPS化とカスタムドメイン設定(オプション)

検索

検索条件に一致する記事が見つかりませんでした