本来 k8s 的 crontab 是启动一个容器来运行的,很简单,如下:

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: curl-cron
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: curl-cron
            image: radial/busyboxplus:curl
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date;echo "run crontab";curl http://www.baidu.com
          restartPolicy: OnFailure
  successfulJobsHistoryLimit: 2
  failedJobsHistoryLimit: 2

上面就跑了一个 busybox 的 pod,每分钟去访问百度,然后在 stdout 输出结果

这个没什么,注意上面文件的最后两行。限制成功以及失败 job 的 History 数量,如果不加限制,kubectl get pods 会看到无穷无尽的completed 状态的 curl-cron pod。



分割线,部署 crontab 的 yaml 很简单。但是,现在产品部有个需求,他们要在某一天进行促销活动,大概2天,期间会流量大增,于是想应用 hpa 来伸缩 pod,活动结束后关闭 hpa。之后他们还会有不断的促销,研发不知道什么时候开始,什么时候结束;但是研发又不想产品部看到 yaml,并且随时配合部署 hpa。

还有个问题,如果用crontab,那么活动开始运行一次,活动结束一次,最后研发还得清理删除掉这两个 crontab 来恢复正常。

这下麻烦了,有什么页面管理 crontab 的神器,能让产品部自己运行,然后权限分离,而且能指定再未来的某天运行一次或多次,且不用手工清理就好了。

还真的有一个,kala 就是了!!!

Kala 是一个cron管理配置工具,项目地址:

https://github.com/ajvb/kala

image-20211125164706478

卡拉是基于 Airbnb 的 Chronos 翻写的 Go 程序。

它比 crontab 更好一些的是,可以指定未来某天的一次性执行任务,有个使用界面

Kala 可以执行本地的命令,也可以发起 http 的请求。

注意,kala 的源代码里有一个地方需要修改,否则参数bolt-path不生效,我们改掉它自己编译个2进制程序出来。

Modify cmd/server.go

switch viper.GetString("jobdb") {
case "boltdb":
db = boltdb.GetBoltDB(viper.GetString("boltpath"))

Change to
db = boltdb.GetBoltDB(viper.GetString("bolt-path"))

Run it :
./kala serve --jobdb=boltdb --bolt-path=/data/db

我们用 Dockerfile 造一个镜像出来:

FROM alpine:3.12
RUN apk add --update bash && rm -rf /var/cache/apk/*

COPY . /data/

RUN mkdir /lib64 && \
    ln -s /lib/libc.musl-x86_64.so.1 /lib64/ld-linux-x86-64.so.2 && \
    mv /data/kubectl /bin && \
    mv /data/.kube /root && \
    rm -rf /data/Dockerfile

WORKDIR /data
EXPOSE 8000

CMD ["/data/kala", "serve","--jobdb=boltdb","--bolt-path=/data/db"]
#CMD /bin/sh -c "while true; do echo hi; sleep 10; done"

注意上面我们把 kubectl 和 .kube 配置一起放进了镜像,当然一些 yaml 和脚本也可以都打进去,然后推到阿里云镜像仓库去(注意安全问题)。

备注 :kala默认的端口是8000,访问 url 是 /webui

然后我们生成一个 secret 放进 k8s

htpasswd -c auth kala-auth
New password:
Re-type new password:
Adding password for user kala

kubectl create secret generic kala-auth --from-file=kala-auth
secret "kala-auth" created

kubectl get secret kala-auth -o yaml

然后我们定义一系列的资源文件,Deployment、SVC、Ingress,其中 ingress 引用了 kala-auth 的 secret,并且指定了 app-root 是 /webui/。

另外 kala 使用了 k8s-kala-5g 的持久化卷,务必事先准备好 pv 和 pvc,不持久化的话 pod 一重启,之前保存的配置信息就全没了。

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kala-deploy
  labels:
    app: kala
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kala
  template:
    metadata:
      labels:
        app: kala
    spec:
      containers:
      - name: kala
        image: registry.cn-shanghai.aliyun.com/xxx/kala:latest
        imagePullPolicy: Always
        ports:
        - containerPort: 8000
        volumeMounts:
        - mountPath: /data/db
          name: kala-data
      volumes:
        - name: kala-data
          persistentVolumeClaim:
            claimName: k8s-kala-5g
---
apiVersion: v1
kind: Service
metadata:
 name: kala-svc
 labels:
   app: kala
spec:
 ports:
 - name: http
   protocol: TCP
   port: 8000
   targetPort: 8000
 selector:
   app: kala
 type: ClusterIP
---
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: kala-com-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/block-cidrs: 111.201.134.93/32
    nginx.ingress.kubernetes.io/auth-type: basic
    nginx.ingress.kubernetes.io/auth-secret: kala-auth
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - Kala'
    nginx.ingress.kubernetes.io/app-root: '/webui/'
spec:
  rules:
  - host: kala.rendoumi.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kala-svc
          servicePort: 80

这样 kala 就可以从公网访问了,http://kala.rendoumi.com:8000/ ,且需要提供密码才能进入。

下面说一下具体的用法,还是有一些技巧性的,登录进去后的页面:

image-20211125170959532

进入后左上角点击 Create 建立新任务

image-20211125171027673

要填写内容如下:

  • Job name:起个名字,叫:增大HPA

  • Command:要运行的脚本:/data/resize.sh 35 (固定扩大ECI的脚本)

  • Schedule:R0/2021-07-07T06:00:00+08:00/PT0S

​ 这个最重要

​ 这行的格式是这样的:Number of times to repeat/Start Datetime/Interval Between Runs

​ R0 重复0次,即只执行一次,不重复

​ 2021-07-06T06:00:00+8:00 时间戳,注意时区中国+8:00

​ PT0S 无延迟0S启动

​ 合起来就是

​ R0/2021-07-07T06:00:00+08:00/PT0S 就是在2021年7月7日中国时间6点无任何延迟执行一次脚本 resize.sh

  • Reset Form 这个一定要选掉,保持空,否则下次进来上次的数据都没了,还得手动输入,麻烦

其他都不填写,然后 Create 就行了

然后我们可以再建一个恢复的任务

image-20211125171551356

我们去界面就可以看到多了两个任务,增大HPA和恢复HPA,这样就 OK 了

image-20211125171655096

注意:JOB一旦建立,以后就可以直接从界面上手动执行,Run Manually

image-20211125171831594

这东西真是个神器,产品部的同事可以随时修改hpa和恢复,而不用通过研发部了,善莫大焉。