kindind ~Kubernetes in Docker in Dockerでお手軽クラスタ構築~

Dockerコンテナの中にKubernetesクラスタを建てて、複数のクラスタを同時に構築・比較します。コンテナを削除するだけでクラスタのリセットもできるため、特にオンプレ環境でKubernetesクラスタを試す時にめちゃくちゃ有用です。
2023.05.11

はじめに

どうも、おのやんです。

みなさん、Kubernetesクラスタ組んでますか?私は大学の卒業研究としてKubernetesを用いたGPUクラスタをオンプレで構築していました。

Kubernetesクラスタを組んでみると、うまくアプリケーションのデプロイができなかったり、思わぬ部分で部分でつまづいたりします。そのため、初めてクラスタを構築するときだったりアプリケーションを試したいときは、簡単にクラスタを作成・削除できると嬉しいです。

しかし、オンプレ環境でクラスタ構築を試行錯誤していると、操作をミスった際にとても面倒です。操作を行う前の状態にクラスタを戻す必要があったり、最悪クラスタをはじめから構築し直す必要があります。

私はけっこうこの問題に悩まされていましたが、kindを使ってDockerコンテナ内にKubernetesクラスタ建てれんか?と思ってやってみたところ意外とすんなりできました。

今回はそんなkindind (Kubernetes in Docker in Docker)のやり方を紹介します。

使用技術

Dockerコンテナ内にKubernetesクラスタを建てるには、Kubernetes in Docker(kind)Doker in Docker(dind)を使います。

具体的にはdindイメージを使ってコンテナを建てて、その中でkindクラスタを展開します。

kind

kindの公式ドキュメントには、以下のような紹介があります。

kind is a tool for running local Kubernetes clusters using Docker container “nodes”. kind was primarily designed for testing Kubernetes itself, but may be used for local development or CI.

kindはDockerコンテナの "ノード "を使ってローカルのKubernetesクラスタを実行するためのツールです。 kindは主にKubernetes自体のテスト用に設計されましたが、ローカル開発やCIに使用することもできます。(DeepLで翻訳)

kindを使えば、1台のマシンの上にKubernetesクラスタを建てることができます。Kubernetesクラスタを構成するノード(ひとつひとつのマシンのこと)はDockerコンテナです。このおかげで、物理クラスタと比べて簡単にクラスタを構築できます。Kubernetes単体のテストに用いられている公式のプロダクトなので、バグなども積極的に修正されており、動作も信頼できます。

しかし実際にクラスタが展開されるのはマシン上であり、複数種のクラスタの展開はできません。またkindやkubectlのインストールも求められるため、いくらか環境が汚れます。

そこで、これらのクラスタを隔離された環境の中で展開し、簡単に作成・管理・削除ができるようにしたいです。つまりDockerの中でDockerコンテナを建てられればいいわけです。

dind (Docker in Docker)

dindのDockerイメージの説明の一部を抜粋します。

Although running Docker inside Docker is generally not recommended, there are some legitimate use cases, such as development of Docker itself.

Dockerの内部でDockerを実行することは一般的に推奨されませんが、Docker自体の開発など、正当なユースケースも存在します。(DeepLで翻訳)

つまりDockerコンテナの中でDockerコンテナを起動できます。このコンテナイメージを使えば、dindコンテナの中でコンテナをノードとしたkindクラスタを構築できそうですね。

Dockerの内部でDockerを実行することは推奨されていません後述するdocker-composeでのprivileged: trueもセキュリティ上のリスクがありますそのため、本番環境ではこの方法は使用しないでください。あくまで、「kindのKubernetesクラスタをお試しで構築する」という用途に限定してください。

これらを確認した上で、Dockerコンテナにkindクラスタを構築していきましょう。

kindindクラスタ構築

まず、Dockerfile, docker-compose.yaml, kind-config.yamlの3つのファイルを、以下のように作成します。

こちらのファイルは2023年2月に作成したものです。実際に実行する際は、公式ドキュメントを適宜参照して、コマンドや設定を置き換えてくださいm(_ _)m

Dockerfile

FROM docker:stable-dind

