פריסת מסדי נתונים של Oracle בניהול עצמי

במדריך הזה מוסבר איך לפרוס מכונה של Oracle Database Enterprise בניהול עצמי באשכול רגיל של Google Distributed Cloud (GDC) עם air gap. הפריסה הזו מאפשרת להריץ עומסי עבודה של Oracle בסביבה עם air gap, תוך ניצול יכולות האחסון והרשת הקיימות של GDC.

הוא משתמש ב-Oracle Database Operator for Kubernetes הרשמי, שמנהל באופן אוטומטי את מחזור החיים של מסד הנתונים.

ארכיטקטורה

הארכיטקטורה מתארת פריסה של מסד נתונים של Oracle עם מכונה אחת, שמנוהלת על ידי Oracle Database Operator בתוך אשכול רגיל של GDC. במדריך הזה מוסבר איך לפרוס מכונה אחת של מסד נתונים, אבל אתם יכולים לפרוס כמה מכונות שתרצו, בהתאם לקיבולת של האשכול (זיכרון RAM, מעבד, מקום בכונן).

דיאגרמת ארכיטקטורה של פריסת מסד נתונים של Oracle במכונה אחת.

הארכיטקטורה כוללת את הרכיבים העיקריים הבאים:

  • ‫פרויקט GDC: מאגר הפרויקט של המשאבים שלכם.
  • אשכול Kubernetes רגיל: אשכול רגיל שמספק את משאבי המחשוב.
  • ‫Oracle Database Operator: אופרטור של Kubernetes שמבצע אוטומציה של הקצאת משאבים, ניהול מחזור חיים וניטור של מסדי נתונים של Oracle. הוא מפשט משימות מורכבות כמו תיקון, גיבוי ושחזור, ומקל על הפעלת עומסי עבודה של Oracle עם שמירת מצב בסביבה בקונטיינרים.
  • מכונה של מסד נתונים: מסד נתונים של מכונה יחידה (SIDB) של Oracle בקונטיינר עם אחסון מתמיד.
  • ‫Harbor: ה-Container Registry הפרטי שמשמש לאירוח קובצי האימג' של מסד הנתונים, ה-operator והלקוח בסביבה עם air gap.
  • ‫Cert-manager: המפעיל מסתמך על cert-manager לניהול אישורי ה-webhook. ‫cert-manager מותקן מראש באשכולות רגילים של GDC.

במדריך הזה, אתם פורסים את האופרטור במרחב שמות משלו (oracle-database-operator-system) ואת המכונה של מסד הנתונים במרחב שמות נפרד (oracle-db). מרחבי השמות האלה מוצגים בתיבות עם גבולות מקווקווים בדיאגרמת הארכיטקטורה.

מומלץ להפריד בין הפרויקטים כדי לשמור על סדר ובהירות. עם זאת, אתם יכולים להחליט איך לארגן את מסדי הנתונים שלכם. לדוגמה, אתם יכולים לקבץ מסדי נתונים מסוימים במרחבי שמות שונים כדי לנהל בקרת גישה מפורטת (RBAC) על סמך צורכי עומס העבודה, הבעלות של הצוות או מפרטי האבטחה.

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

