# ⌨️ kubectl nhanh: alias, dry-run, jsonpath và kubectl explain

> Sign in to view the full content of this lesson.

## Mục tiêu bài học

Sau bài này, bạn sẽ:

- Cấu hình alias và bash completion để gõ lệnh kubectl nhanh hơn trong phòng thi.
- Dùng `--dry-run=client -o yaml` sinh manifest cho hầu hết object thường gặp trong đề CKA.
- Trích đúng field bằng `-o jsonpath` và `-o custom-columns` mà không phải mở tài liệu.
- Tra cấu trúc field bằng `kubectl explain` (kể cả `--recursive`) ngay trên terminal.
- Nhận ra 5 lỗi khiến lệnh "chạy được nhưng ra sai" và sửa trong vài giây.

Thời lượng gợi ý: 35 phút, trong đó ít nhất 20 phút bạn phải tự gõ trên cluster lab.

## Ôn lại nhanh

Bài trước bạn đã dựng xong lab và kiểm tra `kubectl get nodes` trả về node `Ready`. Bài đó cũng nhắc rằng trong phòng thi bạn có sẵn một terminal, một tab trình duyệt tới kubernetes.io, và mỗi câu hỏi đều ghi rõ context cần `kubectl config use-context`. Bài này chỉ tập trung vào một việc: làm sao gõ ít hơn mà ra manifest đúng hơn.

> [!NOTE]
> Trong CKA, mỗi phút tiết kiệm được là một phút để kiểm tra lại đáp án. Phần lớn thí sinh trượt không phải vì không biết làm, mà vì gõ tay YAML sai chính tả hoặc hết giờ. `--dry-run` sinh manifest giúp bạn loại bỏ hoàn toàn lỗi indentation.

## Ví dụ mẫu từ đầu đến cuối

Đề bài kiểu CKA: "Tạo Deployment tên `web` dùng image `nginx:1.25`, 3 replicas, sau đó in ra tên các pod và node mà chúng đang chạy."

**Bước 1: thiết lập alias một lần cho cả buổi thi**

```bash
alias k=kubectl
complete -o default -F __start_kubectl k
export do="--dry-run=client -o yaml"
```

`$do` không có nháy khi dùng, shell sẽ tách thành 3 tham số đúng như bạn muốn.

**Bước 2: sinh manifest, không tạo gì trên cluster**

```bash
k create deployment web --image=nginx:1.25 --replicas=3 $do > web.yaml
```

**Bước 3: đọc lại và sửa nếu cần**

```bash
cat web.yaml
```

Bạn sẽ thấy `apiVersion: apps/v1`, `kind: Deployment`, cùng `metadata`, `spec.selector`, `spec.template`. Không cần thuộc lòng các field con.

**Bước 4: apply**

```bash
k apply -f web.yaml
k get deploy web
```

**Bước 5: kiểm tra bằng jsonpath**

```bash
k get pods -l app=web -o jsonpath='{.items[*].metadata.name}'
k get pods -l app=web -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName
```

Kết quả bước 5 là bằng chứng nộp bài: tên pod và node. Không cần `grep`, không cần copy tay.

Sơ đồ thao tác bạn sẽ lặp lại cho gần như mọi câu workload trong đề:

```mermaid
flowchart LR
  A["📝<br/>Đọc yêu cầu"] --> B["🏗️<br/>create --dry-run=client -o yaml"]
  B --> C["✏️<br/>Sửa file YAML"]
  C --> D["🚀<br/>apply -f"]
  D --> E["🔍<br/>Verify bằng jsonpath"]
```

## Thực hành có hướng dẫn

Làm tuần tự trên lab của bạn. Mỗi bài có lệnh và kết quả mong đợi, đừng bỏ qua bước kiểm tra.

### Bài 1: alias và completion

```bash
alias k=kubectl
complete -o default -F __start_kubectl k
k version --client
k get no
```

`k get no` phải chạy được, chứng tỏ completion đã gắn với alias `k` chứ không chỉ với `kubectl`.

