13

在Kubernetes的一个Pod内连续依次执行Container

 3 years ago
source link: https://note.qidong.name/2020/08/k8s-sequential-container-in-pod/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在Kubernetes的一个Pod内连续依次执行Container

2020-08-22 16:05:04 +08  字数:1633  标签: K8s

出于某些目的,有时需要在Kubernetes的一个Pod中,连续依次运行多个Container。 这种有明确结束预期的运行,即Kubernetes的Job。 但是,虽然一个Job可以在一个Pod内运行多个Container,然而运行方式是并发的。

一种方法是在业务层处理。 比如,通过共享的本地Volume,使用文件锁的机制,可以实现多个并发的Container依次执行。 但这需要在业务逻辑中,把并发强行改为同步,增加了开发复杂度。 如果能使用Kubernetes本身的机制实现,则减免了很大的开发工作量。

以下是另外的三种方案。

Kubernetes Job

经过调查发现,虽然containers不能依次运行,但是initContainers可以。 它是在containers运行前,执行的初始化操作,依次结束运行并且无异常后,正式的containers才会运行。 利用这一点,可以实现多个任务的依次执行,把前面的任务写到initContainers、最后一个任务写到containers即可。

以下为三个Containter依次执行的样例。

---
apiVersion: batch/v1
kind: Job
metadata:
  name: sequential-jobs
spec:
  backoffLimit: 0
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      activeDeadlinesSeconds: 60
      restartPolicy: Never
      initContainers:
        - name: job-1
          image: alpine:3.11
          command:
            - 'sh'
            - '-c'
            - >
              for i in 1 2 3;
              do
                echo "job-1 `date`";
                sleep 1s;
              done;
              echo code > /srv/input/code
          volumeMounts:
            - mountPath: /srv/input/
              name: input
        - name: job-2
          image: alpine:3.11
          command:
            - 'sh'
            - '-c'
            - >
              for i in 1 2 3;
              do
                echo "job-2 `date`";
                sleep 1s;
              done;
              cat /srv/input/code &&
              echo artifact > /srv/input/output/artifact
          resources:
            requests:
              cpu: 3
          volumeMounts:
            - mountPath: /srv/input/
              name: input
            - mountPath: /srv/input/output/
              name: output
      containers:
        - name: job-3
          image: alpine:3.11
          command:
            - 'sh'
            - '-c'
            - >
              echo "job-1 and job-2 completed";
              sleep 3s;
              cat /srv/output/artifact
          volumeMounts:
            - mountPath: /srv/output/
              name: output
      volumes:
        - name: input
          emptyDir: {}
        - name: output
          emptyDir: {}
      securityContext:
        runAsUser: 2000
        runAsGroup: 2000
        fsGroup: 2000
  • backoffLimit: 0,这句指定这个Job不要失败重启。
  • volumes这部分,使用了inputoutput两个emptyDir,作为输入输出。
  • securityContext可以在镜像默认用户不确定的情况下,使用指定UID进行Volume操作,避免对root的依赖。
  • activeDeadlinesSeconds指定了Job内Pod的超时时间。这个字段同样可以给到Job。
  • ttlSecondsAfterFinished指定了在多久以后,Job会被自动删除。

运行完毕后,日志如下:

$ kubectl logs sequential-jobs-r4725 job-1
job-1 Tue Jul 28 07:50:10 UTC 2020
job-1 Tue Jul 28 07:50:11 UTC 2020
job-1 Tue Jul 28 07:50:12 UTC 2020
$ kubectl logs sequential-jobs-r4725 job-2
job-2 Tue Jul 28 07:50:13 UTC 2020
job-2 Tue Jul 28 07:50:14 UTC 2020
job-2 Tue Jul 28 07:50:15 UTC 2020
code
$ kubectl logs sequential-jobs-r4725 job-3
job-1 and job-2 completed
artifact

Volcano

Volcano前身是kube-batch,声称在调度和管理方面,对原生Job进行了优化。 但是在核心逻辑上,还是一样的,不能支持指定Container顺序执行。

volcano-arch

状态转移图如下:

PendingAbortedRunningCompletedTerminated

在实际测试中,暂时没有发现在当前业务场景下,比原生Job有什么优势。 以下是实测配置。

---
apiVersion: batch.volcano.sh/v1alpha1
kind: Job
metadata:
  name: volcano-sequential-jobs
spec:
  minAvailable: 1
  schedulerName: volcano
  queue: default
  tasks:
    - replicas: 1
      name: "task-1"
      template:
        spec:
          restartPolicy: Never
          initContainers:
            - name: job-1
              image: alpine:3.11
              command:
                - 'sh'
                - '-c'
                - >
                  for i in 1 2 3;
                  do
                    echo "job-1 `date`";
                    sleep 1s;
                  done;
                  echo code > /srv/input/code
              volumeMounts:
                - mountPath: /srv/input/
                  name: input
            - name: job-2
              image: alpine:3.11
              command:
                - 'sh'
                - '-c'
                - >
                  for i in 1 2 3;
                  do
                    echo "job-2 `date`";
                    sleep 1s;
                  done;
                  cat /srv/input/code &&
                  echo artifact > /srv/input/output/artifact
              resources:
                requests:
                  cpu: 3
              volumeMounts:
                - mountPath: /srv/input/
                  name: input
                - mountPath: /srv/input/output/
                  name: output
          containers:
            - name: job-done
              image: alpine:3.11
              command:
                - 'sh'
                - '-c'
                - >
                  echo "job-1 and job-2 completed";
                  sleep 3s;
                  cat /srv/output/artifact
              volumeMounts:
                - mountPath: /srv/output/
                  name: output
          volumes:
            - name: input
              emptyDir: {}
            - name: output
              emptyDir: {}
          securityContext:
            runAsUser: 2000
            runAsGroup: 2000
            fsGroup: 2000

上面与原生相比,虽然多了tasks这一层概念,但是在功能上并无帮助。

运行完毕后,日志如下:

$ kubectl logs volcano-sequential-jobs-task-1-0 job-1
job-1 Tue Jul 28 07:53:17 UTC 2020
job-1 Tue Jul 28 07:53:18 UTC 2020
job-1 Tue Jul 28 07:53:20 UTC 2020
$ kubectl logs volcano-sequential-jobs-task-1-0 job-2
job-2 Tue Jul 28 07:53:21 UTC 2020
job-2 Tue Jul 28 07:53:22 UTC 2020
job-2 Tue Jul 28 07:53:23 UTC 2020
code
$ kubectl logs volcano-sequential-jobs-task-1-0 job-3
job-1 and job-2 completed
artifact

另外,Volcano的文档缺失严重,与kube-batch也不兼容。 目前看来有很多不清楚的问题。

argo

argo是更合适按顺序、依赖关系执行的。 但是经评估,它有一个重大缺陷,不适合当前场景。

argo的每个Task都是独立的Pod,不同Pod未必在同一台机器上。 而Volume则必须使用NFS之类的网络存储位置,性能不符合某些需要密集本地IO的场景。

总结

argo是形式上最合适的,可以避免使用initContainers这种邪道。 但是独立Pod的问题导致它不适合这个场景。

目前看来,原生的Job最合适。

选用Volcano还需要更深入的了解。

参考


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK