はじめに Link to heading

前回の記事で、Terraform実装を完了し、terraform planで検証しました。

今回は、実際にterraform applyを実行してGCPリソースをデプロイします。

ところが、予想以上に多くのエラーに遭遇しました。しかし、一つ一つ丁寧に解決していくことで、最終的にデプロイに成功しました。

この記事では、実際に遭遇したエラーとその解決方法を全て記録します。


📋 デプロイするリソース Link to heading

terraform planの結果:

Plan: 94 to add, 0 to change, 0 to destroy.

内訳:

  • Network: 11リソース(VPC、NAT、Firewall等)
  • IAM: 11リソース(Service Account、権限)
  • Filestore: 3リソース(NFS 1TB)
  • Database: 33リソース(Cloud SQL HA、10DB、Secret 20個)
  • Compute: 14リソース(MIG、Auto Scaling)
  • Load Balancer: 19リソース(LB、CDN、WAF、SSL)
  • Monitoring: 3リソース(Alert、Log Sink)

🚀 terraform apply開始 Link to heading

1cd terraform/environments/prod
2terraform apply -auto-approve

初回実行の結果 Link to heading

順調なスタート: Network、IAM、Secret Managerが成功

長時間待機: Cloud SQL REGIONAL HAの作成に10分以上

エラー発生: その後、連続してエラーが…


❌ エラー1: Cloud SQL innodb_buffer_pool_size が小さすぎる Link to heading

エラー内容 Link to heading

Error: Value requested is not valid. 
Failed to set innodb_buffer_pool_size: 268435456 is smaller than 20% of the instance memory.
For instances in tier GCE_CUSTOM with 7680MB RAM, 'innodb_buffer_pool_size' cannot be less than 1610612736 (1536MB).

原因 Link to heading

  • 設計時に256MBを指定していた
  • Cloud SQLには最小20%のメモリ制約がある
  • 7680MB RAMの場合、最小1536MB必要

解決方法 Link to heading

 1# ❌ 設計時の値
 2database_flags {
 3  name  = "innodb_buffer_pool_size"
 4  value = "268435456" # 256MB
 5}
 6
 7# ✅ 修正後
 8database_flags {
 9  name  = "innodb_buffer_pool_size"
10  value = "5637144576" # 5376MB (70% of 7680MB RAM)
11}

再実行 → また別のエラー Link to heading

Error: 5637144576 is greater than 55% of the instance memory.
Cannot exceed 4429185024 (4224MB).

追加修正 Link to heading

Cloud SQLの制約: 20% ≤ innodb_buffer_pool_size ≤ 55%

1# ✅ 最終版
2database_flags {
3  name  = "innodb_buffer_pool_size"
4  value = "4429185024" # 4224MB (55% of 7680MB RAM - maximum allowed)
5}

❌ エラー2: Cloud SQL point_in_time_recovery_enabled for MySQL Link to heading

エラー内容 Link to heading

Error: point_in_time_recovery_enabled is only available for [POSTGRES SQLSERVER].
You may want to consider using binary_log_enabled instead.

原因 Link to heading

  • PostgreSQL/SQL Server用の設定をMySQLに適用していた

解決方法 Link to heading

1# ❌ PostgreSQL/SQL Server用
2backup_configuration {
3  point_in_time_recovery_enabled = true
4}
5
6# ✅ MySQL用
7backup_configuration {
8  binary_log_enabled = var.availability_type == "REGIONAL"
9}

学び: バイナリログはHA構成(REGIONAL)時のみ有効化すべき


❌ エラー3: Filestore IP範囲が衝突 Link to heading

エラー内容 Link to heading

Error: reserved IP range 10.0.3.0/29 overlaps with the existing allocated IP range 10.0.3.0/29 
in network "prod-wordpress-vpc".

原因 Link to heading

  • 前回のterraform applyで既にFilestoreインスタンスが作成されていた
  • Terraform stateと実際のリソースの不整合

試行1: terraform import Link to heading

1terraform import 'module.filestore.google_filestore_instance.wordpress' \
2  'projects/infra-ai-agent/locations/asia-northeast1-a/instances/prod-wordpress-nfs'
3
4# Error: Resource already managed by Terraform

すでにstate管理下にあった。

試行2: network指定の修正 Link to heading

1# ❌ 問題のあったコード(フルリソース名)
2networks {
3  network = var.network_id  # "projects/.../networks/prod-wordpress-vpc"
4}
5
6# ✅ 修正後(名前のみ)
7networks {
8  network = var.network_name  # "prod-wordpress-vpc"
9}

