我想为一个客户只运行一次容器,但用户体验不够简单。所以我用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的例子可供选择。