从 Google Kubernetes Engine 建立连接

本页面介绍如何设置从 Google Kubernetes Engine 中运行的应用到 Cloud SQL 实例的连接。

简介

如需从 Google Kubernetes Engine 中运行的应用访问 Cloud SQL 实例,您可以使用 Cloud SQL 代理(具有公共或专用 IP 地址)进行连接,也可以直接使用专用 IP 地址进行连接。

即使是使用专用 IP 地址,也建议使用 Cloud SQL 代理连接到 Cloud SQL。这是因为该代理使用 IAM 来提供强加密和身份验证,这有助于确保数据库安全。

数据库连接会消耗服务器和连接应用上的资源。请始终采用最佳连接管理做法,以最大限度减少应用的占用空间,并降低超出 Cloud SQL 连接限制的可能性。 如需了解详情,请参阅管理数据库连接

准备工作

如需连接到 Cloud SQL,您必须满足以下条件:

  • 具有一个 GKE 集群,其中安装有 kubectl 命令行工具且此工具已配置成与该集群通信。

    如需 GKE 入门方面的帮助,请参阅快速入门

    为使用专用 IP 地址进行连接,GKE 集群必须是 VPC 原生集群,并且与 Cloud SQL 实例位于同一 VPC 网络中。

  • 已创建实例。

    如需创建 Cloud SQL 实例的帮助,请参阅创建实例

  • 已在实例上配置 MySQL 用户帐号。

    您的应用将使用此帐号连接到数据库。如需创建用户帐号的帮助,请参阅创建用户

Secret 简介

在 Kubernetes 中,Secret 是一种可以将配置详细信息传递给应用的安全方式。您可以创建一个 Secret,其中包含您的数据库名称、用户和密码等详细信息,您可以将其作为环境变量注入到应用中。

可以通过多种不同的方法来使用 Secret,具体取决于连接类型:

  • 数据库凭据 Secret 包括您在连接时采用的数据库用户身份以及该用户的数据库密码。
  • 如果使用代理进行连接,则可以使用 Secret 来保存服务帐号的凭据文件。
  • 如果使用专用 IP 地址进行连接,则可以使用 Secret 指定 Cloud SQL 实例的专用 IP 地址。

如需查看如何使用 Secret 的完整示例,请参阅本页面后面引用的 GitHub 代码库。

创建 Secret 对象

  1. 您可以使用 kubectl create secret 命令创建 Secret 对象。

    要创建数据库凭据 Secret,请执行以下操作:

    kubectl create secret generic <YOUR-DB-SECRET> \
      --from-literal=username=<YOUR-DATABASE-USER> \
      --from-literal=password=<YOUR-DATABASE-PASSWORD> \
      --from-literal=database=<YOUR-DATABASE-NAME>
    
  2. 创建之后,您可以在 Cloud Console 中 Google Kubernetes Engine 页面的配置部分查看对象。

使用 Cloud SQL 代理连接

当您使用 Cloud SQL 代理进行连接时,系统会使用 sidecar 容器模式将 Cloud SQL 代理添加到 Pod。在该模式下,代理容器与您的应用位于同一 Pod 中,使得应用能够使用 localhost 连接到代理,从而提高安全性和性能。了解详情

如需详细了解 Cloud SQL 代理,请参阅 Cloud SQL 代理简介。如需详细了解如何使用 pod,请参阅 Kubernetes 文档中的 Pod 概览

如需使用 Cloud SQL 代理进行连接,您需要满足以下条件:

  1. 具有 Cloud SQL 实例的实例连接名称。

    实例连接名称可在 Cloud Console 的 Cloud SQL 实例详情页面中找到,或通过 gcloud sql instances describe 命令来获得。

  2. 密钥文件的位置与具有 Cloud SQL 实例的适当权限的服务帐号相关联。

    如需了解详情,请参阅创建服务帐号

  3. 已启用 Cloud SQL Admin API。

    启用 API

为代理提供服务帐号

在 Google Kubernetes Engine 中运行 Cloud SQL 代理的第一步是创建一个服务帐号来代表您的应用。建议您为每个应用创建唯一的服务帐号,而不是在所有地方都使用同一服务帐号。此模式更安全,因为它允许您按应用限制权限。

应用的服务帐号需要满足以下条件:

  • 属于已启用 Cloud SQL Admin API 的项目
  • 已拥有您要连接到的实例所属项目的 Cloud SQL Client IAM 角色(或等效角色)
  • 如果使用专用 IP 地址进行连接,则您必须在 Cloud SQL 实例所属的 VPC 中使用 VPC 原生 GKE 集群

您需要配置 GKE 以向 Cloud SQL 代理提供服务帐号。为此,我们建议使用两种方法:工作负载身份服务帐号密钥文件

Workload Identity

如果您使用的是 Google Kubernetes Engine,则首选方法是使用 GKE 的 Workload Identity 功能。使用此方法,您可以将 Kubernetes 服务帐号 (KSA) 绑定到 Google 服务帐号 (GSA)。然后,GSA 便可供使用匹配的 KSA 的应用进行访问。

