← Kubernetes← Kubernetes
KubernetesKubernetes19 Th7, 2026Jul 19, 202617 phút đọc15 min read

Cluster AdministrationCluster Administration

Thuộc bộ tài liệu kiến thức theo Kubernetes Roadmap.

Tổng quan

Xây dựng ứng dụng trên Kubernetes và vận hành cụm (cluster) nằm bên dưới nó là hai công việc rất khác nhau. Cluster administration là kỷ luật của day-2: bootstrap một cluster, giữ control plane sẵn sàng cao (high availability), bảo vệ kho trạng thái (etcd), đi qua nhịp nâng cấp liên tục mà không downtime, drain node để bảo trì, xoay (rotate) certificate trước khi hết hạn, và lập kế hoạch capacity để scheduler không bao giờ hết chỗ. Đây là những việc quyết định một platform là đáng tin cậy hay là cỗ máy gọi pager lúc 3 giờ sáng.

Mâu thuẫn trung tâm của cluster administration là Kubernetes là một hệ thống phân tán lưu toàn bộ sự thật của nó trong etcd và phục vụ qua một API server duy nhất. Mọi thứ operator làm — HA, backup, upgrade, cert rotation — cuối cùng đều bảo vệ hai thứ đó. Nếu API server không truy cập được, bạn “bay mù”; nếu mất etcd mà không có backup, cluster coi như biến mất. Phần lớn best practice về quản trị, xét đến cùng, đều là cách để không bao giờ chỉ cách một lần lỗi là rơi vào một trong hai kết cục đó.

Một quyết định then chốt định hình bạn thực sự sở hữu bao nhiêu phần: managed vs self-managed. Với offering managed (EKS, GKE, AKS) nhà cung cấp chạy, vá và backup control plane cùng etcd — bạn sở hữu worker node, việc upgrade các node đó, và workload. Với cluster self-managed (kubeadm, kOps, Cluster API, hay kubelet trần) bạn sở hữu mọi thứ, bao gồm etcd backup và control-plane HA. Trang này bao quát toàn bộ bề mặt self-managed vì hiểu nó khiến bạn thành operator giỏi hơn ngay cả trên platform managed — bạn biết nhà cung cấp đang làm gì thay bạn và ranh giới nằm ở đâu.

Kiến thức nền tảng

Control plane, và vì sao cần HA

Control plane là tập hợp các thành phần ra quyết định phạm vi toàn cluster: kube-apiserver (cửa chính duy nhất và là thứ duy nhất nói chuyện với etcd), etcd (kho key-value dựa trên Raft giữ toàn bộ trạng thái cluster), kube-scheduler (đặt Pod), và kube-controller-manager (các vòng reconciliation dựng sẵn). Trên cluster chỉ có một control-plane node, mất node đó nghĩa là không còn scheduling mới, không scale, không self-healing, và không kubectl — Pod đang chạy vẫn chạy, nhưng cluster bị đóng băng và mù.

High availability loại bỏ điểm lỗi đơn đó bằng cách chạy control plane theo bộ ba (hoặc năm) sau một load balancer:

Control plane có tính sẵn sàng cao
  1. Load Balancer (VIP)
    :6443 → apiserver khỏe mạnh
  2. Control nodes
    control-1apiserver, scheduler*, ctrl-mgr*, etcd
    control-2apiserver, scheduler*, ctrl-mgr*, etcd
    control-3apiserver, scheduler*, ctrl-mgr*, etcd
  3. *
    scheduler và controller-manager chạy trên mọi node nhưng chỉ một instance là leader tại một thời điểm (leader election); apiserver active-active.

etcd: viên ngọc quý

etcd giữ toàn bộ trạng thái mong muốn và quan sát được của cluster. Đó là một kho nhất quán mạnh (linearizable) dùng thuật toán đồng thuận Raft. Hai sự thật vận hành theo sau:

  1. etcd là nguồn sự thật duy nhất. Mất nó mà không có backup thì cluster không khôi phục được — bạn dựng lại từ đầu và apply lại manifest (chính lý do GitOps, lưu manifest trong Git, là một chiến lược resilience).
  2. etcd nhạy với latency và I/O. Nó fsync mọi write; disk chậm gây cảnh báo apply request took too long, gây leader election, và gây bất ổn control-plane dây chuyền. Cho nó SSD/NVMe nhanh, để trên disk riêng, và giữ write latency (etcd_disk_wal_fsync_duration_seconds) thấp.

Scheduler và capacity