Thử gõ `k get pod -n kube` rồi bấm Tab, bạn sẽ thấy namespace `kube-system` được gợi ý.

> [!TIP]
> Alias chỉ sống trong shell hiện tại. Trong phòng thi, mỗi lần mở terminal mới bạn phải chạy lại, hoặc thêm 2 dòng trên vào `~/.bashrc` rồi `source ~/.bashrc`.

### Bài 2: dry-run cho 5 loại object hay gặp nhất

Chạy đủ 5 lệnh dưới đây, mỗi lệnh ghi ra một file riêng:

```bash
k run solo --image=busybox:1.36 $do > pod.yaml
k create deployment api --image=nginx:1.25 --replicas=2 $do > deploy.yaml
k create service clusterip api-svc --tcp=80:80 $do > svc.yaml
k create job once --image=busybox:1.36 $do -- sh -c 'echo hello' > job.yaml
k create cronjob every-min --image=busybox:1.36 --schedule='*/1 * * * *' $do -- date > cronjob.yaml
```

Kiểm tra:

```bash
head -5 pod.yaml deploy.yaml svc.yaml job.yaml cronjob.yaml
```

Mỗi file phải có `apiVersion` và `kind` ở 2 dòng đầu. Nếu file rỗng, gần như chắc chắn bạn quên `$do` hoặc đặt `$do` sau dấu `>`.

Lưu ý riêng với `job` và `cronjob`: dấu `--` là bắt buộc, nó ngăn cách phần command truyền vào container. Thiếu nó, kubectl sẽ hiểu `sh` là tham số của chính nó và báo lỗi.

### Bài 3: jsonpath cơ bản

```bash
k get nodes -o jsonpath='{.items[*].metadata.name}'
k get pods -n kube-system -o jsonpath='{.items[*].spec.nodeName}'
k config view --minify -o jsonpath='{.clusters[0].cluster.server}'
```

Kết quả mong đợi: dòng 1 in tên node, dòng 2 in danh sách node (có lặp), dòng 3 in địa chỉ API server.

Thêm định dạng để đọc được nhiều dòng:

```bash
k get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.phase}{"\n"}{end}'
```

Bây giờ thử lấy tên container của một pod cụ thể:

```bash
k get pod solo -o jsonpath='{.spec.containers[0].name}'
```

Chú ý: khi lấy một object đơn (`k get pod solo`), jsonpath **không** có `.items`, vì kết quả không phải list.

<details><summary>Bấm để xem mẹo nhớ nhanh</summary>

List (kết quả `k get pods`) thì bắt đầu bằng `{.items[*]...`. Object đơn (kết quả `k get pod ten`) thì bắt đầu bằng `{.metadata...`. Nếu bạn dùng sai, kubectl trả về chuỗi rỗng chứ không báo lỗi, nên rất dễ nộp nhầm.

</details>

### Bài 4: custom-columns thay cho jsonpath dài

```bash
k get pods -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,NODE:.spec.nodeName
k get pv -o custom-columns=NAME:.metadata.name,CAP:.spec.capacity.storage,MODE:.spec.accessModes
```

Cách này cho ra bảng thẳng hàng, dễ đọc hơn jsonpath khi cần so sánh nhiều pod.

### Bài 5: kubectl explain

```bash
k explain pod
k explain pod.spec.containers.resources
k explain deployment.spec.strategy --recursive
```

Câu hỏi tự kiểm tra: field `resources.requests` nhận những key nào? Chạy `k explain pod.spec.containers.resources.requests` và đọc phần mô tả.

Thử một field bạn chưa từng dùng:

```bash
k explain pod.spec.tolerations
```

`kubectl explain` đọc dữ liệu schema cục bộ của cluster, nên nó chạy được cả khi không có internet. Trong phòng thi, đây là cách nhanh nhất để kiểm tra tên field trước khi viết YAML.

