Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
<PackageVersion Include="StackExchange.Redis" Version="2.8.41" />
<PackageVersion Include="DistributedLock.Redis" Version="1.1.1" />

<!-- Google Cloud Authentication -->
<PackageVersion Include="Google.Apis.Auth" Version="1.69.0" />

<!-- Logging -->
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Enrichers.Environment" Version="3.0.1" />
Expand Down
12 changes: 7 additions & 5 deletions docker/game-realtime/prod/cloudbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
# 使用方法(Cloud Shell または gcloud CLI):
# cd Unity6Portfolio
# gcloud builds submit --config=docker/game-realtime/prod/cloudbuild.yml \
# --substitutions=_JWT_SECRET=xxx,_VALKEY_HOST=10.0.0.3
# --substitutions=_VALKEY_HOST=10.0.0.3
#
# 注意: 機密情報は Secret Manager を使用することを推奨
# 機密情報は Google Cloud Secret Manager で管理

steps:
# ===== Docker イメージをビルド =====
Expand Down Expand Up @@ -57,9 +57,9 @@ steps:
--vpc-connector=${_VPC_CONNECTOR} \
--set-env-vars="ASPNETCORE_ENVIRONMENT=Production" \
--set-env-vars="ConnectionStrings__Valkey=${_VALKEY_HOST}:${_VALKEY_PORT},abortConnect=false,connectTimeout=5000" \
--set-env-vars="Jwt__Secret=${_JWT_SECRET}" \
--set-env-vars="Jwt__Issuer=${_JWT_ISSUER}" \
--set-env-vars="Jwt__Audience=${_JWT_AUDIENCE}" \
--set-secrets="Jwt__Secret=${_SECRET_JWT}:latest" \
--memory=512Mi \
--cpu=1 \
--min-instances=1 \
Expand All @@ -72,9 +72,11 @@ steps:
# ===== 変数(デフォルト値) =====
substitutions:
_REGION: asia-northeast1
_REPO_NAME: game-server
_REPO_NAME: game-realtime
_SERVICE_NAME: game-realtime
_JWT_SECRET: ''
# Secret Manager シークレット名
_SECRET_JWT: game-jwt-secret
# 非機密設定
_JWT_ISSUER: Game.Server
_JWT_AUDIENCE: Game.Client
# Memorystore for Valkey 設定
Expand Down
43 changes: 38 additions & 5 deletions docker/game-server/prod/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,44 @@ DB_NAME=gamedb
DB_USER=gameserver
DB_PASSWORD=secure-password-here
SERVICE_NAME=game-server
JWT_SECRET_KEY=your-32-char-minimum-secret-key
JWT_ISSUER=GameServer
JWT_AUDIENCE=GameClient

# Secret Manager シークレット名
SECRET_DB_CONNECTION=game-server-db-connection-string
SECRET_JWT=game-jwt-secret
SECRET_RESEND=game-resend-api-key

Jwt__Issuer=Game.Server
Jwt__Audience=Game.Client
```

### 2. Secret Manager の設定

機密情報は Google Cloud Secret Manager で管理します。

```bash
# シークレットを作成
# ConnectionStrings__Default: Cloud SQL 接続文字列全体を格納
echo -n "Host=/cloudsql/PROJECT:REGION:INSTANCE;Database=gamedb;Username=gameserver;Password=YOUR_PASSWORD" \
| gcloud secrets create game-server-db-connection-string --data-file=-

echo -n "your-jwt-secret-key-min-32-characters" \
| gcloud secrets create game-jwt-secret --data-file=-

echo -n "re_xxxxxxxx" \
| gcloud secrets create game-resend-api-key --data-file=-

# Cloud Run サービスアカウントに読み取り権限を付与
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")

