Kubernetes:如何停止多容器Pod/Job中的CloudSQL-proxy sidecar容器?

37

我有一个Kubernetes JOB,用于对CloudSQL数据库进行迁移。
从GKE访问CloudSQL数据库的一种方法是使用CloudSQL代理容器,然后通过localhost连接。很好-到目前为止都可以了。但由于我是在K8s JOB中执行此操作,因此该作业未标记为成功完成,因为代理继续运行。

$ kubectrl get po
NAME                      READY     STATUS      RESTARTS   AGE
db-migrations-c1a547      1/2       Completed   0          1m

即使输出显示“完成”,但最初的两个容器之一仍在运行 - 代理。

我该如何让代理在完成容器1内的迁移后退出?


1
没有评论的踩?加上一条注释会更有帮助,指出哪里需要改进... - Philipp Kyeck
嗨@Phillip,你最近找到解决方案了吗?或者有其他方法来进行迁移吗? - Mark Vincze
@MarkVincze 我采用了Christian的方法,创建了一个独立的代理服务,工作可以连接到该服务 - 这样就不需要使用sidecar了。 - Philipp Kyeck
8个回答

35

我发现最好的方法是在容器之间共享进程命名空间,并使用SYS_PTRACE securityContext功能来允许您终止sidecar。

apiVersion: batch/v1
kind: Job
metadata:
  name: my-db-job
spec:
  template:
    spec:
      restartPolicy: OnFailure
      shareProcessNamespace: true
      containers:
      - name: my-db-job-migrations
        command: ["/bin/sh", "-c"]
        args:
          - |
            <your migration commands>;
            sql_proxy_pid=$(pgrep cloud_sql_proxy) && kill -INT $sql_proxy_pid;
        securityContext:
          capabilities:
            add:
              - SYS_PTRACE
      - name: cloudsql-proxy
        image: gcr.io/cloudsql-docker/gce-proxy:1.17
        command:
          - "/cloud_sql_proxy"
        args:
          - "-instances=$(DB_CONNECTION_NAME)=tcp:5432"
          

3
这个答案应该得到更多的赞同票。这是唯一对我有用的选择! - Nazilla
1
这对我完全没有用。我不知道为什么。我看不到发生了什么,但如果我进入容器并尝试手动运行此命令,则会收到“操作不允许”的错误提示。 - Nathan McKaskle
3
谢谢,这个答案指引了我们正确的方向!请注意,上面答案中的代码如果迁移命令以错误代码退出不会使您的工作失败。如果您希望在迁移失败时使作业失败,可以执行以下操作:<您的迁移命令>; migration_exit_code=$?; sql_proxy_pid=$(pgrep cloud_sql_proxy) && kill -INT $sql_proxy_pid && exit $migration_exit_code - Thijs
只有在以 root 用户身份运行 my-db-job-migrations 容器时才能正常工作。 - Yasha

14
可能的一种解决方案是使用独立的 CloudSQL Proxy 部署,配合相应的服务。然后你只需要在连接到代理服务的作业中运行迁移容器。
这样做有一些缺点:
  • 网络延迟更高,无法进行 Pod 本地的 MySQL 通信
  • 如果将 SQL 端口提供给整个 Kubernetes 集群,可能存在安全问题
如果您想向整个集群开放 CloudSQL Proxy,则需要在 cloudsql-proxy 的 `-instance` 参数中用 `tcp:0.0.0.0:3306` 替换 `tcp:3306`。

是的,我也尝试过。但是花了我一段时间才弄清楚 0.0.0.0 的部分 ;) - Philipp Kyeck
1
Google支持不建议使用独立的CloudSQL代理部署。 - bartimar
它对我也有用。我花了很长时间才弄清楚,默认情况下我的SQL代理运行在127.0.0.1端口上,当我们有以上架构(服务和SQL代理Pod放在其下)时,需要使用0.0.0.0:3306进行开放。 - voila
以下是如何安全地完成此操作的方法(基本上是使用service=pgbouncer+cloudsql代理),并配置您的应用程序以安全地连接到pgbouncer https://github.com/GoogleCloudPlatform/cloud-sql-proxy/tree/main/examples/k8s-service - neokyle

11

有三种方法可以做到这一点。

1- 使用私有IP连接K8s作业与Cloud SQL,如@newoxo在其中一个答案中所述。要做到这一点,您的集群需要是一个VPC原生集群。我的不是,而且我也不想将所有的东西都移到一个新的集群中。因此,我无法做到这一点。

2- 将Cloud SQL代理容器放在一个单独的部署中,并使用服务进行管理,如@Christian Kohler所述。这看起来是一个很好的方法,但Google Cloud Support不推荐使用。

