Skip to content

Commit 7f09830

Browse files
authored
docs: Add Cloud Run and JDBC connector examples (#2229)
1 parent 06e7204 commit 7f09830

File tree

13 files changed

+1314
-0
lines changed

13 files changed

+1314
-0
lines changed

examples/cloudrun/README.md

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Connecting Cloud Run to Cloud SQL with the Java Connector (JDBC)
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 Java Connector (JDBC Socket Factory). It covers connecting to instances with both public and private IP addresses and demonstrates how to handle database credentials securely.
4+
5+
## Develop a Java Application
6+
7+
The following Java applications demonstrate how to connect to a Cloud SQL instance using the Cloud SQL Java Connector.
8+
9+
### `mysql/.../Main.java` and `postgres/.../Main.java`
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+
16+
### `sqlserver/.../Main.java`
17+
18+
This file contains the core application logic for connecting to a Cloud SQL for SQL Server instance. It uses the `cloud-sql-connector-jdbc-sqlserver` to create a `HikariDataSource` with password-based authentication at the `/` route.
19+
20+
> [!NOTE]
21+
>
22+
> Cloud SQL for SQL Server does not support IAM database authentication.
23+
24+
25+
> [!NOTE]
26+
> **Lazy Refresh**
27+
>
28+
> The sample code in all three `Main.java` files uses lazy refresh for the `HikariDataSource`. This is a recommended approach to avoid connection errors and optimize cost by preventing background processes from running when the CPU is throttled.
29+
30+
## Global Variables and Lazy Instantiation
31+
32+
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 spun down.
33+
34+
The `HikariDataSource` connection pools are defined as global (static) variables (initially set to `null`) and are lazily instantiated (created only when needed) inside the request handlers (synchronized to ensure thread safety).
35+
36+
This approach offers several benefits:
37+
38+
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.
39+
2. **Resource Efficiency:** Expensive operations, like establishing background connections or fetching secrets, are only performed when actually required.
40+
3. **Connection Reuse:** Once initialized, the global `HikariDataSource` 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.
41+
42+
## IAM Authentication Prerequisites
43+
44+
45+
For IAM authentication to work, you must ensure two things:
46+
47+
1. **The Cloud Run service's service account has the `Cloud SQL Client` role.** You can grant this role with the following command:
48+
```bash
49+
gcloud projects add-iam-policy-binding PROJECT_ID \
50+
--member="serviceAccount:SERVICE_ACCOUNT_EMAIL" \
51+
--role="roles/cloudsql.client"
52+
```
53+
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.
54+
55+
2. **The service account is added as a database user to your Cloud SQL instance.** You can do this with the following command:
56+
```bash
57+
gcloud sql users create SERVICE_ACCOUNT_EMAIL \
58+
--instance=INSTANCE_NAME \
59+
--type=cloud_iam_user
60+
```
61+
Replace `SERVICE_ACCOUNT_EMAIL` with the same service account email and `INSTANCE_NAME` with your Cloud SQL instance name.
62+
63+
## Deploy the Application to Cloud Run
64+
65+
Follow these steps to deploy the application to Cloud Run.
66+
67+
### Build and Push the Docker Image
68+
69+
1. **Enable the Artifact Registry API:**
70+
71+
```bash
72+
gcloud services enable artifactregistry.googleapis.com
73+
```
74+
75+
2. **Create an Artifact Registry repository:**
76+
77+
```bash
78+
gcloud artifacts repositories create REPO_NAME \
79+
--repository-format=docker \
80+
--location=REGION
81+
```
82+
83+
3. **Configure Docker to authenticate with Artifact Registry:**
84+
85+
```bash
86+
gcloud auth configure-docker REGION-docker.pkg.dev
87+
```
88+
89+
4. **Build the Docker image (replace `mysql` with `postgres` or `sqlserver` as needed):**
90+
91+
```bash
92+
docker build -t REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME mysql
93+
```
94+
95+
5. **Push the Docker image to Artifact Registry:**
96+
97+
```bash
98+
docker push REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME
99+
```
100+
101+
### Deploy to Cloud Run
102+
103+
Deploy the container image to Cloud Run using the `gcloud run deploy` command.
104+
105+
106+
**Sample Values:**
107+
* `SERVICE_NAME`: `my-cloud-run-service`
108+
* `REGION`: `us-central1`
109+
* `PROJECT_ID`: `my-gcp-project-id`
110+
* `REPO_NAME`: `my-artifact-repo`
111+
* `IMAGE_NAME`: `my-app-image`
112+
* `INSTANCE_CONNECTION_NAME`: `my-gcp-project-id:us-central1:my-instance-name`
113+
* `DB_USER`: `my-db-user` (for password-based authentication)
114+
* `DB_IAM_USER`: `my-service-account@my-gcp-project-id.iam.gserviceaccount.com` (for IAM-based authentication)
115+
* `DB_NAME`: `my-db-name`
116+
* `DB_PASSWORD`: `my-user-pass-name`
117+
* `VPC_NETWORK`: `my-vpc-network`
118+
* `SUBNET_NAME`: `my-vpc-subnet`
119+
120+
121+
**For MySQL and PostgreSQL (Public IP):**
122+
123+
```bash
124+
gcloud run deploy SERVICE_NAME \
125+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \
126+
--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 \
127+
--region=REGION \
128+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
129+
```
130+
131+
**For MySQL and PostgreSQL (Private IP):**
132+
133+
```bash
134+
gcloud run deploy SERVICE_NAME \
135+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \
136+
--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 \
137+
--network=VPC_NETWORK \
138+
--subnet=SUBNET_NAME \
139+
--vpc-egress=private-ranges-only \
140+
--region=REGION \
141+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
142+
```
143+
144+
**For SQL Server (Public IP):**
145+
146+
```bash
147+
gcloud run deploy SERVICE_NAME \
148+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_NAME \
149+
--set-env-vars=DB_USER=DB_USER,DB_NAME=DB_NAME,DB_SECRET_NAME=DB_SECRET_NAME,INSTANCE_CONNECTION_NAME=INSTANCE_CONNECTION_NAME \
150+
--region=REGION \
151+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
152+
```
153+
154+
**For SQL Server (Private IP):**
155+
156+
```bash
157+
gcloud run deploy SERVICE_NAME \
158+
--image=REGION-docker.pkg.dev/PROJECT_ID/REPO_NAME/IMAGE_name \
159+
--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 \
160+
--network=VPC_NETWORK \
161+
--subnet=SUBNET_NAME \
162+
--vpc-egress=private-ranges-only \
163+
--region=REGION \
164+
--update-secrets=DB_PASSWORD=DB_PASSWORD:latest
165+
```
166+
167+
> [!NOTE]
168+
> **`For PSC connections`**
169+
>
170+
> 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
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
FROM maven:3.9-eclipse-temurin-21 AS builder
2+
3+
WORKDIR /app
4+
5+
# 1. Copy only the POM first
6+
COPY pom.xml .
7+
8+
# 2. CACHING STEP: Download dependencies (including the versions plugin)
9+
# This layer will be cached unless pom.xml changes
10+
RUN mvn dependency:go-offline
11+
12+
# 3. AUTO-UPDATE STEP (As requested)
13+
# This updates pom.xml to use the latest release of all dependencies
14+
RUN mvn versions:use-latest-releases -DallowSnapshots=false
15+
16+
# 4. Copy source code and build
17+
COPY src ./src
18+
RUN mvn package -DskipTests
19+
20+
# --- Final Stage ---
21+
FROM eclipse-temurin:21-jre-alpine
22+
23+
WORKDIR /app
24+
COPY --from=builder /app/target/cloud-sql-mariadb-sample-1.0-SNAPSHOT.jar ./app.jar
25+
26+
CMD ["java", "-jar", "app.jar"]

examples/cloudrun/mariadb/pom.xml

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Copyright 2025 Google LLC
4+
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
18+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
20+
<modelVersion>4.0.0</modelVersion>
21+
<groupId>com.google.cloud.sql</groupId>
22+
<artifactId>cloud-sql-mariadb-sample</artifactId>
23+
<packaging>jar</packaging>
24+
<version>1.0-SNAPSHOT</version>
25+
<name>cloud-sql-mariadb-sample</name>
26+
<url>http://maven.apache.org</url>
27+
28+
<properties>
29+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
30+
<maven.compiler.source>21</maven.compiler.source>
31+
<maven.compiler.target>21</maven.compiler.target>
32+
</properties>
33+
34+
<dependencyManagement>
35+
<dependencies>
36+
<dependency>
37+
<groupId>com.google.cloud</groupId>
38+
<artifactId>libraries-bom</artifactId>
39+
<version>26.54.0</version>
40+
<type>pom</type>
41+
<scope>import</scope>
42+
</dependency>
43+
</dependencies>
44+
</dependencyManagement>
45+
46+
<dependencies>
47+
<dependency>
48+
<groupId>com.google.cloud.sql</groupId>
49+
<artifactId>mariadb-socket-factory</artifactId>
50+
<version>1.27.0</version>
51+
</dependency>
52+
<dependency>
53+
<groupId>org.mariadb.jdbc</groupId>
54+
<artifactId>mariadb-java-client</artifactId>
55+
<version>3.5.5</version>
56+
</dependency>
57+
<dependency>
58+
<groupId>com.zaxxer</groupId>
59+
<artifactId>HikariCP</artifactId>
60+
<version>6.2.1</version>
61+
</dependency>
62+
<dependency>
63+
<groupId>com.google.cloud</groupId>
64+
<artifactId>google-cloud-secretmanager</artifactId>
65+
</dependency>
66+
<dependency>
67+
<groupId>org.slf4j</groupId>
68+
<artifactId>slf4j-simple</artifactId>
69+
<version>2.0.13</version>
70+
</dependency>
71+
</dependencies>
72+
73+
<build>
74+
<plugins>
75+
<plugin>
76+
<groupId>org.apache.maven.plugins</groupId>
77+
<artifactId>maven-shade-plugin</artifactId>
78+
<version>3.6.0</version>
79+
<executions>
80+
<execution>
81+
<phase>package</phase>
82+
<goals>
83+
<goal>shade</goal>
84+
</goals>
85+
<configuration>
86+
<transformers>
87+
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
88+
<mainClass>com.google.cloud.sql.mariadb.Main</mainClass>
89+
</transformer>
90+
<transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
91+
</transformers>
92+
<filters>
93+
<filter>
94+
<artifact>*:*</artifact>
95+
<excludes>
96+
<exclude>META-INF/*.SF</exclude>
97+
<exclude>META-INF/*.DSA</exclude>
98+
<exclude>META-INF/*.RSA</exclude>
99+
<exclude>META-INF/services/java.net.spi.InetAddressResolverProvider</exclude>
100+
</excludes>
101+
</filter>
102+
</filters>
103+
</configuration>
104+
</execution>
105+
</executions>
106+
</plugin>
107+
<plugin>
108+
<groupId>org.codehaus.mojo</groupId>
109+
<artifactId>versions-maven-plugin</artifactId>
110+
<version>2.16.2</version>
111+
</plugin>
112+
</plugins>
113+
</build>
114+
</project>

0 commit comments

Comments
 (0)