เชื่อมต่อแอปพลิเคชัน Java กับ AlloyDB สำหรับ PostgreSQL ด้วยการตรวจสอบสิทธิ์ IAM

1. ก่อนเริ่มต้น

Java และ AlloyDB: การเชื่อมต่อที่ง่ายและปลอดภัย

AlloyDB for PostgreSQL เป็นบริการฐานข้อมูลที่มีการจัดการครบวงจรและเข้ากันได้กับ PostgreSQL ของ Google Cloud สำหรับภาระงานขององค์กรที่มีความต้องการสูง การเชื่อมต่อแอปพลิเคชัน Java กับ AlloyDB อย่างปลอดภัยและมีประสิทธิภาพทำได้ผ่าน AlloyDB Java Connector (alloydb-jdbc-connector) ซึ่งเป็นไลบรารีของไคลเอ็นต์ที่เหนือกว่าไดรเวอร์ JDBC มาตรฐาน

ความมหัศจรรย์ของ AlloyDB Java Connector

alloydb-jdbc-connector ทำหน้าที่เป็นSocketFactoryอัจฉริยะสำหรับไดรเวอร์ JDBC มาตรฐานของ PostgreSQL (org.postgresql:postgresql) โดยจะแยกการสร้างการเชื่อมต่อฐานข้อมูลที่ปลอดภัยออกไป เพื่อให้คุณมุ่งเน้นที่ตรรกะของแอปพลิเคชันแทนการรักษาความปลอดภัยของเครือข่ายหรือการหมุนเวียนข้อมูลเข้าสู่ระบบ

  • การเข้ารหัส mTLS 1.3: สร้างการเชื่อมต่อ TLS 1.3 ที่ตรวจสอบสิทธิ์ร่วมกันระหว่าง JVM และอินสแตนซ์ AlloyDB โดยอัตโนมัติ โดยที่คุณไม่ต้องจัดการใบรับรองไคลเอ็นต์ SSL/TLS ด้วยตนเอง
  • การให้สิทธิ์ตาม IAM: แทนที่จะใช้รหัสผ่านฐานข้อมูลแบบคงที่ ตัวเชื่อมต่อจะใช้ Google Cloud IAM และข้อมูลรับรองเริ่มต้นของแอปพลิเคชัน (ADC) เพื่อรับโทเค็นเพื่อการเข้าถึง OAuth2 ที่มีอายุสั้นโดยอัตโนมัติ
  • การแปลง IP แบบไดนามิก: ค้นหา AlloyDB Admin API เพื่อแปลงที่อยู่ IP ปัจจุบัน (PRIVATE, PUBLIC หรือ Private Service Connect PSC) ของอินสแตนซ์ฐานข้อมูลแบบไดนามิก คุณไม่จำเป็นต้องฮาร์ดโค้ดชื่อโฮสต์หรือที่อยู่ IP ซึ่งจะทำให้การเฟลโอเวอร์และการบำรุงรักษาเป็นไปอย่างโปร่งใส

วิธีการทำงานเบื้องหลังของ AlloyDB Java Connector

ข้อกำหนดเบื้องต้น

  • ความคุ้นเคยกับ Java, Maven และแนวคิด SQL/JDBC พื้นฐาน
  • มีความคุ้นเคยเบื้องต้นกับ Google Cloud CLI (gcloud) และบทบาท IAM

สิ่งที่คุณจะทำ

  • กำหนดค่าอินสแตนซ์ AlloyDB และบัญชีบริการสำหรับการตรวจสอบสิทธิ์ฐานข้อมูล IAM แบบไม่ใช้รหัสผ่าน
  • ให้สิทธิ์สคีมาและตาราง PostgreSQL ที่มีสิทธิ์ขั้นต่ำที่สุดแก่ผู้ใช้หลักของ IAM
  • กำหนดค่า DataSource ระดับเวอร์ชันที่ใช้งานจริงด้วย Plain JDBC และ HikariCP โดยใช้ SocketFactory ของ AlloyDB
  • ผสานรวม AlloyDB Java Connector ในแอปพลิเคชัน Spring Boot, Quarkus และ Micronaut
  • ลงทะเบียน AlloyDB SocketFactory สำหรับการคอมไพล์ GraalVM Native Image และแก้ปัญหาข้อผิดพลาดที่พบบ่อยเกี่ยวกับการตรวจสอบสิทธิ์ IAM

