🎯 MỤC TIÊU BÀI HỌC
Sau khi hoàn thành bài học này, bạn sẽ:
- ✅ Tạo kubeadm configuration file cho HA topology
- ✅ Khởi tạo control plane đầu tiên thành công
- ✅ Hiểu certificate structure và certificate rotation
- ✅ Copy kubeconfig và truy cập cluster bằng kubectl
- ✅ Hiểu stacked etcd topology vs external etcd
PHẦN 1: KUBERNETES HA TOPOLOGIES
1.1. Stacked etcd vs External etcd
Option A: Stacked etcd (Recommended cho hầu hết cases)
graph LR
subgraph M1["🖥️ master1"]
A1["API Server<br/>Scheduler<br/>Controller"]
E1["etcd"]
A1 --- E1
end
subgraph M2["🖥️ master2"]
A2["API Server<br/>Scheduler<br/>Controller"]
E2["etcd"]
A2 --- E2
end
subgraph M3["🖥️ master3"]
A3["API Server<br/>Scheduler<br/>Controller"]
E3["etcd"]
A3 --- E3
end
E1 <-->|"Raft"| E2
E2 <-->|"Raft"| E3
style M1 fill:#0f172a,stroke:#3b82f6,color:#e2e8f0
style M2 fill:#0f172a,stroke:#3b82f6,color:#e2e8f0
style M3 fill:#0f172a,stroke:#3b82f6,color:#e2e8f0
style E1 fill:#15803d,stroke:#22c55e,color:#e2e8f0
style E2 fill:#15803d,stroke:#22c55e,color:#e2e8f0
style E3 fill:#15803d,stroke:#22c55e,color:#e2e8f0
✅ Ít servers · ✅ Đơn giản · ❌ etcd + API coupled
Option B: External etcd
graph TD
subgraph MASTERS["Control Plane — 3 masters"]
MA["API Server<br/>Scheduler<br/>Controller"]
MB["API Server<br/>Scheduler<br/>Controller"]
MC["API Server<br/>Scheduler<br/>Controller"]
end
subgraph ETCDS["etcd Cluster — 3 dedicated nodes"]
EA["etcd"]
EB["etcd"]
EC["etcd"]
EA <-->|"Raft"| EB
EB <-->|"Raft"| EC
end
MA --> EA
MB --> EB
MC --> EC
style MASTERS fill:#0f172a,stroke:#3b82f6,color:#e2e8f0
style ETCDS fill:#1e293b,stroke:#15803d,color:#e2e8f0
style EA fill:#15803d,stroke:#22c55e,color:#e2e8f0
style EB fill:#15803d,stroke:#22c55e,color:#e2e8f0
style EC fill:#15803d,stroke:#22c55e,color:#e2e8f0
✅ Isolation · ✅ Independent scaling · ❌ Cần 6 servers
👉 Chọn Stacked etcd cho khóa học này — đơn giản, đủ tốt cho hầu hết production workloads. External etcd chỉ cần cho clusters rất lớn (100+ nodes).
PHẦN 2: TẠO KUBEADM CONFIGURATION
2.1. kubeadm-config.yaml chi tiết
# Trên master1, tạo config file: cat > /root/kubeadm-config.yaml << 'EOF' --- apiVersion: kubeadm.k8s.io/v1beta4 kind: InitConfiguration localAPIEndpoint: advertiseAddress: "10.10.20.11" # IP của master1 trên cluster network bindPort: 6443 nodeRegistration: name: master1 criSocket: unix:///run/containerd/containerd.sock taints: - key: "node-role.kubernetes.io/control-plane" effect: "NoSchedule"
apiVersion: kubeadm.k8s.io/v1beta4 kind: ClusterConfiguration kubernetesVersion: "v1.31.0" # Exact version clusterName: "production" controlPlaneEndpoint: "10.10.20.100:6443" # ← VIP (HAProxy)! certificatesDir: /etc/kubernetes/pki
networking: podSubnet: "10.244.0.0/16" # Pod CIDR (cho Cilium) serviceSubnet: "10.96.0.0/12" # Service CIDR dnsDomain: "cluster.local"
etcd: local: dataDir: /var/lib/etcd extraArgs: listen-metrics-urls: "http://0.0.0.0:2381" # Prometheus metrics
apiServer: extraArgs: authorization-mode: "Node,RBAC" enable-admission-plugins: "NodeRestriction,PodSecurity" audit-log-path: "/var/log/kubernetes/audit.log" audit-log-maxage: "30" audit-log-maxbackup: "10" audit-log-maxsize: "100" event-ttl: "4h" # Encryption at rest (thêm sau khi tạo encryption config) # encryption-provider-config: "/etc/kubernetes/encryption-config.yaml" extraVolumes: - name: audit-log hostPath: /var/log/kubernetes mountPath: /var/log/kubernetes pathType: DirectoryOrCreate
controllerManager: extraArgs: bind-address: "0.0.0.0" # Cho Prometheus scrape terminated-pod-gc-threshold: "100"
scheduler: extraArgs: bind-address: "0.0.0.0" # Cho Prometheus scrape
apiVersion: kubelet.config.k8s.io/v1beta1 kind: KubeletConfiguration cgroupDriver: systemd # Match containerd SystemdCgroup containerRuntimeEndpoint: unix:///run/containerd/containerd.sock evictionHard: memory.available: "500Mi" nodefs.available: "10%" imagefs.available: "15%" systemReserved: cpu: "500m" memory: "1Gi" kubeReserved: cpu: "500m" memory: "1Gi" maxPods: 110 # Default 110, tăng nếu cần serializeImagePulls: false # Parallel image pulls
apiVersion: kubeproxy.config.k8s.io/v1alpha1 kind: KubeProxyConfiguration mode: "ipvs" # IPVS mode (better than iptables) ipvs: strictARP: true # Required cho MetalLB scheduler: "rr" # Round-robin EOF
2.2. Giải thích các tham số quan trọng
| Tham số | Giá trị | Ý nghĩa |
|---|---|---|
| controlPlaneEndpoint | 10.10.20.100:6443 | VIP của HAProxy — TẤT CẢ components kết nối qua đây |
| podSubnet | 10.244.0.0/16 | CIDR cho pod IPs (65,534 pods max) |
| serviceSubnet | 10.96.0.0/12 | CIDR cho ClusterIP services (1,048,574 IPs) |
| mode: ipvs | kube-proxy | IPVS load balancing (better than iptables khi nhiều services) |
| strictARP: true | kube-proxy | Required cho MetalLB L2 mode |
| cgroupDriver: systemd | kubelet | Match containerd SystemdCgroup = true |
⚠️ CRITICAL: controlPlaneEndpoint PHẢI trỏ tới VIP (HAProxy), KHÔNG phải IP của master1. Đây là yếu tố then chốt cho HA.
PHẦN 3: KHỞI TẠO CLUSTER
3.1. Tạo audit log directory
# Trên master1:
mkdir -p /var/log/kubernetes
3.2. Chạy kubeadm init
# Trên master1: kubeadm init --config /root/kubeadm-config.yaml --upload-certsOutput (giữ lại CẨN THẬN):
────────────────────────────────────────────────────────────
Your Kubernetes control-plane has initialized successfully!
To start using your cluster, you need to run the following as a regular user:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
You can now join any number of control-plane node by running:
kubeadm join 10.10.20.100:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:... \
--control-plane --certificate-key <CERTIFICATE_KEY>
Then you can join any number of worker nodes by running:
kubeadm join 10.10.20.100:6443 --token abcdef.0123456789abcdef \
--discovery-token-ca-cert-hash sha256:...
────────────────────────────────────────────────────────────
⚠️ GHI LẠI ngay lập tức:
--token: Dùng cho join nodes (hết hạn sau 24h)--discovery-token-ca-cert-hash: SHA256 hash của CA cert--certificate-key: Dùng cho join control-plane nodes (hết hạn sau 2h)
3.3. Setup kubeconfig
# Trên master1: mkdir -p $HOME/.kube cp -i /etc/kubernetes/admin.conf $HOME/.kube/config chown $(id -u):$(id -g) $HOME/.kube/configVerify cluster
kubectl get nodes
Output:
NAME STATUS ROLES AGE VERSION
master1 NotReady control-plane 30s v1.31.0
← NotReady vì chưa cài CNI (Cilium sẽ fix ở Bài 8)
Kiểm tra tất cả system pods
kubectl get pods -n kube-system
Output:
NAME READY STATUS RESTARTS AGE
coredns-xxx-xxx 0/1 Pending 0 30s ← Pending vì chưa có CNI
coredns-xxx-xxx 0/1 Pending 0 30s
etcd-master1 1/1 Running 0 35s
kube-apiserver-master1 1/1 Running 0 35s
kube-controller-manager-master1 1/1 Running 0 35s
kube-scheduler-master1 1/1 Running 0 35s
Kiểm tra etcd health
kubectl -n kube-system exec etcd-master1 -- etcdctl
--endpoints=https://127.0.0.1:2379
--cacert=/etc/kubernetes/pki/etcd/ca.crt
--cert=/etc/kubernetes/pki/etcd/server.crt
--key=/etc/kubernetes/pki/etcd/server.key
endpoint healthOutput: https://127.0.0.1:2379 is healthy: successfully committed proposal
PHẦN 4: CERTIFICATE STRUCTURE
4.1. Kiến trúc Certificates
/etc/kubernetes/pki/
├── ca.crt ─── Kubernetes CA (root cert)
├── ca.key ─── CA private key (PROTECT!)
├── apiserver.crt ─── API Server cert
├── apiserver.key
├── apiserver-kubelet-client.crt
├── apiserver-kubelet-client.key
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
├── front-proxy-ca.crt ─── Front Proxy CA
├── front-proxy-ca.key
├── front-proxy-client.crt
├── front-proxy-client.key
├── sa.pub ─── Service Account public key
├── sa.key ─── Service Account private key
└── etcd/
├── ca.crt ─── etcd CA
├── ca.key
├── server.crt ─── etcd server cert
├── server.key
├── peer.crt ─── etcd peer cert
├── peer.key
├── healthcheck-client.crt
└── healthcheck-client.key
4.2. Kiểm tra Certificate Expiry
# Xem tất cả cert expiry: kubeadm certs check-expiration # Output: # CERTIFICATE EXPIRES RESIDUAL TIME # admin.conf Apr 02, 2027 07:00 UTC 364d # apiserver Apr 02, 2027 07:00 UTC 364d # apiserver-etcd-client Apr 02, 2027 07:00 UTC 364d # ... # ca Mar 30, 2036 07:00 UTC 9y ← CA valid 10 years # etcd-ca Mar 30, 2036 07:00 UTC 9y⚠️ Certificates mặc định valid 1 NĂM (trừ CA: 10 năm)
Phải renew trước khi expire!
Renew tất cả certs:
kubeadm certs renew all
(Sẽ học chi tiết ở Bài 46: Nâng cấp Cluster)
PHẦN 5: VERIFY HA READINESS
5.1. Kiểm tra API Server qua VIP
# Verify API server accessible qua VIP (HAProxy): curl -sk https://10.10.20.100:6443/healthz # Output: okKiểm tra HAProxy backend status:
curl -s http://lb1:9000/stats\;csv | grep k8s-api
master1 → UP, master2 → DOWN, master3 → DOWN (chưa join)
API server accessible trực tiếp:
curl -sk https://10.10.20.11:6443/healthz
Output: ok
5.2. Lưu join commands (quan trọng!)
# Lưu join commands vào file: cat > /root/join-commands.sh << 'CMDS' # === Join Control Plane nodes (master2, master3) === # Certificate key expires in 2 HOURS! kubeadm join 10.10.20.100:6443 \ --token <TOKEN> \ --discovery-token-ca-cert-hash sha256:<HASH> \ --control-plane \ --certificate-key <CERT_KEY>=== Join Worker nodes ===
kubeadm join 10.10.20.100:6443
--token <TOKEN>
--discovery-token-ca-cert-hash sha256:<HASH> CMDSNếu token hết hạn, tạo mới:
kubeadm token create --print-join-command
Output: kubeadm join 10.10.20.100:6443 --token NEW_TOKEN --discovery-token-ca-cert-hash sha256:HASH
Nếu certificate-key hết hạn, upload certs lại:
kubeadm init phase upload-certs --upload-certs
Output: Using certificate key: NEW_CERTIFICATE_KEY
💡 KEY TAKEAWAYS
- controlPlaneEndpoint phải trỏ tới VIP (HAProxy), không phải IP cụ thể
- Stacked etcd đủ tốt cho hầu hết deployments, etcd chạy cùng control plane
- --upload-certs flag tự động distribute certificates cho join control-plane
- Token hết hạn 24h, certificate-key hết hạn 2h — phải lưu và join nodes sớm
- ipvs mode + strictARP cho kube-proxy là requirement cho MetalLB
- Node status NotReady là bình thường trước khi cài CNI (Cilium)
🎯 BÀI TẬP
Bài tập 1: Init control plane
- Tạo kubeadm-config.yaml trên master1
- Chạy kubeadm init --config --upload-certs
- Setup kubeconfig, verify kubectl get nodes
- GHI LẠI join commands
Bài tập 2: Verify certificates
- List tất cả certificates trong /etc/kubernetes/pki/
- Chạy kubeadm certs check-expiration
- Verify API server qua VIP: curl -sk https://VIP:6443/healthz
📚 BÀI TIẾP THEO
Trong Bài 7: Join thêm Control Plane và Worker Nodes, chúng ta sẽ join master2, master3 vào control plane HA và join worker nodes vào cluster.