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

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

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

ארכיטקטורה

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

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

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

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

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

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

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

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

  • יוצרים פרויקט שישמש כמאגר לכל המשאבים שנוצרו במהלך המדריך הזה.
  • נותנים למשתמש את התפקידים Cluster Admin ו-Standard Cluster Admin בפרויקט. כך תוכלו ליצור אשכול 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:

    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"
    

    הערה לגבי רשת: המדריך הזה מבוסס על ההנחה שהוא מופעל מצומת bastion שיש לו גישה לממשקי ה-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. תשכפלו את האופרטור, את מסד הנתונים של הארגון ואת הלקוח המיידי לצורך בדיקה.

  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: מעניק הרשאות לקריאת טופולוגיית הצומת, שחשובה לתזמון נכון של הפודים.

    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 שמתאימה לפיתוח או לבדיקות.

  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: בקשה לנפח אחסון מתמיד (persistent volume) בנפח 50Gi באמצעות standard-rwo StorageClass, שיוצר דיסק מתמיד אזורי ב-GDC.
    • replicas: מגדיר את מספר ה-Pods ל-1. בדרך כלל, הערך של 1 מתאים למופע יחיד, אבל אפשר להגדיל אותו לתרחישי שימוש ספציפיים כמו עדכונים מתגלגלים (שבהם נוצר פוד חדש לפני שהפוד הישן מסתיים) או אם אתם משתמשים בקצה עורפי של אחסון משותף שתומך בגישה בו-זמנית. בפריסות בסיסיות של מופע יחיד, 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.

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

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

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

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

  1. כדי לבצע אימות מאובטח, צריך להתחבר ישירות מ-pod לקוח זמני.
  2. בודקים את השירותים שזמינים במרחב השמות. שימו לב לשם השירות ClusterIP ${DB_NAME} (לדוגמה, my-db). השם הזה משמש כשם המארח לחיבורים פנימיים.
  3. פורסים פוד זמני שמכיל את לקוח 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 (מאזן עומסים פנימי)

כדי לחשוף את מסד הנתונים למשאבים אחרים (כמו מכונות וירטואליות) שנמצאים באותו פרויקט GDC או באותו VPC, אבל מחוץ לאשכול 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}"
    

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