> [!IMPORTANT]
> Đường dẫn trong `kubectl explain` dùng tên field, không dùng dấu ngoặc, không dùng `[]`. Viết `pod.spec.containers`, không viết `pod.spec.containers[]`. Viết `pod.spec.volumes.configMap`, không viết `pod.spec.volumes.configMap.name`.

### Bài 6: bài tổng hợp 5 phút

Yêu cầu: tạo Deployment `cache` image `redis:7`, 2 replicas, sau đó in ra tên pod kèm node.

Tự làm trước khi xem đáp án.

<details><summary>Bấm để xem đáp án gợi ý</summary>

```bash
k create deployment cache --image=redis:7 --replicas=2 $do > cache.yaml
k apply -f cache.yaml
k get pods -l app=cache -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName
```

Nếu label không phải `app=cache`, kiểm tra `k get deploy cache -o jsonpath='{.spec.selector.matchLabels}'` để lấy label đúng.

</details>

## Lỗi thường gặp và cách xử lý

| Lỗi | Triệu chứng | Cách sửa |
| --- | --- | --- |
| Quên `--dry-run` | Lệnh vừa tạo object thật vừa in YAML | Luôn dùng `$do` đã export, kiểm tra trước khi Enter |
| Dùng `-o yaml` mà không redirect | YAML trôi trên màn hình, không lưu | Thêm `> file.yaml` ở cuối |
| Dùng `>` làm mất file cũ | File manifest bị ghi đè | Kiểm tra `ls` trước, hoặc dùng tên file mới |
| jsonpath trong nháy kép | `$` và `{}` bị shell xử lý trước, kết quả rỗng | Luôn dùng nháy đơn: `'{.items[*].metadata.name}'` |
| Sai `explain` path | Báo "field not found" | Bỏ `[]`, dùng tên field đúng như trong YAML, kiểm tra bằng `--recursive` |
| Quên `--` với job/cronjob | kubectl hiểu sai tham số | Đặt `--` ngay trước command của container |
| Alias mất sau khi mở terminal mới | `k: command not found` | Chạy lại alias hoặc thêm vào `~/.bashrc` |

Hai điểm cần nhớ thêm về `--dry-run`:

- `--dry-run=client` chỉ render ở máy bạn, không gửi gì lên API server.
- `--dry-run=server` gửi lên API server để validate và nhận default value, nhưng cũng không tạo object. Dùng khi bạn muốn chắc manifest qua được admission.

Nếu `kubectl explain` trả về "the server doesn't have a resource type", bạn đang truyền tên viết tắt chưa được hỗ trợ. Dùng tên đầy đủ (`deployment` thay vì `deploy`) hoặc thêm `--api-version` khi cần chỉ rõ nhóm API.

## Góc chuyên gia

Vài thói quen mình rút ra sau khi thi:

1. **Đặt alias trước khi đọc câu hỏi đầu tiên.** Hai dòng alias và completion mất 5 giây, tiết kiệm hàng phút về sau.
2. **Không bao giờ gõ YAML tay từ đầu.** Luôn `k create ... $do > file.yaml` rồi sửa. Xác suất sai indentation khi gõ tay gần như 100% dưới áp lực thời gian.
3. **`kubectl explain` trước, google sau.** Tab tài liệu của bạn chỉ nên dùng cho những khái niệm lớn (Ingress, NetworkPolicy), còn tên field thì `explain` nhanh hơn nhiều.
4. **Dùng `-o wide` trước, jsonpath sau.** `k get pods -o wide` cho bạn node, IP, image trong một dòng. Chỉ dùng jsonpath khi đề yêu cầu đúng một giá trị cụ thể.
5. **Kiểm tra định kỳ context.** Trước khi apply, chạy `k config current-context`. Đây là lỗi tốn điểm nhiều nhất trong CKA.
6. **Đặt giới hạn thời gian cho mỗi câu.** Nếu một câu quá 6 phút mà chưa xong, đánh dấu và đi tiếp. Quay lại sau.

> [!CAUTION]
> Đừng dùng `--force --grace-period=0` như một phản xạ. Nó hữu ích khi xóa pod bị kẹt, nhưng nếu bạn xóa nhầm object của câu khác thì không có cách hoàn tác trong phòng thi.

