Back to blog

Kubernetes Cơ Bản

kubernetesdockerdevopscontainerstutorial
Kubernetes Cơ Bản

Chào mừng đến Giai đoạn 3 của Lộ Trình Học Docker & Kubernetes! Bạn đã thành thạo Docker cơ bản, viết Dockerfile, và điều phối ứng dụng multi-container với Docker Compose. Giờ là lúc bước nhảy lớn — Kubernetes.

Docker Compose ngon lành cho deploy trên một máy. Nhưng khi app cần chạy trên nhiều server thì sao? Khi container crash lúc 3 giờ sáng mà không ai thức để restart? Khi traffic Black Friday đổ về và bạn cần 50 replicas thay vì 2? Đó là lúc Kubernetes xuất hiện.

Bạn Sẽ Học Được

✅ Hiểu kiến trúc Kubernetes (control plane và worker nodes)
✅ Cài đặt Kubernetes cluster local với minikube
✅ Tạo và quản lý Pods — đơn vị deploy nhỏ nhất
✅ Dùng Deployments cho rolling updates và rollbacks
✅ Expose ứng dụng với Services (ClusterIP, NodePort, LoadBalancer)
✅ Quản lý configuration với ConfigMaps và Secrets
✅ Tổ chức resources với Namespaces, Labels và Selectors
✅ Debug và troubleshoot Kubernetes workloads

Thời gian: 7–10 ngày, 1–2 giờ mỗi ngày
Yêu cầu: Giai đoạn 2: Docker Compose & Ứng Dụng Multi-Container


Phần 1: Kubernetes Là Gì?

Kubernetes (hay viết tắt là K8s — số 8 đại diện cho 8 ký tự giữa "K" và "s") là một platform orchestration container mã nguồn mở. Ban đầu được Google thiết kế, giờ do Cloud Native Computing Foundation (CNCF) duy trì, nó tự động hóa việc deploy, scale và quản lý ứng dụng container.

Tại Sao Cần Kubernetes?

Docker chạy containers. Kubernetes chạy containers ở quy mô lớn. Đây là những gì nó mang lại:

Khả năngKhông có K8sCó K8s
ScalingTự tay start thêm containerskubectl scale --replicas=10
Self-healingContainer crash → bạn phải restartK8s tự động restart containers lỗi
Load balancingTự setup nginx/HAProxyBuilt-in Service load balancing
Rolling updatesTắt cũ → bật mới (downtime)Zero-downtime rolling deployments
Service discoveryHardcode IPs hoặc dùng external DNSBuilt-in DNS cho mọi Service
ConfigurationFile environment trên từng serverTập trung qua ConfigMaps và Secrets
Resource managementCầu trời containers không ngốn hết tài nguyênCPU/memory requests và limits

Kubernetes vs Docker Compose vs Docker Swarm

FeatureDocker ComposeDocker SwarmKubernetes
Độ phức tạpĐơn giảnTrung bìnhPhức tạp
Multi-nodeKhôngCóCó
Auto-scalingKhôngHạn chếNâng cao (HPA, VPA)
Self-healingKhôngCơ bảnNâng cao
EcosystemChỉ DockerChỉ DockerKhổng lồ (CNCF)
Learning curveThấpTrung bìnhCao
Phù hợp choDevelopmentProduction nhỏProduction quy mô lớn

Các Bản Phân Phối Kubernetes

Không phải Kubernetes nào cũng giống nhau. Có nhiều distribution:

  • Vanilla Kubernetes (kubeadm) — Bản tiêu chuẩn, tự cài đặt
  • minikube — Cluster single-node cho development local
  • kind (Kubernetes in Docker) — Chạy K8s nodes như Docker containers
  • k3s — K8s nhẹ của Rancher (tuyệt vời cho edge/IoT)
  • MicroK8s — K8s nhẹ của Canonical (dựa trên snap)
  • OpenShift — Platform Kubernetes enterprise của Red Hat
  • Managed services — EKS (AWS), GKE (Google), AKS (Azure)

Để học, chúng ta sẽ dùng minikube — cách dễ nhất để có Kubernetes cluster chạy trên laptop.


Phần 2: Kiến Trúc Kubernetes

Hiểu kiến trúc Kubernetes là điều cần thiết trước khi bạn deploy bất cứ thứ gì. Một Kubernetes cluster có hai phần chính: control plane và worker nodes.

Các Thành Phần Control Plane

Control plane là "bộ não" của cluster. Nó đưa ra quyết định về scheduling, phát hiện events và phản hồi thay đổi.