לפני שמתחילים את הפריסה, צריך לוודא שהסביבה עומדת בדרישות הבאות:

  • יוצרים פרויקט שיכיל את כל המשאבים שייווצרו במהלך המדריך הזה.
  • מקצים למשתמש את התפקידים "אדמין של אשכול" ו"אדמין של אשכול רגיל" בפרויקט. כך תוכלו ליצור אשכול Kubernetes רגיל ולנהל את המשאבים שלו:

    export PROJECT_ID=PROJECT_ID
    export USER_NAME=USER_NAME
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=cluster-admin
    
    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=standard-cluster-admin
    
  • יוצרים מכונה של Harbor ופרויקט ב-Harbor כדי לארח את קובצי האימג' של הקונטיינרים שדרושים למדריך הזה.

  • כדי להעלות קובצי אימג' למכונה של Harbor, צריך לתת למשתמש את התפקיד Harbor Instance Admin:

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=harbor-instance-admin
    
  • יוצרים חשבון רובוט של Harbor בפרויקט Harbor. בהמשך המדריך, פרטי הכניסה של חשבון הרובוט יאוחסנו בסודות של Kubernetes, כדי לאפשר לאשכול לשלוף קובצי אימג' מ-Harbor בזמן יצירת קונטיינרים.

  • יוצרים אשכול Kubernetes רגיל עם שני צומתי עובדים, שלכל אחד מהם יש זיכרון של 16GB לפחות. לדוגמה:

    kubectl --kubeconfig MGMT_API_KUBECONFIG create -f - <<EOF
    apiVersion: cluster.gdc.goog/v1
    kind: Cluster
    metadata:
      name: ${CLUSTER_NAME}
      namespace: ${PROJECT_ID}
    spec:
      nodePools:
      - machineTypeName: n3-standard-8-gdc
        nodeCount: 2
        name: ${CLUSTER_NAME}-node-pool
    EOF
    
  • מגדירים את משתני הסביבה. המשתנים האלה ישמשו לאורך המדריך ליצירת משאבים ולהפניה אליהם:

    # General info
    export PROJECT_ID="PROJECT_ID"
    export ZONE="ZONE"
    export ORG_NAME="ORG_NAME"
    export CLUSTER_NAME="CLUSTER_NAME"
    
    # Oracle operator settings
    export ORACLE_OPERATOR_VERSION="2.1.0"
    export ORACLE_DB_VERSION="23.26.1.0"
    export ORACLE_OPERATOR_NAMESPACE="ORACLE_DBS_OPERATOR-SYSTEM"
    
    # Harbor config
    export HARBOR_INSTANCE_PROJECT_ID="HARBOR_PROJECT_ID"
    export HARBOR_INSTANCE_NAME="HARBOR_INSTANCE_NAME"
    export HARBOR_INSTANCE_URL="HARBOR_INSTANCE_URL"
    export HARBOR_PROJECT="HARBOR_PROJECT"
    export HARBOR_PULL_SECRET_NAME="HARBOR_PULL_SECRET_NAME"
    export HARBOR_ROBOT_ACCOUNT="robot\$HARBOR_PROJECT+ROBOT_NAME"
    export HARBOR_ROBOT_SECRET="HARBOR_ROBOT_SECRET"
    
    # Oracle database config
    export ADMIN_PASSWORD="ADMIN_PASSWORD"
    export DB_NAMESPACE="DB_NAMESPACE"
    export DB_NAME="DB_NAME"
    

    הערה לגבי הרשת: ההנחה במדריך הזה היא שההפעלה מתבצעת מצומת באסטיון שיש לו גישה לממשקי ה-API של GDC וגם גישה לאינטרנט, כדי להוריד את המניפסטים ואת קובצי האימג' של הקונטיינרים של Oracle Operator. אם אתם מריצים את הפקודה ממכונה ללא גישה לאינטרנט, אתם צריכים להשיג את הנכסים האלה בנפרד (לדוגמה, באמצעות docker save כדי לייצא תמונות ממכונה מחוברת ו-docker load כדי לייבא אותן) ולהעלות אותן באופן מאובטח לסביבה שלכם לפני שתמשיכו.

  • יוצרים חשבון ומקבלים אסימון API בכתובת container-registry.oracle.com, ואז מאשרים את הסכם הרישיון של קובצי האימג' של Oracle Database Enterprise Edition ושל Oracle Instant Client לפני שממשיכים.

טעינת קובצי אימג' ל-Harbor

מכיוון שאשכולות ב-Google Distributed Cloud במודל Air-gapped לא יכולים לגשת למאגרים חיצוניים, צריך לשקף את קובצי האימג' הנדרשים למכונה הפרטית של Harbor.

כניסה ל-Oracle Container Registry

כדי לשלוף את קובצי האימג' הבסיסיים, קודם צריך לבצע אימות מול המרשם הרשמי של Oracle:

