42

我确定我遗漏了一些明显的东西。我查看了 Kubernetes 上的 ScheduledJobs / CronJobs 的文档,但我找不到按计划执行以下操作的方法:

  1. 连接到现有 Pod
  2. 执行脚本
  3. 断开

我有其他方法可以做到这一点,但他们感觉不对。

  1. 安排一个 cron 任务: kubectl exec -it $(kubectl get pods --selector=some-selector | head -1) /path/to/script

  2. 创建一个部署,其中包含一个包含应用程序的“Cron Pod”,以及许多只是应用程序的“非 Cron Pod”。Cron Pod 将使用不同的映像(一个计划了 cron 任务的映像)。

如果可能的话,我更喜欢使用 Kubernetes ScheduledJobs 来防止同一个 Job 一次运行多次,并且因为它让我觉得这是一种更合适的方式。

ScheduledJobs / CronJobs 有没有办法做到这一点?

http://kubernetes.io/docs/user-guide/cron-jobs/

4

6 回答 6

11

据我所知,没有“官方”方式可以按照您想要的方式执行此操作,我相信这是设计使然。Pod 应该是短暂的和水平可扩展的,而 Jobs 旨在退出。将 cron 作业“附加”到现有 pod 不适合该模块。调度程序不知道作业是否完成。

相反,Job 可以启动应用程序的一个实例,专门用于运行 Job,然后在 Job 完成后将其删除。为此,您可以为作业使用与部署相同的图像,但通过设置使用不同的“入口点” command:

如果他们的工作需要访问由您的应用程序创建的数据,那么这些数据将需要在应用程序/Pod 之外持久化,您可以通过几种方式实现,但显而易见的方式是数据库或持久卷。例如,使用数据库看起来像这样:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: APP
spec:
  template:
    metadata:
      labels:
        name: THIS
        app: THAT
    spec:
      containers:
        - image: APP:IMAGE
          name: APP
          command:
          - app-start
          env:
            - name: DB_HOST
              value: "127.0.0.1"
            - name: DB_DATABASE
              value: "app_db"

还有一个连接到同一个数据库但具有不同“入口点”的作业:

apiVersion: batch/v1
kind: Job
metadata:
  name: APP-JOB
spec:
  template:
    metadata:
      name: APP-JOB
      labels:
        app: THAT
    spec:
      containers:
      - image: APP:IMAGE
        name: APP-JOB
        command:
        - app-job
        env:
          - name: DB_HOST
            value: "127.0.0.1"
          - name: DB_DATABASE
            value: "app_db"

或者持久卷方法看起来像这样:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: APP
spec:
  template:
    metadata:
      labels:
        name: THIS
        app: THAT
    spec:
      containers:
        - image: APP:IMAGE
          name: APP
          command:
          - app-start
          volumeMounts:
          - mountPath: "/var/www/html"
            name: APP-VOLUME
      volumes:
        - name:  APP-VOLUME
          persistentVolumeClaim:
            claimName: APP-CLAIM

---

apiVersion: v1
kind: PersistentVolume
metadata:
  name: APP-VOLUME
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Retain
  nfs:
    path: /app

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: APP-CLAIM
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 10Gi
  selector:
    matchLabels:
      service: app

使用这样的作业,附加到同一卷:

apiVersion: batch/v1
kind: Job
metadata:
  name: APP-JOB
spec:
  template:
    metadata:
      name: APP-JOB
      labels:
        app: THAT
    spec:
      containers:
      - image: APP:IMAGE
        name: APP-JOB
        command:
        - app-job
        volumeMounts:
        - mountPath: "/var/www/html"
          name: APP-VOLUME
    volumes:
      - name:  APP-VOLUME
        persistentVolumeClaim:
          claimName: APP-CLAIM
于 2017-12-27T15:04:43.427 回答
8

创建一个使用 Kubernetes API 的计划 pod,通过该exec函数在目标 pod 上运行您想要的命令。pod 映像应该包含用于访问 API 的客户端库——其中许多是可用的,或者您可以构建自己的。

例如,这是一个使用 Python 客户端的解决方案,它执行到每个 ZooKeeper pod 并运行数据库维护命令:

import time

from kubernetes import config
from kubernetes.client import Configuration
from kubernetes.client.apis import core_v1_api
from kubernetes.client.rest import ApiException
from kubernetes.stream import stream
import urllib3

config.load_incluster_config()

configuration = Configuration()
configuration.verify_ssl = False
configuration.assert_hostname = False
urllib3.disable_warnings()
Configuration.set_default(configuration)

api = core_v1_api.CoreV1Api()
label_selector = 'app=zk,tier=backend'
namespace = 'default'

resp = api.list_namespaced_pod(namespace=namespace,
                               label_selector=label_selector)