Scheduler đặt Pod lên node dựa trên requests tài nguyên (không phải limit hay usage thực). Capacity khả dụng (allocatable) của một node là tổng capacity trừ đi phần reserve cho kubelet, OS, và ngưỡng eviction. Khi tổng tài nguyên được request không còn node nào đủ chỗ, Pod chuyển Pending — đó là tín hiệu capacity, không phải bug. Capacity planning là nghệ thuật giữ đủ headroom để lỗi node và rollout không gây cơn bão Pending, mà không phải trả tiền cho máy chạy không.

Certificate ở khắp nơi

Kubernetes là một mạng lưới TLS xác thực lẫn nhau: apiserver↔etcd, apiserver↔kubelet, controller-manager/scheduler↔apiserver, và kubelet client cert. kubeadm sinh một cluster CA và cấp các cert này với tuổi thọ mặc định 1 năm. Chúng tự gia hạn khi kubeadm upgrade, nhưng một cluster để yên không upgrade cả năm sẽ có cert control plane hết hạn — một sự cố kinh điển và tránh được. Cert serving/client của chính kubelet có thể tự rotate qua feature RotateKubeletServerCertificate/RotateKubeletClientCertificate và API CSR.

Khái niệm chính

Bootstrap một cluster

Công cụHợp nhất choGhi chú
kubeadmCluster self-managed productionBootstrapper chính thức, có quan điểm; dựng control plane + join token; bạn tự lo CNI, load balancer, automation
kind (K8s in Docker)CI và test cục bộCả cluster trong container Docker; nhanh, dùng-rồi-bỏ, multi-node; công cụ chính Kubernetes dùng cho CI
minikubePhát triển cục bộCluster cục bộ single-node (hoặc nhỏ) với add-on (ingress, dashboard, registry); driver VM hoặc Docker
k3s / k3dEdge, IoT, devNhẹ (một binary, SQLite hoặc etcd), do Rancher duy trì; k3d chạy k3s trong Docker
kOpsSelf-managed trên AWS/GCPQuản lý toàn bộ vòng đời cluster kể cả hạ tầng cloud
Cluster API (CAPI)Fleet, hạ tầng khai báoVòng đời cluster kiểu Kubernetes — cluster là CRD (xem file 12)

Managed control plane (EKS/GKE/AKS) bỏ qua hoàn toàn việc bootstrap control plane — nhà cung cấp chạy và backup apiserver + etcd. Bạn vẫn chọn node group, CNI (đôi khi), và thời điểm upgrade. Đánh đổi: gánh nặng vận hành ít hơn và control plane được vá, làm cứng vs kiểm soát ít hơn (flag apiserver giới hạn, cửa sổ hỗ trợ phiên bản do nhà cung cấp đặt, và chi phí theo mỗi cluster).

Bootstrap tối thiểu control-plane node đầu tiên cho một cluster HA bằng kubeadm:

# Trên control-plane node đầu tiên — trỏ vào VIP của load balancer để các
# control-plane node khác join sau cùng một endpoint.
kubeadm init \
  --control-plane-endpoint "k8s-api.internal:6443" \
  --upload-certs \
  --pod-network-cidr "10.244.0.0/16" \
  --kubernetes-version v1.30.2

# kubeadm in ra hai lệnh join: một cho control-plane node (kèm
# --control-plane --certificate-key) và một cho worker.
# Cài CNI (vd Cilium/Calico) trước khi node báo Ready:
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml

Backup và restore etcd

Đây là kỹ năng quản trị quan trọng nhất cho cluster self-managed. Chụp một snapshot nhất quán bằng etcdctl snapshot save, lưu ngoài cluster, và — mấu chốt — diễn tập restore.

# Backup etcd (chạy trên control-plane node, hoặc bất cứ đâu có cert).
# Endpoint/cert khớp với etcd do kubeadm quản lý.
ETCDCTL_API=3 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 \
  snapshot save /var/backups/etcd-$(date +%Y%m%d-%H%M%S).db

# Kiểm tra snapshot còn nguyên vẹn (check hash, in revision/size).
ETCDCTL_API=3 etcdctl snapshot status /var/backups/etcd-20260718.db -w table

Restore (disaster recovery) — restore snapshot vào một data directory mới, rồi trỏ etcd vào đó:

# 1. Dừng kubelet và etcd (kubeadm chạy etcd như static Pod;
#    chuyển manifest của nó ra khỏi /etc/kubernetes/manifests để dừng).
# 2. Restore vào data dir mới:
ETCDCTL_API=3 etcdctl snapshot restore /var/backups/etcd-20260718.db \
  --data-dir=/var/lib/etcd-restored

