じゃあ、おうちで学べる

思考を呼び覚ます このコードに、君は抗えるか。

Kubernetes 上でsablier を用いてZero Scale を実現する 前編

前回のエントリー

syu-m-5151.hatenablog.com

はじめに

やはり、人は強欲らしいのでコンテナを使っているのに必要な時必要な分だけのリソースを起動させてほしいという願いを常に持っている。Kubernetes の場合はKnativeなどを利用すれば達成できる。sablierはリバースプロキシを利用してアクセスがない時は自動的にシャットダウンしてアクセスがあれば指定のコンテナを起動することができるツールです。 前回はdocker 上での動作確認を行った。引き続き今回はKubernetes 環境でのsablierの検証を行いました。今回はsablierTraefik 、各種ミドルウェアの設定ファイルに関しては言及してません。気合があれば後編として書いていきます。

sablier/hourglass.png at main · acouvreur/sablier · GitHub より

やってみる

公式サイトにはサンプルコード「Sablier Guide: Code-Server + Traefik + Kubernetes Ingress」としてKubernetes 上で Cloud Native なアプリケーションプロキシーTraefikKubernetes Ingressを用いたものが紹介されている。

k3s を用いて Kubernetes Cluster を作成する

以下の内容をdocker-compose.ymlというファイルにコピーして、docker compose up -dを実行します。

version: '3'
services:
  server:
    image: "rancher/k3s:v1.24.8-k3s1"
    command: server --no-deploy traefik
    tmpfs:
      - /run
      - /var/run
    ulimits:
      nproc: 65535
      nofile:
        soft: 65535
        hard: 65535
    privileged: true
    restart: always
    environment:
      - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
      - K3S_KUBECONFIG_MODE=666
    volumes:
      # This is just so that we get the kubeconfig file out
      - .:/output
    ports:
      - 6443:6443  # Kubernetes API Server
      - 8080:80  # Ingress controller port 80

docker compose up -dを実行します。

$ docker compose up -d
[+] Running 3/3
 ⠿ server Pulled
   ⠿ 73c47571f4bd Pull complete
   ⠿ 210e8c1c5e29 Pull complete
[+] Running 2/2
 ⠿ Network sablier-code-server-traefik-kubernetes_default     Created
 ⠿ Container sablier-code-server-traefik-kubernetes-server-1  Started

set -x KUBECONFIG ./kubeconfig.yaml:/Users/nwiizo/.kube/config のような設定が環境変数として入っているのでカレントディレクトリにあるkubeconfig.yamlKUBECONFIGとして優先的に実行される。そこでkubectl get node を実行するとCluster が準備できていることが分かる。

$ kubectl get node
NAME           STATUS     ROLES                  AGE     VERSION
58160ffa6e9b   Ready      control-plane,master   3m56s   v1.24.8+k3s1

Helmを用いたTraefikの作成

helm のインストールに関しては各自「helm install」とかで調べてほしい。とりあえず、traefikのHelmリポジトリを追加します。

$ helm repo add traefik https://helm.traefik.io/traefik
$ helm repo update

helm でデプロイするリソースは事前に確認しておいたほうがよいので確認しておきます。

$ helm show all traefik/traefik

デプロイをするのですが既存のHelm templateに自分が利用したい値を渡してデプロイします。templateに値を渡す方法は主に二つあります。

  1. values.yamlを利用者が用意する
  2. chartの利用者が helm install コマンド時に値を渡す(values.yamlの上書き可能)

今回はvalues.yaml を以下のように作成してデプロイを行う

image:
  tag: "2.9.1"

experimental:
  plugins:
    enabled: true

additionalArguments:
  - "--experimental.plugins.sablier.moduleName=github.com/acouvreur/sablier"
  - "--experimental.plugins.sablier.version=v1.1.1"

providers:
  kubernetesIngress:
    enabled: true
    allowEmptyServices: true

traefikチャートをvalues.yamlファイルとともにインストールします。また、kube-system というシステムコンポーネントやアドオンとして位置づけられているものをデプロイするためのNamespaceを用います。

$ helm install traefik traefik/traefik -f values.yaml --namespace kube-system
NAME: traefik
LAST DEPLOYED: Wed Nov 30 07:58:21 2022
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Traefik Proxy v2.9.5 has been deployed successfully
on kube-system namespace !

Sablier を作成していく

再三の説明になるのですがsablier はアプリケーションをシャットダウンさせたりしているアプリです。それ故に強い権限が必要になります。そのため、Sablier 用のサービスアカウント作成して、 Sablier のデプロイを行います。

sablier-sa.yaml というファイルで権限周りを一つにした。

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sablier
  namespace: kube-system
---
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
---
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

こちらをデプロイ

