修复Kubernetes上一次性任务的用户体验(附实例)

·  阅读 100
修复Kubernetes上一次性任务的用户体验(附实例)

Fixing the UX for one-time tasks on Kubernetes

我想为一个客户只运行一次容器,但用户体验不够简单。所以我用Go和KubernetesAPI创建了一个新的OSS工具。

部署通常会重启,Pod也会。你可以将 "重启政策 "设置为 "从不",但对于那些旨在启动--运行和完成的东西来说,它仍然不是很合适。

为什么你想运行一个一次性的工作?

这里有几个想法:

  • 在集群内的特定服务账户下运行kubectl
  • 检查curl ,在集群内工作
  • 检查节点间的DNS是否工作
  • 清理一个数据库索引
  • 用nmap进行网络扫描
  • 你通常会在cron上运行的任务
  • 动态DNS更新
  • 生成每周一次的反馈/报告/更新/同步
  • 用Kaniko运行一个容器的构建

另一个用例可能是你正在运行bash,但需要在集群中执行某些任务,并在进一步行动之前得到结果。

我将向你展示今天使用Job的工作情况,以及我们如何通过一点Go代码使其变得更好。

作业是什么样子的

所以我又看了看Kubernetes Job,这是一个我很少看到的API。它们看起来有点像这样:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  suspend: true
  parallelism: 1
  completions: 5
  template:
    spec:
      containers:
      - name: pi
        image: perl:5.34.0
        command: ["perl",  "-Mbignum=bpi", "-wle", "print bpi(2000)"]
      restartPolicy: Always
  backoffLimit: 4
复制代码

因此,整体规格与Pod相似,但它不熟悉,以至于在手工组成这些时导致语法错误。

更重要的是:一个作业不是你运行后就能得到它的日志,然后继续你的一天。

你必须描述这个工作,找到一个它用半随机的名字创建的Pod,然后从那里获得日志:

kubectl describe jobs/myjob

Name:           myjob
...
Events:
  Type    Reason            Age   From            Message
  ----    ------            ----  ----            -------
  Normal  SuccessfulCreate  12m   job-controller  Created pod: myjob-hlrpl
  Normal  SuccessfulDelete  11m   job-controller  Deleted pod: myjob-hlrpl
  Normal  Suspended         11m   job-controller  Job suspended
  Normal  SuccessfulCreate  3s    job-controller  Created pod: myjob-jvb44
  Normal  Resumed           3s    job-controller  Job resumed
复制代码

所以你最终会得到这样的东西:

apiVersion: batch/v1
kind: Job
metadata:
  name: checker
  namespace: openfaas
spec:
  completions: 1
  parallelism: 1
  template:
    metadata:
      name: checker
    spec:
      serviceAccount: openfaas-checker
      containers:
      - name: checker
        image: ghcr.io/openfaas/config-checker:latest
        imagePullPolicy: Always
      restartPolicy: Never
复制代码

而在我的例子中,我也想要RBAC

然后你就会:

  • 应用RBAC文件
  • 应用作业
  • 描述该任务
  • 找到Pod的名字
  • 获取Pod日志
  • 删除作业/花苞
#!/bin/bash

JOBUUID=$(kubectl get job -n openfaas $JOBNAME -o "jsonpath={.metadata.labels.controller-uid}")
PODNAME=$(kubectl get po -n openfaas -l controller-uid=$JOBUUID -o name)

kubectl logs -n openfaas $PODNAME > $(date '+%Y-%m-%d_%H_%M_%S').txt

kubectl delete -n openfaas $PODNAME
kubectl delete -f ./artifacts/job.yaml
复制代码

我们能做得更好吗?

我认为我们可以。而在前世,一直到2017年,在Kubernetes赢得我之前,我一直在使用Docker Swarm。

我写了一个叫 "jaas "的小工具,结果发现它很受欢迎,人们用它来完成一次性的任务,并在Docker Swarm上运行。

查看代码:alexellis/jaas

我还记得Stefan Prodan,他是我的一个朋友,也是OpenFaaS过去的贡献者,曾经有这种痒,并创造了一个叫kjob的东西。

Stefan已经三年没有碰过代码了,但他的方法是把CronJob作为一个模板,然后加入一些改动。这很灵活,因为它意味着你可以对规格做任何你想做的事情,然后你可以覆盖一两个字段。

但我想摆脱不太知名的Kubernetes API,并获得一个可以简单的用户体验,如。

kubectl apply -f ./artifacts/rbac.yaml
run-job ./job.yaml -o report.txt
复制代码

输入run-job

于是,run-job就这样诞生了。

我之前的例子变成了

name: checker
image: ghcr.io/openfaas/config-checker:latest
namespace: openfaas
service_account: openfaas-checker
复制代码

而这是用run-job -f job.yaml 来运行的。

然后我想,让我们把它变得更有趣。OpenFaaS中的cows函数是最受欢迎的演示之一,也是jaas代码库的一部分。

下面是它的Docker文件。

FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine:3.16 as builder

ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH

RUN mkdir -p /home/app

RUN apk add --no-cache nodejs npm

# Add non root user
RUN addgroup -S app && adduser app -S -G app
RUN chown app /home/app

WORKDIR /home/app
USER app

COPY package.json    .
RUN npm install --omit=dev
COPY index.js        .

CMD ["/usr/bin/node", "./index.js"]
复制代码

它是多架构的,你可以使用buildx或faas-cli来编译它,以适应各种架构。

