部署自管式 Oracle 数据库

本指南概述了如何在 Google Distributed Cloud (GDC) 网闸隔离配置标准集群上部署自行管理的 Oracle Database Enterprise 实例。通过此部署,您可以在经过网闸隔离的环境中运行 Oracle 工作负载,并利用 GDC 现有的存储和网络功能。

它使用官方 Oracle Database Operator for Kubernetes,可自动执行数据库的生命周期管理。

架构

该架构描述了由 GDC 标准集群中的 Oracle Database Operator 管理的单实例 Oracle 数据库部署。虽然本指南演示了如何部署单个数据库实例,但您可以根据集群容量(RAM、CPU、磁盘可用空间)允许的范围部署任意数量的实例。

单实例 Oracle 数据库部署架构图。

该架构包含以下关键组件:

  • GDC 项目:资源的项目容器。
  • 标准 Kubernetes 集群:提供计算资源的标准集群。
  • Oracle Database Operator:一种 Kubernetes operator,可自动执行 Oracle 数据库的预配、生命周期管理和可观测性。它简化了修补、备份和恢复等复杂任务,使您能够更轻松地在容器化环境中运行有状态 Oracle 工作负载。
  • 数据库实例:具有永久性存储空间的容器化 Oracle 单实例数据库 (SIDB)。
  • Harbor:用于在经过网闸隔离的环境中托管数据库、Operator 和客户端映像的私有 Container Registry。
  • Cert-manager:Operator 依赖 cert-manager 来管理网络钩子证书。cert-manager 已预安装在 GDC 标准集群上。

在本指南中,您将在其自己的命名空间 (oracle-database-operator-system) 中部署 Operator,并在单独的命名空间 (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 Instance Admin 角色,以便您可以将映像上传到 Harbor 实例:

    gdcloud projects add-iam-policy-binding ${PROJECT_ID} \
      --member="user:${USER_NAME}" \
      --role=harbor-instance-admin
    
  • 在 Harbor 项目中创建 Harbor 机器人账号。在本指南的后面部分,机器人账号的凭据将存储在 Kubernetes Secret 中,以便集群在实例化容器时从 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"
    

    网络说明:本指南假定它是从堡垒节点运行的,该节点可以访问 GDC API,也可以访问互联网以下载 Oracle Operator 的清单和容器映像。如果您在无法访问互联网的机器上运行此命令,则必须单独获取这些资源(例如,使用 docker save 从已连接的机器导出映像,并使用 docker load 导入映像),并将其安全地上传到您的环境,然后再继续。

  • 在 container-registry.oracle.com 上创建一个账号并获取 API 令牌,然后接受 Oracle Database Enterprise Edition 和 Oracle Instant Client 映像的许可协议,然后再继续。

将映像加载到 Harbor

由于 Google Distributed Cloud 网闸隔离配置中的集群无法访问外部注册表,因此您必须将所需映像镜像到您的私有 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 项目。您将镜像 Operator、企业数据库和 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 数据库企业版映像:

    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"
    

创建 Secret

创建一个 Kubernetes Secret,以允许集群使用保存在本地 ./docker-harbor/config.json 中的凭据从 Harbor 拉取映像。您需要在 Operator 的命名空间(用于拉取 Operator 映像)和数据库的命名空间(用于拉取数据库映像)中都提供此 Secret。

  1. 为 Operator 创建命名空间:

    kk create ns ${ORACLE_OPERATOR_NAMESPACE}
    
  2. 为 Operator 创建拉取 Secret:

    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. 为数据库创建拉取 Secret:

    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. 集群角色绑定:为 Operator 设置在整个集群范围内运行所需的权限。

    kk apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/refs/tags/v${ORACLE_OPERATOR_VERSION}/rbac/cluster-role-binding.yaml
    
  2. 节点 RBAC:授予读取节点拓扑的权限,这对于正确调度 Pod 至关重要。

    kk apply -f https://raw.githubusercontent.com/oracle/oracle-database-operator/refs/tags/v${ORACLE_OPERATOR_VERSION}/rbac/node-rbac.yaml
    
  3. Operator 部署:部署 Operator Pod 和自定义资源定义 (CRD)。此命令会下载官方清单,将映像路径替换为您的 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 -
    

    等待 Operator Pod 运行:

    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
    

部署新的数据库实例

在 Operator 运行后,您现在可以部署单实例 Oracle 数据库。本指南将创建一个适合开发或测试的基本企业版实例。

  1. 创建 Kubernetes Secret 以存储数据库管理密码:

    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:使用 standard-rwo StorageClass 请求 50Gi 永久性卷,这会在 GDC 中创建可用区级永久性磁盘。
    • replicas:将 Pod 数量设置为 1。虽然 1 通常适用于单个实例,但您可以针对特定用例(例如滚动更新,即在旧 Pod 终止之前创建新 Pod)或在使用支持并发访问的共享存储空间后端时增加此值。对于基本的单实例部署,1 是标准配置。

    如需查看配置选项(包括自定义初始化参数和资源限制)的完整列表,请参阅官方文档。

    数据库创建过程会占用大量资源,可能需要 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):用于外部访问。默认情况下,这会在每个节点的高端口上公开数据库。您可以通过在 SingleInstanceDatabase 规范中设置 loadBalancer: true,将其升级为负载均衡器服务。

如需详细了解如何自定义这些服务(例如定义特定的 NodePort),请参阅 GitHub 文档。

根据您的需求,选择以下方法之一来访问数据库。如需详细了解 GDC 服务类型,请参阅公开服务。

集群内访问 (ClusterIP)

如需从在同一 Kubernetes 集群中运行的其他 Pod 访问数据库,请使用 ClusterIP 服务。

  1. 如需安全地验证这一点,请直接从临时客户端 Pod 进行连接。
  2. 检查命名空间中的可用服务。请注意名为 ${DB_NAME} 的 ClusterIP 服务(例如 my-db)。此名称用作内部连接的主机名。
  3. 部署包含 SQL*Plus 客户端的临时 Pod。您可以使用镜像到 Harbor 注册表的 instantclient 映像:

    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 之外的客户端(例如,来自企业网络或外部客户端)公开数据库,您可以使用外部负载均衡器。这会分配一个可从隔离的 VPC 边界外部访问的 IP 地址。如需了解详情,请参阅 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}"
    

后续步骤