zaki work log

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

[Kubernetes / kind] kindを使ってDocker上にクラスタを高速作成し、MetalLBも併用してクラスタ横断した通信お試し

DockerでKubernetesを動かす。
何をいってるかわからないかもしれないけど、コンテナをK8sノードとして動かすものです。

下の図のような感じで、Kubernetesノードとして動作するコンテナがDockerで動く、というものです。
(なので、podがこのコンテナの中で動く、、というDocker in Docker構成の親戚みたいな感じ)

f:id:zaki-hmkc:20200801155039p:plain

VMに比べたコンテナの特性として、作成がとても高速なので、作って捨てるサイクルが速い使い方には最適です。

kind.sigs.k8s.io

以下、Quick Startに沿ってクラスタを作った作業ログなので、実際にやるときは公式ドキュメント見ること。(お約束)


8/1夜追記: ツイッターで頂いた認証情報と使い方(ストレージ・ポート関連)に関して追記。

必要な環境

  • Dockerインストール済み
  • 操作したいユーザーでdockersudo無しで実行できる
  • kubectlは別途インストールする

2番目のsudoについては、kind実行時にsudoを付与すれば構築(k8sコンテナの起動)はできるけど、自動で設定されるクラスタ設定($HOME/.kube/config)がrootユーザーに行われるので、設定コピーするか、kubectlの実行もsudo付けるなど、いろいろと面倒なので、 後述のkind get kubeconfigで設定情報を取得して$KUBECONFIGに設定するか、dockersudo無しで実行できるようにしておけばクラスタ構築時に実行したユーザーの$HOME/.kube/configを更新してくれるので楽。

[zaki@cloud-dev kind]$ sudo gpasswd -a zaki docker
ユーザ zaki をグループ docker に追加

このあと1度ログアウト・ログインし直せば、sudoなしでdockerが実行できるはず。

(準備)CLIインストール

まずCLIツールkindが必要です。
バイナリをダウンロードするか、go getでインストールします。

今回はお手がるにバイナリのダウンロードで。
(Go環境がまだ無いホストだった)

[zaki@cloud-dev kind]$ curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.8.1/kind-linux-amd64
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    97  100    97    0     0    176      0 --:--:-- --:--:-- --:--:--   176
100   629  100   629    0     0    594      0  0:00:01  0:00:01 --:--:--  614k
100 9900k  100 9900k    0     0  2125k      0  0:00:04  0:00:04 --:--:-- 3508k
[zaki@cloud-dev kind]$ chmod +x kind
[zaki@cloud-dev kind]$ sudo mv kind /usr/local/bin/
[zaki@cloud-dev kind]$ kind --version
kind version 0.8.1
[zaki@cloud-dev kind]$ kind version
kind v0.8.1 go1.14.2 linux/amd64
[zaki@cloud-dev kind]$ kind --help
kind creates and manages local Kubernetes clusters using Docker container 'nodes'

Usage:
  kind [command]

Available Commands:
  build       Build one of [node-image]
  completion  Output shell completion code for the specified shell (bash, zsh or fish)
  create      Creates one of [cluster]
  delete      Deletes one of [cluster]
  export      Exports one of [kubeconfig, logs]
  get         Gets one of [clusters, nodes, kubeconfig]
  help        Help about any command
  load        Loads images into nodes
  version     Prints the kind CLI version

Flags:
  -h, --help              help for kind
      --loglevel string   DEPRECATED: see -v instead
  -q, --quiet             silence all stderr output
  -v, --verbosity int32   info log verbosity
      --version           version for kind

Use "kind [command] --help" for more information about a command.

クラスタ作成

デフォルト

設定を特に指定せずにクラスタ作成すると、最新安定板(現在はv1.18.2)のシングルノークラスタになります。

before

[zaki@cloud-dev ~]$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[zaki@cloud-dev ~]$ time kind create cluster
Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.18.2) 🖼 
 ✓ 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 question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂

