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: あり │
└─────────────────────────┘ └─────────────────────────┘
| 項目 | 状態 |
|---|---|
| VNet | 10.0.0.0/16 |
| Container Apps Environment | gx-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 │
└─────────────────────────────────────────────────────────────────────────────┘
| 項目 | 状態 |
|---|---|
| VNet | 10.1.0.0/16(新規) |
| Container Apps Environment | gx-private-container-app-env(新規) |
| internal-only | ✅ あり |
| Application Gateway | ✅ テナント単位(オプション) |
| WAF | ✅ OWASP 3.2 ルールセット |
アーキテクチャの特徴
| 要素 | 配置 | 説明 |
|---|---|---|
| VNet + Subnets | shared-v2 | 全テナント共有 |
| Container Apps Environment | shared-v2 | 全テナント共有(internal-only) |
| Application Gateway | 各テナントの RG | テナント単位で独立 |
| Container Apps | 各テナントの RG | テナント単位で独立 |
| Database | 各テナントの RG | テナント単位で独立 |
ネットワーク設計
新 VNet 構成
| サブネット | CIDR | 用途 |
|---|---|---|
app-gateway-subnet | 10.1.0.0/24 | Application Gateway |
container-app-subnet | 10.1.64.0/18 | Container Apps Environment |
database-subnet | 10.1.128.0/19 | MySQL、Redis |
reserved | 10.1.160.0/19 | 将来拡張用 |
Application Gateway 設計(テナント単位)
各テナントの Application Gateway は config.toml で設定します:
| 項目 | 設定 | 変更可否 |
|---|---|---|
| SKU | WAF_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 分 |
| 3 | NSG 作成・適用 | インフラ | 15 分 |
| 4 | 新 Container Apps Environment 作成 | インフラ | 15 分 |
| 5 | Application Gateway + WAF 作成 | インフラ | 30 分 |
| 6 | Private DNS Zone 設定 | インフラ | 10 分 |
1-2. アプリケーションデプロイ
| Step | 作業 | 担当 | 所要時間 |
|---|---|---|---|
| 7 | 各テナントの Container Apps デプロイ | インフラ | テナント数 × 15 分 |
| 8 | 環境変数設定 | インフラ | テナント数 × 5 分 |
| 9 | App Gateway Backend Pool 設定 | インフラ | テナント数 × 10 分 |
| 10 | SSL 証明書設定 | インフラ | 30 分 |
1-3. 動作確認
| Step | 作業 | 担当 | 所要時間 |
|---|---|---|---|
| 11 | App Gateway 経由でアクセステスト | インフラ | テナント数 × 15 分 |
| 12 | WAF ルール動作確認 | インフラ | 30 分 |
| 13 | パフォーマンステスト | インフラ | 1 時間 |
Phase 2: ドメイン切り替え(数分)
| Step | 作業 | ダウンタイム | 所要時間 |
|---|---|---|---|
| 1 | DNS TTL 短縮(事前に 300s → 60s) | なし | 事前に実施 |
| 2 | メンテナンス通知 | なし | 事前に実施 |
| 3 | DNS 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 |
ロールバック手順
-
即時ロールバック(DNS 切り替え後 24 時間以内)
- DNS CNAME を旧環境に戻す
- 所要時間: 数分(DNS 伝播時間)
-
完全ロールバック(旧環境削除前)
- 旧環境はそのまま稼働可能
- 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 1 | Terraform モジュール開発、レビュー |
| Phase 1 | Week 2 | 新環境構築、テスト |
| Phase 2 | Week 3 初日 | ドメイン切り替え |
| Phase 3 | Week 3-4 | 安定稼働確認 |
| Phase 4 | Week 5 | クリーンアップ |
関連ドキュメント
- ネットワークセキュリティ設計 - セキュリティ設計の詳細
- ネットワーク構成図 - 現在のネットワーク構成
- アーキテクチャリファレンス - 詳細なアーキテクチャ説明
承認
| 役割 | 氏名 | 日付 | 承認 |
|---|---|---|---|
| インフラリード | |||
| セキュリティ担当 | |||
| プロジェクトマネージャー |