Kubernetes: 更好地支持批处理作业中的 sidecar 容器

创建于 2016-05-19  ·  116评论  ·  资料来源: kubernetes/kubernetes

考虑其中有两个容器的作业——一个完成工作然后终止,另一个不是设计为明确退出但提供某种支持功能,如日志或指标收集。

做这样的事情有哪些选择? 应该存在哪些选项?

目前,只要第二个容器继续运行,作业就会继续运行,这意味着用户必须以某种方式修改第二个容器以检测第一个容器何时完成,以便它也可以干净地退出。

不久前在 Stack Overflow 上问过这个

@kubernetes/goog-control-plane @erictune

arebatch areworkload-apjob kinfeature lifecyclfrozen prioritimportant-longterm siapps sinode

最有用的评论

作为参考,这是我用来模拟所需边车行为的 bash madness:

containers:
  - name: main
    image: gcr.io/some/image:latest
    command: ["/bin/bash", "-c"]
    args:
      - |
        trap "touch /tmp/pod/main-terminated" EXIT
        /my-batch-job/bin/main --config=/config/my-job-config.yaml
    volumeMounts:
      - mountPath: /tmp/pod
        name: tmp-pod
  - name: envoy
    image: gcr.io/our-envoy-plus-bash-image:latest
    command: ["/bin/bash", "-c"]
    args:
      - |
        /usr/local/bin/envoy --config-path=/my-batch-job/etc/envoy.json &
        CHILD_PID=$!
        (while true; do if [[ -f "/tmp/pod/main-terminated" ]]; then kill $CHILD_PID; fi; sleep 1; done) &
        wait $CHILD_PID
        if [[ -f "/tmp/pod/main-terminated" ]]; then exit 0; fi
    volumeMounts:
      - mountPath: /tmp/pod
        name: tmp-pod
        readOnly: true
volumes:
  - name: tmp-pod
    emptyDir: {}

所有116条评论

/子

还使用此处建议的活跃度问题http://stackoverflow.com/questions/36208211/sidecar-containers-in-kubernetes-jobs不起作用,因为 pod 将被视为失败并且整个工作不会被视为成功。

我们如何声明一个作业成功探测器,以便作业可以探测它以检测成功,而不是等待 pod 返回 0。
一旦探针返回成功,则可以终止 pod。

可以针对已经退出的容器运行探测,或者会在那里
是一场正在被拆除的比赛吗?

另一种选择是将某些退出代码指定为具有特殊含义。

“整个 Pod 的成功”或“整个 Pod 的失败”都是
有用。

这需要在 Pod 对象上,所以这是一个很大的 API 变化。

2016 年 9 月 22 日星期四下午 1:41,Ming Fang [email protected]写道:

我们声明一个工作成功探测怎么样,这样工作就可以探测到
检测成功而不是等待 pod 返回 0。

一旦探针返回成功,则可以终止 pod。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/kubernetes/kubernetes/issues/25908#issuecomment -249021627,
或静音线程
https://github.com/notifications/unsubscribe-auth/AHuudjrpVtef6U35RWRlZr3mDKcCRo7oks5qsugRgaJpZM4IiqQH
.

@erictune好点; 我们无法探测退出的容器。

我们能否将 Pod 中的特定容器指定为“完成”容器,以便在该容器退出时我们可以说作业已完成?

Sidecar 容器往往可以用于日志传送和监控之类的事情。
一旦工作完成,我们可以强制终止它们。

我们能否将 Pod 中的特定容器指定为“完成”容器,以便在该容器退出时我们可以说作业已完成?

您是否查看过此文档的第 3 点,此处详细描述了您基本上不设置.spec.completions并且一旦第一个容器以 0 退出代码完成,作业就完成了。

Sidecar 容器往往可以用于日志传送和监控之类的事情。
一旦工作完成,我们可以强制终止它们。

就我个人而言,这些在我看来更像是 RS,而不是一份工作,但这是我个人的看法,最重要的是我不知道您设置的全部细节。

通常,还有以下讨论https://github.com/kubernetes/kubernetes/issues/17244https://github.com/kubernetes/kubernetes/issues/30243也涉及到这个话题。

@soltysh您在上面发送的链接,第 3 点引用了 pod 完成而不是容器完成。

这两个容器可以共享一个 emptyDir,第一个容器可以将“我正在退出”消息写入文件,另一个容器可以在看到该消息时退出。

@erictune我有一个用例,我认为它属于这个

我正在使用 client-go 库对以下所有内容进行编码:

所以,我有一份工作,主要是在一个容器 pod 中运行一个工具。 一旦该工具完成运行,它就会生成一个结果文件。 我似乎无法捕获这个结果文件,因为一旦该工具完成运行,pod 就会删除并且我丢失了结果文件。

如果我使用HostPath作为 VolumeSource,我能够捕获这个结果文件,并且由于我在本地运行 minikube,结果文件被保存到我的工作站上。

但是,我知道这对于生产容器来说是不推荐和理想的。 所以,我按照上面的建议使用了EmptyDir 。 但是,再一次,如果我这样做,我就无法真正捕获它,因为它会随 pod 本身一起被删除。

那么,我是否也应该使用 sidecar 容器模式来解决我的问题?

基本上,按照您上面的建议进行操作。 每当作业开始时,在 pod 中启动 2 个容器。 1 容器运行作业,一旦作业完成,就会丢弃一条消息,该消息被另一个容器拾取,然后获取结果文件并将其存储在某处?

我不明白为什么我们首先需要 2 个容器。 为什么工作容器不能自己做这一切? 也就是说,完成工作,将结果文件保存在某处,访问它/读取它并将其存储在某处。

@anshumanbh我建议你:

  1. 使用持久存储,您保存结果文件
  2. hostPath mount,和1差不多,你已经试过了
  3. 将结果文件上传到已知的远程位置(s3、google drive、dropbox),通常是任何类型的共享驱动器