Google 服务帐号 (GSA) 是在 Google Cloud 中代表应用的 IAM 身份。类似地,Kubernetes 服务帐号 (KSA) 是在 Google Kubernetes Engine 集群中代表应用的身份。

Workload Identity 会将 KSA 绑定到 GSA,当具有该 KSA 的任何部署与 Google Cloud 进行交互时,将使用该 GSA 进行身份验证。

  1. 为集群启用 Workload Identity
  2. 通常,每个应用都有自己的身份,由一个 KSA 和 GSA 对表示。运行 kubectl apply -f service-account.yaml,为您的应用创建 KSA:

    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: <YOUR-KSA-NAME> # TODO(developer): replace these values
  3. YOUR-GSA-NAMEYOUR-KSA-NAME 之间启用 IAM 绑定:

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:<YOUR-GCP-PROJECT>.svc.id.goog[<YOUR-K8S-NAMESPACE>/<YOUR-KSA-NAME>]" \
      <YOUR-GSA-NAME>@<YOUR-GCP-PROJECT>.iam.gserviceaccount.com
    
  4. YOUR-KSA-NAME 添加注释以完成绑定:

    kubectl annotate serviceaccount \
       <YOUR-KSA-NAME> \
       iam.gke.io/gcp-service-account=<YOUR-GSA-NAME>@<YOUR-GCP-PROJECT>.iam.gserviceaccount.com
    
  5. 最后,请务必为 k8s 对象指定服务帐号。

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: <YOUR-DEPLOYMENT-NAME>
    spec:
      selector:
        matchLabels:
          app: <YOUR-APPLICATION-NAME>
      template:
        metadata:
          labels:
            app: <YOUR-APPLICATION-NAME>
        spec:
          serviceAccountName: <YOUR-KSA-NAME>

服务帐号密钥文件

或者,如果您无法使用 Workload Identity,则推荐的模式是将服务帐号密钥文件装载到 Cloud SQL 代理 Pod 中并使用 -credential_file 标志。

  1. 为您的服务帐号密钥创建凭据文件:

    gcloud iam service-accounts keys create ~/key.json \
      --iam-account <YOUR-SA-NAME>@project-id.iam.gserviceaccount.com
    
  2. 将您的服务帐号密钥转换为 k8s Secret

    kubectl create secret generic <YOUR-SA-SECRET> \
    --from-file=service_account.json=~/key.json
    
  3. 以适用于 k8s 对象的 spec: 将 Secret 装载为卷:

    volumes:
    - name: <YOUR-SA-SECRET-VOLUME>
      secret:
        secretName: <YOUR-SA-SECRET>
    
  4. 请按照下一部分中的说明操作,从代理的 Pod 访问该卷。

将代理作为 Sidecar 运行

我们建议以 sidecar 模式运行代理(作为与您的应用共享 Pod 的其他容器)。与将代理作为单独的服务运行相比,我们建议使用以上方法,原因如下:

  • 防止 SQL 流量在本地公开;代理会对传出连接进行加密,但您需要限制传入连接的公开程度
  • 防止出现单点故障;每个应用对您的数据库的访问都独立于其他应用,从而提高其弹性。
  • 限制对代理的访问权限,可让您按应用使用 IAM 权限,而不是将数据库公开给整个集群
  • 您可以更准确地确定资源请求的范围;因为代理会根据用量以线性方式使用资源,所以此模式可让您更准确地确定资源范围并请求资源以匹配应用扩缩情况
  1. 将 Cloud SQL 代理添加到 containers: 下的 Pod 配置:

    - name: cloud-sql-proxy
      # It is recommended to use the latest version of the Cloud SQL proxy
      # Make sure to update on a regular schedule!
      image: gcr.io/cloudsql-docker/gce-proxy:1.17
      command:
        - "/cloud_sql_proxy"
    
        # If connecting from a VPC-native GKE cluster, you can use the
        # following flag to have the proxy connect over private IP
        # - "-ip_address_types=PRIVATE"
    
        # Replace DB_PORT with the port the proxy should listen on
        # Defaults: MySQL: 3306, Postgres: 5432, SQLServer: 1433
        - "-instances=<INSTANCE_CONNECTION_NAME>=tcp:<DB_PORT>"
      securityContext:
        # The default Cloud SQL proxy image runs as the
        # "nonroot" user and group (uid: 65532) by default.
        runAsNonRoot: true

    如果您使用的是服务帐号密钥,请指定 Secret 卷并在命令中添加 -credential_file 标志:

      # This flag specifies where the service account key can be found
      - "-credential_file=/secrets/service_account.json"
    securityContext:
      # The default Cloud SQL proxy image runs as the
      # "nonroot" user and group (uid: 65532) by default.
      runAsNonRoot: true
    volumeMounts:
    - name: <YOUR-SA-SECRET-VOLUME>
      mountPath: /secrets/
      readOnly: true
  2. 最后,配置应用以使用 127.0.0.1 通过您在命令部分中指定的任何 DB_PORT 进行连接。