for SECRET in game-server-db-connection-string game-jwt-secret game-resend-api-key; do
gcloud secrets add-iam-policy-binding $SECRET \
--member="serviceAccount:${PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
--role="roles/secretmanager.secretAccessor"
done
```

> **注意**: `DB_PASSWORD` は `.env` に残りますが、これはローカルの db.sh/db.ps1(Cloud SQL Proxy 経由)でのみ使用されます。Cloud Run へのデプロイでは Secret Manager のシークレットが使用されます。

## デプロイ方法

### 方法1: デプロイスクリプト(推奨)
Expand Down Expand Up @@ -136,12 +169,12 @@ Cloud Run デプロイ時に Cloud SQL を接続:
# 接続名を取得
$CONNECTION_NAME = gcloud sql instances describe $env:INSTANCE_NAME --format="value(connectionName)"

# Cloud SQL 付きでデプロイ
# Cloud SQL 付きでデプロイ(機密情報は Secret Manager から取得)
gcloud run deploy $env:SERVICE_NAME `
--image="${IMAGE}:latest" `
--region=$env:REGION `
--add-cloudsql-instances=$CONNECTION_NAME `
--set-env-vars="ConnectionStrings__Default=Host=/cloudsql/$CONNECTION_NAME;Database=$env:DB_NAME;Username=$env:DB_USER;Password=$env:DB_PASSWORD"
--set-secrets="ConnectionStrings__Default=$env:SECRET_DB_CONNECTION`:latest"
```

## Memorystore for Valkey 設定
Expand Down
28 changes: 16 additions & 12 deletions docker/game-server/prod/cloudbuild.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
# 使用方法(Cloud Shell または gcloud CLI):
# cd Unity6Portfolio
# gcloud builds submit --config=docker/game-server/prod/cloudbuild.yml \
# --substitutions=_INSTANCE_NAME=game-db,_DB_NAME=gamedb,_DB_USER=gameserver,_DB_PASSWORD=xxx
# --substitutions=_INSTANCE_NAME=game-db
#
# 注意: 機密情報は Secret Manager を使用することを推奨
# 機密情報は Google Cloud Secret Manager で管理
# 事前に Secret Manager にシークレットを作成し、Cloud Run サービスアカウントに
# roles/secretmanager.secretAccessor を付与してください

steps:
# ===== Docker イメージをビルド =====
Expand Down Expand Up @@ -58,6 +60,10 @@ steps:
- '-c'
- |
CONNECTION_NAME=$(cat /workspace/connection_name.txt)
SECRETS="ConnectionStrings__Default=${_SECRET_DB_CONNECTION}:latest,Jwt__Secret=${_SECRET_JWT}:latest,RequestSigning__SecretKey=${_SECRET_REQUEST_SIGNING}:latest,ConnectionStrings__Valkey=${_SECRET_VALKEY_CONNECTION}:latest"
if [ -n "${_SECRET_RESEND}" ]; then
SECRETS="$${SECRETS},Resend__ApiKey=${_SECRET_RESEND}:latest"
fi
gcloud run deploy ${_SERVICE_NAME} \
--image=${_REGION}-docker.pkg.dev/${PROJECT_ID}/${_REPO_NAME}/game-server:${SHORT_SHA} \
--region=${_REGION} \
Expand All @@ -66,11 +72,9 @@ steps:
--add-cloudsql-instances=$${CONNECTION_NAME} \
--vpc-connector=${_VPC_CONNECTOR} \
--set-env-vars="ASPNETCORE_ENVIRONMENT=Production" \
--set-env-vars="ConnectionStrings__Default=Host=/cloudsql/$${CONNECTION_NAME};Database=${_DB_NAME};Username=${_DB_USER};Password=${_DB_PASSWORD}" \
--set-env-vars="ConnectionStrings__Valkey=${_VALKEY_HOST}:${_VALKEY_PORT},abortConnect=false,connectTimeout=5000" \
--set-env-vars="Jwt__Secret=${_JWT_SECRET}" \
--set-env-vars="Jwt__Issuer=${_JWT_ISSUER}" \
--set-env-vars="Jwt__Audience=${_JWT_AUDIENCE}" \
--set-secrets="$${SECRETS}" \
--memory=512Mi \
--cpu=1 \
--min-instances=0 \
Expand All @@ -84,15 +88,15 @@ substitutions:
_REPO_NAME: game-server
_SERVICE_NAME: game-server
_INSTANCE_NAME: game-db
_DB_NAME: gamedb
_DB_USER: gameserver
_DB_PASSWORD: ''
_JWT_SECRET: ''
# Secret Manager シークレット名
_SECRET_DB_CONNECTION: game-server-db-connection-string
_SECRET_JWT: game-jwt-secret
_SECRET_REQUEST_SIGNING: game-request-signing-secret
_SECRET_RESEND: ''
_SECRET_VALKEY_CONNECTION: game-valkey-connection
# 非機密設定
_JWT_ISSUER: Game.Server
_JWT_AUDIENCE: Game.Client
# Memorystore for Valkey 設定
_VALKEY_HOST: ''
_VALKEY_PORT: '6379'
# VPC Connector(Memorystore 接続に必要)
_VPC_CONNECTOR: ''

Expand Down
55 changes: 21 additions & 34 deletions docker/game-server/prod/deploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,14 @@ if (Test-Path $EnvFile) {
}

# Check required variables
$RequiredVars = @("PROJECT_ID", "REGION", "REPO_NAME", "SERVICE_NAME", "INSTANCE_NAME", "DB_NAME", "DB_USER", "DB_PASSWORD")
$RequiredVars = @("PROJECT_ID", "REGION", "REPO_NAME", "SERVICE_NAME", "INSTANCE_NAME", "SECRET_DB_CONNECTION", "SECRET_JWT", "SECRET_REQUEST_SIGNING", "SECRET_VALKEY_CONNECTION")
foreach ($var in $RequiredVars) {
if (-not (Get-Item -Path "Env:$var" -ErrorAction SilentlyContinue)) {
Write-Host "[ERROR] Required variable $var is not set in .env" -ForegroundColor Red
exit 1
}
}

# Check JWT settings (warning only)
if (-not $env:Jwt__Secret) {
Write-Host "[WARN] Jwt__Secret is not set. JWT authentication may fail." -ForegroundColor Yellow
}

$IMAGE = "$env:REGION-docker.pkg.dev/$env:PROJECT_ID/$env:REPO_NAME/game-server"

# Get Cloud SQL connection name
Expand All @@ -57,12 +52,10 @@ if (-not $CONNECTION_NAME) {
exit 1
}

# Check Valkey settings (warning only)
$ValkeyEnabled = $false
if ($env:VALKEY_HOST -and $env:VPC_CONNECTOR) {
$ValkeyEnabled = $true
} elseif ($env:VALKEY_HOST -or $env:VPC_CONNECTOR) {
Write-Host "[WARN] Valkey requires both VALKEY_HOST and VPC_CONNECTOR to be set." -ForegroundColor Yellow
# Check VPC Connector (required for Memorystore access)
$VpcConnectorEnabled = $false
if ($env:VPC_CONNECTOR) {
$VpcConnectorEnabled = $true
}

Write-Host ""
Expand All @@ -73,12 +66,10 @@ Write-Host "SERVICE_NAME: $env:SERVICE_NAME"
Write-Host "IMAGE: ${IMAGE}:${Tag}"
Write-Host "CLOUD_SQL: $CONNECTION_NAME"
Write-Host "DATABASE: $env:DB_NAME"
if ($ValkeyEnabled) {
Write-Host "VALKEY: $env:VALKEY_HOST`:$env:VALKEY_PORT"
if ($VpcConnectorEnabled) {
Write-Host "VPC_CONNECTOR: $env:VPC_CONNECTOR"
} else {
Write-Host "VALKEY: (not configured)"
}
Write-Host "VALKEY_SECRET: $env:SECRET_VALKEY_CONNECTION"
Write-Host "=================================" -ForegroundColor Cyan
Write-Host ""

