kuKubernetes の Install Tools というページでは、kubernetes を local で動かすための tool として kind が紹介されています。今日はこの kind について内部構造及び使い方を見てみます。
kind とは
kind は「Docker container の中で Kubernetes を動かすことが出来るツール」です。kind という命名は「Kubernetes in Docker」から来ていて、K-in-D という頭文字をとったものになっています。
kind については KubeCon + CloudNativeCon で何度か紹介されているようです。例えば KubeCon + CloudNativeCon North America 2019 における以下の “Deep Dive: Kind” というトーク では、「kind とは何か?」について内部実装など含めて紹介されています。
上記のトークについて簡単に summary を書くと、kind は以下のようなものとして紹介されています。
- Docker container として Node(をシミュレートする container)を動かし、その中で Kubernetes を動かすツール
- Node image の中に、Kubernetes を動かすために必要な全てを詰める
- kubelet
- kubeadm
- docker
- systemd
- core images (Kuberentes にとって重要な container image)
- etcd
- coredns
- pause
- kube-apiserver
- etc.
- Node image の中に、Kubernetes を動かすために必要な全てを詰める
- multi-node 構成も可能
- Kubernetes を source code から build して動かすことが可能
- 30s 程度で Kuberenetes cluster を作ることが出来る
- kind は「Kuberentes 自体の test」のために作られた
- Kuberenetes の CI は Kubernetes で動いており、全ての test は Pod の中で実行される。そのため、Kuberenetes 自体を test するには、「Kuberentes を container の中で動かす」必要があった。
上記のトークの中で出てくる以下のスライドが、kind の仕組みを端的に示していて分かりやすいと思います。“Node” Container の中で、kubelet や containerd, そして containered を通じて起動された「Kuberentes の動作を支える各種 container」が動いている事が分かります。
ここまでで、「kind の内部構造」を説明しました。次に、実際に kind を動かしてみましょう。
kind を動かしてみる
kind の利用方法は実はとても簡単で、README で以下のように言及されている通り「GO111MODULE="on" go get sigs.k8s.io/kind@v0.9.0 && kind create cluster
を実行するだけ」となっています。
If you have go (1.11+) and docker installed
GO111MODULE="on" go get sigs.k8s.io/kind@v0.9.0 && kind create cluster
is all you need!
実際に実行すると、以下のように Kubernetes cluster 作成が進みます。自分の環境では、Kuberentes cluster 作成にかかる時間は 1min 強でした。
(なお、Ensuring node image
という step で少し時間がかかりますが、これは後述する「kind が利用する kindest/node
という docker image の pull に時間がかかっている」だけです。一度 image pull が完了すると次からは 30s 程度で高速に Kubernetes cluster が作成出来る様になります)。
$ GO111MODULE="on" go get sigs.k8s.io/kind@v0.9.0 && kind create cluster
go: downloading sigs.k8s.io/kind v0.9.0
go: downloading github.com/spf13/cobra v1.0.0
go: downloading k8s.io/apimachinery v0.18.8
go: downloading gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776
go: downloading github.com/mattn/go-isatty v0.0.12
go: downloading github.com/alessio/shellescape v1.2.2
go: downloading sigs.k8s.io/yaml v1.2.0
go: downloading github.com/pelletier/go-toml v1.8.0
go: downloading github.com/evanphx/json-patch v0.0.0-20200808040245-162e5629780b
go: downloading golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed
go: downloading github.com/evanphx/json-patch/v5 v5.1.0
GO111MODULE="on" go get sigs.k8s.io/kind@v0.9.0 8.93s user 3.64s system 102% cpu 12.228 total
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.19.1) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/
kind create cluster 4.95s user 2.65s system 9% cpu 1:16.71 total
これで既に「Kubernetes cluster が Docker container の中で動いている状態」となっています。簡単ですね!
kind で動く Kubernetes cluster を利用してみる
次に、実際に kind で動く Kuberentes cluster を利用してみましょう。
$ kind create cluster
を実行した際の message に Set kubectl context to "kind-kind"
と出ていたように、既に kubectl の context は「kind で作成した Kubenetes cluster(= kind-kind
という名前の cluster)」に切り替わっています。つまり、この状態で $ kubectl
を利用すれば、kind-kind
cluster の API server に対して通信が行われるようになっています。
実際、以下のように $ kubectl config current-context
や $ kubectl cluster-info
の結果を見てみると「local で動く kind-kind
cluster に context が切り替わっている」事が確認出来ます。
$ kubectl config current-context
kind-kind
$ kubectl cluster-info
Kubernetes control plane is running at https://127.0.0.1:55999
KubeDNS is running at https://127.0.0.1:55999/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
$ kubectl
で cluster 内の k8s object を見てみましょう。例えば namespace や pod, service を見てみると、以下のような内容になっています。
$ kubectl get namespaces
NAME STATUS AGE
default Active 11m
kube-node-lease Active 11m
kube-public Active 11m
kube-system Active 11m
local-path-storage Active 11m
$ kubectl get po --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-f9fd979d6-9cgbl 1/1 Running 0 11m
kube-system coredns-f9fd979d6-wlnmw 1/1 Running 0 11m
kube-system etcd-kind-control-plane 1/1 Running 0 11m
kube-system kindnet-phgz9 1/1 Running 0 11m
kube-system kube-apiserver-kind-control-plane 1/1 Running 0 11m
kube-system kube-controller-manager-kind-control-plane 1/1 Running 0 11m
kube-system kube-proxy-dxx9q 1/1 Running 0 11m
kube-system kube-scheduler-kind-control-plane 1/1 Running 0 11m
local-path-storage local-path-provisioner-78776bfc44-66wln 1/1 Running 0 11m
$ kubectl get svc --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 18m
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 18m
さらに、適当に deployment と service の追加もしてみましょう。自分が昔作った https://github.com/south37/dumper という「request header を print するだけの Docker container」を動かしてみます。
まず、deployment と service の manifest file を用意します。
# dumper-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: dumper
name: dumper
namespace: default
spec:
selector:
matchLabels:
app: dumper
template:
metadata:
annotations:
labels:
app: dumper
name: dumper
spec:
containers:
- image: south37/dumper
livenessProbe:
httpGet:
path: /ping
port: 8080
name: dumper
ports:
- containerPort: 8080
name: http
readinessProbe:
httpGet:
path: /ping
port: 8080
# dumper-service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: dumper
name: dumper
namespace: default
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
selector:
app: dumper
type: ClusterIP
次に、これらの manfest file を apply します。
$ kubectl apply -f dumper-deployment.yaml
deployment.apps/dumper created
$ kubectl apply -f dumper-service.yaml
service/dumper created
apply したものがちゃんと作られている事を確認します。
$ kubectl get all -n default
NAME READY STATUS RESTARTS AGE
pod/dumper-6465654fdc-qn729 1/1 Running 0 118s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/dumper ClusterIP 10.110.60.42 <none> 80/TCP 114s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 21m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/dumper 1/1 1 1 118s
NAME DESIRED CURRENT READY AGE
replicaset.apps/dumper-6465654fdc 1 1 1 118s
まず、pod の log を見てみます。すると、healthcheck 用の GET /ping
の reqeust が来ている事が確認できます。
$ kubectl logs dumper-6465654fdc-qn729
.
.
.
2020/12/30 12:09:55 GET /ping HTTP/1.1
2020/12/30 12:09:55 Host: 10.244.0.5:8080
2020/12/30 12:09:55 User-Agent: kube-probe/1.19
2020/12/30 12:09:55 Accept-Encoding: gzip
2020/12/30 12:09:55 Connection: close
次に、Kuberentes cluster の中から Service を通した request をしてみましょう。 curl が入っている docker image として radial/busyboxplus:curl
を使うことにします(注: radial/busyboxplus:curl
は https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/ の中でも利用されてるので、安全な image だと判断して利用してます)。
すると、以下のように Kubernetes cluster 内の pod から $ curl http://dumper.default
で 「default
namespace の dumper
service へ HTTP request」をして、ちゃんと response が返る事を確認出来ました!
$ kubectl run --rm -it busybox --image=radial/busyboxplus:curl
If you don't see a command prompt, try pressing enter.
[ root@busybox:/ ]$ curl http://dumper.default
Hello, dumper!
pod の log を見てみると、ちゃんと上記 request が pod に到達していることも確認できます。
$ kubectl logs dumper-6465654fdc-qn729
.
.
.
2020/12/30 12:12:00 GET / HTTP/1.1
2020/12/30 12:12:00 Host: dumper.default
2020/12/30 12:12:00 User-Agent: curl/7.35.0
2020/12/30 12:12:00 Accept: */*
簡単にではありますが、Kubernetes cluster としての動作が確認できました。
Docker container として動く Node container の中を見てみる
次に、kind の動作をもう少し深掘りしてみます。具体的には、Deep Dive: Kind - KubeCon + CloudNativeCon North America 2019 の中で紹介されていた「Node container の動作」をもう少し見てみます。
まず、Kubernetes cluster を一度削除して作り直すことにします。これは、「作成されたばかりの状態の Kubernetes cluster」で実験するための操作です(特に気にならない人は不要です)。
$ kind delete cluster
Deleting cluster "kind" ...
$ kind create cluster
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.19.1) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/
kind create cluster 4.62s user 2.53s system 19% cpu 36.518 total
次に、$ kind create cluster
を実行した後の状態で $ docker ps
すると、kindest/node:v1.19.1
という image の container が起動している事が分かります。これが、kind が利用する「Node container」のようです。
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
45a383679dd2 kindest/node:v1.19.1 "/usr/local/bin/entr…" About a minute ago Up 59 seconds 127.0.0.1:56856->6443/tcp kind-control-plane
$ docker exec
して Node container の中をみてみましょう。$ ps fax
で process 一覧を見てみると、Deep Dive: Kind - KubeCon + CloudNativeCon North America 2019 の中で説明されていた通り、Kubernetes の動作を支える様々な process が起動している事が分かります。containerd
や kubelet
など一部を除くと、そのほかの process は /usr/local/bin/containerd-shim-runc-v2
経由で起動している(= container として起動している)ことも分かります。
$ docker exec -it 45a383679dd2 bash
root@kind-control-plane:/# ps fax
PID TTY STAT TIME COMMAND
2060 pts/1 Ss 0:00 bash
2189 pts/1 R+ 0:00 \_ ps fax
1 ? Ss 0:00 /sbin/init
124 ? S<s 0:00 /lib/systemd/systemd-journald
135 ? Ssl 0:11 /usr/local/bin/containerd
310 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 1c426469eb3ae09b744f1c116e6798c65886e218271dfa105fba747b4bfde0d3 -address /run/containerd/containerd.soc
405 ? Ss 0:00 \_ /pause
502 ? Ssl 0:06 \_ kube-scheduler --authentication-kubeconfig=/etc/kubernetes/scheduler.conf --authorization-kubeconfig=/etc/kubernetes/scheduler.conf --bind-address=127.0.0.1 --ku
311 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 23d54713b4401fb309725273c78961ea5d7f3a5be157d2a139813b9b9611e220 -address /run/containerd/containerd.soc
418 ? Ss 0:00 \_ /pause
620 ? Ssl 0:20 \_ etcd --advertise-client-urls=https://172.19.0.2:2379 --cert-file=/etc/kubernetes/pki/etcd/server.crt --client-cert-auth=true --data-dir=/var/lib/etcd --initial-a
318 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id a24a84c14111721de85b657f2bb0b89db44d87e97452ef86690060a0f0fcd3bc -address /run/containerd/containerd.soc
425 ? Ss 0:00 \_ /pause
563 ? Ssl 1:08 \_ kube-apiserver --advertise-address=172.19.0.2 --allow-privileged=true --authorization-mode=Node,RBAC --client-ca-file=/etc/kubernetes/pki/ca.crt --enable-admissi
373 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 8a6a56e83a590f4958dbd3262e4b0adbe36acad229ebcb54ef13c657f39c2c0a -address /run/containerd/containerd.soc
433 ? Ss 0:00 \_ /pause
548 ? Ssl 0:24 \_ kube-controller-manager --allocate-node-cidrs=true --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf --authorization-kubeconfig=/etc/kubernetes
667 ? Ssl 0:25 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --cont
797 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 841020b0c947c1a8c2047a542268bf3dd91f8fb6b15cfc0a993dc61c0769e660 -address /run/containerd/containerd.soc
819 ? Ss 0:00 \_ /pause
898 ? Ssl 0:00 \_ /usr/local/bin/kube-proxy --config=/var/lib/kube-proxy/config.conf --hostname-override=kind-control-plane
833 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 6171f628b9c0658a1983b198160fe76b53eeb4118aa8a0c71491ff5516453268 -address /run/containerd/containerd.soc
864 ? Ss 0:00 \_ /pause
948 ? Ssl 0:00 \_ /bin/kindnetd
1110 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 1fd32fb832a5e3782d1f39b26b99a30de8872a0a69600a77db3631f274eeb819 -address /run/containerd/containerd.soc
1153 ? Ss 0:00 \_ /pause
1226 ? Ssl 0:03 \_ /coredns -conf /etc/coredns/Corefile
1112 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 725b634f7fc5d93ebfb1e63afb8aabcd936e6297ed7ab552e314e1cc90efeec5 -address /run/containerd/containerd.soc
1160 ? Ss 0:00 \_ /pause
1229 ? Ssl 0:01 \_ local-path-provisioner --debug start --helper-image k8s.gcr.io/build-image/debian-base:v2.1.0 --config /etc/config/config.json
1350 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 9d07db434b356d9fddb2ae31715adc1209406a604b7068560f379832f5d79ba2 -address /run/containerd/containerd.soc
1373 ? Ss 0:00 \_ /pause
1404 ? Ssl 0:03 \_ /coredns -conf /etc/coredns/Corefile
containerd で起動している container は、containerd の CLI tool である ctr
で管理する事が出来るはずです。少し見てみましょう。
まず、namespace 一覧を見てみると k8s.io
という名前の namespace が見つかります(注: この namespace は kuberentes の namespace とは無関係で、「containerd が container を管理する際に利用する namespace 機能」のはずです)。
root@kind-control-plane:/# ctr namespaces ls
NAME LABELS
k8s.io
次に、この k8s.io
namespace 内の container 一覧を $ ctr --namespace k8s.io containers ls
で見てみると、予想通り「Kuberentes cluster の動作に利用される container 一覧」をみる事が出来ました。
root@kind-control-plane:/# ctr --namespace k8s.io containers ls
CONTAINER IMAGE RUNTIME
0e7fc11f71638b86f8fd41f046101ebcb16b48976f06826add2d35df4e2ccc10 k8s.gcr.io/kube-controller-manager:v1.19.1 io.containerd.runc.v2
114d8f7f34ebc00c00236d7a111961193a6fa300dc90a4114385134f9eeda412 k8s.gcr.io/kube-proxy:v1.19.1 io.containerd.runc.v2
1c426469eb3ae09b744f1c116e6798c65886e218271dfa105fba747b4bfde0d3 k8s.gcr.io/pause:3.3 io.containerd.runc.v2
1fd32fb832a5e3782d1f39b26b99a30de8872a0a69600a77db3631f274eeb819 k8s.gcr.io/pause:3.3 io.containerd.runc.v2
2299e2a7b2b7afbb0789b30a4d7f4e57220a650f7368c04439e91c72e5049356 k8s.gcr.io/kube-apiserver:v1.19.1 io.containerd.runc.v2
23d54713b4401fb309725273c78961ea5d7f3a5be157d2a139813b9b9611e220 k8s.gcr.io/pause:3.3 io.containerd.runc.v2
35ab22ae753e043553188810bddfec43aa048d8c93f1ca38cf868ee31dbe06fc k8s.gcr.io/coredns:1.7.0 io.containerd.runc.v2
6171f628b9c0658a1983b198160fe76b53eeb4118aa8a0c71491ff5516453268 k8s.gcr.io/pause:3.3 io.containerd.runc.v2
6212a3ea51397a8491a8defb188faa1c9afb4b678a8fa102ab15ba8c78f98aa2 sha256:0369cf4303ffdb467dc219990960a9baa8512a54b0ad9283eaf55bd6c0adb934 io.containerd.runc.v2
624a9cf197014dcdbf0be5ddd68995566d14ceab00c8ca18fd51eb35cfe999cb k8s.gcr.io/kube-scheduler:v1.19.1 io.containerd.runc.v2
62d494fe1a94a396494ecd30cfa8538db2e1d2055fedac216d19fd21332d3841 sha256:b77790820d01598b2c56f823fa489e3f56be2cb5d6f7dd9eecd68a1995b89c13 io.containerd.runc.v2
725b634f7fc5d93ebfb1e63afb8aabcd936e6297ed7ab552e314e1cc90efeec5 k8s.gcr.io/pause:3.3 io.containerd.runc.v2
841020b0c947c1a8c2047a542268bf3dd91f8fb6b15cfc0a993dc61c0769e660 k8s.gcr.io/pause:3.3 io.containerd.runc.v2
8a6a56e83a590f4958dbd3262e4b0adbe36acad229ebcb54ef13c657f39c2c0a k8s.gcr.io/pause:3.3 io.containerd.runc.v2
9d07db434b356d9fddb2ae31715adc1209406a604b7068560f379832f5d79ba2 k8s.gcr.io/pause:3.3 io.containerd.runc.v2
a24a84c14111721de85b657f2bb0b89db44d87e97452ef86690060a0f0fcd3bc k8s.gcr.io/pause:3.3 io.containerd.runc.v2
b7775d2582ea9fdd481cf308d9c8bafa28fffbdaa2c8c0bad3377a9254876a59 k8s.gcr.io/coredns:1.7.0 io.containerd.runc.v2
ecbad3d6f6f5321e46b0d3ac395cb25227b42cfc04b1cec5a2b659fe45fab6cc sha256:e422121c9c5f97623245b7e600eeb5e223ee623f21fa04da985ae71057d8d70b io.containerd.runc.v2
このように、「Node container の中で containerd を動かし、その containerd 経由で Kuberentes cluster に必要な container を動かす」という形で kind は動作するようです。Deep Dive: Kind - KubeCon + CloudNativeCon North America 2019 の中で説明されていた事ではありますが、改めてその動作を確認できました。
kind と minikube の使い分けについて
さて、ここまでは kind というツールの機能について説明してきました。一方で、「local で Kubernetes を動かすツール」としては他に minikube も存在します。これらの使い分けはどうするのが良いでしょうか?
kind vs minikube
で検索すると、この2つの使い分けについて言及している記事がいくつか見つかります。例えば https://brennerm.github.io/posts/minikube-vs-kind-vs-k3s.html という記事では、以下のようにそれぞれの特徴が述べられています。
- minikube
- VM で Kubernetes を起動する
$ minikube dashboard
機能や minikube の addon system が有用
- kind
- Docker container で Kubernetes を起動する
$ kind load docker-image <my-custom-image>:<tag>
を実行する事で local で build した image を container registry へ push する事なく Kubernetes cluster から利用する事が可能
kind について言えば、$ kind load docker-image
の機能はかなり強力で、例えば custom controller のような「Kubernetes の中で動かしながら開発を進めたいもの」においては極めて有用です。実際、こういった側面があるためか、kubebuilder の Tutorial の Quick Start では「local で Kuberentes cluster を動かす選択肢」として kind が紹介されていたりします。
You’ll need a Kubernetes cluster to run against. You can use KIND to get a local cluster for testing, or run against a remote cluster.
cf. https://book.kubebuilder.io/quick-start.html#test-it-out
また、「VM よりも Docker container の方が扱いに慣れてる」などのケースでも kind の利用にはメリットがありそうだと個人的には感じました。
まとめ
kind の内部構造や使い方についてざっと眺めてみました。
「Kuberentes を Docker container の中で動かす」という言葉だけを聞くと突拍子も無いアイディアに聞こえますが、kubelet
や containerd
, その他の Kubernetes cluster に必要な component を “Node” image の中にまとめて配布してると思えば確かに自然ですし、実際にちゃんと動作することも確認できました。
Kuberentes の CI で使われている限り、これからも継続してメンテナンスされていきそうなのも良い点です。「30s で Kubernetes cluster を起動して気軽に動かせるツール」として、とても有用なものだと言えそうです。
なお、今回は「kind の Node container で動く container 一覧を眺めた」だけで、1つ1つの container の動きについては特に言及しませんでした。そのほとんどは「Kubernetes cluster に共通で必要な component」のはずですが、kindnetd
は kind におけるデフォルトの CNI plugin である kindnet の daemon だそうです。
Deep Dive: Kind - KubeCon + CloudNativeCon North America 2019 ではこの kindnet を含めて、このブログで言及してない様々な事を説明してるので、さらに詳細が気になる方はぜひ動画の方もみてみてください。また、自分も一部しか読んでませんが、kind の document (https://kind.sigs.k8s.io/) も理解を深める上でとても有用だと思います。
参考情報
- Kubernetes tools: https://kubernetes.io/docs/tasks/tools/
- kind: https://github.com/kubernetes-sigs/kind
- kind quick start: https://kind.sigs.k8s.io/docs/user/quick-start
- Deep Dive: Kind - KubeCon + CloudNativeCon North America 2019: https://www.youtube.com/watch?v=tT-GiZAr6eQ
おまけ: 既に node image を pull 済の場合に Kuberentes cluster 作成にかかる時間について
最初に kind を動かしてみた際に、「kindest/node
の docker pull 部分で時間がかかる」と書きました。そこで、「既に node image を pull 済みの場合」についても、試しに計測してみましょう。
まず、先ほど作成した cluster を削除します。
$ kind delete cluster
Deleting cluster "kind" ...
この状態でも、kindest/node
docker image は残っている事が確認できます。
$ docker images | grep kindest
kindest/node <none> 37ddbc9063d2 3 months ago 1.33GB
次に、この状態で再度 $ kind create cluster
を実行してみます。
$ kind create cluster
Creating cluster "kind" ...
✓ Ensuring node image (kindest/node:v1.19.1) 🖼
✓ Preparing nodes 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing CNI 🔌
✓ Installing StorageClass 💾
Set kubectl context to "kind-kind"
You can now use your cluster with:
kubectl cluster-info --context kind-kind
Have a nice day! 👋
kind create cluster 4.53s user 2.37s system 21% cpu 32.535 total
今回は、上記のように 30s 程度で Kubernetes cluster が起動することを確認できました!🎉 Node image の pull が無ければ、Kuberentes cluster をとても素早く作成出来る事が分かりますね!
おまけ 2: deployment を apply した状態で Node container の中を見てみる
deployment を apply した状態で、Node container の中を見てみましょう。
$ kubectl apply -f dumper-deployment.yaml
deployment.apps/dumper created
この状態だと、(当然ではありますが)apply した内容の pod に相当する container が起動している様子を確認できます。containerd の動作を感じる事が出来て、とても良いですね!
$ docker exec -it 7ff1d05ef709 bash
root@kind-control-plane:/# ps fax
PID TTY STAT TIME COMMAND
.
.
.
1729 ? Sl 0:00 /usr/local/bin/containerd-shim-runc-v2 -namespace k8s.io -id 34794c98df201080b5b22bcef08805e03748023c35f0deec77f962ff301a6835 -address /run/containerd/containerd.sock
1752 ? Ss 0:00 \_ /pause
1879 ? Ssl 0:00 \_ /app/dumper
root@kind-control-plane:/# ctr --namespace k8s.io images ls | grep dumper
docker.io/south37/dumper:latest application/vnd.docker.distribution.manifest.v2+json sha256:5efcf15fbd3439b2c2fff2415957933b45b9531401526c026c41219aed15701c 290.0 MiB linux/amd64 io.cri-containerd.image=managed
docker.io/south37/dumper@sha256:5efcf15fbd3439b2c2fff2415957933b45b9531401526c026c41219aed15701c application/vnd.docker.distribution.manifest.v2+json sha256:5efcf15fbd3439b2c2fff2415957933b45b9531401526c026c41219aed15701c 290.0 MiB linux/amd64 io.cri-containerd.image=managed
root@kind-control-plane:/# ctr --namespace k8s.io containers ls | grep dumper
ab80cafc653433c2b74713b85679797b4ffad5ae54eed733bb40d41af7bb9f43 docker.io/south37/dumper:latest io.containerd.runc.v2
おまけ 3: $ kind load docker-image
の実装について
kind の強力な機能である $ kind load docker-image
機能がどう実現されているのか気になったので、少しコードを読んでみました(対象は kind の v0.9.0
tag のコードです)。
まず、$ kind load
コマンド自体は https://github.com/kubernetes-sigs/kind/blob/v0.9.0/pkg/cmd/kind/load/load.go で実装されていて、その中の $ kind load docker-image
サブコマンドは https://github.com/kubernetes-sigs/kind/blob/v0.9.0/pkg/cmd/kind/load/docker-image/docker-image.go で実装されてるようです。さらにコードを読み進めると、nodeutils
package の LoadImageArchive
という関数にたどり着きます。
import (
...
dockerimage "sigs.k8s.io/kind/pkg/cmd/kind/load/docker-image"
...
)
// NewCommand returns a new cobra.Command for get
func NewCommand(logger log.Logger, streams cmd.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Args: cobra.NoArgs,
Use: "load",
Short: "Loads images into nodes",
Long: "Loads images into node from an archive or image on host",
}
// add subcommands
cmd.AddCommand(dockerimage.NewCommand(logger, streams))
cmd.AddCommand(imagearchive.NewCommand(logger, streams))
return cmd
}
cf. https://github.com/kubernetes-sigs/kind/blob/v0.9.0/pkg/cmd/kind/load/load.go#L38
// Load the image on the selected nodes
fns := []func() error{}
for _, selectedNode := range selectedNodes {
selectedNode := selectedNode // capture loop variable
fns = append(fns, func() error {
return loadImage(imageTarPath, selectedNode)
})
}
// loads an image tarball onto a node
func loadImage(imageTarName string, node nodes.Node) error {
f, err := os.Open(imageTarName)
if err != nil {
return errors.Wrap(err, "failed to open image")
}
defer f.Close()
return nodeutils.LoadImageArchive(node, f)
}
nodeutils.LoadImageArchive
は以下のような実装になっていて、「Node container の中で $ ctr
コマンドを呼び出して、container image の load を行う」ようです。
// LoadImageArchive loads image onto the node, where image is a Reader over an image archive
func LoadImageArchive(n nodes.Node, image io.Reader) error {
cmd := n.Command("ctr", "--namespace=k8s.io", "images", "import", "-").SetStdin(image)
if err := cmd.Run(); err != nil {
return errors.Wrap(err, "failed to load image")
}
return nil
}
cf. https://github.com/kubernetes-sigs/kind/blob/v0.9.0/pkg/cluster/nodeutils/util.go#L77-L84
nodeutils.LoadImageArchive
は「1 つ 1 つの Node container に対して loop を回して実行してる」ようです。つまり、魔法のように見えた $ kind load docker-image
の機能は、「それぞれの Node container の中で $ ctr images import
を実行して、container image を import する」事で実現しているようです。面白いですね!