API Server (kube-apiserver)

  • Cánh cửa chính vào Kubernetes — mọi command đều đi qua đây
  • kubectl giao tiếp với API server
  • RESTful API validate và xử lý requests
  • Thành phần duy nhất nói chuyện trực tiếp với etcd

etcd

  • Key-value store phân tán lưu trữ toàn bộ cluster state
  • Lưu configuration, secrets, service discovery data
  • Nếu etcd chết, cluster mất bộ nhớ
  • Luôn chạy với backup trong production

Scheduler (kube-scheduler)

  • Quyết định node nào sẽ chạy Pod mới
  • Xem xét: resource requirements, affinity rules, taints/tolerations
  • Không chạy Pod — chỉ gán nó cho một node

Controller Manager (kube-controller-manager)

  • Chạy các controller loop theo dõi cluster state và sửa chữa
  • Deployment controller: đảm bảo đúng số replicas đang chạy
  • Node controller: giám sát sức khỏe node
  • Job controller: quản lý batch jobs

Các Thành Phần Worker Node

Worker nodes là các máy thực sự chạy containers.

kubelet

  • Agent chạy trên mọi worker node
  • Nhận Pod specs từ API server
  • Đảm bảo containers đang chạy và healthy
  • Báo cáo status của node và Pod về control plane

kube-proxy

  • Network proxy trên mỗi node
  • Duy trì network rules cho Service communication
  • Implement Service abstraction (load balancing giữa các Pods)

Container Runtime

  • Phần mềm chạy containers (containerd, CRI-O)
  • Docker là runtime ban đầu nhưng Kubernetes đã bỏ hỗ trợ Docker từ v1.24
  • containerd hiện là runtime phổ biến nhất

Mọi Thứ Kết Hợp Như Thế Nào

Khi bạn chạy kubectl apply -f deployment.yaml, đây là những gì xảy ra:


Phần 3: Cài Đặt Kubernetes

Cài Đặt minikube

minikube tạo một single-node Kubernetes cluster trên máy local.

macOS:

# Cài bằng Homebrew
brew install minikube
 
# Hoặc download trực tiếp
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-arm64
sudo install minikube-darwin-arm64 /usr/local/bin/minikube

Linux:

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

Windows (PowerShell):

winget install Kubernetes.minikube

Khởi Động Cluster

# Start minikube với cài đặt mặc định
minikube start
 
# Start với tài nguyên cụ thể
minikube start --cpus=4 --memory=8192 --driver=docker
 
# Kiểm tra cluster status
minikube status

Output:

minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

Cài Đặt kubectl

kubectl là công cụ command-line để tương tác với Kubernetes.

macOS:

brew install kubectl

Linux:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install kubectl /usr/local/bin/kubectl

Kiểm tra cài đặt:

# Kiểm tra kubectl version
kubectl version --client
 
# Kiểm tra kết nối cluster
kubectl cluster-info
 
# Xem cluster nodes
kubectl get nodes

Output:

NAME       STATUS   ROLES           AGE   VERSION
minikube   Ready    control-plane   2m    v1.31.0

Các Lệnh kubectl Cơ Bản

Đây là những commands bạn sẽ dùng hàng ngày:

# Lấy resources
kubectl get pods                    # Liệt kê tất cả pods
kubectl get pods -o wide            # Với chi tiết thêm (IP, node)
kubectl get services                # Liệt kê tất cả services
kubectl get all                     # Liệt kê mọi thứ
 
# Describe (thông tin chi tiết)
kubectl describe pod <pod-name>     # Chi tiết pod
kubectl describe node minikube      # Chi tiết node
 
# Tạo và apply
kubectl apply -f manifest.yaml     # Apply manifest (tạo hoặc cập nhật)
kubectl create -f manifest.yaml    # Tạo mới (lỗi nếu đã tồn tại)
 
# Xóa
kubectl delete pod <pod-name>      # Xóa một pod
kubectl delete -f manifest.yaml    # Xóa resources từ manifest
 
# Logs và debugging
kubectl logs <pod-name>            # Xem pod logs
kubectl logs -f <pod-name>         # Stream logs (follow)
kubectl exec -it <pod-name> -- sh  # Shell vào pod
 
# Quản lý context
kubectl config get-contexts        # Liệt kê contexts
kubectl config use-context minikube # Chuyển context

Mẹo: Tạo alias để gõ nhanh hơn:
alias k=kubectl
Giờ bạn gõ k get pods thay vì kubectl get pods.