Expand Down Expand Up @@ -108,31 +99,26 @@ if (-not $BuildOnly) {
# Cloud Run deployment
Write-Host "[4/4] Deploying to Cloud Run..." -ForegroundColor Yellow

# Build connection string
$ConnectionString = "Host=/cloudsql/$CONNECTION_NAME;Database=$env:DB_NAME;Username=$env:DB_USER;Password=$env:DB_PASSWORD"

# Build environment variables
# Build environment variables (non-sensitive only)
$EnvVars = @(
"ASPNETCORE_ENVIRONMENT=Production",
"ConnectionStrings__Default=$ConnectionString"
"ASPNETCORE_ENVIRONMENT=Production"
)

# Add JWT settings if configured
if ($env:Jwt__Secret) { $EnvVars += "Jwt__Secret=$env:Jwt__Secret" }
if ($env:Jwt__Issuer) { $EnvVars += "Jwt__Issuer=$env:Jwt__Issuer" }
if ($env:Jwt__Audience) { $EnvVars += "Jwt__Audience=$env:Jwt__Audience" }

# Add Resend settings if configured
if ($env:Resend__ApiKey) { $EnvVars += "Resend__ApiKey=$env:Resend__ApiKey" }

# Add Valkey settings if configured
if ($ValkeyEnabled) {
$ValkeyPort = if ($env:VALKEY_PORT) { $env:VALKEY_PORT } else { "6379" }
$EnvVars += "ConnectionStrings__Valkey=$env:VALKEY_HOST`:${ValkeyPort},abortConnect=false,connectTimeout=5000"
}

$EnvVarsString = $EnvVars -join ","

# Build Secret Manager secrets
$Secrets = @(
"ConnectionStrings__Default=$env:SECRET_DB_CONNECTION`:latest",
"Jwt__Secret=$env:SECRET_JWT`:latest",
"RequestSigning__SecretKey=$env:SECRET_REQUEST_SIGNING`:latest",
"ConnectionStrings__Valkey=$env:SECRET_VALKEY_CONNECTION`:latest"
)
if ($env:SECRET_RESEND) { $Secrets += "Resend__ApiKey=$env:SECRET_RESEND`:latest" }
$SecretsString = $Secrets -join ","

# Build deploy command
$DeployArgs = @(
"run", "deploy", $env:SERVICE_NAME,
Expand All @@ -142,6 +128,7 @@ if (-not $BuildOnly) {
"--allow-unauthenticated",
"--add-cloudsql-instances=$CONNECTION_NAME",
"--set-env-vars=$EnvVarsString",
"--set-secrets=$SecretsString",
"--memory=512Mi",
"--cpu=1",
"--min-instances=0",
Expand All @@ -151,7 +138,7 @@ if (-not $BuildOnly) {
)

# Add VPC Connector if Valkey is enabled
if ($ValkeyEnabled) {
if ($VpcConnectorEnabled) {
$DeployArgs += "--vpc-connector=$env:VPC_CONNECTOR"
}

Expand Down
49 changes: 16 additions & 33 deletions docker/game-server/prod/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,14 @@ else
fi

# 必須変数の確認
REQUIRED_VARS=("PROJECT_ID" "REGION" "REPO_NAME" "SERVICE_NAME" "INSTANCE_NAME" "DB_NAME" "DB_USER" "DB_PASSWORD")
REQUIRED_VARS=("PROJECT_ID" "REGION" "REPO_NAME" "SERVICE_NAME" "INSTANCE_NAME" "SECRET_DB_CONNECTION" "SECRET_JWT" "SECRET_REQUEST_SIGNING" "SECRET_VALKEY_CONNECTION")
for var in "${REQUIRED_VARS[@]}"; do
if [[ -z "${!var}" ]]; then
echo "[ERROR] Required variable $var is not set in .env"
exit 1
fi
done

# JWT 設定の確認(警告のみ)
if [[ -z "$Jwt__Secret" ]]; then
echo "[WARN] Jwt__Secret is not set. JWT authentication may fail."
fi

IMAGE="${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPO_NAME}/game-server"

# Cloud SQL 接続名を取得
Expand All @@ -62,12 +57,10 @@ if [[ -z "$CONNECTION_NAME" ]]; then
exit 1
fi

# Valkey 設定の確認
VALKEY_ENABLED=false
if [[ -n "$VALKEY_HOST" && -n "$VPC_CONNECTOR" ]]; then
VALKEY_ENABLED=true
elif [[ -n "$VALKEY_HOST" || -n "$VPC_CONNECTOR" ]]; then
echo "[WARN] Valkey requires both VALKEY_HOST and VPC_CONNECTOR to be set."
# VPC Connector の確認(Memorystore 接続に必要)
VPC_CONNECTOR_ENABLED=false
if [[ -n "$VPC_CONNECTOR" ]]; then
VPC_CONNECTOR_ENABLED=true
fi

echo ""
Expand All @@ -78,12 +71,10 @@ echo "SERVICE_NAME: $SERVICE_NAME"
echo "IMAGE: ${IMAGE}:${TAG}"
echo "CLOUD_SQL: $CONNECTION_NAME"
echo "DATABASE: $DB_NAME"
if [[ "$VALKEY_ENABLED" == "true" ]]; then
echo "VALKEY: ${VALKEY_HOST}:${VALKEY_PORT:-6379}"
if [[ "$VPC_CONNECTOR_ENABLED" == "true" ]]; then
echo "VPC_CONNECTOR: $VPC_CONNECTOR"
else
echo "VALKEY: (not configured)"
fi
echo "VALKEY_SECRET: $SECRET_VALKEY_CONNECTION"
echo "================================="
echo ""

Expand All @@ -109,26 +100,17 @@ if [[ "$BUILD_ONLY" != "true" ]]; then
# Cloud Run デプロイ
echo "[4/4] Deploying to Cloud Run..."

# 接続文字列を構築
CONNECTION_STRING="Host=/cloudsql/$CONNECTION_NAME;Database=$DB_NAME;Username=$DB_USER;Password=$DB_PASSWORD"

# 環境変数を構築
# 環境変数を構築(非機密のみ)
ENV_VARS="ASPNETCORE_ENVIRONMENT=Production"
ENV_VARS="$ENV_VARS,ConnectionStrings__Default=$CONNECTION_STRING"

# JWT 設定を追加(設定されている場合)
[[ -n "$Jwt__Secret" ]] && ENV_VARS="$ENV_VARS,Jwt__Secret=$Jwt__Secret"
[[ -n "$Jwt__Issuer" ]] && ENV_VARS="$ENV_VARS,Jwt__Issuer=$Jwt__Issuer"
[[ -n "$Jwt__Audience" ]] && ENV_VARS="$ENV_VARS,Jwt__Audience=$Jwt__Audience"

# Resend 設定を追加(設定されている場合)
[[ -n "$Resend__ApiKey" ]] && ENV_VARS="$ENV_VARS,Resend__ApiKey=$Resend__ApiKey"

# Valkey 設定を追加(設定されている場合)
if [[ "$VALKEY_ENABLED" == "true" ]]; then
VALKEY_PORT="${VALKEY_PORT:-6379}"
ENV_VARS="$ENV_VARS,ConnectionStrings__Valkey=${VALKEY_HOST}:${VALKEY_PORT},abortConnect=false,connectTimeout=5000"
fi
# Secret Manager シークレットを構築
SECRETS="ConnectionStrings__Default=${SECRET_DB_CONNECTION}:latest"
SECRETS="$SECRETS,Jwt__Secret=${SECRET_JWT}:latest"
SECRETS="$SECRETS,RequestSigning__SecretKey=${SECRET_REQUEST_SIGNING}:latest"
SECRETS="$SECRETS,ConnectionStrings__Valkey=${SECRET_VALKEY_CONNECTION}:latest"
[[ -n "$SECRET_RESEND" ]] && SECRETS="$SECRETS,Resend__ApiKey=${SECRET_RESEND}:latest"

# デプロイコマンドを構築
DEPLOY_ARGS=(
Expand All @@ -139,6 +121,7 @@ if [[ "$BUILD_ONLY" != "true" ]]; then
"--allow-unauthenticated"
"--add-cloudsql-instances=$CONNECTION_NAME"
"--set-env-vars=$ENV_VARS"
"--set-secrets=$SECRETS"
"--memory=512Mi"
"--cpu=1"
"--min-instances=0"
Expand All @@ -148,7 +131,7 @@ if [[ "$BUILD_ONLY" != "true" ]]; then
)

# VPC Connector を追加(Valkey 有効時)
if [[ "$VALKEY_ENABLED" == "true" ]]; then
if [[ "$VPC_CONNECTOR_ENABLED" == "true" ]]; then
DEPLOY_ARGS+=("--vpc-connector=$VPC_CONNECTOR")
fi

Expand Down
Loading
Loading