我本来要朝着这个方向(解决方案#2)前进,但我决定尝试其他的东西。

以下是对我有效的解决方案:

3- 您可以使用文件系统在同一Pod / Job中的不同容器之间通信。思路是告诉Cloud SQL代理容器主要作业何时完成,然后杀死cloud sql代理。以下是如何操作:

在yaml文件(my-job.yaml)中

apiVersion: v1
kind: Pod
metadata:
  name: my-job-pod
  labels:
    app: my-job-app
spec:
  restartPolicy: OnFailure
  containers:
  - name: my-job-app-container
    image: my-job-image:0.1
    command: ["/bin/bash", "-c"]
    args:
      - |
        trap "touch /lifecycle/main-terminated" EXIT
        { your job commands here }
    volumeMounts:
      - name: lifecycle
        mountPath: /lifecycle
  - name: cloudsql-proxy-container
    image: gcr.io/cloudsql-docker/gce-proxy:1.11
    command: ["/bin/sh", "-c"]
    args:
      - |
        /cloud_sql_proxy -instances={ your instance name }=tcp:3306 -credential_file=/secrets/cloudsql/credentials.json &
        PID=$!
        while true
            do
                if [[ -f "/lifecycle/main-terminated" ]] 
                then
                    kill $PID
                    exit 0
                fi
                sleep 1
            done
    securityContext:
      runAsUser: 2  # non-root user
      allowPrivilegeEscalation: false
    volumeMounts:
      - name: cloudsql-instance-credentials
        mountPath: /secrets/cloudsql
        readOnly: true
      - name: lifecycle
        mountPath: /lifecycle
  volumes:
  - name: cloudsql-instance-credentials
    secret:
      secretName: cloudsql-instance-credentials
  - name: lifecycle
    emptyDir:

基本上,当您的主要工作完成后,它将在 /lifecycle 中创建一个文件,并由添加到 cloud-sql-proxy 容器中的 watcher 所识别,该 watcher 将关闭代理并终止容器。

希望有所帮助!如果您有任何问题,请告诉我。

来源: https://dev59.com/Rq7la4cB1Zd3GeqPXh53#52156131


每次执行kill命令时,都会出现“kill:(7)-操作不允许”的错误,无法正常工作。 - Nathan McKaskle

3

3

文档链接或示例? - Archonic
1
@Archonic:您可以在此处找到文档 https://cloud.google.com/sql/docs/mysql/connect-kubernetes-engine#private-ip - newoxo

2
一种可能的解决方案是在作业spec中设置concurrencyPolicy: Replace,这将在需要再次运行时使用新实例来替换当前的Pod。但是,您必须确保后续的cron运行足够分离。

0
Cloud-sql-proxy 2 支持一个 quitquitquit 端点,可以用来关闭 sidecar 代理。不再需要像共享进程空间或写入文件这样的解决方法。
只需在 sql-cloud-proxy 中添加 --quitquitquit 标志,并通过作业定义的 args 发送一个 POST 请求来终止 sidecar:
;exit_code=$?; curl -X POST localhost:9091/quitquitquit && exit $exit_code 在此处阅读更多信息:https://github.com/GoogleCloudPlatform/cloud-sql-proxy/issues/828

0

很不幸,其他答案对我来说都无法工作,因为CloudSQLProxy在一个没有shell的distroless环境中运行

我通过将CloudSQLProxy二进制文件与我的部署捆绑在一起,并运行一个bash脚本来启动CloudSQLProxy,然后启动我的应用程序来解决了这个问题。

Dockerfile:

FROM golang:1.19.4

RUN apt update
COPY . /etc/mycode/
WORKDIR /etc/mycode
RUN chmod u+x ./scripts/run_migrations.sh
RUN chmod u+x ./bin/cloud_sql_proxy.linux-amd64

RUN go install
ENTRYPOINT ["./scripts/run_migrations.sh"]

Shell脚本(run_migrations.sh):

#!/bin/sh

# This script is run from the parent directory
dbConnectionString=$1
cloudSQLProxyPort=$2

echo "Starting Cloud SQL Proxy"
./bin/cloud_sql_proxy.linux-amd64 -instances=${dbConnectionString}=tcp:5432 -enable_iam_login -structured_logs &
CHILD_PID=$!
echo "CloudSQLProxy PID: $CHILD_PID"

echo "Migrating DB..."
go run ./db/migrations/main.go
MAIN_EXIT_CODE=$?

kill $CHILD_PID;
echo "Migrations complete.";

exit $MAIN_EXIT_CODE

K8s(通过 Pulumi):

import * as k8s from '@pulumi/kubernetes'

const jobDBMigrations = new k8s.batch.v1.Job("job-db-migrations", {
      metadata: {
        namespace: namespaceName,
        labels: appLabels,
      },
      spec: {
        backoffLimit: 4,
        template: {
          spec: {
            containers: [
              {
                image: pulumi.interpolate`gcr.io/${gcpProject}/${migrationsId}:${migrationsVersion}`,
                name: "server-db-migration",
                args: [
                  dbConnectionString,
                ],
              },
            ],
            restartPolicy: "Never",
            serviceAccount: k8sSAMigration.metadata.name,
          },
        },
      },
    },
    {
      provider: clusterProvider,
    });

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接