@soltysh我不想永久存储文件。 在每次运行时,我只想将该结果与上次结果进行比较。 所以,我想这样做的方式是在每次运行时提交一个 github 存储库,然后做一个 diff 以查看发生了什么变化。 因此,为了做到这一点,我只需要将结果临时存储在某个地方,以便我可以访问它并将其发送到 Github。 说得通?

@anshumanbh非常清楚,但仍然不属于边车容器的类别。 您想要实现的所有目标都是目前可以提供的工作。

@soltysh所以考虑到我想从你上面建议的列表中选择选项 3,我将如何实施它?

我面临的问题是,一旦作业完成,容器就会退出并且我丢失了文件。 如果我没有该文件,如何将其上传到 S3/Google Drive/Dropbox 等共享驱动器? 我无法修改作业的代码以在它退出之前自动将其上传到某处,因此不幸的是,我必须先运行该作业,然后将文件保存在某处。

如果你不能修改job的代码,你需要把它包装成这样才能上传文件。 如果您使用的是图像,则只需使用复制代码对其进行扩展。

@soltysh是的,这是有道理的。 我可以做到。 但是,我的下一个问题是 - 假设我需要运行多个作业(将其视为运行不同的工具)并且这些工具都没有内置上传部分。 所以,现在,我必须构建该包装器并使用上传部分扩展这些工具中的每一个。 有没有一种方法可以只编写一次包装器/扩展程序并将其用于所有工具?

侧车模式不适合这种情况吗?

是的,可以。 虽然我会尝试在同一个 pod 中使用多个容器,但模式。 哎哟。 您的 pod 正在运行作业容器,另外还有一个等待输出并上传的容器。 不确定这有多可行,但您已经可以尝试了。

温和的 ping——sidecar 意识将使微服务代理(如 Envoy)的管理更加愉快。 有什么进展可以分享吗?

目前的状态是每个容器都需要捆绑工具来协调生命周期,这意味着我们不能直接使用上游容器镜像。 这也使模板变得非常复杂,因为我们必须注入额外的 argv 和挂载点。

较早的建议是将一些容器指定为“完成”容器。 我想提出相反的建议——将一些容器指定为“边车”的能力。 当 Pod 中的最后一个非 sidecar 容器终止时,Pod 应该将TERM发送到 sidecar。 这类似于许多线程库中的“后台线程”概念,例如 Python 的Thread.daemon

示例配置,当容器main结束时,kubelet 将杀死envoy

containers:
  - name: main
    image: gcr.io/some/image:latest
    command: ["/my-batch-job/bin/main", "--config=/config/my-job-config.yaml"]
  - name: envoy
    image: lyft/envoy:latest
    sidecar: true
    command: ["/usr/local/bin/envoy", "--config-path=/my-batch-job/etc/envoy.json"]

作为参考,这是我用来模拟所需边车行为的 bash madness:

containers:
  - name: main
    image: gcr.io/some/image:latest
    command: ["/bin/bash", "-c"]
    args:
      - |
        trap "touch /tmp/pod/main-terminated" EXIT
        /my-batch-job/bin/main --config=/config/my-job-config.yaml
    volumeMounts:
      - mountPath: /tmp/pod
        name: tmp-pod
  - name: envoy
    image: gcr.io/our-envoy-plus-bash-image:latest
    command: ["/bin/bash", "-c"]
    args:
      - |
        /usr/local/bin/envoy --config-path=/my-batch-job/etc/envoy.json &
        CHILD_PID=$!
        (while true; do if [[ -f "/tmp/pod/main-terminated" ]]; then kill $CHILD_PID; fi; sleep 1; done) &
        wait $CHILD_PID
        if [[ -f "/tmp/pod/main-terminated" ]]; then exit 0; fi
    volumeMounts:
      - mountPath: /tmp/pod
        name: tmp-pod
        readOnly: true
volumes:
  - name: tmp-pod
    emptyDir: {}

我想提出相反的建议——将一些容器指定为“边车”的能力。 当 Pod 中的最后一个非 sidecar 容器终止时,Pod 应该向 sidecar 发送 TERM。

@jmillikin-stripe 我喜欢这个想法,尽管我不确定这是否遵循在 Pod 中以不同方式处理某些容器或在它们之间引入依赖项的原则。 我将推迟到@erictune进行最后一次通话。

不过,您是否检查过 #17244,这种类型的解决方案是否适合您的用例? 这是@erictune之前提到的一些评论

另一种选择是将某些退出代码指定为具有特殊含义。

@jmillikin-stripe 我喜欢这个想法,尽管我不确定这是否遵循在 Pod 中以不同方式处理某些容器或在它们之间引入依赖项的原则。 我将推迟到@erictune进行最后一次通话。

我认为 Kubernetes 可能需要灵活对待不以不同方式对待容器的原则。 我们 (Stripe) 不想改造 Envoy 等第三方代码以拥有 Lamprey 风格的生命周期钩子,并且尝试采用 Envelope 风格的 exec 反转比让 Kubelet 终止特定的 sidecar 复杂得多。

不过,您是否检查过 #17244,这种类型的解决方案是否适合您的用例? 这是@erictune之前提到的一些评论:

另一种选择是将某些退出代码指定为具有特殊含义。

我非常反对 Kubernetes 或 Kubelet 以比“零或非零”更精细的粒度解释错误代码。 Borglet 使用退出代码幻数是一个令人不快的错误功能,在 Kubernetes 中情况会更糟,因为特定容器映像可能是不同 Pod 中的“主”或“边车”。

也许额外的生命周期钩子足以解决这个问题?

可能:

  • PostStop:有一种在pod中的其他容器上触发生命周期事件的方法(即触发停止)
  • PeerStopped:表示 Pod 中的“对等”容器已经死亡 - 可能以退出代码作为参数

