【问题标题】:Application running in Kubernetes cron job does not connect to database in same Kubernetes cluster在 Kubernetes cron 作业中运行的应用程序未连接到同一 Kubernetes 集群中的数据库
【发布时间】:2021-04-18 18:52:34
【问题描述】:

我有一个运行 PostgreSQL 数据库、Grafana 仪表板和一个 Python 单运行应用程序(构建为 Docker 映像)的 Kubernetes 集群,该应用程序每小时在 Kubernetes CronJob 内运行(参见下面的清单)。此外,这一切都是使用带有 Istio side-car 注入的 ArgoCD 进行部署的。

我遇到的问题(如标题所示)是我的 Python 应用程序无法连接到集群中的数据库。这对我来说很奇怪,因为仪表板实际上可以连接到数据库,所以我不确定 Python 应用程序可能有什么不同。

以下是我的清单(更改了一些内容以删除可识别信息):

database.yaml的内容:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: database
  name: database
spec:
  replicas: 1
  selector:
    matchLabels:
      app: database
  strategy: {}
  template:
    metadata:
      labels:
        app: database
    spec:
      containers:
      - image: postgres:12.5
        imagePullPolicy: ""
        name: database
        ports:
        - containerPort: 5432
        env:
          - name: POSTGRES_DB
            valueFrom:
              secretKeyRef:
                name: postgres-secret
                key: POSTGRES_DB
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                name: postgres-secret
                key: POSTGRES_USER
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                name: postgres-secret
                key: POSTGRES_PASSWORD
        resources: {}
        readinessProbe:
          initialDelaySeconds: 30
          tcpSocket:
            port: 5432
      restartPolicy: Always
      serviceAccountName: ""
      volumes: null
status: {}
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: database
  name: database
spec:
  ports:
  - name: "5432"
    port: 5432
    targetPort: 5432
  selector:
    app: database
status:
  loadBalancer: {}

dashboard.yaml的内容:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: dashboard
  name: dashboard
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dashboard
  strategy: {}
  template:
    metadata:
      labels:
        app: dashboard
    spec:
      containers:
      - image: grafana:7.3.3
        imagePullPolicy: ""
        name: dashboard
        ports:
          - containerPort: 3000
        resources: {}
        env:
          - name: POSTGRES_DB
            valueFrom:
              secretKeyRef:
                name: postgres-secret
                key: POSTGRES_DB
          - name: POSTGRES_USER
            valueFrom:
              secretKeyRef:
                name: postgres-secret
                key: POSTGRES_USER
          - name: POSTGRES_PASSWORD
            valueFrom:
              secretKeyRef:
                name: postgres-secret
                key: POSTGRES_PASSWORD
        volumeMounts:
          - name: grafana-datasource
            mountPath: /etc/grafana/provisioning/datasources
        readinessProbe:
          initialDelaySeconds: 30
          httpGet:
            path: /
            port: 3000
      restartPolicy: Always
      serviceAccountName: ""
      volumes:
        - name: grafana-datasource
          configMap:
            defaultMode: 420
            name: grafana-datasource
        - name: grafana-dashboard-provision
status: {}
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: dashboard
  name: dashboard
spec:
  ports:
  - name: "3000"
    port: 3000
    targetPort: 3000
  selector:
    app: dashboard
status:
  loadBalancer: {}

cronjob.yaml的内容:

---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: python
spec:
  concurrencyPolicy: Replace
  # TODO: Go back to hourly when finished testing/troubleshooting
  # schedule: "@hourly"
  schedule: "*/15 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - image: python-tool:1.0.5
            imagePullPolicy: ""
            name: python
            args: []
            command:
              - /bin/sh
              - -c
              - >-
                echo "$(POSTGRES_USER)" > creds/db.creds;
                echo "$(POSTGRES_PASSWORD)" >> creds/db.creds;
                echo "$(SERVICE1_TOKEN)" > creds/service1.creds;
                echo "$(SERVICE2_TOKEN)" > creds/service2.creds;
                echo "$(SERVICE3_TOKEN)" > creds/service3.creds;
                python3 -u main.py;
                echo "Job finished with exit code $?";
            env:
              - name: POSTGRES_DB
                valueFrom:
                  secretKeyRef:
                    name: postgres-secret
                    key: POSTGRES_DB
              - name: POSTGRES_USER
                valueFrom:
                  secretKeyRef:
                    name: postgres-secret
                    key: POSTGRES_USER
              - name: POSTGRES_PASSWORD
                valueFrom:
                  secretKeyRef:
                    name: postgres-secret
                    key: POSTGRES_PASSWORD
              - name: SERVICE1_TOKEN
                valueFrom:
                  secretKeyRef:
                    name: api-tokens-secret
                    key: SERVICE1_TOKEN
              - name: SERVICE2_TOKEN
                valueFrom:
                  secretKeyRef:
                    name: api-tokens-secret
                    key: SERVICE2_TOKEN
              - name: SERVICE3_TOKEN
                valueFrom:
                  secretKeyRef:
                    name: api-tokens-secret
                    key: SERVICE3_TOKEN
          restartPolicy: OnFailure
          serviceAccountName: ""
status: {}

现在,正如我提到的,Istio 也是这张图片的一部分,所以我有一个用于仪表板的虚拟服务,因为它应该可以在集群外部访问,但仅此而已。

解决了所有这些问题,以下是我自己尝试解决此问题的方法:

  1. 确认CronJob 使用正确的连接设置(即主机、数据库名称、用户名和密码)连接到数据库。

    为此,我在 CronJob 部署中添加了 echo 语句,显示了用户名和密码(我知道,我知道),它们是预期值。我也知道这些是数据库的正确连接设置,因为我逐字使用它们将仪表板连接到数据库,从而成功连接。

    Grafana 仪表板的数据源设置:

    来自 Python 应用程序的错误消息(显示在容器的 ArgoCD 日志中):

  2. 考虑到 Istio 可能会导致此问题,我尝试为 CronJob 资源禁用 Istio side-car injection(通过将此注释添加到 metadata.annotations 部分:sidecar.istio.io/inject: false)但该注释实际上从未出现在Argo 日志,CronJob 运行时没有观察到任何变化。

  3. 我尝试将 kubectl execing 放入运行 Python 脚本的 CronJob 容器中以进行更多调试,但实际上一直无法调试,因为一旦发生连接错误,容器就退出了。

也就是说,我在这件事上已经把头撞到墙上已经够久了。谁能发现我可能缺少的东西并指出正确的方向吗?

【问题讨论】:

  • 你能在你的python命令中添加一个telnet、netcat或curl到database:5432吗?

标签: python-3.x postgresql kubernetes istio


【解决方案1】:

我认为问题在于您的 pod 会在 istio sidecar 准备好之前尝试连接到数据库。因此无法建立连接。

Istio 运行一个初始化容器,用于配置 pod 路由表,因此所有流量都通过 sidecar 路由。所以如果 sidecar 没有运行,而另一个 pod 尝试连接到 db,则无法建立连接。

有两种解决方案。

首先,您的工作可能会等待例如 30 秒,然后再使用一些 sleep 命令调用 main.py

您也可以启用holdApplicationUntilProxyStarts。通过这个主容器在 sidecar 运行之前不会启动。

【讨论】:

  • 啊,就是这样!非常感谢您的帮助!
猜你喜欢
  • 1970-01-01
  • 2018-07-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
  • 2019-02-17
  • 2017-05-02
  • 1970-01-01
相关资源
最近更新 更多