# Create a kind cluster with Podman
## Preface
本篇文章會介紹如何安裝 kind、設定 node provider 為 podman,並實作建立一座 kind Cluster
## kind 簡介
kind is a tool for running local Kubernetes clusters using Podman/Docker container “nodes”.
kind was primarily designed for testing Kubernetes itself, but may be used for local development or CI/CD.
## OS 環境設定
### SLES 15 SP5
```bash!
# 1.在 SLES 15 SP5 上安裝 4.8.3 版本的 podman 和 netavark
$ sudo SUSEConnect -p sle-module-containers/15.5/x86_64
$ sudo zypper -n install podman-4.8.3-150500.3.9.1 netavark-1.5.0-150500.1.4
# 2. 啟動 cgroup v2
## 2.1. 在 GRUB_CMDLINE_LINUX_DEFAULT 這個變數中添加 "systemd.unified_cgroup_hierarchy=1"
$ sudo nano /etc/default/grub
...
GRUB_CMDLINE_LINUX_DEFAULT="splash=silent mitigations=auto quiet security=apparmor crashkernel=302M,high crashkernel=72M,low systemd.unified_cgroup_hierarchy=1"
## 2.2. update GRUB 讓設定永久生效
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg
# 3. 設定 Pomdman 的 networkBackend 為 netavark
$ sudo cp /usr/share/containers/containers.conf /etc/containers/containers.conf
## 3.1. 修改設定檔,將 networkBackend 的值改為 netavark
$ sudo nano /etc/containers/containers.conf
[network]
network_backend = "netavark"
## 3.2. 重設 Podman
$ sudo podman system reset --force
# 4. 設定 kind 使用 Podman 建立 node
$ sudo nano /etc/profile
...
## 添加以下字串進去
export KIND_EXPERIMENTAL_PROVIDER="podman"
# 5. 重新開機
$ sudo init 6
# 6. 檢查 podman 網路是否成功
$ podman info --format {{.Host.NetworkBackend}}
netavark
# 7. 檢查 cgroup 版本
$ podman info | grep 'cgroupVersion'
cgroupVersion: v2
# 8. 檢查 kind 的 node provider 是否設為 podman
$ env | grep KIND_EXPERIMENTAL_PROVIDER
KIND_EXPERIMENTAL_PROVIDER=podman
```
### Ubuntu server 22.04
```bash!
# 1. 下載 podman,版本 v4.8.3
$ curl -fsSL -o podman-linux-amd64.tar.gz https://github.com/mgoltzsche/podman-static/releases/download/v4.8.3/podman-linux-amd64.tar.gz && \
tar -xzf podman-linux-amd64.tar.gz && \
sudo cp -r podman-linux-amd64/usr podman-linux-amd64/etc /
> To restart containers with restart-policy=always on boot, enable the podman-restart systemd service
# 2. 下載 netavark、aardvark-dns 和一些必要套件
$ sudo apt update &&
sudo apt-get install -y \
containernetworking-plugins \
protobuf-compiler \
cargo \
make \
jq \
uidmap && \
sudo apt autoremove
$ git clone https://github.com/containers/netavark.git && \
cd netavark/ && \
sudo make && \
sudo cp ./bin/netavark /usr/local/lib/podman/
$ git clone https://github.com/containers/aardvark-dns.git && \
cd aardvark-dns/ && \
sudo make && \
sudo cp ./bin/aardvark-dns /usr/local/lib/podman/
# 3. 修改設定檔,將 networkBackend 的值改為 netavark
$ sudo nano /etc/containers/containers.conf
[network]
network_backend = "netavark"
## 3.1. 重設 Podman
$ sudo podman system reset --force
# 4. 設定 kind 使用 Podman 建立 node
$ sudo nano /etc/profile
...
## 添加以下字串進去
export KIND_EXPERIMENTAL_PROVIDER="podman"
# 5. 重新開機
$ sudo init 6
# 6. 檢查 podman 網路是否成功
$ podman info --format {{.Host.NetworkBackend}}
netavark
# 7. 檢查 cgroup 版本
$ podman info | grep 'cgroupVersion'
cgroupVersion: v2
# 8. 檢查 kind 的 node provider 是否設為 podman
$ env | grep KIND_EXPERIMENTAL_PROVIDER
KIND_EXPERIMENTAL_PROVIDER=podman
```
### Google Cloud Shell
<font color=red>以下設定在 Google Cloud Shell 被重製後都會失效。</font>
```bash!
# 1. 移除 docker
$ sudo apt-get remove -y docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
$ sudo apt autoremove
# 2. 啟用 IPV6
$ sudo nano /etc/sysctl.conf
...
net.ipv6.conf.all.disable_ipv6=0
net.ipv6.conf.default.disable_ipv6=0
net.ipv6.conf.lo.disable_ipv6=0
$ sudo sysctl -w net.ipv6.conf.all.disable_ipv6=0
$ sudo sysctl -w net.ipv6.conf.default.disable_ipv6=0
$ sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0
$ sudo sysctl -p
# 3. 下載 podman,版本 v4.8.3
$ curl -fsSL -o podman-linux-amd64.tar.gz https://github.com/mgoltzsche/podman-static/releases/download/v4.8.3/podman-linux-amd64.tar.gz && \
tar -xzf podman-linux-amd64.tar.gz && \
sudo cp -r podman-linux-amd64/usr podman-linux-amd64/etc /
# 4. 下載 netavark、aardvark-dns 和一些必要套件
$ sudo apt update &&
sudo apt-get install -y \
containernetworking-plugins \
protobuf-compiler \
cargo \
make \
jq \
uidmap && \
sudo apt autoremove
$ git clone https://github.com/containers/netavark.git && \
cd netavark/ && \
sudo make && \
sudo cp ./bin/netavark /usr/local/lib/podman/
$ git clone https://github.com/containers/aardvark-dns.git && \
cd aardvark-dns/ && \
sudo make && \
sudo cp ./bin/aardvark-dns /usr/local/lib/podman/
# 5. 修改設定檔,將 networkBackend 的值改為 netavark
$ sudo nano /etc/containers/containers.conf
[network]
network_backend = "netavark"
## 5.1. 重設 Podman
$ sudo podman system reset --force
# 6. 設定 kind 使用 Podman 建立 node
$ sudo nano /etc/profile
...
## 添加以下字串進去
export KIND_EXPERIMENTAL_PROVIDER="podman"
# 7. 檢查 podman 網路是否成功
$ podman info --format {{.Host.NetworkBackend}}
netavark
# 8. 檢查 cgroup 版本
$ podman info | grep 'cgroupVersion'
cgroupVersion: v2
# 9. 檢查 kind 的 node provider 是否設為 podman
$ sudo su - k130
$ env | grep KIND_EXPERIMENTAL_PROVIDER
KIND_EXPERIMENTAL_PROVIDER=podman
```
## 安裝 kind
```bash!
# 1. 安裝 kind
$ curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 && \
chmod +x ./kind && \
sudo mv ./kind /usr/local/bin/kind
# 2. 檢查 Podman 版本
$ sudo podman version
Client: Podman Engine
Version: 4.8.3
API Version: 4.8.3
Go Version: go1.21.8
Built: Tue Mar 19 20:00:00 2024
OS/Arch: linux/amd64
# 3. 檢查 kind 版本
$ sudo kind --version
kind version 0.23.0
```
## 建立 Cluster
```bash!
$ mkdir ~/k8s
$ echo 'kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: c30
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.98.0.0/12"
disableDefaultCNI: true
nodes:
- role: control-plane
image: docker.io/kindest/node:v1.30.0
- role: worker
image: docker.io/kindest/node:v1.30.0' > ~/k8s/c30.yaml
$ sudo kind create cluster --config ~/k8s/c30.yaml
enabling experimental podman provider
Creating cluster "c30" ...
✓ Ensuring node image (docker.io/kindest/node:v1.30.0) 🖼
✓ Preparing nodes 📦 📦
✓ Writing configuration 📜
✓ Starting control-plane 🕹️
✓ Installing StorageClass 💾
✓ Joining worker nodes 🚜
Set kubectl context to "kind-c30"
You can now use your cluster with:
kubectl cluster-info --context kind-c30
Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/
```
### kind Cluster 設定檔詳解
```yaml=
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: c30
networking:
podSubnet: "10.244.0.0/16"
serviceSubnet: "10.98.0.0/12"
disableDefaultCNI: true
nodes:
- role: control-plane
image: docker.io/kindest/node:v1.30.0
- role: worker
image: docker.io/kindest/node:v1.30.0
```
- `kind: Cluster`,指定要設定的是 KIND Cluster
- `apiVersion: kind.x-k8s.io/v1alpha4`,KIND 設定檔的版號,不同的版號在設定檔中的一些設定值或選項,它們的含意會變得不同。
- `name: c30`,設定 KIND Cluster 的名字,這裡取名 `c30`。
- `neteorking`,設定 Cluster 的網路,有很多設定可以客製化。
- `podSubnet`,設定 pod 會得到的 IP 範圍。
- `serviceSubnet`,設定 Kubernetes service 會得到的 IP 範圍。
- `disableDefaultCNI: true`,KIND 會附帶一個簡單的網路實作(“kindnetd”),這是根據標準的 CNI 插件實現的(如 `ptp`, `host-local` 等)和簡單的 netlink routes。
- 這個 CNI 也會處理 IP 偽裝。
- 可以選擇關閉預設的 CNI 設定,改安裝其他的 CNI。這是一個進階用戶功能,支援有限,但已知很多常見的 CNI 設定檔都能正常運作,例如 Calico。。
## 檢查 c30 Cluster
```bash!
$ sudo kind get clusters
enabling experimental podman provider
c30
$ sudo kind get nodes --name c30
enabling experimental podman provider
c30-control-plane
c30-worker
$ sudo podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3eb5630a3ee3 docker.io/kindest/node:v1.30.0 38 minutes ago Up 38 minutes 127.0.0.1:40297->6443/tcp c30-control-plane
f0d28b8e2a4e docker.io/kindest/node:v1.30.0 38 minutes ago Up 37 minutes c30-worker
```
## 設定 K8S Node
```bash!
$ mkdir ~/cni/
$ curl -sL "$(curl -sL https://api.github.com/repos/containernetworking/plugins/releases/latest | jq -r '.assets[].browser_download_url' | grep 'linux-amd64.*.tgz$')" -o ~/cni/cni-plugins.tgz
# 如果上面那行錯誤,就執行這行命令
$ curl -sL https://github.com/containernetworking/plugins/releases/download/v1.5.0/cni-plugins-linux-amd64-v1.5.0.tgz -o ~/cni/cni-plugins.tgz
$ tar xf ~/cni/cni-plugins.tgz -C ~/cni; rm ~/cni/cni-plugins.tgz
$ sudo podman cp ~/cni/bridge c30-control-plane:/opt/cni/bin/bridge
$ sudo podman cp ~/cni/bridge c30-worker:/opt/cni/bin/bridge
```
## 登入 Control Plane
```bash!
$ sudo podman exec -it c30-control-plane bash
root@c30-control-plane:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
NAME="Debian GNU/Linux"
VERSION_ID="12"
VERSION="12 (bookworm)"
VERSION_CODENAME=bookworm
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```
## 安裝 Canal 網路套件
```bash!
# 在 c30-control-plane 中執行已下命令
$ kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/canal.yaml
## 檢查 Canal 網路套件
$ kubectl -n kube-system get pods
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-6df7596dbd-c2smj 1/1 Running 0 7m17s
canal-kmjk9 2/2 Running 0 7m17s
canal-vtxlq 2/2 Running 0 7m18s
coredns-7db6d8ff4d-2mrc7 1/1 Running 1 63m
coredns-7db6d8ff4d-cjhnn 1/1 Running 0 63m
etcd-c30-control-plane 1/1 Running 0 64m
kube-apiserver-c30-control-plane 1/1 Running 0 64m
kube-controller-manager-c30-control-plane 1/1 Running 2 (5m57s ago) 64m
kube-proxy-8jrdn 1/1 Running 0 63m
kube-proxy-l9dpx 1/1 Running 0 63m
kube-scheduler-c30-control-plane 1/1 Running 2 (5m ago) 64m
```
## 檢測 KIND K8S
```bash!
$ kubectl run nginx --image=quay.io/cloudwalker/nginx
pod/nginx created
$ kubectl get pods nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx 1/1 Running 0 10m 10.244.1.5 c30-worker <none> <none>
$ curl $(kubectl get pods nginx --no-headers -o custom-columns=ip:.status.podIP)
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
$ kubectl delete pod nginx
pod "nginx" deleted
$ exit
```
## 關閉 c30 Cluster
```bash!
$ sudo podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
3eb5630a3ee3 docker.io/kindest/node:v1.30.0 About an hour ago Up About an hour 127.0.0.1:40297->6443/tcp c30-control-plane
f0d28b8e2a4e docker.io/kindest/node:v1.30.0 About an hour ago Up About an hour
c30-worker
$ sudo podman stop c30-control-plane c30-worker
c30-worker
c30-control-plane
```
## 刪除 c30 Cluster
```bash!
$ sudo kind get clusters
enabling experimental podman provider
c30
$ sudo kind delete clusters c30
enabling experimental podman provider
Deleted nodes: ["c30-control-plane" "c30-worker"]
Deleted clusters: ["c30"]
$ sudo podman ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
```
## 管理不同叢集
在建立 Kind 叢集後,預設情況下,如果未設定 `$KUBECONFIG` 環境變數,則 K8s 叢集入口憑證檔會儲存在 `${HOME}/.kube/config` 中。
不過因為我們建 kind 的時候有加 `sudo` ,所以 `kubeconfig` 會放在 `/root` 底下,如果有多個叢集的話,會 append 進同一個 `kubeconfig` 檔案裡面。
```bash!
# 1. 檢視在 KubeConfig 中定義的所有叢集
$ sudo kubectl config get-clusters
NAME
kind-c30
kind-c29
# 2. 檢視在 KubeConfig 中定義的所有 Contexts
$ sudo kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
kind-c29 kind-c29 kind-c29
* kind-c30 kind-c30 kind-c30
# 3. 檢視當前所在的 Contexts
$ sudo kubectl config current-context
kind-c30
# 4. 切換要管理的 K8s 叢集
$ sudo kubectl config use-context kind-c29
Switched to context "kind-c29".
# 5. 檢視 C29 叢集 Node 狀態
$ sudo kubectl get nodes
NAME STATUS ROLES AGE VERSION
c29-control-plane Ready control-plane 23m v1.29.1
c29-worker Ready <none> 22m v1.29.1
```
>### What is context meaning?
>
> * kubeconfig 檔案中的 Context 元素用於將存取叢集的參數集中在一個方便的名稱下。
> * 每個 context 有三個參數:
> * cluster:集群的網址或名稱
> * namespace:要使用的 K8s namespace
> * user:登入的使用者
> * kubectl 指令 預設會使用 目前設定檔 (`KubeConfig`) 裡 `current context` 的參數來存取要管理的 Kubernetes 集群。