[指令] 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 %}