สิ่งที่คุณต้องมี

  • โปรเจ็กต์ที่อยู่ในระบบคลาวด์ของ Google ที่เปิดใช้การเรียกเก็บเงินและคลัสเตอร์ AlloyDB และอินสแตนซ์หลักที่ใช้งานอยู่ (ดูคู่มือการสร้างคลัสเตอร์ AlloyDB หรือสร้างคลัสเตอร์ AlloyDB Express ในเวลาไม่ถึง 2 วินาที)
  • บทบาท IAM roles/alloydb.admin และ roles/resourcemanager.projectIamAdmin ในโปรเจ็กต์ของคุณ
  • ติดตั้ง Java 17 ขึ้นไป (แนะนำ Java 17 หรือ 21 LTS) และ Apache Maven
  • ติดตั้ง gcloud CLI และไคลเอ็นต์ psql แล้ว (หรือเข้าถึง Google Cloud Shell)

2. ตั้งค่าการตรวจสอบสิทธิ์ฐานข้อมูล IAM ใน AlloyDB

ก่อนเขียนโค้ด Java คุณต้องกำหนดค่าอินสแตนซ์ AlloyDB ให้ยอมรับโทเค็น IAM และสร้างข้อมูลประจำตัว IAM ที่แอปพลิเคชันจะใช้ใน 3 เลเยอร์ ได้แก่ Google Cloud IAM, AlloyDB Control Plane และเครื่องมือ PostgreSQL

การตั้งค่า 3 ชั้นสำหรับการตรวจสอบสิทธิ์ฐานข้อมูล IAM ของ AlloyDB

ตั้งค่าตัวแปรสภาพแวดล้อม

ในเทอร์มินัล ให้ส่งออกตัวแปรที่สอดคล้องกับโปรเจ็กต์ที่อยู่ในระบบคลาวด์ของ Google และอินสแตนซ์ AlloyDB (แทนที่ตัวยึดตำแหน่ง ด้วยค่าของคุณ)

export PROJECT_ID="<PROJECT_ID>"
export REGION="europe-west1"
export CLUSTER_ID="<CLUSTER_ID>"
export INSTANCE_ID="<INSTANCE_ID>"
export DB_NAME="dvdrental"

เปิดใช้ API ที่จำเป็น

เปิดใช้ AlloyDB, Compute Engine, Cloud Resource Manager, IAM และ Service Usage API ในโปรเจ็กต์โดยทำดังนี้

gcloud services enable \
    alloydb.googleapis.com \
    compute.googleapis.com \
    cloudresourcemanager.googleapis.com \
    iam.googleapis.com \
    serviceusage.googleapis.com \
    --project="${PROJECT_ID}"

สร้างบัญชีบริการและให้สิทธิ์บทบาท IAM

สร้างบัญชีบริการเฉพาะที่จะทำหน้าที่เป็นข้อมูลประจำตัวของแอปพลิเคชัน Java โดยทำดังนี้

gcloud iam service-accounts create java-alloydb-connector \
    --project="${PROJECT_ID}" \
    --display-name="Java Application DB Connector"

มอบบทบาท IAM 2 บทบาทที่ AlloyDB Java Connector กำหนดให้แก่บัญชีบริการ ดังนี้

  1. roles/alloydb.client: จำเป็นสำหรับการดึงข้อมูลเมตาของอินสแตนซ์และขอใบรับรอง mTLS ชั่วคราวจาก AlloyDB Admin API
  2. roles/serviceusage.serviceUsageConsumer: ต้องใช้สำหรับการตรวจสอบสิทธิ์ฐานข้อมูล IAM
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/alloydb.client"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/serviceusage.serviceUsageConsumer"

เปิดใช้การตรวจสอบสิทธิ์ IAM ในอินสแตนซ์ AlloyDB

อัปเดตแฟล็กอินสแตนซ์ AlloyDB เพื่อตั้งค่า alloydb.iam_authentication=on ซึ่งเป็นแฟล็กแบบไดนามิกและมีผลทันทีโดยไม่ต้องรีสตาร์ทอินสแตนซ์

gcloud alloydb instances update "${INSTANCE_ID}" \
    --cluster="${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --database-flags=alloydb.iam_authentication=on

ลงทะเบียนผู้ใช้ฐานข้อมูล IAM

ลงทะเบียนบัญชีบริการเป็นผู้ใช้ฐานข้อมูล IAM_BASED ในคลัสเตอร์ AlloyDB โดยใช้ gcloud ดังนี้

