Label, selector, namespace và annotation
Suốt bốn part qua ta gõ -l app=web, -l job-name=..., --field-selector status.phase=... như phản xạ mà chưa lần nào dừng lại mổ xẻ. Part V chuyển trọng tâm từ "chạy cái gì" (controller) sang "tổ chức và truy vấn cái gì" (object và API), và điểm khởi đầu tự nhiên là chính bộ công cụ phân loại đó: label, selector, namespace, annotation, và field selector. Đây là nền cho mọi thứ phía sau: Service tìm pod, controller quản pod, RBAC giới hạn theo namespace, tất cả đều đứng trên mấy khái niệm này.
Label: nhãn để chọn
Tài liệu định nghĩa: "Labels are key/value pairs that are attached to objects such as Pods. Labels are intended to be used to specify identifying attributes of objects that are meaningful and relevant to users, but do not directly imply semantics to the core system." Câu sau quan trọng: label không mang ý nghĩa với hệ thống lõi — tier=frontend không khiến Kubernetes làm gì đặc biệt; nó chỉ có nghĩa khi bạn (hay một controller/Service) dùng selector để gom nhóm theo nó. Và "The label selector is the core grouping primitive in Kubernetes" — selector là nguyên thủy gom nhóm cốt lõi.
Dựng một rổ pod gắn nhãn để truy vấn:
# web-prod: app=web, env=prod, tier=frontend
# web-dev: app=web, env=dev, tier=frontend
# api-prod: app=api, env=prod, tier=backend
# cache: app=redis (KHÔNG có nhãn tier/env)
kubectl get pods --show-labels
NAME ... LABELS
api-prod app=api,env=prod,tier=backend
cache app=redis
web-dev app=web,env=dev,tier=frontend
web-prod app=web,env=prod,tier=frontend
Selector equality-based
Kiểu đơn giản nhất, tài liệu: "Three kinds of operators are admitted =,==,!=." Hai cái đầu là bằng (đồng nghĩa), cái cuối là khác:
kubectl get pods -l env=prod # bằng
kubectl get pods -l 'tier!=frontend' # khác
# env=prod:
api-prod
web-prod
# tier!=frontend:
api-prod
cache
env=prod lọc đúng hai pod prod. tier!=frontend trả api-prod (tier=backend) và cache — để ý cache không có nhãn tier nhưng vẫn lọt, vì nó đâu mang giá trị frontend. Ghép nhiều điều kiện bằng dấu phẩy là phép AND:
kubectl get pods -l 'env=prod,tier=frontend'
web-prod
Chỉ web-prod thỏa cả hai. Service ở Bài 16 và ReplicaSet ở Bài 24 chọn pod đúng theo cách này: selector: {matchLabels: {app: web}} tương đương app=web.
Selector set-based
Mạnh hơn là lọc theo tập giá trị. Tài liệu: "Three kinds of operators are supported: in,notin and exists."
kubectl get pods -l 'env in (prod,qa)' # giá trị nằm trong tập
kubectl get pods -l 'tier notin (frontend)' # giá trị ngoài tập (+ pod không có nhãn)
kubectl get pods -l 'tier' # tồn tại nhãn tier (không xét giá trị)
kubectl get pods -l '!tier' # KHÔNG tồn tại nhãn tier
# env in (prod,qa): api-prod, web-prod
# tier notin (frontend): api-prod, cache
# tier (exists): api-prod, web-dev, web-prod
# !tier (not exists): cache
Bốn dòng phủ hết các phép. exists (-l tier) gom mọi pod có nhãn tier bất kể giá trị — ba pod. !tier lấy ngược lại — chỉ cache. Tài liệu lưu ý đúng điều ta thấy: notin cũng gom luôn pod không có nhãn đó (cache lọt vào tier notin (frontend)). Set-based hữu ích khi quản theo nhóm: "mọi pod env trong (prod, staging)", "mọi pod chưa gắn nhãn team".
Label có thể đổi bất cứ lúc nào (kubectl label pod web-dev env=staging --overwrite), và vì controller/Service chọn pod theo label động, đổi nhãn một pod có thể đưa nó vào/ra khỏi tầm quản của một controller. Đó là con dao hai lưỡi: tiện để "gỡ" một pod hỏng ra khỏi Service mà vẫn giữ nó để khám (đổi nhãn cho nó rớt khỏi selector), nhưng cũng dễ gây tai nạn nếu sửa nhầm.
Annotation: metadata không để chọn
Nếu label là để chọn, thì annotation là để đính kèm thông tin. Tài liệu phân biệt thẳng: "Labels can be used to select objects ... In contrast, annotations are not used to identify and select objects. The metadata in an annotation can be small or large, structured or unstructured, and can include characters not permitted by labels." Pod web-prod của ta mang vài annotation kiểu thật:
kubectl get pod web-prod -o jsonpath='{.metadata.annotations}'
{"kkloud.io/git-commit":"a1b2c3d4e5f6 (nhánh release, PR #142)",
"kkloud.io/owner":"team-platform, pager +84-xxx",
"kubectl.kubernetes.io/last-applied-configuration":"{...}"}
Giá trị chứa dấu ngoặc, dấu #, dấu +, khoảng trắng — những ký tự label cấm. Annotation chứa thoải mái: commit hash, số PR, người trực, thậm chí cả cấu hình JSON (chính last-applied-configuration mà kubectl apply tự ghi — và là cái dính tới cảnh báo rollback ở Bài 24). Điểm mấu chốt: annotation không dùng để chọn được:
kubectl get pods -l 'kkloud.io/owner'
No resources found in default namespace.
-l chỉ soi labels, không soi annotations — nên dù mọi pod có annotation đó, selector trả về rỗng. Quy tắc ngón tay cái: thứ gì bạn cần lọc/gom theo thì để label (ngắn, ràng buộc ký tự); thứ gì chỉ để đọc/tham chiếu (tool, người, build info) thì để annotation.
Namespace: cô lập tên và phạm vi
Tới giờ mọi thứ ta tạo nằm trong namespace default. Namespace chia cluster thành các không gian tên độc lập — tên object chỉ cần duy nhất trong một namespace, không phải toàn cluster. Tạo một namespace và đặt vào đó một pod trùng tên với pod ở default:
kubectl create namespace shop
# tạo pod tên web-prod TRONG namespace shop (đã có web-prod ở default)
kubectl get pods -A --field-selector metadata.name=web-prod
NAMESPACE NAME
default web-prod
shop web-prod
Hai pod cùng tên web-prod sống song song — vì khác namespace. Đây là nền của đa-tenant: mỗi team/môi trường một namespace, không giẫm tên nhau, và (với RBAC ở Part XI) giới hạn quyền theo namespace. Lệnh không kèm -n chạy trong default; -n shop nhắm namespace khác; -A (hay --all-namespaces) gom tất cả. Lưu ý: không phải object nào cũng thuộc namespace — Node, PersistentVolume, Namespace bản thân nó là cluster-scoped (toàn cụm), còn Pod/Service/Deployment là namespaced.
Field selector: lọc theo trường có sẵn
Label selector lọc theo nhãn bạn gắn. Field selector lọc theo trường có sẵn của đối tượng, những thứ Kubernetes tự điền như status.phase, spec.nodeName, metadata.namespace. Ta đã dùng nó ở Bài 23 (--field-selector=status.phase=Succeeded); giờ nhìn kỹ:
kubectl get pods --field-selector 'status.phase=Running,spec.nodeName=worker-0'
kubectl get pods -o wide # để đối chiếu nodeName
# kết quả field-selector:
cache
web-dev
# đối chiếu:
api-prod ... worker-1
cache ... worker-0
web-dev ... worker-0
web-prod ... worker-1
Field selector trả đúng hai pod đang Running và nằm trên worker-0 (cache, web-dev), khớp cột nodeName. Khác biệt cốt lõi với label selector: field selector soi trạng thái/cấu trúc thật của object (pod đang chạy ở đâu, phase gì), không phải metadata bạn tự dán. Tập trường hỗ trợ field selector hẹp hơn label (tùy loại resource — phổ biến là metadata.name, metadata.namespace, status.phase, spec.nodeName), nhưng nó là cách duy nhất lọc theo những thuộc tính mà bạn không kiểm soát qua nhãn.
🧹 Dọn dẹp
kubectl delete pod web-prod web-dev api-prod cache --now
kubectl delete namespace shop # xóa namespace dọn sạch mọi thứ bên trong
Xóa namespace shop kéo theo web-prod trong đó. Cụm về lại hai pod CoreDNS. Manifest ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 28-labels-selectors.
Tổng kết
Bốn công cụ tổ chức object, mỗi cái một vai. Label là cặp key/value để gom nhóm và chọn, qua selector equality (=, !=) và set-based (in, notin, exists/!), ghép bằng dấu phẩy là AND; đây là nguyên thủy mà Service và controller dùng để tìm pod. Annotation đính metadata không-định-danh (build info, người trực, JSON), chứa được ký tự/độ dài label cấm, nhưng không select được bằng -l. Namespace cô lập tên (hai object trùng tên sống được nếu khác namespace) và là phạm vi cho RBAC/quota về sau. Field selector lọc theo trường có sẵn (status.phase, spec.nodeName...), tức trạng thái thật của object, khác với nhãn do người dùng dán. Nắm bộ này thì mọi -l, -n, --field-selector từ giờ đọc ra ngay ý định.
Bài 29 đào tiếp phần vòng đời và liên kết giữa các object: finalizer (chặn xóa tới khi dọn xong tài nguyên ngoài), ownerReferences (quan hệ sở hữu, ta đã thấy Pod→ReplicaSet→Deployment, Job→CronJob), và garbage collection tự xóa object con khi cha mất.