Phần 4: Pods

Pod là đơn vị deploy nhỏ nhất trong Kubernetes. Nó là wrapper bao quanh một hoặc nhiều containers chia sẻ cùng network và storage.

Tại Sao Pod Chứ Không Phải Container?

Trong Docker, bạn nghĩ theo containers. Trong Kubernetes, bạn nghĩ theo Pods. Tại sao thêm một lớp nữa?

  • Pods có thể chạy nhiều containers cần làm việc cùng nhau (sidecar pattern)
  • Containers trong cùng Pod chia sẻ cùng IP address và localhost
  • Containers trong cùng Pod chia sẻ volumes
  • Pods là đơn vị scheduling — chúng luôn chạy trên cùng node

Hầu hết thời gian, một Pod chạy một container. Multi-container Pods dành cho các pattern cụ thể như logging sidecars, proxies, hoặc init containers.

Tạo Pod Đầu Tiên

Cách imperative (test nhanh):

# Chạy một pod trực tiếp
kubectl run nginx --image=nginx:alpine --port=80
 
# Kiểm tra nó đang chạy
kubectl get pods

Output:

NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          10s

Cách declarative (khuyến khích):

Tạo file pod.yaml:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
  labels:
    app: my-app
    environment: dev
spec:
  containers:
    - name: app
      image: nginx:alpine
      ports:
        - containerPort: 80
      resources:
        requests:
          memory: "64Mi"
          cpu: "100m"
        limits:
          memory: "128Mi"
          cpu: "250m"

Apply:

kubectl apply -f pod.yaml
kubectl get pods
kubectl describe pod my-app

Vòng Đời Pod

Pods trải qua nhiều giai đoạn:

Giai đoạnÝ nghĩa
PendingPod được chấp nhận nhưng containers chưa chạy (đang pull images, scheduling)
RunningÍt nhất một container đang chạy
SucceededTất cả containers thoát thành công (exit code 0)
FailedÍt nhất một container thoát với lỗi
UnknownKhông xác định được state của Pod (thường do vấn đề giao tiếp node)

Multi-Container Pods

Đôi khi containers cần làm việc cùng nhau chặt chẽ. Các pattern phổ biến:

Sidecar pattern — container phụ trợ bên cạnh app chính:

apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
  labels:
    app: web
spec:
  containers:
    # Ứng dụng chính
    - name: app
      image: nginx:alpine
      ports:
        - containerPort: 80
      volumeMounts:
        - name: shared-logs
          mountPath: /var/log/nginx
 
    # Sidecar: gửi logs đến hệ thống tập trung
    - name: log-shipper
      image: busybox
      command: ["sh", "-c", "tail -f /logs/access.log"]
      volumeMounts:
        - name: shared-logs
          mountPath: /logs
 
  volumes:
    - name: shared-logs
      emptyDir: {}

Cả hai containers chia sẻ volume shared-logs. Container nginx ghi logs, container log-shipper đọc và chuyển tiếp chúng.

Init Containers

Init containers chạy trước khi containers chính start. Hoàn hảo cho các setup tasks:

apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
  labels:
    app: web
spec:
  initContainers:
    # Đợi database sẵn sàng
    - name: wait-for-db
      image: busybox
      command: ["sh", "-c", "until nc -z db-service 5432; do echo waiting for db; sleep 2; done"]
 
    # Chạy database migrations
    - name: run-migrations
      image: my-app:latest
      command: ["python", "manage.py", "migrate"]
 
  containers:
    - name: app
      image: my-app:latest
      ports:
        - containerPort: 8000

Init containers chạy tuần tự — mỗi cái phải hoàn thành trước khi cái tiếp theo bắt đầu. Container chính chỉ start sau khi tất cả init containers thành công.

Debug Pods

# Xem chi tiết pod (events ở cuối hữu ích nhất)
kubectl describe pod my-app
 
# Xem logs
kubectl logs my-app
kubectl logs my-app -c log-shipper    # Container cụ thể trong multi-container pod
kubectl logs my-app --previous        # Logs từ container đã crash
 
# Shell vào pod đang chạy
kubectl exec -it my-app -- /bin/sh
 
# Port-forward để truy cập pod từ local
kubectl port-forward my-app 8080:80
# Giờ truy cập http://localhost:8080
 
# Kiểm tra resource usage
kubectl top pod my-app

Các vấn đề Pod thường gặp và cách fix:

StatusNguyên nhân phổ biếnLệnh debug
ImagePullBackOffSai tên image hoặc không có quyền truy cập registrykubectl describe pod → check Events
CrashLoopBackOffApp crash khi khởi độngkubectl logs --previous
PendingKhông đủ resources trên nodeskubectl describe pod → check Events
OOMKilledContainer vượt quá memory limitTăng resources.limits.memory

Phần 5: Deployments

Trong thực tế, bạn hầu như không bao giờ tạo Pods trực tiếp. Thay vào đó, bạn dùng Deployment — một resource cấp cao hơn quản lý Pods cho bạn.

Tại Sao Dùng Deployments?

FeaturePod đơn lẻDeployment
Self-healingPod chết → nằm chết luônPod chết → tạo cái mới
ScalingTự tay tạo podkubectl scale --replicas=5
Rolling updatesTắt cũ, bật mớiZero-downtime updates
RollbackKhông thểkubectl rollout undo
HistoryKhông cóLịch sử revision đầy đủ

Tạo Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: app
          image: nginx:1.25-alpine
          ports:
            - containerPort: 80
          resources:
            requests:
              memory: "64Mi"
              cpu: "100m"
            limits:
              memory: "128Mi"
              cpu: "250m"
          readinessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 5
            periodSeconds: 10
          livenessProbe:
            httpGet:
              path: /
              port: 80
            initialDelaySeconds: 15
            periodSeconds: 20

Phân tích các phần quan trọng:

  • replicas: 3 — Chạy 3 Pods giống nhau
  • selector.matchLabels — Cách Deployment tìm Pods của nó
  • template — Template cho Pod (mỗi replica trông thế nào)
  • readinessProbe — Kiểm tra Pod đã sẵn sàng nhận traffic chưa
  • livenessProbe — Kiểm tra Pod còn sống không (restart nếu không)

Apply và kiểm tra:

kubectl apply -f deployment.yaml
 
# Xem deployment
kubectl get deployments
kubectl get rs    # ReplicaSet được tạo bởi Deployment
kubectl get pods  # Pods được tạo bởi ReplicaSet

Output:

NAME     READY   UP-TO-DATE   AVAILABLE   AGE
my-app   3/3     3            3           30s
 
NAME                DESIRED   CURRENT   READY   AGE
my-app-7d9f8b6c5   3         3         3       30s
 
NAME                      READY   STATUS    RESTARTS   AGE
my-app-7d9f8b6c5-abc12   1/1     Running   0          30s
my-app-7d9f8b6c5-def34   1/1     Running   0          30s
my-app-7d9f8b6c5-ghi56   1/1     Running   0          30s

Chú ý thứ bậc: Deployment → ReplicaSet → Pods. Deployment quản lý ReplicaSets, và ReplicaSets quản lý Pods.

Scaling

# Scale up
kubectl scale deployment my-app --replicas=5
 
# Scale down
kubectl scale deployment my-app --replicas=2
 
# Kiểm tra kết quả
kubectl get pods

Rolling Updates

Đây là nơi Deployments tỏa sáng. Khi bạn update container image, Kubernetes dần thay thế Pods cũ bằng mới — zero downtime.

# Cập nhật image
kubectl set image deployment/my-app app=nginx:1.26-alpine
 
# Theo dõi rollout
kubectl rollout status deployment/my-app

Điều gì xảy ra trong rolling update:

Bạn có thể kiểm soát update strategy:

spec:
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # Tối đa pods thêm trong lúc update
      maxUnavailable: 0   # Zero downtime — không bao giờ xóa pod cho đến khi pod mới sẵn sàng

Rollbacks

Version mới có vấn đề? Roll back ngay lập tức:

# Xem rollout history
kubectl rollout history deployment/my-app
 
# Roll back về version trước
kubectl rollout undo deployment/my-app
 
# Roll back về revision cụ thể
kubectl rollout undo deployment/my-app --to-revision=2
 
# Kiểm tra rollout status
kubectl rollout status deployment/my-app

Health Checks (Probes)

Probes cho Kubernetes biết ứng dụng có healthy không:

ProbeMục đíchHành động khi fail
livenessProbeContainer còn sống không?Restart container
readinessProbeContainer sẵn sàng nhận traffic?Gỡ khỏi Service endpoints
startupProbeApp đã khởi động xong chưa?Kill và restart
containers:
  - name: app
    image: my-app:latest
    # Startup probe: cho app khởi động chậm thời gian boot
    startupProbe:
      httpGet:
        path: /healthz
        port: 8080
      failureThreshold: 30
      periodSeconds: 10
 
    # Liveness probe: restart nếu app bị treo
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 0
      periodSeconds: 15
      failureThreshold: 3
 
    # Readiness probe: chỉ gửi traffic khi sẵn sàng
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5

