Implementa bases de datos de Oracle autoadministradas

En esta guía, se describe la implementación de una instancia de Oracle Database Enterprise autoadministrada en un clúster estándar aislado de Google Distributed Cloud (GDC). Esta implementación te permite ejecutar cargas de trabajo de Oracle dentro del entorno aislado, aprovechando las capacidades de almacenamiento y redes existentes de GDC.

Usa el operador oficial de Oracle Database para Kubernetes, que automatiza la administración del ciclo de vida de la base de datos.

Arquitectura

La arquitectura describe una implementación de base de datos de Oracle de una sola instancia administrada por el operador de Oracle Database dentro de un clúster estándar de GDC. Si bien en esta guía se muestra la implementación de una sola instancia de base de datos, puedes implementar tantas instancias como lo permita la capacidad de tu clúster (RAM, CPU, espacio en disco).

Diagrama de arquitectura de implementación de la base de datos de Oracle de una sola instancia.

La arquitectura comprende los siguientes componentes clave:

  • Proyecto de GDC: Es el contenedor del proyecto para tus recursos.
  • Clúster estándar de Kubernetes: Es un clúster estándar que proporciona los recursos de procesamiento.
  • Operador de Oracle Database: Es un operador de Kubernetes que automatiza el aprovisionamiento, la administración del ciclo de vida y la observabilidad de las bases de datos de Oracle. Simplifica tareas complejas, como la aplicación de parches, la copia de seguridad y la recuperación, lo que facilita la ejecución de cargas de trabajo de Oracle con estado en un entorno en contenedores.
  • Instancia de base de datos: Es la base de datos de instancia única de Oracle en contenedores (SIDB) con almacenamiento persistente.
  • Harbor: Es el registro de contenedores privado que se usa para alojar la base de datos, el operador y las imágenes del cliente dentro del entorno aislado.
  • Cert-manager: El operador depende de cert-manager para administrar los certificados de webhook. cert-manager viene preinstalado en los clústeres estándar de GDC.

En esta guía, implementarás el operador en su propio espacio de nombres (oracle-database-operator-system) y la instancia de base de datos en un espacio de nombres independiente (oracle-db). Estos espacios de nombres se ilustran con cuadros de borde discontinuo en el diagrama de arquitectura.

Se recomienda esta separación para mayor claridad y capacidad de administración. Sin embargo, depende de ti decidir cómo organizar tus bases de datos. Por ejemplo, puedes agrupar ciertas bases de datos en diferentes espacios de nombres para administrar el control de acceso detallado (RBAC) según las necesidades de la carga de trabajo, la propiedad del equipo o las especificaciones de seguridad.

Antes de comenzar

Antes de iniciar la implementación, debes asegurarte de que tu entorno cumpla con los siguientes requisitos:

  • Crea un proyecto que servirá como contenedor para todos los recursos generados en esta guía.
  • Otorga a tu usuario los roles de administrador de clústeres y administrador de clústeres estándar para tu proyecto. Esto te permitirá crear un clúster estándar de Kubernetes y administrar sus recursos:

    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
    
  • Crea una instancia de Harbor y un proyecto de Harbor para alojar las imágenes de contenedor necesarias para esta guía.

  • Otorga a tu usuario el rol de administrador de instancias de Harbor para que puedas subir imágenes a tu instancia de Harbor:

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=harbor-instance-admin
    
  • Crea una cuenta de robot de Harbor en tu proyecto de Harbor. Más adelante en esta guía, las credenciales de la cuenta de robot se almacenarán en secretos de Kubernetes, lo que permitirá que el clúster extraiga imágenes de Harbor cuando se creen instancias de contenedores.

  • Crea un clúster estándar de Kubernetes con dos nodos de trabajador, cada uno con un mínimo de 16 GB de memoria. Por ejemplo:

    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
    
  • Configura tus variables de entorno. Se usarán en toda la guía para crear recursos y hacer referencia a ellos:

    # 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"
    

    Nota de red: En esta guía, se supone que se ejecuta desde un nodo bastión que tiene acceso a las APIs de GDC y también a Internet para descargar los manifiestos y las imágenes de contenedor del operador de Oracle. Si ejecutas esta operación desde una máquina sin acceso a Internet, debes obtener estos recursos por separado (por ejemplo, con docker save para exportar imágenes desde una máquina conectada y docker load para importarlas) y subirlas de forma segura a tu entorno antes de continuar.

  • Crea una cuenta y obtén un token de API en container-registry.oracle.com, y, luego, acepta el contrato de licencia para las imágenes de Oracle Database Enterprise Edition y Oracle Instant Client antes de continuar.

Carga imágenes en Harbor