完整的示例配置文件:

使用 Workload Identity 的代理

apiVersion: apps/v1
kind: Deployment
metadata:
  name: <YOUR-DEPLOYMENT-NAME>
spec:
  selector:
    matchLabels:
      app: <YOUR-APPLICATION-NAME>
  template:
    metadata:
      labels:
        app: <YOUR-APPLICATION-NAME>
    spec:
      serviceAccountName: <YOUR-KSA-NAME>
      containers:
      - name: <YOUR-APPLICATION-NAME>
        # ... other container configuration
        env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: username
        - name: DB_PASS
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: password
        - name: DB_NAME
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: database
      - name: cloud-sql-proxy
        # It is recommended to use the latest version of the Cloud SQL proxy
        # Make sure to update on a regular schedule!
        image: gcr.io/cloudsql-docker/gce-proxy:1.17
        command:
          - "/cloud_sql_proxy"

          # If connecting from a VPC-native GKE cluster, you can use the
          # following flag to have the proxy connect over private IP
          # - "-ip_address_types=PRIVATE"

          # Replace DB_PORT with the port the proxy should listen on
          # Defaults: MySQL: 3306, Postgres: 5432, SQLServer: 1433
          - "-instances=<INSTANCE_CONNECTION_NAME>=tcp:<DB_PORT>"
        securityContext:
          # The default Cloud SQL proxy image runs as the
          # "nonroot" user and group (uid: 65532) by default.
          runAsNonRoot: true

使用服务帐号密钥的代理

apiVersion: apps/v1
kind: Deployment
metadata:
  name: <YOUR-DEPLOYMENT-NAME>
spec:
  selector:
    matchLabels:
      app: <YOUR-APPLICATION-NAME>
  template:
    metadata:
      labels:
        app: <YOUR-APPLICATION-NAME>
    spec:
      containers:
      - name: <YOUR-APPLICATION-NAME>
        # ... other container configuration
        env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: username
        - name: DB_PASS
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: password
        - name: DB_NAME
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: database
      - name: cloud-sql-proxy
        # It is recommended to use the latest version of the Cloud SQL proxy
        # Make sure to update on a regular schedule!
        image: gcr.io/cloudsql-docker/gce-proxy:1.17
        command:
          - "/cloud_sql_proxy"

          # If connecting from a VPC-native GKE cluster, you can use the
          # following flag to have the proxy connect over private IP
          # - "-ip_address_types=PRIVATE"

          # Replace DB_PORT with the port the proxy should listen on
          # Defaults: MySQL: 3306, Postgres: 5432, SQLServer: 1433
          - "-instances=<INSTANCE_CONNECTION_NAME>=tcp:<DB_PORT>"

          # This flag specifies where the service account key can be found
          - "-credential_file=/secrets/service_account.json"
        securityContext:
          # The default Cloud SQL proxy image runs as the
          # "nonroot" user and group (uid: 65532) by default.
          runAsNonRoot: true
        volumeMounts:
        - name: <YOUR-SA-SECRET-VOLUME>
          mountPath: /secrets/
          readOnly: true
      volumes:
      - name: <YOUR-SA-SECRET-VOLUME>
        secret:
          secretName: <YOUR-SA-SECRET>

在不使用 Cloud SQL 代理的情况下进行连接

虽然安全性更低,但可以在不使用代理的情况下通过专用 IP 地址从 VPC 原生 GKE 集群连接到同一 VPC 上的 Cloud SQL 实例。

  1. 使用实例的专用 IP 地址创建 Secret:

    kubectl create secret generic <YOUR-PRIVATE-IP-SECRET> \
        --from-literal=db_host=<YOUR-PRIVATE-IP-ADDRESS>
    
  2. 接下来,确保在应用的容器中添加该 Secret:

    - name: DB_HOST
      valueFrom:
        secretKeyRef:
          name: <YOUR-PRIVATE-IP-SECRET>
          key: db_host
  3. 最后,配置应用以使用 DB_HOST 环境变量中的 IP 地址进行连接。您需要为 MySQL 使用正确的端口:3306。

完整的示例配置文件:

无代理 - 专用 IP 地址

apiVersion: apps/v1
kind: Deployment
metadata:
  name: <YOUR-DEPLOYMENT-NAME>
spec:
  selector:
    matchLabels:
      app: <YOUR-APPLICATION-NAME>
  template:
    metadata:
      labels:
        app: <YOUR-APPLICATION-NAME>
    spec:
      containers:
      - name: <YOUR-APPLICATION-NAME>
        # ... other container configuration
        env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: username
        - name: DB_PASS
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: password
        - name: DB_NAME
          valueFrom:
            secretKeyRef:
              name: <YOUR-DB-SECRET>
              key: database
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: <YOUR-PRIVATE-IP-SECRET>
              key: db_host

需要帮助?如需获得代理问题排查方面的帮助,请参阅排查 Cloud SQL 代理连接问题。也可参阅我们的 Cloud SQL 支持页面

后续步骤