メインコンテンツまでスキップ

Container Apps プライベート環境移行計画書

Container Apps Environment を internal-only に移行し、Application Gateway 経由でインターネット公開する計画書です。

概要

目的

  • Container Apps を完全にプライベート化(Public IP の排除)
  • Application Gateway + WAF によるセキュリティ強化
  • Azure ベストプラクティスに準拠したアーキテクチャへの移行

移行方針

Blue-Green デプロイメントを採用し、ほぼゼロダウンタイムで移行を実現します。

項目内容
方式Blue-Green デプロイメント
ダウンタイムほぼゼロ(DNS 切り替え時の数秒〜数分)
ロールバックDNS を戻すだけで即時復旧可能
並行運用期間1-2 週間(安定稼働確認後に旧環境削除)

アーキテクチャ

現行アーキテクチャ(Blue)

┌─────────────────────────────────────────────────────────────────┐
│ Internet │
└────────────────────────────┬────────────────────────────────────┘

┌──────────────┴──────────────┐
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ Frontend Container App │ │ Backend Container App │
│ (External Ingress) │ │ (External + IP制限) │
│ │ │ │
│ Public IP: あり │ │ Public IP: あり │
└─────────────────────────┘ └─────────────────────────┘
項目状態
VNet10.0.0.0/16
Container Apps Environmentgx-shared-container-app-env
internal-only❌ なし
Application Gateway❌ なし
WAF❌ なし

目標アーキテクチャ(Green)

テナント単位の Application Gateway 構成を採用します。各テナントが独自の AGW を持つことで、独立したセキュリティ設定と課金管理が可能になります。

┌─────────────────────────────────────────────────────────────────────────────┐
│ Internet │
└────────────┬─────────────────────────────────────────┬──────────────────────┘
│ │
▼ ▼
┌────────────────────────────┐ ┌────────────────────────────┐
│ AGW + WAF (Tenant A) │ │ AGW + WAF (Tenant B) │
│ agw-tenant-a-dev │ │ agw-tenant-b-dev │
│ Public IP: xxx.xxx.xxx.xx │ │ Public IP: yyy.yyy.yyy.yy │
└────────────┬───────────────┘ └────────────┬───────────────┘
│ │
│ Private (VNet 内通信のみ) │
│ │
▼ ▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Container Apps Environment (internal-only) │
│ gx-private-container-app-env │
│ │
│ ┌────────────────────────────┐ ┌────────────────────────────┐ │
│ │ Tenant A Apps │ │ Tenant B Apps │ │
│ │ ├─ Frontend (internal) │ │ ├─ Frontend (internal) │ │
│ │ └─ Backend (internal) │ │ └─ Backend (internal) │ │
│ │ │ │ │ │
│ │ rg-tenant-a-dev │ │ rg-tenant-b-dev │ │
│ └────────────────────────────┘ └────────────────────────────┘ │
│ │
│ Public IP: なし │
│ Internal FQDN: *.internal.{region}.azurecontainerapps.io │
└─────────────────────────────────────────────────────────────────────────────┘
項目状態
VNet10.1.0.0/16(新規)
Container Apps Environmentgx-private-container-app-env(新規)
internal-only✅ あり
Application Gateway✅ テナント単位(オプション)
WAF✅ OWASP 3.2 ルールセット

アーキテクチャの特徴

要素配置説明
VNet + Subnetsshared-v2全テナント共有
Container Apps Environmentshared-v2全テナント共有(internal-only)
Application Gateway各テナントの RGテナント単位で独立
Container Apps各テナントの RGテナント単位で独立
Database各テナントの RGテナント単位で独立

ネットワーク設計

新 VNet 構成

サブネットCIDR用途
app-gateway-subnet10.1.0.0/24Application Gateway
container-app-subnet10.1.64.0/18Container Apps Environment
database-subnet10.1.128.0/19MySQL、Redis
reserved10.1.160.0/19将来拡張用

Application Gateway 設計(テナント単位)

各テナントの Application Gateway は config.toml で設定します:

項目設定変更可否
SKUWAF_v2固定
容量自動スケール(min/max は config.toml で設定)✅ テナント毎
WAF モードDetection / Prevention✅ テナント毎
WAF ルールセットOWASP 3.2固定
SSL ポリシーAppGwSslPolicy20220101固定
SSL 証明書Key Vault から取得(オプション)✅ テナント毎

テナント単位 AGW のメリット

メリット説明
独立した課金テナント単位でコスト管理が可能
独立した WAF 設定テナント毎に Detection/Prevention を切り替え
独立した SSL 証明書テナント毎のドメイン・証明書を設定
独立したスケーリングトラフィック量に応じた個別スケーリング
障害分離一つの AGW 障害が他テナントに影響しない

ルーティング設計