docker --config=./docker-oracle login container-registry.oracle.com

אחרי התחברות מוצלחת, פרטי הכניסה יישמרו ב-./docker-oracle/config.json.

טעינת קובצי אימג' ל-Harbor

מאמתים באמצעות המכונה הפרטית של Harbor:

docker --config=./docker-harbor login ${HARBOR_INSTANCE_URL} \
  -u ${HARBOR_ROBOT_ACCOUNT} \
  -p ${HARBOR_ROBOT_SECRET}

אחרי התחברות מוצלחת, פרטי הכניסה של החשבון הרובוטי יישמרו ב-./docker-harbor/config.json.

שליפה, תיוג ודחיפה של קובצי אימג'

מורידים את קובצי האימג' מ-Oracle Container Registry הרשמי ומעבירים אותם בדחיפה לפרויקט Harbor הפנימי. תשכפלו את האופרטור, את מסד הנתונים של הארגון ואת ה-instant client לצורך בדיקה.

  1. משכפלים את קובץ האימג' של Oracle Database Operator:

    docker --config=./docker-oracle pull \
      container-registry.oracle.com/database/operator:${ORACLE_OPERATOR_VERSION} \
      --platform linux/amd64
    docker tag container-registry.oracle.com/database/operator:${ORACLE_OPERATOR_VERSION} \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-operator:${ORACLE_OPERATOR_VERSION}
    docker --config=./docker-harbor push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-operator:${ORACLE_OPERATOR_VERSION}
    
  2. משכפלים את קובץ האימג' של Oracle database Enterprise:

    docker --config=./docker-oracle pull \
      container-registry.oracle.com/database/enterprise:${ORACLE_DB_VERSION} \
      --platform linux/amd64
    docker tag container-registry.oracle.com/database/enterprise:${ORACLE_DB_VERSION} \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-enterprise:${ORACLE_DB_VERSION}
    docker --config=./docker-harbor push \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-enterprise:${ORACLE_DB_VERSION}
    
  3. משכפלים את קובץ האימג' של Oracle Instant Client:

    docker --config=./docker-oracle pull container-registry.oracle.com/database/instantclient:latest \
      --platform linux/amd64
    docker tag container-registry.oracle.com/database/instantclient:latest \
      ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-instantclient:latest
    docker --config=./docker-harbor push ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-instantclient:latest
    

הגדרת הגישה לאשכול

לפני פריסת המשאבים, מאחזרים את פרטי הכניסה לאשכול הרגיל ויוצרים כינוי נוח:

  1. מאחזרים את ה-kubeconfig של האשכול הרגיל:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  2. יוצרים את הכינוי kk כדי לפשט את הפקודות הבאות:

    alias kk="kubectl --kubeconfig kubeconfig-${CLUSTER_NAME}.yaml"
    

יצירת סודות

