# Kubernetes cluster deployment ## 📌 0. 環境架構與前置條件 - 已安裝Keepalived&HaProxy,並設置好VIP ### 🔹 節點規劃 (Topology) | Hostname | Role | IP Address | OS | 備註 | |:------------| :--- |:---------------|:-------------| :--- | | **VIP** | **Load Balancer** | **10.10.0.83** | HAProxy | **API Server 統一入口 (Port 6444)** | | `doris-f01` | **Master** | `10.10.0.85` | Ubuntu 24.04 | 第一台初始節點 | | `doris-f02` | **Master** | `10.10.0.87` | Ubuntu 24.04 | 後續加入的 Control Plane | | `doris-f03` | **Master** | `10.10.0.89` | Ubuntu 24.04 | 後續加入的 Control Plane | | `doris-b01` | **Worker** | `10.10.0.93` | Ubuntu 24.04 | 工作節點 | | `doris-b02` | **Worker** | `10.10.0.94` | Ubuntu 24.04 | 工作節點 | | `doris-b03` | **Worker** | `10.10.0.95` | Ubuntu 24.04 | 工作節點 | | `doris-b04` | **Worker** | `10.10.0.96` | Ubuntu 24.04 | 工作節點 | ## 📌 1. 基礎設定(所有節點) ### 🔹 設定 Hostname 若已設定就跳過 ```shell hostnamectl set-hostname ``` ### 🔹 關閉 swap 1. Swap 當記憶體不足時,會將記憶體數據寫入速度極慢的硬碟 2. K8s 依賴精確的 RAM (物理記憶體) 數據來排程 Pod 3. Linux 核心會先使用 Swap 而不是觸發 Kubelet 的驅逐 ```shell sudo swapoff -a sudo sed -i '/swap/d' /etc/fstab ``` 1. overlay:這是容器運行時用來創建疊加文件系統的模組(Overlay Filesystem) 2. br_netfilter:橋接網路過濾的基礎。它允許 Linux 核心的 iptables(防火牆規則)能夠正確處理橋接網路流量。 ### 🔹 載入核心模組 ```shell cat < # 從 kubeadm init 輸出獲取 caCertHashes: - "sha256:" # CA 證書 hash controlPlane: # 標記為 control plane 節點 localAPIEndpoint: advertiseAddress: "10.10.0.87" # **F02 的 IP** (F03 用 .89) bindPort: 6443 certificateKey: # 證書加密 key (kubeadm init 輸出) nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock name: doris-f02 # 節點名稱 taints: - effect: NoSchedule key: node-role.kubernetes.io/control-plane ``` ```yaml apiVersion: kubeadm.k8s.io/v1beta3 kind: ClusterConfiguration # K8s 版本 (確保與 kubeadm 版本一致) kubernetesVersion: v1.30.14 # 控制平面 HA 端點 (HAProxy VIP) controlPlaneEndpoint: "10.10.0.83:6444" # 網路配置 networking: podSubnet: "10.244.0.0/16" # Pod 網段 (Calico 使用) serviceSubnet: "10.96.0.0/12" # Service 網段 # API Server 配置 apiServer: # 證書 SAN 列表 (所有可能訪問 API Server 的地址) certSANs: - "10.10.0.83" # VIP (必須!) - "10.10.0.85" # F01 - "10.10.0.87" # F02 - "10.10.0.89" # F03 - "127.0.0.1" # localhost - "kubernetes" - "kubernetes.default" - "kubernetes.default.svc" - "kubernetes.default.svc.cluster.local" extraArgs: # API Server 監聽在所有網路介面 bind-address: "0.0.0.0" # 此節點對外宣告的 IP (F01) advertise-address: "10.10.0.85" # API Server 實際監聽端口 (避免與 HAProxy 6443 衝突) secure-port: "6443" # 審計日誌 (可選,用於安全審計) audit-log-path: /var/log/kubernetes/audit.log audit-log-maxage: "30" audit-log-maxbackup: "10" audit-log-maxsize: "100" # 使用外部 etcd cluster (B01-B03) #etcd: # external: # endpoints: # - https://10.10.0.93:2379 # B01 # - https://10.10.0.94:2379 # B02 # - https://10.10.0.95:2379 # B03 # # etcd TLS 證書路徑 # caFile: /etc/kubernetes/pki/etcd/ca.crt # certFile: /etc/kubernetes/pki/etcd/client.crt # keyFile: /etc/kubernetes/pki/etcd/client.key # Controller Manager 配置 controllerManager: extraArgs: bind-address: "0.0.0.0" # Node 心跳超時時間 node-monitor-grace-period: "40s" # Pod 驅逐超時時間 pod-eviction-timeout: "5m0s" # Scheduler 配置 scheduler: extraArgs: bind-address: "0.0.0.0" # DNS 配置 dns: imageRepository: registry.k8s.io/coredns imageTag: v1.10.1 # 鏡像倉庫 (如果使用國內鏡像) # imageRepository: registry.aliyuncs.com/google_containers --- # 初始化配置 (僅第一個 master 需要) apiVersion: kubeadm.k8s.io/v1beta3 kind: InitConfiguration # 本地 API Server 端點配置 localAPIEndpoint: advertiseAddress: "10.10.0.85" # F01 的 IP bindPort: 6443 # 監聽端口 # 節點註冊配置 nodeRegistration: criSocket: unix:///var/run/containerd/containerd.sock name: doris-f01 # 節點名稱 # Master 節點污點 (防止普通 Pod 調度) taints: - effect: NoSchedule key: node-role.kubernetes.io/control-plane # 節點標籤 kubeletExtraArgs: node-labels: "node-role.kubernetes.io/control-plane=" # Bootstrap Token 配置 (用於節點加入) bootstrapTokens: - groups: - system:bootstrappers:kubeadm:default-node-token ttl: 24h0m0s usages: - signing - authentication --- # Kubelet 配置 apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration # 容器運行時 cgroupDriver: systemd # 資源預留 systemReserved: cpu: "500m" memory: "1Gi" ephemeral-storage: "1Gi" kubeReserved: cpu: "500m" memory: "1Gi" ephemeral-storage: "1Gi" # 驅逐閾值 evictionHard: memory.available: "200Mi" nodefs.available: "10%" imagefs.available: "15%" # 日誌配置 logging: format: json # 序列化 image pulls (適合網路較慢的環境) serializeImagePulls: false maxParallelImagePulls: 3 ``` ### 🔹kubeadm執行初始化 ```shell # 建議先預拉映像檔,避免初始化超時 sudo kubeadm config images pull --config kubeadm-config.yaml # 正式初始化 # --upload-certs:將憑證加密上傳,讓後續加入的 Master 節點能自動下載使用 sudo kubeadm init --config kubeadm-config.yaml --upload-certs ``` 命令成功後會得到: add master node 指令: ```shell sudo kubeadm join 10.10.0.83:6444 --token \ --discovery-token-ca-cert-hash sha256: \ --control-plane --certificate-key \ --apiserver-advertise-address= \ --apiserver-bind-port=6443 ``` add worker node: ```shell sudo kubeadm join 10.10.0.83:6444 --token \ --discovery-token-ca-cert-hash sha256: ``` ### 🔹 設定 kubeconfig: **目的:** 將集群的管理員憑證 (`admin.conf`) 複製到使用者的預設目錄,讓 `kubectl` 能夠讀取並取得控制權限 1. **建立目錄**:`kubectl` 預設會去 `~/.kube/` 找設定檔 2. **複製憑證**:將 `kubeadm` 產生的最高權限設定檔 (`admin.conf`) 複製過來 3. **變更權限**:將檔案擁有者改為目前使用者,**避免每次執行 kubectl 都要打 sudo** 4. **etc/kubernetes/admin.conf**:集群最高權限身分證 (Master Key), 由`kubaadmi init`自動生成,裡面包含: 1. Cluster: API Server 的網址 (例如 https://10.10.0.83:6444) 和 CA 根憑證。 2. User: 一個名為 kubernetes-admin 的使用者,以及它的 客戶端憑證 (Client Certificate) 和 私鑰 (Key)。這個憑證擁有 system:masters 群組權限(K8s 裡的上帝視角)。 3. Context: 把上面的 Cluster 和 User 綁定在一起。 ```shell mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config sudo chown $(id -u):$(id -g) ~/.kube/config ``` --- ## 📌 5. 安裝 Pod 網路(Flannel) Kubernetes 本身**不提供** Pod 之間的網路通訊功能,它只定義了介面 (CNI)。我們必須安裝網路插件來實現以下功能: 1. **指派 IP**:根據我們在初始化時設定的 `podSubnet` (10.244.0.0/16),為每一個 Pod 分配唯一的 IP 地址。 2. **建立覆蓋網路 (Overlay Network)**:在現有的實體網路之上,建立一層虛擬網路,讓不同節點上的 Pod 覺得彼此在同一個區域網路內。 3. **激活節點**:CoreDNS 與節點狀態依賴於網路插件。未安裝前,執行 `kubectl get nodes` 會看到狀態為 **NotReady**。 ```shell kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml ``` 檢查: 執行 `kubectl get nodes`,doris-f01 的狀態應該會從 NotReady 變成 Ready。 **補充知識** - 同一台節點內: 其實不用 Flannel,Docker/Containerd 本身就能讓同一台節點上的容器互通(透過本機的 Bridge)。 - 跨節點: 這是困難點。當 Node A 的 Pod (IP 10.244.1.2) 想找 Node B 的 Pod (IP 10.244.2.3) 時,連線節點的路由器(無論是實體路由或 VM 的 vSwitch) 看不懂這些 10.244 的 Pod IP,它只認得 10.10 的節點 IP。 - Flannel 的工作: 它負責把這些封包「包裝」起來(封裝),偽裝成節點對節點的流量,透過節點網路傳送到對面,再由對面的 Flannel「拆開」,交給目標 Pod。 --- ## 📌 6. 加入 Master 節點 在要加入 master 的機器執行 kubeadm 給的 kubeadm join: ```shell sudo kubeadm join 10.10.0.83:6444 --token \ --discovery-token-ca-cert-hash sha256: \ --control-plane --certificate-key \ --apiserver-advertise-address= \ --apiserver-bind-port=6443 ``` 設定 Kubeconfig (讓 kubectl 可用) 加入成功後,必須配置憑證才能使用 kubectl 指令 ```shell mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf ~/.kube/config # kubeadm join 會自動在 /etc/kubernetes/ 產生這份檔案 sudo chown $(id -u):$(id -g) ~/.kube/config ``` ## 📌 7. 加入 Worker 節點 在每台 worker 機器執行 kubeadm 給的 kubeadm join: ```shell sudo kubeadm join 10.10.0.83:6444 --token --discovery-token-ca-cert-hash sha256: ``` 到master node上檢查節點: ```shell kubectl get nodes -o wide ```