Como los clústeres dentro de Google Distributed Cloud aislados no pueden acceder a registros externos, debes duplicar las imágenes necesarias en tu instancia privada de Harbor.

Accede a Oracle Container Registry

Primero, debes autenticarte con el registro oficial de Oracle para extraer las imágenes base:

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

Después de acceder correctamente, las credenciales se guardarán en ./docker-oracle/config.json.

Carga imágenes en Harbor

Autentícate con tu instancia privada de Harbor:

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

Después de acceder correctamente, las credenciales de la cuenta de robot se guardarán en ./docker-harbor/config.json.

Extrae, etiqueta y envía imágenes

Descarga las imágenes del registro oficial de Oracle Container Registry y envíalas a tu proyecto interno de Harbor. Duplicarás el operador, la base de datos empresarial y el cliente instantáneo para realizar pruebas.

  1. Duplica la imagen del operador de Oracle Database:

    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. Duplica la imagen de 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. Duplica la imagen de 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
    

Configura el acceso al clúster

Antes de implementar recursos, recupera las credenciales de tu clúster estándar y crea un alias conveniente:

  1. Recupera el kubeconfig para tu clúster estándar:

    KUBECONFIG=kubeconfig-${CLUSTER_NAME}.yaml gdcloud clusters \
      get-credentials ${CLUSTER_NAME} \
      --standard \
      --project ${PROJECT_ID} \
      --zone ${ZONE}
    
  2. Crea el alias kk para simplificar los comandos posteriores:

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

Crea Secrets

Crea un secreto de Kubernetes para permitir que el clúster extraiga imágenes de Harbor con las credenciales guardadas en tu archivo ./docker-harbor/config.json local. Necesitas este secreto en el espacio de nombres del operador (para extraer la imagen del operador) y en el espacio de nombres de la base de datos (para extraer la imagen de la base de datos).

  1. Crea el espacio de nombres para el operador:

    kk create ns ${ORACLE_OPERATOR_NAMESPACE}
    
  2. Crea el secreto de extracción para el operador:

    kk create secret docker-registry ${HARBOR_PULL_SECRET_NAME} \
      --from-file=.dockerconfigjson=./docker-harbor/config.json \
      -n ${ORACLE_OPERATOR_NAMESPACE}
    
  3. Crea el espacio de nombres para la base de datos:

    kk create ns ${DB_NAMESPACE}
    
  4. Crea el secreto de extracción para la base de datos:

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

Instala el operador de Oracle Database

Ahora instalarás el operador de Oracle Database en tu clúster aplicando tres manifiestos:

  1. Vinculación de rol de clúster: Configura los permisos necesarios para que el operador funcione en todo el clúster.

    kk apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/refs/tags/v${ORACLE_OPERATOR_VERSION}/rbac/cluster-role-binding.yaml
    
  2. RBAC de nodos: Otorga permisos para leer la topología de nodos, lo que es fundamental para la programación correcta de pods.

    kk apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/refs/tags/v${ORACLE_OPERATOR_VERSION}/rbac/node-rbac.yaml
    
  3. Implementación del operador: Implementa los pods del operador y las definiciones de recursos personalizados (CRDs). Este comando descarga el manifiesto oficial, reemplaza la ruta de acceso de la imagen por la URL de Harbor, inserta la configuración imagePullSecrets para que Kubernetes pueda autenticarse con Harbor y aplica el resultado:

    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 -
    

    Espera a que se ejecuten los pods del operador:

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

    El resultado debe verse de la siguiente manera:

    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
    

Implementa una nueva instancia de base de datos

Con el operador en ejecución, ahora puedes implementar una base de datos de Oracle de una sola instancia. En esta guía, se crea una instancia básica de Enterprise Edition adecuada para el desarrollo o las pruebas.

  1. Crea un secreto de Kubernetes para almacenar la contraseña administrativa de la base de datos:

    kk create secret generic oracle-db-password \
      --from-literal=password=${ADMIN_PASSWORD} \
      -n ${DB_NAMESPACE}
    
  2. Aplica el manifiesto SingleInstanceDatabase para crear la base de datos:

    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
    

    Parámetros de configuración clave:

    • sid / pdbName: Define el identificador del sistema (SID) y el nombre de la base de datos conectable (PDB).
    • edition: Especifica la edición de la base de datos (enterprise en este caso).
    • image: Apunta a la imagen de registro de Harbor privada.
    • persistence: Solicita un volumen persistente de 50 Gi con la StorageClass standard-rwo, que crea un disco persistente zonal en GDC.
    • replicas: Establece la cantidad de pods en 1. Si bien 1 es típico para una sola instancia, puedes aumentar este valor para casos de uso específicos, como actualizaciones continuas (en las que se crea un pod nuevo antes de que finalice el anterior) o si usas un backend de almacenamiento compartido que admite acceso simultáneo. Para implementaciones básicas de una sola instancia, 1 es el estándar.

    Para obtener una lista completa de las opciones de configuración, incluidos los parámetros de inicialización personalizados y los límites de recursos, consulta la documentación oficial.

    La creación de la base de datos requiere muchos recursos y puede tardar entre 10 y 20 minutos.

  3. Espera hasta que el pod de la base de datos esté Running:

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

    El resultado debe verse de la siguiente manera:

    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. Luego, observa los registros y espera el mensaje DATABASE IS READY TO USE!:

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

    El resultado debería contener lo siguiente:

    #########################
    DATABASE IS READY TO USE!
    #########################
    
  5. Verifica que el estado sea Healthy:

    kk get singleinstancedatabase -n ${DB_NAMESPACE}
    

    El resultado debe verse de la siguiente manera:

    NAME    EDITION      STATUS    ROLE
    my-db   Enterprise   Healthy   PRIMARY
    

