Quản lý object, nhãn khuyến nghị và storage version
Suốt series ta đã tạo object bằng đủ kiểu mà chưa gọi tên: lúc kubectl create deployment, lúc kubectl apply -f, lúc kubectl create -f. Tài liệu coi đây là ba kỹ thuật quản lý object khác nhau, và cảnh báo ngay: "A Kubernetes object should be managed using only one technique. Mixing and matching techniques for the same object results in undefined behavior." Bài này khép Part V, đặt ba kỹ thuật cạnh nhau, rồi nối sang hai khái niệm vận hành: nhãn khuyến nghị để các công cụ nói chung một ngôn ngữ, và storage version, phiên bản API mà object thực sự nằm trong etcd.
Ba kỹ thuật quản lý object
Imperative command — thao tác thẳng live object
Kiểu nhanh nhất, tài liệu: "a user operates directly on live objects in a cluster. The user provides operations to the kubectl command as arguments or flags ... This is the recommended way to get started or to run a one-off task ... it provides no history of previous configurations."
kubectl create deployment imp-cmd --image=busybox:1.36 -- sleep 3600
deployment.apps/imp-cmd created
Không file, không lịch sử, chỉ một động từ tác động thẳng vào cluster. Hợp cho thử nghiệm hay việc một-lần (kubectl run, kubectl expose, kubectl scale). Nhược điểm tài liệu liệt kê: không tích hợp review, không vết audit, không có "bản gốc" ngoài chính object đang sống.
Imperative object configuration — create -f một file
Một bậc nghiêm túc hơn: "the kubectl command specifies the operation (create, replace, etc.), optional flags and at least one file name. The file specified must contain a full definition of the object."
kubectl create -f cm.yaml # cm.yaml định nghĩa ConfigMap imp-obj
kubectl create -f cm.yaml # chạy LẦN NỮA
configmap/imp-obj created
Error from server (AlreadyExists): ... configmaps "imp-obj" already exists
File cấu hình lưu được vào Git, review được, nhưng lệnh là mệnh lệnh: create lần hai báo AlreadyExists chứ không tự cập nhật. Muốn sửa phải kubectl replace -f, mà replace thay sạch spec cũ bằng file: tài liệu cảnh báo nó "drops all changes to the object missing from the configuration file", nguy với những trường cluster tự điền (như externalIPs của Service LoadBalancer). Đây là lý do người ta chuyển sang kiểu thứ ba.
Declarative — apply cả thư mục
Kiểu khai báo, tài liệu: "the user does not define the operations to be taken on the files. Create, update, and delete operations are automatically detected per-object by kubectl." Bạn nói trạng thái mong muốn (file), kubectl tự tính phải tạo hay sửa. Và trước khi sửa, kubectl diff cho xem sẽ đổi gì:
kubectl apply -f dep.yaml # tạo Deployment decl-app (replicas: 2)
# ... sửa replicas 2 -> 3 trong file ...
kubectl diff -f dep.yaml
- generation: 1
+ generation: 2
- replicas: 2
+ replicas: 3
kubectl apply -f dep.yaml # apply LẦN NỮA
deployment.apps/decl-app configured
Khác hẳn create -f: apply lần hai không báo lỗi mà in configured, vì nó phát hiện khác biệt rồi vá. Bí quyết, theo tài liệu: "using the patch API operation to write only observed differences, instead of using the replace API operation". apply chỉ vá phần đổi, nên "changes made directly to live objects are retained, even if they are not merged back into the configuration files." Nó so file của bạn với annotation last-applied-configuration (cái ta gặp ở Bài 24 và 29) để biết bạn cố ý đổi gì, không đụng tới phần người/controller khác chỉnh. apply hợp với thư mục (apply -f configs/) và là nền của GitOps. Bảng tổng kết của tài liệu: imperative command cho development, hai kiểu file cho production; learning curve tăng dần command → object config → declarative.
Quy tắc vàng nhắc lại: chỉ dùng một kỹ thuật cho một object. Trộn apply với edit/replace làm lệch last-applied-configuration và sinh hành vi khó lường.
Nhãn khuyến nghị: để công cụ hiểu nhau
Bài 28 nói label là tùy bạn đặt, nhưng nếu mỗi người đặt một kiểu thì công cụ (Helm, dashboard, monitoring) không cách nào hiểu chung. Kubernetes vì thế khuyến nghị một bộ nhãn dùng tiền tố app.kubernetes.io/. Tài liệu: "Shared labels and annotations share a common prefix: app.kubernetes.io. Labels without a prefix are private to users. The shared prefix ensures that shared labels do not interfere with custom user labels."
metadata:
labels:
app.kubernetes.io/name: mysql # tên ứng dụng
app.kubernetes.io/instance: mysql-abcxyz # phân biệt nhiều bản cài
app.kubernetes.io/version: "8.0.36" # phiên bản app
app.kubernetes.io/component: database # vai trò trong kiến trúc
app.kubernetes.io/part-of: wordpress # thuộc hệ lớn hơn
app.kubernetes.io/managed-by: kubectl # công cụ quản lý
Vì là label (Bài 28), chúng truy vấn được như mọi nhãn, nhưng giờ theo một quy ước mọi công cụ cùng hiểu:
kubectl get deploy -l app.kubernetes.io/part-of=wordpress
kubectl get deploy -l 'app.kubernetes.io/component in (database)'
recommended
recommended
part-of=wordpress gom mọi thành phần của cùng một hệ (database, frontend, cache...); component in (database) lọc theo vai trò. Đây là lý do nên gắn bộ nhãn này từ đầu: một dashboard bất kỳ cũng dựng được "cây ứng dụng" mà không cần biết quy ước riêng của bạn.
Storage version: object nằm ở phiên bản API nào trong etcd
Khái niệm cuối, tinh tế nhất. Một resource có thể được API server phục vụ ở nhiều phiên bản cùng lúc:
kubectl api-versions | grep -E "autoscaling|networking"
autoscaling/v1
autoscaling/v2
networking.k8s.io/v1
networking.k8s.io/v1beta1
autoscaling có cả v1 lẫn v2; bạn kubectl get hpa.v1.autoscaling hay hpa.v2.autoscaling đều được, vì API server chuyển đổi qua lại. Nhưng trong etcd, mỗi object chỉ được lưu một lần, ở đúng một phiên bản gọi là storage version. Đào thật bằng cách đọc etcd trên controller-0 (đúng cụm ta dựng ở Bài 6), lấy chính Deployment decl-app:
sudo etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/etcd/etcd-ca.pem --cert=/etc/etcd/etcd.pem --key=/etc/etcd/etcd-key.pem \
get /registry/deployments/default/decl-app | head -c 80 | tr -c '[:print:]' '.'
/registry/deployments/default/decl-app.k8s.....apps/v1..Deployment........decl-a
Đọc được rất nhiều thứ trong vài byte đầu: object nằm dưới key /registry/deployments/default/decl-app; giá trị mở đầu bằng k8s, magic prefix báo đây là dữ liệu mã hóa protobuf (không phải JSON, gọn hơn); rồi apps/v1 Deployment, chính là storage version. Dù bạn tạo Deployment qua phiên bản API nào, etcd lưu nó ở apps/v1. (So với Bài 7, nơi ta đọc /registry/secrets/... thấy tiền tố k8s:enc:aescbc:... của mã hóa at-rest: cùng một chỗ, khác loại object.)
Điều này đáng bận tâm khi nâng cấp cluster (Bài 63): một phiên bản Kubernetes mới có thể đổi storage version của một resource (ví dụ bỏ v1beta1, chuyển sang v1). Object cũ trong etcd vẫn nằm ở version cũ tới khi được ghi lại. Đây là gốc rễ của việc "phải migrate object trước khi gỡ một API version", một bước trong quy trình nâng cấp mà ta sẽ gặp lại. Hiểu storage version giờ để lúc đó không bỡ ngỡ.
🧹 Dọn dẹp
kubectl delete deployment imp-cmd decl-app recommended
kubectl delete cm imp-obj
Tất cả là object trong cluster, xóa là sạch (Deployment kéo theo ReplicaSet + Pod qua GC của Bài 29). Cụm về lại hai pod CoreDNS. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 30-object-management.
Tổng kết
Ba kỹ thuật quản lý object, đừng trộn: imperative command (kubectl create deployment, thẳng vào live object, không lịch sử, hợp thử nghiệm); imperative object configuration (create -f/replace -f, có file để vào Git, nhưng create lần hai báo AlreadyExists, replace thay sạch spec); declarative (apply -f/diff -f, tự phát hiện create/update, vá phần đổi qua last-applied-configuration, in configured chứ không lỗi, nền của GitOps). Nhãn khuyến nghị app.kubernetes.io/* (name/instance/version/component/part-of/managed-by) cho công cụ hiểu nhau, truy vấn như label thường. Storage version: API server phục vụ một resource ở nhiều phiên bản (ta thấy autoscaling/v1 + v2) nhưng etcd lưu một; ta đọc thẳng etcd thấy decl-app nằm ở apps/v1, protobuf, nền cho việc migrate khi nâng cấp.
Hết Part V. Part VI bước vào cấu hình và chính sách: Bài 31 mở đầu với ConfigMap và Secret, cách tách cấu hình khỏi image, tiêm vào pod qua env hay volume, và Secret khác ConfigMap ra sao (gồm cả chuyện mã hóa at-rest mà ta đã thấy dấu vết trong etcd ở bài này).