最近世界中で騒ぎになっている通り、Kubernetes 1.20でDockerが非推奨になり、近いうちにdockershimが廃止されるんだけど、dockershimの後継としてcri-dockerdというのが開発されてるので、KubernetesでDockerが使えなくなるわけではなさそうという話し。

Kubernetes Advent Calendar 2020 その3の6日目の記事です。

KubernetesでDockerが非推奨になるとは

2020/12/1にRC初版がリリースされたKubernetes 1.20のCHANGELOGに、Dockerのサポートがdeprecatedになったというのが書いてある。 正確には、kubeletに組み込まれたdockershimの使用が非推奨になって、将来のリリースでdockershimが削除される、というアナウンスだ。

dockershimとは

kubeletはコンテナランタイムに指示してコンテナを起動したりするわけだけど、コンテナランタイムと話すためのインターフェースはCRIと呼ばれていて、dockershimはそのCRIの最初の実装。 当時コンテナランタイムとしてほぼ無二の存在だったDockerがCRIを実装していなかったので、kubeletが話すCRIとDockerのAPIを取り持つサービスとしてdockershimは作られた。

図にすると以下のような感じ。

cr4.png

kubeletとdockershimは/var/run/dockershim.sockというソケットファイルで通信するんだけど、dockershimはkubeletプロセス内で動いているので、結局/var/run/dockershim.sockを読むのも書くのもkubelet、というちょっと歪な構造。

また、上の図で、containerdというのがDocker(dockerd)の下で動いているんだけど、これが実はCRIを実装している。

誰が見ても、kubeletから直接containerdにCRIで話せばいいんじゃないの? と思うはず。 KubernetesからDockerのサポートが落ちるのは自然な流れなんじゃなかろうか。

dockershimの後継、cri-dockerd

しかし、Dockerが非推奨になるというアナウンスはぱっと見かなりインパクトがあって、Dockerすぐ死んでしまうん?みたいな世間の誤解もあって大騒ぎになってしまった。

で、Docker陣営側からそれを鎮めるため、2020/12/4にdockershimの後継を開発中だから安心しろというアナウンスがでた。 その後継というのがcri-dockerdというやつで、今はプロトタイプ版が@dims氏のリポジトリで開発されてて、その内Mirantis社のリポジトリでDocker社とともに開発されることになっている。

cri-dockerdはまだリリースバイナリがなくて、ドキュメントも何もないけど、適当にビルドして動かしたら動いたのでメモを残しておく。

cri-dockerdを試す

試すKubernetesクラスタはいつもの通り、VirtualBoxのVMにOracle Linux 7を入れて自前のPlaybookで構築したシングルノードクラスタ。 Kubernetesのバージョンは1.19.2。 実はすでに脱Dockerしてcontainerdしか動いてなかったんだけど、Dockerを入れてkubeletとつなげておいた。

cri-dockerdはまだリリースバイナリがないので自分でビルドする必要がある。 Mirantis社のリポジトリは2020/12/6現時点で空なので@dims氏のリポジトリを使う。 リポジトリをクローンしてgo buildしたら簡単にビルドできた。

[root]# git clone https://github.com/dims/cri-dockerd.git
[root]# cd cri-dockerd
[root]# GO111MODULE=on go build -o /usr/local/bin/cri-dockerd


出来たバイナリでcri-dockerdのhelpを見てみると、デフォルトではdockershimと同様に/var/run/dockershim.sockでCRIを聞いてくれるっぽい。

[root]# cri-dockerd -h
CRI that connects to Docker Daemon

Usage:
  DockerCRI [flags]