real    2m34.551s
user    0m0.861s
sys     0m0.738s
[zaki@cloud-dev ~]$ 

できました。わずか2分半です。このスピードはすごい。
そして表示がちょっとかわいい。

$HOME/.kube/configも更新されるので、すぐにkubectlが実行可能。

[zaki@cloud-dev ~]$ kubectl config get-contexts 
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         kind-kind                     kind-kind    kind-kind          
          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin   
[zaki@cloud-dev ~]$ kubectl get node
NAME                 STATUS     ROLES    AGE   VERSION
kind-control-plane   NotReady   master   38s   v1.18.2
[zaki@cloud-dev ~]$ kubectl get pod -A
NAMESPACE            NAME                                         READY   STATUS    RESTARTS   AGE
kube-system          coredns-66bff467f8-lj9gx                     0/1     Pending   0          30s
kube-system          coredns-66bff467f8-ncqj8                     0/1     Pending   0          30s
kube-system          etcd-kind-control-plane                      1/1     Running   0          39s
kube-system          kindnet-h92x5                                1/1     Running   0          29s
kube-system          kube-apiserver-kind-control-plane            1/1     Running   0          39s
kube-system          kube-controller-manager-kind-control-plane   1/1     Running   0          39s
kube-system          kube-proxy-9g546                             1/1     Running   0          29s
kube-system          kube-scheduler-kind-control-plane            1/1     Running   0          39s
local-path-storage   local-path-provisioner-bd4bb6b75-dhkxs       0/1     Pending   0          30s

クラスタ作成直後はまだCoreDNSとストレージがPending状態で、ノードもNotReadyになっています。

このときのDockerコンテナの状態はこの通り。

[zaki@cloud-dev ~]$ docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED              STATUS              PORTS                       NAMES
850b8661017a        kindest/node:v1.18.2   "/usr/local/bin/entr…"   About a minute ago   Up About a minute   127.0.0.1:40679->6443/tcp   kind-control-plane
[zaki@cloud-dev ~]$ 

しばらく待てば、PodはすべてRunningとなり、ノードもReadyに遷移します。

[zaki@cloud-dev ~]$ kubectl get pod -A
NAMESPACE            NAME                                         READY   STATUS    RESTARTS   AGE
kube-system          coredns-66bff467f8-lj9gx                     1/1     Running   0          54s
kube-system          coredns-66bff467f8-ncqj8                     1/1     Running   0          54s
kube-system          etcd-kind-control-plane                      1/1     Running   0          63s
kube-system          kindnet-h92x5                                1/1     Running   0          53s
kube-system          kube-apiserver-kind-control-plane            1/1     Running   0          63s
kube-system          kube-controller-manager-kind-control-plane   1/1     Running   0          63s
kube-system          kube-proxy-9g546                             1/1     Running   0          53s
kube-system          kube-scheduler-kind-control-plane            1/1     Running   0          63s
local-path-storage   local-path-provisioner-bd4bb6b75-dhkxs       1/1     Running   0          54s
[zaki@cloud-dev ~]$ kubectl get node
NAME                 STATUS   ROLES    AGE   VERSION
kind-control-plane   Ready    master   76s   v1.18.2

ストレージも使用可能(名前的にhost pathですかね)

[zaki@cloud-dev ~]$ kubectl get sc
NAME                 PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
standard (default)   rancher.io/local-path   Delete          WaitForFirstConsumer   false                  85s

ノードの情報(describe抜粋)

Addresses:
  InternalIP:  172.20.0.2
  Hostname:    kind-control-plane
Capacity:
  cpu:                4
  ephemeral-storage:  37202180Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             7990132Ki
  pods:               110
Allocatable:
  cpu:                4
  ephemeral-storage:  37202180Ki
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             7990132Ki
  pods:               110