Phần 6: Services

Pods là tạm thời — chúng đến rồi đi, nhận IP address mới mỗi lần. Bạn không thể dựa vào Pod IPs. Services cung cấp network endpoint ổn định để truy cập nhóm Pods.

Services Hoạt Động Như Thế Nào

Service dùng label selectors để tìm Pods và điều hướng traffic đến chúng:

Các Loại Service

Kubernetes cung cấp bốn loại Service, mỗi loại với mức truy cập khác nhau:

1. ClusterIP (mặc định)

Chỉ truy cập được trong cluster. Hoàn hảo cho giao tiếp service-to-service nội bộ.

apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  type: ClusterIP
  selector:
    app: backend
  ports:
    - port: 80          # Service port
      targetPort: 8080   # Container port
      protocol: TCP

Các Pods khác có thể truy cập service này tại backend-service:80 hoặc backend-service.default.svc.cluster.local:80.

2. NodePort

Expose Service trên port cố định trên mọi node. Truy cập được từ bên ngoài cluster.

apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - port: 80
      targetPort: 3000
      nodePort: 30080    # Port bên ngoài (30000-32767)

Truy cập qua <node-ip>:30080. Với minikube:

minikube service frontend-service --url

3. LoadBalancer

Tạo external load balancer (trên cloud providers như AWS, GKE, AKS). Trong minikube, dùng minikube tunnel để mô phỏng.

apiVersion: v1
kind: Service
metadata:
  name: public-api
spec:
  type: LoadBalancer
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 8080
# Trong terminal riêng (cho minikube)
minikube tunnel
 
# Kiểm tra external IP
kubectl get service public-api

4. ExternalName

Map Service đến external DNS name. Không proxy — chỉ là DNS alias.

apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: db.example.com

Pods có thể truy cập external-db và nó resolve đến db.example.com.

Service Discovery

Kubernetes cung cấp built-in DNS cho Services. Mọi Service đều có DNS entry:

<service-name>.<namespace>.svc.cluster.local

Trong cùng namespace, bạn chỉ cần dùng tên service:

# Bên trong Pod cùng namespace
import requests
 
# Dạng ngắn (cùng namespace)
response = requests.get("http://backend-service:80/api/users")
 
# Full DNS name (cross-namespace)
response = requests.get("http://backend-service.production.svc.cluster.local:80/api/users")

Phần 7: ConfigMaps và Secrets

Hardcode configuration trong container images là practice tệ. ConfigMaps và Secrets cho phép bạn tách biệt configuration khỏi application code.

ConfigMaps

ConfigMaps lưu trữ configuration data không nhạy cảm dạng key-value pairs.

Tạo ConfigMaps:

# Từ literal values
kubectl create configmap app-config \
  --from-literal=APP_ENV=production \
  --from-literal=LOG_LEVEL=info \
  --from-literal=MAX_CONNECTIONS=100
 
# Từ file
kubectl create configmap nginx-config --from-file=nginx.conf
 
# Xem ConfigMap
kubectl get configmap app-config -o yaml

Declarative YAML:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  MAX_CONNECTIONS: "100"
 
  # Multi-line config file
  app.properties: |
    server.port=8080
    spring.profiles.active=production
    logging.level.root=INFO

Dùng ConfigMaps trong Pods:

apiVersion: v1
kind: Pod
metadata:
  name: app-with-config
spec:
  containers:
    - name: app
      image: my-app:latest
 
      # Cách 1: Dạng environment variables
      envFrom:
        - configMapRef:
            name: app-config
 
      # Cách 2: Keys cụ thể làm env vars
      env:
        - name: DATABASE_HOST
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_ENV
 
      # Cách 3: Mount dạng files
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
          readOnly: true
 
  volumes:
    - name: config-volume
      configMap:
        name: app-config

Secrets

Secrets giống ConfigMaps nhưng cho dữ liệu nhạy cảm — passwords, API keys, TLS certificates. Values được base64-encoded (không encrypted mặc định).

Tạo Secrets:

# Từ literal values
kubectl create secret generic db-credentials \
  --from-literal=DB_USER=admin \
  --from-literal=DB_PASSWORD=s3cur3p@ss
 