# 3. Trỏ static Pod manifest của etcd vào /var/lib/etcd-restored (hostPath),
#    restart kubelet, và etcd trở lại với trạng thái của snapshot.

Với etcd nhiều thành viên, restore trên một thành viên với --initial-cluster / --initial-advertise-peer-urls đúng, rồi để các peer re-sync. Công cụ cấp cao hơn — Velero (với etcd snapshot hoặc backup mức resource) hoặc snapshot của nhà cung cấp — tự động hóa việc này. Backup duy nhất có giá trị là backup bạn đã từng restore; lên lịch diễn tập restore định kỳ vào một cluster nháp.

Cluster upgrade

Kubernetes ra một minor release khoảng ba lần mỗi năm và hỗ trợ ba minor mới nhất (~14 tháng patch mỗi bản). Tụt lại nghĩa là phiên bản không được hỗ trợ và upgrade nhiều bước đau đớn — nên hãy upgrade liên tục.

Chính sách version skew (các luật khiến rolling upgrade an toàn):

Thành phầnSkew cho phép so với kube-apiserver
kube-apiserver khác (HA)trong 1 minor
kubelettối đa 3 minor cũ hơn (không bao giờ mới hơn)
kube-controller-manager, scheduler, cloud-ctrl-mgr1 minor cũ hơn (khuyến nghị cùng minor)
kubectl1 minor cũ hơn hoặc mới hơn

Các luật vàng: upgrade control plane trước node, không bao giờ nhảy cóc minor (1.29 → 1.30 → 1.31, không phải 1.29 → 1.31), và không bao giờ chạy kubelet mới hơn apiserver.

Luồng kubeadm upgrade (mỗi lần một minor):

# ---- Control-plane node, từng cái một ----
# 1. Nâng binary kubeadm lên phiên bản đích (qua package manager).
apt-get update && apt-get install -y kubeadm=1.30.2-*

# 2. Plan: kubeadm hiển thị phiên bản component sẽ chuyển tới và
#    các bước thủ công / API bị bỏ cần xử lý.
kubeadm upgrade plan

# 3. Apply trên control-plane node ĐẦU TIÊN (nâng static Pod apiserver,
#    controller-manager, scheduler, gia hạn cert control-plane, bump etcd):
kubeadm upgrade apply v1.30.2

# 3b. Trên các control-plane node KHÁC, dùng biến thể node:
#     kubeadm upgrade node

# 4. Giờ nâng kubelet + kubectl trên node này, rồi restart kubelet:
apt-get install -y kubelet=1.30.2-* kubectl=1.30.2-*
systemctl daemon-reload && systemctl restart kubelet

# ---- Worker node, từng cái một (xem draining bên dưới) ----
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
#   ssh vào node: cài kubeadm 1.30.2, chạy `kubeadm upgrade node`,
#   cài kubelet 1.30.2, restart kubelet
kubectl uncordon <node>

Trước mọi upgrade, kiểm tra API đã bị xóa bằng cảnh báo deprecation của kubectl hoặc công cụ như pluto/kubent, và test toàn bộ luồng ở cluster staging trước.

Quản lý node: cordon, drain, uncordon

Bảo trì (vá kernel, upgrade kubelet, làm phần cứng) đòi hỏi sơ tán workload của node an toàn:

# Đánh dấu node không cho schedule — không Pod MỚI nào vào đây, Pod cũ giữ nguyên.
kubectl cordon node-7

# Evict Pod nhẹ nhàng, tôn trọng PodDisruptionBudget và grace period.
#   --ignore-daemonsets: Pod DaemonSet không evict được (thuộc mọi node)
#   --delete-emptydir-data: chấp nhận dữ liệu emptyDir sẽ mất
#   --grace-period / --timeout: giới hạn thời gian chờ
kubectl drain node-7 --ignore-daemonsets --delete-emptydir-data --timeout=300s

# ... làm bảo trì ...

# Trả node về pool của scheduler.
kubectl uncordon node-7

Quản lý và rotate certificate

# Xem mọi certificate control-plane hết hạn khi nào (chạy trên control-plane node).
kubeadm certs check-expiration

# Gia hạn thủ công tất cả cert control-plane (cũng tự xảy ra khi upgrade).
kubeadm certs renew all
systemctl restart kubelet   # và restart các component control-plane static-Pod