System Info:
  Machine ID:                 3580f7d61b0e4da9818e0b8438b770ad
  System UUID:                e9fe840f-ee00-4ff3-9a38-4efe44b08ff5
  Boot ID:                    7e6869fe-fda7-4841-ae79-99860e101bef
  Kernel Version:             3.10.0-1062.el7.x86_64
  OS Image:                   Ubuntu 19.10
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://1.3.3-14-g449e9269
  Kubelet Version:            v1.18.2
  Kube-Proxy Version:         v1.18.2
PodCIDR:                      10.244.0.0/24

マルチノードクラスタの作成

複数のコンテナを起動することで、kindでもマルチノードクラスタを作成することができます。
また、作成済みクラスタがある場合でも、追加のクラスタを作成も可能。

前述のシングルノークラスタ"kind"がある状態で、2個目のクラスタをマルチノードで作成してみます。

マルチノードクラスタを作成するには、こちら
YAMLの定義ファイルを作成し、そのファイルを--configで指定すればOK

検証環境で割とよく作りがちなコントロールプレーンが1台、ワーカーが2台であれば以下の通り。

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

この内容のYAMLファイルを作成し、kind create clusterを実行。
--configYAMLファイルを指定し、あと--nameクラスタ名も指定。(省略時は"kind"になる)

[zaki@cloud-dev kind]$ time kind create cluster --config multinode.yaml --name multicluster
Creating cluster "multicluster" ...
 ✓ Ensuring node image (kindest/node:v1.18.2) 🖼
 ✓ Preparing nodes 📦 📦 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
 ✓ Joining worker nodes 🚜 
Set kubectl context to "kind-multicluster"
You can now use your cluster with:

kubectl cluster-info --context kind-multicluster

Thanks for using kind! 😊

real    2m28.784s
user    0m1.553s
sys     0m1.146s

"Preparing nodes"で作成されるノード分のアイコンが出るんだけど、何度見てもこれ食パンに見えるんだよね。。

f:id:zaki-hmkc:20200801131127p:plain

.kube/configも更新されます。

[zaki@cloud-dev kind]$ kubectl config get-contexts 
CURRENT   NAME                          CLUSTER             AUTHINFO            NAMESPACE
          kind-kind                     kind-kind           kind-kind
*         kind-multicluster             kind-multicluster   kind-multicluster
          kubernetes-admin@kubernetes   kubernetes          kubernetes-admin
[zaki@cloud-dev kind]$ kc get node
NAME                         STATUS   ROLES    AGE     VERSION
multicluster-control-plane   Ready    master   2m57s   v1.18.2
multicluster-worker          Ready    <none>   2m25s   v1.18.2
multicluster-worker2         Ready    <none>   2m23s   v1.18.2
[zaki@cloud-dev kind]$ kc get pod -A
NAMESPACE            NAME                                                 READY   STATUS    RESTARTS   AGE
kube-system          coredns-66bff467f8-mgj67                             1/1     Running   0          2m39s
kube-system          coredns-66bff467f8-t8jkx                             1/1     Running   0          2m39s
kube-system          etcd-multicluster-control-plane                      1/1     Running   0          2m50s
kube-system          kindnet-g4qdv                                        1/1     Running   0          2m39s
kube-system          kindnet-hqz66                                        1/1     Running   1          2m25s
kube-system          kindnet-s2g9n                                        1/1     Running   0          2m27s
kube-system          kube-apiserver-multicluster-control-plane            1/1     Running   0          2m50s
kube-system          kube-controller-manager-multicluster-control-plane   1/1     Running   1          2m50s
kube-system          kube-proxy-gflzl                                     1/1     Running   0          2m39s
kube-system          kube-proxy-jml42                                     1/1     Running   0          2m25s
kube-system          kube-proxy-t8v69                                     1/1     Running   0          2m27s
kube-system          kube-scheduler-multicluster-control-plane            1/1     Running   1          2m50s
local-path-storage   local-path-provisioner-bd4bb6b75-wxg42               1/1     Running   0          2m39s