$ kubectl apply -f sablier-sa.yaml 
serviceaccount/sablier created
clusterrole.rbac.authorization.k8s.io/sablier created
clusterrolebinding.rbac.authorization.k8s.io/sablier created

sablier-deploy.yaml というファイルでリソース周りを一つにした。

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.1.1
        args:
        - "start"
        - "--provider.name=kubernetes"
        ports:
        - containerPort: 10000
---
apiVersion: v1
kind: Service
metadata:
  name: sablier
  namespace: kube-system
spec:
  selector:
    app: sablier
  ports:
    - protocol: TCP
      port: 10000
      targetPort: 10000

こちらもデプロイ

$ kubectl apply -f sablier-deploy.yaml 
deployment.apps/sablier-deployment created
service/sablier created

きちんとデプロイされているか確認する。また、kubectl -n kube-system logs -l=app=sablier でログを確認するのも良いと思う

$ kubectl -n kube-system get deployments -l=app=sablier
NAME                 READY   UP-TO-DATE   AVAILABLE   AGE
sablier-deployment   1/1     1            1           6m9s

アプリケーション本体のデプロイ

app-deployment.yaml でアプリケーションのリソースをデプロイします。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: code-server-deployment
  namespace: default
  labels:
    app: code-server
spec:
  replicas: 1
  selector:
    matchLabels:
      app: code-server
  template:
    metadata:
      labels:
        app: code-server
    spec:
      containers:
      - name: code-server
        image: codercom/code-server:4.8.3
        ports:
        - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: code-server-service
  namespace: default
spec:
  selector:
    app: code-server
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 8080

kubectlk というalias を貼っている。手癖でこうなったのでブログでも記載しておく。リソースの確認をk get pod したらさっさと次に行く

$ k apply -f app-deployment.yaml 
deployment.apps/code-server-deployment created
service/code-server-service created

Sablier PluginによるTraefik経由でのIngressの設定を行う

app-ingress.yaml でデプロイする

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: code-server-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
  - host: localhost
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: code-server-service
            port:
              number: 8080

http://localhost:8080 にアクセスできたと思います。 その後、アプリケーションのレプリカセットを0にします。がこれは削除ではないです。

$ k scale deployment code-server-deployment --replicas=0
deployment.apps/code-server-deployment scaled
# 削除されたわけではないので確認できる
$  k get deployments/code-server-deployment 
NAME                     READY   UP-TO-DATE   AVAILABLE   AGE
code-server-deployment   0/0     0            0           12m

app-sablier-middleware.yaml をデプロイする。sessionDuration: 2m に設定をしたので2分後には落ちるはずです。

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
  name: code-server-sablier
  namespace: default
spec:
  plugin:
    sablier:
      names: deployment_default_code-server-deployment_1
      sablierUrl: 'http://sablier:10000'
      sessionDuration: 2m
      dynamic:
        displayName: 'Code Server Demo'
        showDetails: true
        theme: hacker-terminal
        refreshFrequency: 5s
$ k apply -f app-sablier-middleware.yaml
$ k get middleware
NAME                  AGE
code-server-sablier   2m5s

その後にapp-ingress-patch.yaml を作成し、kubectl patch ingress code-server-ingress --patch-file app-ingress-patch.yamlIngressにパッチを当てます。

metadata:
  annotations:
    traefik.ingress.kubernetes.io/router.middlewares: default-code-server-sablier@kubernetescrd

パッチを当てた直後はアクセスがないのでpod 数は0です。

$ k get pod 
No resources found in default namespace.

しかし、traefik 及びsablier の動作によってhttp://localhost:8080 に何もせずにアクセスできました。 この時に関連している各種ログを確認すると動作していることがわかります。

$ k get pod 
NAME                                      READY   STATUS    RESTARTS   AGE
code-server-deployment-7f56554786-j4b69   1/1     Running   0          2m44s

そして、2分後にはシャットダウンされていると思います。

# -w で継続的にウォッチする
$ k get po -w
NAME                                      READY   STATUS    RESTARTS   AGE
code-server-deployment-7f56554786-t5j8x   1/1     Running   0          36s
code-server-deployment-7f56554786-t5j8x   1/1     Terminating   0          2m17s
code-server-deployment-7f56554786-t5j8x   0/1     Terminating   0          2m18s
code-server-deployment-7f56554786-t5j8x   0/1     Terminating   0          2m18s
code-server-deployment-7f56554786-t5j8x   0/1     Terminating   0          2m18s

さいごに

本来やりたかった。Kubernetes 環境での動作確認までできました。此処から先は皆さんの環境に合うようにいくつかの設定ファイルを見ていく会を本来やれれば良かったですが眠いのでおやすみです。

作業リポジトリ

github.com