这也可以定义一种方法来定义自定义策略以重新启动容器 - 甚至启动默认情况下未启动的容器以允许一些容器的菊花链(当容器 a 完成时然后启动容器 b)

也缺这个。 我们每 30 分钟运行一次需要 VPN 客户端进行连接的作业,但似乎有很多用例可能非常有用(例如需要 kubectl 代理的东西)。 目前,我使用jobSpec.concurrencyPolicy: Replace作为解决方法,但当然这仅适用于 a.) 您可以在没有并行作业运行的情况下生存并且 b.) 作业执行时间比调度间隔短。

编辑:在我的用例中,在作业规范中有一些属性将容器标记为终止容器并让作业监视该容器的退出状态并杀死剩余的容器就足够了。

我也有这个需要在我们的例子中,这是一项使用 cloudsql-proxy 容器作为 sidecar 服务的工作。

可能添加一个映射到 pod 中“主要”容器名称的注释怎么样? 这样 pod 规范就不需要修改了。

就 Pod 的设计方式而言,这似乎是一个非常常见的用例。 @soltysh @erictune 有没有计划尽快解决这个问题? 很高兴在可能的情况下提供帮助:)

也需要这个功能。 对于我们的用例
pod A必须到容器

  • 容器 A1 :一个运行到完成的容器,将日志打印到文件
  • 容器 A2 :sidecar 容器,它只是将日志从文件拖尾到标准输出

我想要的是:当容器 A1成功完成时, pod A成功完成。 我们可以将容器 A1标记为主容器,当主容器退出时,pod 退出吗? @erictune(这个想法也由@mingfang描述)

嘿伙计们,我看到这个问题已经开放一个月了。 这方面的最新情况是什么? 我们有一个用例,我们想在其中运行一个作业。 该作业运行一个main容器,其中包含一些 side-car containers 。 我们希望作业在main容器退出时退出。 共享file以在容器之间发送signal是否是最先进的技术?

我不介意就此开始一些工作,想知道是否有人可以审查即将发布的 PR(可能在 kubecon 之后)。

抄送@erictune @a-robinson @soltysh

@andrewsykim你会采取什么方法。 另外,我知道我在这里添加它,添加对依赖项的支持需要什么。 就像main容器在 sidecars 初始化之前不应该启动

就像主容器在 sidecars 初始化之前不应该启动

我认为这种情况不是问题,因为main应该能够检查 sidecar 何时初始化(或使用就绪探测器)。 对于此问题,情况并非如此,因为main会退出 :)

我最终编写了一个简单的脚本,该脚本监视 kubernetes API 并使用匹配的注释终止作业,并且其主容器已退出。 它并不完美,但它解决了核心需求。 如果人们感兴趣,我可以分享它。

@ajbouh如果您将其作为要点分享,我个人会很感激。 我正要写类似的东西

@nrmitchi这是我写的 yaml 的要点。 它非常符合 shell 脚本,但也许它是您的一个很好的起点,就使用哪些 API 以及如何获得有效的东西而言。 如果您有任何问题,我可以回答有关它在做什么的问题。

https://gist.github.com/ajbouh/79b3eb4833aa7b068de640c19060d126

我有与@mrbobbytables 相同的 Cloud SQL 代理用例。 为了安全地连接到云 SQL,建议使用代理,但该代理不会在工作完成后终止,这会导致疯狂的黑客攻击或如下所示的监控。 在这方面有什么前进的道路吗?

image

@amaxwell01关于 Cloud SQL 代理在这方面的参与,我向 Google 提出了一个问题,您可以https :

谢谢@abevoelker我正在关注你的帖子。 加上你的评论让我笑了👍

我们也受到这个问题的影响。
我们的微服务上有几个 django 管理命令,它们可以在 k8s cronjobs 上运行,但由于 cloudsqlproxy sidecar 在作业完成时不会停止而未能成功。
关于我们何时可以找到解决方案的任何更新?
sidecar 容器模式被越来越多地使用,在这个问题得到解决之前,我们中的很多人将无法使用 k8s cronjobs 和作业。

只是想为此投入我的+1。 我和其他人一样遇到了 GCE Cloud SQL 代理问题。 它正在杀死我...... helm deploy 失败,这反过来又使我的 terraform apply 失败。

真的很想看到某种解决方案......来自@ajbouh 的 jist看起来可以工作......但是天哪,这太糟糕了。

对于需要cloudsql-proxy ,将cloudsql-proxy作为 DaemonSet 运行是否适合您的用例? 在我的例子中,我有一个持久部署和一个需要代理的 CronJob,所以将它从单个 pod 中分离出来,而是为每个节点附加一个实例是有意义的。

是的,

我们决定移除 cloudsql 代理 sidecars 并建立一个池
cloudsql 代理在其中央命名空间中,它完美运行并允许
移动可扩展性和更轻松的部署。
现在我们可以毫无问题地运行作业和 cronjobs。

2018 年 2 月 7 日星期三上午 9:37,Rob Jackson通知@github.com
写道:

对于需要 cloudsql-proxy 的任何其他人,它是否适合您的用例
将 cloudsql-proxy 作为 DaemonSet 运行? 在我的情况下,我有一个持久的
部署和需要代理的 CronJob,因此分离是有意义的
它来自单个 pod,而是为每个节点附加一个实例。


您收到此消息是因为您发表了评论。
直接回复本邮件,在GitHub上查看
https://github.com/kubernetes/kubernetes/issues/25908#issuecomment-363710890
或静音线程
https://github.com/notifications/unsubscribe-auth/ACAWMwetx6gA_SrHL_RRbTMJVOhW1FKLks5tSW7JgaJpZM4IiqQH
.

有趣的是,使用 deamonset 听起来是个不错的选择。 @RJacksonm1 & @devlounge - 使用守护进程时云 sql 代理的发现是如何工作的?

