[元件] Pod === ###### tags: `K8s / 元件` ###### tags: `Kubernetes`, `k8s`, `Pod` <br> [TOC] <br> ## Examples ### empty pod ```yaml= apiVersion: v1 kind: Pod metadata: name: empty-pod spec: containers: - name: empty-container image: alpine command: ['sleep', 'infinity'] ``` ### gpu pod ```yaml= # test-nvidia-smi-gpu-pod.yaml apiVersion: v1 kind: Pod metadata: name: test-nvidia-smi-gpu-pod spec: restartPolicy: Never # ✅ 不要自動重啟 containers: - name: cuda-container image: nvidia/cuda:12.2.0-base-ubuntu20.04 resources: limits: nvidia.com/gpu: 4 command: ["sh", "-c", "nvidia-smi && sleep infinity"] ``` - ### image source https://hub.docker.com/r/nvidia/cuda/tags?name=20.04&page=6 - [nvidia/cuda:12.2.0-base-ubuntu20.04](https://hub.docker.com/layers/nvidia/cuda/12.2.0-base-ubuntu20.04/images/sha256-0a42e6fffe9f41cbb0d15dcf3e178b86e99cba9a3c209a035a6fa155e9716873) <br> --- ## Probe <br> --- ## Probe ### 案例學習:Startup vs Readiness vs Liveness - ### Question ``` $ kubectl -n slurm describe pod/mariadb-0 ... Liveness: exec [bash -c mariadb -u root -p"${MARIADB_ROOT_PASSWORD}" -e "SELECT 1;"] delay=20s timeout=5s period=10s #success=1 #failure=3 Readiness: exec [bash -c mariadb -u root -p"${MARIADB_ROOT_PASSWORD}" -e "SELECT 1;"] delay=20s timeout=5s period=10s #success=1 #failure=3 Startup: exec [bash -c mariadb -u root -p"${MARIADB_ROOT_PASSWORD}" -e "SELECT 1;"] delay=20s timeout=5s period=10s #success=1 #failure=3 ``` 三者的用途為何? - ### gpt-5 (2025/10/08) ### 三種 Probe 的用途與差異 * **Startup Probe(啟動探針)** 用來判斷「程式是否已完成啟動」。在它**第一次成功**之前,Kubelet **不會執行 liveness/readiness**;若連續失敗達門檻,就**重啟容器**。適合啟動很慢或要資料庫恢復/重播日誌的情境。 → 你現在的設定代表:容器啟動 **20 秒後**開始檢查,每 **10 秒**檢一次,**連續 3 次失敗**就判定啟動失敗並重啟;也就是**最長約 50 秒啟動寬限**(20 + 3×10)。 * **Readiness Probe(就緒探針)** 用來判斷「**能不能收流量**」。失敗時**不會重啟**容器,但 Pod 會**從 Service 的 Endpoints 移除**(不接請求);成功時再加回去。適合「依賴外部服務尚未可用」或「資料庫暫時拒絕連線」時避免流量打到它。 * **Liveness Probe(存活探針)** 用來判斷「**程式是否卡死/壞掉**」。**連續失敗達門檻就重啟容器**,以自我修復。適合偵測 process 雖在、但已無法對外服務的狀況。 ### 你的這條指令在三個 Probe 中各自代表什麼 ``` exec [bash -c mariadb -u root -p"${MARIADB_ROOT_PASSWORD}" -e "SELECT 1;"] delay=20s timeout=5s period=10s #failure=3 ``` * **語意**:在容器內執行一個「能用 root 密碼連上 MariaDB 並跑 `SELECT 1`」的檢查。 * **對三種 Probe 的效果**: * **Startup**:用這個條件判斷「啟動完成」。若 InnoDB 還在 warm up 或 redo/DDL 還沒跑完,容易連續失敗→**被當成啟動失敗而重啟**。 * **Readiness**:用這個條件判斷「可否接流量」。失敗時 **不重啟**,只是從 Service 下線。 * **Liveness**:用這個條件判斷「是否壞掉」。連續失敗達門檻 → **重啟**。 ### 實務建議(MariaDB 常見最佳化) 1. **給 Startup 更長的寬限** 50 秒對資料庫常常太短(尤其首次啟動、資料量大、慢磁碟、做 crash recovery 時)。 建議把 **failureThreshold** 拉高(或 **periodSeconds** 拉長),給 3–10 分鐘都很常見。 2. **Probe 指令拆工** * **Startup/Liveness**:用「**能不能回應 ping**」即可,較輕量與穩定: `mariadb-admin --user=root --password="$MARIADB_ROOT_PASSWORD" ping --silent` * **Readiness**:維持 `SELECT 1` 或 `SELECT 1 FROM DUAL;`,因為它代表「能接受查詢」。 3. **避免把密碼出現在行程參數** `-pPASSWORD` 可能出現在 `ps`;雖然在容器內風險較小,仍建議: * 用 `-p`(互動輸入)+ `MYSQL_PWD`/`MARIADB_PWD` 環境變數,或 * 用掛載的 `/etc/mysql/conf.d/probe.cnf`(0600 權限)存密碼。 4. **調整 timeoutSeconds** `timeout=5s` 對偶發 IO 卡頓可能太緊,**10–15s** 較保險。 5. **注意 root 的認證外掛** 若啟用了 `unix_socket` 外掛,**密碼驗證會失敗**;請改用對應使用者或調整 root 認證方式。 ### 參考設定範例(更耐用) ```yaml startupProbe: exec: command: ["bash", "-lc", 'mariadb-admin --user=root --password="$MARIADB_ROOT_PASSWORD" ping --silent'] initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 10 failureThreshold: 60 # ≈ 10 分鐘寬限 readinessProbe: exec: command: ["bash", "-lc", 'mariadb -u root -p"$MARIADB_ROOT_PASSWORD" -e "SELECT 1;"'] initialDelaySeconds: 5 periodSeconds: 10 timeoutSeconds: 10 failureThreshold: 3 livenessProbe: exec: command: ["bash", "-lc", 'mariadb-admin --user=root --password="$MARIADB_ROOT_PASSWORD" ping --silent'] initialDelaySeconds: 5 periodSeconds: 15 timeoutSeconds: 10 failureThreshold: 4 ``` ### 一句話總結 * **Startup**:啟動期的「一次成功門檻」,成功前不跑其他 probe;失敗到門檻就重啟。 * **Readiness**:決定要不要**接流量**,失敗不重啟。 * **Liveness**:決定要不要**重啟容器**,失敗到門檻就重啟。 <br> --- <br> ## 討論 ### 如何讓 Pod 可以跑在 control-plane node? - ### gpt-4o (2025/05/20) - ### 方法 1:移除這個 taint(推薦單機測試) ```bash kubectl taint nodes <NODE_NAME> node-role.kubernetes.io/control-plane- ``` - ### 何時會出現這個 taint? - 使用 kubeadm init 建立叢集後的 control-plane 節點 - 這是 Kubernetes 官方預設行為,用來保護 control-plane node 不跑一般工作負載 - ### control-plane node 移除前的設定: ```yaml spec: podCIDR: 10.244.0.0/24 podCIDRs: - 10.244.0.0/24 taints: - effect: NoSchedule key: node-role.kubernetes.io/control-plane ``` - ### control-plane node 移除後的設定: ```ㄇㄩㄠ spec: podCIDR: 10.244.0.0/24 podCIDRs: - 10.244.0.0/24 ``` - ### 方法 2:在 Pod 加上 toleration(適合進階控制) ```yaml tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" ``` - ### 這句話的意思是: 「這個 Pod 願意容忍所有帶有 key=`node-role.kubernetes.io/control-plane`、effect=`NoSchedule` 的 taint,請不要擋我。」 - ### empty pod 的範例設定 ```yaml= apiVersion: v1 kind: Pod metadata: name: empty-pod spec: containers: - name: empty-container image: alpine command: ['sleep', 'infinity'] tolerations: - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" ``` <br> --- ### K8s pod 如何 mount 一個檔案 (提供簡單範例) - ### gpt-5 (2025/11/17) 可以的,而且很常見 👍 下面給你兩個「只掛一個檔案」的簡單範例: --- ### 做法 1:用 ConfigMap 掛一個設定檔到 Pod #### 1. 先準備一個設定檔 `app.conf` ```conf HELLO=world ``` #### 2. 建立 ConfigMap ```bash kubectl create configmap app-config --from-file=app.conf ``` - `app.conf` 檔案,可以是相對路徑的檔案 - 這會產生一個 ConfigMap,裡面有一個 key:`app.conf`。 ``` $ kubectl get configmap app-config -o yaml ``` ```yaml= apiVersion: v1 data: app.conf: | HELLO=world kind: ConfigMap metadata: creationTimestamp: "2025-11-17T03:46:26Z" name: app-config namespace: default resourceVersion: "24137594" uid: 21ef80a0-bd02-4027-95e4-a3814ac92eac ``` #### 3. Pod YAML:用 `subPath` 把單一檔案掛進去 ```yaml= apiVersion: v1 kind: Pod metadata: name: file-mount-demo spec: containers: - name: app image: busybox command: ["sh", "-c", "echo 'content:'; cat /etc/app/app.conf; sleep 3600"] volumeMounts: - name: config-file mountPath: /etc/app/app.conf # 容器內看到的檔案路徑 subPath: app.conf # 指定 ConfigMap 裡的單一檔案 key volumes: - name: config-file configMap: name: app-config # 指向上面建立的 ConfigMap ``` 套用: ```bash kubectl apply -f file-mount-demo.yaml kubectl exec -it file-mount-demo -- cat /etc/app/app.conf ``` 你會看到 `HELLO=world`。 > 重點: > > * ConfigMap / Secret 掛進來時預設是「一個目錄」。 > * 想要只掛其中一個檔案,就用 `subPath` 讓它變成單一檔案掛載。 --- ### 做法 2:用 hostPath 直接掛宿主機上的單一檔案 假設 **Node 上** 有檔案:`/data/config/app.conf`,想掛到 Pod 的 `/etc/app/app.conf`: ```yaml= apiVersion: v1 kind: Pod metadata: name: hostpath-file-demo spec: containers: - name: app image: busybox command: ["sh", "-c", "cat /etc/app/app.conf; sleep 3600"] volumeMounts: - name: host-file mountPath: /etc/app/app.conf # 這裡直接是一個檔案路徑 readOnly: true volumes: - name: host-file hostPath: path: /data/config/app.conf # Node 上的單一檔案 type: File ``` > ⚠️ `hostPath` 通常只建議在單機測試或特別情境使用(跟 Node 綁死)。 --- - ### 掛載單一 script 範例 - ### 將 script 檔案導入到 K8s - ### 準備檔案:`my_script.sh` ``` date "+%T" ``` - ### 導入到 K8s ``` kubectl create configmap my-script --from-file=my_script.sh ``` - ### 查看導入結果 - `$ kubectl get configmap/my-script -o yaml` ```yaml= apiVersion: v1 data: my_script.sh: | date "+%T" kind: ConfigMap metadata: creationTimestamp: "2025-11-17T01:37:08Z" name: my-script namespace: default resourceVersion: "24114851" uid: 237876ef-75e6-4990-95e5-71bd5d51c5d7 ``` - ### 掛載單一 script 測試 - ### 準備檔案:`file-mount-demo.yaml` ```yaml= apiVersion: v1 kind: Pod metadata: name: file-mount-demo spec: containers: - name: my-container image: ubuntu:24.04 command: ["sleep", "infinity"] volumeMounts: - name: my-script-file mountPath: /mnt/script/my_script.sh subPath: my_script.sh # key in configMap volumes: - name: my-script-file configMap: name: my-script ``` - ### 執行結果 ``` $ kubectl apply -f file-mount-demo.yaml $ kubectl exec -it pod/file-mount-demo -- bash root@file-mount-demo:/# cd /mnt/script/ root@file-mount-demo:/mnt/script# ll total 12 drwxr-xr-x 2 root root 4096 Nov 17 01:49 ./ drwxr-xr-x 1 root root 4096 Nov 17 01:49 ../ -rw-r--r-- 1 root root 11 Nov 17 01:49 my_script.sh root@file-mount-demo:/mnt/script# bash my_script.sh 01:49:45 ``` <br> {%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}