---
id: kubernetes-architecture
title: "Kiến trúc Kubernetes"
track: kubernetes
trackTitle: "Kubernetes"
order: 1
level: core
status: published
estimatedMinutes: 45
prerequisites:
  - docker-compose
next: kubernetes-manifests
reviewedAgainst: 2026-10-15
sources:
  - title: "KodeKloud Engineer — 100 Days of DevOps"
    url: https://engineer.kodekloud.com/practice
  - title: "Kubernetes components"
    url: https://kubernetes.io/docs/concepts/overview/components/
  - title: "Pods"
    url: https://kubernetes.io/docs/concepts/workloads/pods/
  - title: "kubectl run"
    url: https://kubernetes.io/docs/reference/kubectl/generated/kubectl_run/
  - title: "Service"
    url: https://kubernetes.io/docs/concepts/services-networking/service/
  - title: "Ingress"
    url: https://kubernetes.io/docs/concepts/services-networking/ingress/
---

# Kiến trúc Kubernetes

Compose giữ vài container trên một máy. Máy tắt, process chết, bạn tự bật lại. Kubernetes giữ số replica đã khai, tạo Pod mới khi Pod lệch trạng thái đó. Lab 100 Days chạy trên cluster có sẵn; bài này chưa cài kind hay AKS.

## Để làm gì

Bạn chỉ ra được object nào tạo process, object nào giữ số bản, object nào cấp tên ổn định, và tạo được một Pod bằng YAML hoặc `kubectl run`.

## Gồm những gì

- Control plane và node.
- Pod: đơn vị nhỏ nhất, một hoặc nhiều container, một IP.
- `kubectl run` tạo Pod. `--restart` mặc định `Always`.
- Deployment, Service, Ingress — đủ để biết chỗ đứng. YAML đầy đủ ở bài sau.

Gateway API chỉ nhắc tên. Cài cluster để bài khác.

## Dùng khi nào

Cần hơn một bản, tự thay bản hỏng, hoặc nhiều máy. Một container trên laptop thì Docker đủ.

Đừng ghi IP Pod vào config của app khác. Pod tạo lại là IP khác.

## Kiến thức cốt lõi

Control plane nhận khai báo. Node chạy Pod. `kubectl` chỉ nói chuyện với `kube-apiserver`.

| Thành phần | Việc làm |
| --- | --- |
| `kube-apiserver` | Cửa API. Mọi lệnh `kubectl` đi vào đây |
| `etcd` | Kho trạng thái cluster |
| `kube-scheduler` | Chọn node cho Pod chưa có node |
| `kube-controller-manager` | Kéo trạng thái thật về đúng khai báo |
| `kubelet` | Agent trên node, chạy Pod được gán |
| `kube-proxy` | Quy tắc mạng để Service tới Pod |

Pod là chỗ process sống. Trong Pod: chung network namespace, có thể chung volume. Mỗi Pod một IP. Xóa Pod là mất filesystem của container, trừ khi dữ liệu nằm trên volume — bài sau.

Một Pod trần không tự sống lại khi node mất, trừ khi có controller. `restartPolicy` của Pod chỉ nói kubelet có restart container trong Pod đó không. Muốn 3 bản và tự thay: Deployment.

`kubectl run` hiện tạo **Pod**, không tạo Deployment. Docs: `kubectl run NAME --image=image`. `--restart` mặc định `Always`. Lab một phát rồi thoát: `--restart=Never`. `--env`, `--port`, `--labels` gắn lúc tạo. `--dry-run=client` in YAML, chưa gửi API. `--expose` tạo Service `ClusterIP`, bắt buộc có `--port`.

Service chọn Pod bằng label, không bằng tên Deployment. `ClusterIP` là mặc định: chỉ gọi từ trong cluster. `NodePort` mở port trên node. `LoadBalancer` nhờ hạ tầng cấp LB. `ExternalName` trả DNS ra ngoài, không chuyển theo selector Pod.

Ingress mô tả route HTTP vào Service. Thiếu ingress controller thì object Ingress nằm đó, không có traffic. Lab 100 Days phần lớn dừng ở Pod / Deployment / Service.

<div class="net-flow" aria-label="Đường traffic vào app trên Kubernetes">
<span class="net-step" style="--layer:6">Client</span>
<span class="net-arrow" aria-hidden="true"></span>
<span class="net-step" style="--layer:5">Ingress</span>
<span class="net-arrow" aria-hidden="true"></span>
<span class="net-step" style="--layer:4">Service</span>
<span class="net-arrow" aria-hidden="true"></span>
<span class="net-step" style="--layer:3">Pod</span>
</div>

<p class="osi-note">Ingress chỉ có khi đã cài controller. Lab một mình thường dừng ở Service + <code>kubectl port-forward</code>.</p>

Label lệch selector thì Service không có endpoint. Pod vẫn `Running`. `curl` vào Service bị refused hoặc treo, tùy môi trường.

## Lệnh và code mẫu

Pod tối thiểu. Image pin tag; `latest` đổi lúc người khác push.

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: lab-nginx
  labels:
    app: web
spec:
  containers:
    - name: nginx
      image: nginx:1.27-alpine
      ports:
        - containerPort: 80