# Client cert của kubelet tự rotate qua API CSR; xem CSR đang chờ:
kubectl get csr
kubectl certificate approve <csr-name>   # nếu chưa được auto-approve

Chỉ rotate cluster CA khi bị lộ — việc này gây gián đoạn (mọi cert phải cấp lại và mọi kubeconfig phải làm mới). Với workload cần cert, dùng cert-manager thay vì tự tay dựng PKI.

Flag API server và feature gate

apiserver được cấu hình bằng flag (trong static-Pod manifest với kubeadm, tại /etc/kubernetes/manifests/kube-apiserver.yaml). Các núm thường gặp: --audit-policy-file (audit logging), --encryption-provider-config (mã hóa Secret at rest bằng KMS), --enable-admission-plugins, và --feature-gates. Feature gate bật/tắt tính năng theo độ chín — Alpha (mặc định tắt, có thể vỡ/đổi), Beta (ngày càng bật mặc định), GA (khóa bật). Platform managed chỉ mở một tập nhỏ, chọn lọc; cluster self-managed có toàn bộ bề mặt nhưng tự chịu rủi ro.

Namespace và multi-tenancy cơ bản

Namespace phân vùng cluster cho isolation, quota, và scoping RBAC. Soft multi-tenancy (các team tin cậy dùng chung một cluster) được hỗ trợ tốt; hard multi-tenancy (tenant không tin cậy) cần isolation mạnh hơn và thường tốt hơn nếu tách cluster riêng hoặc dùng công cụ virtual-cluster (vCluster).

apiVersion: v1
kind: ResourceQuota
metadata: { name: team-a-quota, namespace: team-a }
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "100"
---
apiVersion: v1
kind: LimitRange          # request/limit mặc định cho Pod chưa đặt
metadata: { name: defaults, namespace: team-a }
spec:
  limits:
    - type: Container
      default: { cpu: 500m, memory: 512Mi }
      defaultRequest: { cpu: 100m, memory: 128Mi }

Kết hợp ResourceQuota theo namespace (giới hạn tổng tiêu thụ), LimitRange (mặc định hợp lý cho mỗi Pod), RBAC (ai làm được gì, ở đâu), NetworkPolicy (default-deny lưu lượng liên namespace), và Pod Security Admission (baseline/restricted) để có một thiết lập soft-multi-tenant khả dụng.

Best Practices

  1. Chạy control plane HA với 3 (hoặc 5) thành viên etcd — số lẻ cho quorum, sau một API endpoint được load-balance. Một control-plane node là ổn để học, không bao giờ cho production; một máy lỗi không được phép đóng băng cluster.
  2. Backup etcd theo lịch và diễn tập restore — một snapshot chưa từng restore là phỏng đoán, không phải backup. Lưu snapshot ngoài cluster và chạy diễn tập restore định kỳ vào cluster nháp để disaster recovery thành phản xạ, không phải ứng biến.
  3. Cho etcd storage nhanh, riêng, latency thấp — etcd fsync mọi write; disk chậm gây leader election và bất ổn control-plane. Giám sát etcd_disk_wal_fsync_duration_seconds và mức bão hòa disk, và giữ etcd khỏi volume dùng chung/ồn ào.
  4. Upgrade liên tục, mỗi lần một minor — Kubernetes chỉ hỗ trợ ba minor mới nhất và không nhảy cóc phiên bản. Luôn cập nhật để tránh upgrade nhiều bước đau đớn và cluster không được hỗ trợ; test mỗi upgrade ở staging trước.
  5. Tôn trọng chính sách version skew — upgrade control plane trước node, giữ kubelet không tụt quá số minor cho phép so với apiserver, và không bao giờ chạy kubelet mới hơn apiserver.
  6. Quét API đã bị xóa trước mỗi upgrade — dùng pluto/kubent và để ý cảnh báo deprecation của kubectl; một manifest dùng phiên bản API đã xóa sẽ âm thầm apply thất bại sau upgrade.
  7. Luôn drain trước khi bảo trì node, và uncordon sau đó — không bao giờ rút node ra dưới chân Pod đang chạy. Draining evict nhẹ nhàng và tôn trọng grace period cùng PodDisruptionBudget.
  8. Cho mọi workload quan trọng một PodDisruptionBudget và ≥2 replica — không có PDB, một lần drain có thể đưa service về 0; có PDB nhưng một replica, drain sẽ treo. Cả hai đều cần cho gián đoạn tự nguyện an toàn.
  9. Giám sát hết hạn certificate và để upgrade gia hạn cert — chạy kubeadm certs check-expiration (hoặc cảnh báo trên nó); một cluster để yên cả năm sẽ hết hạn cert control-plane và sập. Bật auto-rotation cert của kubelet.
  10. Mã hóa Secret at rest bằng KMS provider — etcd lưu Secret dạng base64, tức thực chất là plaintext. Cấu hình --encryption-provider-config với KMS bên ngoài và giới hạn ai đọc được Secret qua RBAC.
  11. Lập kế hoạch capacity có headroom cho lỗi và rollout — sizing sao cho mất một node (hoặc một zone) không gây cơn bão Pending, và chừa chỗ cho surge khi rolling update. Right-size request từ dữ liệu usage thực thay vì đoán.
  12. Reserve tài nguyên cho kubelet và OS — đặt --kube-reserved/--system-reserved và ngưỡng eviction để daemon mức node không bao giờ bị Pod bỏ đói, thứ sẽ gây bất ổn node và eviction dây chuyền.
  13. Cô lập tenant bằng namespace + ResourceQuota + LimitRange + RBAC + NetworkPolicy — soft multi-tenancy cần cả năm; với tenant không tin cậy hãy chọn cluster riêng hoặc virtual cluster thay vì cố làm cho một cluster kín tuyệt đối.
  14. Giữ mọi manifest trong Git (GitOps) — disaster recovery nhanh nhất sau khi mất etcd là apply lại nguồn sự thật được Git theo dõi. Cấu hình cluster khai báo, có version-control cũng khiến audit và rollback thành chuyện dễ.
  15. Bật audit logging và tập trung log/metric control-plane — bạn không thể quản trị thứ bạn không thấy. Đẩy audit log của apiserver và metric control-plane ra ngoài cluster để một sự cố control-plane không kéo theo cả bằng chứng điều tra.
  16. Ưu tiên managed control plane trừ khi có lý do không — EKS/GKE/AKS loại bỏ phần vận hành khó và rủi ro nhất (HA control-plane, backup etcd, patching). Chỉ self-manage khi bạn cần khả năng mà platform managed không có, và biết mình đang gánh việc chăm sóc etcd.