gcloud alloydb users create "java-alloydb-connector@${PROJECT_ID}.iam" \
    --cluster="${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --type=IAM_BASED

ให้สิทธิ์สคีมาและตาราง PostgreSQL

ผู้ใช้ฐานข้อมูล IAM ที่สร้างขึ้นใหม่จะไม่มีสิทธิ์เข้าถึงตารางโดยค่าเริ่มต้น เชื่อมต่อกับอินสแตนซ์ AlloyDB ในฐานะผู้ใช้ที่ดูแลระบบ postgres (ผ่าน psql หรือ AlloyDB Studio ในคอนโซล Cloud) และให้สิทธิ์เข้าถึงฐานข้อมูลและตารางเป้าหมาย (เช่น ฐานข้อมูลตัวอย่าง DVD Rental)

-- Connect to your target database first
\c dvdrental

-- (Optional) Create a sample film table if your database is empty
CREATE TABLE IF NOT EXISTS public.film (
    film_id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    release_year INTEGER DEFAULT 2006,
    rating VARCHAR(10) DEFAULT 'PG',
    rental_rate NUMERIC(4,2) DEFAULT 2.99
);

INSERT INTO public.film (film_id, title, release_year, rating, rental_rate) VALUES
    (1, 'Academy Dinosaur', 2006, 'PG', 0.99),
    (2, 'Ace Goldfinger', 2006, 'G', 4.99),
    (3, 'Adaptation Holes', 2006, 'NC-17', 2.99),
    (4, 'Affair Prejudice', 2006, 'G', 2.99),
    (5, 'African Egg', 2006, 'G', 2.99)
ON CONFLICT (film_id) DO NOTHING;

-- Grant permissions to the IAM user (double quotes are required because of '@' and '.')
GRANT CONNECT ON DATABASE "dvdrental" TO "java-alloydb-connector@<PROJECT_ID>.iam";
GRANT USAGE ON SCHEMA public TO "java-alloydb-connector@<PROJECT_ID>.iam";
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO "java-alloydb-connector@<PROJECT_ID>.iam";

เมื่อเรียกใช้คำสั่งเหล่านี้ใน AlloyDB Studio (หรือ psql) คุณสามารถตรวจสอบสคีมา public.film และยืนยันแถวตัวอย่างได้ ดังนี้

การให้สิทธิ์ IAM และตรวจสอบ public.film ใน AlloyDB Studio

กำหนดค่าข้อมูลรับรองเริ่มต้นของแอปพลิเคชัน (ADC) ในเครื่อง

เมื่อเรียกใช้ใน Cloud Run, Google Kubernetes Engine (GKE) หรือ Compute Engine ให้แนบบัญชีบริการ java-alloydb-connector กับภาระงานโดยตรง เมื่อเรียกใช้ในเวิร์กสเตชันของคุณ ให้แอบอ้างเป็นบัญชีบริการเพื่อให้ ADC สร้างโทเค็นสำหรับข้อมูลประจำตัวนั้น โดยทำดังนี้

gcloud auth application-default login \
    --impersonate-service-account="java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com"

3. เชื่อมต่อด้วย Plain JDBC และ HikariCP

สำหรับแอปพลิเคชัน Java ระดับการใช้งานจริง พูลการเชื่อมต่อเป็นสิ่งจำเป็น และ HikariCP เป็นมาตรฐานอุตสาหกรรม ในขั้นตอนนี้ คุณจะสร้างแอปพลิเคชัน JDBC + HikariCP แบบธรรมดาที่มอบสิทธิ์การสร้างซ็อกเก็ตให้กับ com.google.cloud.alloydb.SocketFactory

สร้างโปรเจ็กต์ Maven (pom.xml)

สร้างไดเรกทอรีใหม่ jdbc-app ด้วยไฟล์ pom.xml ที่มีไดรเวอร์ JDBC สำหรับ PostgreSQL (42.7.14), AlloyDB Java Connector (1.4.0) และ HikariCP (7.1.0) ดังนี้

mkdir -p jdbc-app/src/main/java
cd jdbc-app

สร้าง pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example.alloydb</groupId>
    <artifactId>alloydb-jdbc-sample</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <exec.mainClass>AlloyDBConnectivity</exec.mainClass>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.14</version>
        </dependency>

        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>alloydb-jdbc-connector</artifactId>
            <version>1.4.0</version>
        </dependency>

        <dependency>
            <groupId>com.zaxxer</groupId>
            <artifactId>HikariCP</artifactId>
            <version>7.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>2.0.16</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.5.0</version>
                <configuration>
                    <cleanupDaemonThreads>false</cleanupDaemonThreads>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