```

Cùng việc bằng `kubectl run` (tạo Pod, chưa apply file):

```bash
kubectl run lab-nginx --image=nginx:1.27-alpine --port=80 --labels=app=web --dry-run=client -o yaml
kubectl run lab-nginx --image=nginx:1.27-alpine --port=80 --labels=app=web
kubectl get pod lab-nginx -o wide
kubectl delete pod lab-nginx
```

`--dry-run=client` trước khi gửi. Xóa Pod trần là hết. Deployment thì xóa một replica, controller tạo cái khác.

Label Deployment và Service phải cùng key:

```text
template.metadata.labels.app = web
Service spec.selector.app    = web
```

Tên Deployment có thể là `web-app`. Service không tìm chữ `web-app`.

## Thực hành

1. Vẽ Client → Service → Pod. Ghi label Pod và selector Service.
2. `kubectl run lab-nginx --image=nginx:1.27-alpine --dry-run=client -o yaml`. Đọc `kind: Pod`.
3. Nếu có cluster lab: bỏ `--dry-run`, `kubectl get pod`, rồi `kubectl delete pod lab-nginx`.
4. Chọn kiểu Service cho lab một mình: `ClusterIP` + port-forward.

## Kết quả mong đợi

Dry-run in `kind: Pod`. Có cluster thì Pod lên `Running`. Xóa xong `get pod` không còn tên đó.

## Checklist

- [ ] Nói được Pod khác Deployment chỗ nào.
- [ ] `kubectl run` tạo Pod, không phải Deployment.
- [ ] App khác gọi DNS Service, không gọi IP Pod.
- [ ] Bốn kiểu Service: ai gọi được.
- [ ] Ingress không nhận traffic nếu thiếu controller.

## Thẻ ôn

### kubernetes-architecture-pod-deploy
kind: distinguish
cue: Pod là chỗ process chạy. Deployment giữ số replica và tạo Pod mới khi Pod mất.

Q: Bạn `kubectl run` một Nginx. Node reboot, Pod biến. Muốn 3 bản và tự thay thì thiếu object nào, và `run` vừa tạo ra cái gì?

A: `kubectl run` tạo một Pod. Pod trần không có controller giữ số bản. Node mất hoặc ai `delete` thì hết. Deployment (qua ReplicaSet) giữ `replicas` và tạo Pod mới khi lệch. `restartPolicy` chỉ restart container trong cùng Pod, không thay Pod đã mất.

### kubernetes-architecture-run
kind: command
cue: `kubectl run NAME --image=…` tạo Pod. `--restart` mặc định Always. `--dry-run=client` in YAML chưa gửi.

Q: Lab bảo tạo Pod tên `lab-nginx` từ image Nginx, chưa cần Deployment. Lệnh nào, flag nào xem YAML trước, và `--restart=Never` khác mặc định thế nào?

A: `kubectl run lab-nginx --image=nginx:1.27-alpine`. Thêm `--dry-run=client -o yaml` để in spec, chưa tạo. Mặc định `--restart=Always`: kubelet bật lại container nếu process chết. `--restart=Never` dùng khi chạy một lệnh rồi thoát, không restart.

### kubernetes-architecture-selector
kind: trap
cue: Service chọn Pod theo label. Tên Deployment không phải selector.

Q: Deployment tên `web-app`, Pod gắn `app: web`. Service để `selector: { app: web-app }` vì trùng tên. Traffic vào Service đi đâu?

A: Không tới Pod. Selector đọc label trên Pod, không đọc `metadata.name` của Deployment. Phải `app: web`, khớp `template.metadata.labels`. Tên `web-app` chỉ để `kubectl get deploy`.

### kubernetes-architecture-clusterip
kind: default
cue: Service không ghi type thì ClusterIP. Chỉ gọi từ trong cluster.

Q: Manifest Service không có `type`. Từ laptop `curl` IP Service. Vì sao thường không vào được, và lab làm sao thử?

A: Mặc định `ClusterIP`: địa chỉ nội bộ cluster. Máy bạn không nằm trên mạng đó. Lab: `kubectl port-forward service/… host:service`. Muốn từ ngoài thì NodePort, LoadBalancer, hoặc Ingress — khi đã có hạ tầng / controller.

### kubernetes-architecture-ingress
kind: trap
cue: Object Ingress không nhận traffic nếu chưa có ingress controller.

Q: Tạo Ingress, `kubectl get ingress` chưa có ADDRESS. Đợi thêm năm phút có tự lên không?

A: Không. Ingress là khai báo route. Controller (nginx, traefik, cloud) mới mở listen và gắn địa chỉ. Thiếu controller thì object nằm đó. Lab 100 Days phần lớn chưa cần Ingress; Service đủ.

## Lỗi thường gặp

**Coi Pod như VM nhỏ.** Pod dùng kernel node. CPU/memory phải khai `requests`/`limits`. Không có thì scheduler xếp mù.

**Ghi IP Pod vào config.** Pod thay là IP mất. Điểm ổn định là DNS Service.

**Tưởng `kubectl run` ra Deployment.** Bản hiện tại tạo Pod. Cần replica thì viết Deployment.

**Tạo Ingress rồi chờ IP public.** Cần controller, thường cần thêm DNS/LB của môi trường.

## Nguồn

1. [KodeKloud Engineer — 100 Days of DevOps](https://engineer.kodekloud.com/practice) — đề cương ngày Deploy Pods in Kubernetes Cluster. Trang practice cần đăng nhập. Bài này viết lại lệnh, không chép lab.
2. [Kubernetes components](https://kubernetes.io/docs/concepts/overview/components/) — control plane và node.
3. [Pods](https://kubernetes.io/docs/concepts/workloads/pods/) — đơn vị chạy, IP, nhiều container.
4. [kubectl run](https://kubernetes.io/docs/reference/kubectl/generated/kubectl_run/) — tạo Pod, `--image`, `--restart`, `--dry-run`.
5. [Service](https://kubernetes.io/docs/concepts/services-networking/service/) — kiểu và selector.
6. [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) — tầng route ngoài, cần controller.

## Tiếp theo

[Deployment và Service](./02-manifests.md)