$ cat <<EOF > cows.yaml
# Multi-arch image for arm64, amd64 and armv7l
image: alexellis2/cows:2022-09-05-1955
name: cows
EOF
复制代码

这里是它的外观:

$ run-job -f cows.yaml

        ()  ()
         ()()
         (oo)
  /-------UU
 / |     ||
*  ||w---||
   ^^    ^^
Eh, What's up Doc?
复制代码

通过kubectl get events -w ,我们可以看到幕后实际发生的事情:

0s          Normal   SuccessfulCreate   job/cows         Created pod: cows-5qld5
0s          Normal   Scheduled          pod/cows-5qld5   Successfully assigned default/cows-5qld5 to k3s-eu-west-agent-1
0s          Normal   Pulling            pod/cows-5qld5   Pulling image "alexellis2/cows:2022-09-05-1955"
0s          Normal   Pulled             pod/cows-5qld5   Successfully pulled image "alexellis2/cows:2022-09-05-1955" in 877.707423ms
1s          Normal   Created            pod/cows-5qld5   Created container cows
0s          Normal   Started            pod/cows-5qld5   Started container cows
0s          Normal   Completed          job/cows         Job completed
复制代码

一个在工作中使用的例子

但我们也可以用一次性任务来做工作上的事情。

想象一下,你想获得所有Kubernetes节点的元数据。

你需要一个RBAC文件来做这个:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: kubectl-run-job
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  labels:
    app: run-job
  name: kubectl-run-job
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs:
  - get
  - list
  - watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  labels:
    app: run-job
  name: kubectl-run-job
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kubectl-run-job
subjects:  
  - kind: ServiceAccount
    name: kubectl-run-job
    namespace: default
---
复制代码

那么你就需要一个预装了kubectl的容器镜像,所以我们来做这个:

FROM --platform=${TARGETPLATFORM:-linux/amd64} alpine:3.16 as builder

ARG TARGETPLATFORM
ARG BUILDPLATFORM
ARG TARGETOS
ARG TARGETARCH

RUN mkdir -p /home/app

# Add non root user
RUN addgroup -S app && adduser app -S -G app
RUN chown app /home/app

RUN apk add --no-cache curl && curl -SLs https://get.arkade.dev | sh

WORKDIR /home/app
USER app

RUN arkade get kubectl@v1.24.1 --quiet

CMD ["/home/app/.arkade/bin/kubectl", "get", "nodes", "-o", "wide"]
复制代码

我们还需要为这个任务创建一个YAML文件:

name: get-nodes
image: alexellis2/kubectl:2022-09-05-2243
namespace: default
service_account: kubectl-run-job
复制代码

最后,我们要运行这个作业:

$ kubectl apply ./examples/kubectl/rbac.yaml
$ run-job -f ./examples/kubectl/kubectl_get_nodes_job.yaml

Created job get-nodes.default (4097ed06-9422-41c2-86ac-6d4a447d10ab)
....
Job get-nodes.default (4097ed06-9422-41c2-86ac-6d4a447d10ab) succeeded 
Deleted job get-nodes

Recorded: 2022-09-05 21:43:57.875629 +0000 UTC

NAME           STATUS   ROLES                       AGE   VERSION        INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
k3s-server-1   Ready    control-plane,etcd,master   25h   v1.24.4+k3s1   192.168.2.1   <none>        Raspbian GNU/Linux 10 (buster)   5.10.103-v7l+      containerd://1.6.6-k3s1
k3s-server-2   Ready    control-plane,etcd,master   25h   v1.24.4+k3s1   192.168.2.2   <none>        Raspbian GNU/Linux 10 (buster)   5.10.103-v7l+      containerd://1.6.6-k3s1
k3s-server-3   Ready    control-plane,etcd,master   25h   v1.24.4+k3s1   192.168.2.3   <none>        Raspbian GNU/Linux 10 (buster)   5.10.103-v7l+    containerd://1.6.6-k3s1
复制代码

现在,你也可以在作业文件中指定命令和参数,所以你可以在JSON中获得相同的输出,并通过管道将其传递给jq,从而在bash中实现自动化:

name: get-nodes
image: alexellis2/kubectl:2022-09-05-2243
namespace: default
service_account: kubectl-run-job
command:
 - kubectl
args:
 - get nodes
 - -o
 - json
复制代码

总结

run-job的真正意义在于使在Kubernetes上运行一个一次性的容器比精心设计YAML和输入半打kubectl命令的经历更简单。

如果你认为它可能对你有用,那么run-job可以通过 arkade get run-job以及其他100多个CLI,下载速度比brew快得多。

我们也有了对 repo 的第一个贡献,那就是在YAML文件中添加 "command "和 "args "选项。我们的想法并不是要复制Pod和Job规范中的每一个字段,而是要让 "run-job -f job-file.yaml "在支持客户和运行临时任务时足够有用和方便。

在GitHub上的明星:alexellis/run-job

在OpenFaaS有限公司,我们已经和一个客户一起使用run-job来检查他们的OpenFaaS部署和功能的配置。容器是一个Go程序,你可能想看一下,以适应它来检查你自己的系统?

对于我们这些对从代码中操作和查询Kubernetes集群感兴趣的人来说,Go客户端提供了一个非常好的体验,有数百个OSS的例子可供选择。

分类:
后端
标签:
收藏成功!
已添加到「」, 点击更改