Amazon Elastic Kubernetes-Service (Amazon EKS)
In diesem Thema wird eine Legacy-Standalone-Java-Anwendung zu einem containerisierten Microservice modernisiert, der auf Amazon Elastic Kubernetes Service (Amazon EKS) ausgeführt wird und über ein mTLS-Wallet eine Verbindung zu Oracle Autonomous AI Database auf dedizierter Infrastruktur 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 (Dedicated) 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.
- AWS-CLI und eksctl: Installieren Sie die AWS-CLI für grundlegende AWS-Interaktionen, und eksctl, um das Cluster-Provisioning zu vereinfachen.
- Kubernetes-CLI (kubectl): So interagieren Sie mit dem Amazon EKS-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); // Root Health Check Context server.createContext("/", new RootHandler()); // API Context 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); } // A simple handler to verify the LoadBalancer is successfully routing traffic to the Pod static class RootHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { System.out.println("[LOG] Health check ping received at /"); String response = "Product API is up and running! Access /api/products for data."; exchange.sendResponseHeaders(200, response.length()); OutputStream os = exchange.getResponseBody(); os.write(response.getBytes()); os.close(); } } static class ProductApiHandler implements HttpHandler { @Override public void handle(HttpExchange exchange) throws IOException { String method = exchange.getRequestMethod(); System.out.println("[LOG] Incoming " + method + " request to /api/products"); // 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(method)) { exchange.sendResponseHeaders(204, -1); return; } exchange.getResponseHeaders().add("Content-Type", "application/json"); StringBuilder jsonResponse = new StringBuilder(); System.out.println("[LOG] Opening Oracle Database Connection..."); try (Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS)) { System.out.println("[LOG] Database Connection Successful."); 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(); System.out.println("[LOG] Request completed successfully."); } catch (SQLException e) { System.err.println("[ERROR] Database failure: " + e.getMessage()); 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 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 – Amazon Elastic Kubernetes Service (Amazon EKS)Öffnen Sie PowerShell, die Eingabeaufforderung oder Zsh, und melden Sie sich bei AWS an:
aws sts get-caller-identity- Amazon Elastic Kubernetes-Service und Container Registry bereitstellen
- Definieren Sie die Variablen, und erstellen Sie dann das Amazon Elastic Container Registry-(ECR-)Repository und das Amazon EKS-Cluster.
AWS_ACCOUNT_ID="123456789012" # REPLACE with your actual AWS Account ID AWS_REGION="us-east-1" REPO_NAME="oracle-microservices/product-api" CLUSTER_NAME="oracle-eks-cluster" # 1. Create Amazon ECR for Docker images aws ecr create-repository \ --repository-name $REPO_NAME \ --region $AWS_REGION # 2. Create EKS Cluster (using eksctl for a streamlined 1-node setup) # Note: eksctl automatically provisions VPCs, Subnets, and IAM roles for you. eksctl create cluster \ --name $CLUSTER_NAME \ --region $AWS_REGION \ --nodegroup-name standard-workers \ --node-type t3.medium \ --nodes 1 # 3. Get kubectl credentials to connect to your new cluster aws eks update-kubeconfig --region $AWS_REGION --name $CLUSTER_NAME
- Definieren Sie die Variablen, und erstellen Sie dann das Amazon Elastic Container Registry-(ECR-)Repository und das Amazon EKS-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 Amazon ECR aws ecr get-login-password --region $AWS_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com # 2. Define your full image path IMAGE_PATH="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_[REGION.amazonaws.com/$REPO_NAME:v1](https://REGION.amazonaws.com/$REPO_NAME:v1)" # 3. Build the image locally (Enforce AMD64 architecture for cloud EC2 compatibility) docker build --platform linux/amd64 -t $IMAGE_PATH . # 4. Push the image to AWS ECR 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
- Oracle Autonomous AI Database on Dedicated Infrastructure verwendet ein mTLS-Wallet, das der autonomen KI-Datenbank ähnelt. Sie entsprechen jedoch einer bestimmten dedizierten Datenbankinstanz. Laden Sie die ZIP-Datei des Instanz-Wallets herunter, und extrahieren Sie sie lokal.
# 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!"
- Oracle Autonomous AI Database on Dedicated Infrastructure verwendet ein mTLS-Wallet, das der autonomen KI-Datenbank ähnelt. Sie entsprechen jedoch einer bestimmten dedizierten Datenbankinstanz. Laden Sie die ZIP-Datei des Instanz-Wallets herunter, und extrahieren Sie sie lokal.
- Für Amazon EKS 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-aws-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 AWS Account ID and Region image: <your-aws-account-id>[.dkr.ecr.us-east-1.amazonaws.com/oracle-microservices/product-api:v1](https://.dkr.ecr.us-east-1.amazonaws.com/oracle-microservices/product-api:v1) imagePullPolicy: Always # Forces K8s to download the newest image from ECR ports: - containerPort: 8080 # THIS IS WHERE THE ENVIRONMENT VARIABLES ARE SET FOR JAVA env: - name: DB_URL # Replace 'my_adbd_service' with the exact alias found in your Dedicated ADB tnsnames.ora file value: "jdbc:oracle:thin:@my_adbd_service?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
- Amazon Elastic Kubernetes-Service und Container Registry bereitstellen
- Mit der API interagieren
Nachdem die API von der UI getrennt und auf Amazon EKS 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-EKS-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-D Product API version: 1.0.0 description: Full CRUD API to perform operations on the Product table in ADB Dedicated. servers: - url: http://<your-elb-dns-name> 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 Abschluss des Tests die Cloud-Ressourcen, um AWS-Compute-Kosten zu vermeiden.
- Führen Sie den folgenden Befehl zum Bereinigen der Ressource aus.
# 1. Delete the EKS Cluster (eksctl cleanly destroys the CloudFormation stack) eksctl delete cluster --name $CLUSTER_NAME --region $AWS_REGION # 2. Delete the ECR repository (Force deletes the images inside it) aws ecr delete-repository --repository-name $REPO_NAME --region $AWS_REGION --force # Optional: Remove the local kubectl context kubectl config delete-context $(kubectl config current-context)
- Führen Sie den folgenden Befehl zum Bereinigen der Ressource aus.