[指令] kubectl === ###### tags: `K8s` ###### tags: `Kubernetes`, `k8s`, `kubectl`, `kubectl get`, `kubectl explain`, `kubectl logs`, `CRD`, `CR`, `finalizers` <br> [TOC] <br> <br> ## kubectl get ### `kubectl get pod` > 查詢 pod (帶有 prefix=`pod/`) ``` $ kubectl get pod --show-kind ``` - **輸出清單時,帶有 prefix `pod/`** - **沒有 `--show-kind` 參數** ``` $ kubectl get pod NAME READY STATUS RESTARTS AGE hello-world-57959d477f-cbgpz 1/1 Running 0 3d17h ``` - **有 `--show-kind` 參數** ``` $ kubectl get pod --show-kind NAME READY STATUS RESTARTS AGE pod/hello-world-57959d477f-cbgpz 1/1 Running 0 3d17h ``` <br> ### `kubectl get crd` > 查詢每個客製化資源(CRD)的版本 `$ kubectl get crd -o=custom-columns='NAME:.metadata.name,STORED_VERSION:.status.storedVersions,SERVED_VERSIONS:.spec.versions[*].name'` <br> ### `kubectl get events` ``` kubectl get events --sort-by='.lastTimestamp' ``` <br> ### `kubectl get apiservice` ``` kubectl get apiservice -l app.kubernetes.io/instance=keda ``` <br> ## kubectl explain - ### 限制 - CRD 定義中的 `served: false` (預設 `false`),API Server 並不會對外暴露這個版本,自然找不到任何資源 ``` $ kubectl explain xxx error: the server doesn't have a resource type "xxx" ``` - `必須設定 `served: true` - 查詢 CRD sepc: `kubectl explain slurmcluster.spec` - 查詢 CRD sepc (特定版本): `kubectl explain slurmcluster.spec --api-version=slurm.nebius.ai/v1` 範例如下: ``` GROUP: slurm.nebius.ai KIND: SlurmCluster VERSION: v1 FIELD: spec <Object> DESCRIPTION: SlurmClusterSpec defines the desired state of SlurmCluster FIELDS: clusterType <string> enum: gpu, cpu ClusterType define type of slurm worker nodes crVersion <string> CRVersion defines the version of the Operator the Custom Resource belongs to customSlurmConfig <string> CustomSlurmConfig represents the raw Slurm configuration from slurm.conf. All options are provided as a raw string. Soperator does not guarantee the validity of the raw configuration. Raw config is merged with existing SlurmConfig values. healthCheckConfig <Object> HealthCheckConfig defines Slurm health check configuration. ... ``` <br> ## kubectl logs | 指令 | 預設行為 | |------|----------| | `kubectl logs <pod>` | 顯示 **整個容器的 log**(除非加了 `--tail` 或其他限制)。 | | `kubectl logs -l <label>` | 只會顯示 **每個符合條件 Pod 的最後 10 行 log**(除非指定 `--tail`)。 | | `kubectl logs -f ...` | 會跟著 log 持續更新(stream 模式)。 | --- ### `--tail=-1`(或指定筆數)情境 ``` # 顯示所有 log kubectl logs -n soperator -l control-plane=controller-manager -c manager --tail=-1 # 顯示最近 100 行 kubectl logs -n soperator -l control-plane=controller-manager -c manager --tail=100 ``` - `--tail=-1` 表示「不要限制 log 行數」,等同於 `cat` 整份 log。 - 為什麼會這樣設計? - 使用 `-l` 可能會選到很多 Pod(例如某些 deployment 有 replicas), - 為了避免意外撈出大量 log,Kubernetes 預設只會顯示每個 Pod 的最後 10 行。 - 小技巧:加上 `--prefix` 幫你辨識是誰的 log ``` kubectl logs -n soperator -l control-plane=controller-manager -c manager --tail=100 --prefix ``` - 每行前面都有 `[PodName]`,便於辨識多個 Pod 的 log 混在一起時,誰是誰。 <br> ## kubectl api-resources ### 列出可查詢清單的資源 ``` kubectl api-resources --verbs=list --namespaced -o name ``` - 執行結果: ``` configmaps endpoints events limitranges persistentvolumeclaims pods podtemplates replicationcontrollers resourcequotas secrets serviceaccounts services ... ... ``` <br> ### 掃描某個 namespace 下的資源 - ### 單行 (使用 xargs) ```bash= kubectl api-resources --verbs=list --namespaced -o name \ | xargs -n 1 -I {} kubectl get {} -n tj-slurm4 --ignore-not-found ``` - ### `-n <count>` - **功能**:每次執行命令時,從輸入中取 `<count>` 個參數,放到一次命令呼叫裡。 - **範例**: - `-n 1`:每次只取一個(最常見的用法)。 - 如果不用 `-n`,預設會盡可能多地把所有參數傳給一次執行(一次呼叫)——但有時候一次傳太多參數會超過系統限制或不符合命令預期。 - ### `-I <replace-str>` - **功能**:指定一個「替換字串」`<replace-str>`,每讀到一筆輸入,就把這個字串替換成輸入的內容,再呼叫一次目標命令。 - **範例**: ```bash echo pod deployment | xargs -n 1 -I {} kubectl get {} -n my-namespace ``` - 先讀到 `pod`,執行 `kubectl get pod -n my-namespace` - 再讀到 `deployment`,執行 `kubectl get deployment -n my-namespace` - ### 單行 (使用 for-loop) ```for res in `kubectl api-resources --verbs=list --namespaced -o name`; do echo "===== $res ====="; kubectl get $res -n tj-slurm4 --ignore-not-found; done``` - ### 多行 ```bash for res in $(kubectl api-resources --verbs=list --namespaced -o name); do echo "===== $res =====" kubectl get "$res" -n tj-slurm4 done ``` <br> ### 為何 api-resources 不一定是 CRD ? - ### gpt-4.1-mini (2025/06/17) ### 1. Kubernetes API 資源類型的分類 Kubernetes 裡的 API 資源可以分成三類: | 類別 | 說明 | 是否 CRD? | | -------------- | -------------------------- | ------- | | **內建資源** | 系統本來就有的資源,比如 Pod、Service 等 | 不是 CRD | | **自訂資源 (CRD)** | 使用者自己定義並新增的資源 | 是 CRD | | **擴充資源** | 透過額外服務新增的資源(非 CRD) | 不是 CRD | --- ### 2. `kubectl api-resources` 是什麼? * 這個指令會列出目前 Kubernetes 裡所有可用的 API 資源,包括內建、自訂和擴充資源。 --- ### 3. 為什麼有些資源不是 CRD? #### 3.1 內建資源 * 系統一開始就有的資源,不用特別新增 CRD,就能直接用。 #### 3.2 擴充資源 * 透過額外服務加進來的資源,不是用 CRD 方式新增,但一樣能用。 --- ### 4. 總結 | 問題 | 答案 | | ---------------------------- | --------------------- | | `kubectl api-resources` 是什麼? | 列出所有可用的 Kubernetes 資源 | | 為什麼有些資源不是 CRD? | 因為它們是系統內建或額外加進來的擴充資源 | | CRD 是什麼? | 用來新增自訂資源的機制 | --- 想知道某資源是不是 CRD,可以用: ```bash kubectl get crd | grep 資源名稱 ``` 找不到不代表不存在,可能是內建或擴充資源。 <br> ## kubectl delete ### 強制刪除 Pod(不等、直接砍) ``` kubectl delete pod <your-pod> --grace-period=0 --force --wait=false ``` - ### 如果還是卡住(通常是有 finalizers),移除 finalizers 後再刪 ```bash # 先看有沒有 finalizers kubectl get pod <your-pod> -o jsonpath='{.metadata.finalizers}{"\n"}' # 直接把 finalizers 清空 kubectl patch pod <your-pod> --type=merge -p '{"metadata":{"finalizers":[]}}' # 再刪一次(保險) kubectl delete pod <your-pod> --grace-period=0 --force --wait=false ``` <br> ### 用 kubectl raw 觀察刪除流程 ``` kubectl delete namespace <your-namespace> --v=8 ``` - 如果想看到 API Server 真正執行的呼叫,可以用高 verbose 模式呼叫刪除: - 它會列出每一個底層的 HTTP request(GET、DELETE)到 `/api/v1/namespaces/tj-slurm2/...`、`/apis/external.metrics.k8s.io/v1beta1/...`,並印出錯誤回應。 <br> --- --- <br> ## Blogs - Q & A ### [How to run kubectl commands inside a container?](https://stackoverflow.com/questions/42642170/) ``` curl http://localhost:8080/api/v1/namespaces/default/pods ``` <br> ### [Kubernetes: kubectl run: command not found](https://stackoverflow.com/questions/55360666/) ``` curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" ``` - `curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt` `v1.31.0` - `-s, --silent` Silent mode - `-O, --remote-name` Write output to a file named as the remote file - `-L` 應指 Location - [[MDN Web Docs] HTTP 的重定向](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Guides/Redirections) - ### Taiwan LLM (2025/04/10) -L 參數用於告訴 curl 在遇到 HTTP 重定向時自動跟隨新的 URL。 在這個命令中,使用者透過 -L 參數確保即使下載連結(`https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl`)返回 301 或 302 狀態碼指向另一個 URL,curl 也會跟隨該重定向,繼續下載檔案。這避免了手動處理重定向的麻煩,確保無論原始 URL 如何變更,都能正確下載 Kubernetes 的 kubectl 二進位。 同時,-L 與 -O 參數配合使用,允許下載的檔案命名為 URL 的最後一段路徑,而不是預設的檔案名稱。這樣 維持了檔名的可預測性,方便後續使用。 總結:-L 參數的作用是自動跟隨重定向,確保下載過程的 完整性和可靠性。 - ### gpt-o3-mini-high (2025/04/10) 在 curl 中,`-L` 參數的作用是讓 curl 自動跟隨 HTTP 重定向(redirects)的 URL。例如,當伺服器返回 3xx 狀態碼(如 301、302 等)時,curl 會根據回應中的 Location 標頭自動發出新的請求,直到獲取最終的資源位置。 在這個例子中: ```bash curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" ``` - `-L` 使 curl 自動跟隨可能的重定向。 - `-O` 則指示 curl 使用遠端檔案的檔名來儲存下載的文件。 這樣,即使初始 URL 發生重定向,使用了 `-L` 參數後,curl 仍能正確跟蹤到最終的檔案位置並下載它。 - ### Google Gemini 2.0 Flash Thinking (experimental) `curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl"` 在上面的 `curl` 命令中,`-L` 參數的作用是 **指示 `curl` 跟隨 HTTP 重定向 (HTTP redirects)**。 以下是更詳細的解釋: * 當伺服器收到一個 HTTP 請求,但請求的資源已經移動到另一個 URL 時,伺服器通常會發送一個 HTTP 重定向響應(例如,HTTP 301 Moved Permanently 或 HTTP 302 Found)。 * 預設情況下,`curl` 不會自動跟隨這些重定向。它會將重定向響應作為結果返回。 * 使用 `-L` 參數後,`curl` 會檢查伺服器的響應頭。如果響應頭包含指示重定向的資訊(例如,`Location` 頭),`curl` 會自動向新的 URL 發送另一個請求,並繼續下載資源。 在這個特定的命令中,`-L` 確保了如果 Kubernetes 發布的穩定版本 URL 發生了重定向,`curl` 仍然能夠找到並下載 `kubectl` 二進制檔案。 總之,`-L` 參數使得 `curl` 能夠處理網站或資源移動的情況,確保命令能夠成功地獲取到最終的目標資源。 - ### kubectl 檔案大小: 54M - ### 接續 kubectl 安裝指令: ```bash # Make the kubectl binary executable: chmod +x ./kubectl # Move the binary in to your PATH: sudo mv ./kubectl /usr/local/bin/kubectl # Test to ensure the version you installed is up-to-date: kubectl cluster-info kubectl version ``` - ### 測試 kubectl 指令 ``` $ kubectl version Client Version: v1.31.0 Kustomize Version: v5.4.2 Server Version: v1.29.4 WARNING: version difference between client (1.31) and server (1.29) exceeds the supported minor version skew of +/-1 $ kubectl cluster-info Kubernetes control plane is running at https://10.96.0.1:443 CoreDNS is running at https://10.96.0.1:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy ``` - 留意這個 endpoint `https://10.96.0.1:443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy` <br> --- --- <br> ## 討論 ### `rollout restart` vs `scale --replicas=<amount>` - ### Question ``` # rollout 指令會無法重啟 controller,controller 會起不來 $> kubectl -n slurm rollout restart statefulset.apps/slurm-controller # 只能用 scale 指令: replicas: 1 -> 0 -> 1 $> kubectl -n slurm scale statefulset.apps/slurm-controller --replicas=1 ``` 不確定兩者在底層的差異 - ### gpt-4.1 (2025/07/115) 你問得非常好,這也是很多在操作 StatefulSet(或 Deployment)時常見的疑惑。 ### 兩種重啟 StatefulSet 的方法 1. **rollout restart** ```bash kubectl -n slurm rollout restart statefulset.apps/slurm-controller ``` * **作用**:會「溫和地」逐台重新建立 Pod,背後原理是更新 `pod-template-hash`,強制重新 create Pod,通常用於 image 有更新或想無痛重啟應用程式。 * **K8s 運作**:它其實是**patch**了 StatefulSet 的 `spec.template.metadata.annotations`(加個時間戳),讓 StatefulSet controller 感知到「Pod 內容變了」,然後滾動更新(restart)。 * **常見問題**:有些狀況,像 StatefulSet 管理的 controller pod 內部有鎖(或 DB/lock 等資源沒釋放),Pod 就算 delete,也無法順利啟動,或因資源未釋放而卡住。 2. **scale** ```bash kubectl -n slurm scale statefulset.apps/slurm-controller --replicas=0 kubectl -n slurm scale statefulset.apps/slurm-controller --replicas=1 ``` * **作用**:直接將 StatefulSet **縮為 0**,把所有 pod 都「殺掉」(不是滾動,而是直接刪除),再開回 1,等於「全新啟動」。 * **K8s 運作**:這是**完全刪除 pod**,並從 0 開始建立新 pod,這對於有些 sticky/lock 問題特別有效。 * **常見用途**:這是 K8s 裡最「物理」的 restart,尤其解決 stuck 或 dirty state 問題最直接。 --- ### 你遇到的情況 * **rollout restart 無法重啟**: 多半是 StatefulSet(或裡面的應用)有 resource lock、volume lock、或其他 dirty state,導致 Pod restart 起來時卡住。 * **scale to 0 再 scale to 1 可以**: 因為你強制「全關全開」,把所有 pod 狀態清除掉,再讓 controller 重新 create,通常 sticky/lock 問題都能解決。 --- ### 進階差異 | 方法 | 適合用途 | 風險 | 原理 | | --------------- | -------------- | ------------------ | -------------------- | | rollout restart | 溫和、滾動更新(不中斷服務) | 卡 lock/dirty state | patch + recreate pod | | scale 0 → 1 | 強制重啟、清除所有狀態 | 全部 shutdown 期間無服務 | 完全 kill pod 再重建 | --- ### 建議 * **正常服務重啟** → rollout restart。 * **服務卡死/lock stuck/Dirty State** → scale to 0/1。 * StatefulSet 比 Deployment 更容易遇到 lock/stickiness 問題,因為 volume/persistent storage 是 sticky 的。 --- ### 一句話總結 * `rollout restart` 只是 patch metadata 讓 K8s 滾動重建 pod,但有時 sticky 狀態無法解掉。 * `scale to 0/1` 是最強制的 reset,可以解掉 sticky/lock 問題,但會有停機。 - ### 自我小結 | 指令 | 作用 | 狀態 | |------|-----|-----| | `rollout restart` | 透過 patch 修改 annotations | pod 狀態可能沒有清除 | | `scale --repliacas` | 直接 kill pod 重來 | pod 狀態會被 reset | - 簡單講 - `rollout restart` -> 溫和重啟 (狀態:維持原本狀態) - `scale --repliacas` -> 暴力重啟 (狀態:Terminating -> Init) - ### sticky 狀態,中文為何?如何解釋? - ### gpt-4.1 (2025/07/115) ### 中文翻譯 * **sticky 狀態**:常被翻譯為「**黏著狀態**」、「**黏滯狀態**」、「**黏著性問題**」、「**卡住狀態**」,或依上下文直接描述為「某個資源被鎖住,無法釋放」。 * 在 K8s 或 IT 維運語境裡,**sticky** 比較像是「某個狀態/資源**被綁定**、**未釋放**,導致後續動作都受影響」。 ### 說明範例 * **技術解釋**: 當一個 Pod 因為資源(像是 Volume、lock file、socket 等)還被綁著沒釋放,導致重啟時仍然無法正常啟動,就稱為進入了 sticky 狀態。 * **白話說法**: 「Pod 之前使用過的某些資源沒有被釋放(例如:檔案鎖還在、資料卷沒卸載),所以即使重啟,還是會卡住。這種就叫做 sticky 狀態。」 --- ### 建議用詞 * **正式一點**: * 黏著狀態 * 黏滯狀態 * 資源未釋放狀態 * 資源卡住狀態 * **口語/維運常用**: * 卡住狀態 * 被綁住 --- ### 例句 * 這個 StatefulSet pod 因為 volume 還沒卸載,導致 sticky 狀態,重啟後還是卡住。 * 如果遇到 sticky 狀態,建議直接 scale 到 0 再 scale 回 1,強制清掉所有綁定資源。 <br> {%hackmd vaaMgNRPS4KGJDSFG0ZE0w %}