## Tóm tắt và bước tiếp theo

Bốn kỹ năng của bài này tạo thành một vòng lặp bạn sẽ dùng hàng chục lần trong 2 giờ thi:

- **alias**: gõ ít hơn, ít lỗi hơn.
- **dry-run**: sinh manifest chuẩn thay vì gõ tay.
- **jsonpath / custom-columns**: trích đúng giá trị để verify.
- **explain**: tra field ngay trên terminal, không phụ thuộc internet.

Ôn lại bằng thẻ ghi nhớ:

```flashcard
[
  { "front": "Lệnh sinh YAML của Deployment mà không tạo gì trên cluster?", "back": "kubectl create deployment ten --image=nginx --replicas=3 --dry-run=client -o yaml > ten.yaml" },
  { "front": "--dry-run=client khác --dry-run=server ở điểm nào?", "back": "client chỉ render ở máy local; server gửi lên API server để validate và nhận default value, nhưng vẫn không tạo object." },
  { "front": "Hai lệnh thiết lập nhanh alias k cho kubectl kèm completion?", "back": "alias k=kubectl và complete -o default -F __start_kubectl k" },
  { "front": "Biến môi trường nào thay cho cụm --dry-run=client -o yaml?", "back": "export do=\"--dry-run=client -o yaml\", sau đó dùng $do trong lệnh." },
  { "front": "jsonpath lấy tên tất cả pod trong namespace hiện tại?", "back": "kubectl get pods -o jsonpath='{.items[*].metadata.name}'" },
  { "front": "Vì sao jsonpath phải đặt trong nháy đơn?", "back": "Để shell không expand $ và {} trước khi kubectl nhận biểu thức." },
  { "front": "Lấy field từ một object đơn thì jsonpath bắt đầu bằng gì?", "back": "{.metadata.name}, không có .items, vì kết quả không phải list." },
  { "front": "kubectl explain dùng để làm gì?", "back": "Tra documentation của từng field trong manifest ngay trên terminal, dùng schema của cluster nên không cần internet." },
  { "front": "Xem toàn bộ cây field của Deployment?", "back": "kubectl explain deployment --recursive" },
  { "front": "In tên pod kèm node bằng custom-columns?", "back": "kubectl get pods -o custom-columns=NAME:.metadata.name,NODE:.spec.nodeName" },
  { "front": "Lệnh tạo CronJob bằng dry-run?", "back": "kubectl create cronjob ten --image=busybox --schedule='*/1 * * * *' --dry-run=client -o yaml -- date" },
  { "front": "Dấu -- trong kubectl create job có tác dụng gì?", "back": "Ngăn cách phần command và args truyền vào container, thiếu nó kubectl sẽ hiểu sai tham số." }
]
```

Điền nhanh các chỗ trống để tự kiểm tra:

```cloze
Để sinh manifest mà không tạo object trên cluster, dùng {{c1::--dry-run=client}} kèm -o yaml.
Biểu thức jsonpath {{c2::.items[*].metadata.name}} lấy tên của mọi pod trong kết quả dạng list.
Lệnh kubectl {{c3::explain}} pod.spec.containers.resources cho biết cấu trúc field resources mà không cần mở browser.
Alias {{c4::k}} là cách tiết kiệm thời gian gõ phổ biến nhất trong phòng thi.
Muốn API server kiểm tra manifest trước khi tạo, dùng {{c5::--dry-run=server}}.
Với job và cronjob, phải đặt {{c6::--}} ngay trước command của container.
```

**Bước tiếp theo:** bài kế tiếp là Checkpoint của Module 1, gồm câu hỏi tổng hợp về cấu trúc đề thi, môi trường lab và chính bốn kỹ năng kubectl vừa luyện. Hãy chắc rằng bạn đã làm hết 6 bài thực hành ở trên trước khi vào checkpoint, vì checkpoint sẽ không nhắc lại lệnh mà chỉ đưa tình huống.
