2.4. Core Concepts: Services and Namespaces
Mục lục
- 1. Kubernetes Services là gì
- 2. NodePort Service
- 3. ClusterIP Service
- 4. LoadBalancer Service
- 5. Các lệnh kubectl thường dùng với Service
- 6. Namespaces
- 7. Các lệnh kubectl thường dùng với Namespace
1. Kubernetes Services là gì
Kubernetes services kích hoạt giao tiếp giữa các thành phần khác nhau, bên trong và bên ngoài ứng dụng: nối các ứng dụng lại với nhau, hoặc nối chúng với người dùng. Ví dụ, ứng dụng có các nhóm Pod chạy các phần khác nhau, như một nhóm phục vụ tải front-end cho người dùng, một nhóm khác chạy các tiến trình back-end, và nhóm thứ ba kết nối tới một nguồn dữ liệu bên ngoài. Chính Services kích hoạt kết nối giữa các nhóm Pod này: làm ứng dụng front-end khả dụng cho người dùng cuối, cho Pod back-end và front-end nói chuyện được với nhau, và thiết lập kết nối tới nguồn dữ liệu bên ngoài.
💡 Hình dung: Service giống số tổng đài chung của công ty: khách hàng gọi đúng 1 số duy nhất, không cần biết nhân viên nào đang trực máy nào. Nhân viên nghỉ, đổi ca, hay có người mới thay thế, số tổng đài vẫn không đổi. Đây là "kết nối lỏng lẻo" (loose coupling): Pod phía sau đổi liên tục, nhưng địa chỉ Service phía trước luôn ổn định.
Giao tiếp ra bên ngoài
Xét một use case của Services: đã bàn về việc Pod giao tiếp với nhau qua networking nội bộ, giờ tới lượt giao tiếp ra bên ngoài. Đã deploy một Pod chạy web application. Làm sao, với vai trò người dùng bên ngoài, truy cập trang web đó?
Trước tiên, xem thiết lập hiện có: Kubernetes node có một IP, ví dụ 192.168.1.2. Laptop cũng nằm trên cùng mạng, có IP 192.168.1.10. Mạng Pod nội bộ nằm trong dải 10.244.0.0, và Pod có IP 10.244.0.2. Rõ ràng không thể ping hay truy cập trực tiếp Pod ở địa chỉ 10.244.0.2 vì nó nằm trên một mạng riêng biệt. Với sơ đồ mạng đó, chỉ còn vài lựa chọn để xem được trang web.
Cách đầu tiên: SSH vào Kubernetes node tại 192.168.1.2, rồi từ node curl vào trang web của Pod. Hoặc nếu node có GUI, mở trình duyệt và xem trang web tại địa chỉ http://10.244.0.2. Nhưng đây là truy cập từ bên trong Kubernetes node, không phải điều mong muốn. Mục tiêu là truy cập web server từ chính laptop, không cần SSH vào node, chỉ bằng cách truy cập IP của Kubernetes node.
Vì vậy cần một thứ ở giữa, giúp map request từ laptop, qua node, tới Pod đang chạy web container. Đây là lúc Kubernetes Service vào cuộc. Kubernetes Service là một object, giống như Pods, ReplicaSets, hay Deployments đã làm việc trước đó. Một trong các use case của nó là lắng nghe trên một port của node và forward request trên port đó tới một port trên Pod đang chạy web application. Loại service này gọi là NodePort Service, vì service lắng nghe trên một port của node và forward request tới các Pod.
Các loại Service
Loại thứ nhất là NodePort, vừa bàn ở trên, nơi service làm cho một port nội bộ truy cập được trên một port của node.
Loại thứ hai là ClusterIP. Trong trường hợp này, service tạo một virtual IP bên trong cluster để kích hoạt giao tiếp giữa các service khác nhau, ví dụ giữa một tập front-end server và một tập back-end server.
Loại thứ ba là LoadBalancer, nơi nó cấp phát (provision) một load balancer cho ứng dụng trên các nhà cung cấp cloud được hỗ trợ. Một ví dụ tốt là phân phối tải giữa các web server khác nhau ở tầng front-end.
Cả 3 loại đều dùng chung 1 object Service, chỉ khác nhau ở field type. Bắt đầu từ loại đơn giản và trực quan nhất: NodePort.
2. NodePort Service
Quay lại NodePort: đã bàn về việc truy cập bên ngoài vào ứng dụng, và một Service giúp được việc đó bằng cách map một port trên node tới một port trên Pod. Nhìn kỹ hơn vào Service: có 3 port liên quan.
Port trên Pod, nơi web server chạy, là 80, và nó được gọi là targetPort, vì đó là nơi service forward request tới. Port thứ hai là port trên chính service, gọi gọn là port. (Ghi nhớ, các thuật ngữ này đều nhìn từ góc độ của Service.) Service thực chất giống một virtual server bên trong cluster: nó có địa chỉ IP riêng, và địa chỉ IP đó gọi là ClusterIP của Service. Cuối cùng là port trên chính node, dùng để truy cập web server từ bên ngoài, gọi là NodePort. Trong ví dụ này, nó được set là 30008, vì NodePort chỉ có thể nằm trong một dải hợp lệ, mặc định từ 30000 đến 32767.
Tạo Service
Cũng giống như cách đã tạo Deployment, ReplicaSet, hay Pod trước đó, dùng một definition file để tạo Service. Cấu trúc cấp cao của file vẫn giữ nguyên như trước: có apiVersion, kind, metadata, và spec.
apiVersion sẽ là v1, kind tất nhiên là Service. metadata sẽ có name, tức tên của Service. Nó có thể có label, nhưng chưa cần dùng ở đây. Tiếp theo là spec, phần quan trọng nhất của file, vì đây là nơi định nghĩa cho Service và cũng là phần khác nhau giữa các loại object.
Trong phần spec của Service, có type và ports. type là loại service đang tạo, có thể là ClusterIP, NodePort, hoặc LoadBalancer như đã bàn. Trong trường hợp này, vì đang tạo một NodePort, set là NodePort.
Phần tiếp theo của spec là ports. Đây là nơi cung cấp thông tin đã bàn ở trên. Loại port đầu tiên là targetPort, set là 80. Cái tiếp theo đơn giản là port, một port trên đối tượng Service, cũng set là 80. Thứ ba là nodePort, set là 30008 hoặc bất kỳ số nào trong dải hợp lệ.
Lưu ý, trong các field này, chỉ có port là field bắt buộc. Nếu không cung cấp targetPort, nó được giả định giống với port. Nếu không cung cấp nodePort, một port trống trong dải hợp lệ từ 30000 đến 32767 sẽ được tự động cấp phát. Cũng lưu ý ports là một array: dấu gạch ngang dưới phần ports chỉ ra phần tử đầu tiên. Về lý thuyết, có thể có nhiều port mapping như vậy trong cùng một service.
Vậy đã có đủ thông tin, nhưng còn thiếu một thứ quan trọng: chưa có gì trong definition file kết nối Service với Pod. Mới chỉ chỉ định targetPort, chưa nhắc gì tới Pod đích, mà có thể có hàng trăm Pod khác chạy web service trên port 80. Vậy làm sao chỉ đúng Pod cần tới?
Cách làm giống hệt ReplicaSet ở Part 2.3, một kỹ thuật sẽ thấy rất thường xuyên trong Kubernetes: dùng label và selector để liên kết chúng lại với nhau. Pod đã được tạo với một label, giờ cần đưa label đó vào trong Service definition file. Có một property mới trong phần spec, gọi là selector. Dưới selector, cung cấp danh sách label để xác định Pod. Tham khảo pod definition file dùng để tạo Pod, lấy label từ đó và đặt dưới phần selector. Việc này liên kết Service với Pod.
File service-definition.yaml hoàn chỉnh:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80
port: 80
nodePort: 30008
selector:
app: myapp
Xong, tạo service bằng lệnh kubectl create, input service definition file:
kubectl create -f service-definition.yaml
# Output:
service/myapp-service created
Để xem service vừa tạo:
kubectl get services
# Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-service NodePort 10.106.5.201 <none> 80:30008/TCP 8s
Lệnh này liệt kê service, ClusterIP, và các port được map. type là NodePort như đã tạo, và port trên node được set là 30008, vì đó là port chỉ định trong definition file. Giờ có thể dùng port này để truy cập web service, qua curl hay trình duyệt: curl 192.168.1.2:30008.
Service với nhiều Pod
Tới đây mới bàn về Service map tới một Pod duy nhất. Nhưng không phải lúc nào cũng vậy. Trong môi trường production, thường có nhiều instance của web application chạy để có tính sẵn sàng cao và cân bằng tải. Khi đó, có nhiều Pod tương tự chạy web application. Tất cả đều có cùng label: key app, giá trị myapp. Cùng label này được dùng làm selector khi tạo Service.
Vậy khi Service được tạo, nó tìm các Pod khớp với label và tìm thấy 3 Pod. Service sau đó tự động chọn cả 3 Pod làm endpoint để forward request bên ngoài từ người dùng, không phải cấu hình thêm bất cứ gì để đạt được điều này. Còn thuật toán nó dùng để cân bằng tải giữa 3 Pod khác nhau là một thuật toán ngẫu nhiên (random algorithm). Nhờ vậy, Service hoạt động như một built-in load balancer để phân phối tải giữa các Pod khác nhau.
Service khi Pod trải trên nhiều node
Cuối cùng, xem điều gì xảy ra khi các Pod trải trên nhiều node. Lúc này web application nằm trên các Pod ở những node riêng biệt trong cluster. Khi tạo một service, không cần thực hiện thêm cấu hình gì, Kubernetes tự động tạo một service trải rộng trên toàn bộ node trong cluster, và map target port tới cùng một NodePort trên mọi node. Kết quả là có thể truy cập ứng dụng bằng IP của bất kỳ node nào trong cluster, dùng cùng một số port, ở đây là 30008.
Dù là một Pod duy nhất trên một node duy nhất, nhiều Pod trên một node duy nhất, hay nhiều Pod trên nhiều node, Service được tạo theo hệt cùng một cách mà không cần thêm bước nào. Khi Pod bị xóa hoặc thêm vào, Service tự động được cập nhật, tự thích ứng mà không cần ai can thiệp. Một khi đã tạo, thường sẽ không phải sửa cấu hình gì nữa.
NodePort giải quyết tốt việc truy cập từ bên ngoài, nhưng còn giao tiếp nội bộ giữa các tầng của cùng một ứng dụng (front-end gọi back-end, back-end gọi database) thì sao? Đó là lúc cần tới loại Service thứ hai: ClusterIP.
Bàn về việc dùng selector trong Service so với ReplicaSet/Deployment
matchLabels với nhiều label luôn là điều kiện AND, không có OR: selector có app: myapp và tier: backend thì pod phải khớp đồng thời cả hai label đó, thiếu một trong hai là bị loại ngay. Về bản chất, matchLabels chỉ là cú pháp rút gọn của matchExpressions với operator In chứa đúng 1 giá trị, nên nguyên tắc AND này áp dụng cho toàn bộ LabelSelector: mọi phần tử trong matchLabels cộng với mọi phần tử trong matchExpressions đều nối với nhau bằng AND, và không có cách nào tạo OR giữa các key khác nhau.
Muốn có OR thì phải dùng matchExpressions với operator In và liệt kê nhiều giá trị cho cùng một key, ví dụ tier In [backend, cache]. Đây là OR trên các giá trị của cùng một key, còn giữa các key khác nhau thì vẫn luôn AND.
Riêng với Deployment, spec.selector là bất biến (immutable) sau khi tạo, và bắt buộc phải là tập con của spec.template.metadata.labels. Chiều ngược lại thì thoải mái hơn nhiều: pod có nhiều label hơn selector yêu cầu vẫn match bình thường, vì cơ chế match hoạt động theo kiểu tập hợp con (subset), tức pod chỉ cần chứa đủ tất cả label mà selector yêu cầu, có thêm bao nhiêu label khác cũng không ảnh hưởng. Viết gọn thành pod labels ⊇ selector labels là match, không cần bằng tuyệt đối, và quy tắc này áp dụng chung cho Service selector, Deployment/ReplicaSet selector, và NetworkPolicy selector.
Chính sự thoải mái đó lại là nguồn rủi ro thực tế: nếu nhiều pod chia sẻ chung một label khớp với selector của một Deployment/ReplicaSet khác, dễ xảy ra tình trạng "cướp" pod ngoài ý muốn, tức pod bị một ReplicaSet khác nhận nuôi hoặc xóa nhầm. Nên luôn thêm một label đặc trưng riêng (ví dụ pod-template-hash hoặc tên riêng) để tránh đụng độ.
Vậy vì sao Service lại chỉ dùng selector đơn giản, thay vì matchLabels/matchExpressions đầy đủ như Deployment/ReplicaSet? Đây là lý do lịch sử về API, không phải khác biệt logic:
Service.spec.selectorlà kiểu map[string]string đơn giản, thuộc nhóm API core/v1, một trong những object cũ và ổn định nhất, được định nghĩa trước khi có LabelSelector.- LabelSelector (với
matchLabels+matchExpressions, hỗ trợ set-based:In,NotIn,Exists,DoesNotExist) được thêm sau, dùng cho các resource ở nhóm apps/v1 (ReplicaSet, Deployment, DaemonSet, StatefulSet) và batch/v1 (Job). - Vì Service thuộc API core đã "đóng băng" để đảm bảo tương thích ngược, không thể đổi kiểu
selectorsangLabelSelectormà không phá vỡ các manifest hiện có. - Về hiệu lực,
Service.spec.selectortương đương đúng phầnmatchLabelscủaLabelSelector: toàn bộ cặp key-value đều là AND, chỉ hỗ trợ so khớp bằng (equality-based). Service selector không hỗ trợ set-based requirements (In/NotIn/Exists) như ReplicaSet/Deployment. Cần logic OR hay NotIn cho việc chọn pod thì phải làm ở tầng Deployment/ReplicaSet, không làm được trực tiếp ở Service.
3. ClusterIP Service
Một ứng dụng web full-stack thường có nhiều loại Pod host các phần khác nhau của ứng dụng. Có thể có một tập Pod chạy front-end web server, một tập Pod khác chạy back-end server, một tập Pod chạy key-value store như Redis, và một tập Pod khác chạy database bền vững (persistent database) như MySQL. Web front-end server cần giao tiếp với back-end server, và back-end server cần giao tiếp với database cũng như service Redis, v.v. Vậy đâu là cách đúng để thiết lập kết nối giữa các service hay tầng khác nhau của ứng dụng?
Tất cả các Pod đều được gán một địa chỉ IP. Nhưng các IP này, như đã biết, không cố định. Các Pod có thể sập bất cứ lúc nào, và Pod mới liên tục được tạo. Vì vậy không thể dựa vào các địa chỉ IP này để giao tiếp nội bộ giữa các thành phần ứng dụng. Còn một vướng mắc nữa: Pod front-end đầu tiên tại 10.244.0.3 cần kết nối tới một back-end service thì chưa có gì nói cho nó biết chọn cái nào trong 3 cái, cũng chưa có ai đứng ra quyết định việc đó.
Một Kubernetes Service có thể giúp group các Pod lại với nhau và cung cấp một interface duy nhất để truy cập các Pod trong một nhóm. Ví dụ, một service được tạo cho các Pod back-end sẽ giúp group toàn bộ Pod back-end lại, và cung cấp một interface duy nhất để các Pod khác truy cập service. Các request được forward tới một trong các Pod dưới service đó một cách ngẫu nhiên. Tương tự, tạo thêm các service cho Redis, và cho phép Pod back-end truy cập hệ thống Redis thông qua service.
Với cách này, việc deploy một ứng dụng theo kiến trúc microservices trên Kubernetes cluster trở nên dễ dàng và hiệu quả: mỗi tầng giờ có thể scale hoặc di chuyển tùy ý mà không ảnh hưởng giao tiếp giữa các service khác nhau. Đúng như diagram trên, backend gọi tới Redis chỉ bằng tên redis, không quan tâm Pod Redis đang chạy ở đâu hay IP bao nhiêu.
Mỗi service được gán một IP và tên bên trong cluster, và đó là tên các Pod khác dùng để truy cập service. Loại service này gọi là ClusterIP.
Tạo ClusterIP Service
Để tạo loại service này, vẫn dùng một definition file. Trong service definition file, dùng template mặc định có apiVersion, kind, metadata, và spec. apiVersion là v1, kind là Service, và đặt tên cho service, lần này gọi là back-end. Dưới đặc tả (specification), có type và ports; type là ClusterIP. Thực tế, ClusterIP là type mặc định: dù không chỉ định, nó tự động được coi là type ClusterIP.
Dưới ports, có targetPort và port. targetPort là port nơi back-end được expose, trong trường hợp này là 80, và port là nơi service được expose, cũng là 80. Để liên kết service với một tập Pod, dùng selector. Tham khảo pod definition file và copy label từ đó, đặt dưới selector. Vậy là xong.
apiVersion: v1
kind: Service
metadata:
name: back-end
spec:
type: ClusterIP
ports:
- targetPort: 80
port: 80
selector:
app: myapp
type: backend
Giờ có thể tạo service bằng lệnh kubectl create, rồi kiểm tra trạng thái bằng lệnh kubectl get services:
kubectl create -f service-definition.yaml
kubectl get services
# Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
back-end ClusterIP 10.106.9.42 <none> 80/TCP 6s
Service có thể được các Pod khác truy cập bằng ClusterIP hoặc tên của service: mọi Pod khác trong cùng namespace chỉ cần gọi tới back-end.
Đã giải quyết được truy cập từ bên ngoài (NodePort) và giao tiếp nội bộ (ClusterIP). Còn một tình huống nữa: khi chạy trên cloud, làm sao đưa cho end user đúng 1 URL duy nhất thay vì hàng loạt cặp IP:port của NodePort? Đó là việc của LoadBalancer.
4. LoadBalancer Service
Giờ xem một loại service khác gọi là LoadBalancer. Đã thấy NodePort Service, giúp làm cho một ứng dụng hướng ra bên ngoài (external-facing) khả dụng trên một port của các worker node.
Xét các ứng dụng front-end gồm voting app và result app. Các Pod này được host trên các worker node trong cluster, giả sử là một cluster 4 node. Để các ứng dụng này truy cập được bởi người dùng bên ngoài, tạo các Service loại NodePort.
Các service loại NodePort giúp nhận traffic trên các port của node và route traffic tới các Pod tương ứng. Nhưng nên đưa URL nào cho end user để truy cập ứng dụng? Có thể truy cập cả 2 ứng dụng bằng IP của bất kỳ node nào và high port mà service được expose, tức là 4 tổ hợp IP-port cho voting app và 4 tổ hợp IP-port cho result app. Đó không phải điều end user muốn. Họ cần một URL duy nhất, như example-votingapp.com, để truy cập ứng dụng.
Một cách là tạo một VM mới cho mục đích load balancer, và cài đặt, cấu hình một load balancer phù hợp trên đó, như HAProxy hay NGINX, v.v. Rồi cấu hình load balancer route traffic tới các node bên dưới. Việc thiết lập external load balancing như vậy, rồi duy trì và quản lý nó, có thể là một công việc khá tốn công.
💡 Hình dung: LoadBalancer giống tổng đài 1800 toàn quốc: khách hàng chỉ cần nhớ đúng 1 số, hệ thống tự phân phối cuộc gọi tới chi nhánh còn rảnh. So với việc phải nhớ số điện thoại riêng của từng chi nhánh (NodePort), rõ ràng tiện hơn nhiều.
Tuy nhiên, nếu đang chạy trên một cloud platform được hỗ trợ như Google Cloud, AWS, hay Azure, có thể tận dụng load balancer gốc (native) của cloud platform đó. Kubernetes hỗ trợ tích hợp với load balancer gốc của một số cloud provider nhất định, và tự cấu hình cho mình. Chỉ cần set type của service cho front-end service là LoadBalancer thay vì NodePort.
Lưu ý, cách này chỉ hoạt động với các cloud platform được hỗ trợ, trong đó chắc chắn có GCP, AWS, và Azure. Nếu set type của service là LoadBalancer trong một môi trường không được hỗ trợ như VirtualBox hay môi trường khác, nó sẽ có tác dụng y hệt như set NodePort: service vẫn được expose trên một port cao (high-end port) trên các node. Nó không thực hiện bất kỳ cấu hình external load balancer nào.
Đã nắm đủ 3 loại Service. Phần tiếp theo tổng hợp lại toàn bộ lệnh kubectl hay dùng nhất với Service, kể cả cách debug khi Service không route được traffic, một trong những lỗi hay gặp nhất khi vận hành thực tế.
5. Các lệnh kubectl thường dùng với Service
Tạo Service
Ngoài cách dùng file YAML đã thấy ở trên, kubectl expose tạo nhanh Service ngay từ 1 Pod hay Deployment đang chạy, không cần viết YAML:
kubectl expose pod myapp-pod --port=80 --target-port=80 --name=myapp-service --type=NodePort
# Output:
service/myapp-service exposed
kubectl expose deployment myapp-deployment --port=80 --target-port=80 --type=ClusterIP
# Output:
service/myapp-deployment exposed
Xem Service
kubectl get svc
# Output:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10d
myapp-service NodePort 10.106.5.201 <none> 80:30008/TCP 8s
Xem chi tiết (chỗ đầu tiên cần nhìn khi Service không route được traffic, nhờ mục Endpoints):
kubectl describe svc myapp-service
# Output (rút gọn):
Name: myapp-service
Type: NodePort
IP: 10.106.5.201
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30008/TCP
Endpoints: 10.244.0.5:80,10.244.0.6:80,10.244.0.7:80
Selector: app=myapp
Endpoints là danh sách IP:port mà Service đang forward request tới, mỗi dòng tương ứng 1 Pod khớp selector.
Debug Service không route được traffic
| Triệu chứng | Nguyên nhân | Cách xử lý |
|---|---|---|
Endpoints: <none> | selector không khớp label của bất kỳ Pod nào | So kubectl get pods --show-labels với selector trong Service — chỉ cần lệch 1 ký tự (vd FR thay vì FE) là 0 endpoint |
| Có Endpoints nhưng vẫn không truy cập được | targetPort khác với port container thực sự lắng nghe | Kiểm tra containerPort trong Pod spec so với targetPort của Service |
| NodePort không vào được từ máy ngoài cluster | nodePort nằm ngoài dải 30000-32767, hoặc firewall/security group chặn port đó | kubectl get svc xem lại nodePort đã cấp đúng dải chưa; kiểm tra firewall ở tầng hạ tầng |
Danh sách lệnh chính
| Lệnh | Công dụng |
|---|---|
kubectl create -f <file>.yaml | Tạo Service từ file YAML |
kubectl expose pod/deployment <name> --port=<p> --target-port=<p> | Tạo nhanh Service từ Pod/Deployment đang chạy |
kubectl get svc | Xem danh sách Service, TYPE, CLUSTER-IP, PORT(S) |
kubectl describe svc <name> | Xem chi tiết — quan trọng nhất là mục Endpoints khi debug |
Đã xong phần Service. Giờ đến một khái niệm khác giúp tổ chức và cô lập mọi thứ vừa học (Pod, ReplicaSet, Deployment, Service) thành các "khu vực" riêng biệt trong cùng 1 cluster: Namespace.
6. Namespaces
Bắt đầu với một analogy. Có 2 cậu bé cùng tên Randall. Để phân biệt chúng, gọi bằng họ của chúng: Smith và Williams. Tất nhiên, chúng đến từ 2 nhà khác nhau: nhà Smith và nhà Williams. Trong mỗi nhà còn có các thành viên khác. Các thành viên bên trong nhà gọi nhau đơn giản bằng tên (first name), ví dụ người cha gọi Randall đơn giản là Randall. Nhưng nếu người cha muốn gọi tới Randall ở nhà kia, họ sẽ dùng tên đầy đủ. Ai đó bên ngoài 2 nhà này cũng sẽ dùng tên đầy đủ để nhắc tới 2 cậu bé hay bất kỳ ai trong 2 nhà.
Mỗi nhà này có bộ quy tắc riêng, định nghĩa ai được làm gì. Mỗi nhà cũng có tập tài nguyên riêng mà họ được phép tiêu thụ.
Quay lại Kubernetes: các "nhà" này tương ứng với namespace. Tới lúc này, tài liệu đã tạo các object như Pod, Deployment, và Service trong cluster, và mọi thứ đã làm đều nằm trong một namespace: suốt thời gian qua vẫn đang ở "trong một nhà". Namespace này gọi là namespace default, được tạo tự động bởi Kubernetes khi cluster được setup lần đầu.
Kubernetes tạo một tập Pod và Service cho mục đích nội bộ, như những gì cần cho giải pháp networking, DNS service, v.v. Để cô lập chúng khỏi người dùng và tránh việc vô tình xóa hay sửa các service này, Kubernetes tạo chúng dưới một namespace khác, được tạo lúc cluster khởi động, tên là kube-system.
Namespace thứ ba được Kubernetes tự động tạo là kube-public. Đây là nơi các resource cần được cung cấp cho tất cả người dùng được tạo.
Nếu môi trường nhỏ, hoặc đang học và thử nghiệm với một cluster nhỏ, chưa cần lo về namespace: cứ tiếp tục làm việc trong namespace default. Nhưng khi phát triển và dùng Kubernetes cluster cho mục đích doanh nghiệp hoặc production, nên cân nhắc dùng namespace, và có thể tự tạo namespace riêng.
Ví dụ, nếu muốn dùng cùng cluster cho cả môi trường dev và production, nhưng đồng thời muốn cô lập tài nguyên giữa chúng, có thể tạo một namespace riêng cho mỗi cái. Như vậy, khi làm việc trong môi trường dev, sẽ không vô tình sửa tài nguyên ở production. Mỗi namespace có thể có bộ policy riêng, định nghĩa ai được làm gì. Cũng có thể gán quota tài nguyên cho mỗi namespace, để mỗi namespace được đảm bảo một lượng nhất định và không dùng vượt quá giới hạn cho phép.
Giao tiếp giữa các resource trong và ngoài namespace
Quay lại namespace default đang làm việc, giống như các thành viên trong một nhà gọi nhau bằng tên đầu, các resource trong một namespace có thể tham chiếu tới nhau đơn giản bằng tên. Trong trường hợp này, Pod web app có thể reach tới service DB đơn giản bằng hostname db-service.
Khi cần, Pod web app cũng có thể reach tới một service ở namespace khác. Để làm được, phải thêm tên của namespace vào tên của service. Ví dụ, để Pod web ở namespace default kết nối tới database trong môi trường (namespace) dev, dùng định dạng tên: service-name.namespace.svc.cluster.local. Cụ thể sẽ là db-service.dev.svc.cluster.local. Có thể làm được điều này vì khi Service được tạo, một entry DNS được thêm tự động theo đúng định dạng này.
Nhìn kỹ vào tên DNS của service: phần cuối, cluster.local, là tên miền mặc định của Kubernetes cluster. svc là subdomain cho service, theo sau bởi namespace, rồi tới tên của chính service.
Các thao tác vận hành với namespace
Xem một số khía cạnh vận hành của namespace, bắt đầu với các lệnh kubectl.
Lệnh sau dùng để list tất cả các Pod:
kubectl get pods
Nhưng nó chỉ list các Pod trong namespace default. Để list Pod trong một namespace khác, dùng option --namespace kèm tên namespace, ví dụ kube-system:
kubectl get pods --namespace=kube-system
Với một pod definition file có sẵn, khi tạo Pod dùng file này, Pod được tạo trong namespace default. Để tạo Pod trong một namespace khác, dùng option --namespace:
kubectl create -f pod-definition.yaml --namespace=dev
Nếu muốn đảm bảo Pod này luôn được tạo trong môi trường dev, ngay cả khi không chỉ định namespace trên command line, có thể di chuyển định nghĩa namespace vào chính pod definition file, dưới phần metadata. Đây là cách tốt để đảm bảo tài nguyên luôn được tạo trong cùng một namespace.
Vậy làm sao tạo một namespace mới, và làm sao tránh phải gõ --namespace mỗi lần gõ lệnh? Phần cuối tổng hợp lại toàn bộ lệnh kubectl hay dùng nhất với Namespace.
7. Các lệnh kubectl thường dùng với Namespace
Tạo Namespace
kubectl create namespace dev
# Output:
namespace/dev created
Hoặc bằng file YAML, hữu ích khi cần thêm label/annotation cho namespace:
apiVersion: v1
kind: Namespace
metadata:
name: dev
Xem và chuyển namespace
kubectl get namespaces
# Output:
NAME STATUS AGE
default Active 10d
kube-system Active 10d
kube-public Active 10d
dev Active 8s
Xem Pod ở tất cả namespace cùng lúc, rất hữu ích khi không nhớ resource nằm ở namespace nào:
kubectl get pods --all-namespaces
# Output:
NAMESPACE NAME READY STATUS RESTARTS AGE
default myapp-deployment-... 1/1 Running 0 2m
kube-system coredns-... 1/1 Running 0 10d
dev redis 1/1 Running 0 20s
Gõ --namespace=<ns> mỗi lần khá phiền, nên đổi hẳn namespace mặc định của context hiện tại để khỏi phải lặp lại:
kubectl config set-context --current --namespace=dev
# Output:
Context "kubernetes-admin@kubernetes" modified.
Từ giờ, mọi lệnh kubectl không chỉ định -n sẽ tự áp dụng cho namespace dev.
Tạo resource trong 1 namespace cụ thể
kubectl run redis --image=redis --namespace=finance
# Output:
pod/redis created
kubectl get pods --namespace=finance
# Output:
NAME READY STATUS RESTARTS AGE
redis 1/1 Running 0 8s
Danh sách lệnh chính
| Lệnh | Công dụng |
|---|---|
kubectl create namespace <name> | Tạo Namespace mới |
kubectl get ns / get namespaces | Xem danh sách Namespace |
kubectl get pods -n <ns> / --namespace=<ns> | Xem Pod trong 1 namespace cụ thể |
kubectl get pods --all-namespaces / -A | Xem resource ở mọi namespace cùng lúc |
kubectl config set-context --current --namespace=<ns> | Đổi namespace mặc định của context hiện tại |
kubectl create -f <file>.yaml -n <ns> | Tạo resource từ YAML vào đúng namespace chỉ định |
Vậy là đã đi qua toàn bộ 4 khối xây dựng nền tảng của Kubernetes: Pod, ReplicaSet/Deployment, Service, và Namespace. Bước tiếp theo là hiểu sự khác nhau giữa cách khai báo imperative (ra lệnh trực tiếp qua CLI) và declarative (khai báo trạng thái mong muốn qua YAML), thứ quyết định cách nên thao tác trong thực tế lẫn trong kỳ thi CKA, ở Part 2.5.
Nguồn tham khảo
Nguồn gốc: Khóa "Certified Kubernetes Administrator (CKA)" — phần "Core Concepts" (Kubernetes Services: NodePort, ClusterIP, LoadBalancer; Namespaces: analogy nhà Smith/Williams, default/kube-system/kube-public, DNS cross-namespace), nền tảng KodeKloud. Giảng viên: Mumshad Mannambeth.
Repo ghi chú, link tài liệu, và đáp án các practice question của toàn bộ khóa học: kodekloudhub/certified-kubernetes-administrator-course.