zaki work log

作業ログやら生活ログやらなんやら

[Kubernetes] 軽量ディストリビューションk3sのコンテナ版の紹介

本記事は「エーピーコミュニケーションズ Advent Calendar 2022」の21日目のエントリです。

軽量Kubernetesディストリビューションの一つにk3sというものがあります。
以下のコマンドで簡単にクラスタをデプロイでき、少ないリソースで動作し、プラットフォームもARMにも対応しているので幅広く使用できます。(Raspberry Piとか、Oracle Cloud無料枠のARMコンピュートインスタンスとか)

curl -sfL https://get.k3s.io | sh - 

k3s.io

実はこのk3sはコンテナ版もあって、Linux OSに直接インストールするのでなく、Dockerコンテナとしてノードを動作させることもできます。Docker上でKubernetesをデプロイできるkindと同じで、Kubernetes in Dockerの構成。

コンテナ版k3sのデプロイには、Docker Composeを使う方法と、CLIツールを使う方法があり、本記事ではそれぞれ紹介します。
CLIの方はkindと使い勝手的にはかなり似ています。k3sは「停止」が用意されているので「使いたいときだけ動かす」がやりやすいかも。(kindだとdocker stopを直接実行するしかない…よね)

CLIを使った構築

k3dインストール

k3dコマンドのインストール

$ curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
Preparing to install k3d into /usr/local/bin
k3d installed into /usr/local/bin/k3d
Run 'k3d --help' to see what you can do with it.

他にもプラットフォームにあわせてHomebrewやChocolateyを使用可能。
他のバージョンを使いたいなどは、インストールドキュメント参照

クラスタのデプロイ

最も単純な1ノード(コントロールプレーン・ワーカーのオールインワンシングルノード構成)の場合はオプション指定も特になくcreateのみでOK
(※この場合コントロールプレーンノードのスケールはあとからできない。後述)

$ k3d cluster create
INFO[0000] Prep: Network                                
INFO[0000] Created network 'k3d-k3s-default'            
INFO[0000] Created image volume k3d-k3s-default-images  
INFO[0000] Starting new tools node...                   
INFO[0001] Creating node 'k3d-k3s-default-server-0'     
INFO[0001] Pulling image 'ghcr.io/k3d-io/k3d-tools:5.4.6' 
INFO[0003] Starting Node 'k3d-k3s-default-tools'        
INFO[0003] Pulling image 'docker.io/rancher/k3s:v1.24.4-k3s1' 
INFO[0016] Creating LoadBalancer 'k3d-k3s-default-serverlb' 
INFO[0017] Pulling image 'ghcr.io/k3d-io/k3d-proxy:5.4.6' 
INFO[0020] Using the k3d-tools node to gather environment information 
INFO[0021] HostIP: using network gateway 172.18.0.1 address 
INFO[0021] Starting cluster 'k3s-default'               
INFO[0021] Starting servers...                          
INFO[0021] Starting Node 'k3d-k3s-default-server-0'     
INFO[0027] All agents already running.                  
INFO[0027] Starting helpers...                          
INFO[0027] Starting Node 'k3d-k3s-default-serverlb'     
INFO[0033] Injecting records for hostAliases (incl. host.k3d.internal) and for 2 network members into CoreDNS configmap... 
INFO[0036] Cluster 'k3s-default' created successfully!  
INFO[0036] You can now use it like this:                
kubectl cluster-info

マルチノードの場合はオプションを指定します。

$ k3d cluster create --servers 3 --agents 2

起動するとホストOSの$HOME/.kube/configが生成・更新されるので、kubectlはすぐ使用可能。ノード状態は以下の通り。

zaki@dev-server:~$ kubectl get node
NAME                       STATUS   ROLES                       AGE   VERSION
k3d-k3s-default-agent-0    Ready    <none>                      26s   v1.24.4+k3s1
k3d-k3s-default-agent-1    Ready    <none>                      26s   v1.24.4+k3s1
k3d-k3s-default-server-0   Ready    control-plane,etcd,master   60s   v1.24.4+k3s1
k3d-k3s-default-server-1   Ready    control-plane,etcd,master   46s   v1.24.4+k3s1
k3d-k3s-default-server-2   Ready    control-plane,etcd,master   33s   v1.24.4+k3s1

クラスタの状態

K3dとしてのクラスタ一覧

$ k3d cluster list
NAME          SERVERS   AGENTS   LOADBALANCER
k3s-default   1/1       0/0      true

コンテナの状態。
コントロールプレーンノード以外にLBやAPIサーバーとして動作するコンテナが1つ起動しています。

$ docker ps
CONTAINER ID   IMAGE                            COMMAND                  CREATED              STATUS              PORTS                             NAMES
cb7ba9c9a6ed   ghcr.io/k3d-io/k3d-proxy:5.4.6   "/bin/sh -c nginx-pr…"   About a minute ago   Up 54 seconds       80/tcp, 0.0.0.0:43515->6443/tcp   k3d-k3s-default-serverlb
75a6838bc838   rancher/k3s:v1.24.4-k3s1         "/bin/k3d-entrypoint…"   About a minute ago   Up About a minute                                     k3d-k3s-default-server-0