发现这个看起来可以解决问题...
https://buoyant.io/2016/10/14/a-service-mesh-for-kubernetes-part-ii-pods-are-great-until-theyre-not/

基本上涉及使用这样的东西来获取主机IP:

env:
- name: NODE_NAME
  valueFrom:
    fieldRef:
      fieldPath: spec.nodeName

@RJacksonm1 - 你做了什么特别的事情来让hostPort工作吗? 将它与fieldPath: spec.nodeName方法结合使用时,我不断得到connection refused 🤔

编辑:我已经确保spec.nodeName正确通过并且我在 GKE v1.9.2-gke.1

@cvallance我已经设置了一个服务来公开 DaemonSet,然后我的应用程序可以通过 DNS 访问它。 这并不能保证应用程序将与在同一主机上运行的cloudsql-proxy实例对话,但它确实保证cloudsql-proxy将与整个集群一起扩展(最初我有代理作为部署和 Horizo​​ntalPodAutoscaler,但发现它向上/向下扩展太多 - 导致应用程序中出现MySQL has gone away错误)。 我想这不符合 DaemonSet 的真正精神......🤔

@RJacksonm1 - 让它与hostPortspec.nodeName ......现在他们将直接连接到他们节点上的 DaemonSet 😄

CloudSql 代理命令不起作用:
-instances={{ .Values.sqlConnectionName }}=tcp:{{ .Values.internalPort }}
在职的:
-instances={{ .Values.sqlConnectionName }}=tcp:0.0.0.0:{{ .Values.internalPort }}

🤦‍♂️

我们可以做些什么来在这个问题上获得一些牵引力?
它已经开放了将近 2 年,但我们仍然只有解决方法

我怀疑即使我自愿实现这一点,我也无法实现,因为它需要内部人员的一些批准才能实现哪些解决方案、API 更改等。

我能做些什么来帮助完成这件事?

作为参考,我制作了 @jmillikin-stripe 解决方法的 cloud-sql-proxy sidecar 版本,其中共享卷中的文件将状态传达给 sidecar。

它工作正常,但迄今为止我的 K8s 配置中最讨厌的 hack :(

apiVersion: batch/v1
kind: Job
metadata:
  name: example-job
spec:
  template:
    spec:
      containers:
      - name: example-job
        image: eu.gcr.io/example/example-job:latest
        command: ["/bin/sh", "-c"]
        args:
          - |
            trap "touch /tmp/pod/main-terminated" EXIT
            run-job.sh
        volumeMounts:
          - mountPath: /tmp/pod
            name: tmp-pod
      - name: cloudsql-proxy
        image: gcr.io/cloudsql-docker/gce-proxy:1.11
        command: ["/bin/sh", "-c"]
        args:
          - |
            /cloud_sql_proxy --dir=/cloudsql -instances=example:europe-west3:example=tcp:3306 -credential_file=/secrets/cloudsql/credentials.json &
            CHILD_PID=$!
            (while true; do if [[ -f "/tmp/pod/main-terminated" ]]; then kill $CHILD_PID; echo "Killed $CHILD_PID as the main container terminated."; fi; sleep 1; done) &
            wait $CHILD_PID
            if [[ -f "/tmp/pod/main-terminated" ]]; then exit 0; echo "Job completed. Exiting..."; fi
        volumeMounts:
          - name: cloudsql-instance-credentials
            mountPath: /secrets/cloudsql
            readOnly: true
          - name: cloudsql
            mountPath: /cloudsql
          - mountPath: /tmp/pod
            name: tmp-pod
            readOnly: true
      restartPolicy: Never
      volumes:
        - name: cloudsql-instance-credentials
          secret:
            secretName: cloudsql-instance-credentials
        - name: cloudsql
          emptyDir:
        - name: tmp-pod
          emptyDir: {}
  backoffLimit: 1

项目内部的任何人都可以评论这个问题的进展吗?

同样的问题在这里

cc @kubernetes/sig-apps-feature-requests @kubernetes/sig-node-feature-requests

允许用户(按名称)指定他们希望成功完成的作业中的容器以便将作业 pod 标记为完成(其他容器已停止)是否有意义,如下所示:

apiVersion: batch/v2beta1
kind: Job
metadata:
  name: my-job
  namespace: app
spec:
  template:
    spec:
      containers:
        - name: my-container
          image: my-job-image
          ...
        - name: cloudsql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:1.11
          ...
  backoffLimit: 2
  jobCompletedWith:
    - my-container

即 pod 会运行,等到my-container成功退出,然后终止cloudsql-proxy

编辑:向上滚动这个线程,我现在看到以前已经提出过这个。 @erictune或其他人可以重新阐述为什么这行不通吗?

是的,我认为那将是完美的。 只是允许您查看作业状态并在完成后继续管道的东西

是的,那就完美了。

我喜欢这个想法@jpalomaki

我对纯粹在 Job 控制器中解决此问题的方法的一个担忧是,Pod 将在 Job 完成后继续运行。 目前,Pod 进入 Terminated 阶段,Node 可以释放这些资源。

当控制器决定它完成时,您可以让作业控制器删除 Pod,但这也与当前行为不同,终止的 Pod 记录保留在 API 服务器中(不占用节点资源)。

由于这些原因,在 Pod API 级别解决这个问题对我来说似乎更清晰,如果有的话。 节点是唯一应该进入并杀死单个容器的东西,因为您关心的“完成”容器已经终止。 这可以采用 Pod 级 API 的形式,让您指定它应该等待哪些容器的概念,或者采用 Pod 级 API 的形式让外部代理(例如作业控制器)强制 Pod 终止而不实际删除豆荚。

我也在寻找一种解决方案来上传容器生成的文件,如果处理器容器成功退出。

我不确定我是否理解@mingfang提出的反对让 sidecar 容器通过 k8s API 监视容器状态以了解是否以及何时开始上传或退出的论点。 当 sidecar 容器退出 pod 并且作业应该成功退出时。

另一个想法,这确实看起来像一个 hack,但我想知道将数据生成容器变成一个 init 容器,并拥有数据上传容器(它不再需要是 sidecar 容器)会有多糟糕) 仅在处理器容器成功退出后自动启动。 就我而言,我还需要一个数据下载器容器作为第一个 init 容器,以向处理容器提供数据。如果这是一个特别糟糕的主意,我很想了解原因。