# Từ files (ví dụ TLS certificates)
kubectl create secret tls my-tls-secret \
  --cert=tls.crt \
  --key=tls.key
 
# Xem secret (values là base64 encoded)
kubectl get secret db-credentials -o yaml

Declarative YAML:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  # Values phải base64 encoded
  DB_USER: YWRtaW4=              # echo -n "admin" | base64
  DB_PASSWORD: czNjdXIzcEBzcw==  # echo -n "s3cur3p@ss" | base64
---
# Hoặc dùng stringData cho plain text (Kubernetes tự encode)
apiVersion: v1
kind: Secret
metadata:
  name: api-keys
type: Opaque
stringData:
  API_KEY: "my-super-secret-api-key"
  JWT_SECRET: "jwt-signing-secret-256-bit"

Dùng Secrets trong Pods:

apiVersion: v1
kind: Pod
metadata:
  name: app-with-secrets
spec:
  containers:
    - name: app
      image: my-app:latest
 
      # Dạng environment variables
      env:
        - name: DB_USER
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: DB_USER
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: DB_PASSWORD
 
      # Mount dạng files
      volumeMounts:
        - name: secret-volume
          mountPath: /etc/secrets
          readOnly: true
 
  volumes:
    - name: secret-volume
      secret:
        secretName: db-credentials

Quan trọng: Kubernetes Secrets được base64-encoded, không phải encrypted. Cho production, hãy cân nhắc:

  • Bật encryption at rest cho etcd
  • Dùng external secret managers (Vault, AWS Secrets Manager, Azure Key Vault)
  • Dùng External Secrets Operator để sync từ external stores

Phần 8: Namespaces

Namespaces là clusters ảo trong một cluster vật lý. Chúng cung cấp isolation, tổ chức và quản lý resources.

Namespaces Mặc Định

Mọi Kubernetes cluster bắt đầu với các namespaces này:

kubectl get namespaces
NAME              STATUS   AGE
default           Active   1d    # Nơi resources của bạn vào nếu không chỉ định
kube-system       Active   1d    # Kubernetes system components (API server, DNS, etc.)
kube-public       Active   1d    # Dữ liệu công khai (hiếm khi dùng)
kube-node-lease   Active   1d    # Node heartbeat leases

Tạo và Sử Dụng Namespaces

apiVersion: v1
kind: Namespace
metadata:
  name: staging
  labels:
    environment: staging
---
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    environment: production
# Tạo namespace
kubectl apply -f namespaces.yaml
 
# Tạo resources trong namespace cụ thể
kubectl apply -f deployment.yaml -n staging
 
# Liệt kê resources trong namespace
kubectl get pods -n staging
kubectl get all -n production
 
# Liệt kê resources trên tất cả namespaces
kubectl get pods --all-namespaces
kubectl get pods -A    # Dạng ngắn

Đặt Default Namespace

Chán gõ -n staging mỗi lần?

# Đặt default namespace cho context hiện tại
kubectl config set-context --current --namespace=staging
 
# Giờ tất cả commands nhắm vào 'staging' mặc định
kubectl get pods    # Hiển thị pods trong 'staging'

Resource Quotas

Kiểm soát namespace tiêu thụ bao nhiêu resources:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: staging-quota
  namespace: staging
spec:
  hard:
    pods: "20"
    requests.cpu: "4"
    requests.memory: "8Gi"
    limits.cpu: "8"
    limits.memory: "16Gi"
    services: "10"
    persistentvolumeclaims: "5"

Giao Tiếp Cross-Namespace

Services ở namespaces khác nhau có thể giao tiếp bằng full DNS name:

<service-name>.<namespace>.svc.cluster.local
# Pod trong namespace 'staging' truy cập service trong namespace 'production'
env:
  - name: API_URL
    value: "http://api-service.production.svc.cluster.local:80"

Phần 9: Labels và Selectors

Labels là cặp key-value gắn vào Kubernetes objects. Chúng là cách Kubernetes tổ chức và chọn resources.

Thêm Labels

apiVersion: v1
kind: Pod
metadata:
  name: api-server
  labels:
    app: api
    environment: production
    team: backend
    version: v2.1.0
# Thêm label vào resource có sẵn
kubectl label pod api-server tier=backend
 
# Xóa label
kubectl label pod api-server tier-
 
# Cập nhật label
kubectl label pod api-server version=v2.2.0 --overwrite

Dùng Selectors

Selectors lọc resources theo labels:

# Equality-based selectors
kubectl get pods -l app=api
kubectl get pods -l environment=production
kubectl get pods -l 'environment!=staging'
 
