איך מחברים אפליקציות Java ל-AlloyDB ל-PostgreSQL באמצעות אימות IAM

1. לפני שמתחילים

‫Java ו-AlloyDB: קישוריות פשוטה ומאובטחת

‫AlloyDB ל-PostgreSQL הוא שירות מנוהל של מסד נתונים ב-Google Cloud שתואם ל-PostgreSQL, ומיועד לעומסי עבודה כבדים בארגון. כדי לחבר אפליקציית Java ל-AlloyDB בצורה מאובטחת ויעילה, משתמשים במחבר Java ל-AlloyDB (alloydb-jdbc-connector) – ספריית לקוח שהיא יותר ממנהל התקן JDBC רגיל.

הקסם של מחבר Java ל-AlloyDB

‫alloydb-jdbc-connector פועל כ-SocketFactory חכם למנהל ההתקן הרגיל של PostgreSQL JDBC‏ (org.postgresql:postgresql). הוא מסתיר את תהליך יצירת החיבור המאובטח למסד הנתונים, כדי שתוכלו להתמקד בלוגיקה של האפליקציה במקום באבטחת הרשת או ברוטציית פרטי הכניסה:

  • הצפנת mTLS 1.3: יוצרת באופן אוטומטי חיבור TLS 1.3 עם אימות הדדי בין ה-JVM לבין מכונת AlloyDB, בלי שתצטרכו לנהל באופן ידני את אישורי הלקוח לשרת בחיבור SSL/TLS.
  • הרשאה מבוססת-IAM: במקום להסתמך על סיסמאות סטטיות של מסדי נתונים, המחבר משתמש ב-Google Cloud IAM ובApplication Default Credentials ‏(ADC) כדי לקבל באופן אוטומטי טוקני גישה זמניים של OAuth2.
  • ‫Dynamic IP Resolution: שאילתות ל-AlloyDB Admin API כדי לפתור באופן דינמי את כתובת ה-IP הנוכחית (PRIVATE,‏ PUBLIC או Private Service Connect PSC) של מכונת מסד הנתונים. אין צורך להגדיר בקוד שמות מארחים או כתובות IP, כך שפעולות של יתירות כשל ותחזוקה מתבצעות באופן שקוף.

איך פועל מחבר Java ל-AlloyDB

דרישות מוקדמות

  • היכרות עם Java,‏ Maven ומושגים בסיסיים של SQL/JDBC.
  • היכרות בסיסית עם Google Cloud CLI (gcloud) ותפקידי IAM.

הפעולות שתבצעו:

  • מגדירים מכונה של AlloyDB וחשבון שירות לאימות מסד נתונים של IAM ללא סיסמה.
  • מקצים הרשאות מינימליות לסכימה ולטבלה ב-PostgreSQL לחשבון משתמש ב-IAM.
  • מגדירים DataSource ברמת ייצור עם Plain JDBC ו-HikariCP באמצעות SocketFactory של AlloyDB.
  • לשלב את מחבר Java ל-AlloyDB באפליקציות Spring Boot,‏ Quarkus ו-Micronaut.
  • רושמים את AlloyDB SocketFactory ל-GraalVM Native Image לצורך קומפילציה ופותרים בעיות נפוצות באימות IAM.

הדרישות

  • פרויקט בענן של Google Cloud עם חיוב מופעל ואשכול AlloyDB ומכונה ראשית פעילים (אפשר לעיין במדריך ליצירת אשכול AlloyDB או להתחיל הרצה של אשכול AlloyDB Express תוך פחות משתי שניות).
  • תפקידי ה-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 שהאפליקציה תשתמש בה בשלוש שכבות: Google Cloud IAM, מישור הבקרה של AlloyDB ומנוע PostgreSQL.

הגדרה תלת-שכבתית לאימות מסד נתונים של AlloyDB IAM

הגדרה של משתני סביבה

בטרמינל, מייצאים את המשתנים שתואמים לפרויקט ב-Google Cloud ולמכונה של AlloyDB (מחליפים את ה-placeholder‏ בערכים שלכם):

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

הפעלת ממשקי ה-API הנדרשים