Tài liệu tham khảo

Part of the Kubernetes Roadmap knowledge base.

Overview

Building an application on Kubernetes and operating the cluster underneath it are two very different jobs. Cluster administration is the day-2 discipline: bootstrapping a cluster, keeping the control plane highly available, protecting the state store (etcd), moving through the relentless upgrade cadence without downtime, draining nodes for maintenance, rotating certificates before they expire, and planning capacity so the scheduler never runs out of room. These are the tasks that decide whether a platform is reliable or a 3 a.m. pager magnet.

The central tension of cluster administration is that Kubernetes is a distributed system that stores all of its truth in etcd and serves it through a single API server. Everything an operator does — HA, backups, upgrades, cert rotation — ultimately protects those two things. If the API server is unreachable, you fly blind; if etcd is lost without a backup, the cluster is gone. Most administration best practices are, at bottom, ways of never being one failure away from either outcome.

A key decision shapes how much of this you actually own: managed vs self-managed. With a managed offering (EKS, GKE, AKS) the provider runs, patches, and backs up the control plane and etcd — you own worker nodes, upgrades of those nodes, and workloads. With a self-managed cluster (kubeadm, kOps, Cluster API, or bare kubelets) you own everything, including etcd backups and control-plane HA. This page covers the full self-managed surface because understanding it makes you a better operator even on managed platforms — you know what the provider is doing for you and where the seams are.

Fundamentals

The control plane, and why HA matters

The control plane is the set of components that make cluster-wide decisions: kube-apiserver (the only front door and the only thing that talks to etcd), etcd (the Raft-backed key-value store holding all cluster state), kube-scheduler (Pod placement), and kube-controller-manager (the built-in reconciliation loops). On a single-control-plane cluster, losing that one node means no new scheduling, no scaling, no self-healing, and no kubectl — existing Pods keep running, but the cluster is frozen and blind.

High availability removes that single point of failure by running the control plane in triplicate (or five) behind a load balancer:

Highly available control plane
  1. Load Balancer (VIP)
    :6443 → healthy apiservers
  2. Control nodes
    control-1apiserver, scheduler*, ctrl-mgr*, etcd
    control-2apiserver, scheduler*, ctrl-mgr*, etcd
    control-3apiserver, scheduler*, ctrl-mgr*, etcd
  3. *
    scheduler and controller-manager run active on all nodes but only one is the leader at a time (leader election); apiserver is active-active.