# Set-based selectors
kubectl get pods -l 'environment in (production, staging)'
kubectl get pods -l 'team notin (frontend)'
kubectl get pods -l 'app, environment'    # Có cả hai labels (bất kỳ value)
 
# Kết hợp selectors
kubectl get pods -l 'app=api,environment=production'

Labels vs Annotations

FeatureLabelsAnnotations
Mục đíchXác định và chọn resourcesGắn metadata không dùng để xác định
Dùng bởi selectorsCóKhông
Giới hạn size63 chars (value)256KB
Ví dụapp=web, env=proddescription, git-commit, config-hash
metadata:
  labels:
    app: api                     # Dùng cho selection
    environment: production      # Dùng cho selection
  annotations:
    description: "Main API server"           # Không dùng cho selection
    git-commit: "abc123def456"               # Build metadata
    kubectl.kubernetes.io/last-applied-configuration: "..."  # System annotation

Label Best Practices

Theo convention labeling được khuyến nghị:

metadata:
  labels:
    # Labels khuyến nghị (kubernetes.io convention)
    app.kubernetes.io/name: my-app
    app.kubernetes.io/instance: my-app-prod
    app.kubernetes.io/version: "2.1.0"
    app.kubernetes.io/component: backend
    app.kubernetes.io/part-of: ecommerce
    app.kubernetes.io/managed-by: helm
 
    # Custom labels cho tổ chức của bạn
    team: platform
    cost-center: engineering

Phần 10: Kết Hợp Tất Cả

Hãy deploy một ứng dụng hoàn chỉnh: Node.js API với PostgreSQL database.

Cấu Trúc Project

k8s/
├── namespace.yaml
├── configmap.yaml
├── secret.yaml
├── postgres-deployment.yaml
├── postgres-service.yaml
├── api-deployment.yaml
└── api-service.yaml

Bước 1: Namespace

# k8s/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: demo-app
  labels:
    app: demo

Bước 2: ConfigMap và Secret

# k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
  namespace: demo-app
data:
  NODE_ENV: "production"
  PORT: "3000"
  DB_HOST: "postgres-service"
  DB_PORT: "5432"
  DB_NAME: "myapp"
# k8s/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
  namespace: demo-app
type: Opaque
stringData:
  DB_USER: "appuser"
  DB_PASSWORD: "supersecretpassword"
  POSTGRES_PASSWORD: "supersecretpassword"

Bước 3: PostgreSQL

# k8s/postgres-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: demo-app
  labels:
    app: postgres
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:16-alpine
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_DB
              valueFrom:
                configMapKeyRef:
                  name: api-config
                  key: DB_NAME
            - name: POSTGRES_USER
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: DB_USER
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: POSTGRES_PASSWORD
          volumeMounts:
            - name: postgres-data
              mountPath: /var/lib/postgresql/data
          resources:
            requests:
              memory: "256Mi"
              cpu: "250m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          readinessProbe:
            exec:
              command: ["pg_isready", "-U", "appuser"]
            initialDelaySeconds: 5
            periodSeconds: 10
      volumes:
        - name: postgres-data
          emptyDir: {}    # Dùng PersistentVolume trong production!
# k8s/postgres-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: postgres-service
  namespace: demo-app
spec:
  type: ClusterIP
  selector:
    app: postgres
  ports:
    - port: 5432
      targetPort: 5432

Bước 4: Node.js API

# k8s/api-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: demo-app
  labels:
    app: api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: api
    spec:
      initContainers:
        - name: wait-for-db
          image: busybox
          command: ["sh", "-c", "until nc -z postgres-service 5432; do echo waiting for postgres; sleep 2; done"]
      containers:
        - name: api
          image: node:20-alpine
          command: ["node", "server.js"]
          ports:
            - containerPort: 3000
          envFrom:
            - configMapRef:
                name: api-config
          env:
            - name: DB_USER
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: DB_USER
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: DB_PASSWORD
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 15
# k8s/api-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: api-service
  namespace: demo-app
spec:
  type: NodePort
  selector:
    app: api
  ports:
    - port: 80
      targetPort: 3000
      nodePort: 30000

Bước 5: Deploy Tất Cả

# Apply tất cả manifests theo thứ tự
kubectl apply -f k8s/namespace.yaml
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secret.yaml
kubectl apply -f k8s/postgres-deployment.yaml
kubectl apply -f k8s/postgres-service.yaml
kubectl apply -f k8s/api-deployment.yaml
kubectl apply -f k8s/api-service.yaml
 