prod/main.tf も修正:

1module "filestore" {
2  network_id = module.network.vpc_name  # vpc_id → vpc_name
3}

❌ エラー4: Terraform変数とシェル変数の衝突 Link to heading

エラー内容 Link to heading

Error: failed to render : <template_file>:64,7-13: Unknown variable; 
There is no variable named "NFS_IP"., and 38 other diagnostic(s)

原因 Link to heading

起動スクリプト内で、TerraformテンプレートとBashシェルの両方で${}記法を使用していたため、Terraformがシェル変数をTerraform変数として解釈しようとした。

問題のあったコード Link to heading

1# 環境変数(Terraform変数)
2NFS_IP="${nfs_ip}"         # ✅ Terraform変数
3NFS_PATH="${nfs_path}"     # ✅ Terraform変数
4
5# NFSマウント(シェル変数)
6mount -t nfs ${NFS_IP}:${NFS_PATH} /var/www/wordpress  # ❌ Terraform変数と解釈される

解決方法: $$エスケープ Link to heading

1# シェル変数として使用する箇所は$$でエスケープ
2mount -t nfs $${NFS_IP}:$${NFS_PATH} /var/www/wordpress  # ✅

大量の修正が必要 Link to heading

起動スクリプト全体で、シェル変数を全て$$にエスケープ

  • Nginx設定生成関数内の${site_num}${domain}$${site_num}$${domain}
  • WordPressセットアップスクリプト内の全シェル変数
  • ディレクトリ作成ループの${i}$${i}

修正箇所: 約50箇所

HereDocumentの引用符制御 Link to heading

1# ❌ 引用符付き(Terraform変数が展開されない)
2cat > /usr/local/bin/setup-wordpress-site.sh << 'SETUP_SCRIPT'
3  echo "${ENV}"  # ${ENV}がそのまま出力される
4SETUP_SCRIPT
5
6# ✅ 引用符なし(Terraform変数を展開、シェル変数は$$)
7cat > /usr/local/bin/setup-wordpress-site.sh << SETUP_SCRIPT
8  echo "$${ENV}"  # prod が出力される
9SETUP_SCRIPT

❌ エラー5: Service Account Scope 無効 Link to heading

エラー内容 Link to heading

Error: One or more service account scopes are invalid: 
'https://www.googleapis.com/auth/secretmanager'

原因 Link to heading

https://www.googleapis.com/auth/secretmanager という個別scopeは存在しない。

解決方法 Link to heading

 1# ❌ 個別scope(無効)
 2service_account {
 3  email = var.service_account_email
 4  scopes = [
 5    "https://www.googleapis.com/auth/cloud-platform",
 6    "https://www.googleapis.com/auth/logging.write",
 7    "https://www.googleapis.com/auth/monitoring.write",
 8    "https://www.googleapis.com/auth/secretmanager",  # ❌ 存在しない
 9  ]
10}
11
12# ✅ cloud-platform統合scope
13service_account {
14  email = var.service_account_email
15  scopes = [
16    "https://www.googleapis.com/auth/cloud-platform",  # すべてのGCP APIにアクセス可能
17  ]
18}

学び: cloud-platform scopeがあれば、IAM権限に基づいて全サービスにアクセス可能


❌ エラー6: IAM secretCreator ロール不可 Link to heading

エラー内容 Link to heading

Error: Role roles/secretmanager.secretCreator is not supported for this resource.

原因 Link to heading

roles/secretmanager.secretCreator はプロジェクトレベルのIAMバインディングには使用できない。

解決方法 Link to heading

 1# ❌ プロジェクトレベル不可
 2resource "google_project_iam_member" "web_secret_creator" {
 3  role = "roles/secretmanager.secretCreator"
 4  member  = "serviceAccount:${google_service_account.web_server.email}"
 5}
 6
 7# ✅ 最小権限の2ロールのみ
 8# 1. 読み取り
 9resource "google_project_iam_member" "web_secret_accessor" {
10  role = "roles/secretmanager.secretAccessor"
11}
12
13# 2. バージョン追加
14resource "google_project_iam_member" "web_secret_version_adder" {
15  role = "roles/secretmanager.secretVersionAdder"
16}

結果: Secretの新規作成は別の方法(事前作成、gcloud CLI等)で対応