สร้างAlloyDBConnectivity.java

สร้าง src/main/java/AlloyDBConnectivity.java (ดูใน GitHub Gist) ดังนี้

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class AlloyDBConnectivity {
    // Retrieve configuration from environment variables
    private static final String ALLOYDB_INSTANCE_URI = System.getenv("ALLOYDB_INSTANCE_NAME"); // e.g., projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>
    private static final String ALLOYDB_DB_NAME = System.getenv("ALLOYDB_DB"); // e.g., "dvdrental"
    private static final String ALLOYDB_USER = System.getenv("ALLOYDB_USER"); // e.g., "java-alloydb-connector@<PROJECT_ID>.iam"
    private static final String ALLOYDB_PASS = System.getenv("ALLOYDB_PASS"); // Database password if not using IAM auth

    public static HikariDataSource createDataSource() {
        HikariConfig config = new HikariConfig();

        // The JDBC URL does not need a host or port. The SocketFactory will resolve the IP address.
        config.setJdbcUrl(String.format("jdbc:postgresql:///%s", ALLOYDB_DB_NAME));
        config.setUsername(ALLOYDB_USER);

        // Only set the password if you are NOT using IAM authentication.
        if (System.getenv("ALLOYDB_ENABLE_IAM_AUTH") == null || !System.getenv("ALLOYDB_ENABLE_IAM_AUTH").equalsIgnoreCase("true")) {
            config.setPassword(ALLOYDB_PASS);
        }

        // 1. Set the SocketFactory to the AlloyDB Connector's implementation.
        config.addDataSourceProperty("socketFactory", "com.google.cloud.alloydb.SocketFactory");

        // 2. Tell the connector which AlloyDB instance to connect to.
        config.addDataSourceProperty("alloydbInstanceName", ALLOYDB_INSTANCE_URI);

        // 3. (Recommended) Enable IAM database authentication.
        if (System.getenv("ALLOYDB_ENABLE_IAM_AUTH") != null && System.getenv("ALLOYDB_ENABLE_IAM_AUTH").equalsIgnoreCase("true")) {
            config.addDataSourceProperty("alloydbEnableIAMAuth", "true");
        }

        // 4. (Optional) Specify IP type (e.g., "PRIVATE", "PUBLIC", "PSC"). Defaults to PRIVATE.
        String ipType = System.getenv("ALLOYDB_IP_TYPE");
        if (ipType != null) {
            config.addDataSourceProperty("alloydbIpType", ipType);
        }

        // Baseline HikariCP settings
        config.setMaximumPoolSize(10);
        config.setMinimumIdle(2);
        config.setConnectionTimeout(30000); // 30 seconds
        config.setIdleTimeout(600000);      // 10 minutes
        config.setMaxLifetime(1800000);     // 30 minutes

        return new HikariDataSource(config);
    }

    public static void main(String[] args) {
        String sql = "SELECT title, release_year FROM public.film ORDER BY film_id LIMIT 5";

        try (HikariDataSource pool = createDataSource();
             Connection conn = pool.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql);
             ResultSet rs = stmt.executeQuery()) {
            System.out.println("Successfully connected to AlloyDB via IAM! Sample films:");
            while (rs.next()) {
                System.out.printf(" - %s (%d)%n", rs.getString("title"), rs.getInt("release_year"));
            }
        } catch (SQLException e) {
            System.err.println("Database connection failed: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

โครงสร้างของ URL ของ AlloyDB JDBC และการกำหนดค่า HikariCP

เรียกใช้แอปพลิเคชัน JDBC แบบธรรมดา

ส่งออกตัวแปรสภาพแวดล้อมสำหรับอินสแตนซ์ AlloyDB (ตั้งค่า ALLOYDB_IP_TYPE="PUBLIC" หากเชื่อมต่อจากภายนอก VPC ผ่าน IP สาธารณะ หรือ "PRIVATE" / "PSC" เมื่ออยู่ภายใน VPC) แล้วเรียกใช้แอปพลิเคชัน ดังนี้

export ALLOYDB_INSTANCE_NAME="projects/${PROJECT_ID}/locations/${REGION}/clusters/${CLUSTER_ID}/instances/${INSTANCE_ID}"
export ALLOYDB_DB="${DB_NAME}"
export ALLOYDB_USER="java-alloydb-connector@${PROJECT_ID}.iam"
export ALLOYDB_ENABLE_IAM_AUTH="true"
export ALLOYDB_IP_TYPE="PRIVATE" # Set to PUBLIC if connecting over Public IP from a local laptop

mvn compile exec:java

คุณควรเห็นเอาต์พุตที่คล้ายกับตัวอย่างต่อไปนี้

[AlloyDBConnectivity.main()] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
[AlloyDBConnectivity.main()] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@1998bf07
[AlloyDBConnectivity.main()] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
Successfully connected to AlloyDB via IAM! Sample films:
 - Academy Dinosaur (2006)
 - Ace Goldfinger (2006)
 - Adaptation Holes (2006)
 - Affair Prejudice (2006)
 - African Egg (2006)

เอาต์พุตเทอร์มินัลของตัวอย่าง JDBC และ HikariCP แบบธรรมดา

4. เชื่อมต่อกับ Spring Boot

Spring Boot ช่วยให้การเชื่อมต่อฐานข้อมูลง่ายขึ้นโดยการกำหนดค่าพูลการเชื่อมต่อ HikariCP โดยอัตโนมัติจากพร็อพเพอร์ตี้แอปพลิเคชันของคุณ การผสานรวม AlloyDB Java Connector ไม่จำเป็นต้องใช้โค้ด DataSource Bean ที่กำหนดเองเลย

สร้างโปรเจ็กต์ Spring Boot (pom.xml)

สร้างไดเรกทอรี spring-boot-app ใหม่โดยทำดังนี้

cd ..
mkdir -p spring-boot-app/src/main/java/com/example/alloydb
mkdir -p spring-boot-app/src/main/resources
cd spring-boot-app

สร้าง pom.xml ด้วย spring-boot-starter-jdbc (ซึ่งรวมถึง HikariCP), org.postgresql:postgresql และ com.google.cloud:alloydb-jdbc-connector:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>4.0.5</version>
        <relativePath/>
    </parent>

    <groupId>com.example.alloydb</groupId>
    <artifactId>alloydb-spring-boot-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <java.version>17</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.7.14</version>
        </dependency>

        <dependency>
            <groupId>com.google.cloud</groupId>
            <artifactId>alloydb-jdbc-connector</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

กำหนดค่า application.properties หรือตัวแปรสภาพแวดล้อม

คุณกำหนดค่า URL การเชื่อมต่อได้ใน src/main/resources/application.properties ดังนี้

# No host or port is needed; the SocketFactory handles it.
spring.datasource.url=jdbc:postgresql:///dvdrental?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>&alloydbEnableIAMAuth=true
spring.datasource.username=java-alloydb-connector@<PROJECT_ID>.iam
# No password required when alloydbEnableIAMAuth is true

หากต้องการให้ไฟล์ต้นฉบับไม่มีรหัสโปรเจ็กต์หรือชื่ออินสแตนซ์เลย คุณสามารถปล่อย application.properties ว่างไว้และระบุตัวแปรสภาพแวดล้อม Spring Boot มาตรฐานเหล่านี้ที่รันไทม์ได้

ตัวแปรสภาพแวดล้อม

ค่า

SPRING_DATASOURCE_URL

jdbc:postgresql:///dvdrental?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects//locations//clusters//instances/&alloydbEnableIAMAuth=true

SPRING_DATASOURCE_USERNAME

java-alloydb-connector@.iam

สร้างAlloyDbTestApplication.java

สร้าง src/main/java/com/example/alloydb/AlloyDbTestApplication.java (ดูใน GitHub Gist) ดังนี้

package com.example.alloydb;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;

@SpringBootApplication
public class AlloyDbTestApplication {

    public static void main(String[] args) {
        System.exit(SpringApplication.exit(SpringApplication.run(AlloyDbTestApplication.class, args)));
    }

    @Bean
    CommandLineRunner testConnection(JdbcTemplate jdbcTemplate) {
        return args -> {
            String result = jdbcTemplate.queryForObject("SELECT 'Connection Successful!'", String.class);
            System.out.println(">>> ALLOYDB STATUS: " + result);
        };
    }
}

เรียกใช้แอปพลิเคชัน Spring Boot

ส่งออกตัวแปรสภาพแวดล้อมและเรียกใช้แอปด้วย mvn spring-boot:run:

export SPRING_DATASOURCE_URL="jdbc:postgresql:///${DB_NAME}?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects/${PROJECT_ID}/locations/${REGION}/clusters/${CLUSTER_ID}/instances/${INSTANCE_ID}&alloydbEnableIAMAuth=true"
export SPRING_DATASOURCE_USERNAME="java-alloydb-connector@${PROJECT_ID}.iam"

mvn spring-boot:run

คุณควรเห็นข้อมูลต่อไปนี้

>>> ALLOYDB STATUS: Connection Successful!

การกำหนดค่าอัตโนมัติของ Spring Boot และเอาต์พุตการดำเนินการแบบเรียลไทม์

5. เชื่อมต่อกับ Quarkus, Micronaut และ GraalVM Native Image

เนื่องจาก AlloyDB Java Connector ทำงานที่ระดับไดรเวอร์ JDBC การสลับระหว่างเฟรมเวิร์ก Java ที่ดำเนินการบนระบบคลาวด์จึงไม่เปลี่ยนวิธีรักษาความปลอดภัยการรับส่งข้อมูลฐานข้อมูลหรือจัดการข้อมูลประจำตัว ทั้ง Quarkus และ Micronaut จะรวมไดรเวอร์ JDBC ที่สำคัญและมอบสิทธิ์การเข้ารหัส mTLS, การแปลง IP แบบไดนามิก และการตรวจสอบสิทธิ์ IAM ให้กับเครื่องมือเชื่อมต่อผ่าน ADC

เฟรมเวิร์ก Java ที่ดำเนินการบนระบบคลาวด์และ GraalVM Native Image ที่มี AlloyDB

การกำหนดค่า Quarkus (application.properties ด้วย Agroal)

ในแอปพลิเคชัน Quarkus (quarkus-agroal และ quarkus-jdbc-postgresql) ให้เพิ่ม com.google.cloud:alloydb-jdbc-connector:1.4.0 ลงในทรัพยากร Dependency และส่งพารามิเตอร์เครื่องมือเชื่อมต่อผ่าน quarkus.datasource.jdbc.additional-jdbc-properties ใน src/main/resources/application.properties:

quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=java-alloydb-connector@<PROJECT_ID>.iam
quarkus.datasource.jdbc.url=jdbc:postgresql:///dvdrental
quarkus.datasource.jdbc.additional-jdbc-properties.socketFactory=com.google.cloud.alloydb.SocketFactory
quarkus.datasource.jdbc.additional-jdbc-properties.alloydbInstanceName=projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>
quarkus.datasource.jdbc.additional-jdbc-properties.alloydbEnableIAMAuth=true

การกำหนดค่า Micronaut (application.yml พร้อม HikariCP)

ในแอปพลิเคชัน Micronaut (micronaut-jdbc-hikari) ให้เพิ่ม com.google.cloud:alloydb-jdbc-connector:1.4.0 และกำหนดค่า src/main/resources/application.yml ดังนี้

datasources:
  default:
    url: jdbc:postgresql:///dvdrental?socketFactory=com.google.cloud.alloydb.SocketFactory&alloydbInstanceName=projects/<PROJECT_ID>/locations/<REGION>/clusters/<CLUSTER_ID>/instances/<INSTANCE_ID>&alloydbEnableIAMAuth=true
    username: java-alloydb-connector@<PROJECT_ID>.iam
    driver-class-name: org.postgresql.Driver

การลงทะเบียนเครื่องมือเชื่อมต่อสำหรับ GraalVM Native Image

เมื่อคอมไพล์แอปพลิเคชันเป็น GraalVM Native Image (ด้วย Spring Boot 3+, Quarkus หรือ Micronaut) จะมีรายละเอียดสำคัญอย่างหนึ่งคือ Reflection

ไดรเวอร์ JDBC สำหรับ PostgreSQL จะสร้างอินสแตนซ์ com.google.cloud.alloydb.SocketFactory แบบไดนามิกตามชื่อคลาสโดยใช้การสะท้อน หากต้องการป้องกันไม่ให้การวิเคราะห์แบบคงที่ของ GraalVM ตัดคลาสออกระหว่างการคอมไพล์แบบเนทีฟ ให้ลงทะเบียนคลาสดังกล่าวอย่างชัดแจ้ง ดังนี้

  • Spring Boot 3 ขึ้นไป: เพิ่ม @RegisterReflectionForBinding(com.google.cloud.alloydb.SocketFactory.class) ลงในแอปพลิเคชันหลักหรือคลาส @Configuration ดังนี้
    @SpringBootApplication
    @RegisterReflectionForBinding(com.google.cloud.alloydb.SocketFactory.class)
    public class AlloyDbTestApplication { ... }
    
  • Quarkus: เพิ่ม @RegisterForReflection(targets = com.google.cloud.alloydb.SocketFactory.class) ลงในคลาสหลักหรือ CDI Bean:
    @QuarkusMain
    @RegisterForReflection(targets = com.google.cloud.alloydb.SocketFactory.class)
    public class QuarkusAlloyDbApp implements QuarkusApplication { ... }
    

6. แก้ปัญหาที่พบได้ทั่วไปเกี่ยวกับ IAM และการเชื่อมต่อ

หากพบข้อผิดพลาด FATAL: password authentication failed for user หรือ AlloyDB IAM authentication failed ให้ตรวจสอบ 4 ส่วนต่อไปนี้

ข้อมูลสรุปเกี่ยวกับการแก้ปัญหา IAM และการเชื่อมต่อของ AlloyDB

1. นำ .gserviceaccount.com ออกจากชื่อผู้ใช้ฐานข้อมูลของบัญชีบริการ

สาเหตุที่พบบ่อยที่สุดของการตรวจสอบสิทธิ์ IAM ไม่สำเร็จคือการส่งอีเมลบัญชีบริการแบบเต็มเป็นชื่อผู้ใช้ฐานข้อมูล AlloyDB จะจัดรูปแบบชื่อผู้ใช้ฐานข้อมูล IAM แตกต่างกันไปตามประเภทหลัก ดังนี้

  • สำหรับบัญชีบริการ IAM (ตัด .gserviceaccount.com): หากอีเมลบัญชีบริการ Google Cloud คือ java-alloydb-connector@.iam.gserviceaccount.com คุณต้องนำคำต่อท้าย .gserviceaccount.com ออกและตั้งค่าชื่อผู้ใช้ฐานข้อมูล JDBC/AlloyDB เป็น java-alloydb-connector@.iam
  • สำหรับบัญชีผู้ใช้หรือกลุ่ม IAM (เก็บอีเมลแบบเต็มไว้): หากคุณตรวจสอบสิทธิ์ในฐานะผู้ใช้ Google Workspace หรือ Cloud Identity ที่เป็นบุคคลธรรมดา ให้ใช้อีเมลแบบเต็มโดยไม่มีการแก้ไข (เช่น )

2. ยืนยันวิธีสร้างผู้ใช้ฐานข้อมูล

คุณได้ลงทะเบียนผู้ใช้ผ่าน gcloud alloydb users create --type=IAM_BASED (หรือ Console/Terraform) หรือไม่ การสร้างผู้ใช้ด้วย SQL ดิบ CREATE USER จะใช้ไม่ได้กับการตรวจสอบสิทธิ์ IAM เนื่องจาก AlloyDB จำกัด GRANT alloydbiamuser ไว้ที่ระนาบควบคุม

3. ยืนยันข้อมูลประจำตัว ADC ที่ใช้งานอยู่

ตรวจสอบว่าสภาพแวดล้อมที่กระบวนการ Java ทำงานอยู่ได้รับการตรวจสอบสิทธิ์เป็น IAM Principal เป้าหมายจริง โดยทำดังนี้

  • เวิร์กสเตชันภายใน (เชื่อมต่อเป็นบัญชีบริการ): เรียกใช้ gcloud auth application-default login --impersonate-service-account=java-alloydb-connector@.iam.gserviceaccount.com เพื่อให้ ADC สร้างโทเค็นสำหรับบัญชีบริการแทนอีเมลส่วนตัวของคุณ
  • Cloud Run / GKE: ยืนยันว่าบัญชีบริการเชื่อมโยงกับภาระงานและมีทั้ง roles/alloydb.client และ roles/serviceusage.serviceUsageConsumer

4. พิจารณาการเผยแพร่ IAM และการกำหนดเส้นทางเครือข่าย

  • ความล่าช้าในการเผยแพร่ IAM: การเชื่อมโยงนโยบาย IAM อาจใช้เวลา 60-120 วินาทีในการเผยแพร่ หากเพิ่งสร้างบัญชีบริการหรือการเชื่อมโยงบทบาท ให้รอสักครู่แล้วลองอีกครั้ง
  • การเข้าถึงเครือข่าย VPC: โดยค่าเริ่มต้น ตัวเชื่อมต่อ AlloyDB Java จะเชื่อมต่อกับ IP PRIVATE ของอินสแตนซ์ หากแอปพลิเคชันทำงานบน Cloud Run ให้กำหนดค่าข้อมูลขาออก VPC โดยตรง (แนะนำสำหรับเวลาในการตอบสนองและค่าใช้จ่ายที่ต่ำกว่าเมื่อเทียบกับเครื่องมือเชื่อมต่อการเข้าถึง VPC แบบ Serverless เดิม) หรือเชื่อมต่อผ่าน Private Service Connect (PSC) หากเชื่อมต่อจากเครื่องภายในที่อยู่นอก VPC ให้เปิดใช้ IP สาธารณะในอินสแตนซ์และส่ง alloydbIpType=PUBLIC

7. ล้างข้อมูล

โปรดล้างทรัพยากรด้านล่างเพื่อหลีกเลี่ยงการเรียกเก็บเงินที่ไม่จำเป็นในบัญชี Google Cloud และนำข้อมูลประจำตัว IAM ที่สร้างขึ้นระหว่าง Codelab นี้ออก

1. นำผู้ใช้ฐานข้อมูล IAM และบัญชีบริการของ AlloyDB ออก

ลบผู้ใช้ฐานข้อมูล IAM ออกจากคลัสเตอร์ AlloyDB และนำบัญชีบริการออกโดยทำดังนี้

# Delete the IAM database user from AlloyDB
gcloud alloydb users delete "java-alloydb-connector@${PROJECT_ID}.iam" \
    --cluster="${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --quiet

# Remove IAM policy bindings
gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/alloydb.client"

gcloud projects remove-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --role="roles/serviceusage.serviceUsageConsumer"

# Delete the Service Account
gcloud iam service-accounts delete \
    "java-alloydb-connector@${PROJECT_ID}.iam.gserviceaccount.com" \
    --project="${PROJECT_ID}" \
    --quiet

2. รีเซ็ตข้อมูลรับรองเริ่มต้นของแอปพลิเคชันในเครื่อง

หากคุณกำหนดค่าการแอบอ้างเป็น ADC ในเครื่อง ให้รีเซ็ตข้อมูลเข้าสู่ระบบในเครื่องกลับเป็นข้อมูลประจำตัวผู้ใช้เริ่มต้น โดยทำดังนี้

gcloud auth application-default login

3. (ไม่บังคับ) ลบคลัสเตอร์ AlloyDB

gcloud alloydb clusters delete "${CLUSTER_ID}" \
    --region="${REGION}" \
    --project="${PROJECT_ID}" \
    --force \
    --quiet

8. ขอแสดงความยินดี

คุณได้ทำ Codelab เสร็จแล้ว และสร้างการเชื่อมต่อที่ปลอดภัยและไม่ต้องใช้รหัสผ่านจาก Java ไปยัง AlloyDB สำหรับ PostgreSQL

สิ่งที่คุณทำสำเร็จ

  1. กำหนดค่าการตรวจสอบสิทธิ์ฐานข้อมูล IAM: สร้างบัญชีบริการที่มีสิทธิ์ขั้นต่ำที่สุด เปิดใช้ alloydb.iam_authentication=on ลงทะเบียนผู้ใช้ IAM ในคลัสเตอร์ AlloyDB และให้สิทธิ์ตาราง PostgreSQL
  2. เชื่อมต่อด้วย Plain JDBC และ HikariCP: กำหนดค่า HikariConfig ด้วย com.google.cloud.alloydb.SocketFactory สำหรับการเข้ารหัส mTLS 1.3 อัตโนมัติ, การรีเฟรชโทเค็น IAM และการแปลง IP แบบไดนามิก
  3. เฟรมเวิร์กที่ดำเนินการบนระบบคลาวด์แบบผสานรวม: กำหนดค่า Spring Boot, Quarkus (Agroal) และ Micronaut (HikariCP) โดยไม่มี Boilerplate ของพูลการเชื่อมต่อที่กำหนดเอง พร้อมคำแนะนำการสะท้อนสำหรับ GraalVM Native Image
  4. เชี่ยวชาญการแก้ปัญหา IAM: ได้เรียนรู้วิธีวินิจฉัยปัญหาการจัดรูปแบบชื่อผู้ใช้ การแอบอ้างเป็น ADC และการกำหนดเส้นทาง VPC

ดูข้อมูลเพิ่มเติม