# Hoặc apply tất cả cùng lúc
kubectl apply -f k8s/
 
# Kiểm tra mọi thứ đang chạy
kubectl get all -n demo-app

Output mong đợi:

NAME                            READY   STATUS    RESTARTS   AGE
pod/api-7d9f8b6c5-abc12        1/1     Running   0          60s
pod/api-7d9f8b6c5-def34        1/1     Running   0          60s
pod/api-7d9f8b6c5-ghi56        1/1     Running   0          60s
pod/postgres-5c8f9d7b2-xyz89   1/1     Running   0          65s
 
NAME                       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)
service/api-service        NodePort    10.96.100.50    <none>        80:30000/TCP
service/postgres-service   ClusterIP   10.96.100.51    <none>        5432/TCP
 
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/api        3/3     3            3           60s
deployment.apps/postgres   1/1     1            1           65s
# Truy cập API (minikube)
minikube service api-service -n demo-app --url
 
# Test
curl http://<minikube-ip>:30000/health

Dọn Dẹp

# Xóa tất cả resources trong namespace
kubectl delete namespace demo-app
 
# Hoặc xóa resources cụ thể
kubectl delete -f k8s/

Bài Tập

Bài Tập 1: Deploy Web Application

Deploy nginx web server với:

  • 3 replicas
  • ConfigMap chứa custom index.html
  • NodePort Service trên port 30080
  • Liveness và readiness probes

Bài Tập 2: Thực Hành Rolling Update

  1. Deploy nginx:1.24 với 3 replicas
  2. Update lên nginx:1.25 và theo dõi rolling update
  3. Kiểm tra rollout history
  4. Roll back về 1.24
  5. Xác nhận rollback thành công

Bài Tập 3: Ứng Dụng Multi-Service

Deploy full stack trong namespace my-project:

  • Redis deployment (1 replica) với ClusterIP Service
  • Python/Node.js API (3 replicas) kết nối đến Redis
  • Dùng ConfigMaps cho Redis connection settings
  • Dùng Secrets cho authentication
  • Init container đợi Redis sẵn sàng

Tiếp Theo

Trong bài tiếp theo, chúng ta sẽ đào sâu vào Dockerfile Best Practices & Multi-Stage Builds — làm chủ các kỹ thuật nâng cao để build container images production-ready:

  • Tối ưu image size (alpine, slim, distroless, scratch)
  • Chiến lược layer caching
  • Multi-stage builds cho mọi ngôn ngữ
  • Security hardening (non-root, read-only filesystem)
  • BuildKit features (cache mounts, secrets, multi-platform)

Tóm Tắt và Điểm Chính

✅ Kubernetes orchestrate containers ở quy mô lớn với self-healing, scaling và rolling updates
✅ Control plane (API server, etcd, scheduler) quản lý cluster state; worker nodes chạy Pods
✅ Pods là đơn vị deploy nhỏ nhất — dùng Deployments để quản lý, đừng bao giờ tạo Pods trực tiếp
✅ Deployments cung cấp rolling updates, rollbacks và scaling với zero downtime
✅ Services cho Pods network endpoint ổn định — dùng ClusterIP cho internal, NodePort/LoadBalancer cho external
✅ ConfigMaps lưu configuration; Secrets lưu dữ liệu nhạy cảm (base64-encoded, không encrypted)
✅ Namespaces isolate resources và cho phép resource quotas theo team hoặc environment
✅ Labels và selectors là cách Kubernetes tổ chức và kết nối resources
✅ Luôn set resource requests/limits và health checks (liveness, readiness, startup probes)
✅ Dùng kubectl describe và kubectl logs làm công cụ debug chính


Series: Lộ Trình Học Docker & Kubernetes
Trước đó: Giai đoạn 2: Docker Compose & Ứng Dụng Multi-Container
Tiếp theo: Deep Dive: Dockerfile Best Practices & Multi-Stage Builds


Có câu hỏi về Kubernetes? Hãy liên hệ hoặc để lại comment!

📬 Đăng ký nhận bản tin

Nhận những bài viết mới nhất qua email mỗi tuần. Không spam, hủy đăng ký bất cứ lúc nào.

Chúng tôi tôn trọng quyền riêng tư của bạn. Hủy đăng ký bất cứ lúc nào.

💬 Comments

Đăng nhập để bình luận

Chúng tôi sẽ không đăng bất cứ điều gì mà không có sự cho phép của bạn.