etcd: the crown jewels

etcd holds the entire desired and observed state of the cluster. It is a strongly consistent (linearizable) store using the Raft consensus algorithm. Two operational truths follow:

  1. etcd is the single source of truth. Lose it with no backup and the cluster is unrecoverable — you rebuild from scratch and re-apply manifests (which is exactly why GitOps, storing manifests in Git, is a resilience strategy).
  2. etcd is latency- and I/O-sensitive. It fsyncs every write; slow disks cause apply request took too long warnings, leader elections, and cascading control-plane instability. Give it fast SSD/NVMe, keep it on dedicated disks, and keep write latency (etcd_disk_wal_fsync_duration_seconds) low.

The scheduler and capacity

The scheduler places Pods onto nodes using resource requests (not limits or actual usage). A node’s allocatable capacity is its total capacity minus reservations for the kubelet, OS, and eviction thresholds. When the sum of requested resources leaves no node with room, Pods go Pending — a capacity signal, not a bug. Capacity planning is the art of keeping enough headroom that node failures and rollouts don’t cause Pending storms, without paying for idle machines.

Certificates everywhere

Kubernetes is a mesh of mutually authenticated TLS: apiserver↔etcd, apiserver↔kubelet, controller-manager/scheduler↔apiserver, and kubelet client certs. kubeadm generates a cluster CA and issues these certs with a 1-year default lifetime. They renew automatically on kubeadm upgrade, but a cluster left un-upgraded for a year will have its control plane certs expire — a classic, avoidable outage. The kubelet’s own serving/client certs can auto-rotate via the RotateKubeletServerCertificate/RotateKubeletClientCertificate features and the CSR API.

Key Concepts

Bootstrapping a cluster

ToolBest forNotes
kubeadmProduction self-managed clustersOfficial, opinionated bootstrapper; sets up control plane + join tokens; you bring the CNI, load balancer, and automation
kind (K8s in Docker)CI and local testingWhole cluster in Docker containers; fast, disposable, multi-node; the tool Kubernetes itself uses for CI
minikubeLocal developmentSingle-node (or small) local cluster with add-ons (ingress, dashboard, registry); VM or Docker driver
k3s / k3dEdge, IoT, devLightweight (single binary, SQLite or etcd), Rancher-maintained; k3d runs k3s in Docker
kOpsSelf-managed on AWS/GCPManages the full cluster lifecycle including cloud infra
Cluster API (CAPI)Fleets, declarative infraKubernetes-native cluster lifecycle — clusters as CRDs (see file 12)

Managed control planes (EKS/GKE/AKS) skip bootstrapping the control plane entirely — the provider runs and backs up apiserver + etcd. You still choose node groups, CNI (sometimes), and upgrade timing. Trade-off: less operational burden and a hardened, patched control plane vs less control (limited apiserver flags, provider-set version support windows, and cost per cluster).

A minimal kubeadm bootstrap of the first control-plane node for an HA cluster:

# On the first control-plane node — point at the load balancer VIP so
# additional control-plane nodes can join behind the same endpoint.
kubeadm init \
  --control-plane-endpoint "k8s-api.internal:6443" \
  --upload-certs \
  --pod-network-cidr "10.244.0.0/16" \
  --kubernetes-version v1.30.2

# kubeadm prints two join commands: one for control-plane nodes
# (includes --control-plane --certificate-key) and one for workers.
# Install a CNI (e.g. Cilium/Calico) before nodes report Ready:
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.28.0/manifests/calico.yaml

etcd backup and restore

This is the single most important administration skill for self-managed clusters. Take a consistent snapshot with etcdctl snapshot save, store it off-cluster, and — crucially — rehearse the restore.

# Back up etcd (run on a control-plane node, or anywhere with the certs).
# The endpoints/certs match a kubeadm-managed etcd.
ETCDCTL_API=3 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 \
  snapshot save /var/backups/etcd-$(date +%Y%m%d-%H%M%S).db

# Verify the snapshot is intact (checks the hash and prints revision/size).
ETCDCTL_API=3 etcdctl snapshot status /var/backups/etcd-20260718.db -w table

Restore (disaster recovery) — restore the snapshot into a new data directory, then point etcd at it:

# 1. Stop the kubelet and etcd (kubeadm runs etcd as a static Pod;
#    move its manifest out of /etc/kubernetes/manifests to stop it).
# 2. Restore into a fresh data dir:
ETCDCTL_API=3 etcdctl snapshot restore /var/backups/etcd-20260718.db \
  --data-dir=/var/lib/etcd-restored

