# 利用 Multipass 在區域網路架設一套 MicroK8s 叢集環境

我之前有篇 [使用 MicroK8s 架設 Kubernetes 叢集的完整過程解析](/post/2021/12/05/Running-Kubernetes-with-MicroK8s) 文章，講解了完整的 [MicroK8s](https://microk8s.io) 體驗的過程。雖然你可以在 Windows 上輕鬆的架設出一套 Kubernetes 叢集，但是由於 MicroK8s 的底層使用到了 [Multipass](https://multipass.run) 工具來建立虛擬機器(VM)，事實上透過 Multipass 建立的 VM 在每次重開機的時候都會變更 IP 地址，因此只要 VM 重開機，基本上叢集就會壞掉，因為所有 Worker Nodes 將無法找到 Control Plane 的 IP 地址。這篇文章我來說說這個問題的解決方法！

首先，你必須先透過 [Multipass](https://multipass.run) 建立一台 Ubuntu VM，你可以參考我的 [如何替 Multipass 建立的 VM 設定一組固定 IP 地址](/post/2022/01/13/Creating-a-Fixed-IP-address-for-Multipass-VM) 文章，先建立一個擁有**固定 IP 地址**的 `microk8s-vm` 虛擬機。

1.  檢查 Multipass 可用的網路清單
    
    ```bash
    multipass networks --format yaml
    ```
    
2.  設定預設的 Bridge 網路名稱
    
    假設我們的 **External** 虛擬交換器名稱為 `LANBridge`：
    
    ```bash
    multipass set local.bridged-network=LANBridge
    ```
    
3.  透過 Multipass **建立虛擬機器** (`microk8s-vm`) 並**指定網路**與設定一張**手動的網路介面**
    
    我們特別給 `microk8s-vm` 這台虛擬機擁有 `2` vCore, `50GB` Disk, `2GB` Memory 的規格。
    
    從 Disk 方面來說，[Multipass](https://multipass.run) 預設建立的 VM 磁碟大小只有 `5GB` 而已，對 Kubernetes 來說是不夠的，建議調整到 `50GB` 左右！
    
    而在 CPU 方面，大多數內建的系統 Pods 都會設定 [Requests and limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) 為 `100m` (即 `0.1` 顆 CPU 核心)，但是系統 Pods 隨著你安裝的 [MicroK8s Add ons](https://microk8s.io/docs/addons) 越來越多，執行的 Pods 也會很容易超過 10 個，因此整個叢集若 CPU 數只有 1 核心的話，很多應用程式將無法成功啟動，因此預設值也是遠遠不夠用的，因此我們先給 2 核心來用。
    
    ```bash
    multipass launch --name 'microk8s-vm' --bridged --disk 50G --cpus 2 --mem 2G
    ```
    
    > 注意: 之後所建立的 Worker Nodes 都需要跟 Control Plane 有著相同的 Network 設定，這樣兩邊的網路才能暢通。
    
4.  查詢網路介面資訊 (包含 IP 資訊)
    
    ```bash
    multipass exec 'microk8s-vm' -- ip -br address show scope global
    ```
    
    此時你應該會看到三張網路介面：
    
    ```
    eth0             UP             172.29.217.133/20
    eth1             UP             192.168.1.12/24
    ```
    
    這裡的 `eth0` 為 [Multipass](https://multipass.run) 內部使用的網路介面，這個 IP 地址每次 VM 重開機都會變更。
    
    而這個 `eth1` 的 IP 為 Bridge 網路透過**區域網路**的 DHCP Server 自動取得的 IP 地址，我們將手動調整為固定 IP 地址。
    
5.  手動調整 VM 的固定 IP 地址
    
    編輯 `/etc/netplan/50-cloud-init.yaml` 網路設定：
    
    ```bash
    multipass exec 'microk8s-vm' -- sudo vi /etc/netplan/50-cloud-init.yaml
    ```
    
    調整 `extra0` 網路介面設定後的檔案內容長這樣：
    
    ```yaml
    network:
        ethernets:
            default:
                dhcp4: true
                match:
                    macaddress: 52:54:00:d1:11:e1
            extra0:
                addresses: [192.168.1.101/24]
                gateway4: 192.168.1.1
                nameservers:
                   addresses: [168.95.1.1, 8.8.8.8]
                match:
                    macaddress: 52:54:00:2d:44:64
        version: 2
    ```
    
6.  透過「重新開機」套用網路更新
    
    ```bash
    multipass restart 'microk8s-vm'
    ```
    
7.  查詢網路介面資訊 (包含 IP 資訊)
    
    ```bash
    multipass exec 'microk8s-vm' -- ip -br address show scope global
    ```
    
    此時你應該會看到三張網路介面：
    
    ```
    eth0             UP             172.29.217.133/20
    eth1             UP             192.168.1.101/24
    ```
    
    其中 `eth1` 的 `192.168.1.101` 就會是一個固定的 IP 地址！👍
    
8.  我們拿 `microk8s-vm` 來架設 MicroK8s 的 **Control Plane** (`master`)
    
    ```bash
    multipass exec 'microk8s-vm' -- sudo snap install microk8s --classic
    multipass exec 'microk8s-vm' -- sudo usermod -a -G microk8s ubuntu
    
    # 在 microk8s-vm 虛擬機中啟動 MicroK8s 服務
    multipass exec 'microk8s-vm' -- microk8s start
    ```
    
9.  透過本機使用 `microk8s.exe` 工具程式管理 MicroK8s 叢集
    
    ```powershell
    # 取得 MicroK8s 專用的 config 內容
    mkdir $env:LOCALAPPDATA\MicroK8s -Force
    microk8s config > $env:LOCALAPPDATA\MicroK8s\config
    
    # 取得 KUBECONFIG 憑證連線資訊
    microk8s config > ~/.kube/config
    
    # 測試可否從本機管理 MicroK8s
    microk8s enable dns storage
    microk8s kubectl get sc
    ```
    
    由於我們先前設定了固定 IP 地址，這裡的 `microk8s config` 預設就會採用額外的 IP 地址當成我們主要的連線目標，其中 TLS 憑證也會自動加入這個 IP 作為憑證別名 (`alt_names`)，你可以透過以下檔案查看憑證申請時的 OpenSSL 設定檔：
    
    ```bash
    multipass exec 'microk8s-vm' -- sudo cat /var/snap/microk8s/current/certs/csr.conf
    ```
    
10.  建立 MicroK8s 的工作節點 (Worker Nodes)
     
     ```bash
     # node1
     multipass launch --name 'node1' --bridged --disk 10G
     multipass exec 'node1' -- sudo snap install microk8s --classic
     multipass exec 'node1' -- sudo usermod -a -G microk8s ubuntu
     
     # node2
     multipass launch --name 'node2' --bridged --disk 10G
     multipass exec 'node2' -- sudo snap install microk8s --classic
     multipass exec 'node2' -- sudo usermod -a -G microk8s ubuntu
     ```
     
11.  將 `node1` 與 `node2` 加入 MicroK8s 叢集
     
     ```powershell
     # 產生加入叢集的命令
     multipass exec 'microk8s-vm' -- microk8s add-node
     
     # 記得要選取使用固定 IP 的那行命令，確保工作節點未來可以持續連到 control plane 節點
     multipass exec 'node1' -- microk8s join 192.168.1.101:25000/d06855a366499b47c8ce106015664bff/aea0aaad938e
     
     # 產生加入叢集的命令
     multipass exec 'microk8s-vm' -- microk8s add-node
     
     # 記得要選取使用固定 IP 的那行命令，確保工作節點未來可以持續連到 control plane 節點
     multipass exec 'node2' -- microk8s join 192.168.1.101:25000/dae59f9219ab4aec33d90fcfc09f88a2/aea0aaad938e
     ```
     
     > 請記得選用 `microk8s-vm` 的固定 IP 地址喔！
     
12.  查看 MicroK8s 叢集狀態
     
     ```bash
     multipass exec 'microk8s-vm' -- microk8s status
     multipass exec 'microk8s-vm' -- microk8s kubectl cluster-info
     multipass exec 'microk8s-vm' -- microk8s kubectl get no -o wide
     multipass exec 'microk8s-vm' -- microk8s kubectl version --short
     ```
     
13.  從本機取得 MicroK8s 叢集資訊
     
     ```powershell
     kubectl cluster-info
     kubectl get no -o wide
     kubectl version --short
     ```
     
14.  設定 [MetalLB Loadbalancer](https://metallb.universe.tf/) 功能 ([Add on: MetalLB](https://microk8s.io/docs/addon-metallb))
     
     ```powershell
     microk8s enable metallb:192.168.1.101-192.168.1.101
     ```
     
     部署測試應用程式
     
     ```powershell
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-deployment.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-service.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-deployment.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-service.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
     ```
     
     查看服務狀態
     
     ```powershell
     kubectl get service frontend
     ```
     
     將應用程式對外服務改成 LoadBalancer
     
     ```powershell
     kubectl patch svc/frontend -p '{\"spec\": {\"type\": \"LoadBalancer\"}}'
     ```
     
     查看服務狀態 (此時應該可以取得一個外部的 IP 地址)
     
     ```powershell
     kubectl get service frontend
     ```
     
     > 嘗試連線 http://192.168.1.101/ 看有沒有頁面出現！
     
     刪除範例應用程式
     
     ```powershell
     kubectl delete -f https://k8s.io/examples/application/guestbook/redis-leader-deployment.yaml
     kubectl delete -f https://k8s.io/examples/application/guestbook/redis-leader-service.yaml
     kubectl delete -f https://k8s.io/examples/application/guestbook/redis-follower-deployment.yaml
     kubectl delete -f https://k8s.io/examples/application/guestbook/redis-follower-service.yaml
     kubectl delete -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
     kubectl delete -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
     ```
     
15.  設定 Ingress 服務
     
     ```bash
     microk8s enable ingress
     ```
     
     建立一個 `ingress-service.yml` 檔案：
     
     ```yml
     apiVersion: v1
     kind: Service
     metadata:
       name: ingress
       namespace: ingress
     spec:
       selector:
         name: nginx-ingress-microk8s
       type: LoadBalancer
       # loadBalancerIP is optional. MetalLB will automatically allocate an IP
       # from its pool if not specified. You can also specify one manually.
       # loadBalancerIP: x.y.z.a
       ports:
         - name: http
           protocol: TCP
           port: 80
           targetPort: 80
         - name: https
           protocol: TCP
           port: 443
           targetPort: 443
     ```
     
     套用 `ingress-service.yml` 檔案：
     
     ```powershell
     kubectl apply -f ./ingress-service.yml
     ```
     
     > 現在 Ingress 已經取得了一個 Load Balancer IP 地址，當你連到 http://192.168.1.101/ 將會看到一個來自 nginx 的 `404 Not Found` 頁面！
     
     部署測試應用程式
     
     ```powershell
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-deployment.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-leader-service.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-deployment.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/redis-follower-service.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-deployment.yaml
     kubectl apply -f https://k8s.io/examples/application/guestbook/frontend-service.yaml
     ```
     
     建立一個 `ingress.yml` 檔案：
     
     ```yml
     apiVersion: networking.k8s.io/v1
     kind: Ingress
     metadata:
       name: minimal-ingress
       annotations:
         nginx.ingress.kubernetes.io/rewrite-target: /
     spec:
       rules:
       - http:
           paths:
           - path: /
             pathType: Prefix
             backend:
               service:
                 name: frontend
                 port:
                   number: 80
     ```
     
     套用 `ingress.yml` 檔案：
     
     ```powershell
     kubectl apply -f ./ingress.yml
     ```
     
     > 現在 Ingress 已經設定完成，當你連到 http://192.168.1.101/ 將會看到 **Guestbook** 應用程式的頁面！
     

### 刪除所有已建立資源

```bash
multipass delete node1 node2 microk8s-vm
multipass purge
```

### 相關連結

-   [MicroK8s - Get started](https://microk8s.io/docs/getting-started)
    -   [Additional network interfaces | Multipass documentation](https://multipass.run/docs/additional-networks)
    -   [MicroK8s - Create a MicroK8s cluster](https://microk8s.io/docs/clustering)
-   [Resource Management for Pods and Containers | Kubernetes](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)
-   [Ingress | Kubernetes](https://kubernetes.io/docs/concepts/services-networking/ingress/)
-   [microk8s, DEVOPS : Unable to connect to the server: x509: certificate is valid for <internal IPs>, not <external IP> - Stack Overflow](https://stackoverflow.com/a/65571967/910074)
