今回はDockerの中でKubernetesクラスターを簡単に作成できる「kind」を使ってみたいと思います。 kindは、Dockerコンテナの「ノード」を使用してローカルのKubernetesクラスタを実行するためのツールです。 kindは元々、主にKubernetes自体をテストするために設計されましたが、ローカル開発またはCI/CDに使用されることもあります。
早速動かしてみます。
インストールの前に必要なもの
実際、kindをインストールする前に必要なものはDockerが動く環境です。ちょっと触ってみたいだけなら、手持ちのクライアントにDocker DesktopかRancher Desktopを入れておきます。
自由に使えるマシンがあるなら、LinuxをインストールしてDockerを入れておきます。今回の内容のレベルであれば、Linuxデスクトップ環境は必須ではありません。
インストール
macOSの場合はHomebrewやMacPorts、Windowsの場合はChocolateyを使ったkindのインストール例が載っています。今回はmacOSの場合の例を示します。
% brew install kind
他のOS向けのセットアップ方法については、QuickStartガイドに書かれています。
これでインストールされます。 次にクラスターの作成はこんな感じです。名前を指定しなかった場合は、デフォルトの名前でクラスターが作られます。
% kind create cluster --name mykind Creating cluster "mykind" ... ✓ Ensuring node image (kindest/node:v1.23.4) ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Set kubectl context to "kind-mykind" You can now use your cluster with: kubectl cluster-info --context kind-mykind Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/ % kind get clusters mykind
コンテナアプリを動かしてみよう
ではいつものマニフェストファイルを使って、NGINX Podを作ってみましょう。サーバーにアクセスできるようにサービスも作っておきます。
--- apiVersion: apps/v1 kind: Deployment metadata: name: nginxapp1 spec: replicas: 1 selector: matchLabels: app: nginxapp1 template: metadata: labels: app: nginxapp1 spec: containers: - name: nginxapp1 image: docker.io/library/nginx:alpine ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginxapp1-nodeport labels: app: nginxapp1 spec: type: NodePort ports: - port: 80 targetPort: 80 nodePort: 30080 selector: app: nginxapp1
Podとサービスを作成します。
% kubectl apply -f nginx-deployment.yaml deployment.apps/nginxapp1 created service/nginxapp1-nodeport created
問題なく作成できました。
% kubectl get -f nginx-deployment.yaml NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginxapp1 0/1 1 0 5s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/nginxapp1-nodeport NodePort 10.96.108.158 <none> 80:30080/TCP 5s
type: NodePort
を使ってサービス定義していますので、ブラウザーでアクセスしてみましょう。すると、次のようにアクセスできません。
% curl http://192.168.0.6:30080 curl: (7) Failed to connect to 192.168.0.6 port 30080 after 13 ms: Connection refused
kindはminikubeとはちょっと異なり、全てのサービスがDockerコンテナ内で動作します。そのため、事前に必要なポートをマッピングする形で用意する必要があります。では具体的にどう設定するかですが、kindの「Extra Port Mappings」を使った方法をとります。
kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraPortMappings: - containerPort: 30080 hostPort: 30080
NodePortが使える様に作り直す
一度クラスターを削除して、設定ファイルを指定してクラスターを作り直してみます。
% kind delete cluster --name mykind
Deleting cluster "mykind" ...
% kind create cluster --name mykind --config=config.yaml
もう一度試してみます。アクセスできるようになるまで少々時間がかかります。
% kubectl apply -f nginx-deployment.yaml % curl http://192.168.0.6:30080 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
Portがいくつか必要であれば、列挙しておけば良いようです。
--- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraPortMappings: - containerPort: 30080 hostPort: 30080 - containerPort: 30443 hostPort: 30443
クラスターバージョンの指定についても、設定ファイルに書くことで可能になっています。kindはKubernetesのバージョンについてはKindのバージョンに設定されたデフォルトバージョンが利用されます。通常はそれでも良いですが、バージョンを指定しておきたい時もあると思います。
その場合にはまず、現在使っているkindのバージョンを確認します。
% kind --version kind version 0.12.0
そのリリースページを確認します。対応しているバージョンが書かれているので、あとはそれを次のように書くだけです。
例えばKubernetes 1.22.7を使いたい場合は、次のように指定できます。バージョン指定する場合に注意する点として、そのバージョンのkindで対応していイメージを指定しないとクラスターが正常に動かない場合があるという点と、バージョンを指定しなかった場合はおおよそ最新のバージョンでクラスタ作成されるという点です。
kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane image: kindest/node:v1.22.7@sha256:1dfd72d193bf7da64765fd2f2898f78663b9ba366c2aa74be1fd7498a1873166 extraPortMappings: - containerPort: 30080 hostPort: 30080 - containerPort: 30443 hostPort: 30443 - containerPort: 30950 hostPort: 30950
コンテナアプリケーションの開発にDevOps CI/CDの概念を取り込んだ場合に、開発したコンテナアプリケーションの実行をテストしたい場合、kindを使うと便利かもしれません。 CI/CDのなかでKubernetesクラスターを作成してコンテナアプリケーションが動くか試すといった場合には、バージョン指定をしておくと良いと思います。
クラスターのバージョンを確認すると、1.22.7になっていることが確認できます。
% kubectl get no NAME STATUS ROLES AGE VERSION mykind-control-plane Ready control-plane,master 7m57s v1.22.7
ローカルイメージレジストリーを構築しよう
CI処理では、開発しているアプリケーションが安定稼働するまで何度も何度もCI/CDのサイクルが回ることになります。コンテナーをアプリケーションのプラットフォームとして使う場合、コンテナーイメージとイメージレジストリーが重要になってきます。
コンテナーイメージはイメージレジストリーからイメージをダウンロードしてランタイムエンジン(例えばDockerやcontainerd、CRI-O)にキャッシュしてクラスター内で使われます。イメージのダウンロード元はランタイムの設定で定義されていますが、多くの場合はDocker Hubのレジストリーからイメージをダウンロードします。
Docker Hub以外にもコンテナイメージを配信しているレジストリーはいろいろ存在しますが、多くのレジストリーではイメージのPull制限があることが一般的です。Docker Hubも例外ではなく、Dockerのライセンスやログインの有無によって若干変わってきます。
そしてPull制限に引っかかってしまうと具体的にどんな問題が起こるかというとイメージのPull、つまりダウンロードができないので、CIの処理がうまく回らなくなります。
そこで、CIで利用するイメージをローカルのコンテナレジストリーを構築して、そこでイメージの管理、利用をしようということになります。
kindはCI/CDなどでも使うことを想定して、それを実現する方法が用意されてます。
この情報をベースに、NodePortも使える構成でクラスターを作ってみましょう。次の様なシェルスクリプトを用意します。
#!/bin/sh set -o errexit # create registry container unless it already exists reg_name='kind-registry' reg_port='5001' if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then docker run \ -d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" \ registry:2 fi # create a cluster with the local registry enabled in containerd # ちょっとだけ追記 cat <<EOF | kind create cluster --config=- kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane extraPortMappings: - containerPort: 30080 hostPort: 30080 - containerPort: 30443 hostPort: 30443 containerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"] endpoint = ["http://${reg_name}:5000"] EOF # connect the registry to the cluster network if not already connected if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then docker network connect "kind" "${reg_name}" fi # Document the local registry # https://github.com/kubernetes/enhancements/tree/master/keps/sig-cluster-lifecycle/generic/1755-communicating-a-local-registry cat <<EOF | kubectl apply -f - apiVersion: v1 kind: ConfigMap metadata: name: local-registry-hosting namespace: kube-public data: localRegistryHosting.v1: | host: "localhost:${reg_port}" help: "https://kind.sigs.k8s.io/docs/user/local-registry/" EOF
実行してみます。
% ./kind-with-registry.sh Unable to find image 'registry:2' locally 2: Pulling from library/registry a5e44472bb1f: Pull complete 39b0b4c4587f: Pull complete bd7c1c2b0748: Pull complete c82d7ec052b5: Pull complete a372bb0b4117: Pull complete Digest: sha256:9f2f33b11e13618ce2f7c6e4bd70fc479aac816116eb5a590edd4176065f0471 Status: Downloaded newer image for registry:2 b99c5c2d705eab0685fd4d16b6f5311f019c5156ca31f67603ef1eea417df874 Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.23.4) 🖼 ✓ 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/ configmap/local-registry-hosting created
うまく動いたようですね。
ローカルイメージレジストリーを使ってみる
% docker pull gcr.io/google-samples/hello-app:1.0 1.0: Pulling from google-samples/hello-app 59bf1c3509f3: Pull complete da75187f77d8: Pull complete Digest: sha256:88b205d7995332e10e836514fbfd59ecaf8976fc15060cd66e85cdcebe7fb356 Status: Downloaded newer image for gcr.io/google-samples/hello-app:1.0 gcr.io/google-samples/hello-app:1.0 % docker tag gcr.io/google-samples/hello-app:1.0 localhost:5001/hello-app:1.0 % docker push localhost:5001/hello-app:1.0 The push refers to repository [localhost:5001/hello-app] 6aebdb5560a6: Pushed 8d3ac3489996: Pushed 1.0: digest: sha256:88b205d7995332e10e836514fbfd59ecaf8976fc15060cd66e85cdcebe7fb356 size: 739
上手く動いているようです。 それでは、この格納したイメージを使って、Podを作ってみます。
% kubectl create deployment hello-server --image=localhost:5001/hello-app:1.0 deployment.apps/hello-server created % kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE hello-server 1/1 1 1 26s
問題なく動作するのを確認できました。
kindによって構築できるKubernetesクラスターは厳密には最終的にアプリケーションを動かすクラウドのKubernretsとは全く同一ではありません。従って最終的には本番環境のKubernetesクラスターで動作チェックすることにはなると思うのですが、初期の動作検証にはkindを利用したKubernetesクラスターをテスト用に使うのは良いと思います。
今回は省略しましたが、環境さえ整えばIngressやLoadbalancerも利用でき、よりプロダクション環境に近付くと思います。まだkindを触ったことがない方は試してみてください。効率的にCI/CDを実践していきましょう。