将 sidecar 提升为一流的 k8s 概念不会解决这个问题吗? 如果 Pod 中所有正在运行的容器都被标记为 sidecar,Kubelet 将能够终止该 Pod。

FWIW,我通过将 Cloud SQL 代理部署为常规部署 ( replicas: 1 ) 解决了这个问题,并使我的JobCronJob通过type: ClusterIP使用它服务。 工作现在完成得很好。

我很想在这方面发表官方立场。

如果我们不打算从 API 获得一些支持,我们至少应该将替代解决方案正式记录在案,以便人们在遇到此问题时知道该怎么做。

我不知道该联系谁或如何引起注意...

解决这个问题真的很好。 除了 Job 永远不会消失之外,Pod 的整体状态显然是不正确的:

Init Containers:
  initializer:
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Wed, 21 Mar 2018 17:52:57 -0500
      Finished:     Wed, 21 Mar 2018 17:52:57 -0500
    Ready:          True
Containers:
  sideCar:
    State:          Running
      Started:      Wed, 21 Mar 2018 17:53:40 -0500
    Ready:          True
  mainContainer:
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Wed, 21 Mar 2018 17:53:41 -0500
      Finished:     Wed, 21 Mar 2018 17:55:12 -0500
    Ready:          False
Conditions:
  Type           Status
  Initialized    True 
  Ready          False 
  PodScheduled   True 

有趣的是注意 initContainer(已终止、已完成、Ready=True)和主应用程序容器(已终止、已完成、Ready=False)的状态和就绪状态。 这似乎正在推动 Pod Ready 状态为 False - 在我看来,这是错误的。 这导致此 Pod 在我们的仪表板上被标记为存在问题。

我有另一个客户专门遇到了 Cloud SQL 代理的这个问题。 他们不希望将其作为持久服务运行以允许 cron 作业访问 Cloud SQL。

@yuriatgoogle最简单的解决方案仍然是 bash 和 emptyDir“魔法”,例如: https :

这是一个黑客,但它必须这样做。 无意冒犯@phidah。

看起来肯定有很多人出于各种原因想要这个。 如果有官方支持就好了。 我自己的 sidecar 和作业也有同样的问题,所以我让 sidecar 使用 kube api 来观察 pod 中另一个容器的状态,如果它以completed终止,sidecar 将退出 0,如果它错误地将 sidecar 退出 1。也许不是最优雅的解决方案,但它做到了这一点,而无需我们的开发人员进行太多更改。 如果有人感兴趣,代码在这里: https :

这让我想起了 Gorillaz 的歌曲 M1 A1...

你好? 喂喂喂? 有人在吗?

是的,让我们获得一些牵引力 +1

因此,需要上游更改的建议解决方案是:

  1. sidecar: true来自@jmillikin-stripe
  2. @msperl 的额外生命周期钩子
  3. jobCompletedWith来自@jpalomaki

sidecar的临时解决方案,一个hacky的(但有效):

  1. cloudsql-proxy sidecar by @phidah

我很想看到 Kubernetes 维护者对提议的解决方案的回应,请就如何使用现有的 kubernetes 版本解决这个用例向我们提出建议。 谢谢!

花了一天时间尝试编写一个日志代理,将我的渲染任务的 stdout/stderr 上传到数据库后,才发现这个线程,结果发现 pod 中代理的存在意味着作业永远不会终止。

在上面给出的建议中,我最喜欢“sidecar: true”,因为它简单明了——对于像我这样的开发人员来说非常容易理解。 我可能会称它为稍微不同的东西,因为“sidecar”实际上是一种 pod 设计模式,它不仅适用于工作,还暗示了除完成要求之外的其他事情。 如果你原谅我的自行车棚,我可能会称之为“环境:真实”,以表明即使此任务仍在运行,也可以认为作业已完成。 其他词可能是“辅助”或“支持”。

我也遇到过这个问题,与许多其他人描述的工作流程相同(用于代理连接或收集指标的 sidecar 容器,在 pod 中的另一个容器成功退出后没有任何用途)。

较早的建议是将一些容器指定为“完成”容器。 我想提出相反的建议——将一些容器指定为“边车”的能力。 当 Pod 中的最后一个非 sidecar 容器终止时,Pod 应该向 sidecar 发送 TERM。

这也是我理想的解决方案。 我可能会建议 SIGHUP 而不是 SIGTERM - 这似乎是 SIGHUP 语义相关的确切用例! - 但我对任何一个都很满意。

实际上,在 Kubernetes 上运行作业需要手动修补上游容器映像以在非 sidecar 容器完成时处理特定于 Kubernetes 的容器间通信,或者手动干预以终止每个作业的 sidecar 以便僵尸 Pod 不会到处转转。 两者都不是特别愉快。

我愿意为此打一个补丁,但在深入研究任何代码之前,我希望得到@kubernetes/sig-apps-feature-requests 的一些指导。 我们可以在 pod 规范中添加一个sidecar字段来完成这项工作吗? 在不确定我们是否想要它的情况下,我犹豫是否对 pod 规范进行任何更改。 也许现在使用注释?

@andrewsykim我关注这个问题已经有一段时间了(只是没有尝试自己解决这个问题),但我建议现在只使用注释。

我的理由是:

  • 这个问题已经存在将近 2 年了,并没有真正引起 Kubernetes 核心的太多关注。 因此,如果我们等待更改 pod 规范,或者等待直接输入,我们可能会等待很长时间。
  • 一个可行的 PR 比一个老问题更容易引起关注。
  • 将来切换注释方法以使用 pod 属性应该很漂亮