מפעילים בפרויקט את ממשקי ה-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 שנדרשים למחבר Java ל-AlloyDB:

  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

הגדרת Application Default Credentials (ADC) מקומיים

כשמפעילים את java-alloydb-connector ב-Cloud Run, ב-Google Kubernetes Engine ‏(GKE) או ב-Compute Engine, צריך לצרף את חשבון השירות ישירות לעומס העבודה. כשמריצים באופן מקומי בתחנת העבודה, מתחזים לחשבון השירות כדי ש-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 שמכיל את מנהל ההתקן של PostgreSQL JDBC‏ (42.7.14), את מחבר Java ל-AlloyDB‏ (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

הפעלת אפליקציית Plain 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)

פלט הטרמינל של הדוגמה של Plain JDBC ו-HikariCP

4. חיבור באמצעות Spring Boot

‫Spring Boot מפשט את הקישוריות למסד הנתונים על ידי הגדרה אוטומטית של מאגר חיבורים של HikariCP ממאפייני האפליקציה. כדי לשלב את מחבר Java ל-AlloyDB, לא צריך לכתוב קוד bean מותאם אישית של DataSource.

יצירת פרויקט 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

מכיוון שמחבר Java ל-AlloyDB פועל ברמת מנהל ההתקן של JDBC, מעבר בין מסגרות Java מבוססות-ענן לא משנה את האופן שבו מאבטחים את התנועה במסד הנתונים או מנהלים את הזהויות. גם Quarkus וגם Micronaut כוללים את מנהל ההתקן הבסיסי של JDBC, ומעבירים את הצפנת ה-mTLS, את הרזולוציה הדינמית של כתובות ה-IP ואת האימות של IAM למחבר באמצעות ADC.

‫Frameworks של Java מבוססי-ענן ו-GraalVM Native Image עם AlloyDB

הגדרת Quarkus (application.properties עם Agroal)

באפליקציית Quarkus (quarkus-agroal ו-quarkus-jdbc-postgresql), מוסיפים את com.google.cloud:alloydb-jdbc-connector:1.4.0 ליחסי התלות ומעבירים את הפרמטרים של המחבר באמצעות 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.

מנהל ההתקן של PostgreSQL JDBC יוצר מופע של 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 { ... }
    
  • קווארקוס: הוסף @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, בדקו את ארבעת התחומים הבאים:

תקציר לפתרון בעיות ב-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:

  • תחנת עבודה מקומית (התחברות כחשבון שירות): מריצים את הפקודה 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: יכול להיות שיעברו 60 עד 120 שניות עד שהקישורים של מדיניות IAM יופצו. אם יצרתם את חשבון השירות או את קישור התפקיד ממש עכשיו, חכו דקה ונסו שוב.
  • גישה לרשת VPC: כברירת מחדל, מחבר Java ל-AlloyDB מתחבר לכתובת ה-IP PRIVATE של המכונה. אם האפליקציה פועלת ב-Cloud Run, צריך להגדיר Direct VPC Egress (מומלץ כדי להפחית את זמן האחזור והעלות בהשוואה למחברים מדור קודם של חיבור לרשת (VPC) מאפליקציית serverless) או להתחבר באמצעות Private Service Connect (PSC). אם מתחברים ממכונה מקומית מחוץ ל-VPC, צריך להפעיל כתובת IP ציבורית במכונה ולהעביר את alloydbIpType=PUBLIC.

7. הסרת המשאבים

כדי להימנע מחיובים מיותרים בחשבון Google Cloud ולהסיר את זהויות ה-IAM שנוצרו במהלך ה-Codelab הזה, צריך לנקות את המשאבים הבאים.

1. הסרת משתמש מסד הנתונים וחשבון השירות של AlloyDB IAM

מוחקים את משתמש מסד הנתונים של 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. איפוס של Application Default Credentials מקומיים

אם הגדרתם התחזות ל-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) של מאגר חיבורים בהתאמה אישית, וגם רמזים להשתקפות (reflection) של GraalVM Native Image.
  4. פתרון בעיות מתקדם ב-IAM: למדתי איך לאבחן בעיות בפורמט של שמות משתמשים, התחזות ל-ADC וניתוב VPC.

מידע נוסף