יוצרים סוד של Kubernetes כדי לאפשר לאשכול לשלוף תמונות מ-Harbor באמצעות פרטי הכניסה שנשמרו בקובץ ./docker-harbor/config.json המקומי. הסוד הזה נדרש גם במרחב השמות של המפעיל (כדי לשלוף את קובץ האימג' של המפעיל) וגם במרחב השמות של מסד הנתונים (כדי לשלוף את קובץ האימג' של מסד הנתונים).

  1. יוצרים את מרחב השמות של האופרטור:

    kk create ns ${ORACLE_OPERATOR_NAMESPACE}
    
  2. יוצרים את הסוד למשיכה של האופרטור:

    kk create secret docker-registry ${HARBOR_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker-harbor/config.json \
      -n ${ORACLE_OPERATOR_NAMESPACE}
    
  3. יוצרים את מרחב השמות של מסד הנתונים:

    kk create ns ${DB_NAMESPACE}
    
  4. יוצרים את סוד המשיכה למסד הנתונים:

    kk create secret docker-registry ${HARBOR_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker-harbor/config.json \
      -n ${DB_NAMESPACE}
    

התקנת Oracle Database Operator

עכשיו מתקינים את Oracle Database Operator באשכול באמצעות החלת שלושה מניפסטים:

  1. Cluster Role Binding: הגדרת ההרשאות הנדרשות לאופרטור כדי לפעול בכל האשכול.

    kk apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/refs/tags/v${ORACLE_OPERATOR_VERSION}/rbac/cluster-role-binding.yaml
    
  2. ‫Node RBAC: נותן הרשאות לקרוא את הטופולוגיה של הצומת, שחיונית לתזמון נכון של pod.

    kk apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/refs/tags/v${ORACLE_OPERATOR_VERSION}/rbac/node-rbac.yaml
    
  3. פריסת האופרטור: פריסת הפודים של האופרטור והגדרות המשאבים בהתאמה אישית (CRD). הפקודה הזו מורידה את המניפסט הרשמי, מחליפה את נתיב התמונה בכתובת ה-URL של Harbor, מוסיפה את ההגדרות של imagePullSecrets כדי ש-Kubernetes יוכל לבצע אימות מול Harbor, ומחילה את התוצאה:

    curl -L https://raw.githubusercontent.com/oracle/oracle-database-operator/refs/tags/v${ORACLE_OPERATOR_VERSION}/oracle-database-operator.yaml \
      | sed "s|container-registry.oracle.com/database/operator:latest|${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-operator:${ORACLE_OPERATOR_VERSION}|g" \
      | awk "/terminationGracePeriodSeconds: 10/{print; print \"      imagePullSecrets:\n      - name: ${HARBOR_PULL_SECRET_NAME}\"; next}1" \
      | kk apply -f -
    

    מחכים שהפודים של המפעיל יפעלו:

    kk get pods -n ${ORACLE_OPERATOR_NAMESPACE} --watch
    

    הפלט אמור להיראות כך:

    NAME                                                           READY   STATUS    RESTARTS   AGE
    oracle-database-operator-controller-manager-5f7b56874d-k9v4z   1/1     Running   0          45s
    oracle-database-operator-controller-manager-5f7b56874d-n2x8m   1/1     Running   0          45s
    oracle-database-operator-controller-manager-5f7b56874d-r6z7q   1/1     Running   0          45s
    

פריסת מכונה חדשה של מסד נתונים

כשהאופרטור פועל, אפשר לפרוס מסד נתונים של Oracle עם מכונה אחת. במדריך הזה ניצור מכונה בסיסית של Enterprise Edition שמתאימה לפיתוח או לבדיקה.

  1. יוצרים סוד ב-Kubernetes כדי לאחסן את סיסמת הניהול של מסד הנתונים:

    kk create secret generic oracle-db-password \
      --from-literal=password=${ADMIN_PASSWORD} \
      -n ${DB_NAMESPACE}
    
  2. מחילים את המניפסט SingleInstanceDatabase כדי ליצור את מסד הנתונים:

    apiVersion: database.oracle.com/v4
    kind: SingleInstanceDatabase
    metadata:
      name: ${DB_NAME}
      namespace: ${DB_NAMESPACE}
    spec:
      sid: ORCLCDB
      pdbName: ORCLPDB1
      edition: enterprise
      replicas: 1
      image:
        pullFrom: ${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-enterprise:${ORACLE_DB_VERSION}
        pullSecrets: ${HARBOR_PULL_SECRET_NAME}
        prebuiltDB: true
      persistence:
        size: 50Gi
        storageClass: standard-rwo
        accessMode: ReadWriteOnce
      adminPassword:
        secretName: oracle-db-password
        secretKey: password
    

    פרמטרים עיקריים להגדרה:

    • sid / pdbName: מגדיר את מזהה המערכת (SID) ואת שם מסד הנתונים הניתן לחיבור (PDB).
    • ‫edition: מציין את מהדורת מסד הנתונים (enterprise במקרה הזה).
    • ‫image: מפנה לתמונת המאגר הפרטי של Harbor.
    • ‫persistence: בקשה לנפח אחסון מתמיד של 50Gi באמצעות StorageClass‏ standard-rwo, שיוצר דיסק לאחסון מתמיד של תחום ב-GDC.
    • ‫replicas: מגדיר את מספר הפודים ל-1. הערך 1 אופייני למכונה אחת, אבל אפשר להגדיל אותו במקרים ספציפיים כמו עדכונים בהדרגה (rolling) (שבהם נוצר Pod חדש לפני שה-Pod הישן מסתיים) או אם משתמשים בקצה עורפי של נפח אחסון משותף שתומך בגישה בו-זמנית. בפריסות בסיסיות של מכונה אחת, 1 הוא הסטנדרט.

    רשימה מלאה של אפשרויות ההגדרה, כולל פרמטרים מותאמים אישית של init ומגבלות על משאבים, מופיעה במסמכי התיעוד הרשמיים.

    יצירת מסד הנתונים דורשת הרבה משאבים ויכולה להימשך בין 10 ל-20 דקות.

  3. מחכים עד שה-pod של מסד הנתונים יהיה במצב Running:

    kk get po -n ${DB_NAMESPACE} -l app=${DB_NAME} -w
    

    הפלט אמור להיראות כך:

    NAME                   READY   STATUS    RESTARTS   AGE
    my-db-i5xdj   0/1     Pending   0          0s
    my-db-i5xdj   0/1     Pending   0          0s
    my-db-i5xdj   0/1     Pending   0          1s
    my-db-i5xdj   0/1     Init:0/1   0          1s
    my-db-i5xdj   0/1     PodInitializing   0          98s
    my-db-i5xdj   0/1     Running           0          99s
    my-db-i5xdj   1/1     Running           0          99s
    
  4. אחר כך עוקבים אחרי היומנים ומחכים להודעה DATABASE IS READY TO USE!:

    kk logs -n ${DB_NAMESPACE} -l app=${DB_NAME} -f
    

    הפלט אמור לכלול את הפרטים הבאים:

    #########################
    DATABASE IS READY TO USE!
    #########################
    
  5. מוודאים שהסטטוס הוא Healthy:

    kk get singleinstancedatabase -n ${DB_NAMESPACE}
    

    הפלט אמור להיראות כך:

    NAME    EDITION      STATUS    ROLE
    my-db   Enterprise   Healthy   PRIMARY
    

גישה למסד הנתונים וחשיפה שלו

כברירת מחדל, האופרטור יוצר שני שירותים למסד הנתונים:

  1. ‫${DB_NAME} (ClusterIP): לתנועה פנימית בתוך האשכול. השתמשו בשם ה-DNS היציב הזה לאפליקציות שפועלות באותו אשכול.
  2. ‫${DB_NAME}-ext (NodePort): לגישה חיצונית. כברירת מחדל, הפעולה הזו חושפת את מסד הנתונים ביציאה גבוהה בכל צומת. אפשר לשדרג אותו לשירות של איזון עומסים על ידי הגדרת loadBalancer: true במפרט של SingleInstanceDatabase.

למידע נוסף על התאמה אישית של השירותים האלה, כמו הגדרת NodePorts ספציפיים, עיינו במסמכי התיעוד של GitHub.

בוחרים באחת מהשיטות הבאות כדי לגשת למסד הנתונים, בהתאם לצרכים שלכם. לפרטים נוספים על סוגי השירותים של GDC, קראו את המאמר חשיפת שירותים.

גישה בתוך האשכול (ClusterIP)

כדי לגשת למסד הנתונים מקבוצות Pod אחרות שפועלות באותו אשכול Kubernetes, משתמשים בשירות ClusterIP.

  1. כדי לאמת את זה בצורה מאובטחת, צריך להתחבר ישירות מ-Pod זמני של לקוח.
  2. בודקים את השירותים הזמינים במרחב השמות. שימו לב לשירות ClusterIP שנקרא ${DB_NAME} (לדוגמה, my-db). השם הזה משמש כשם המארח לחיבורים פנימיים.
  3. פורסים pod זמני שמכיל את הלקוח SQL*Plus. משתמשים בתמונת instantclient שמשתקפת במאגר Harbor:

    kk run sqlplus-client -n ${DB_NAMESPACE} --rm -it --restart=Never \
      --image=${HARBOR_INSTANCE_URL}/${HARBOR_PROJECT}/oracle-instantclient:latest \
      --image-pull-policy=Always \
      --overrides='{"spec": {"imagePullSecrets": [{"name": "'${HARBOR_PULL_SECRET_NAME}'"}]}}' \
      -- sqlplus sys/${ADMIN_PASSWORD}@${DB_NAME}:1521/ORCLPDB1 as sysdba
    

    אמורה להופיע הנחיית SQL שמציינת שהחיבור הצליח.

  4. יוצרים טבלה לדוגמה כדי לאמת את הרשאת הכתיבה:

    CREATE TABLE employees (id NUMBER, name VARCHAR2(50));
    INSERT INTO employees VALUES (1, 'John Doe');
    COMMIT;
    SELECT * FROM employees;
    

    הפלט אמור להיראות כך:

            ID NAME
    ---------- --------------------------------------------------
            1 John Doe
    
  5. כדי לצאת מהסשן:

    exit
    

גישה בתוך ה-VPC (מאזן עומסים פנימי)

כדי לחשוף את מסד הנתונים למשאבים אחרים (כמו מכונות וירטואליות) שנמצאים באותו פרויקט או VPC ב-GDC, אבל מחוץ לאשכול Kubernetes, צריך להשתמש במאזן עומסים פנימי. כך התנועה נשארת פרטית בסביבת הרשת המבודדת שלכם. פרטים נוספים זמינים במסמכי התיעוד של מאזן העומסים הפנימי של GDC.

מכיוון שהאופרטור לא תומך באופן אוטומטי בהוספת הערות לשירות שנוצר, צריך ליצור משאב שירות נפרד. שימו לב להערה networking.gke.io/load-balancer-type: internal, שנדרשת כדי להקצות מאזן עומסים פנימי.

  1. יוצרים את שירות מאזן העומסים הפנימי:

    apiVersion: v1
    kind: Service
    metadata:
      name: ${DB_NAME}-internal
      namespace: ${DB_NAMESPACE}
      annotations:
        networking.gke.io/load-balancer-type: internal
    spec:
      type: LoadBalancer
      selector:
        app: ${DB_NAME}
      ports:
      - name: sqlnet
        port: 1521
        targetPort: 1521
    
  2. מאחזרים את כתובת ה-IP הפנימית:

    export DB_INT_IP=$(kk get svc ${DB_NAME}-internal -n ${DB_NAMESPACE} \
      -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo "Database Internal IP: ${DB_INT_IP}"
    

גישה מחוץ ל-VPC (מאזן עומסים חיצוני)

כדי לחשוף את מסד הנתונים ללקוחות שנמצאים לגמרי מחוץ לסביבת GDC או ל-VPC (לדוגמה, מרשת ארגונית או מלקוח חיצוני), אתם יכולים להשתמש במאזן עומסים חיצוני. כך מוקצית כתובת IP שאפשר להגיע אליה מחוץ לגבולות ה-VPC המבודד. פרטים נוספים זמינים במסמכי התיעוד של מאזן העומסים החיצוני של GDC.

כדי ליצור מאזן עומסים חיצוני, מעדכנים את המפרט של SingleInstanceDatabase ומגדירים את loadBalancer: true. הפעולה הזו משנה את סוג השירות הקיים ${DB_NAME}-ext מ-NodePort ל-LoadBalancer.

  1. מעדכנים את המפרט:

    kk patch sidb ${DB_NAME} -n ${DB_NAMESPACE} --type='merge' \
      -p '{"spec":{"loadBalancer":true}}'
    
  2. מאחזרים את כתובת ה-IP החיצונית:

    export DB_EXT_IP=$(kk get svc ${DB_NAME}-ext -n ${DB_NAMESPACE} \
      -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    echo "Database External IP: ${DB_EXT_IP}"
    

המאמרים הבאים