[元件] 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 %}