Kubernetesバージョンの指定

検証環境などでバージョン指定したい場合も設定のYAMLに記述します。
(未指定だと最新安定板になり、2020.08.01時点でv1.18.2になる)

バージョン番号だけ指定できれば楽そうだけど、kindでは「指定バージョン用のイメージとそのsha256ハッシュ値を指定」すればOK。

例えばv1.15(v1.15.11)の3ノードクラスタであれば以下の通り。

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
  image: kindest/node:v1.15.11@sha256:6cc31f3533deb138792db2c7d1ffc36f7456a06f1db5556ad3b6927641016f50
- role: worker
  image: kindest/node:v1.15.11@sha256:6cc31f3533deb138792db2c7d1ffc36f7456a06f1db5556ad3b6927641016f50
- role: worker
  image: kindest/node:v1.15.11@sha256:6cc31f3533deb138792db2c7d1ffc36f7456a06f1db5556ad3b6927641016f50

クラスタ作成

[zaki@cloud-dev kind]$ kind create cluster --config multinode-v1.15.11.yaml --name multi-1.15
Creating cluster "multi-1.15" ...
 ✓ Ensuring node image (kindest/node:v1.15.11) 🖼 
 ✓ Preparing nodes 📦 📦 📦  
 ✓ Writing configuration 📜 
 ✓ Starting control-plane 🕹️ 
 ✓ Installing CNI 🔌 
 ✓ Installing StorageClass 💾 
 ✓ Joining worker nodes 🚜 
Set kubectl context to "kind-multi-1.15"
You can now use your cluster with:

kubectl cluster-info --context kind-multi-1.15

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
[zaki@cloud-dev kind]$ kubectl get node
NAME                       STATUS   ROLES    AGE     VERSION
multi-1.15-control-plane   Ready    master   2m12s   v1.15.11
multi-1.15-worker          Ready    <none>   91s     v1.15.11
multi-1.15-worker2         Ready    <none>   92s     v1.15.11

この通り、v1.15.11でデプロイされています。

指定可能なバージョン(というかイメージ)は全ての一覧表はちょっとなさそうだけど、Releaseページから該当イメージの記載を探す感じ。
もしくは、GitHubのkindest/nodeイメージのタグ一覧から探してもよさそう。

github.com

hub.docker.com

kindのクラスタ管理

クラスタ情報

kind get clustersクラスタ一覧を見れます。

[zaki@cloud-dev kind]$ kind get clusters
kind
multi-1.15
multicluster

クラスタ名を指定せずにcreateすると、クラスタ名はkindになります。
2個目、3個目のクラスタを作成すれば、一覧に追加されます。

認証情報

kindで作成したクラスタの認証情報(kubeconfig)はkind get kubeconfigで取得できます。
STDOUTに出力されるので、ファイルに保存して$KUBECONFIGを設定すればkubectl使ってクラスタにアクセスできます。

[zaki@cloud-dev kind]$ sudo kind get kubeconfig
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: ......
    server: https://127.0.0.1:38366
  name: kind-kind
contexts:
- context:
    cluster: kind-kind
    user: kind-kind
  name: kind-kind
current-context: kind-kind
kind: Config
preferences: {}
users:
- name: kind-kind
  user:
    client-certificate-data: ........
    client-key-data: ........

dockerの実行にsudoが必要な場合はkindにもsudoが(内部でDocker操作するため)必要だけど、↑の認証情報を$KUBECONFIGに設定すれば、Kubernetesへのアクセスはsudoは不要です。

クラスタ削除

[zaki@cloud-dev kind]$ kind delete cluster --name multi-1.15
Deleting cluster "multi-1.15" ...

kind上のクラスタ状態、ノード用コンテナ、~/.kube/configの該当設定など諸々が削除されます。

LoadBalancer Service

デフォルトでは使用できないけど、MetalLBを導入してL2設定で簡単に使えるようになります。