Accede a la base de datos y exponla

De forma predeterminada, el operador crea dos servicios para la base de datos:

  1. ${DB_NAME} (ClusterIP): Para el tráfico interno dentro del clúster. Usa este nombre de DNS estable para las aplicaciones que se ejecutan dentro del mismo clúster.
  2. ${DB_NAME}-ext (NodePort): Para el acceso externo. De forma predeterminada, esto expone la base de datos en un puerto alto en cada nodo. Puedes actualizarla a un servicio de balanceador de cargas si configuras loadBalancer: true en la especificación SingleInstanceDatabase.

Para obtener más información sobre cómo personalizar estos servicios, como definir NodePorts específicos, consulta la documentación de GitHub .

Elige uno de los siguientes métodos para acceder a tu base de datos según tus necesidades. Para obtener más detalles sobre los tipos de servicio de GDC, consulta Exponer servicios.

Acceso dentro del clúster (ClusterIP)

Para acceder a la base de datos desde otros pods que se ejecutan dentro del mismo clúster de Kubernetes, usa el servicio ClusterIP.

  1. Para verificar esto de forma segura, conéctate directamente desde un pod de cliente temporal.
  2. Verifica los servicios disponibles en tu espacio de nombres. Ten en cuenta el servicio ClusterIP llamado ${DB_NAME} (por ejemplo, my-db). Este nombre sirve como nombre de host para las conexiones internas.
  3. Implementa un pod temporal que contenga el cliente SQL*Plus. Usas la imagen instantclient duplicada en tu registro de 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
    

    Deberías ver el mensaje de SQL que indica una conexión exitosa.

  4. Crea una tabla de muestra para verificar el acceso de escritura:

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

    El resultado debe verse de la siguiente manera:

            ID NAME
    ---------- --------------------------------------------------
            1 John Doe
    
  5. Sal de la sesión:

    exit
    

Acceso dentro de la VPC (balanceador de cargas interno)

Para exponer la base de datos a otros recursos (como VMs) ubicados dentro del mismo proyecto o VPC de GDC, pero fuera del clúster de Kubernetes, usa un balanceador de cargas interno. Esto mantiene el tráfico privado dentro de tu entorno de red aislado. Consulta la documentación del balanceador de cargas interno de GDC para obtener más detalles.

Como el operador no admite automáticamente agregar anotaciones al servicio generado, debes crear un recurso de servicio independiente. Ten en cuenta la anotación networking.gke.io/load-balancer-type: internal, que es necesaria para aprovisionar un balanceador de cargas interno.

  1. Crea el servicio de balanceador de cargas interno:

    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. Recupera la dirección IP interna:

    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}"
    

Acceso desde fuera de la VPC (balanceador de cargas externo)

Para exponer la base de datos a clientes completamente fuera del entorno o la VPC de GDC (por ejemplo, desde una red corporativa o un cliente externo), puedes usar un balanceador de cargas externo. Esto asigna una dirección IP a la que se puede acceder desde fuera del límite de la VPC aislada. Consulta la documentación del balanceador de cargas externo de GDC para obtener más detalles.

Para crear un balanceador de cargas externo, actualiza la especificación SingleInstanceDatabase para configurar loadBalancer: true. Esto cambia el tipo de servicio ${DB_NAME}-ext existente de NodePort a LoadBalancer.

  1. Actualiza la especificación:

    kk patch sidb ${DB_NAME} -n ${DB_NAMESPACE} --type='merge' \
      -p '{"spec":{"loadBalancer":true}}'
    
  2. Recupera la dirección IP externa:

    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}"
    

¿Qué sigue?