❌ エラー7: Logging Sink GCSバケット不足 Link to heading

エラー内容 Link to heading

Error: GCS bucket must be of form: storage.googleapis.com/[BUCKET_ID]

原因 Link to heading

1resource "google_logging_project_sink" "wordpress_logs" {
2  destination = "storage.googleapis.com/"  # バケット名が空
3}

解決方法 Link to heading

Phase 2に延期 - GCS bucketを先に作成する必要がある

1# Phase 2で実装: ログ用のGCS bucketを作成してから有効化
2# resource "google_logging_project_sink" "wordpress_logs" {
3#   name        = "${var.env}-wordpress-logs-sink"
4#   destination = "storage.googleapis.com/${var.project_id}-wordpress-logs"
5#   filter      = "resource.type = \"gce_instance\" AND labels.service = \"wordpress\""
6#   unique_writer_identity = true
7# }

outputsも同様にコメントアウト。


✅ 最終的なterraform apply成功! Link to heading

1terraform apply -auto-approve

結果 Link to heading

Apply complete! Resources: 0 added, 1 changed, 1 destroyed.

Outputs:

database_names = [
  "wordpress_site_1",
  "wordpress_site_2",
  "wordpress_site_3",
  "wordpress_site_4",
  "wordpress_site_5",
  "wordpress_site_6",
  "wordpress_site_7",
  "wordpress_site_8",
  "wordpress_site_9",
  "wordpress_site_10",
]
database_private_ip = "192.168.0.2"
database_secret_ids = [
  "prod-wordpress-db-password-1",
  ...
  "prod-wordpress-db-password-10",
]
load_balancer_ip = "34.50.146.93"
nfs_mount_command = "mount -t nfs 10.0.3.2:/wordpress /mnt/wordpress"
ssl_certificate_status = "projects/infra-ai-agent/global/sslCertificates/prod-wordpress-ssl"
vpc_id = "projects/infra-ai-agent/global/networks/prod-wordpress-vpc"
web_server_service_account = "prod-web-server@infra-ai-agent.iam.gserviceaccount.com"

🎉 デプロイ完了!


📊 デプロイされたインフラ Link to heading

カテゴリリソース状態
NetworkVPC, Subnet×2, NAT, Firewall×4
ComputeMIG (2-4台), Auto Scaling
DatabaseCloud SQL HA, 10DB
StorageFilestore NFS (1TB)
Load BalancerGlobal LB, CDN, WAF
SecurityCloud Armor, Secret Manager
SSLGoogle-managed SSL⏳ プロビジョニング中
MonitoringHTTP/Health Check Alerts

🎓 学んだこと Link to heading

1. Cloud SQLの制約を理解する Link to heading

innodb_buffer_pool_sizeの制約:

  • 最小: インスタンスメモリの20%
  • 最大: インスタンスメモリの55%

設計時の注意点:

  • 小さすぎるとパフォーマンス低下
  • 制約を超えるとデプロイエラー
  • ドキュメントを確認してから設定

2. データベースエンジン固有の設定 Link to heading

設定PostgreSQL/SQL ServerMySQL
PITRpoint_in_time_recovery_enabledbinary_log_enabled
用途ポイントインタイムリカバリバイナリログレプリケーション

教訓: エンジンごとに設定項目が異なる。コピペ厳禁。

3. Terraformテンプレートとシェル変数の共存 Link to heading

問題:

1${VARIABLE}  # TerraformもBashも同じ記法

解決:

1${terraform_var}  # Terraform変数
2$${shell_var}     # シェル変数($$でエスケープ)

HereDocument制御:

1<< 'MARKER'  # 引用符付き: 変数展開なし
2<< MARKER    # 引用符なし: 変数展開あり

4. GCP Service Account Scopeの理解 Link to heading

個別scope vs 統合scope:

アプローチScope利点欠点
個別auth/logging.write細かい制御一部無効なscopeあり
統合auth/cloud-platformシンプル、確実IAM権限で制御

推奨: cloud-platform + IAMで最小権限を実現

5. IAMロールのリソースレベル制約 Link to heading

secretCreatorの例:

ロールプロジェクトレベルリソースレベル
secretmanager.secretAccessor
secretmanager.secretVersionAdder
secretmanager.secretCreator

教訓: ロールごとに使用可能なレベルが異なる。ドキュメント確認必須。

6. terraform applyは段階的に実行 Link to heading

