פריסת מסדי נתונים של 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 project: מאגר הפרויקט של המשאבים.
  • אשכול Kubernetes רגיל: אשכול רגיל שמספק את משאבי המחשוב.
  • ‫Oracle Database Operator: אופרטור של Kubernetes שמבצע אוטומציה של הקצאת משאבים, ניהול מחזור חיים ויכולת צפייה של מסדי נתונים של Oracle. הוא מפשט משימות מורכבות כמו תיקון, גיבוי ושחזור, ומקל על הפעלת עומסי עבודה של 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 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: בקשה לנפח אחסון מתמיד (persistent volume) בנפח 50Gi באמצעות standard-rwo StorageClass, שיוצר דיסק אחסון מתמיד אזורי ב-GDC.
    • ‫replicas: מגדיר את מספר הפודים ל-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. כדי לבצע אימות מאובטח, צריך להתחבר ישירות ממאגר לקוחות זמני.
  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}"
    

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