for x in resp.items:
  name = x.spec.hostname

  resp = api.read_namespaced_pod(name=name,
                                 namespace=namespace)

  exec_command = [
  '/bin/sh',
  '-c',
  'opt/zookeeper/bin/zkCleanup.sh -n 10'
  ]

  resp = stream(api.connect_get_namespaced_pod_exec, name, namespace,
              command=exec_command,
              stderr=True, stdin=False,
              stdout=True, tty=False)

  print("============================ Cleanup %s: ============================\n%s\n" % (name, resp if resp else "<no output>"))

以及相关的 Dockerfile:

FROM ubuntu:18.04

ADD ./cleanupZk.py /

RUN apt-get update \
  && apt-get install -y python-pip \
  && pip install kubernetes \
  && chmod +x /cleanupZk.py

CMD /cleanupZk.py

请注意,如果您有一个启用 RBAC 的集群,您可能需要创建一个服务帐户和适当的角色才能使此 API 调用成为可能。如下所示的角色足以列出 pod 并运行 exec,例如上面的示例脚本需要:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-list-exec
  namespace: default
rules:
  - apiGroups: [""] # "" indicates the core API group
    resources: ["pods"]
    verbs: ["get", "list"]
  - apiGroups: [""] # "" indicates the core API group
    resources: ["pods/exec"]
    verbs: ["create", "get"]

相关 cron 作业的示例:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: zk-maint
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: zk-maint-pod-list-exec
  namespace: default
subjects:
- kind: ServiceAccount
  name: zk-maint
  namespace: default
roleRef:
  kind: Role
  name: pod-list-exec
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: zk-maint
  namespace: default
  labels:
    app: zk-maint
    tier: jobs
spec:
  schedule: "45 3 * * *"
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: zk-maint
            image: myorg/zkmaint:latest
          serviceAccountName: zk-maint
          restartPolicy: OnFailure
          imagePullSecrets:
          - name: azure-container-registry
于 2019-06-25T18:48:31.783 回答
2

这似乎是一种反模式。为什么你不能把你的工作 pod 作为一个工作 pod 运行?

无论如何,您似乎都非常确信您需要这样做。这就是我要做的。

使用您的工作 pod 并将您的 shell 执行包装在一个简单的 web 服务中,使用几乎任何语言都需要 10 分钟。公开端口并将服务放在该工人/工人面前。然后你的工作 pod 可以简单地 curl ..svc.cluster.local:/ (除非你已经使用 dns)。

于 2018-07-18T15:19:42.673 回答
1

我设法通过使用 doctl(DigitalOcean 的命令行界面)和 kubectl 创建自定义图像来做到这一点。CronJob 对象将使用这两个命令来下载集群配置并对容器运行命令。

这是一个示例 CronJob:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: drupal-cron
spec:
  schedule: "*/5 * * * *"
  concurrencyPolicy: Forbid
  jobTemplate:
    spec:
      template:
        spec:
          containers:
            - name: drupal-cron
              image: juampynr/digital-ocean-cronjob:latest
              env:
                - name: DIGITALOCEAN_ACCESS_TOKEN
                  valueFrom:
                    secretKeyRef:
                      name: api
                      key: key
              command: ["/bin/bash","-c"]
              args:
                - doctl kubernetes cluster kubeconfig save drupster;
                  POD_NAME=$(kubectl get pods -l tier=frontend -o=jsonpath='{.items[0].metadata.name}');
                  kubectl exec $POD_NAME -c drupal -- vendor/bin/drush core:cron;
          restartPolicy: OnFailure

这是 CronJob 使用的 Docker 映像:https ://hub.docker.com/repository/docker/juampynr/digital-ocean-cronjob

如果您不使用 DigitalOcean,请弄清楚如何下载集群配置以便kubectl使用它。例如,使用 Google Cloud,您必须下载gcloud.

这是我实现此https://github.com/juampynr/drupal8-do的项目存储库。

于 2020-06-11T09:25:00.317 回答
1

听起来您可能希望在 pod 本身内运行计划的工作,而不是在 Kubernetes 级别执行此操作。我会使用传统的 Linux crontab 将其作为容器内的 cronjob 来处理。考虑:

kind: Pod
apiVersion: v1
metadata:
  name: shell
spec:
  init-containers:
  - name: shell
    image: "nicolaka/netshoot"
    command:
    - /bin/sh
    - -c
    - |
      echo "0 */5 * * * /opt/whatever/bin/do-the-thing" | crontab -
      sleep infinity

如果您想跟踪来自这些进程的日志,则需要一种流利的机制来跟踪这些日志文件。

于 2022-02-01T20:40:59.420 回答
-1

这个应该有帮助。

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/30 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            kubectl exec -it  <podname> "sh script.sh ";
          restartPolicy: OnFailure
于 2018-10-03T19:05:11.530 回答