Flags:
      --alsologtostderr                         log to standard error as well as files
      --cni-bin-dir string                      <Warning: Alpha feature> A comma-separated list of full paths of directories in which to search for CNI plugin binaries.
      --cni-cache-dir string                    <Warning: Alpha feature> The full path of the directory in which CNI should store cache files.
      --cni-conf-dir string                     <Warning: Alpha feature> The full path of the directory in which to search for CNI config files
      --container-runtime-endpoint string       The endpoint of remote runtime service. Currently unix socket and tcp endpoints are supported on Linux, while npipe and tcp endpoints are supported on windows.  Examples:'unix:///var/run/dockershim.sock', 'npipe:////./pipe/dockershim' (default "unix:///var/run/dockershim.sock")
      --docker-endpoint string                  Use this for the docker endpoint to communicate with. (default "unix:///var/run/docker.sock")
      --dockershim-root-directory string        Path to the dockershim root directory. (default "/var/lib/dockershim")
  -h, --help                                    help for DockerCRI
      --image-pull-progress-deadline duration   If no pulling progress is made before this deadline, the image pulling will be cancelled. (default 1m0s)
      --log-backtrace-at traceLocation          when logging hits line file:N, emit a stack trace (default :0)
      --log-dir string                          If non-empty, write log files in this directory
      --log-file string                         If non-empty, use this log file
      --log-flush-frequency duration            Maximum number of seconds between log flushes (default 5s)
      --logtostderr                             log to standard error instead of files (default true)
      --network-plugin string                   <Warning: Alpha feature> The name of the network plugin to be invoked for various events in kubelet/pod lifecycle.
      --network-plugin-mtu int32                <Warning: Alpha feature> The MTU to be passed to the network plugin, to override the default. Set to 0 to use the default 1460 MTU.
      --pod-cidr string                         The CIDR to use for pod IP addresses, only used in standalone mode.  In cluster mode, this is obtained from the master. For IPv6, the maximum number of IP's allocated is 65536
      --pod-infra-container-image string        The image whose network/ipc namespaces containers in each pod will use (default "k8s.gcr.io/pause:3.1")
      --runtime-cgroups string                  Optional absolute name of cgroups to create and run the runtime in.
      --stderrthreshold severity                logs at or above this threshold go to stderr (default 2)
  -v, --v Level                                 number for the log level verbosity
      --version version[=true]                  Print version information and quit
      --vmodule moduleSpec                      comma-separated list of pattern=N settings for file-filtered logging

CRIを聞くソケットファイルのパスは--container-runtime-endpointで変更可能なようなので、今回はdockershimのと紛らわしくならないように、/var/run/hoge.sockにすることにする。


cri-dockerdのバイナリは、今回はsystemdでデーモンとして起動する。(コンテナで動かしてもいいはず。) ユニットファイルは以下。 見ての通り、--container-runtime-endpointをつけて実行するだけ。

/etc/systemd/system/cri-dockerd.service:

[Unit]
Description=cri-dockerd
After=docker.service

[Service]
User=root
Group=root
ExecStart=/usr/local/bin/cri-dockerd --container-runtime-endpoint /var/run/hoge.sock
Restart=always
RestartSec=10s
KillMode=process

[Install]
WantedBy=multi-user.target

あとはsystemctl enable cri-dockerd && systemctl start cri-dockerdすればcri-dockerdが起動する。


起動させたcri-dockerdとつなげるため、kubeletのユニットファイルを以下のように変更して、組み込みのdockershimの代わりに/var/run/hoge.sockと話すようにしてやる。

/etc/systemd/system/kubelet.service:

 [Unit]
 Description=Kubernetes Kubelet
 Documentation=https://github.com/kubernetes/kubernetes
-After=docker.service
-Requires=docker.service
+After=cri-dockerd.service
+Requires=cri-dockerd.service

 [Service]
 CPUAccounting=true
 MemoryAccounting=true
 User=root
 Group=root
 ExecStart=/usr/bin/kubelet \
   --bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \
   --cert-dir=/etc/kubernetes/pki \
   --config=/etc/kubernetes/kubelet.conf \
   --cni-bin-dir=/opt/cni/bin \
   --cni-conf-dir=/etc/cni/net.d \
   --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
   --network-plugin=cni \
   --pod-infra-container-image=k8s.gcr.io/pause-amd64:3.1 \
   --v=1 \
+  --container-runtime=remote \
+  --container-runtime-endpoint=unix:///var/run/hoge.sock \
   --root-dir=/var/lib/kubelet
 Restart=always
 RestartSec=10s
 KillMode=process

 [Install]
 WantedBy=multi-user.target

あとはsystemctl daemon-reload && systemctl restart kubeletでkubeletを再起動すれば設定完了。


/var/run/dockershim.sockがなくても、

[root]# ls /var/run/*.sock
/var/run/docker.sock  /var/run/hoge.sock

Podは元気に動いてるし、

[root]# kubectl get po -n kube-system
NAME                       READY   STATUS    RESTARTS   AGE
coredns-6b5cbb9f46-v8p9q   1/1     Running   0          2m38s
coredns-6b5cbb9f46-xknw6   1/1     Running   1          7h54m
weave-net-zprrm            2/2     Running   3          72d

Podの再起動もできる。

[root]# kubectl delete po -n kube-system coredns-6b5cbb9f46-xknw6
pod "coredns-6b5cbb9f46-xknw6" deleted
[root]# kubectl get po -n kube-system
NAME                       READY   STATUS    RESTARTS   AGE
coredns-6b5cbb9f46-85zjs   1/1     Running   0          4s
coredns-6b5cbb9f46-v8p9q   1/1     Running   0          3m33s
weave-net-zprrm            2/2     Running   3          72d


これでdockershimが無くてもKubernetesでDockerが使えることが確認できた。 一安心。

まあDocker使わないんだけど。