はじめに Link to heading
「データベースに接続できない」
この一言の裏には、ネットワーク、認証、SSL証明書、パスワード管理など、複数の階層にわたる設定が絡み合っています。
GCP上でWordPress マルチテナント環境を構築する過程で、Cloud SQLへの接続で何度も壁にぶつかりました。この記事では、その経験から得たCloud SQL接続のベストプラクティスとトラブルシューティング手法を共有します。
この記事で扱う内容:
- Cloud SQLの接続方式(プライベートIP vs パブリックIP)
- SSL/TLS設定の理解と選択
- Secret Managerを使ったパスワード管理
- 接続エラーのデバッグ手法
- Terraformでの適切な設定方法
1. Cloud SQL接続方式の選択 Link to heading
プライベートIP vs パブリックIP Link to heading
Cloud SQLには2つの接続方式があります:
| 接続方式 | 説明 | ユースケース | SSL要件 |
|---|---|---|---|
| プライベートIP | VPC内部からのみアクセス可能 | 本番環境、セキュアな接続 | 不要(推奨) |
| パブリックIP | インターネットから接続可能 | 開発環境、外部ツール接続 | 必須 |
今回の構成: プライベートIP接続
┌────────────────────────────────────────┐
│ VPC Network │
│ │
│ ┌──────────────┐ ┌─────────────┐ │
│ │ Compute VM │───▶│ Cloud SQL │ │
│ │ 10.0.1.x │ │ 10.168.0.2 │ │
│ └──────────────┘ └─────────────┘ │
│ │
│ Private IP Connection │
│ No SSL Required │
└────────────────────────────────────────┘
プライベートIP接続のメリット Link to heading
- セキュリティ: インターネットに公開されない
- レイテンシ: VPC内部通信で低遅延
- コスト: エグレス料金が発生しない
- シンプルさ: SSL証明書の管理が不要
Terraformでの設定 Link to heading
1# terraform/modules/database/main.tf
2resource "google_sql_database_instance" "wordpress" {
3 name = "${var.env}-wordpress-db"
4 database_version = "MYSQL_8_0"
5 region = var.region
6
7 settings {
8 tier = "db-f1-micro"
9
10 # プライベートIP接続設定
11 ip_configuration {
12 ipv4_enabled = false # パブリックIPを無効化
13 private_network = var.network_id # VPCネットワークを指定
14 require_ssl = false # SSL要件を無効化
15 }
16
17 # バックアップ設定
18 backup_configuration {
19 enabled = true
20 start_time = "03:00"
21 binary_log_enabled = true
22 }
23 }
24}
重要なポイント Link to heading
ipv4_enabled = false
- パブリックIPアドレスを割り当てない
- インターネットからのアクセスを完全にブロック
private_network = var.network_id
- VPCネットワークを指定
- Private Service Connectionを使用
require_ssl = false
- プライベートIP接続ではSSL不要
- これを
trueにすると証明書管理が必要になる
2. SSL/TLS設定の理解 Link to heading
SSL証明書が必要なケース Link to heading
1必要:
2 □ パブリックIP接続
3 □ インターネット経由でのアクセス
4 □ コンプライアンス要件
5
6不要:
7 □ プライベートIP接続(VPC内部)
8 □ トラフィックが暗号化されたネットワーク
SSL証明書のライフサイクル Link to heading
Cloud SQLでSSL証明書を使う場合の手順:
1. サーバーCA証明書の取得 Link to heading
1# サーバーCA証明書をダウンロード
2gcloud sql ssl-certs describe server-ca \
3 --instance=prod-wordpress-db \
4 --format="value(cert)" > server-ca.pem
2. クライアント証明書の作成 Link to heading
1# クライアント証明書を作成
2gcloud sql ssl-certs create wordpress-client \
3 --instance=prod-wordpress-db
4
5# クライアント証明書をダウンロード
6gcloud sql ssl-certs describe wordpress-client \
7 --instance=prod-wordpress-db \
8 --format="value(cert)" > client-cert.pem
9
10# 秘密鍵をダウンロード
11gcloud sql ssl-certs describe wordpress-client \
12 --instance=prod-wordpress-db \
13 --format="value(privateKey)" > client-key.pem
3. MySQLクライアントでの使用 Link to heading
1mysql \
2 --host=10.168.0.2 \
3 --user=wp_user_1 \
4 --password \
5 --ssl-ca=server-ca.pem \
6 --ssl-cert=client-cert.pem \
7 --ssl-key=client-key.pem
SSL証明書の有効期限管理 Link to heading
1# 証明書一覧と有効期限確認
2gcloud sql ssl-certs list \
3 --instance=prod-wordpress-db \
4 --format="table(commonName,expirationTime)"
5
6# 出力例
7# COMMON_NAME EXPIRATION_TIME
8# wordpress-client 2025-01-20T12:00:00Z
9# server-ca 2035-01-18T12:00:00Z
注意点:
- クライアント証明書: 有効期限10年
- サーバーCA証明書: 有効期限10年
- 期限切れ前に再発行が必要
SSL無効化の判断基準 Link to heading
SSL無効化を推奨するケース:
- ✅ プライベートIP接続のみ
- ✅ VPC内部の通信
- ✅ 証明書管理コストを削減したい
- ✅ パフォーマンスを優先したい
SSL有効化が必須のケース:
- ❌ パブリックIP接続
- ❌ インターネット経由のアクセス
- ❌ コンプライアンス要件(PCI-DSS等)
- ❌ 監査要件
3. Secret Managerを使ったパスワード管理 Link to heading
なぜSecret Managerを使うのか Link to heading
NGパターン:
1# ❌ 平文でパスワードを記述
2resource "google_sql_user" "wordpress_user" {
3 password = "my_super_secret_password" # 絶対にダメ!
4}
問題点:
- Terraformステートファイルに平文で保存される
- Gitにコミットされるリスク
- チームメンバー全員がパスワードを知ることになる
正しいアプローチ:
1# ✅ Secret Managerでパスワード管理
2resource "random_password" "db_passwords" {
3 count = 10
4 length = 20
5 special = true
6}
7
8resource "google_secret_manager_secret" "db_passwords" {
9 count = 10
10 secret_id = "${var.env}-wordpress-db-password-${count.index + 1}"
11
12 replication {
13 auto {}
14 }
15}
16
17resource "google_secret_manager_secret_version" "db_passwords" {
18 count = 10
19 secret = google_secret_manager_secret.db_passwords[count.index].id
20 secret_data = random_password.db_passwords[count.index].result
21}
パスワードのライフサイクル管理 Link to heading
1. パスワード生成(Terraform) Link to heading
1resource "random_password" "db_passwords" {
2 length = 20
3 special = true
4
5 lifecycle {
6 ignore_changes = [length, special] # 再生成を防ぐ
7 }
8}
2. Secret Managerに保存 Link to heading
1# 手動でパスワードを設定する場合
2echo -n "your_secure_password" | \
3 gcloud secrets versions add prod-wordpress-db-password-1 \
4 --data-file=-
3. Cloud SQLユーザーに設定 Link to heading
1# Secret Managerからパスワードを取得してCloud SQLに設定
2PASSWORD=$(gcloud secrets versions access latest \
3 --secret=prod-wordpress-db-password-1)
4
5gcloud sql users set-password wp_user_1 \
6 --instance=prod-wordpress-db \
7 --password="$PASSWORD"
4. アプリケーションからの取得 Link to heading
1# Ansibleでの取得例
2- name: Secret Managerからパスワード取得
3 command: >
4 gcloud secrets versions access latest
5 --secret={{ env }}-wordpress-db-password-{{ item }}
6 --project={{ gcp_project_id }}
7 register: db_password
8 no_log: true # ログに出力しない
9 loop: "{{ range(1, 11) | list }}"
パスワード不一致のトラブルシューティング Link to heading
症状:
ERROR 1045 (28000): Access denied for user 'wp_user_1'@'10.0.1.21' (using password: YES)
原因:
- Secret ManagerとCloud SQLのパスワードが不一致
- Terraform applyでパスワードが再生成された
- 手動でパスワードを変更した
解決策: 全ユーザーのパスワードを同期
1#!/bin/bash
2# sync-db-passwords.sh
3
4PROJECT_ID="infra-ai-agent"
5INSTANCE_NAME="prod-wordpress-db"
6ENV="prod"
7
8for i in {1..10}; do
9 echo "Syncing password for wp_user_$i..."
10
11 # Secret Managerから最新のパスワードを取得
12 PASSWORD=$(gcloud secrets versions access latest \
13 --secret="${ENV}-wordpress-db-password-${i}" \
14 --project="${PROJECT_ID}")
15
16 # Cloud SQLユーザーのパスワードを更新
17 gcloud sql users set-password "wp_user_${i}" \
18 --instance="${INSTANCE_NAME}" \
19 --password="${PASSWORD}" \
20 --project="${PROJECT_ID}"
21
22 echo "✅ wp_user_$i synchronized"
23done
24
25echo "🎉 All passwords synchronized!"
実行結果:
1chmod +x sync-db-passwords.sh
2./sync-db-passwords.sh
3
4# Syncing password for wp_user_1...
5# ✅ wp_user_1 synchronized
6# Syncing password for wp_user_2...
7# ✅ wp_user_2 synchronized
8# ...
9# 🎉 All passwords synchronized!
4. 接続エラーのデバッグ手法 Link to heading
デバッグの基本フロー Link to heading
┌─────────────────────────────────────────┐
│ Step 1: ネットワーク疎通確認 │
│ ping, nc, telnet │
└────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Step 2: MySQLクライアントで接続テスト │
│ mysql -h HOST -u USER -p │
└────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Step 3: Cloud Loggingでログ確認 │
│ gcloud logging read │
└────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Step 4: 設定の検証 │
│ Terraform, wp-config.php │
└─────────────────────────────────────────┘
Step 1: ネットワーク疎通確認 Link to heading
1# 1. Cloud SQLインスタンスのIPアドレス確認
2gcloud sql instances describe prod-wordpress-db \
3 --format="value(ipAddresses[0].ipAddress)"
4# 出力: 10.168.0.2
5
6# 2. Compute VMからping
7gcloud compute ssh prod-web-l0br \
8 --zone=asia-northeast1-a \
9 --tunnel-through-iap \
10 --command="ping -c 3 10.168.0.2"
11
12# 3. ポート疎通確認(3306)
13gcloud compute ssh prod-web-l0br \
14 --zone=asia-northeast1-a \
15 --tunnel-through-iap \
16 --command="nc -zv 10.168.0.2 3306"
17# 出力: Connection to 10.168.0.2 3306 port [tcp/mysql] succeeded!
Step 2: MySQLクライアントで接続テスト Link to heading
1# Cloud SQL Proxyを使った接続テスト
2cloud_sql_proxy -instances=infra-ai-agent:asia-northeast1:prod-wordpress-db=tcp:3306 &
3
4# ローカルから接続
5mysql -h 127.0.0.1 -u wp_user_1 -p
6
7# 接続成功の確認
8mysql> SELECT USER(), DATABASE();
9+-------------------------+----------+
10| USER() | DATABASE()|
11+-------------------------+----------+
12| wp_user_1@10.0.1.21 | NULL |
13+-------------------------+----------+
Step 3: Cloud Loggingでログ確認 Link to heading
1# Cloud SQLの接続ログを確認
2gcloud logging read \
3 'resource.type="cloudsql_database"
4 AND logName="projects/infra-ai-agent/logs/cloudsql.googleapis.com%2Fmysql.err"' \
5 --limit 50 \
6 --format json \
7 --project=infra-ai-agent
8
9# 認証エラーの検索
10gcloud logging read \
11 'resource.type="cloudsql_database"
12 AND textPayload=~"Access denied"' \
13 --limit 10 \
14 --format json
Step 4: 設定ファイルの検証 Link to heading
wp-config.php の確認 Link to heading
1# リモートサーバーでwp-config.phpを確認
2gcloud compute ssh prod-web-l0br \
3 --zone=asia-northeast1-a \
4 --tunnel-through-iap \
5 --command="sudo grep -E '^define.*DB_' /var/www/wordpress/site1/wp-config.php"
6
7# 出力例
8# define('DB_NAME', 'wordpress_db_1');
9# define('DB_USER', 'wp_user_1');
10# define('DB_PASSWORD', '***');
11# define('DB_HOST', '10.168.0.2');
データベース存在確認 Link to heading
1# データベース一覧を取得
2gcloud sql databases list \
3 --instance=prod-wordpress-db \
4 --format="table(name,charset,collation)"
5
6# ユーザー一覧を取得
7gcloud sql users list \
8 --instance=prod-wordpress-db \
9 --format="table(name,host)"
よくあるエラーと解決策 Link to heading
エラー1: Can't connect to MySQL server
Link to heading
原因:
- ネットワーク疎通がない
- ファイアウォールルールが不足
- Cloud SQLが起動していない
確認:
1# Cloud SQLの状態確認
2gcloud sql instances describe prod-wordpress-db \
3 --format="value(state)"
4# 期待値: RUNNABLE
5
6# ファイアウォールルール確認
7gcloud compute firewall-rules list \
8 --filter="network:prod-vpc" \
9 --format="table(name,direction,allowed[].ports)"
エラー2: Access denied for user
Link to heading
原因:
- パスワードが間違っている
- ユーザーが存在しない
- ホスト制限
確認:
1# ユーザーの存在確認
2gcloud sql users list --instance=prod-wordpress-db
3
4# パスワード同期
5PASSWORD=$(gcloud secrets versions access latest --secret=prod-wordpress-db-password-1)
6gcloud sql users set-password wp_user_1 \
7 --instance=prod-wordpress-db \
8 --password="$PASSWORD"
エラー3: SSL connection error
Link to heading
原因:
require_ssl = trueだが証明書がない- 証明書のパスが間違っている
解決:
1# Terraformで設定変更
2resource "google_sql_database_instance" "wordpress" {
3 settings {
4 ip_configuration {
5 require_ssl = false # プライベートIP接続ならfalse
6 }
7 }
8}
5. ベストプラクティス Link to heading
セキュリティ Link to heading
1□ プライベートIP接続を使用
2□ パブリックIPは無効化(ipv4_enabled = false)
3□ Secret Managerでパスワード管理
4□ 最小権限の原則(データベース権限)
5□ IAMによるCloud SQLへのアクセス制御
可用性 Link to heading
1□ 自動バックアップ有効化
2□ バイナリログ有効化(ポイントインタイムリカバリ用)
3□ 高可用性構成(本番環境)
4□ メンテナンスウィンドウの設定
パフォーマンス Link to heading
1□ 適切なマシンタイプ選択
2□ ストレージタイプの選択(SSD推奨)
3□ 接続プーリングの使用
4□ スロークエリログの監視
運用 Link to heading
1□ Cloud Loggingでログ監視
2□ Cloud Monitoringでメトリクス監視
3□ アラートポリシーの設定
4□ 定期的なバックアップテスト
Terraformでの完全な設定例 Link to heading
1# terraform/modules/database/main.tf
2resource "google_sql_database_instance" "wordpress" {
3 name = "${var.env}-wordpress-db"
4 database_version = "MYSQL_8_0"
5 region = var.region
6
7 settings {
8 tier = "db-custom-2-7680" # 本番用
9 availability_type = "REGIONAL" # 高可用性
10
11 # IPアドレス設定
12 ip_configuration {
13 ipv4_enabled = false
14 private_network = var.network_id
15 require_ssl = false
16 }
17
18 # バックアップ設定
19 backup_configuration {
20 enabled = true
21 start_time = "03:00"
22 binary_log_enabled = true
23 transaction_log_retention_days = 7
24 backup_retention_settings {
25 retained_backups = 30
26 retention_unit = "COUNT"
27 }
28 }
29
30 # メンテナンス設定
31 maintenance_window {
32 day = 7 # 日曜日
33 hour = 3 # 午前3時
34 update_track = "stable"
35 }
36
37 # データベースフラグ
38 database_flags {
39 name = "max_connections"
40 value = "200"
41 }
42
43 database_flags {
44 name = "slow_query_log"
45 value = "on"
46 }
47
48 # ログ設定
49 insights_config {
50 query_insights_enabled = true
51 query_plans_per_minute = 5
52 query_string_length = 1024
53 record_application_tags = true
54 }
55 }
56
57 deletion_protection = true # 本番環境では必須
58
59 lifecycle {
60 prevent_destroy = true
61 }
62}
63
64# データベース作成
65resource "google_sql_database" "wordpress_dbs" {
66 count = 10
67 name = "wordpress_db_${count.index + 1}"
68 instance = google_sql_database_instance.wordpress.name
69 charset = "utf8mb4"
70 collation = "utf8mb4_unicode_ci"
71}
72
73# ユーザー作成
74resource "google_sql_user" "wordpress_users" {
75 count = 10
76 name = "wp_user_${count.index + 1}"
77 instance = google_sql_database_instance.wordpress.name
78 password = random_password.db_passwords[count.index].result
79
80 lifecycle {
81 ignore_changes = [password] # パスワード再生成を防ぐ
82 }
83}
6. トラブルシューティングチェックリスト Link to heading
接続できない時の確認順序 Link to heading
1. ネットワーク層
□ Cloud SQLのIPアドレスは正しいか?
□ VPCネットワークは同じか?
□ Private Service Connectionは設定されているか?
□ ファイアウォールルールは適切か?
□ pingは通るか?
□ ポート3306は開いているか?
2. 認証層
□ ユーザー名は正しいか?
□ パスワードは正しいか?
□ Secret Managerと同期されているか?
□ ユーザーは存在するか?
□ ホスト制限は適切か?
3. SSL層
□ require_ssl設定は正しいか?
□ 証明書は有効期限内か?
□ 証明書のパスは正しいか?
□ プライベートIP接続ならSSL不要か確認
4. データベース層
□ データベースは存在するか?
□ ユーザーに権限はあるか?
□ Cloud SQLは起動しているか?
□ メンテナンス中ではないか?
5. アプリケーション層
□ wp-config.phpの設定は正しいか?
□ PHPのmysqli拡張は有効か?
□ 接続プーリングは適切か?
□ タイムアウト設定は十分か?
コマンド集 Link to heading
1# === Cloud SQL情報取得 ===
2# インスタンス詳細
3gcloud sql instances describe INSTANCE_NAME
4
5# IPアドレス取得
6gcloud sql instances describe INSTANCE_NAME \
7 --format="value(ipAddresses[0].ipAddress)"
8
9# 状態確認
10gcloud sql instances describe INSTANCE_NAME \
11 --format="value(state)"
12
13# === データベース管理 ===
14# データベース一覧
15gcloud sql databases list --instance=INSTANCE_NAME
16
17# ユーザー一覧
18gcloud sql users list --instance=INSTANCE_NAME
19
20# パスワード変更
21gcloud sql users set-password USER_NAME \
22 --instance=INSTANCE_NAME \
23 --password="NEW_PASSWORD"
24
25# === 接続テスト ===
26# ポート疎通確認
27nc -zv DB_IP 3306
28
29# MySQLクライアント接続
30mysql -h DB_IP -u USER_NAME -p
31
32# === Secret Manager ===
33# シークレット一覧
34gcloud secrets list
35
36# シークレット取得
37gcloud secrets versions access latest --secret=SECRET_NAME
38
39# === ログ確認 ===
40# Cloud SQLエラーログ
41gcloud logging read \
42 'resource.type="cloudsql_database"
43 AND logName=~"mysql.err"' \
44 --limit 50
45
46# 認証エラー検索
47gcloud logging read \
48 'resource.type="cloudsql_database"
49 AND textPayload=~"Access denied"' \
50 --limit 10
まとめ Link to heading
Cloud SQL接続の3大ポイント Link to heading
プライベートIP接続 + SSL無効化
- セキュアかつシンプル
- 証明書管理不要
- パフォーマンス向上
Secret Managerでパスワード管理
- 平文でのパスワード保存を避ける
- 定期的な同期スクリプト実行
lifecycle.ignore_changesで再生成防止
段階的なデバッグ
- ネットワーク → 認証 → SSL → アプリケーション
- Cloud Loggingを活用
- 各層で確実に検証
よくあるミスと対策 Link to heading
| ミス | 対策 |
|---|---|
| パスワード不一致 | 同期スクリプトの定期実行 |
| SSL証明書エラー | プライベートIP接続ならSSL無効化 |
| ネットワーク疎通なし | Private Service Connection確認 |
| 権限不足 | IAMロールとデータベース権限の両方確認 |
次のステップ Link to heading
- Cloud SQL Proxyの導入検討
- 読み取りレプリカの設定
- パフォーマンスチューニング
- 監視とアラート強化
参考リンク Link to heading
- Cloud SQL - Private IP
- Cloud SQL - SSL/TLS certificates
- Secret Manager
- Terraform - google_sql_database_instance
この記事のコード Link to heading
GitHub: infra-ai-agent
関連ファイル:
この記事が役に立ったら: GitHub Starをいただけると嬉しいです! infra-ai-agent