如何正确创建 sidecar 容器以在 kubernetes pod 中创建 SSH 隧道

How to properly create sidecar container for creating SSH tunnel in kubernetes pod

我在 AWS 中有一个数据库,我需要从 Kubernetes 连接到该数据库,但该数据库中的安全设置阻止了这一点。我的解决方案是通过 SSH 隧道从 Kubernetes pod 连接到代理,并通过它连接到 AWS 中的数据库。

但是,我不太确定如何在 Kubernetes 中实际执行此操作,因为边车容器会抛出“CrashLoopBackOff”错误。

我的 Dockerfile 非常薄。这是一个真正什么都不做的高山容器,除了复制一个处理隧道的 shell 脚本。

Dockerfile

FROM alpine:3.14.0

COPY tunnel.sh /

RUN apk update && apk add curl \
    wget \
    nano \
    bash \
    ca-certificates \
    openssh-client

RUN chmod +x /tunnel.sh
RUN mkdir ~/.ssh

RUN ssh-keyscan -Ht ecdsa proxysql-sshtunnel.domain.com > ~/.ssh/known_hosts

CMD /bin/bash

tunnel.sh

#!/bin/bash
ssh -i /keys/sql_proxy.private -L 3306:10.0.0.229:6033 centos@proxysql-sshtunnel.domain.com -N

它们的 SSH 密钥从 Kubernetes 中的秘密卷安装到 pod。我的部署如下所示:

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: accounts-deployment
  namespace: default
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      app: api-accounts
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    spec:
      containers:
      - image: gcr.io/xxxxxxxx/accounts:VERSION-2.0.6
        imagePullPolicy: Always
        name: accounts
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /tmp
          name: accounts-keys
          readOnly: true
        - mountPath: /var/www/html/var/spool
          name: mail-spool
      - image: gcr.io/xxxxxxxx/sql-proxy:latest
        imagePullPolicy: IfNotPresent
        name: sql-proxy
        args:
          - -c
          - /tunnel.sh
        command:
          - /bin/bash
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
        volumeMounts:
        - mountPath: /keys
          name: keys-sql-proxy
          readOnly: true
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
      volumes:
      - name: accounts-keys
        secret:
          defaultMode: 420
          secretName: accounts-keys
      - name: spoonity-sql-proxy
        secret:
          defaultMode: 420
          secretName: spoonity-sql-proxy
      - emptyDir: {}
        name: mail-spool
status:

<-------- 相关部分在这里------>

...
- image: gcr.io/xxxxxxxx/sql-proxy:latest
  imagePullPolicy: IfNotPresent
  name: sql-proxy
  args:
    - -c
    - /tunnel.sh
  command:
    - /bin/bash
  resources: {}
  terminationMessagePath: /dev/termination-log
  terminationMessagePolicy: File
  volumeMounts:
    - mountPath: /keys
      name: keys-sql-proxy
      readOnly: true
...

我从 Kubernetes 获得的唯一日志是:“/bin/bash: line 1: /tunnel.sh: No such file or directory

如果我尝试使用 docker run sql-proxy:latest /tunnel.sh 在 docker 中本地 运行 容器,然后我会收到一个不同的错误,抱怨密钥不存在(这正是我期待看到)。

不确定这个问题出在哪里。

编辑:尝试在本地重建容器并手动包含密钥。我能够成功启动容器。所以看起来这肯定是 Kubernetes 的问题,但我真的不确定为什么。

这里的问题是您可能正在将文件复制到容器的 / 目录,但是当您启动容器时,shell 从 ~/ 目录开始。所以它找不到文件。

在 Dockerfile 的开头添加一个 WORKDIR 语句,这将确保当您启动容器时,您知道从哪里开始。

FROM alpine:3.14.0

WORKDIR /usr/src/app

COPY tunnel.sh .

RUN apk update && apk add curl \
    wget \
    nano \
    bash \
    ca-certificates \
    openssh-client

RUN chmod +x ./tunnel.sh

RUN mkdir ~/.ssh

RUN ssh-keyscan -Ht ecdsa proxysql-sshtunnel.domain.com > ~/.ssh/known_hosts

CMD /bin/bash

此外,建议将 CMD 更改为您想要 运行 的实际命令,而不是通过 kubernetes 传递它。

所以问题出在这里:

volumes:
      - name: accounts-keys
        secret:
          defaultMode: 420
          secretName: accounts-keys
      - name: spoonity-sql-proxy
        secret:
          defaultMode: 420 #<----------- this is wrong
          secretName: spoonity-sql-proxy

SSH 需要特定的密钥权限才能连接。 Kubernetes 使用基于十进制的文件权限,因此这里的正确值应该是 384,这将在 Linux.

中挂载具有适当权限 0600 的密钥

由于权限错误,每次脚本尝试执行时都会失败退出,触发Kubernetes尝试重启。

仍然不确定为什么从未生成这些日志,但我通过任意更改我的部署清单中的 commandargs 来发现这一点,而不是连续 ping 本地主机,这样容器至少开始:

...
 - image: gcr.io/xxxxxxxxx/sql-proxy:latest
   command: ["ping"]
   args: ["127.0.0.1"]
...

然后我连接到 now-运行ning pod,并尝试手动 运行 tunnel.sh 命令。现在我可以真正看到它失败的原因,我可以修复它。