---
# System prepended metadata

title: Setup K3d on Alpine Linux

---

# Setup K3D on Alpine Linux

## 1. Prerequest

- [x] 已安裝 Alpine Linux 3.20.x

## 2. 環境準備

以下環境設定會使用 `Podman` 而不是 `Docker`，因為 Podman 有 [Docker API compatibility layer](https://podman.io/blogs/2020/06/29/podman-v2-announce.html#restful-api)。 k3d 使用 Docker API 與 Podman v4 或更新版相容.

```bash!
# 2.1. 新增套件儲存庫 (請使用 root 執行)
$ echo 'http://dl-cdn.alpinelinux.org/alpine/latest-stable/main/
http://dl-cdn.alpinelinux.org/alpine/latest-stable/community/' > /etc/apk/repositories

# 2.2. 更新套件，並安裝 Podman
$ apk update && apk upgrade && \
apk add podman curl nano iptables-legacy sudo && exit 

# 2.3. 確認 CGroup 版本為 v2 
$ cat /etc/rc.conf | grep "rc_cgroup_mode"
#rc_cgroup_mode="unified"

> [註] "unified" mounts cgroups version 2 on /sys/fs/cgroup

# 2.4. Enable and start the cgroups and podman services
$ sudo rc-update add cgroups && \
sudo rc-service cgroups start && \
sudo rc-update add podman

# 2.5. Setup rootless mode with Podman
$ echo "$USER":500000:65536 | sudo tee /etc/subuid && \
echo "$USER":500000:65536 | sudo tee /etc/subgid

# 2.6. load needed kernel modules
$ echo -e "tun\nfuse\nip_tables\nip_vs\nbridge\nbr_netfilter" | sudo tee -a /etc/modules

# 2.7. 設定開機自動建置 Podman Socket 軟連結
$ sudo rc-update add local && \
echo '[[ -h /var/run/docker.sock ]] || ln -s /run/podman/podman.sock /var/run/docker.sock' | sudo tee /etc/local.d/rc.local.start && \
sudo chmod +x /etc/local.d/rc.local.start

# 2.8. 重啟主機
$ sudo reboot

# 2.9. 檢查需要的 Service 開機會自動啟動
$ sudo rc-status | grep -Ew "podman|local|cgroups"
 cgroups                                                           [  started  ]
 podman                                                [  started 00:03:15 (0) ]
 local                                                             [  started  ]
 
# 2.10. 檢查 Podman Socket 軟連結是否存在
$ ls -l /var/run/docker.sock
lrwxrwxrwx    1 root     root            23 Jul 22 09:59 /var/run/docker.sock -> /run/podman/podman.sock

# 2.11. 下載與設定 kubectl 命令
$ curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
sudo chmod +x kubectl && \
sudo mv kubectl /usr/local/bin/

# 2.12. 確認 kubectl 版本
$ kubectl version --client
Client Version: v1.30.3
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3

# 2.13. 安裝當前最新版本的 k3d
$ wget -q -O - https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

# 2.14. 檢查 k3d 版本
$ k3d version
k3d version v5.7.2
k3s version v1.29.6-k3s2 (default)

# 2.15. 設定 IPVS Mode
$ sudo apk add dpkg && \
sudo update-alternatives --install /sbin/ip6tables ip6tables /sbin/iptables-legacy 10 --slave /sbin/ip6tables-restore ip6tables-restore /sbin/ip6tables-legacy-restore --slave /sbin/ip6tables-save ip6tables-save /sbin/ip6tables-legacy-save

# 2.16. 建立 Podman Network
$ sudo podman network create --subnet 172.22.1.0/24 --gateway 172.22.1.254 --subnet fd52:2a5a:747e:3acd::/64 --gateway fd52:2a5a:747e:3acd::10 c29-network
```

## 3. Building a customized K3s image

```!
# 1. 建立工作目錄區
$ mkdir -p k3d/{bin,config/{c24/addon,c29/addon},wulin}

> [註] 以上命令需要由 Bash Shell 執行才會在 k3d 目錄區底下分別建立那 3 個子目錄

# 2. 設定 K3s 的 Containerd 能夠到 dkreg 下載 Container Images
$ echo 'server = "http://dkreg.kube-system:5000"
[host."http://dkreg.kube-system:5000"]
  capabilities = ["pull", "resolve", "push"]
  skip_verify = true' > ~/k3d/config/c29/hosts.toml

# 3. 
$ echo '{{ 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"' > ~/k3d/config/c29/config.toml.tmpl

# 3. 編輯 Containerfile
$ nano config/c29/Containerfile

# 4. Build Container Image
$ sudo podman build --no-cache -t k3s-add-runtime:v1.29.6-k3s1 ~/k3d/config/c29/
```

### 3.1. Containerfile:

```yaml!
ARG K3S_TAG="v1.29.6-k3s1"
ARG ALPINE_TAG="3.20.2"

FROM docker.io/rancher/k3s:$K3S_TAG as k3s
FROM docker.io/library/alpine:$ALPINE_TAG

# Install OCI runtime spec (crun, runsc)
RUN apk -U add crun bash && \
    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 /usr/bin

COPY --from=k3s / /

# Configuring containerd with Base template
RUN mkdir -p /var/lib/rancher/k3s/agent/etc/containerd/cert.d/dkreg.kube-system:5000
COPY config.toml.tmpl /var/lib/rancher/k3s/agent/etc/containerd/
COPY hosts.toml /var/lib/rancher/k3s/agent/etc/containerd/cert.d/dkreg.kube-system:5000/

VOLUME /var/lib/kubelet
VOLUME /var/lib/rancher/k3s
VOLUME /var/lib/cni
VOLUME /var/log

ENV PATH="$PATH:/bin/aux"

ENTRYPOINT ["/bin/k3s"]
CMD ["agent"]
```




## 4. 撰寫 k3d 叢集設定檔

```bash!
# 4.1. 編輯 k3d 叢集設定檔
$ nano ~/k3d/config/c29/c29.yaml
apiVersion: k3d.io/v1alpha5
kind: Simple
metadata:
  name: c29
servers: 1
agents: 2
kubeAPI:
  host: "k1.org"
  hostIP: "0.0.0.0"
  hostPort: "6443"
#image: docker.io/rancher/k3s:v1.29.6-k3s1
image: localhost/k3s-add-runtime:v1.29.6-k3s1
network: c29-network
token: c29Token
volumes:
  - volume: /home/bigred/k3d/wulin:/opt/kadm
    nodeFilters:
      - "server:0"
#  - volume: /home/bigred/k3d/cni:/opt/cni/bin
#    nodeFilters:
#      - "server:*"
#      - "agent:*"
#  - volume: /lib/modules:/lib/modules
#    nodeFilters:
#      - "server:*"
#      - "agent:*"
files:
  - description: 'Canal v1.27.3 Manifests'
    source: addon/canal-v1.27.3.yaml
    destination: /var/lib/rancher/k3s/server/manifests/canal.yaml
    nodeFilters:
    - "server:0"
  - description: 'Runtime Class: crun, runsc'
    source: addon/runtimeclass.yaml
    destination: /var/lib/rancher/k3s/server/manifests/runtimeclass.yaml
    nodeFilters:
    - "server:0"
ports:
  - port: 22100:22100
    nodeFilters:
      - "server:0"
  - port: 5000:5000
    nodeFilters:
      - "server:0"
  - port: 8080:8080
    nodeFilters:
      - "server:0"
  - port: 9000:9000
    nodeFilters:
      - "server:0"
  - port: 80:80
    nodeFilters:
      - "server:0"
  - port: 443:443
    nodeFilters:
      - "server:0"
hostAliases:
  - ip: 1.1.1.1
    hostnames:
      - cloud.flare.dns
options:
  k3d:
    wait: true
    timeout: "60s"
    disableLoadbalancer: false
    disableImageVolume: false
    disableRollback: true
  k3s:
    extraArgs:
      - arg: "--cluster-cidr=10.244.0.0/16"
        nodeFilters:
          - server:*
      - arg: "--service-cidr=10.98.0.0/16"
        nodeFilters:
          - server:*
#      - arg: "--kube-proxy-arg=proxy-mode=ipvs"
#        nodeFilters:
#          - server:*
#          - agent:*
      - arg: "--egress-selector-mode=disabled"
        nodeFilters:
          - server:*
      - arg: "--disable=traefik"
        nodeFilters:
          - server:*
      - arg: "--flannel-backend=none"
        nodeFilters:
          - server:*
      - arg: "--disable-network-policy"
        nodeFilters:
          - server:*
    nodeLabels:
      - label: app=taroko
        nodeFilters:
          - "server:0"
  kubeconfig:
    updateDefaultKubeconfig: true
    switchCurrentContext: true
```

### 4.1. 設定檔欄位解釋

- `apiVersion: k3d.io/v1alpha5`，設定檔的版本，這將會在未來改變，因為我們會讓一切變得更穩定，這是必填的欄位。
- `kind: Simple`，定義要使用的設定檔案類型，在內部也有 Cluster 的設定，但尚未對外提供，這是必填的欄位。
- `metadata`，定義設定檔的重要資訊。
    - `name`，要賦予 K3D 叢集的名稱 (仍將以 `k3d-` 為前綴)。
- `servers`，定義要幾台 Control-plane 節點。
- `agents`，定義要幾台 Worker 節點。
- `kubeAPI`
    - `host`，定義 K3D 叢集的 Domain name
    - `hostIP`，將監聽 Kubernetes API 的位置
    - `hostPort`，Kubernetes API 在 Host 主機系統上監聽連接埠的對應位置
- `image`，定義要使用哪一個版本 Container Image 來建立 K3s
- `network`，使用已經存在的 Podman Netwrok，如果沒有則會建立新的 Podman Netwrok
- `subnet`，為新建立的 Cotainer 網路定義子網路
- `token`，指定叢集的 Token，K3s 使用 Token 來保護節點加入過程，Token 會在叢集加入新節點時進行身分驗證的動作。
- `volumes`，定義在 Host 主機有哪些 Volume 要掛載進 K3D 的節點。
    - `volume`，指定 Host 主機的目錄區，將之掛載到 K3D 的節點中。
        - 格式 : `/my/host/path:/path/in/node`
    - `nodeFilters`，定義要將 Volume 掛載到哪些 K3D 的節點中。
        - 格式 : `server:0` (第一台 Control-plane)，也可以用 `*` 代表每一台 Control-plane。
- `ports`，定義 K3d 叢集有哪些 Ports 要 Mapping 出來到 Host 主機。
    - `port`，將 K3s 節點 (Container) 的 ports 透過 serverlb 這台 Loadbalancer Maping 到 Host 主機的 ports (via the serverlb)
        - 格式 : `HOSTPORT:CONTAINERPORT`
    - `nodeFilters`，定義要將哪一台 K3s 的節點中的 Port Mapping 到 Host 主機上。
        - 格式 : `loadbalancer`
- `env`，將環境變數加入節點。
    - `envVar`，宣告要定義的環境變數。
        - Example: `bar=baz`
    - `nodeFilters`，定義要將環境變數加入哪台 K3d 節點。
        - Example: `server:0`
- `files`，定義要自動部屬哪些 Workload 在 K3d 叢集中。
    - `description`，描述 Workload 的字串
    - `source`，Workload 的 Yaml 檔內容，格式可以直接是檔案內容，或給檔案名稱。
        - Example1 :
            ```
            source: |
              apiVersion: v1
              kind: Namespace
              metadata:
                name: foo
            ```
        - Example2 :
            ```
            source: ns-baz.yaml
            ```
    - `destination`，Workload 的 Yaml 檔儲存在 K3d 節點中的哪個目錄區。
        - 格式: 可以是絕對路徑或相對路徑。
            - Example1 : 
                ```
                destination: k3s-manifests-custom/foo.yaml
                ```
                Workload 的 Yaml 檔實際會被存儲存成 `/var/lib/rancher/k3s/server/manifests/custom/foo.yaml`
            - Example2 :
                ```
                destination: /var/lib/rancher/k3s/server/manifests/baz.yaml
                nodeFilters:
                - "server:*"
                ```
                用絕對路徑定義 Workload 的 Yaml 檔，並指定要儲存在哪些 K3d 的節點上。
- `hostAliases`，定義要寫入到 K3D 叢集結點中的 `/etc/hosts` 檔案內容和 CoreDNS 的 NodeHosts 部分中
    - `ip`，服務的 IP Address
    - `hostnames`，服務的名稱
- `options`
    - `k3d`，k3d runtime 的設定
        - `wait: true`，等待群集可用後才返回
        - `timeout: "60s"`，定義建置 K3D 叢集超時的時間，如果超過時間會停止建立的動作。
        - `disableLoadbalancer: false`，停用在 Contol-plane 節點前面建立 LoadBalancer。
        - `disableImageVolume: false`，不要為 K3D 節點的根檔案系統建立永存的 Volume
        - `disableRollback: false`，如果叢集建立失敗，不要回溯變更。
        - `loadbalancer`，設定 Nginx Loadbalancer 的設定檔
            - `configOverrides`，要覆蓋的設定值
                - Example: `settings.workerConnections=2048`
- `k3s`，設定 K3s 本身
    - `extraArgs`，傳給 `k3s server|agent` 指令的附加參數；與 `--k3s-arg` 相同
        - Example:
            ```
            k3s:
              extraArgs:
                - arg: "--tls-san=my.host.domain"
                nodeFilters:
                  - server:*
            ```
    - `nodeLabels`，定義要對哪一台 K3s 的節點貼上標籤
        - Example:
            ```
            nodeLabels:
              - label: foo=bar 
                nodeFilters:
                  - agent:1
            ```
- `kubeconfig`，設定 KubeConfig
    - `updateDefaultKubeconfig: true`，會把建立好的 K3s 叢集加到預設的 KubeConfig 檔案中。
    - `switchCurrentContext: true`，會將 KubeConfig 檔中的 Current Context 切換到新建立好的叢集。
- `runtime`，設定 Container Runtime (Podman/Docker)
    - `gpuRequest: all`，要新增至 K3s 叢集節點的 GPU 裝置。
        - `all` 會把所有 GPU 裝置都掛載進去。
    - `labels`，要對 Host 主機上的 K3s 節點 (Conttainer) 貼哪些標籤。
        - Example:
            ```
            labels:
              - label: bar=baz 
                nodeFilters:
                  - agent:1
            ```
    - `ulimits`，設定 Process 的資源
        - `name: nofile`，限制 Open File 的數量 (max number of open file descriptors)
        - `soft: 26677`，在任何指定時間內，允許一個程序使用的最大的 Open File 數量。
        - `hard: 26677`，即使暫時超過 soft limit，程序仍可使用的最大的 Open File 數量。
        > 例如，預設情況下，一個程序可以擁有的開啟檔案數量的 soft limit 是 1024。這表示一個程序在任何時候最多可以有 1024 個開啟的檔案。但是，如果程序嘗試開啟超過 1024 個檔案，則不允許這樣做。預設開啟檔案數量的 hard limit 為 4096。這表示，即使暫時超過 1024 的 soft limit，程序開啟的檔案也不會超過 4096 個。
        > soft limit 之所以有用，是因為它們允許程序暫時超過其限制而不會造成問題。例如，Web 伺服器在處理大量流量時，可能需要開啟大量檔案。soft limit 允許網頁伺服器在不當機的情況下執行此動作。但是，hard limit 可以防止 Web 伺服器使用過多資源而導致系統出現問題。
        > soft limit 和 hard limit 可以為每個使用者和每個程序設定。預設限制由系統管理員設定，但使用者可以變更自己的限制。要變更 soft limit 或hard limit，可以使用 `ulimit` 指令。

## 5. 準備 Auto-Deploying Manifests

```!
$ wget --no-check-certificate --no-cache -P ~/k3d/config/c29/addon/ https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/canal.yaml
```

## 6. 在 K3s 中甚麼是 Tunnel Proxy? 為什麼要使用它? K8s 為什麼沒有?

在 K3s 中預設都會有 Tunnel Proxy，它的功能是讓 API Server 和 Worker Node 節點之間做溝通。

因為 K3s 在設計的時候是給









