背景

我的测试环境有两个Deployment,我想在开发的时候启动,30分钟没请求后自动关闭,不占用资源。

Sablier就解决了这个问题,它支持Kubernetes,并且支持Traefik。

环境

traefik: v2.9.10
kubernetes: v1.24.6+k3s1
Sablier: v1.7.0

安装

在K3S中,我们已经内置了Traefik,所以如果想安装插件,我们需要对内置的Traefik进行改造,添加插件。

由于K3S会周期性的执行 /var/lib/rancher/k3s/server/manifests 目录下的所有yaml,而如果我们修改了默认的traefik.yaml,K3S会自动恢复默认文件,所以我们这里需要新建一个yaml文件,用HelmChartConfig的方式修改traefik安装的配置即可。

创建配置文件 nano /var/lib/rancher/k3s/server/manifests/traefik-config.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
additionalArguments:
- "--experimental.plugins.sablier.modulename=github.com/acouvreur/sablier"
- "--experimental.plugins.sablier.version=v1.7.0-beta.7"
- "--providers.kubernetescrd=true"
- "--providers.kubernetesingress=true"
experimental:
plugins:
enabled: true
providers:
kubernetesCRD:
allowCrossNamespace: true
kubernetesIngress:
allowEmptyServices: true
image:
name: "rancher/mirrored-library-traefik"
tag: "2.9.10"
  • additionalArguments用于添加插件的启动参数,在启动Traefik时,他会自动下载,需要注意的是,他是自主访问github下载的,如果网络不通畅,下载插件会失败,但Traefik会正常启动,导致所有使用了fail2ban的Middleware全部失效,所以启动后还要确保有没有fail2ban的成功日志信息
  • experimental用于启动插件(新版本好像不配置也可以)
  • allowCrossNamespace允许跨命名空间调用(比如我在A命名空间创建了有关fail2ban的Middleware,但我想在B命名空间同样使用这个Middleware,所以需要允许跨命名空间调用)
  • allowEmptyServices启用后,服务不可用返回5xx,如果不启用,服务返回404(该插件必须必须启用)
  • 因为自带的Traefik版本比较低,所以指定下新版本的镜像,亲测在2.10版本中无法使用,所以只能升级到2.9版本的,在自带的2.6版本同样无法使用,会报plugin: unknown plugin type: fail2ban的错误,重启也无效

部署

  1. 创建Sablier用户文件 sablier-sa.yaml,然后执行kubectl apply -f sablier-sa.yaml

    1
    2
    3
    4
    5
    apiVersion: v1
    kind: ServiceAccount
    metadata:
    name: sablier
    namespace: kube-system
  2. 创建Sablier角色文件 sablier-cr.yaml,然后执行kubectl apply -f sablier-cr.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
    name: sablier
    namespace: kube-system
    rules:
    - apiGroups:
    - apps
    - ""
    resources:
    - deployments
    - deployments/scale
    - statefulsets
    - statefulsets/scale
    verbs:
    - patch # Scale up and down
    - get # Retrieve info about specific deployment or statefulset
    - update # Scale up and down
    - list # Events
    - watch # Events
  3. 创建Sablier角色绑定文件 sablier-crb.yaml,然后执行kubectl apply -f sablier-crb.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
    name: sablier
    namespace: kube-system
    roleRef:
    apiGroup: rbac.authorization.k8s.io
    kind: ClusterRole
    name: sablier
    subjects:
    - kind: ServiceAccount
    name: sablier
    namespace: kube-system
  4. 创建Sablier Deployment文件 sablier-deployment.yaml,然后执行kubectl apply -f sablier-deployment.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    apiVersion: apps/v1
    kind: Deployment
    metadata:
    name: sablier-deployment
    namespace: kube-system
    labels:
    app: sablier
    spec:
    replicas: 1
    selector:
    matchLabels:
    app: sablier
    template:
    metadata:
    labels:
    app: sablier
    spec:
    serviceAccountName: sablier
    serviceAccount: sablier
    containers:
    - name: sablier
    image: acouvreur/sablier:1.7.0
    args:
    - "start"
    - "--provider.name=kubernetes"
    ports:
    - containerPort: 10000
  5. 创建Sablier Service文件 sablier-service.yaml,然后执行kubectl apply -f sablier-service.yaml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    apiVersion: v1
    kind: Service
    metadata:
    name: sablier
    namespace: kube-system
    spec:
    selector:
    app: sablier
    ports:
    - protocol: TCP
    port: 10000
    targetPort: 10000
  6. 使用kubectl -n kube-system get deployments 查看Sablier是否创建成功:

    1
    2
    3
    4
    5
    6
    NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
    local-path-provisioner 1/1 1 1 8m25s
    coredns 1/1 1 1 8m25s
    metrics-server 1/1 1 1 8m24s
    traefik 1/1 1 1 3m25s
    sablier-deployment 1/1 1 1 90s

    或者使用 kubectl -n kube-system logs deployments/sablier-deployment 查看是否启动成功:

    1
    2
    3
    4
    time="2022-11-14T01:40:49Z" level=info msg="(version=1.1.1, branch=HEAD, revision=a913bc2a3b0f4aca5b9ac7ddc9af5428ef411dba)"
    time="2022-11-14T01:40:49Z" level=info msg="using provider \"kubernetes\""
    time="2022-11-14T01:40:49Z" level=info msg="initialized storage to /etc/sablier/state.json"
    time="2022-11-14T01:40:49Z" level=info msg="server listening :10000"

    现在,在cluster中,就可以用http://sablier:10000访问了。

Middleware

创建Traefik Middleware 文件code-server-sablier-middleware.yaml 然后执行 kubectl apply -f code-server-sablier-middleware.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-api-sablier
namespace: test-app
spec:
plugin:
sablier:
names: deployment_test-app_test-api_1,deployment_test-app_test-service_1
sablierUrl: 'http://sablier:10000'
sessionDuration: 30m
dynamic:
displayName: 'test'
showDetails: true
theme: hacker-terminal
refreshFrequency: 5s

其中spec.plugin.sablier.names是我们侦测的服务,因为我的两个都是Deployment,所以deployment开头,然后是命名空间test-app,然后是服务名test-api,最后因为我只有1个pod,所以根据pod名称再加上1,最后这些用_连接即可,多个服务用逗号分割

最终的规则如下:类型_命名空间_服务名_1,对于其他的配置,详细查看官方文档

接下来添加到你的Ingress即可

1
2
3
4
5
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
traefik.ingress.kubernetes.io/router.middlewares: test-app-test-api-sablier@kubernetescrd

测试

直接访问服务即可,你会看到加载页,pod处于健康后,流量会从sablier转到对应服务中,session到期后,pod数量会自动缩减为0,然后自动删除。经测试,有时候是不太准确启动的,比如启动了第一个,第二个有很小的几率没启动,好像是因为version的原因导致启动失败了,总之,这尽量用于不太重要的服务或者前端页面,对于后端而言还是测试环境吧~