G-genの福井です。Cloud Run から Cloud SQL に対し、内部通信と IAM データベース認証を使用してセキュアに接続する手順を紹介します。
はじめに
当記事の概要
当記事では、Google Cloud のフルマネージドなコンテナ実行環境である Cloud Run から、同じくフルマネージドなリレーショナルデータベースサービスである Cloud SQL へセキュアに接続する手順について解説します。
具体的には、以下2点の方法でセキュアな接続を実現します。
内部通信での接続
当記事では、Cloud SQL インスタンスに外部 IP アドレスを付与せず、VPC ネットワーク内の閉じた経路で Cloud Run から接続します。これにより、セキュリティリスクを低減します。
また、Cloud Run から VPC ネットワークへのアクセスには、Direct VPC Egress を利用します。
IAM データベース認証
Cloud SQL のデータベースユーザーとパスワードでの認証の代わりに、サービスアカウントを利用した IAM データベース認証を使用します。これにより、パスワード管理の煩雑さから解放され、アクセス制御を IAM で統一管理できるため、よりセキュアにデータベースへのアクセスが実現できます。
- 参考 : IAM 認証
事前準備
API の有効化
Cloud Run、Cloud SQL、VPC ネットワーク、およびコンテナイメージの保存に必要な Artifact Registry などの API を有効化します。
gcloud services enable \ sqladmin.googleapis.com \ compute.googleapis.com \ servicenetworking.googleapis.com \ artifactregistry.googleapis.com \ cloudbuild.googleapis.com \ run.googleapis.com
環境変数の設定
以降の手順で繰り返し使用する Google Cloud プロジェクト ID やリージョンなどの情報を環境変数に設定します。
ご自身の環境に合わせて、以下のコマンドで環境変数を設定してください。
# Google Cloud プロジェクト ID # ※ ご自身の Google Cloud プロジェクト ID に置き換えてください export PROJECT_ID="your-project-id" # プロジェクトのデフォルトリージョン export REGION="asia-northeast1" # Cloud SQL インスタンス名 export SQL_INSTANCE_NAME="my-postgres-instance" # Cloud Run サービス名 export RUN_SERVICE_NAME="my-app-service" # VPC ネットワーク名 export VPC_NETWORK_NAME="my-vpc-network" # サブネット名 export SUBNET_NAME="my-subnet" # Cloud SQL が使用するプライベート IP アドレス範囲名 export PRIVATE_IP_RANGE_NAME="my-ip-range" # Cloud Run で使用するサービスアカウント名 export RUN_SA_NAME="run-sa-for-sql" # Cloud SQL のデータベース名 export DB_NAME="postgres"
環境構築
ネットワーク環境の構築
Cloud Run と Cloud SQL が安全に通信するためのプライベートなネットワーク環境を構築します。
VPC ネットワークの作成
Cloud Run サービスが Cloud SQL インスタンスへプライベートにアクセスするための基盤となる、VPC ネットワークを作成します。
この VPC ネットワークを通じて、Cloud Run と Cloud SQL 間の安全な通信経路を確立します。今回は、サブネットの IP アドレス範囲を自分で定義できるカスタムモードで作成します。
gcloud compute networks create $VPC_NETWORK_NAME \ --subnet-mode custom
サブネットの作成
作成した VPC ネットワーク内にサブネットを作成します。
このサブネットは、Cloud Run サービスから VPC ネットワーク内への通信(Direct VPC Egress)における経由点となります。
gcloud compute networks subnets create $SUBNET_NAME \ --network=$VPC_NETWORK_NAME \ --range=10.0.0.0/24 \ --region=$REGION
プライベートサービスアクセスのための IP アドレス範囲の作成
Cloud SQL などの Google マネージドサービスを VPC ネットワークにプライベート接続するためには、それらのサービス専用の IP アドレス範囲を VPC ネットワーク内に予約する必要があります。
以下のコマンドで、VPC ネットワークに名前付きの IP アドレス範囲を作成します。この範囲は、Cloud SQL インスタンスが VPC ネットワーク内で使用する IP アドレスのプールとなります。
gcloud compute addresses create $PRIVATE_IP_RANGE_NAME \ --global \ --purpose=VPC_PEERING \ --prefix-length=24 \ --network=$VPC_NETWORK_NAME
プライベートサービスアクセスの構成
作成した IP アドレス範囲を使用して、VPC ネットワークと Google Cloud のサービスプロデューサーネットワークとの間にプライベートサービスアクセスの接続を確立します。
gcloud services vpc-peerings connect \ --service=servicenetworking.googleapis.com \ --network=$VPC_NETWORK_NAME \ --ranges=$PRIVATE_IP_RANGE_NAME
Cloud SQL インスタンスの作成
Cloud Run サービスから接続する Cloud SQL for PostgreSQL インスタンスを作成します。
インスタンスの作成
以下のコマンドで、PostgreSQL インスタンスを作成します。重要なポイントは以下の通りです。
パラメータ | 説明 |
---|---|
--network | 先ほど作成し構成した VPC ネットワークを指定します。これにより、Cloud SQL インスタンスがこの VPC ネットワークに関連付けられます。 |
--no-assign-ip | インスタンスに外部 IP アドレスが割り当てられないようにし、内部 IP アドレスのみを持つようにします。これにより、VPC ネットワーク外部からの直接的なアクセスを防ぎ、セキュリティを向上させます。 |
--database-flags=cloudsql.iam_authentication=on | IAM データベース認証を有効にします。これにより、データベースユーザー名とパスワードの代わりに、IAM プリンシパルを使用して認証できます。 |
gcloud sql instances create $SQL_INSTANCE_NAME \ --database-version=POSTGRES_16 \ --region=$REGION \ --tier=db-custom-1-3840 \ --edition=ENTERPRISE \ --network=$VPC_NETWORK_NAME \ --no-assign-ip \ --database-flags=cloudsql.iam_authentication=on
アプリケーションの準備
ディレクトリ構成
今回開発する Python アプリケーションのディレクトリ構成は以下のとおりです。
cloud-sql-sec-connect-demo-app |-- requirements.txt |-- Dockerfile |-- main.py
アプリケーションソース
Cloud SQL に接続して簡単な情報を取得する Python のウェブアプリケーションを作成します。
Cloud SQL への接続では、Cloud SQL Python Connector を使用します。Cloud SQL Python Connector は、IAM データベース認証などの安全な接続を容易に実現するためのライブラリです。
requirements.txt
Flask==3.1.1 gunicorn==23.0.0 Werkzeug==3.1.3 SQLAlchemy==2.0.41 cloud-sql-python-connector[pg8000]==1.18.2
Dockerfile
FROM python:3.12-slim WORKDIR /usr/src/app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 7860 CMD [ "python", "./main.py" ]
main.py
import os from flask import Flask, jsonify from google.cloud.sql.connector import Connector, IPTypes import sqlalchemy from sqlalchemy import text app = Flask(__name__) INSTANCE_CONNECTION_NAME = os.environ["INSTANCE_CONNECTION_NAME"] DB_USER = os.environ["DB_USER"] DB_NAME = os.environ["DB_NAME"] connector = Connector() engine = sqlalchemy.create_engine( "postgresql+pg8000://", creator=lambda: connector.connect( INSTANCE_CONNECTION_NAME, "pg8000", user=DB_USER, db=DB_NAME, enable_iam_auth=True, ip_type=IPTypes.PRIVATE, ), ) @app.route("/") def index(): with engine.connect() as conn: version = conn.execute(text("SELECT version()")).scalar() current_db = conn.execute(text("SELECT current_database()")).scalar() current_user = conn.execute(text("SELECT current_user")).scalar() return jsonify({ "postgres_version": version, "current_database": current_db, "current_user": current_user }) if __name__ == "__main__": app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 7860)))
Cloud Run へのデプロイ
サービスアカウントの作成と権限付与
Cloud Run サービスが Cloud SQL インスタンスへ安全に接続し、IAM データベース認証を利用するためには、専用のサービスアカウントを作成し、必要な IAM ロールを付与します。サービスアカウントを使用することで、個人の認証情報ではなく、アプリケーション固有の認証情報で Google Cloud サービスを認証できます。
サービスアカウントの作成
gcloud iam service-accounts create $RUN_SA_NAME \ --display-name="Cloud Run Service Account for SQL"
サービスアカウントへの権限付与
# Cloud SQL インスタンスに接続するためのロール gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$RUN_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/cloudsql.client" # IAM データベース認証を使用して Cloud SQL インスタンスにログインするためのロール gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$RUN_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/cloudsql.instanceUser" # Cloud Run サービスが Cloud Logging にログを書き込むためのロール gcloud projects add-iam-policy-binding $PROJECT_ID \ --member="serviceAccount:$RUN_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com" \ --role="roles/logging.logWriter"
Cloud SQL ユーザーとしてのサービスアカウント登録
前の手順でサービスアカウントに必要な IAM ロール(roles/cloudsql.instanceUser
など)を付与しましたが、それだけでは IAM データベース認証は完了しません。実際にデータベースにログインできるようにするには、このサービスアカウントを Cloud SQL インスタンスのユーザーとして登録する必要があります。
以下のコマンドを実行して、先ほど作成したサービスアカウントを Cloud SQL インスタンスに IAM 認証ユーザーとして追加します。
重要な点として、--type=CLOUD_IAM_SERVICE_ACCOUNT
を指定し、ユーザー名にはサービスアカウントのメールアドレスから末尾の .gserviceaccount.com
を除いた部分を指定します。
gcloud sql users create $RUN_SA_NAME@$PROJECT_ID.iam \ --instance=$SQL_INSTANCE_NAME \ --type=CLOUD_IAM_SERVICE_ACCOUNT
Cloud Run にデプロイ
Dockerfile が存在するディレクトリで、以下の gcloud コマンドを順次実行します。
gcloud run deploy $RUN_SERVICE_NAME --source . \ --region=$REGION \ --allow-unauthenticated \ --port 7860 \ --memory=1Gi \ --min-instances=0 \ --max-instances=1 \ --service-account=$RUN_SA_NAME@$PROJECT_ID.iam.gserviceaccount.com \ --network=$VPC_NETWORK_NAME \ --subnet=$SUBNET_NAME \ --vpc-egress=private-ranges-only \ --set-env-vars=INSTANCE_CONNECTION_NAME=$PROJECT_ID:$REGION:$SQL_INSTANCE_NAME,DB_USER=$RUN_SA_NAME@$PROJECT_ID.iam,DB_NAME=$DB_NAME
動作確認
Cloud Run サービスのデプロイが完了したら、実際にサービスにアクセスして、Cloud SQL インスタンスへ正しく接続できているかを確認します。
gcloud run deploy
コマンドの実行が成功すると、コンソールに Service URL:
としてデプロイされたサービスの URL が表示されます。この URL にウェブブラウザでアクセスするか、curl
コマンドでリクエストを送信してください。
# デプロイ時に表示された Service URL に置き換えてください
curl https://my-app-service-XXXXX.asia-northeast1.run.app
正しく設定されていれば、以下のような JSON 形式のレスポンスが返ってきます。表示される内容は、ご自身の環境や PostgreSQL のバージョンによって若干異なる場合があります。
{ "current_database": "postgres", "current_user": "run-sa-for-sql@your-project-id.iam", "postgres_version": "PostgreSQL 16.9 on x86_64-pc-linux-gnu, compiled by Debian clang version 12.0.1, 64-bit" }
福井 達也(記事一覧)
カスタマーサクセス課 エンジニア
2024年2月 G-gen JOIN
元はアプリケーションエンジニア(インフラはAWS)として、PM/PL・上流工程を担当。G-genのGoogle Cloudへの熱量、Google Cloudの魅力を味わいながら日々精進