# 3. Point etcd's static Pod manifest at /var/lib/etcd-restored (hostPath),
#    restart the kubelet, and etcd comes back with the snapshot's state.

For multi-member etcd, restore on one member with the correct --initial-cluster / --initial-advertise-peer-urls, then let peers re-sync. Higher-level tools — Velero (with etcd snapshot or resource-level backup) or provider snapshots — automate this. The only backup that counts is one you have restored; schedule regular restore drills into a scratch cluster.

Cluster upgrades

Kubernetes ships a minor release roughly three times a year and supports the latest three minors (~14 months of patches each). Falling behind means unsupported versions and painful multi-hop upgrades — so upgrade continuously.

Version skew policy (the rules that make rolling upgrades safe):

ComponentAllowed skew relative to kube-apiserver
Another kube-apiserver (HA)within 1 minor
kubeletup to 3 minors older (never newer)
kube-controller-manager, scheduler, cloud-ctrl-mgr1 minor older (same minor recommended)
kubectl1 minor older or newer

The golden rules: upgrade the control plane before the nodes, never skip a minor version (1.29 → 1.30 → 1.31, not 1.29 → 1.31), and never run a kubelet newer than the apiserver.

The kubeadm upgrade flow (one minor version at a time):

# ---- Control-plane node(s), one at a time ----
# 1. Upgrade the kubeadm binary to the target version (via your package manager).
apt-get update && apt-get install -y kubeadm=1.30.2-*

# 2. Plan: kubeadm shows the component versions it will move to and any
#    manual steps / deprecated APIs to address.
kubeadm upgrade plan

# 3. Apply on the FIRST control-plane node (upgrades apiserver, controller-
#    manager, scheduler static Pods, renews control-plane certs, bumps etcd):
kubeadm upgrade apply v1.30.2

# 3b. On OTHER control-plane nodes, use the node variant instead:
#     kubeadm upgrade node

# 4. Now upgrade the kubelet + kubectl on this node, then restart kubelet:
apt-get install -y kubelet=1.30.2-* kubectl=1.30.2-*
systemctl daemon-reload && systemctl restart kubelet

# ---- Worker nodes, one at a time (see draining below) ----
kubectl drain <node> --ignore-daemonsets --delete-emptydir-data
#   ssh to node: install kubeadm 1.30.2, run `kubeadm upgrade node`,
#   install kubelet 1.30.2, restart kubelet
kubectl uncordon <node>

Before any upgrade, check for removed APIs with kubectl deprecation warnings or a tool like pluto/kubent, and test the whole flow in a staging cluster first.

Node management: cordon, drain, uncordon

Maintenance (kernel patch, kubelet upgrade, hardware work) requires safely evacuating a node’s workloads:

# Mark the node unschedulable — no NEW Pods land here, existing ones stay.
kubectl cordon node-7

# Evict the Pods gracefully, respecting PodDisruptionBudgets and grace periods.
#   --ignore-daemonsets: DaemonSet Pods can't be evicted (they belong to every node)
#   --delete-emptydir-data: acknowledge that emptyDir data will be lost
#   --grace-period / --timeout: bound how long to wait
kubectl drain node-7 --ignore-daemonsets --delete-emptydir-data --timeout=300s

# ... do the maintenance ...

# Return the node to the scheduler's pool.
kubectl uncordon node-7

Certificate management and rotation

# See when every control-plane certificate expires (run on a control-plane node).
kubeadm certs check-expiration

# Manually renew all control-plane certs (also happens automatically on upgrade).
kubeadm certs renew all
systemctl restart kubelet   # and restart the static-Pod control-plane components

# The kubelet's own client cert auto-rotates via the CSR API; pending CSRs:
kubectl get csr
kubectl certificate approve <csr-name>   # if not auto-approved

Rotate the cluster CA only when compromised — it’s disruptive (every cert must be re-issued and every kubeconfig refreshed). For workloads that need certs, use cert-manager rather than hand-rolling PKI.

API server flags and feature gates

The apiserver is configured by flags (in its static-Pod manifest for kubeadm, at /etc/kubernetes/manifests/kube-apiserver.yaml). Common knobs: --audit-policy-file (audit logging), --encryption-provider-config (encrypt Secrets at rest with KMS), --enable-admission-plugins, and --feature-gates. Feature gates toggle features by maturity — Alpha (off by default, may break/change), Beta (increasingly on by default), GA (locked on). Managed platforms expose a limited, curated subset of these; self-managed clusters get the full surface but own the risk.