# Install kind
RUN apk update && apk add curl && \
  curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64 && \
  chmod +x ./kind && \
  mv ./kind /usr/local/bin/kind

# Install kubectl
RUN curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" && \
  chmod +x ./kubectl && \
  mv ./kubectl /usr/local/bin/kubectl

# Install helm
RUN curl https://raw.githubusercontent.com/helm/helm/HEAD/scripts/get-helm-3 | ash

docker-compose.yaml

version: '3.7'

services:
  kind:
    build:
      context: ./
      dockerfile: ./Dockerfile
    tty: true
    container_name: kind
    privileged: true
    volumes:
      - ../:/app/

kind-config.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker

このうち、kind-config.yamlkindがクラスタを作成する際に参照する設定ファイルです。クラスタがどういう構成になっているのかをyaml形式で記述しています

これらのファイルをすべて同ディレクトリに配置して、Dockerfileを元にDockerイメージを作成していきます。具体的には、Docker Composeでイメージをビルドします。

$ docker-compose build

イメージをビルドできたら、コンテナを起動します。

$ docker-compose up -d

今回は、/app直下にDoclerfileなどを展開しています。 今回はコンテナの中に入って、/app/kindディレクトリまで移動します。

$ docker-compose exec -it kind ash

# cd app/kind

このkindディレクトリ直下でクラスタを作成します。

/app/kind # kind create cluster

Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.25.3) ?
 ✓ 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/

クラスタの作成が完了すると、Dockerコンテナの中にいながらKubernetesクラスタの管理が可能となります!

/app/kind # kubectl get all -A

NAMESPACE            NAME                                             READY   STATUS    RESTARTS   AGE
kube-system          pod/coredns-565d847f94-d9jbg                     1/1     Running   0          3m15s
kube-system          pod/coredns-565d847f94-h2ggw                     1/1     Running   0          3m14s
kube-system          pod/etcd-kind-control-plane                      1/1     Running   0          3m27s
kube-system          pod/kindnet-nkzcb                                1/1     Running   0          3m15s
kube-system          pod/kube-apiserver-kind-control-plane            1/1     Running   0          3m27s
kube-system          pod/kube-controller-manager-kind-control-plane   1/1     Running   0          3m27s
kube-system          pod/kube-proxy-88vml                             1/1     Running   0          3m15s
kube-system          pod/kube-scheduler-kind-control-plane            1/1     Running   0          3m27s
local-path-storage   pod/local-path-provisioner-684f458cdd-gf6ld      1/1     Running   0          3m15s

NAMESPACE     NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
default       service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP                  3m29s
kube-system   service/kube-dns     ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   3m28s

NAMESPACE     NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-system   daemonset.apps/kindnet      1         1         1       1            1           <none>                   3m26s
kube-system   daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   3m28s

NAMESPACE            NAME                                     READY   UP-TO-DATE   AVAILABLE   AGE
kube-system          deployment.apps/coredns                  2/2     2            2           3m28s
local-path-storage   deployment.apps/local-path-provisioner   1/1     1            1           3m25s

NAMESPACE            NAME                                                DESIRED   CURRENT   READY   AGE
kube-system          replicaset.apps/coredns-565d847f94                  2         2         2       3m15s
local-path-storage   replicaset.apps/local-path-provisioner-684f458cdd   1         1         1       3m15s


/app/kind # docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                       NAMES
53dc503dd135        kindest/node:v1.25.3   "/usr/local/bin/entr…"   4 minutes ago       Up 4 minutes        127.0.0.1:38973->6443/tcp   kind-control-plane

これらのファイルや操作は、私のGitHubの卒論ディレクトリにまとめていますので、よければご参照ください。

おわりに

Dockerコンテナの中にKubernetesクラスタを建てることで、複数のクラスタを同時変更で構築・比較できます。さらにコンテナを削除するだけでクラスタをリセットできるため、特にオンプレ環境でKubernetesクラスタを試す時にめちゃくちゃ有用です。私はこれで卒論を乗り切りました。

巷では「オンプレのクラスタ構築はしんどい」とか言われますが、オンプレクラスタ構築に悩んでる人は試しに使ってみてください!では!