Google Kubernetes-Engine (GKE)
In diesem Thema wird eine Legacy-Standalone-Java-Anwendung zu einem containerisierten Microservice modernisiert, der auf Google Kubernetes Engine (GKE) ausgeführt wird und über ein mTLS-Wallet eine Verbindung zu Oracle Autonomous AI Database herstellt.
Voraussetzungen
In diesem Abschnitt werden die Anforderungen von Oracle AI Database und Tabellen für die Java-Anwendung beschrieben, um eine Verbindung zu Oracle Autonomous AI Database (serverlos) herzustellen und auf die Tabelle Produkt zuzugreifen.
Oracle Autonomous AI Database
- Oracle Autonomous AI Database Wallet für die Verbindung.
- Oracle Database-Benutzerzugangsdaten zum Erstellen einer Datenbanksession und Ausführen von SQL-Befehlen.
- Konnektivität vom Anwendungsserver zu Oracle AI Database.
- Eine Produkttabelle in Oracle AI Database.
-- Create the Product table
CREATE TABLE Product (
id NUMBER PRIMARY KEY,
name VARCHAR2(100) NOT NULL,
price NUMBER(10, 2) NOT NULL
);
-- Insert a quick test record (optional, so your UI isn't empty on first load)
INSERT INTO Product (id, name, price)
VALUES (1, 'Test Migration Item', 99.99);
-- Commit the transaction
COMMIT;Implementierung
- Entwicklungsmaschinen einrichten
- Tools und Librarys: Installieren Sie die folgenden Librarys und Tools auf dem Entwicklungsrechner:
- Java Development Kit (JDK): JDK 25 oder höher.
- Oracle JDBC-Treiber: Laden Sie die eigenständige Datei ojdbc17.jar herunter.
- Rancher Desktop: Installieren Sie die Container-Engine dockerd (moby), und wählen Sie sie beim Setup aus. Dadurch erhalten Sie den standardmäßigen CLI-Befehl
docker.- Sie können andere Anwendungen verwenden, die Rancher Desktop ähnlich sind, wie Docker Desktop, Podman Desktop, Colima, OrbStack.
- Google Cloud-CLI (gcloud): So stellen Sie Cloud-Ressourcen bereit.
- Kubernetes-CLI (kubectl) und GKE-Authentifizierungs-Plugin: So interagieren Sie mit dem GKE-Cluster.
- Der Java-Quellcode (
ProductApiApp.java)- Erstellen Sie die Datei
ProductApiApp.java, und kopieren Sie den folgenden Inhalt in die Datei.import java.io.*; import java.net.InetSocketAddress; import java.sql.*; import com.sun.net.httpserver.*; public class ProductApiApp { // These environment variables are injected by the Kubernetes deployment.yaml private static final String DB_URL = System.getenv("DB_URL"); private static final String DB_USER = System.getenv("DB_USER"); private static final String DB_PASS = System.getenv("DB_PASS"); public static void main(String[] args) throws Exception { if (DB_URL == null || DB_USER == null || DB_PASS == null) { System.err.println("ERROR: Missing DB_URL, DB_USER, or DB_PASS"); System.exit(1); } // Bind to 0.0.0.0 (all interfaces) so the Kubernetes LoadBalancer can route traffic to it HttpServer server = HttpServer.create(new InetSocketAddress("0.0.0.0", 8080), 0); server.createContext("/api/products", new ProductApiHandler()); server.setExecutor(null); server.start(); System.out.println("API Microservice running on port 8080..."); System.out.println("Connecting to database using URL: " + DB_URL); } static class ProductApiHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { // Enable CORS so the UI microservice and remote callers can fetch data from this API exchange.getResponseHeaders().add("Access-Control-Allow-Origin", "*"); exchange.getResponseHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); exchange.getResponseHeaders().add("Access-Control-Allow-Headers", "Content-Type"); // Handle preflight requests for CORS if ("OPTIONS".equalsIgnoreCase(exchange.getRequestMethod())) { exchange.sendResponseHeaders(204, -1); return; } exchange.getResponseHeaders().add("Content-Type", "application/json"); String method = exchange.getRequestMethod(); StringBuilder jsonResponse = new StringBuilder(); try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS)) { if ("GET".equalsIgnoreCase(method)) { // READ: List all products jsonResponse.append("["); try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT id, name, price FROM Product ORDER BY id")) { boolean first = true; while (rs.next()) { if (!first) jsonResponse.append(","); jsonResponse.append("{") .append("\"id\":").append(rs.getInt("id")).append(",") .append("\"name\":\"").append(rs.getString("name")).append("\",") .append("\"price\":").append(rs.getDouble("price")) .append("}"); first = false; } } jsonResponse.append("]"); } else if ("POST".equalsIgnoreCase(method) || "PUT".equalsIgnoreCase(method) || "DELETE".equalsIgnoreCase(method)) { // Read the ENTIRE request payload (handles multi-line pretty JSON from Postman) InputStreamReader isr = new InputStreamReader(exchange.getRequestBody(), "utf-8"); StringBuilder payloadBuilder = new StringBuilder(); int b; while ((b = isr.read()) != -1) { payloadBuilder.append((char) b); } String payload = payloadBuilder.toString(); String idStr = extractJsonValue(payload, "id"); String name = extractJsonValue(payload, "name"); String priceStr = extractJsonValue(payload, "price"); int id = idStr.isEmpty() ? 0 : Integer.parseInt(idStr); double price = priceStr.isEmpty() ? 0.0 : Double.parseDouble(priceStr); if ("POST".equalsIgnoreCase(method)) { // CREATE String sql = "INSERT INTO Product (id, name, price) VALUES (?, ?, ?)"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, id); pstmt.setString(2, name); pstmt.setDouble(3, price); pstmt.executeUpdate(); } jsonResponse.append("{\"status\": \"Product created successfully\"}"); } else if ("PUT".equalsIgnoreCase(method)) { // UPDATE String sql = "UPDATE Product SET name=?, price=? WHERE id=?"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setString(1, name); pstmt.setDouble(2, price); pstmt.setInt(3, id); pstmt.executeUpdate(); } jsonResponse.append("{\"status\": \"Product updated successfully\"}"); } else if ("DELETE".equalsIgnoreCase(method)) { // DELETE String sql = "DELETE FROM Product WHERE id=?"; try (PreparedStatement pstmt = conn.prepareStatement(sql)) { pstmt.setInt(1, id); pstmt.executeUpdate(); } jsonResponse.append("{\"status\": \"Product deleted successfully\"}"); } } byte[] responseBytes = jsonResponse.toString().getBytes("UTF-8"); exchange.sendResponseHeaders(200, responseBytes.length); OutputStream os = exchange.getResponseBody(); os.write(responseBytes); os.close(); } catch (SQLException e) { String errorJson = "{\"error\":\"" + e.getMessage().replace("\"", "\\\"") + "\"}"; byte[] responseBytes = errorJson.getBytes("UTF-8"); exchange.sendResponseHeaders(500, responseBytes.length); OutputStream os = exchange.getResponseBody(); os.write(responseBytes); os.close(); } } // Lightweight JSON parser helper for zero-dependency constraint // Updated to handle arbitrary spaces and multi-line structures private String extractJsonValue(String json, String key) { if (json == null) return ""; String searchKey = "\"" + key + "\""; int start = json.indexOf(searchKey); if (start == -1) return ""; start = json.indexOf(":", start) + 1; int end = json.indexOf(",", start); if (end == -1) end = json.indexOf("}", start); if (end == -1) end = json.length(); return json.substring(start, end).replace("\"", "").trim(); } } }
- Erstellen Sie die Datei
- Die Containerisierung (
Dockerfile)- Erstellen Sie eine Datei namens
Dockerfilein demselben Verzeichnis wie Ihr Java-Code und die Dateiojdbc17.jar. Kompilieren Sie den Code im Container, um Build-Abhängigkeiten auf dem lokalen Rechner zu vermeiden.# Use Eclipse Temurin base image for Java FROM eclipse-temurin:25-jdk-jammy WORKDIR /app COPY ProductApiApp.java /app/ COPY ojdbc17.jar /app/ # Compile the Java application RUN javac -cp ojdbc17.jar ProductApiApp.java EXPOSE 8080 CMD ["java", "-cp", ".:ojdbc17.jar", "ProductApiApp"]
- Erstellen Sie eine Datei namens
- Tools und Librarys: Installieren Sie die folgenden Librarys und Tools auf dem Entwicklungsrechner:
- Bereitstellungsumgebung – Google Kubernetes Engine (GKE)Öffnen Sie PowerShell, Eingabeaufforderung oder Zsh, und melden Sie sich dann bei Google Cloud an:
gcloud auth login- Google Kubernetes Engine und Container Registry bereitstellen
- Definieren Sie die Variablen, und erstellen Sie dann das Google Artifact Registry-(GAR-) und das Google Kubernetes Engine-(GKE-)Cluster.
PROJECT_ID="your-gcp-project-id" # REPLACE with your actual GCP Project ID REGION="us-central1" REPO_NAME="mycompanyrepo123" CLUSTER_NAME="oracle-gke-cluster" # 1. Set the active project gcloud config set project $PROJECT_ID # 2. Enable Required APIs (Artifact Registry and Kubernetes Engine) gcloud services enable artifactregistry.googleapis.com container.googleapis.com # 3. Create Google Artifact Registry (GAR) for Docker images gcloud artifacts repositories create $REPO_NAME \ --repository-format=docker \ --location=$REGION \ --description="Docker repository for Oracle microservices" # 4. Create GKE Cluster (Standard, 1 node for testing) gcloud container clusters create $CLUSTER_NAME \ --region=$REGION \ --num-nodes=1 # 5. Get kubectl credentials to connect to your new cluster gcloud container clusters get-credentials $CLUSTER_NAME --region=$REGION
- Definieren Sie die Variablen, und erstellen Sie dann das Google Artifact Registry-(GAR-) und das Google Kubernetes Engine-(GKE-)Cluster.
- Container erstellen und übertragen (mit Rancher Desktop)
- Stellen Sie sicher, dass Rancher Desktop ausgeführt wird, und führen Sie dann die folgenden Befehle aus.
# 1. Configure local Docker/Rancher CLI to authenticate with Google Artifact Registry gcloud auth configure-docker $REGION-docker.pkg.dev # 2. Define your full image path IMAGE_PATH="$REGION-docker.pkg.dev/$PROJECT_ID/$REPO_NAME/product-api:v1" # 3. Build the image locally (Enforce AMD64 architecture for cloud compatibility) docker build --platform linux/amd64 -t $IMAGE_PATH . # 4. Push the image to Google Cloud docker push $IMAGE_PATH
- Stellen Sie sicher, dass Rancher Desktop ausgeführt wird, und führen Sie dann die folgenden Befehle aus.
- Oracle Wallet und Datenbank-Secrets konfigurieren
- Die autonome KI-Datenbank (serverlos) verwendet ein mTLS-Wallet. Laden Sie die ZIP-Datei des Instanz-Wallets über die OCI-Konsole herunter, und extrahieren Sie sie in den lokalen Ordner. Beispiel:
./adb-wallet.# 1. Upload the Wallet files into Kubernetes as a Secret kubectl create secret generic adb-wallet \ --from-file=./adb-wallet/cwallet.sso \ --from-file=./adb-wallet/tnsnames.ora \ --from-file=./adb-wallet/sqlnet.ora # 2. Upload your Database Credentials as a Secret kubectl create secret generic db-credentials \ --from-literal=username="ADMIN" \ --from-literal=password="<Your_ADB_Password123!>"
- Die autonome KI-Datenbank (serverlos) verwendet ein mTLS-Wallet. Laden Sie die ZIP-Datei des Instanz-Wallets über die OCI-Konsole herunter, und extrahieren Sie sie in den lokalen Ordner. Beispiel:
- Für GKE bereitstellen
- Erstellen Sie eine Datei mit dem Namen
deployment.yaml. Der WertDB_URLverwendet den Oracle TNS-Alias in der Datei tnsnames.ora und verweist auf das Wallet-Verzeichnis (/app/wallet), das von Kubernetes gemountet wird.- Aktualisieren Sie
<your-gcp-project-id>im nachstehenden Attributimage:, damit es mit Ihrer tatsächlichen Projekt-ID übereinstimmt. - Ersetzen Sie
my_adb_highdurch den tatsächlichen TNS-Namen.
apiVersion: apps/v1 kind: Deployment metadata: name: product-api spec: replicas: 2 selector: matchLabels: app: product-api template: metadata: labels: app: product-api spec: containers: - name: api # UPDATE THIS image string with your actual project ID image: us-central1-docker.pkg.dev/<your-gcp-project-id>/mycompanyrepo123/product-api:v1 imagePullPolicy: Always # Forces K8s to download the newest image from GAR ports: - containerPort: 8080 # THIS IS WHERE THE ENVIRONMENT VARIABLES ARE SET FOR JAVA env: - name: DB_URL # The '?TNS_ADMIN=/app/wallet' parameter tells the JDBC driver where to look for the wallet. value: "jdbc:oracle:thin:@my_adb_high?TNS_ADMIN=/app/wallet" - name: DB_USER valueFrom: secretKeyRef: name: db-credentials key: username - name: DB_PASS valueFrom: secretKeyRef: name: db-credentials key: password volumeMounts: - name: wallet-volume mountPath: /app/wallet readOnly: true volumes: - name: wallet-volume secret: secretName: adb-wallet --- apiVersion: v1 kind: Service metadata: name: api-service spec: type: LoadBalancer ports: - port: 80 targetPort: 8080 selector: app: product-api - Aktualisieren Sie
- Erstellen Sie eine Datei mit dem Namen
- Anwendung bereitstellenSobald die
EXTERNAL-IPangezeigt wird, ist Ihre API über das Internet vollständig zugänglich.kubectl apply -f deployment.yaml # Monitor the deployment until an EXTERNAL-IP is assigned kubectl get services --watch
- Google Kubernetes Engine und Container Registry bereitstellen
- Mit der API interagieren
Nachdem die API von der Benutzeroberfläche getrennt und in GKE bereitgestellt wurde, können Sie mit jedem REST-Client oder einem entkoppelten Frontend damit interagieren.
- Auf Ihre laufende Anwendung zugreifen
Sobald die
EXTERNAL-IPangezeigt wird, z.B.20.124.x.x, ist die API über das Internet zugänglich.Auch wenn Ihre Java-Anwendung in der Dockerfile
EXPOSE 8080lautet, ordnet der Kubernetes-Service (api-service, wie oben definiert) den Standard-Webport 80 dem Containerport 8080 zu. Daher müssen Sie keinen Port in Ihrer URL angeben.Format für Zugriffs-URL:
http://<EXTERNAL-IP>/api/products- Testen Sie es von Ihrem Terminal aus:
curl http://<EXTERNAL-IP>/api/products
- Testen Sie es von Ihrem Terminal aus:
- OpenAPI-Spezifikationen aktivieren (optional)
Sie können
openai.yamlin Postman oder einem anderen REST-Client verwenden, um mit der grafischen Benutzeroberfläche zu interagieren.- Speichern Sie den folgenden Inhalt als
openai.yaml. Importieren Sie die Datei in Postman, und ersetzen Sie<your-GKE-api-external-ip>durch die IP-Adresse aus dem vorherigen Schritt. Die KI verwendet dieses Schema, um automatisch gültige JSON-Payloads zu generieren und aktuelle Oracle-Daten abzurufen.openapi: 3.0.0 info: title: Oracle ADB-S Product API version: 1.0.0 description: Full CRUD API to perform operations on the Product table. servers: - url: http://<your-gke-api-external-ip> paths: /api/products: get: summary: Read all products operationId: getProducts responses: '200': description: A JSON array of products content: application/json: schema: type: array items: $ref: '#/components/schemas/Product' post: summary: Create a new product operationId: createProduct requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Product' responses: '200': description: Product created successfully put: summary: Update an existing product operationId: updateProduct requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Product' responses: '200': description: Product updated successfully delete: summary: Delete a product operationId: deleteProduct requestBody: required: true content: application/json: schema: type: object properties: id: type: integer responses: '200': description: Product deleted successfully components: schemas: Product: type: object properties: id: type: integer name: type: string price: type: number
- Speichern Sie den folgenden Inhalt als
- Auf Ihre laufende Anwendung zugreifen
- Bereinigen
Löschen Sie nach dem Testen die Cloud-Ressourcen, um die Google Cloud-Compute-Kosten zu vermeiden.
- Führen Sie den folgenden Befehl zum Bereinigen der Ressource aus.
# 1. Delete the GKE Cluster gcloud container clusters delete $CLUSTER_NAME --region=$REGION --quiet # 2. Delete the Artifact Registry repository gcloud artifacts repositories delete $REPO_NAME --location=$REGION --quiet # Optional: Remove the local kubectl context kubectl config delete-context gke_${PROJECT_ID}_${REGION}_${CLUSTER_NAME}
- Führen Sie den folgenden Befehl zum Bereinigen der Ressource aus.