如何在 kubernetes pod 中正确创建用于创建 SSH 隧道的 sidecar 容器
Posted
技术标签:
【中文标题】如何在 kubernetes pod 中正确创建用于创建 SSH 隧道的 sidecar 容器【英文标题】:How to properly create sidecar container for creating SSH tunnel in kubernetes pod 【发布时间】:2021-09-29 00:25:57 【问题描述】:我在 AWS 中有一个需要从 Kubernetes 连接的数据库,但该数据库中的安全设置阻止了这种情况。我的解决方案是从 Kubernetes pod 内通过 SSH 隧道连接到代理,并通过该隧道连接到 AWS 中的数据库。
但是,我不太确定如何在 Kubernetes 中真正实现这一点,因为 sidecar 容器会抛出“CrashLoopBackOff”错误。
我的 Dockerfile 很薄。它是一个 alpine 容器,除了复制一个处理隧道的 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 的问题,但我真的不知道为什么。
【问题讨论】:
从下面的答案“建议将 CMD 更改为您要运行的实际命令,而不是通过 kubernetes 传递它。”这是选项之一。其次是将命令/参数重写为this structure 的想法。我用你的选择进行了测试,它对我的情况不起作用。 【参考方案1】:这里的问题是您可能正在将文件复制到容器的/
目录,但是当您启动容器时,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 传递。
【讨论】:
这并不能解决问题。我也不确定为什么会这样。该脚本位于根目录 (/)。它是从 / 在 Kubernetes 中调用的。将该绝对路径设为相对路径不应产生任何实质性影响。【参考方案2】:所以问题就在这里:
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尝试重启。
仍然不确定为什么从未生成这些日志,但我通过在我的部署清单中任意更改 command
和 args
来发现这一点,而不是连续 ping localhost 以便容器至少可以启动:
...
- image: gcr.io/xxxxxxxxx/sql-proxy:latest
command: ["ping"]
args: ["127.0.0.1"]
...
然后我连接到正在运行的 pod,并尝试手动运行 tunnel.sh 命令。现在我可以真正了解它失败的原因,我可以修复它。
【讨论】:
以上是关于如何在 kubernetes pod 中正确创建用于创建 SSH 隧道的 sidecar 容器的主要内容,如果未能解决你的问题,请参考以下文章
理解Kubernetes的NodePort、LoadBalancer和Ingress
通过 Airflow 创建的 Kubernetes pod 保持运行状态
Kubernetes Pod 中如何查看动态创建绑定的远程磁盘使用情况
Kubernetes 如何在 StatefulSet 中跟踪哪个云盘挂载到哪个 Pod?