想法?

嗨,我与 kubecon 的一些 sig-apps 人员讨论了这个问题,基本上这不是他们直接路线图上的东西,但他们认为这是一个有效的用例。 他们对解决此问题的社区人员非常开放。

我已经为解决这个问题的增强提案创建了一个 PR,所以我希望这会产生一些讨论https://github.com/kubernetes/community/pull/2148。

感谢您将这些放在一起@Joseph-Irving! 似乎还有更多细节需要解决,所以我会推迟做任何工作,直到那时:)

持久性长期问题:(

抄送@kow3ns @janetkuo

无意使问题进一步复杂化,能够与initContainers一起运行“sidecar”样式容器也很有用。

我的用例和这里的人类似,我需要在运行数据库迁移的 initContainer 的同时运行云 sql 代理。 由于 initContainers 一次运行一个,我看不出有什么方法可以做到这一点,除了将代理作为部署 + 服务运行,但我希望还有其他用例(日志管理等),这不是一个合适的工作大约。

@mcfedr有一个相当积极的增强提案,它可能会欣赏关于 init 容器行为的观察。 我不清楚这是否在本提案的范围内,或相关的改进,但我认为它足够相关,值得提出考虑。

尽管存在潜在的实现/兼容性问题,但您的理想模型大概是让 sidecar init 容器与非 sidecar init 容器同时运行,它们像现在一样继续按顺序运行,并且 sidecar 在主序列容器启动之前终止?

就其价值而言,我还想表达需要忽略仍然像 CloudSQL Proxy 等人一样运行的 sidecar。

我在 30 秒后设法杀死了 cloudsql 容器,因为我知道我的脚本不会花这么长时间。 这是我的方法:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: schedule
spec:
  concurrencyPolicy: Forbid
  schedule: "*/10 * * * *"
  startingDeadlineSeconds: 40
  jobTemplate:
    spec:
      completions: 1
      template:
        spec:
          containers:
          - image: someimage
            name: imagename
            args:
            - php
            - /var/www/html/artisan
            - schedule:run
          - command: ["sh", "-c"]
            args:
            - /cloud_sql_proxy -instances=cloudsql_instance=tcp:3306 -credential_file=some_secret_file.json & pid=$! && (sleep 30 && kill -9 $pid 2>/dev/null)
            image: gcr.io/cloudsql-docker/gce-proxy:1.11
            imagePullPolicy: IfNotPresent
            name: cloudsql
            resources: {}
            volumeMounts:
            - mountPath: /secrets/cloudsql
              name: secretname
              readOnly: true
          restartPolicy: OnFailure
          volumes:
          - name: secretname
            secret:
              defaultMode: 420
              secretName: secretname

它对我有用。
你们看到这种方法有什么缺点吗?

由于我认为它们与 CronJobs 相关并且很容易适应,这是我的解决方案: https :

它基于此处发布的其中一种解决方法,但使用preStop因为它旨在用于部署。 捕获边车会非常有效。

关注这个问题。 还在 cronjob 中使用 cloud_sql_proxy 容器作为边车
我使用了@stiko的超时实现

只是将@oxygen0211提出的关于使用 Replace 的解决方案添加到对话中,现在是一个不错的解决方法,如果您像我一样遇到这个问题,请务必检查一下。

https://github.com/kubernetes/kubernetes/issues/25908#issuecomment -327396198

我们已经获得了这个 KEP 的临时批准https://github.com/kubernetes/community/pull/2148 ,我们仍然有一些事情需要我们达成一致,但希望它能很快就可以开始工作. 请注意,KEP 将在 30 日转移到https://github.com/kubernetes/enhancements ,所以如果你想跟随它就会在那里。

在 sidecar 支持到来之前,您可以使用 docker 级别的解决方案,稍后可以轻松删除该解决方案: https :

它使用带有挂载的 docker 套接字和标准 kubernetes 标签的特权容器来管理作业中的容器。

我们在 Istio 和它的 side car 上遇到了同样的问题,我们决定做的是通过 curl + preStop 钩子像这样删除 pod

给你的工作这样一个最小的 RBAC 规则

apiVersion: v1
kind: ServiceAccount
metadata:
  name: myservice-job
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: myservice-role
rules:
  - apiGroups: [""]
    resources: ["pods"]
    verbs: ["delete"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: myservice-job-rolebinding
subjects:
  - kind: ServiceAccount
    name: myservice-job
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: myservice-role

POD_NAMEPOD_NAMESPACE到您的 ENV 像这样

   env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace

最后,添加一个 preStop 钩子,如

 lifecycle:
      preStop:
        exec:
          command: 
            - "/bin/bash" 
            - "-c"
            - "curl -X DELETE -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt https://$KUBERNETES_SERVICE_HOST/api/v1/namespaces/$POD_NAMESPACE/pods/$POD_NAME?gracePeriodSeconds=1"

有点凌乱,但比试图杀死正确的 docker 容器更安全、更不挑剔。

只是把它扔在这里,但我不久前把一个控制器放在一起,它旨在监控正在运行的 pod 更改,并适当地将 SIGTERM 发送到 sidecar 容器。 它绝对不是最强大的,老实说我已经有一段时间没有使用它了,但可能会有所帮助。

https://github.com/nrmitchi/k8s-controller-sidecars

感谢@jpalomaki at https://github.com/kubernetes/kubernetes/issues/25908#issuecomment -371469801 建议将cloud_sql_proxy作为部署与ClusterIP服务一起运行,并@ cvallancehttps://github.com/kubernetes/kubernetes/issues/25908#issuecomment -364255363关于设置尖端tcp:0.0.0.0cloud_sql_proxy instances参数允许非- 到进程的本地连接。 这些一起使得让 cron 作业使用代理变得轻松。

长期问题(自我说明)

同样的问题。 寻找有关如何使用GKE cron 作业和Cloud SQL的方法或官方文档

边注:
Google 更新了他们的 Cloud SQL -> Connecting from Google Kubernetes Engine文档,现在除了Connecting using the Cloud SQL Proxy Docker image您还可以Connecting using a private IP address
因此,如果您出于同样的原因来到这里(因为 cloud_sql_proxy),您现在可以使用 Private IP 的新功能

边注:
Google 更新了他们的 Cloud SQL -> Connecting from Google Kubernetes Engine文档,现在除了Connecting using the Cloud SQL Proxy Docker image您还可以Connecting using a private IP address
因此,如果您出于同样的原因来到这里(因为 cloud_sql_proxy),您现在可以使用 Private IP 的新功能

私有 IP 功能似乎需要删除整个集群并重新创建一个......?

@cropse仅当您的集群不是 VPC 原生时才需要。

我为这个问题制定了一个解决方法,不是很好的解决方案,但有效,希望在添加功能之前有所帮助,并且 VPC 是一种解决方法,但删除整个集群仍然很痛苦。

补充一下我的两分钱:如果由于 pod 永远不会完成而注入 istio sidecar,那么 helm 测试也会中断。

@dansivite您可以查看我的解决方法,我已经在我的项目中使用 helm 进行了测试。

期待看到这个实施! :)

当 Istio 代理被注入到普通作业中时,我们确实遇到了与普通作业相同的问题,除此之外,我们还希望这样做,因为我们想使用 Prow 运行 CI 作业。
例如,用于测试目的的 Rails 应用程序容器 + Sidecar 数据库容器。

@cropse谢谢。 我还没有尝试过,因为我们需要为所有测试配置它。 我们只是允许 Pod(不幸的是 Helm 测试不允许 Job)失败并依靠手动检查日志,直到这个问题得到长期修复。 但是,这也成为其他工作的问题,因此我们可能会重新考虑该职位。

仅供参考,此功能的跟踪问题在这里https://github.com/kubernetes/enhancements/issues/753如果人们想跟进,我们有一个 KEP,做了一些原型设计(有一个 POC 分支/视频),还需要敲定一些实现细节,才能进入可实现的状态。

边注:
Google 更新了他们的 Cloud SQL -> Connecting from Google Kubernetes Engine文档,现在除了Connecting using the Cloud SQL Proxy Docker image您还可以Connecting using a private IP address
因此,如果您出于同样的原因来到这里(因为 cloud_sql_proxy),您现在可以使用 Private IP 的新功能

由于同样的原因,我来到这里,但是,我们的 Cloud SQL 在此功能准备就绪之前就已配置。 我结合了以前的建议,并为我的 dbmate 迁移器舵图得出了这个(可能不理想,但它有效)。

      containers:
      - name: migrator
        image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
        imagePullPolicy: {{ .Values.image.pullPolicy }}
        command: ["/bin/bash", "-c"]
        args:
          - |
            /cloud_sql_proxy -instances={{ .Values.gcp.project }}:{{ .Values.gcp.region }}:{{ .Values.gcp.cloudsql_database }}=tcp:5432 -credential_file=/secrets/cloudsql/credentials.json &
            ensure_proxy_is_up.sh dbmate up
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: mysecret
              key: DATABASE_URL
        volumeMounts:
          - name: cloudsql-instance-credentials
            mountPath: /secrets/cloudsql
            readOnly: true
      volumes:
        - name: cloudsql-instance-credentials
          secret:
            secretName: cloudsql-instance-credentials

ensure_proxy_is_up.sh

#!/bin/bash

until pg_isready -d $(echo $DATABASE_URL); do
    sleep 1
done

# run the command that was passed in
exec "$@"

现在是在 Kubernetes 中引入 sidecar 容器的概念并允许根据非 sidecar 容器是否已完成来清理 pod 的好时机吗?

@Willux我在我的手机

@krancour感谢您的更新。 我一定是错过了那个细节。 最近这里没有太多活动,所以只是想确保有一些事情正在进行:)

