Kubernetes in Docker (kind)

- notes kubernetes

kind 是 Kubernetes 下的一個子專案,它其實是 Kubernetes IN Docker 的縮寫。

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 最初是被設計用來測試 Kubernetes 用的,畢竟要架設一個可用的最小 Kubernetes 在過去並不是那麼容易,並且它目標只要有 Docker 的環境就能執行。運用 kind 搭配指定的 Kubernetes image 快速建立出特定版本的 Kubernetes 以便快速地執行各種測試。

因為有 Docker 就能跑的設計,讓它也成為在本地端開發的選項之一。開發者不用特別裝特定的 VM 或是得處理因為安裝開發環境而弄亂自己的開發環境。於是,kind 也被廣泛使用在 CI 的情境。舉例來說,我們可以使用 gitlab ci 搭配 docker runner 與 kind 搭建 Kubernetes 測試環境。

kind in 5 minutes

kind 安裝方式相當簡單,直接在 release 頁面下載執行檔回來用即可。

kind creates and manages local Kubernetes clusters using Docker container 'nodes'

Usage:
  kind [command]

Available Commands:
  build       Build one of [base-image, node-image]
  completion  Output shell completion code for the specified shell (bash or zsh)
  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.

配合安裝的 kind 版本,在 release notes 上查出建議的 image 對照表。用它能建出適當版本的 kubernetes

kind create cluster --image kindest/node:v1.16.4@sha256:b91a2c2317a000f3a783489dfb755064177dbc3a0b2f4147d50f04825d016f55
(⎈ N/A:N/A)➜  ~ kind create cluster --image kindest/node:v1.16.4@sha256:b91a2c2317a000f3a783489dfb755064177dbc3a0b2f4147d50f04825d016f55

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

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
(⎈ N/A:N/A)➜  ~ kind create cluster --image kindest/node:v1.15.7@sha256:e2df133f80ef633c53c0200114fce2ed5e1f6947477dbc83261a6a921169488d

Creating cluster "kind" ...
 ✓ Ensuring node image (kindest/node:v1.15.7) 🖼
 ✓ 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 後,接著就能直接用 kubectl 來操作它,它建立了單 node 的 kubernetes

(⎈ kind-kind:default)➜  ~ kubectl get node
NAME                 STATUS   ROLES    AGE    VERSION
kind-control-plane   Ready    master   3m3s   v1.15.7

並裝了這些東西:

(⎈ kind-kind:default)➜  ~ kubectl get all --all-namespaces
NAMESPACE            NAME                                             READY   STATUS    RESTARTS   AGE
kube-system          pod/coredns-5d4dd4b4db-n8wpl                     1/1     Running   0          2m31s
kube-system          pod/coredns-5d4dd4b4db-wxtd9                     1/1     Running   0          2m31s
kube-system          pod/etcd-kind-control-plane                      1/1     Running   0          91s
kube-system          pod/kindnet-klbxt                                1/1     Running   0          2m31s
kube-system          pod/kube-apiserver-kind-control-plane            1/1     Running   0          109s
kube-system          pod/kube-controller-manager-kind-control-plane   1/1     Running   0          114s
kube-system          pod/kube-proxy-qhrm5                             1/1     Running   0          2m31s
kube-system          pod/kube-scheduler-kind-control-plane            1/1     Running   0          112s
local-path-storage   pod/local-path-provisioner-d4947b89c-nv667       1/1     Running   0          2m31s

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

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

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

NAMESPACE            NAME                                               DESIRED   CURRENT   READY   AGE
kube-system          replicaset.apps/coredns-5d4dd4b4db                 2         2         2       2m31s
local-path-storage   replicaset.apps/local-path-provisioner-d4947b89c   1         1         1       2m31s

在 CI 中執行

運用 kind 只要有 docker 就能執行的特性,我們可以試著把它放入支援 docker 的 CI 服務中執行。要在 gitlab ci 中執行 docker 指令,我們需要使用到 docker in docker 的做法,對細節有興趣的朋友可以直接看範例,概念其實只有:

相容性測試

由於 Kubernetes 本身與各式相依的專案還在快速發展的階段,為了確認各個環節的相容性常,常需要將特定的專案或相依的函式庫在不同的 Kubernetes 版本中安裝測試。

對現代化的 CI 來說 matrix build 是相當基本的功能,我們能用個簡單的 Deployment 來展示相容性問題:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

選用了一個即將被移除的版本 extensions/v1beta1 作為案例,在實際的情境它可能是:

多版本測試設定如下:

test:v1.14:
  <<: *vendor_test_job
  variables:
    KIND_NODE_TAG: v1.14.10@sha256:81ae5a3237c779efc4dda43cc81c696f88a194abcc4f8fa34f86cf674aa14977

test:v1.15:
  <<: *vendor_test_job
  variables:
    KIND_NODE_TAG: v1.15.7@sha256:e2df133f80ef633c53c0200114fce2ed5e1f6947477dbc83261a6a921169488d

test:v1.16:
  <<: *vendor_test_job
  variables:
    KIND_NODE_TAG: v1.16.4@sha256:b91a2c2317a000f3a783489dfb755064177dbc3a0b2f4147d50f04825d016f55

在這個 .gitlab-ci.yml 片段中,僅僅將標準範本的 kind 建立 cluster 指令參數化,透過環境變數填入指定的版本:

kind create cluster --image kindest/node:$KIND_NODE_TAG

日後的維護就只是:

  1. 增加新的 use case
  2. 移動支援的 Kubernetes 版本範圍

例如,加一組 v1.17 的版本:

test:v1.17:
  <<: *vendor_test_job
  variables:
    KIND_NODE_TAG: v1.17.0@sha256:9512edae126da271b66b990b6fff768fbb7cd786c7d39e86bdf55906352fdf62

案例展示

v1.15 的 Release Notes 中提到:

  • DaemonSet, Deployment, and ReplicaSet resources will no longer be served from extensions/v1beta1, apps/v1beta1, or apps/v1beta2 in v1.16. Migrate to the apps/v1 API, available since v1.9. Existing persisted data can be retrieved via the apps/v1 API.

所以,我們預期這次選用的 Deployment 範例會爆炸:

查詢 job 的記錄,會發現這樣的錯誤:

error: unable to recognize “nginx-deployment.yaml”: no matches for kind “Deployment” in version “extensions/v1beta1”

如果是一個真實的情境,就是我還不知道哪些東西升級後會有問題,當它有問題時,我回頭看一下 Release Note,原來應該改成 apps/v1 它在 v1.9 就支援了,我改成它可以放心地支援目前我所有的版本。修正後的版本,就讓整個 pipeline 內的測試都通過囉:

結語

在 Kubernetes 快速發展與軟體署部環境漸漸往它靠攏的潮流,要開發出品質穩定的服務、軟體真是相當大的挑戰。還好有 kind 這樣簡單易用的測試㻲境建構工具,能輔助開發者簡化測試環境準備的部分。

我們只需要再付出一點努力,維護 use case 並隨著每 3 至 6 個月,調整一下要測試的 Kubernetes 版本,這是相當值得投資的基礎建設。