Skip to content

Commit df77601

Browse files
authored
docs: Add Cloud Run and Go connector samples (#1045)
1 parent 3bc612f commit df77601

File tree

13 files changed

+1758
-0
lines changed

13 files changed

+1758
-0
lines changed

examples/cloudrun/README.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Connecting Cloud Run to Cloud SQL with the Go Connector
2+
3+
This guide provides a comprehensive walkthrough of how to connect a Cloud Run service to a Cloud SQL instance using the Cloud SQL Go Connector. It covers connecting to instances with both public and private IP addresses and demonstrates how to handle database credentials securely.
4+
5+
## Develop a Go Application
6+
7+
The following Go applications demonstrate how to connect to a Cloud SQL instance using the Cloud SQL Go Connector.
8+
9+
### `mysql/main.go` and `postgres/main.go`
10+
11+
These files contain the core application logic for connecting to a Cloud SQL for MySQL or PostgreSQL instance. They provide two separate authentication methods, each exposed at a different route:
12+
- `/`: Password-based authentication
13+
- `/iam`: IAM-based authentication
14+
15+
### `sqlserver/main.go`
16+
17+
This file contains the core application logic for connecting to a Cloud SQL for SQL Server instance. It uses the `cloud-sql-go-connector` to create a database connection pool with password-based authentication at the `/` route.
18+
19+
> [!NOTE]
20+
>
21+
> Cloud SQL for SQL Server does not support IAM database authentication.
22+
23+
> [!NOTE]
24+
> **Lazy Refresh**
25+
>
26+
> The sample code in all three `main.go` files initializes the `Dialer` with `WithLazyRefresh()` option. This is a recommended approach to avoid connection errors and optimize cost by preventing background processes from running when the CPU is throttled.
27+
28+
29+
## Lazy Instantiation
30+
31+
In a Cloud Run service, global variables are initialized when the container instance starts up. The application instance then handles subsequent requests until the container is stopped.
32+
33+
The `Connector` (Dialer) and `sql.DB` objects are defined as global variables (initially set to `nil`) and are lazily instantiated (created only when needed) inside the request handlers using `sync.Once`.
34+
35+
This approach offers several benefits:
36+
37+
1. **Faster Startup:** By deferring initialization until the first request, the Cloud Run service can start listening for requests almost immediately, reducing cold start latency.
38+
2. **Resource Efficiency:** Expensive operations, like establishing background connections or fetching secrets, are only performed when actually required.
39+
3. **Connection Reuse:** Once initialized, the global `Connector` and `sql.DB` instances are reused for all subsequent requests to that container instance. This prevents the overhead of creating new connections for every request and avoids hitting connection limits.
40+
41+
## IAM Authentication Prerequisites
42+
43+
For IAM authentication to work, you must ensure two things:
44+
45+
1. **The Cloud Run service's service account has the `Cloud SQL Client` role.** You can grant this role with the following command:
46+
```bash
47+
gcloud projects add-iam-policy-binding PROJECT_ID \
48+
--member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
49+
--role="roles/cloudsql.client"
50+
```
51+
Replace `PROJECT_ID` with your Google Cloud project ID and `SERVICE_ACCOUNT_EMAIL` with the email of the service account your Cloud Run service is using.
52+
53+
2. **The service account is added as a database user to your Cloud SQL instance.** You can do this with the following command:
54+
```bash
55+
gcloud sql users create SERVICE_ACCOUNT_EMAIL \
56+
--instance=INSTANCE_NAME \
57+
--type=cloud_iam_user
58+
```
59+
Replace `SERVICE_ACCOUNT_EMAIL` with the same service account email and `INSTANCE_NAME` with your Cloud SQL instance name.
60+
61+
## Deploy the Application to Cloud Run
62+
63+
Follow these steps to deploy the application to Cloud Run.
64+
65+
### Build and Push the Docker Image
66+
67+
1. **Enable the Artifact Registry API:**
68+
69+
```bash
70+
gcloud services enable artifactregistry.googleapis.com
71+
```
72+
73+
2. **Create an Artifact Registry repository:**
74+
75+
```bash
76+
gcloud artifacts repositories create REPO_NAME \
77+
--repository-format=docker \
78+
--location=REGION
79+
```
80+
81+
3. **Configure Docker to authenticate with Artifact Registry:**
82+
83+
```bash
84+
gcloud auth configure-docker REGION-docker.pkg.dev
85+
```
86+
87+
4. **Build the Docker image (replace `mysql` with `postgres` or `sqlserver` as needed):**
88+
89+
```bash
90+
docker build -t REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME mysql
91+
```
92+
93+
5. **Push the Docker image to Artifact Registry:**
94+
95+
```bash
96+
docker push REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME
97+
```
98+
99+
### Deploy to Cloud Run
100+
101+
Deploy the container image to Cloud Run using the `gcloud run deploy` command.
102+
103+
**Sample Values:**
104+
* `SERVICE_NAME`: `my-cloud-run-service`
105+
* `REGION`: `us-central1`
106+
* `PROJECT_ID`: `my-gcp-project-id`
107+
* `REPO_NAME`: `my-artifact-repo`
108+
* `IMAGE_NAME`: `my-app-image`
109+
* `INSTANCE_CONNECTION_NAME`: `my-gcp-project-id:us-central1:my-instance-name`
110+
* `DB_USER`: `my-db-user` (for password-based authentication)
111+
* `DB_IAM_USER`: `my-service-account@my-gcp-project-id.iam.gserviceaccount.com` (for IAM-based authentication)
112+
* `DB_NAME`: `my-db-name`
113+
* `DB_PASSWORD`: `my-user-pass-name`
114+
* `VPC_NETWORK`: `my-vpc-network`
115+
* `SUBNET_NAME`: `my-vpc-subnet`
116+
117+
**For MySQL and PostgreSQL (Public IP):**
118+
119+
```bash
120+
gcloud run deploy SERVICE_NAME \
121+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \
122+
--set-env-vars=DB_USER=DB_USER,DB_IAM_USER=DB_IAM_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \
123+
--region=REGION \
124+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
125+
```
126+
127+
**For MySQL and PostgreSQL (Private IP):**
128+
129+
```bash
130+
gcloud run deploy SERVICE_NAME \
131+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \
132+
--set-env-vars=DB_USER=DB_USER,DB_IAM_USER=DB_IAM_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME,IP_TYPE=PRIVATE \
133+
--network=VPC_NETWORK \
134+
--subnet=SUBNET_NAME \
135+
--vpc-egress=private-ranges-only \
136+
--region=REGION \
137+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
138+
```
139+
140+
**For SQL Server (Public IP):**
141+
142+
```bash
143+
gcloud run deploy SERVICE_NAME \
144+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \
145+
--set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \
146+
--region=REGION \
147+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
148+
```
149+
150+
**For SQL Server (Private IP):**
151+
152+
```bash
153+
gcloud run deploy SERVICE_NAME \
154+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_name \
155+
--set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME,IP_TYPE=PRIVATE \
156+
--network=VPC_NETWORK \
157+
--subnet=SUBNET_NAME \
158+
--vpc-egress=private-ranges-only \
159+
--region=REGION \
160+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
161+
```
162+
163+
> [!NOTE]
164+
> **`For PSC connections`**
165+
>
166+
> To connect to the Cloud SQL instance with PSC connection type, create a PSC endpoint, a DNS zone and DNS record for the instance in the same VPC network as the Cloud Run service and replace the `IP_TYPE` in the deploy command with `PSC`. To configure DNS records, refer to [Connect to an instance using Private Service Connect](https://docs.cloud.google.com/sql/docs/mysql/configure-private-service-connect) guide

examples/cloudrun/mysql/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Use the official Golang image to create a build artifact.
2+
# This is based on Debian and sets the GOPATH to /go.
3+
# https://hub.docker.com/_/golang
4+
FROM golang:1.25 AS builder
5+
6+
# Create and change to the app directory.
7+
WORKDIR /app
8+
9+
# Retrieve application dependencies.
10+
# This allows the container build to reuse cached dependencies.
11+
# Expecting to copy go.mod and if present go.sum.
12+
COPY go.* ./
13+
RUN go mod download
14+
15+
# Copy local code to the container image.
16+
COPY . .
17+
18+
# Build the binary.
19+
# CGO_ENABLED=0 builds a statically-linked binary.
20+
RUN CGO_ENABLED=0 GOOS=linux go build -v -o server
21+
22+
# Use gcr.io/distroless/static:nonroot image to run the application.
23+
FROM gcr.io/distroless/static:nonroot
24+
25+
# Copy the binary to the production image from the builder stage.
26+
COPY --from=builder /app/server /server
27+
28+
# Run the web service on container startup.
29+
CMD ["/server"]

examples/cloudrun/mysql/go.mod

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module cloud.google.com/go-cloudrun/mysql
2+
3+
go 1.24.0
4+
5+
toolchain go1.24.3
6+
7+
require (
8+
cloud.google.com/go/cloudsqlconn v1.19.0
9+
cloud.google.com/go/secretmanager v1.16.0
10+
github.com/go-sql-driver/mysql v1.9.3
11+
)
12+
13+
require (
14+
cloud.google.com/go/auth v0.17.0 // indirect
15+
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
16+
cloud.google.com/go/compute/metadata v0.9.0 // indirect
17+
cloud.google.com/go/iam v1.5.2 // indirect
18+
filippo.io/edwards25519 v1.1.0 // indirect
19+
github.com/felixge/httpsnoop v1.0.4 // indirect
20+
github.com/go-logr/logr v1.4.3 // indirect
21+
github.com/go-logr/stdr v1.2.2 // indirect
22+
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
23+
github.com/google/s2a-go v0.1.9 // indirect
24+
github.com/google/uuid v1.6.0 // indirect
25+
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
26+
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
27+
go.opencensus.io v0.24.0 // indirect
28+
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
29+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
30+
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
31+
go.opentelemetry.io/otel v1.38.0 // indirect
32+
go.opentelemetry.io/otel/metric v1.38.0 // indirect
33+
go.opentelemetry.io/otel/trace v1.38.0 // indirect
34+
golang.org/x/crypto v0.43.0 // indirect
35+
golang.org/x/net v0.46.0 // indirect
36+
golang.org/x/oauth2 v0.32.0 // indirect
37+
golang.org/x/sync v0.17.0 // indirect
38+
golang.org/x/sys v0.37.0 // indirect
39+
golang.org/x/text v0.30.0 // indirect
40+
golang.org/x/time v0.14.0 // indirect
41+
google.golang.org/api v0.253.0 // indirect
42+
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
43+
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
44+
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
45+
google.golang.org/grpc v1.76.0 // indirect
46+
google.golang.org/protobuf v1.36.10 // indirect
47+
)

0 commit comments

Comments
 (0)