zaki-hmkc.hatenablog.com

MetalLBの3つのマニフェストをデプロイし、

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.9.3/manifests/metallb.yaml
# On first install only
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"

コンテナのネットワークに合わせて、LoadBalancer Service用のIPアドレスを設定したConfigMapのマニフェストをデプロイします。

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 172.20.220.1-172.20.220.49

アドレスについては後述。

multiclusterとmulti-1.15の二つのクラスタにHTTPサーバーとLoadBalancer Serviceをデプロイし、ホストOSからcurlした例

[zaki@cloud-dev kind]$ kc get svc -n sample-app --context=kind-multicluster 
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)        AGE
sample-http2   LoadBalancer   10.108.20.215   172.20.220.1   80:30287/TCP   26s
[zaki@cloud-dev kind]$ kc get svc -n sample-app --context=kind-multi-1.15 
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
sample-http2   LoadBalancer   10.99.250.121   172.20.220.50   80:30988/TCP   6m30s
[zaki@cloud-dev kind]$ curl 172.20.220.50
<html><body><h1>It works!</h1></body></html>
[zaki@cloud-dev kind]$ curl 172.20.220.1
<html><body><h1>It works!</h1></body></html>

そしてさらに"kind"クラスタ(本記事で一番最初に作ったオプション未指定のクラスタ)に何もしないCentOS7 podをデプロイし、そこから上記の2クラスタ上にあるHTTPサーバーへcurlしてみる。

[zaki@cloud-dev kind]$ kc run app-pod --image=centos:7 --context=kind-kind -- tail -f /dev/null
pod/app-pod created
[zaki@cloud-dev kind]$ kc get pod --context=kind-kind 
NAME      READY   STATUS    RESTARTS   AGE
app-pod   1/1     Running   0          28s
[zaki@cloud-dev kind]$ kc exec --context=kind-kind -it app-pod -- bash
[root@app-pod /]# curl 172.20.220.50
<html><body><h1>It works!</h1></body></html>
[root@app-pod /]# curl 172.20.220.1 
<html><body><h1>It works!</h1></body></html>
[root@app-pod /]# 

この通り、クラスタまたいだ通信もOK (後述)

Dockerネットワーク

kindでクラスタ作ると、kind用のDockerネットワークが自動で作成される。

[zaki@cloud-dev kind]$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
f28d1c1cb87b        2nd-network         bridge              local
bfb2b89e5a15        bridge              bridge              local
4a5b091ec94b        host                host                local
11a5b9bc6a2c        kind                bridge              local  # <- これ
7a62608ea9c3        my-network          bridge              local
86ed4375e66a        none                null                local

これはクラスタごとでなく、全クラスタで共通っぽい。
(何かオプション指定で別のネットワークを作成できるかもしれないけど、少なくともデフォルトでは共通)

なので、基本的に全クラスタ全ノードは同じネットワーク上に存在する構成になる。

手元の環境だとアドレス設定はこんな感じ

[zaki@cloud-dev kind]$ docker network inspect kind
[
    {
        "Name": "kind",
        "Id": "11a5b9bc6a2c9938b54fa1fe9e84e05fa9eaa3883a0eda927cbb42c687ba7bc5",
        "Created": "2020-08-01T11:10:50.769278063+09:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": true,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.20.0.0/16",
                    "Gateway": "172.20.0.1"
                },
                {
                    "Subnet": "fc00:f853:ccd:e793::/64"
                }
            ]
        },

[...]

MetalLBに使用するアドレスは、このサブネットに含まれる値を適当に(他と重複しないように)設定すればとりあえず動く。(16ビットマスクなので、相当なことが無い限り重複はしないはず。。)


Dockerのネットワークについてはこちらの資料も参照(ステマ)

speakerdeck.com


朝活ブログのつもりが昼過ぎまで時間かかってしまった(笑)


kind使う場合の注意点

確かにホストOSのストレージは結構喰ってる