戦略:

  1. ネットワーク・IAMから: 依存関係の少ないものから
  2. エラー → 修正 → 再実行: 諦めずに繰り返す
  3. リソースは残る: 再実行時は差分のみ適用

今回の流れ:

初回 → エラー → 修正 → 2回目 → エラー → 修正 → ...
→ 6回目で成功!

重要: Terraformは冪等性があるので、何度でも安全に再実行可能


💡 トラブルシューティングのコツ Link to heading

1. エラーメッセージを正確に読む Link to heading

Error: Value requested is not valid. 
Failed to set innodb_buffer_pool_size: 268435456 is smaller than 20% of the instance memory.
For instances in tier GCE_CUSTOM with 7680MB RAM, 'innodb_buffer_pool_size' cannot be less than 1610612736 (1536MB).

読み取るべき情報:

  • ❌ 現在の値: 268435456 (256MB)
  • ✅ 必要な値: 最低1610612736 (1536MB)
  • 📐 計算式: 7680MB × 20% = 1536MB

2. ドキュメントを確認 Link to heading

エラーが出たら:

  1. Google検索: “gcp cloud sql innodb_buffer_pool_size constraint”
  2. 公式ドキュメント: Cloud SQL設定ページ
  3. Stack Overflow: 同じエラーの人がいないか

3. terraform planで事前検証 Link to heading

1# 修正後は必ずplan
2terraform plan
3
4# エラーがなければapply
5terraform apply

4. 段階的なコメントアウト Link to heading

エラーが多い場合:

1# 一旦コメントアウトして後回し
2# resource "google_logging_project_sink" "wordpress_logs" {
3#   ...
4# }

メリット: 他のリソースを先にデプロイできる

5. terraform state確認 Link to heading

1# 既存リソースの確認
2terraform state list
3
4# 特定リソースの詳細
5terraform state show module.filestore.google_filestore_instance.wordpress

🚀 次のステップ Link to heading

1. DNSレコード設定 Link to heading

10ドメイン全てにAレコードを追加:

ai-jisso.tech               A    34.50.146.93
infra-career.tech           A    34.50.146.93
cloud-migration.tech        A    34.50.146.93
remote-dev.tech             A    34.50.146.93
monitoring-tools.tech       A    34.50.146.93
learn-code.tech             A    34.50.146.93
tech-books.tech             A    34.50.146.93
engineer-money.tech         A    34.50.146.93
travel-hack.tech            A    34.50.146.93
self-hosting.tech           A    34.50.146.93

2. SSL証明書プロビジョニング待機 Link to heading

Google-managed SSL証明書は 15-60分 かかる。

確認コマンド:

1gcloud compute ssl-certificates describe prod-wordpress-ssl --global
2
3# Status: PROVISIONING → ACTIVE

3. VM起動確認 Link to heading

1# インスタンス一覧
2gcloud compute instances list --filter="labels.service=wordpress"
3
4# 起動スクリプトログ確認
5gcloud compute instances get-serial-port-output <INSTANCE_NAME> --zone asia-northeast1-a

4. Health Check確認 Link to heading

1curl -I http://34.50.146.93/health
2# HTTP/1.1 200 OK
3# healthy

5. WordPressセットアップ Link to heading

各サイトにSSHして:

1sudo /usr/local/bin/setup-wordpress-site.sh 1 ai-jisso.tech "AI実装ブログ"

まとめ Link to heading

今回、terraform applyで6つのエラーに遭遇しましたが、一つ一つ丁寧に解決することで、最終的にデプロイに成功しました。

重要な学び:

  1. Cloud SQLの制約を理解 - 20%~55%の範囲内
  2. データベースエンジン固有の設定 - MySQLとPostgreSQLは違う
  3. Terraform変数とシェル変数の区別 - $$エスケープ
  4. GCP Service Account Scopeの選択 - cloud-platformが便利
  5. IAMロールのレベル制約 - ロールごとに異なる
  6. 段階的なデプロイ戦略 - エラーを恐れず、繰り返す

Terraformの強み:

  • 冪等性があるので何度でも再実行可能
  • エラーが出ても、既存リソースは保持される
  • 差分だけを適用するので効率的

次回予告:

  • SSL証明書プロビジョニング完了後の動作確認
  • WordPress初期セットアップ
  • 実運用開始とモニタリング

参考リンク Link to heading


前回の記事: Terraform実装完了!GCP上でWordPressマルチテナント環境を構築

次回予告: WordPress初期セットアップとSSL証明書確認編