K8sとしてのクラスタ情報

$ kubectl cluster-info
Kubernetes control plane is running at https://0.0.0.0:43515
CoreDNS is running at https://0.0.0.0:43515/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
Metrics-server is running at https://0.0.0.0:43515/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
zaki@dev-server:~$ kubectl get node
NAME                       STATUS   ROLES                  AGE     VERSION
k3d-k3s-default-server-0   Ready    control-plane,master   3m20s   v1.24.4+k3s1
zaki@dev-server:~$ kubectl get pod -A
NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
kube-system   local-path-provisioner-7b7dc8d6f5-tt94v   1/1     Running     0          3m8s
kube-system   coredns-b96499967-q5kbw                   1/1     Running     0          3m8s
kube-system   helm-install-traefik-crd-5w7mv            0/1     Completed   0          3m8s
kube-system   helm-install-traefik-xqjkn                0/1     Completed   1          3m8s
kube-system   svclb-traefik-50cc15de-xt892              2/2     Running     0          2m40s
kube-system   metrics-server-668d979685-j4j62           1/1     Running     0          3m8s
kube-system   traefik-7cd4fcff68-78nhc                  1/1     Running     0          2m41s

ノードをあとから追加する

ワーカー(agent)

ワーカーノードを1台追加するには以下のコマンド。

$ k3d node create agent
zaki@dev-server:~$ k3d node list
NAME                       ROLE           CLUSTER       STATUS
k3d-agent-0                agent          k3s-default   running
k3d-k3s-default-server-0   server         k3s-default   running
k3d-k3s-default-serverlb   loadbalancer   k3s-default   running
zaki@dev-server:~$ kubectl get node
NAME                       STATUS   ROLES                  AGE   VERSION
k3d-k3s-default-server-0   Ready    control-plane,master   12d   v1.24.4+k3s1
k3d-agent-0                Ready    <none>                 30s   v1.24.4+k3s1

コントロールプレーン(server)

コントロールプレーンを追加するには--roleオプションを指定する。(デフォルトagentなのでワーカーが追加される)

$ k3d node create master --role server

ただし、「クラスタ作成時に1サーバーノードで作成した場合(--servers nを指定しなかった場合)」は以下の通りエラーとなります。

zaki@dev-server:~$ k3d node create master --role server
INFO[0000] Adding 1 node(s) to the runtime local cluster 'k3s-default'... 
INFO[0000] Using the k3d-tools node to gather environment information 
INFO[0000] Starting new tools node...                   
INFO[0000] Starting Node 'k3d-k3s-default-tools'        
INFO[0000] HostIP: using network gateway 172.22.0.1 address 
INFO[0000] Starting Node 'k3d-master-0'                 
WARN[0001] warning: encountered fatal log from node k3d-master-0 (retrying 0/10): �time="2022-12-18T03:49:11Z" level=fatal msg="starting kubernetes: preparing server: https://k3d-k3s-default-server-0:6443/v1-k3s/server-bootstrap: 400 Bad Request" 
WARN[0001] warning: encountered fatal log from node k3d-master-0 (retrying 1/10): �time="2022-12-18T03:49:12Z" level=fatal msg="starting kubernetes: preparing server: https://k3d-k3s-default-server-0:6443/v1-k3s/server-bootstrap: 400 Bad Request" 

[snip]

WARN[0006] warning: encountered fatal log from node k3d-master-0 (retrying 9/10): �time="2022-12-18T03:49:16Z" level=fatal msg="starting kubernetes: preparing server: https://k3d-k3s-default-server-0:6443/v1-k3s/server-bootstrap: 400 Bad Request" 
FATA[0006] failed to add 1 node(s) to the runtime local cluster 'k3s-default': failed to add one or more nodes: failed to run node 'k3d-master-0': failed to start node 'k3d-master-0': Node k3d-master-0 failed to get ready: error waiting for log line `k3s is up and running` from node 'k3d-master-0': stopped returning log lines: node k3d-master-0 is running=true in status=restarting

🔥 There’s a trap!

If your cluster was initially created with only a single server node, then this will fail. That’s because the initial server node was not started with the --cluster-init flag and thus is not using the etcd backend.

Adding server nodes to a running cluster

構成のコード化

kindと同様に、クラスタ作成時のオプションを設定ファイルに記述することもできます。
例えばコントロールプレーン3台、ワーカー2台のクラスタであれば以下の通り。

apiVersion: k3d.io/v1alpha4
kind: Simple
metadata:
  name: mycluster
servers: 3
agents: 2

このファイルをk3d-config.yamlとして用意し、クラスタ作成時に以下コマンドを実行すれば指定の構成でデプロイされます。

