需求是我在 GCP 用 Spring Boot 部署了一台 Cloud Run,同時也 Cloud Run 申 請了一台 PostgreSQL,我希望能從這台 Cloud Run 使用 JDBC 連線到 Cloud SQL,原本認為應該很容易,意外的卻卡關有點久,整理了一下筆記,至少下次有同樣的需求時不會忘掉
筆記開始前
- 在 Cloud SQL 申請 PostgreSQL,這個部份不在這篇筆記討論的範圍,不過很簡單,一直按下一步就差不多了,要稍微注意的是 GCP 預設申請的是「4 個 vCPU 和 26 GB」的機器,價格有點貴,有預算考量的人記得申請時選等級差一點的機器
- 將 Spring Boot 包成 docker image 檔上傳到 GCP 的 Artifact Registry,這個部份也不在這篇筆記討論的範圍,晚點有空的時候再來整理一下這裏的筆記
什麼是 Cloud Run
Cloud Run 是 GCP 的一個 serverless 服務,和 AWS 的 Lambda 不同的是,Cloud Run 使用 docker container 來部署,也就是說只要能把程式包成 docker image 檔上傳到 GCP 的 Artifact Registry 或 Container Registry,程式就可以跑,不限制使用什麼語言,測試過 Python Flask 和 Java Spring Boot 都能正常執行
然後 Cloud Run 提供了「只在要求處理期間分配 CPU」和「隨時分配 CPU」兩種選項,實測如果選擇「只在要求處理期間分配 CPU」在很久沒有執行後,再次執行大概要等個 10 秒,如果某個服務不需要 24 小時執行(ex:排程執行的服務),這個選擇可以省下不少費用
Spring Boot 怎麼連線 PostgreSQL
這篇文章其實也預設大家已經知道 Spring Boot 怎麼連線 PostgreSQL,所以寫的簡潔一點
- gradle 設定:增加以下兩行設定
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-jdbc'
runtimeOnly 'org.postgresql:postgresql'
}
- application.properties 或 application.yml 設定,以下範例為 application.yml
spring:
datasource:
url: jdbc:postgresql://127.0.0.1:5432/database-name
driverClassName: org.postgresql.Driver
username: user-name
password: ******
Spring Boot 其實算是簡化 db 連線的設定,基本上就是 gradle 設定好,application.properties 設定好就可以了
GCP 如何在 Cloud Run 使用 Spring Boot 連線 Cloud SQL
終於寫到正文
Step 1:Cloud Run 建立服務時,新增 Cloud SQL 連線
如果已經申請好 Cloud SQL 的話,在建立 Cloud Run 時,應該會有選項可以選,如下圖
Step 2:Spring Boot 增加 import library
以下範例為 gradle,使用 maven 請自行修改
dependencies {
implementation 'com.google.cloud.sql:postgres-socket-factory:1.10.0'
}
筆記撰寫時版本為 1.10.0,該版本測試能用,有需要的請自行升版
Step 3:修改 application 設定
修改 application.yml
# DB
spring:
datasource:
url: jdbc:postgresql:///database-name
driverClassName: org.postgresql.Driver
username: user-name
password: ******
cloudSQL:
socketFactory: com.google.cloud.sql.postgres.SocketFactory
cloudSqlInstance: INSTANCE_CONNECTION_NAME
unixSocketPath: /cloudsql/INSTANCE_CONNECTION_NAME/.s.PGSQL.5432
這裏幾個注意的地方
- url 不需要寫 hostname 和 port,用以上的範例置換 database-name 就可以
- cloudSQL.xxxx 名稱是我自己隨便取的,可自己更換,重點在對應下面的新增程式
- INSTANCE_CONNECTION_NAME 格式為:PROJECT-ID:REGION:INSTANCE-ID,也就是下圖中的「執行個體連線名稱」,請自行置換
Step 4:置換 Spring Boot DataSource Configuration
新增一支程式
package tw.havocfuture;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.url}")
private String dataSourceUrl;
@Value("${spring.datasource.username}")
private String user;
@Value("${spring.datasource.password}")
private String password;
@Value("${cloudSQL.socketFactory}")
private String socketFactory;
@Value("${cloudSQL.cloudSqlInstance}")
private String cloudSqlInstance;
@Value("${cloudSQL.unixSocketPath}")
private String unixSocketPath;
@Bean
public DataSource dataSource() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(dataSourceUrl);
config.setUsername(user);
config.setPassword(password);
config.addDataSourceProperty("socketFactory", socketFactory);
config.addDataSourceProperty("cloudSqlInstance", cloudSqlInstance);
config.addDataSourceProperty("unixSocketPath", unixSocketPath);
HikariDataSource ds = new HikariDataSource(config);
return ds;
}
}
Spring Boot 預設就是使用 hikari,這裏主要就是要增加 socketFactory, cloudSqlInstance, unixSocketPath 的設定,用 @Value 取出 application.yml 的值,然後用 @Configuration 置換掉預設值
測試連線成功,問題解決