web-dev-qa-db-de.com

Cron Jobs in Kubernetes - Verbinden Sie sich mit dem vorhandenen Pod und führen Sie das Skript aus

Ich bin mir sicher, dass mir etwas offensichtlich fehlt. Ich habe die Dokumentation für ScheduledJobs/CronJobs auf Kubernetes durchgesehen, finde jedoch keinen Weg, um Folgendes nach einem Zeitplan auszuführen:

  1. Verbinden Sie sich mit einem vorhandenen Pod
  2. Führen Sie ein Skript aus
  3. Trennen

Ich habe alternative Methoden, aber sie fühlen sich nicht richtig an. 

  1. Planen Sie eine Cron-Aufgabe für: kubectl exec -it $ (kubectl get pods --selector = some-selector | head -1)/path/to/script

  2. Erstellen Sie eine Bereitstellung mit einem "Cron-Pod", in dem sich auch die Anwendung befindet, und viele "Nicht-Cron-Pods", die nur die Anwendung darstellen. Der Cron Pod würde ein anderes Image verwenden (eines mit Cron-Tasks).

Ich würde es vorziehen, wenn möglich die Kubernetes ScheduledJobs zu verwenden, um zu verhindern, dass derselbe Job mehrmals auf einmal ausgeführt wird, und auch, weil es mir als die geeignetere Methode erscheint.

Gibt es eine Möglichkeit, dies mit ScheduledJobs/CronJobs zu tun?

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

17
Aeisor

Dies scheint ein Anti-Muster zu sein. Warum können Sie Ihre Worker Pod nicht einfach als Job Pod betreiben?

Unabhängig davon scheinen Sie ziemlich überzeugt zu sein, dass Sie dies tun müssen. Folgendes würde ich tun.

Nehmen Sie Ihren Worker-Pod und verpacken Sie Ihre Shell-Ausführung in einem einfachen Webservice. Es dauert 10 Minuten mit nahezu jeder Sprache. Legen Sie den Hafen frei und stellen Sie einen Mitarbeiter vor diesen Arbeitern. Dann können Ihre Job-Pods einfach mit ..svc.cluster.local:/gerollt werden:/(es sei denn, Sie haben mit dns geputzt).

1
Brett Wagner

Das sollte helfen.

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

Soweit mir bekannt ist, gibt es keinen "offiziellen" Weg, dies so zu tun, wie Sie es wünschen, und das ist, glaube ich, von Design. Die Pods sollen flüchtig und horizontal skalierbar sein, und Jobs sollen beenden. Wenn ein Cron-Job an einen vorhandenen Pod "angehängt" wird, passt dieses Modul nicht. Der Scheduler hat keine Ahnung, ob der Job abgeschlossen ist.

Stattdessen kann ein Job eine Instanz Ihrer Anwendung aufrufen, die speziell für die Ausführung des Jobs ausgeführt wird, und nach dem Abschluss des Jobs heruntergefahren. Dazu können Sie dasselbe Image für den Job wie für Ihre Bereitstellung verwenden, jedoch einen anderen "Entrypoint" verwenden, indem Sie command: einstellen.

Wenn der Job auf Daten zugreifen muss, die von Ihrer Anwendung erstellt werden, müssen diese Daten außerhalb der Anwendung/des Pods gespeichert werden. Sie können dies auf verschiedene Arten tun, aber die offensichtlichen Möglichkeiten wären eine Datenbank oder ein dauerhaftes Volume Die Verwendung einer Datenbank würde ungefähr so ​​aussehen:

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"

Und ein Job, der eine Verbindung zu derselben Datenbank herstellt, jedoch mit einem anderen "Entrypoint":

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"

Oder der Ansatz für beständige Datenträger würde ungefähr so ​​aussehen:

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

Bei einem Job wie diesem, der sich auf demselben Volume befindet:

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
1
Simon I

Erstellen Sie einen geplanten Pod, der mithilfe der Kubernetes-API den gewünschten Befehl über die Funktion exec auf den Ziel-Pods ausführt. Das Pod-Image sollte die Client-Bibliotheken enthalten, um auf die API zuzugreifen - viele davon sind verfügbar oder Sie können Ihre eigenen erstellen.

Hier ist beispielsweise eine Lösung mit dem Python Client, der auf jedem ZooKeeper-Pod ausgeführt wird und einen Datenbankwartungsbefehl ausführt:

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>"))

und das zugehörige Dockerfile:

FROM ubuntu:18.04
LABEL maintainer "reDock Inc."

ADD ./cleanupZk.py /

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

CMD /cleanupZk.py

Beachten Sie, dass Sie bei einem RBAC-fähigen Cluster möglicherweise ein Dienstkonto und entsprechende Rollen erstellen müssen, um diesen API-Aufruf zu ermöglichen. Eine Rolle wie die folgende reicht aus, um Pods aufzulisten und exec auszuführen, wie es das obige Beispielskript erfordert:

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"]

Ein Beispiel für den zugehörigen Cron-Job:

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
0
Raman