$ k3d cluster create -c k3d-config.yaml 
$ k3d cluster list
NAME        SERVERS   AGENTS   LOADBALANCER
mycluster   3/3       2/2      true

他にもオプションで指定できる様々な値をYAMLで指定できます。
詳細はドキュメント参照。

k3d.io

Docker Composeを使った構築

もう一つの構築方法がDocker Composeを使った方法。

前提としてDockerとDocker Composeがインストール済みであること。
(現在はdocker-composeコマンドでなくDockerのプラグインとしてインストールする。手順通りにインストールすればdocker-compose-pluginパッケージに含まれるので使用可能になっているはず)

docs.docker.com

Composeを使ったコンテナ版k3sのデプロイ方法はドキュメントはあまり詳しく載っておらず、「Running K3s in Docker」や、「日本語版K3sマニュアル(PDF版)」の49ページ「8.9 K3d(Docker で動く k3s)を docker-compose で動かす」という項で確認できます。(が、情報量が少ない)

Composeファイル

デプロイに使用するComposeファイルはk3sリポジトリのルートにあります。

github.com

内容を見ればわかりますが、server(コントロールプレーン)と、agent(ワーカー)の2つのノードが起動する設定になっています。
また、server上でAPIサーバーやIngress Controllerが動作するためのポートのpublish設定なども確認できます。

クラスタのデプロイ

Docker Composeの通常の操作で、docker compose upを実行してデプロイします。
ただし準備として、コントロールプレーンとワーカーが互いに通信するためのトークンを環境変数で設定しておく必要があります。

[zaki@cloud-dev2 k3s-compose]$ export K3S_TOKEN=${RANDOM}${RANDOM}${RANDOM} 
[zaki@cloud-dev2 k3s-compose]$ 
[zaki@cloud-dev2 k3s-compose]$ docker compose up -d
[+] Running 4/4
 ⠿ Network k3s-compose_default      Created                                                               0.2s
 ⠿ Volume "k3s-compose_k3s-server"  Created                                                               0.0s
 ⠿ Container k3s-compose-server-1   Started                                                               1.0s
 ⠿ Container k3s-compose-agent-1    Started                                                               1.0s
[zaki@cloud-dev2 k3s-compose]$ 
[zaki@cloud-dev2 k3s-compose]$ docker compose ps
NAME                   COMMAND             SERVICE             STATUS              PORTS
k3s-compose-agent-1    "/bin/k3s agent"    agent               running             
k3s-compose-server-1   "/bin/k3s server"   server              running             0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:6443->6443/tcp, :::6443->6443/tcp

デフォルトの動作では、kubeconfig.yamlはカレントディレクトリに生成されるので、--kubeconfigで指定すれば(またはKUBECONFIG環境変数指定)kubectlが使用可能。

[zaki@cloud-dev2 k3s-compose]$ kubectl get node --kubeconfig ./kubeconfig.yaml 
NAME           STATUS   ROLES                  AGE   VERSION
11d52309842b   Ready    control-plane,master   48s   v1.24.3+k3s1
36e609703847   Ready    <none>                 44s   v1.24.3+k3s1

K3S_TOKENの値はComposeファイル内に指定の方法が記載されています
頻繁に作成と削除を繰り返す場合は、$HOME/.bashrc等で指定しておくのもアリ。ランダムである必要もないので、適当な固定値でもOK

マルチノードの制限

マルチノードについては、Compose版はいろいろ試した限り制限がありそう。

起動時のノード指定

ワーカーノードのみマルチノードにする場合はdocker compose up--scaleオプションで対応可能。
以下のコマンドであれば3ワーカーノードのクラスタをデプロイできます。

$ docker compose up -d --scale agent=3

ただし、コントロールプレーンはポートのpublish設定があるため、単純に--scaleしても起動しません。APIサーバー用のコンテナが起動してコントロールプレーンへのLBとして動作するCLI版と異なり、おそらくコンテナ自身がAPIサーバーとして動作する設計になっているため、スケールできないと思われます。
(たぶんCLI版と同じ構成になるようにComposeファイルを作成すれば動く…かも。未検証)

起動後のノード追加

これはできるかと思ったけど今のところうまく行かなかった。
running中の状態でdocker compose up -d --scale agent=Xすると、動作的には新しいワーカーノードは追加されるが、元からrunningだったノードがNot Readyになってしまった。
これも現状未解決。

CLI版と同じ動きになるようにdocker runでネットワークやボリュームを手動で指定しつつ既存クラスタへ追加することは、技術的には可能と思われるが、こちらも未検証。

まとめ

軽量Kubernetesであるk3sのコンテナ版を2パターン紹介しました。コンテナの特徴である作って壊すが簡単にできるので、検証用途などには特に使い勝手は良いと思います。それぞれ特徴があるので簡単に私見でまとめ。

資料

K3dだったりk3dだったり、公式の表記ゆれが多くてどっちがただしいんだ。。