---
# System prepended metadata

title: Install K3s in Containers without k3d

---

# 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
```