作为参考,我制作了 @jmillikin-stripe 解决方法的 cloud-sql-proxy sidecar 版本,其中共享卷中的文件将状态传达给 sidecar。

它工作正常,但迄今为止我的 K8s 配置中最讨厌的 hack :(

apiVersion: batch/v1
kind: Job
metadata:
  name: example-job
spec:
  template:
    spec:
      containers:
      - name: example-job
        image: eu.gcr.io/example/example-job:latest
        command: ["/bin/sh", "-c"]
        args:
          - |
            trap "touch /tmp/pod/main-terminated" EXIT
            run-job.sh
        volumeMounts:
          - mountPath: /tmp/pod
            name: tmp-pod
      - name: cloudsql-proxy
        image: gcr.io/cloudsql-docker/gce-proxy:1.11
        command: ["/bin/sh", "-c"]
        args:
          - |
            /cloud_sql_proxy --dir=/cloudsql -instances=example:europe-west3:example=tcp:3306 -credential_file=/secrets/cloudsql/credentials.json &
            CHILD_PID=$!
            (while true; do if [[ -f "/tmp/pod/main-terminated" ]]; then kill $CHILD_PID; echo "Killed $CHILD_PID as the main container terminated."; fi; sleep 1; done) &
            wait $CHILD_PID
            if [[ -f "/tmp/pod/main-terminated" ]]; then exit 0; echo "Job completed. Exiting..."; fi
        volumeMounts:
          - name: cloudsql-instance-credentials
            mountPath: /secrets/cloudsql
            readOnly: true
          - name: cloudsql
            mountPath: /cloudsql
          - mountPath: /tmp/pod
            name: tmp-pod
            readOnly: true
      restartPolicy: Never
      volumes:
        - name: cloudsql-instance-credentials
          secret:
            secretName: cloudsql-instance-credentials
        - name: cloudsql
          emptyDir:
        - name: tmp-pod
          emptyDir: {}
  backoffLimit: 1

项目内部的任何人都可以评论这个问题的进展吗?

假设这是我们这些在 GKE 稳定发布渠道上工作的人的最佳选择,这是否公平,因为它可能至少在几个月内赶不上 Kubernetes 1.18?

@Datamance在这一点上,解决这个问题的 KEP看起来像是无限期搁置

我不久前发布了此评论,这是我的旧解决方案。 我不是要在这里推送我自己的东西,只是该评论已在 github 的“100 多条评论...”中丢失,并且认为重新显示它可能再次有用。

@nrmitchi感谢您

如果您将以下内容添加到 Pod 容器中,我们会想出一种不同的方法:

    securityContext:
            capabilities:
                   add:
                    - SYS_PTRACE

然后您将能够在其他容器中 grep PID,我们将在主容器的末尾运行以下内容:
sql_proxy_pid=$(pgrep cloud_sql_proxy) && kill -INT $sql_proxy_pid

@krancour很高兴它有所帮助。 如果您查看该存储库中的网络,则有几个分支几乎肯定比我原来的位置更好,并且可能更好地构建/使用。

IIRC柠檬水-hq 叉有一些有用的补充。

@nrmitchi ,我一直在看代码,但直接问你可能会更快......

您能否简要评论自述文件中未提及的任何先决条件?

例如,您的边车所基于的图像是否需要对此变通方法有任何特殊认识? 例如,他们是否需要在特定端口上监听来自控制器的信号? 或者也许它们必须包含某个外壳(bash?)

@krancour我将在我的回复前加上一条说明,即这个解决方案是几年前写的,我的记忆可能有点生疏。

当时的设计使得有问题的容器不需要知道变通方法。 我们主要在 sidecar 中使用第三方应用程序(例如,我认为stripe/veneur是其中之一),并且不想分叉/修改。

Sidecar 的唯一要求是它们正确监听 SIGTERM 信号,然后关闭。 我记得在 sidecar 中运行的第三方代码存在一些问题,这些代码期望不同的信号并且必须解决,但实际上控制器应该允许指定发送的信号(即,SIGINT 而不是 SIGTERM)。

他们不需要监听任何端口的信号,因为控制器使用exec直接向 sidecar 的主进程发出信号。 当时 IIRC 将该功能从 kubernetes 代码中复制出来,因为它不存在于客户端中。 我相信这现在存在于官方客户端中,可能应该更新。

如果您将以下内容添加到 Pod 容器中,我们会想出一种不同的方法:

    securityContext:
            capabilities:
                   add:
                    - SYS_PTRACE

然后您将能够在其他容器中 grep PID,我们将在主容器的末尾运行以下内容:
sql_proxy_pid=$(pgrep cloud_sql_proxy) && kill -INT $sql_proxy_pid

@ruiyang2015感谢这个黑客。
如果有人实施它,请务必了解在容器之间共享进程 ns 的含义

@nrmitchi

使用 exec 直接向 sidecar 的主进程发出信号

这就是我问的部分原因......我想,具体来说,我想知道这是否不适用于基于FROM scratch构建的图像的容器。

@krancour Fair 点,我从来没有去用scratch容器测试过它。 查看代码(或我的原始版本;这可能会在分叉中发生变化)看起来它将依赖于bash ,但应该能够修改。

它将依赖于 bash,但应该可以修改

当然,但只要它正在执行,它总是会依赖于容器中存在的某些二进制文件,对于临时容器,除了您明确放置的任何内容外,什么都没有。 🤷‍♂

鉴于该限制,我不能将其用于正在运行的容器可能完全是任意的并且由第三方指定的用例。 哦——然后我也有 Windows 容器。

我会提到我将要解决的问题。 对于大多数用例来说,这可能太严厉了,但我提到它是为了以防其他人的用例与我的用例相似,可以摆脱这个......

只要我先记录退出状态,我就可以简单地_删除_一个“主要”容器已经退出的 pod。 因此,我将最终编写一个控制器,该控制器将监视某些指定(通过注释)容器的完成情况,在已经跟踪“作业”状态的数据存储中记录其成功或失败,然后完全删除 pod。

为了更好的衡量,我可能会在 pod 删除上稍微延迟,以最大限度地提高我的中央日志聚合在主容器被鱼雷击中之前获得最后几行输出的机会。

严厉,但可能对某些人有用。

@krancour完全正确。 照原样,控制器不适用于任意使用基础。 老实说,我从来没有回过头来试图抽象一些实现来支持其他情况,因为我真的认为前面提到的 KEP 会被合并,并且对这个功能的需求没有实际意义。

鉴于这个问题已经有 4 年的历史了,KEP 还没有消失,最先进的技术是替换每个入口点的 hacky 内联 shell 脚本,我决定编写“标准”hack(共享卷中的墓碑) ) 转换为 Go 二进制文件,可以使用多阶段构建轻松烘焙到容器映像中。

https://github.com/karlkfi/kubexit

有几种使用方法:

  1. 将其烘焙到您的图像中
  2. 使用 init 容器和临时卷对其进行侧加载。
  3. 在每个节点上提供它并使用主机绑定安装将其加载到容器中

编辑: v0.2.0 现在支持“出生依赖”(延迟启动)和“死亡依赖”(自我终止)。

@vanzin如前所述,KEP 无限期搁置。

我的用例是 Vault 提供 CronJob 运行的凭据。 任务完成后,Vault sidecar 仍会在作业处于挂起状态的情况下运行,这会触发监控系统认为出现问题。 很遗憾 KEP 发生了什么事。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

sjenning picture sjenning  ·  3评论

ddysher picture ddysher  ·  3评论

theothermike picture theothermike  ·  3评论

jadhavnitind picture jadhavnitind  ·  3评论

cooligc picture cooligc  ·  3评论