パスバックエンド説明
/*Frontend Container App全トラフィックを Frontend にルーティング
注記

Backend へのルーティングは Frontend から内部的に行われます。AGW は Frontend への入り口のみを提供します。


移行フェーズ

Phase 1: 新環境構築(1-2 日)

1-1. インフラ構築

Step作業担当所要時間
1新 VNet 作成(vnet-private-v2インフラ10 分
2サブネット作成インフラ10 分
3NSG 作成・適用インフラ15 分
4新 Container Apps Environment 作成インフラ15 分
5Application Gateway + WAF 作成インフラ30 分
6Private DNS Zone 設定インフラ10 分

1-2. アプリケーションデプロイ

Step作業担当所要時間
7各テナントの Container Apps デプロイインフラテナント数 × 15 分
8環境変数設定インフラテナント数 × 5 分
9App Gateway Backend Pool 設定インフラテナント数 × 10 分
10SSL 証明書設定インフラ30 分

1-3. 動作確認

Step作業担当所要時間
11App Gateway 経由でアクセステストインフラテナント数 × 15 分
12WAF ルール動作確認インフラ30 分
13パフォーマンステストインフラ1 時間

Phase 2: ドメイン切り替え(数分)

Step作業ダウンタイム所要時間
1DNS TTL 短縮(事前に 300s → 60s)なし事前に実施
2メンテナンス通知なし事前に実施
3DNS CNAME を App Gateway IP に変更数秒〜数分5 分
4新環境へのアクセス確認なし10 分
5旧環境へのアクセスがないことを確認なし30 分

Phase 3: 安定稼働確認(1-2 週間)

Step作業期間
1監視・アラート設定初日
2エラー監視1-2 週間
3パフォーマンス監視1-2 週間
4ユーザーフィードバック収集1-2 週間

Phase 4: クリーンアップ

Step作業タイミング
1旧 Container Apps 削除Phase 3 完了後
2旧 Container Apps Environment 削除Phase 3 完了後
3旧リソース整理必要に応じて
4ドキュメント更新完了後

Terraform 構成

ディレクトリ構成(実装済み)

terraform/
├── environments/
│ ├── shared/ # 現行環境(変更なし)
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ └── outputs.tf
│ │
│ ├── shared-v2/ # 新環境(実装済み)
│ │ ├── main.tf # shared-v2 モジュール呼び出し
│ │ ├── variables.tf # 環境変数(AGW なし)
│ │ └── outputs.tf # テナント参照用出力
│ │
│ └── tenants/
│ └── {tenant}/
│ ├── dev/
│ │ └── main.tf # shared_state_key で環境切り替え
│ │ # + enable_application_gateway オプション
│ └── prd/

├── modules/
│ ├── shared/ # 現行モジュール
│ │
│ ├── shared-v2/ # 新規モジュール(実装済み)
│ │ ├── main.tf # リソースグループ、プロバイダー
│ │ ├── variables.tf # 入力変数(AGW 変数なし)
│ │ ├── outputs.tf # 出力(shared 互換 + AGW サブネット)
│ │ ├── locals.tf # ローカル変数、CIDR 定義
│ │ ├── vnet.tf # VNet、サブネット(AGW サブネット含む)
│ │ ├── nsg.tf # NSG(Internet アクセスなし)
│ │ └── container-app-env.tf # internal-only CAE + Private DNS
│ │
│ ├── application-gateway/ # AGW モジュール(実装済み)
│ │ ├── main.tf # App Gateway、WAF Policy
│ │ ├── variables.tf # 入力変数
│ │ └── outputs.tf # 出力
│ │
│ └── applications/
│ └── frontend-backend/v1.0.0/ # テナントモジュール(AGW 対応実装済み)
│ ├── main.tf # Container Apps + Application Gateway
│ ├── variables.tf # enable_application_gateway 追加
│ └── outputs.tf # AGW 出力追加

ネットワーク CIDR(実装済み)

VNet: 10.1.0.0/16 (gx-private-vnet)

├── app-gateway-subnet: 10.1.0.0/24 (256 addresses)
├── container-app-subnet: 10.1.64.0/18 (16,384 addresses)
├── db-subnet: 10.1.128.0/19 (8,192 addresses)
├── shared-subnet: 10.1.160.0/19 (8,192 addresses)
└── reserved: 10.1.192.0/18 (将来用)

主要リソース(実装済みコード)

modules/shared-v2/container-app-env.tf(CAE のみ)

# Container Apps Environment with internal-only configuration
resource "azurerm_container_app_environment" "private" {
name = "gx-private-container-app-env"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
log_analytics_workspace_id = azurerm_log_analytics_workspace.container_apps.id
infrastructure_subnet_id = azurerm_subnet.container_app.id
internal_load_balancer_enabled = true # internal-only を有効化

tags = merge(local.common_tags, var.tags)
}

# Private DNS Zone for internal Container Apps
resource "azurerm_private_dns_zone" "container_apps" {
name = azurerm_container_app_environment.private.default_domain
resource_group_name = azurerm_resource_group.main.name
}

# A record for the Container Apps Environment static IP
resource "azurerm_private_dns_a_record" "container_apps_wildcard" {
name = "*"
zone_name = azurerm_private_dns_zone.container_apps.name
resource_group_name = azurerm_resource_group.main.name
ttl = 300
records = [azurerm_container_app_environment.private.static_ip_address]
}

modules/applications/frontend-backend/v1.0.0/main.tf(テナント単位 AGW)

# Application Gateway module (per-tenant, optional)
module "application_gateway" {
count = var.enable_application_gateway ? 1 : 0
source = "../../../../modules/application-gateway"

name = "agw-${var.company_name}-${var.environment}"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
subnet_id = local.shared_infra.app_gateway_subnet_id

# Autoscaling configuration
min_capacity = var.app_gateway_min_capacity
max_capacity = var.app_gateway_max_capacity

# WAF configuration
waf_enabled = true
waf_mode = var.app_gateway_waf_mode
waf_rule_set_type = "OWASP"
waf_rule_set_version = "3.2"

# Backend pools - Frontend Container App
backend_pools = [
{
name = "frontend-backend-pool"
fqdns = [azurerm_container_app.frontend.ingress[0].fqdn]
}
]

# HTTP Listeners, Backend HTTP Settings, Health Probes...
# (詳細は modules/applications/frontend-backend/v1.0.0/main.tf を参照)

depends_on = [
azurerm_container_app.frontend,
azurerm_container_app.backend
]
}

テナント側の設定例(config.toml)

name = "my-tenant"
template = "frontend-backend"
template_version = "v1.0.0"

["environments.dev"]
enabled = true
location = "japaneast"

# Application Gateway を有効化
["environments.dev.application_gateway"]
enabled = true
min_capacity = 1
max_capacity = 5
waf_mode = "Detection" # 最初は Detection で様子を見る
# custom_domain = "app.example.com" # オプション
# ssl_certificate_key_vault_secret_id = "..." # HTTPS の場合

["environments.dev.frontend"]
image_name = "my-app-frontend"
# ...

デプロイ手順

# 1. shared-v2 環境のデプロイ(CAE のみ)
cd terraform/environments/shared-v2
terraform init && terraform apply

# 2. テナントのデプロイ(AGW 含む)
infra generate my-tenant dev --force
infra plan my-tenant dev
infra apply my-tenant dev

# 3. Application Gateway Public IP の確認
cd terraform/environments/tenants/my-tenant/dev
terraform output application_gateway_public_ip

リスク管理

想定リスクと対策

リスク影響度発生確率対策
DNS 切り替え失敗事前に TTL 短縮、ロールバック手順準備
App Gateway 設定ミス事前テスト、チェックリスト
パフォーマンス劣化事前負荷テスト、モニタリング
SSL 証明書エラー事前検証、証明書有効期限確認
WAF 誤検知最初は Detection モード、段階的に Prevention

ロールバック手順

  1. 即時ロールバック(DNS 切り替え後 24 時間以内)

    • DNS CNAME を旧環境に戻す
    • 所要時間: 数分(DNS 伝播時間)
  2. 完全ロールバック(旧環境削除前)

    • 旧環境はそのまま稼働可能
    • DNS を戻すだけで復旧

チェックリスト

Phase 1 完了条件

  • 新 VNet が作成されている
  • 新 Container Apps Environment が internal-only で作成されている
  • Application Gateway が作成されている
  • WAF が有効化されている
  • 全テナントの Container Apps がデプロイされている
  • App Gateway 経由でアクセスできる
  • SSL 証明書が正しく設定されている

Phase 2 実行条件

  • Phase 1 の全項目が完了している
  • 負荷テストが完了している
  • ロールバック手順が確認されている
  • メンテナンス通知が完了している
  • DNS TTL が短縮されている

Phase 3 完了条件

  • 1-2 週間エラーなく稼働している
  • パフォーマンスが許容範囲内
  • ユーザーからの重大な問題報告がない

Phase 4 完了条件

  • 旧環境が削除されている
  • ドキュメントが更新されている
  • 監視設定が完了している

スケジュール(案)

フェーズ期間内容
準備Week 1Terraform モジュール開発、レビュー
Phase 1Week 2新環境構築、テスト
Phase 2Week 3 初日ドメイン切り替え
Phase 3Week 3-4安定稼働確認
Phase 4Week 5クリーンアップ

関連ドキュメント


承認

役割氏名日付承認
インフラリード
セキュリティ担当
プロジェクトマネージャー