# Install K3s in Containers without k3d
## 1. 環境準備
### 1.1. 準備 Podman Network
```bash!
$ sudo podman network create --subnet 172.22.1.0/24 --gateway 172.22.1.254 k3s-network
$ sudo podman network ls
NETWORK ID NAME DRIVER
78b8668c5acd k3s-network bridge
2f259bab93aa podman bridge
```
### 1.2. 設定必要核心參數
```
$ cat <<EOF | sudo tee -a /etc/sysctl.conf
net.bridge.bridge-nf-call-iptables = 1
fs.inotify.max_user_instances=8192
fs.inotify.max_user_watches=524288
net.netfilter.nf_conntrack_max = 131072
kernel.dmesg_restrict = 0
EOF
$ sudo reboot
```
### 1.3. 建立工作目錄
```bash!
$ mkdir -p "$HOME"/k3s/config/c31 &&
cd "$HOME"/k3s/config/c31
```
### 1.4. 產生設定 containerd 的基礎模板
```bash!
$ cat <<EOF > "$HOME"/k3s/config/c31/config.toml.tmpl
{{ template "base" . }}
# RuntimeClass Handler
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
runtime_type = "io.containerd.runsc.v1"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc.options]
BinaryName = "/usr/bin/runsc"
EOF
```
### 1.5. 產生設定私有 Container images Registry 設定檔
```bash!
$ cat <<EOF > "$HOME"/k3s/config/c31/registries.yaml
mirrors:
dkreg.kube-system:5000:
endpoint:
- "http://dkreg.kube-system:5000"
configs:
"dkreg.kube-system:5000":
auth:
username: bigred
password: bigred
EOF
```
### 1.6. 產生 runsc runtimeclass 設定檔
```bash!
$ cat <<EOF > "$HOME"/k3s/config/c31/runtimeclass.yaml
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: gvisor
handler: runsc
EOF
```
### 1.7. 設計客製化 K3s image
```
$ nano "$HOME"/k3s/config/c31/Containerfile
```
內容如下 :
```dockerfile=
# 由於官方提供的 K3s Container images 中缺少套件管理命令,
# 因此我們需要重新製作 Container image,並添加 apk 套件管理工具。
# 同時,我們也將設定好 Containerd,以確保能夠下載來自 dkreg 的 Container images。
ARG K3S_TAG="v1.31.1-k3s1"
ARG ALPINE_TAG="3.20"
FROM docker.io/rancher/k3s:$K3S_TAG as k3s
FROM docker.io/library/alpine:$ALPINE_TAG as collect
# 安裝 OCI runtime spec (包含 crun 和 runsc)
RUN apk -U add crun bash nftables mount && \
ARCH=$(uname -m); URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH} && \
wget ${URL}/runsc ${URL}/runsc.sha512 ${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512 && \
rm -f *.sha512 && \
chmod a+rx runsc containerd-shim-runsc-v1; mv runsc containerd-shim-runsc-v1 /bin
FROM scratch
COPY --from=k3s / /
RUN mkdir /usr/bin && \
mkdir /usr/lib
# Setup musl libc
## musl is an implementation of the C standard library built on top of the Linux system call API
COPY --from=collect /lib/ld-musl-x86_64.so.1 /lib
# Setup Bash Shell
COPY --from=collect /bin/bash /bin
COPY --from=collect /usr/lib/libreadline.so.8 /usr/lib
COPY --from=collect /usr/lib/libncursesw.so.6 /usr/lib
# Setup runsc
COPY --from=collect /bin/runsc /bin
COPY --from=collect /bin/containerd-shim-runsc-v1 /bin
# Setup crun
COPY --from=collect /usr/bin/crun /usr/bin
COPY --from=collect /usr/lib/libseccomp.so.2 /usr/lib
COPY --from=collect /usr/lib/libcap.so.2 /usr/lib
COPY --from=collect /usr/lib/libyajl.so.2 /usr/lib
# Setup nft
COPY --from=collect /usr/sbin/nft /usr/bin
COPY --from=collect /usr/lib/libnftables.so.1 /usr/lib
COPY --from=collect /usr/lib/libreadline.so.8 /usr/lib
COPY --from=collect /usr/lib/libmnl.so.0 /usr/lib
COPY --from=collect /usr/lib/libnftnl.so.11 /usr/lib
COPY --from=collect /usr/lib/libjansson.so.4 /usr/lib
COPY --from=collect /usr/lib/libgmp.so.10 /usr/lib
# Setup mount
COPY --from=collect /bin/mount /bin
COPY --from=collect /lib/libmount.so.1 /lib
COPY --from=collect /lib/libblkid.so.1 /lib
COPY --from=collect /usr/lib/libeconf.so.0 /usr/lib
# 設定 containerd 的基礎模板
## 將 runSC OCI runtimes 的相關設定新增進 containerd
COPY config.toml.tmpl /var/lib/rancher/k3s/agent/etc/containerd/
## 設定私有 Container images Registry,確保可以拉取來自 dkreg 的 Container images
## 參考文章 : https://docs.k3s.io/installation/private-registry#configs
COPY registries.yaml /etc/rancher/k3s/
# 設定 runsc RuntimeClass
COPY runtimeclass.yaml /var/lib/rancher/k3s/server/manifests/
VOLUME /var/lib/kubelet
VOLUME /var/lib/rancher/k3s
VOLUME /var/lib/cni
VOLUME /var/log
ENV PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/bin/aux"
ENV CRI_CONFIG_FILE="/var/lib/rancher/k3s/agent/etc/crictl.yaml"
ENTRYPOINT ["/bin/k3s"]
CMD ["agent"]
```
### 1.8. 建立客製化 K3s image
```bash!
$ K3S_TAG=v1.31.1-k3s1
$ sudo podman build --squash-all --no-cache \
--build-arg K3S_TAG="$K3S_TAG" \
-t k3s-add-runtime:"$K3S_TAG" \
"$HOME"/k3s/config/c${K3S_TAG:3:2}/
```
## 2. 安裝 K3s
### 2.1. 建立 server node
```bash!
# 2.1. 建立 podman volume
$ sudo podman volume create --label server=cni && \
sudo podman volume create --label server=kubelet && \
sudo podman volume create --label server=k3s && \
sudo podman volume create --label server=log
$ server_cni=$(sudo podman volume ls --format "{{.Name}}" --filter "label=server=cni" ) && \
server_kubelet=$(sudo podman volume ls --format "{{.Name}}" --filter "label=server=kubelet") && \
server_k3s=$(sudo podman volume ls --format "{{.Name}}" --filter "label=server=k3s") && \
server_log=$(sudo podman volume ls --format "{{.Name}}" --filter "label=server=log")
# 2.2. 建立 server node container
$ sudo podman run -d \
--name k3s-c31-server-0 \
--privileged \
-e CRI_CONFIG_FILE=/var/lib/rancher/k3s/agent/etc/crictl.yaml \
-e HOSTNAME=k3d-c31-server-0 \
-e K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml \
-e K3S_TOKEN=c31Token \
-e container=podman \
--network k3s-network \
--ip=172.22.1.11 \
--hostname k3s-c31-server-0 \
--ulimit nproc=131072:131072 \
--ulimit nofile=26677:26677 \
--volume /home/bigred/k3d/wulin:/opt/kadm:rw,rprivate,rbind \
--volume ${server_cni}:/var/lib/cni:rprivate,rw,nodev,exec,nosuid,rbind \
--volume ${server_kubelet}:/var/lib/kubelet:rprivate,rw,nodev,exec,nosuid,rbind \
--volume ${server_k3s}:/var/lib/rancher/k3s:rprivate,rw,nodev,exec,nosuid,rbind \
--volume ${server_log}:/var/log:rprivate,rw,nodev,exec,nosuid,rbind \
-p 80:80/tcp \
-p 443:443/tcp \
-p 5000:5000/tcp \
-p 5900-5905:5900-5905/tcp \
-p 6080:6080/tcp \
-p 8080:8080/tcp \
-p 9000:9000/tcp \
-p 22001-22002:22001-22002/tcp \
-p 22100-22102:22100-22102/tcp \
-p 51820:51820/tcp \
-p 6443:30001/tcp \
localhost/k3s-add-runtime:v1.31.1-k3s1 \
server --cluster-domain=k1.org \
--bind-address=172.22.1.11 \
--cluster-cidr=10.244.0.0/16 \
--service-cidr=10.98.0.0/16 \
--kubelet-arg=container-log-max-files=3 \
--kubelet-arg=container-log-max-size=10Mi \
--egress-selector-mode=disabled \
--disable=traefik \
--disable=servicelb \
--node-label app=taroko \
--tls-san k1.org \
--tls-san 172.22.1.11
```
### 2.2. 檢查 K3s server node 狀態
```bash!
$ sudo podman exec k3s-c31-server-0 kubectl get nodes
```
執行結果 :
```bash!
NAME STATUS ROLES AGE VERSION
k3s-c31-server-0 Ready control-plane,master 7s v1.31.1+k3s1
```
### 2.3. 檢查 K3s 所有 pods 狀態
```bash!
$ sudo podman exec k3s-c31-server-0 kubectl get pods -A
```
執行結果 :
```bash
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-56f6fc8fd7-t9hlb 1/1 Running 0 99s
kube-system local-path-provisioner-846b9dcb6c-xddbs 1/1 Running 0 99s
kube-system metrics-server-5985cbc9d7-889p8 1/1 Running 0 99s
```
### 2.4. 建立第一台 agent node
```bash!
# 3.1. 建立 podman volume
$ sudo podman volume create --label agent0=cni && \
sudo podman volume create --label agent0=kubelet && \
sudo podman volume create --label agent0=k3s && \
sudo podman volume create --label agent0=log
$ agent_0_cni=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent0=cni") && \
agent_0_kubelet=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent0=kubelet") && \
agent_0_k3s=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent0=k3s") && \
agent_0_log=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent0=log") && \
server_ip=$(sudo podman exec k3s-c31-server-0 ip a s eth0 | grep "inet " | awk '{print $2}')
# 3.2. 建立 agent node container
$ sudo podman run -d\
--name k3s-c31-agent-0 \
--hostname k3s-c31-agent-0 \
--privileged \
--env K3S_TOKEN=c31Token \
--env K3S_URL=https://${server_ip%/*}:6443 \
--env K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml \
--ulimit nproc=131072:131072 \
--ulimit nofile=26677:26677 \
--network k3s-network \
-p 22011:22011 \
-p 33891:33891 \
-v ${agent_0_cni}:/var/lib/cni:rw,rprivate,nodev,exec,nosuid,rbind \
-v ${agent_0_kubelet}:/var/lib/kubelet:rw,rprivate,nodev,exec,nosuid,rbind \
-v ${agent_0_k3s}:/var/lib/rancher/k3s:rw,rprivate,nodev,exec,nosuid,rbind \
-v ${agent_0_log}:/var/log:rw,rprivate,nodev,exec,nosuid,rbind \
localhost/k3s-add-runtime:v1.31.1-k3s1 \
agent
```
### 2.5. 檢查 K3s agent node 狀態
```bash!
$ sudo podman exec k3s-c31-server-0 kubectl get nodes
```
執行結果 :
```bash!
NAME STATUS ROLES AGE VERSION
k3s-c31-agent-0 Ready <none> 3s v1.31.1+k3s1
k3s-c31-server-0 Ready control-plane,master 2m51s v1.31.1+k3s1
```
### 2.6. 檢查 K3s 所有 pods 狀態
```bash!
$ sudo podman exec k3s-c31-server-0 kubectl get pods -A
```
執行結果 :
```bash
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-56f6fc8fd7-t9hlb 1/1 Running 0 3m39s
kube-system local-path-provisioner-846b9dcb6c-xddbs 1/1 Running 0 3m39s
kube-system metrics-server-5985cbc9d7-889p8 1/1 Running 0 3m39s
```
### 2.7. 建立第二台 agent node
```bash!
# 3.1. 建立 podman volume
$ sudo podman volume create --label agent1=cni && \
sudo podman volume create --label agent1=kubelet && \
sudo podman volume create --label agent1=k3s && \
sudo podman volume create --label agent1=log
$ agent_2_cni=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent1=cni") && \
agent_2_kubelet=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent1=kubelet") && \
agent_2_k3s=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent1=k3s") && \
agent_2_log=$(sudo podman volume ls --format "{{.Name}}" --filter "label=agent1=log") && \
server_ip=$(sudo podman exec k3s-c31-server-0 ip a s eth0 | grep "inet " | awk '{print $2}')
# 3.2. 建立 server node container
$ sudo podman run -d\
--name k3s-c31-agent-1 \
--hostname k3s-c31-agent-1 \
--privileged \
--env K3S_TOKEN=c31Token \
--env K3S_URL=https://${server_ip%/*}:6443 \
--env K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml \
--ulimit nproc=131072:131072 \
--ulimit nofile=26677:26677 \
--network k3s-network \
-p 22012:22012 \
-p 33892:33892 \
-v ${agent_2_cni}:/var/lib/cni:rw,rprivate,nodev,exec,nosuid,rbind \
-v ${agent_2_kubelet}:/var/lib/kubelet:rw,rprivate,nodev,exec,nosuid,rbind \
-v ${agent_2_k3s}:/var/lib/rancher/k3s:rw,rprivate,nodev,exec,nosuid,rbind \
-v ${agent_2_log}:/var/log:rw,rprivate,nodev,exec,nosuid,rbind \
localhost/k3s-add-runtime:v1.31.1-k3s1 \
agent
```
### 2.8. 檢查 K3s agent node 狀態
```bash!
$ sudo podman exec k3s-c31-server-0 kubectl get nodes
```
執行結果 :
```bash!
NAME STATUS ROLES AGE VERSION
k3s-c31-agent-0 Ready <none> 65s v1.31.1+k3s1
k3s-c31-agent-1 Ready <none> 18s v1.31.1+k3s1
k3s-c31-server-0 Ready control-plane,master 3m53s v1.31.1+k3s1
```
### 2.9. 檢查 K3s 所有 pods 狀態
```bash!
$ sudo podman exec k3s-c31-server-0 kubectl get pods -A
```
執行結果 :
```bash
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-56f6fc8fd7-t9hlb 1/1 Running 0 4m3s
kube-system local-path-provisioner-846b9dcb6c-xddbs 1/1 Running 0 4m3s
kube-system metrics-server-5985cbc9d7-889p8 1/1 Running 0 4m3s
```
### 2.10. 設定 KubeConfig
```bash!
$ mkdir -p "$HOME"/.kube && \
sudo podman cp k3s-c31-server-0:/output/kubeconfig.yaml "$HOME"/.kube/config && \
sudo chown -R $(id -u):$(id -g) "$HOME"/.kube
```
### 2.11. 確認可直接執行 kubectl
```bash!
$ kubectl get nodes -o wide
```
> 安裝 `kubectl` 命令
> ```!
> $ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
> chmod +x kubectl; sudo mv kubectl /usr/local/bin/
> ```
執行結果 :
```
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE
KERNEL-VERSION CONTAINER-RUNTIME
k3s-c31-agent-0 Ready <none> 2m48s v1.31.1+k3s1 172.22.1.4 <none> K3s v1.31.1+k3s1 6.6.50-0-lts containerd://1.7.21-k3s2
k3s-c31-agent-1 Ready <none> 2m30s v1.31.1+k3s1 172.22.1.5 <none> K3s v1.31.1+k3s1 6.6.50-0-lts containerd://1.7.21-k3s2
k3s-c31-server-0 Ready control-plane,master 4m24s v1.31.1+k3s1 172.22.1.11 <none> K3s v1.31.1+k3s1 6.6.50-0-lts containerd://1.7.21-k3s2
```
## 3. 完全清除環境
```bash!
$ sudo podman rm -f k3s-c31-server-0 k3s-c31-agent-0 k3s-c31-agent-1 \
k3s-working-container alpine-working-container && \
volume=$(for i in server agent0 agent1; do \
sudo podman volume ls --format "{{.Name}}" --filter label=${i}; \
done) && \
sudo podman volume rm $volume && \
sudo podman rmi localhost/k3s-add-runtime:v1.31.1-k3s1 \
docker.io/rancher/k3s:v1.31.1-k3s1 && \
rm -rf "$HOME"/.kube "$HOME"/k3s
```