Namespaces and multi-tenancy basics

Namespaces partition a cluster for isolation, quotas, and RBAC scoping. Soft multi-tenancy (trusted teams sharing a cluster) is well supported; hard multi-tenancy (untrusted tenants) needs stronger isolation and is often better served by separate clusters or virtual-cluster tools (vCluster).

apiVersion: v1
kind: ResourceQuota
metadata: { name: team-a-quota, namespace: team-a }
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "100"
---
apiVersion: v1
kind: LimitRange          # default requests/limits for Pods with none set
metadata: { name: defaults, namespace: team-a }
spec:
  limits:
    - type: Container
      default: { cpu: 500m, memory: 512Mi }
      defaultRequest: { cpu: 100m, memory: 128Mi }

Pair per-namespace ResourceQuota (caps total consumption), LimitRange (sane per-Pod defaults), RBAC (who can do what, where), NetworkPolicy (default-deny cross-namespace traffic), and Pod Security Admission (baseline/restricted) for a workable soft-multi-tenant setup.

Best Practices

  1. Run an HA control plane with 3 (or 5) etcd members — an odd number for quorum, behind a load-balanced API endpoint. A single control-plane node is fine for learning, never for production; one machine failure should never freeze the cluster.
  2. Back up etcd on a schedule and rehearse the restore — a snapshot you’ve never restored is a guess, not a backup. Store snapshots off-cluster and run periodic restore drills into a scratch cluster so disaster recovery is muscle memory, not improvisation.
  3. Give etcd fast, dedicated, low-latency storage — etcd fsyncs every write; slow disks cause leader elections and control-plane instability. Monitor etcd_disk_wal_fsync_duration_seconds and disk saturation, and keep etcd off shared/noisy volumes.
  4. Upgrade continuously, one minor at a time — Kubernetes supports only the latest three minors and never skips versions. Staying current avoids painful multi-hop upgrades and unsupported clusters; test each upgrade in staging first.
  5. Respect the version skew policy — upgrade the control plane before the nodes, keep kubelets no more than the allowed minors behind apiserver, and never run a kubelet newer than the apiserver.
  6. Scan for removed APIs before every upgrade — use pluto/kubent and heed kubectl deprecation warnings; a manifest using a removed API version will silently fail to apply after the upgrade.
  7. Always drain before node maintenance, and uncordon after — never yank a node from under running Pods. Draining evicts gracefully and honors grace periods and PodDisruptionBudgets.
  8. Give every important workload a PodDisruptionBudget and ≥2 replicas — without a PDB, a drain can take a service to zero; with one but a single replica, drains hang. Both are required for safe voluntary disruptions.
  9. Monitor certificate expiry and let upgrades renew certs — run kubeadm certs check-expiration (or alert on it); a cluster idle for a year expires its control-plane certs and goes down. Enable kubelet cert auto-rotation.
  10. Encrypt Secrets at rest with a KMS provider — etcd stores Secrets base64-encoded, i.e. effectively in the clear. Configure --encryption-provider-config with an external KMS and restrict who can read Secrets via RBAC.
  11. Plan capacity with headroom for failures and rollouts — size so that losing a node (or a zone) doesn’t cause Pending storms, and reserve room for surge during rolling updates. Right-size requests from real usage data rather than guessing.
  12. Reserve resources for the kubelet and OS — set --kube-reserved/--system-reserved and eviction thresholds so node-level daemons never get starved by Pods, which otherwise triggers node instability and cascading evictions.
  13. Isolate tenants with namespaces + ResourceQuota + LimitRange + RBAC + NetworkPolicy — soft multi-tenancy needs all five; for untrusted tenants prefer separate clusters or virtual clusters over trying to make one cluster perfectly airtight.
  14. Keep all manifests in Git (GitOps) — the fastest disaster recovery after losing etcd is re-applying a Git-tracked source of truth. Declarative, version-controlled cluster config also makes audits and rollbacks trivial.
  15. Enable audit logging and centralize control-plane logs/metrics — you cannot administer what you cannot see. Ship apiserver audit logs and control-plane metrics off-cluster so a control-plane outage doesn’t also take your forensics with it.
  16. Prefer managed control planes unless you have a reason not to — EKS/GKE/AKS remove the hardest, riskiest operational work (control-plane HA, etcd backups, patching). Self-manage only when you need capabilities managed platforms don’t offer, and know you’re taking on etcd’s care and feeding.

References