This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Các khái niệm

Phần Khái niệm giúp bạn tìm hiểu về các bộ phận của hệ thống Kubernetes và các khái niệm mà Kubernetes sử dụng để biểu diễn cụm cluster của bạn, đồng thời giúp bạn hiểu sâu hơn về cách thức hoạt động của Kubernetes.

1 - Deployments

Deployment quản lý một tập hợp các Pod để chạy một workload ứng dụng, thường là các ứng dụng không duy trì trạng thái.

Deployment cung cấp khả năng cập nhật dạng khai báo (declarative) cho các Pod và ReplicaSet.

Bạn mô tả trạng thái mong muốn (desired state) trong một Deployment, và Vòng điều khiển (Controller) của Deployment sẽ thay đổi trạng thái thực tế sang trạng thái mong muốn với tốc độ được kiểm soát. Bạn có thể định nghĩa các Deployment để tạo ReplicaSet mới, hoặc để xóa các Deployment hiện có và tiếp nhận toàn bộ tài nguyên của chúng bằng các Deployment mới.

Lưu ý:

Không quản lý trực tiếp các ReplicaSet thuộc sở hữu của một Deployment. Hãy cân nhắc mở một issue trong repository chính của Kubernetes nếu trường hợp sử dụng của bạn không được đề cập bên dưới.

Trường hợp sử dụng

Sau đây là các trường hợp sử dụng điển hình của Deployment:

Tạo một Deployment

Dưới đây là một ví dụ về Deployment. Nó tạo một ReplicaSet để khởi chạy ba Pod nginx:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

Trong ví dụ này:

  • Một Deployment tên là nginx-deployment được tạo, được chỉ định bởi trường .metadata.name. Tên này sẽ trở thành cơ sở để đặt tên cho các ReplicaSet và Pod được tạo sau đó. Xem Viết spec cho Deployment để biết thêm chi tiết.

  • Deployment tạo một ReplicaSet, ReplicaSet này tạo ba Pod replica, được chỉ định bởi trường .spec.replicas.

  • Trường .spec.selector định nghĩa cách ReplicaSet được tạo ra tìm các Pod mà nó cần quản lý. Trong trường hợp này, bạn chọn một label được định nghĩa trong Pod template (app: nginx). Tuy nhiên, có thể sử dụng các quy tắc lựa chọn phức tạp hơn, miễn là bản thân Pod template thỏa mãn quy tắc đó.

    Lưu ý:

    Trường .spec.selector.matchLabels là một map gồm các cặp {key,value}. Một cặp {key,value} trong map matchLabels tương đương với một phần tử của matchExpressions, có trường key là "key", operator là "In", và mảng values chỉ chứa "value". Tất cả các yêu cầu, từ cả matchLabels và matchExpressions, đều phải được thỏa mãn thì mới khớp.
  • Trường .spec.template chứa các trường con sau:

    • Các Pod được gắn label app: nginx thông qua trường .metadata.labels.
    • Đặc tả của Pod template, hay trường .spec, chỉ ra rằng các Pod chạy một container, nginx, container này chạy image nginx trên Docker Hub ở phiên bản 1.14.2.
    • Tạo một container và đặt tên là nginx thông qua trường .spec.containers[0].name.

Trước khi bắt đầu, hãy đảm bảo cluster Kubernetes của bạn đang hoạt động. Làm theo các bước dưới đây để tạo Deployment ở trên:

  1. Tạo Deployment bằng cách chạy lệnh sau:

    kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml
    
  2. Chạy kubectl get deployments để kiểm tra xem Deployment đã được tạo hay chưa.

    Nếu Deployment vẫn đang được tạo, kết quả sẽ tương tự như sau:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   0/3     0            0           1s
    

    Khi bạn kiểm tra các Deployment trong cluster, các trường sau được hiển thị:

    • NAME liệt kê tên của các Deployment trong namespace.
    • READY hiển thị số replica của ứng dụng đang sẵn sàng phục vụ người dùng. Giá trị có dạng ready/desired.
    • UP-TO-DATE hiển thị số replica đã được cập nhật để đạt trạng thái mong muốn.
    • AVAILABLE hiển thị số replica của ứng dụng đang khả dụng cho người dùng.
    • AGE hiển thị khoảng thời gian ứng dụng đã chạy.

    Lưu ý rằng số replica mong muốn là 3, theo trường .spec.replicas.

  3. Để xem trạng thái rollout của Deployment, chạy kubectl rollout status deployment/nginx-deployment.

    Kết quả tương tự như sau:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    deployment "nginx-deployment" successfully rolled out
    
  4. Chạy lại kubectl get deployments sau vài giây. Kết quả tương tự như sau:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           18s
    

    Lưu ý rằng Deployment đã tạo cả ba replica, và tất cả các replica đều đã được cập nhật (chứa Pod template mới nhất) và khả dụng.

  5. Để xem ReplicaSet (rs) được tạo bởi Deployment, chạy kubectl get rs. Kết quả tương tự như sau:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-75675f5897   3         3         3       18s
    

    Kết quả của ReplicaSet hiển thị các trường sau:

    • NAME liệt kê tên của các ReplicaSet trong namespace.
    • DESIRED hiển thị số replica mong muốn của ứng dụng, được bạn định nghĩa khi tạo Deployment. Đây là trạng thái mong muốn.
    • CURRENT hiển thị số replica đang chạy.
    • READY hiển thị số replica của ứng dụng đang sẵn sàng phục vụ người dùng.
    • AGE hiển thị khoảng thời gian ứng dụng đã chạy.

    Lưu ý rằng tên của ReplicaSet luôn có định dạng [DEPLOYMENT-NAME]-[HASH]. Tên này sẽ trở thành cơ sở để đặt tên cho các Pod được tạo ra.

    Chuỗi HASH giống với label pod-template-hash trên ReplicaSet.

  6. Để xem các label được tự động sinh ra cho mỗi Pod, chạy kubectl get pods --show-labels. Kết quả tương tự như sau:

    NAME                                READY     STATUS    RESTARTS   AGE       LABELS
    nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
    

    ReplicaSet được tạo đảm bảo rằng luôn có ba Pod nginx.

Lưu ý:

Bạn phải chỉ định một selector và label của Pod template phù hợp trong Deployment (trong trường hợp này là app: nginx).

Không để label hoặc selector trùng lặp với các controller khác (bao gồm các Deployment và StatefulSet khác). Kubernetes không ngăn bạn làm điều đó, và nếu nhiều controller có selector trùng lặp, các controller đó có thể xung đột và hoạt động không như mong đợi.

Label pod-template-hash

Chú ý:

Không được thay đổi label này.

Label pod-template-hash được Deployment controller thêm vào mỗi ReplicaSet mà một Deployment tạo ra hoặc tiếp nhận (adopt).

Label này đảm bảo rằng các ReplicaSet con của một Deployment không bị trùng lặp. Nó được sinh ra bằng cách băm PodTemplate của ReplicaSet và sử dụng giá trị băm thu được làm giá trị label, được thêm vào selector của ReplicaSet, label của Pod template, và vào mọi Pod hiện có mà ReplicaSet có thể đang quản lý.

Cập nhật một Deployment

Lưu ý:

Rollout của một Deployment được kích hoạt khi và chỉ khi Pod template của Deployment (tức là .spec.template) bị thay đổi, ví dụ khi label hoặc image container của template được cập nhật. Các cập nhật khác, chẳng hạn như scale Deployment, sẽ không kích hoạt rollout.

Làm theo các bước dưới đây để cập nhật Deployment của bạn:

  1. Hãy cập nhật các Pod nginx để sử dụng image nginx:1.16.1 thay cho image nginx:1.14.2.

    kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1
    

    hoặc sử dụng lệnh sau:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    trong đó deployment/nginx-deployment chỉ định Deployment, nginx chỉ định container sẽ được cập nhật và nginx:1.16.1 chỉ định image mới cùng tag của nó.

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment image updated
    

    Ngoài ra, bạn có thể edit Deployment và thay đổi .spec.template.spec.containers[0].image từ nginx:1.14.2 thành nginx:1.16.1:

    kubectl edit deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment edited
    
  2. Để xem trạng thái rollout, chạy:

    kubectl rollout status deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
    

    hoặc

    deployment "nginx-deployment" successfully rolled out
    

Xem thêm chi tiết về Deployment đã được cập nhật:

  • Sau khi rollout thành công, bạn có thể xem Deployment bằng cách chạy kubectl get deployments. Kết quả tương tự như sau:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           36s
    
  • Chạy kubectl get rs để thấy rằng Deployment đã cập nhật các Pod bằng cách tạo một ReplicaSet mới và scale nó lên 3 replica, đồng thời scale ReplicaSet cũ xuống 0 replica.

    kubectl get rs
    

    Kết quả tương tự như sau:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       6s
    nginx-deployment-2035384211   0         0         0       36s
    
  • Lúc này, chạy get pods sẽ chỉ hiển thị các Pod mới:

    kubectl get pods
    

    Kết quả tương tự như sau:

    NAME                                READY     STATUS    RESTARTS   AGE
    nginx-deployment-1564180365-khku8   1/1       Running   0          14s
    nginx-deployment-1564180365-nacti   1/1       Running   0          14s
    nginx-deployment-1564180365-z9gth   1/1       Running   0          14s
    

    Lần tiếp theo khi muốn cập nhật các Pod này, bạn chỉ cần cập nhật lại Pod template của Deployment.

    Deployment đảm bảo rằng chỉ một số lượng nhất định Pod bị ngừng hoạt động trong khi chúng đang được cập nhật. Theo mặc định, nó đảm bảo rằng ít nhất 75% số Pod mong muốn đang chạy (tối đa 25% không khả dụng - max unavailable).

    Deployment cũng đảm bảo rằng chỉ một số lượng nhất định Pod được tạo vượt quá số Pod mong muốn. Theo mặc định, nó đảm bảo rằng tối đa 125% số Pod mong muốn đang chạy (vượt tối đa 25% - max surge).

    Ví dụ, nếu quan sát kỹ Deployment ở trên, bạn sẽ thấy nó tạo một Pod mới trước, sau đó xóa một Pod cũ, rồi tạo thêm một Pod mới khác. Nó không dừng các Pod cũ cho đến khi có đủ số lượng Pod mới đã chạy, và không tạo Pod mới cho đến khi đủ số lượng Pod cũ đã bị dừng. Nó đảm bảo rằng ít nhất 3 Pod khả dụng và tổng cộng tối đa 4 Pod khả dụng. Với một Deployment có 4 replica, số lượng Pod sẽ nằm trong khoảng từ 3 đến 5.

  • Lấy thông tin chi tiết về Deployment của bạn:

    kubectl describe deployments
    

    Kết quả tương tự như sau:

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Thu, 30 Nov 2017 10:56:25 +0000
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=2
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
       Containers:
        nginx:
          Image:        nginx:1.16.1
          Port:         80/TCP
          Environment:  <none>
          Mounts:       <none>
        Volumes:        <none>
      Conditions:
        Type           Status  Reason
        ----           ------  ------
        Available      True    MinimumReplicasAvailable
        Progressing    True    NewReplicaSetAvailable
      OldReplicaSets:  <none>
      NewReplicaSet:   nginx-deployment-1564180365 (3/3 replicas created)
      Events:
        Type    Reason             Age   From                   Message
        ----    ------             ----  ----                   -------
        Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3
        Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2
        Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1
        Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
        Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0
    

    Ở đây bạn thấy rằng khi tạo Deployment lần đầu, nó đã tạo một ReplicaSet (nginx-deployment-2035384211) và scale trực tiếp lên 3 replica. Khi bạn cập nhật Deployment, nó tạo một ReplicaSet mới (nginx-deployment-1564180365), scale lên 1 và chờ Pod đó chạy. Sau đó nó scale ReplicaSet cũ xuống 2 và scale ReplicaSet mới lên 2, sao cho luôn có ít nhất 3 Pod khả dụng và tối đa 4 Pod được tạo tại mọi thời điểm. Sau đó nó tiếp tục scale up ReplicaSet mới và scale down ReplicaSet cũ, với cùng chiến lược rolling update. Cuối cùng, bạn sẽ có 3 replica khả dụng trong ReplicaSet mới, và ReplicaSet cũ được scale xuống 0.

Lưu ý:

Kubernetes không tính các Pod đang dừng khi tính số lượng availableReplicas, giá trị này phải nằm trong khoảng replicas - maxUnavailable đến replicas + maxSurge. Do đó, bạn có thể thấy có nhiều Pod hơn dự kiến trong quá trình rollout, và tổng tài nguyên mà Deployment tiêu thụ lớn hơn replicas + maxSurge cho đến khi terminationGracePeriodSeconds của các Pod đang dừng hết hạn.

Rollover (hay còn gọi là nhiều cập nhật diễn ra đồng thời)

Mỗi khi Deployment controller phát hiện một Deployment mới, một ReplicaSet sẽ được tạo để khởi chạy các Pod mong muốn. Nếu Deployment được cập nhật, ReplicaSet hiện có đang kiểm soát các Pod có label khớp với .spec.selector nhưng có template không khớp với .spec.template sẽ bị scale down. Cuối cùng, ReplicaSet mới được scale lên .spec.replicas và tất cả các ReplicaSet cũ được scale xuống 0.

Nếu bạn cập nhật một Deployment trong khi một rollout đang diễn ra, Deployment sẽ tạo một ReplicaSet mới theo bản cập nhật và bắt đầu scale up ReplicaSet đó, đồng thời rollover ReplicaSet mà nó đang scale up trước đó -- nó sẽ thêm ReplicaSet đó vào danh sách các ReplicaSet cũ và bắt đầu scale down nó.

Ví dụ, giả sử bạn tạo một Deployment để tạo 5 replica của nginx:1.14.2, nhưng sau đó cập nhật Deployment để tạo 5 replica của nginx:1.16.1, khi mới chỉ có 3 replica của nginx:1.14.2 được tạo. Trong trường hợp đó, Deployment ngay lập tức bắt đầu dừng 3 Pod nginx:1.14.2 mà nó đã tạo, và bắt đầu tạo các Pod nginx:1.16.1. Nó không đợi 5 replica của nginx:1.14.2 được tạo xong rồi mới chuyển hướng.

Cập nhật label selector

Nhìn chung, việc cập nhật label selector không được khuyến khích, và bạn nên lên kế hoạch cho các selector từ trước. Label selector của một Deployment là bất biến sau khi được tạo; không thể cập nhật nó bằng kubectl patch, kubectl edit, kubectl apply, hay các công cụ như helm upgrade.

Nếu buộc phải thay đổi selector, bạn cần xóa Deployment rồi tạo lại. Theo mặc định, việc xóa Deployment cũng xóa các Pod đang chạy của nó, gây ra downtime; hãy dùng --cascade=orphan nếu bạn cần các Pod đó tiếp tục chạy trong khi tạo lại Deployment (xem các hệ quả bên dưới). Hãy hết sức thận trọng và đảm bảo bạn hiểu rõ các hệ quả sau:

  • Thêm: Khi bạn tạo một Deployment mới với selector hẹp hơn, Deployment mới bắt buộc phải có Pod template phù hợp. Nếu bạn có một manifest sẵn có và sửa manifest đó để thu hẹp selector, bạn cần sửa metadata của Pod template bên trong Deployment đó, thêm các label mới cho khớp, nếu không API server sẽ trả về lỗi xác thực (validation error). Đây là một thay đổi không chồng lấn (non-overlapping): Deployment mới sẽ không "thấy" các Pod cũ (vốn không có label mới), khiến ReplicaSet cũ bị mồ côi (orphaned) và một ReplicaSet hoàn toàn mới được tạo ra.
  • Thay đổi giá trị: Thay đổi giá trị hiện có của một key trong selector (ví dụ, từ v1 thành v2) dẫn đến hành vi giống như khi thêm (ReplicaSet cũ bị mồ côi và được tạo lại).
  • Xóa: Xóa một key hiện có khỏi selector của Deployment không yêu cầu bất kỳ thay đổi nào đối với label của Pod template. Đây là một thay đổi chồng lấn (overlapping): selector mới, rộng hơn sẽ khớp với các Pod cũ. Các ReplicaSet hiện có không bị mồ côi, và không có ReplicaSet mới nào được tạo, nhưng lưu ý rằng label đã xóa vẫn tồn tại trong các Pod và ReplicaSet hiện có. Bạn có thể dọn dẹp điều đó bằng cách kích hoạt một rollout cho Deployment.

Rollback một Deployment

Đôi khi, bạn có thể muốn rollback một Deployment; ví dụ, khi Deployment không ổn định, chẳng hạn như bị crash liên tục (crash looping). Theo mặc định, toàn bộ lịch sử rollout của Deployment được lưu giữ trong hệ thống để bạn có thể rollback bất cứ lúc nào (bạn có thể thay đổi điều này bằng cách sửa giới hạn lịch sử revision - revision history limit).

Lưu ý:

Revision của một Deployment được tạo khi rollout của Deployment được kích hoạt. Điều này có nghĩa là revision mới được tạo khi và chỉ khi Pod template (.spec.template) của Deployment bị thay đổi, ví dụ khi bạn cập nhật label hoặc image container của template. Các cập nhật khác, chẳng hạn như scale Deployment, không tạo ra revision mới của Deployment, nhờ đó bạn có thể thực hiện đồng thời việc scale thủ công hoặc tự động. Điều này có nghĩa là khi bạn rollback về một revision trước đó, chỉ phần Pod template của Deployment được rollback.
  • Giả sử bạn gõ nhầm khi cập nhật Deployment, đặt tên image là nginx:1.161 thay vì nginx:1.16.1:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.161
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment image updated
    
  • Rollout bị kẹt. Bạn có thể xác minh điều này bằng cách kiểm tra trạng thái rollout:

    kubectl rollout status deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    Waiting for rollout to finish: 1 out of 3 new replicas have been updated...
    
  • Nhấn Ctrl-C để dừng theo dõi trạng thái rollout ở trên. Để biết thêm thông tin về các rollout bị kẹt, đọc thêm tại đây.

  • Bạn thấy rằng số replica cũ (cộng số replica từ nginx-deployment-1564180365 và nginx-deployment-2035384211) là 3, và số replica mới (từ nginx-deployment-3066724191) là 1.

    kubectl get rs
    

    Kết quả tương tự như sau:

    NAME                          DESIRED   CURRENT   READY   AGE
    nginx-deployment-1564180365   3         3         3       25s
    nginx-deployment-2035384211   0         0         0       36s
    nginx-deployment-3066724191   1         1         0       6s
    
  • Xem các Pod đã được tạo, bạn sẽ thấy 1 Pod được tạo bởi ReplicaSet mới bị kẹt trong vòng lặp kéo image (image pull loop).

    kubectl get pods
    

    Kết quả tương tự như sau:

    NAME                                READY     STATUS             RESTARTS   AGE
    nginx-deployment-1564180365-70iae   1/1       Running            0          25s
    nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
    nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
    nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s
    

    Lưu ý:

    Deployment controller tự động dừng rollout lỗi và ngừng scale up ReplicaSet mới. Điều này phụ thuộc vào các tham số rollingUpdate (cụ thể là maxUnavailable) mà bạn đã chỉ định. Theo mặc định, Kubernetes đặt giá trị này là 25%.
  • Lấy mô tả của Deployment:

    kubectl describe deployment
    

    Kết quả tương tự như sau:

    Name:           nginx-deployment
    Namespace:      default
    CreationTimestamp:  Tue, 15 Mar 2016 14:48:04 -0700
    Labels:         app=nginx
    Selector:       app=nginx
    Replicas:       3 desired | 1 updated | 4 total | 3 available | 1 unavailable
    StrategyType:       RollingUpdate
    MinReadySeconds:    0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.161
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    ReplicaSetUpdated
    OldReplicaSets:     nginx-deployment-1564180365 (3/3 replicas created)
    NewReplicaSet:      nginx-deployment-3066724191 (1/1 replicas created)
    Events:
      FirstSeen LastSeen    Count   From                    SubObjectPath   Type        Reason              Message
      --------- --------    -----   ----                    -------------   --------    ------              -------
      1m        1m          1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-2035384211 to 3
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 1
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 2
      22s       22s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 2
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 1
      21s       21s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-1564180365 to 3
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled down replica set nginx-deployment-2035384211 to 0
      13s       13s         1       {deployment-controller }                Normal      ScalingReplicaSet   Scaled up replica set nginx-deployment-3066724191 to 1
    

    Để khắc phục điều này, bạn cần rollback về một revision ổn định trước đó của Deployment.

Kiểm tra lịch sử rollout của một Deployment

Làm theo các bước dưới đây để kiểm tra lịch sử rollout:

  1. Đầu tiên, kiểm tra các revision của Deployment này:

    kubectl rollout history deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    deployments "nginx-deployment"
    REVISION    CHANGE-CAUSE
    1           <none>
    2           <none>
    3           <none>
    

    CHANGE-CAUSE được sao chép từ annotation kubernetes.io/change-cause của Deployment sang các revision của nó khi chúng được tạo. Bạn có thể chỉ định thông điệp CHANGE-CAUSE bằng cách:

    • Gắn annotation cho Deployment bằng kubectl annotate deployment/nginx-deployment kubernetes.io/change-cause="image updated to 1.16.1"
    • Sửa manifest của tài nguyên một cách thủ công.
    • Sử dụng công cụ tự động đặt annotation.

    Lưu ý:

    Trong các phiên bản Kubernetes cũ hơn, bạn có thể dùng cờ --record với các lệnh kubectl để tự động điền trường CHANGE-CAUSE. Cờ này không còn được hỗ trợ (deprecated) và sẽ bị loại bỏ trong một bản phát hành tương lai.
  2. Để xem chi tiết của từng revision, chạy:

    kubectl rollout history deployment/nginx-deployment --revision=2
    

    Kết quả tương tự như sau:

    deployments "nginx-deployment" revision 2
      Labels:       app=nginx
              pod-template-hash=1159050644
      Containers:
       nginx:
        Image:      nginx:1.16.1
        Port:       80/TCP
         QoS Tier:
            cpu:      BestEffort
            memory:   BestEffort
        Environment Variables:      <none>
      No volumes.
    

Rollback về revision trước đó

Làm theo các bước dưới đây để rollback Deployment từ phiên bản hiện tại về phiên bản trước đó, tức là phiên bản 2.

  1. Giả sử bây giờ bạn quyết định hoàn tác rollout hiện tại và rollback về revision trước đó:

    kubectl rollout undo deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment rolled back
    

    Ngoài ra, bạn có thể rollback về một revision cụ thể bằng cách chỉ định nó với --to-revision:

    kubectl rollout undo deployment/nginx-deployment --to-revision=2
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment rolled back
    

    Để biết thêm chi tiết về các lệnh liên quan đến rollout, đọc kubectl rollout.

    Deployment giờ đã được rollback về một revision ổn định trước đó. Như bạn thấy, một sự kiện DeploymentRollback cho việc rollback về revision 2 được tạo ra từ Deployment controller.

  2. Để kiểm tra xem việc rollback đã thành công và Deployment đang chạy như mong đợi hay chưa, chạy:

    kubectl get deployment nginx-deployment
    

    Kết quả tương tự như sau:

    NAME               READY   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment   3/3     3            3           30m
    
  3. Lấy mô tả của Deployment:

    kubectl describe deployment nginx-deployment
    

    Kết quả tương tự như sau:

    Name:                   nginx-deployment
    Namespace:              default
    CreationTimestamp:      Sun, 02 Sep 2018 18:17:55 -0500
    Labels:                 app=nginx
    Annotations:            deployment.kubernetes.io/revision=4
    Selector:               app=nginx
    Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
    StrategyType:           RollingUpdate
    MinReadySeconds:        0
    RollingUpdateStrategy:  25% max unavailable, 25% max surge
    Pod Template:
      Labels:  app=nginx
      Containers:
       nginx:
        Image:        nginx:1.16.1
        Port:         80/TCP
        Host Port:    0/TCP
        Environment:  <none>
        Mounts:       <none>
      Volumes:        <none>
    Conditions:
      Type           Status  Reason
      ----           ------  ------
      Available      True    MinimumReplicasAvailable
      Progressing    True    NewReplicaSetAvailable
    OldReplicaSets:  <none>
    NewReplicaSet:   nginx-deployment-c4747d96c (3/3 replicas created)
    Events:
      Type    Reason              Age   From                   Message
      ----    ------              ----  ----                   -------
      Normal  ScalingReplicaSet   12m   deployment-controller  Scaled up replica set nginx-deployment-75675f5897 to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 2
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 1
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-c4747d96c to 3
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled down replica set nginx-deployment-75675f5897 to 0
      Normal  ScalingReplicaSet   11m   deployment-controller  Scaled up replica set nginx-deployment-595696685f to 1
      Normal  DeploymentRollback  15s   deployment-controller  Rolled back deployment "nginx-deployment" to revision 2
      Normal  ScalingReplicaSet   15s   deployment-controller  Scaled down replica set nginx-deployment-595696685f to 0
    

Scale một Deployment

Bạn có thể scale một Deployment bằng lệnh sau:

kubectl scale deployment/nginx-deployment --replicas=10

Kết quả tương tự như sau:

deployment.apps/nginx-deployment scaled

Giả sử horizontal Pod autoscaling đã được bật trong cluster của bạn, bạn có thể thiết lập một autoscaler cho Deployment và chọn số lượng Pod tối thiểu và tối đa mà bạn muốn chạy dựa trên mức sử dụng CPU của các Pod hiện có.

kubectl autoscale deployment/nginx-deployment --min=10 --max=15 --cpu-percent=80%

Kết quả tương tự như sau:

deployment.apps/nginx-deployment scaled

Scale theo tỷ lệ

Các Deployment kiểu RollingUpdate hỗ trợ chạy nhiều phiên bản của một ứng dụng cùng một lúc. Khi bạn hoặc một autoscaler scale một Deployment RollingUpdate đang ở giữa quá trình rollout (đang diễn ra hoặc đang tạm dừng), Deployment controller sẽ phân bổ cân đối các replica bổ sung vào các ReplicaSet đang hoạt động (các ReplicaSet có Pod) để giảm thiểu rủi ro. Điều này được gọi là scale theo tỷ lệ (proportional scaling).

Ví dụ, bạn đang chạy một Deployment với 10 replica, maxSurge=3, và maxUnavailable=2.

  • Đảm bảo rằng 10 replica trong Deployment của bạn đang chạy.

    kubectl get deploy
    

    Kết quả tương tự như sau:

    NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx-deployment     10        10        10           10          50s
    
  • Bạn cập nhật sang một image mới mà không thể phân giải được từ bên trong cluster.

    kubectl set image deployment/nginx-deployment nginx=nginx:sometag
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment image updated
    
  • Việc cập nhật image khởi động một rollout mới với ReplicaSet nginx-deployment-1989198191, nhưng rollout này bị chặn do yêu cầu maxUnavailable mà bạn đã đề cập ở trên. Kiểm tra trạng thái rollout:

    kubectl get rs
    

    Kết quả tương tự như sau:

    NAME                          DESIRED   CURRENT   READY     AGE
    nginx-deployment-1989198191   5         5         0         9s
    nginx-deployment-618515232    8         8         8         1m
    
  • Sau đó, một yêu cầu scale mới cho Deployment xuất hiện. Autoscaler tăng số replica của Deployment lên 15. Deployment controller cần quyết định nơi để thêm 5 replica mới này. Nếu bạn không sử dụng scale theo tỷ lệ, cả 5 replica sẽ được thêm vào ReplicaSet mới. Với scale theo tỷ lệ, bạn phân bổ các replica bổ sung cho tất cả các ReplicaSet. Phần lớn hơn được chia cho các ReplicaSet có nhiều replica nhất và phần nhỏ hơn được chia cho các ReplicaSet có ít replica hơn. Phần dư sẽ được thêm vào ReplicaSet có nhiều replica nhất. Các ReplicaSet có 0 replica sẽ không được scale up.

Trong ví dụ trên, 3 replica được thêm vào ReplicaSet cũ và 2 replica được thêm vào ReplicaSet mới. Quá trình rollout cuối cùng sẽ chuyển tất cả các replica sang ReplicaSet mới, với giả định các replica mới hoạt động bình thường (healthy). Để xác nhận điều này, chạy:

kubectl get deploy

Kết quả tương tự như sau:

NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment     15        18        7            8           7m

Trạng thái rollout xác nhận cách các replica được thêm vào mỗi ReplicaSet.

kubectl get rs

Kết quả tương tự như sau:

NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-1989198191   7         7         0         7m
nginx-deployment-618515232    11        11        11        7m

Tạm dừng và tiếp tục rollout của một Deployment

Khi bạn cập nhật một Deployment, hoặc dự định cập nhật, bạn có thể tạm dừng các rollout của Deployment đó trước khi kích hoạt một hoặc nhiều cập nhật. Khi bạn đã sẵn sàng áp dụng các thay đổi đó, bạn tiếp tục các rollout cho Deployment. Cách làm này cho phép bạn áp dụng nhiều bản sửa lỗi trong khoảng thời gian giữa lúc tạm dừng và tiếp tục mà không kích hoạt các rollout không cần thiết.

  • Ví dụ, với một Deployment vừa được tạo:

    Lấy thông tin chi tiết của Deployment:

    kubectl get deploy
    

    Kết quả tương tự như sau:

    NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
    nginx     3         3         3            3           1m
    

    Lấy trạng thái rollout:

    kubectl get rs
    

    Kết quả tương tự như sau:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         1m
    
  • Tạm dừng bằng cách chạy lệnh sau:

    kubectl rollout pause deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment paused
    
  • Sau đó cập nhật image của Deployment:

    kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment image updated
    
  • Lưu ý rằng không có rollout mới nào được bắt đầu:

    kubectl rollout history deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    deployments "nginx"
    REVISION  CHANGE-CAUSE
    1   <none>
    
  • Lấy trạng thái rollout để xác minh rằng ReplicaSet hiện có không thay đổi:

    kubectl get rs
    

    Kết quả tương tự như sau:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   3         3         3         2m
    
  • Bạn có thể thực hiện bao nhiêu cập nhật tùy thích, ví dụ, cập nhật các tài nguyên sẽ được sử dụng:

    kubectl set resources deployment/nginx-deployment -c=nginx --limits=cpu=200m,memory=512Mi
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment resource requirements updated
    

    Trạng thái ban đầu của Deployment trước khi tạm dừng rollout vẫn tiếp tục hoạt động, nhưng các cập nhật mới cho Deployment sẽ không có tác dụng chừng nào rollout của Deployment còn đang bị tạm dừng.

  • Cuối cùng, tiếp tục rollout của Deployment và quan sát một ReplicaSet mới được tạo ra với tất cả các cập nhật mới:

    kubectl rollout resume deployment/nginx-deployment
    

    Kết quả tương tự như sau:

    deployment.apps/nginx-deployment resumed
    
  • Theo dõi (watch) trạng thái của rollout cho đến khi hoàn tất.

    kubectl get rs --watch
    

    Kết quả tương tự như sau:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   2         2         2         2m
    nginx-3926361531   2         2         0         6s
    nginx-3926361531   2         2         1         18s
    nginx-2142116321   1         2         2         2m
    nginx-2142116321   1         2         2         2m
    nginx-3926361531   3         2         1         18s
    nginx-3926361531   3         2         1         18s
    nginx-2142116321   1         1         1         2m
    nginx-3926361531   3         3         1         18s
    nginx-3926361531   3         3         2         19s
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         1         1         2m
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         20s
    
  • Lấy trạng thái của rollout mới nhất:

    kubectl get rs
    

    Kết quả tương tự như sau:

    NAME               DESIRED   CURRENT   READY     AGE
    nginx-2142116321   0         0         0         2m
    nginx-3926361531   3         3         3         28s
    

Lưu ý:

Bạn không thể rollback một Deployment đang bị tạm dừng cho đến khi bạn tiếp tục (resume) nó.

Trạng thái của Deployment

Một Deployment trải qua nhiều trạng thái khác nhau trong vòng đời của nó. Nó có thể đang tiến triển trong khi rollout một ReplicaSet mới, có thể hoàn tất, hoặc có thể không tiến triển được.

Deployment đang tiến triển

Kubernetes đánh dấu một Deployment là đang tiến triển (progressing) khi một trong các tác vụ sau được thực hiện:

  • Deployment tạo một ReplicaSet mới.
  • Deployment đang scale up ReplicaSet mới nhất của nó.
  • Deployment đang scale down (các) ReplicaSet cũ hơn của nó.
  • Các Pod mới trở nên ready hoặc available (ready trong ít nhất MinReadySeconds).

Khi rollout chuyển sang trạng thái “progressing”, Deployment controller thêm một condition với các thuộc tính sau vào .status.conditions của Deployment:

  • type: Progressing
  • status: "True"
  • reason: NewReplicaSetCreated | reason: FoundNewReplicaSet | reason: ReplicaSetUpdated

Bạn có thể theo dõi tiến trình của một Deployment bằng cách sử dụng kubectl rollout status.

Deployment hoàn tất

Kubernetes đánh dấu một Deployment là hoàn tất (complete) khi nó có các đặc điểm sau:

  • Tất cả các replica liên kết với Deployment đã được cập nhật lên phiên bản mới nhất mà bạn chỉ định, nghĩa là mọi cập nhật bạn yêu cầu đều đã được hoàn thành.
  • Tất cả các replica liên kết với Deployment đều sẵn sàng (available).
  • Không có replica cũ nào của Deployment đang chạy.

Khi rollout chuyển sang trạng thái “complete”, Deployment controller đặt một condition với các thuộc tính sau vào .status.conditions của Deployment:

  • type: Progressing
  • status: "True"
  • reason: NewReplicaSetAvailable

Condition Progressing này sẽ giữ giá trị trạng thái "True" cho đến khi một rollout mới được khởi tạo. Condition này vẫn giữ nguyên ngay cả khi tính khả dụng của các replica thay đổi (điều này thay vào đó sẽ ảnh hưởng đến condition Available).

Bạn có thể kiểm tra xem một Deployment đã hoàn tất hay chưa bằng cách sử dụng kubectl rollout status. Nếu rollout hoàn tất thành công, kubectl rollout status trả về exit code bằng 0.

kubectl rollout status deployment/nginx-deployment

Kết quả tương tự như sau:

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx-deployment" successfully rolled out

và exit status của kubectl rollout là 0 (thành công):

echo $?
0

Deployment thất bại

Deployment của bạn có thể bị kẹt khi cố gắng triển khai ReplicaSet mới nhất mà không bao giờ hoàn tất. Điều này có thể xảy ra do một số yếu tố sau:

  • Không đủ quota
  • Readiness probe thất bại
  • Lỗi khi kéo image (image pull)
  • Không đủ quyền
  • Limit range
  • Cấu hình runtime của ứng dụng bị sai

Một cách để phát hiện tình trạng này là chỉ định một tham số thời hạn (deadline) trong spec của Deployment: (.spec.progressDeadlineSeconds). .spec.progressDeadlineSeconds biểu thị số giây mà Deployment controller chờ trước khi báo hiệu (trong trạng thái của Deployment) rằng tiến trình của Deployment đã bị đình trệ.

Lệnh kubectl sau đây thiết lập spec với progressDeadlineSeconds để khiến controller báo cáo việc rollout của Deployment không có tiến triển sau 10 phút:

kubectl patch deployment/nginx-deployment -p '{"spec":{"progressDeadlineSeconds":600}}'

Kết quả tương tự như sau:

deployment.apps/nginx-deployment patched

Khi vượt quá thời hạn, Deployment controller sẽ thêm một DeploymentCondition với các thuộc tính sau vào .status.conditions của Deployment:

  • type: Progressing
  • status: "False"
  • reason: ProgressDeadlineExceeded

Condition này cũng có thể thất bại sớm và khi đó được đặt giá trị trạng thái "False" vì các lý do như ReplicaSetCreateError. Ngoài ra, thời hạn sẽ không còn được tính đến khi rollout của Deployment đã hoàn tất.

Xem Kubernetes API conventions để biết thêm thông tin về các status condition.

Lưu ý:

Kubernetes không thực hiện bất kỳ hành động nào đối với một Deployment bị đình trệ ngoài việc báo cáo một status condition với reason: ProgressDeadlineExceeded. Các orchestrator cấp cao hơn có thể tận dụng điều này và hành động tương ứng, ví dụ như rollback Deployment về phiên bản trước đó.

Lưu ý:

Nếu bạn tạm dừng rollout của một Deployment, Kubernetes không kiểm tra tiến trình so với thời hạn bạn đã chỉ định. Bạn có thể an toàn tạm dừng rollout của một Deployment giữa chừng rồi tiếp tục mà không kích hoạt condition vượt quá thời hạn.

Bạn có thể gặp phải các lỗi tạm thời với Deployment, do bạn đặt timeout quá thấp hoặc do bất kỳ loại lỗi nào khác có thể được coi là tạm thời. Ví dụ, giả sử bạn không đủ quota. Nếu bạn describe Deployment, bạn sẽ thấy phần sau:

kubectl describe deployment nginx-deployment

Kết quả tương tự như sau:

<...>
Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     True    ReplicaSetUpdated
  ReplicaFailure  True    FailedCreate
<...>

Nếu bạn chạy kubectl get deployment nginx-deployment -o yaml, trạng thái của Deployment tương tự như sau:

status:
  availableReplicas: 2
  conditions:
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: Replica set "nginx-deployment-4262182780" is progressing.
    reason: ReplicaSetUpdated
    status: "True"
    type: Progressing
  - lastTransitionTime: 2016-10-04T12:25:42Z
    lastUpdateTime: 2016-10-04T12:25:42Z
    message: Deployment has minimum availability.
    reason: MinimumReplicasAvailable
    status: "True"
    type: Available
  - lastTransitionTime: 2016-10-04T12:25:39Z
    lastUpdateTime: 2016-10-04T12:25:39Z
    message: 'Error creating: pods "nginx-deployment-4262182780-" is forbidden: exceeded quota:
      object-counts, requested: pods=1, used: pods=3, limited: pods=2'
    reason: FailedCreate
    status: "True"
    type: ReplicaFailure
  observedGeneration: 3
  replicas: 2
  unavailableReplicas: 2

Cuối cùng, khi vượt quá thời hạn tiến trình của Deployment, Kubernetes sẽ cập nhật trạng thái và lý do (reason) của condition Progressing:

Conditions:
  Type            Status  Reason
  ----            ------  ------
  Available       True    MinimumReplicasAvailable
  Progressing     False   ProgressDeadlineExceeded
  ReplicaFailure  True    FailedCreate

Bạn có thể giải quyết vấn đề thiếu quota bằng cách scale down Deployment, scale down các controller khác mà bạn đang chạy, hoặc tăng quota trong namespace của bạn. Nếu bạn đáp ứng các điều kiện về quota và sau đó Deployment controller hoàn tất rollout của Deployment, bạn sẽ thấy trạng thái của Deployment được cập nhật với một condition thành công (status: "True" và reason: NewReplicaSetAvailable).

Conditions:
  Type          Status  Reason
  ----          ------  ------
  Available     True    MinimumReplicasAvailable
  Progressing   True    NewReplicaSetAvailable

type: Available với status: "True" nghĩa là Deployment của bạn đạt mức khả dụng tối thiểu (minimum availability). Mức khả dụng tối thiểu được quyết định bởi các tham số được chỉ định trong chiến lược triển khai (deployment strategy). type: Progressing với status: "True" nghĩa là Deployment của bạn hoặc đang ở giữa quá trình rollout và đang tiến triển, hoặc đã hoàn tất tiến trình thành công và số lượng replica mới tối thiểu cần thiết đã sẵn sàng (xem Reason của condition để biết chi tiết - trong trường hợp của chúng ta, reason: NewReplicaSetAvailable nghĩa là Deployment đã hoàn tất).

Bạn có thể kiểm tra xem một Deployment có bị thất bại trong việc tiến triển hay không bằng cách sử dụng kubectl rollout status. kubectl rollout status trả về exit code khác 0 nếu Deployment đã vượt quá thời hạn tiến trình.

kubectl rollout status deployment/nginx-deployment

Kết quả tương tự như sau:

Waiting for rollout to finish: 2 out of 3 new replicas have been updated...
error: deployment "nginx" exceeded its progress deadline

và exit status của kubectl rollout là 1 (cho biết có lỗi):

echo $?
1

Thao tác trên một Deployment thất bại

Tất cả các thao tác áp dụng cho một Deployment hoàn tất cũng áp dụng cho một Deployment thất bại. Bạn có thể scale up/down, rollback về một revision trước đó, hoặc thậm chí tạm dừng nó nếu bạn cần áp dụng nhiều điều chỉnh cho Pod template của Deployment.

Chính sách dọn dẹp

Bạn có thể đặt trường .spec.revisionHistoryLimit trong một Deployment để chỉ định số lượng ReplicaSet cũ của Deployment này mà bạn muốn giữ lại. Phần còn lại sẽ được thu gom rác (garbage-collected) ở chế độ nền. Theo mặc định, giá trị này là 10.

Lưu ý:

Việc đặt tường minh trường này bằng 0 sẽ dẫn đến việc dọn sạch toàn bộ lịch sử của Deployment, do đó Deployment đó sẽ không thể rollback.

Việc dọn dẹp chỉ bắt đầu sau khi Deployment đạt trạng thái hoàn tất. Nếu bạn đặt .spec.revisionHistoryLimit bằng 0, mọi rollout vẫn kích hoạt việc tạo một ReplicaSet mới trước khi Kubernetes xóa ReplicaSet cũ.

Ngay cả khi giới hạn lịch sử revision khác 0, bạn vẫn có thể có nhiều ReplicaSet hơn giới hạn mà bạn cấu hình. Ví dụ, nếu các Pod bị crash liên tục (crash looping) và có nhiều sự kiện rolling update được kích hoạt theo thời gian, bạn có thể có nhiều ReplicaSet hơn .spec.revisionHistoryLimit vì Deployment không bao giờ đạt trạng thái hoàn tất.

Canary deployment

Nếu bạn muốn rollout các bản phát hành (release) cho một nhóm nhỏ người dùng hoặc máy chủ bằng Deployment, bạn có thể tạo nhiều Deployment, mỗi Deployment tương ứng với một bản phát hành, theo mô hình canary được mô tả trong quản lý tài nguyên.

Để tìm hiểu chi tiết, hãy làm theo hướng dẫn Triển khai một bản phát hành bằng Canary Deployment.

Viết spec cho Deployment

Giống như mọi cấu hình Kubernetes khác, một Deployment cần các trường .apiVersion, .kind và .metadata. Để biết thông tin chung về cách làm việc với các file cấu hình, xem triển khai ứng dụng, cấu hình container, và sử dụng kubectl để quản lý tài nguyên.

Khi control plane tạo các Pod mới cho một Deployment, .metadata.name của Deployment là một phần cơ sở để đặt tên cho các Pod đó. Tên của Deployment phải là một giá trị DNS subdomain hợp lệ, nhưng điều này có thể gây ra kết quả không mong muốn cho hostname của Pod. Để tương thích tốt nhất, tên nên tuân theo các quy tắc chặt chẽ hơn của một DNS label.

Một Deployment cũng cần có phần .spec.

Pod Template

.spec.template và .spec.selector là các trường bắt buộc duy nhất của .spec.

.spec.template là một Pod template. Nó có schema giống hệt một Pod, ngoại trừ việc nó được lồng bên trong và không có apiVersion hay kind.

Ngoài các trường bắt buộc của một Pod, Pod template trong một Deployment phải chỉ định các label và một restart policy phù hợp. Với label, hãy đảm bảo không trùng lặp với các controller khác. Xem selector.

Chỉ cho phép .spec.template.spec.restartPolicy bằng Always, đây cũng là giá trị mặc định nếu không được chỉ định.

Replicas

.spec.replicas là một trường tùy chọn, chỉ định số lượng Pod mong muốn. Giá trị mặc định là 1.

Nếu bạn scale một Deployment thủ công, ví dụ qua kubectl scale deployment deployment --replicas=X, rồi sau đó cập nhật Deployment đó dựa trên một manifest (ví dụ: bằng cách chạy kubectl apply -f deployment.yaml), thì việc áp dụng manifest đó sẽ ghi đè lên việc scale thủ công mà bạn đã thực hiện trước đó.

Nếu một HorizontalPodAutoscaler (hoặc bất kỳ API tương tự nào cho horizontal scaling) đang quản lý việc scale cho một Deployment, đừng đặt .spec.replicas.

Thay vào đó, hãy để control plane của Kubernetes tự động quản lý trường .spec.replicas.

Selector

.spec.selector là một trường bắt buộc, chỉ định một label selector cho các Pod mà Deployment này nhắm tới.

.spec.selector phải khớp với .spec.template.metadata.labels, nếu không sẽ bị API từ chối.

Trong phiên bản API apps/v1, .spec.selector và .metadata.labels không mặc định lấy giá trị của .spec.template.metadata.labels nếu không được đặt. Vì vậy chúng phải được đặt một cách tường minh. Cũng lưu ý rằng trong apps/v1, .spec.selector là bất biến sau khi Deployment được tạo.

Một Deployment có thể dừng (terminate) các Pod có label khớp với selector nếu template của chúng khác với .spec.template hoặc nếu tổng số lượng các Pod như vậy vượt quá .spec.replicas. Nó sẽ khởi chạy các Pod mới với .spec.template nếu số lượng Pod ít hơn số lượng mong muốn.

Lưu ý:

Bạn không nên tạo các Pod khác có label khớp với selector này, dù là trực tiếp, bằng cách tạo một Deployment khác, hay bằng cách tạo một controller khác như ReplicaSet hoặc ReplicationController. Nếu bạn làm vậy, Deployment đầu tiên sẽ cho rằng chính nó đã tạo ra các Pod khác này. Kubernetes không ngăn bạn làm điều này.

Nếu bạn có nhiều controller có selector trùng lặp nhau, các controller đó sẽ tranh chấp với nhau và sẽ không hoạt động đúng.

Strategy

.spec.strategy chỉ định chiến lược được sử dụng để thay thế các Pod cũ bằng các Pod mới. .spec.strategy.type có thể là "Recreate" hoặc "RollingUpdate". "RollingUpdate" là giá trị mặc định.

Recreate Deployment

Khi .spec.strategy.type==Recreate, tất cả các Pod hiện có sẽ bị dừng trước khi các Pod mới được tạo.

Lưu ý:

Điều này chỉ đảm bảo các Pod bị dừng trước khi tạo Pod mới đối với các lần nâng cấp. Nếu bạn nâng cấp một Deployment, tất cả các Pod của revision cũ sẽ bị dừng ngay lập tức. Hệ thống chờ việc xóa thành công rồi mới tạo bất kỳ Pod nào của revision mới. Nếu bạn xóa thủ công một Pod, vòng đời của nó do ReplicaSet kiểm soát và Pod thay thế sẽ được tạo ngay lập tức (ngay cả khi Pod cũ vẫn đang ở trạng thái Terminating). Nếu bạn cần đảm bảo "tối đa" (at most) cho các Pod của mình, bạn nên cân nhắc sử dụng một StatefulSet.

Rolling Update Deployment

Deployment cập nhật các Pod theo kiểu rolling update (scale down dần các ReplicaSet cũ và scale up ReplicaSet mới) khi .spec.strategy.type==RollingUpdate. Bạn có thể chỉ định maxUnavailable và maxSurge để kiểm soát quá trình rolling update.

Max Unavailable

.spec.strategy.rollingUpdate.maxUnavailable là một trường tùy chọn, chỉ định số lượng Pod tối đa có thể không khả dụng (unavailable) trong quá trình cập nhật. Giá trị có thể là một số tuyệt đối (ví dụ: 5) hoặc tỷ lệ phần trăm của số Pod mong muốn (ví dụ: 10%). Số tuyệt đối được tính từ tỷ lệ phần trăm bằng cách làm tròn xuống. Giá trị này không thể bằng 0 nếu .spec.strategy.rollingUpdate.maxSurge bằng 0. Giá trị mặc định là 25%.

Ví dụ, khi giá trị này được đặt là 30%, ReplicaSet cũ có thể được scale down xuống còn 70% số Pod mong muốn ngay khi rolling update bắt đầu. Khi các Pod mới đã sẵn sàng, ReplicaSet cũ có thể được scale down thêm, tiếp theo là scale up ReplicaSet mới, đảm bảo rằng tổng số Pod khả dụng tại mọi thời điểm trong quá trình cập nhật luôn ít nhất bằng 70% số Pod mong muốn.

Max Surge

.spec.strategy.rollingUpdate.maxSurge là một trường tùy chọn, chỉ định số lượng Pod tối đa có thể được tạo vượt quá số lượng Pod mong muốn. Giá trị có thể là một số tuyệt đối (ví dụ: 5) hoặc tỷ lệ phần trăm của số Pod mong muốn (ví dụ: 10%). Giá trị này không thể bằng 0 nếu maxUnavailable bằng 0. Số tuyệt đối được tính từ tỷ lệ phần trăm bằng cách làm tròn lên. Giá trị mặc định là 25%.

Ví dụ, khi giá trị này được đặt là 30%, ReplicaSet mới có thể được scale up ngay khi rolling update bắt đầu, sao cho tổng số Pod cũ và mới không vượt quá 130% số Pod mong muốn. Khi các Pod cũ đã bị dừng, ReplicaSet mới có thể được scale up thêm, đảm bảo rằng tổng số Pod đang chạy tại mọi thời điểm trong quá trình cập nhật tối đa là 130% số Pod mong muốn.

Dưới đây là một số ví dụ về Rolling Update Deployment sử dụng maxUnavailable và maxSurge:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 labels:
   app: nginx
spec:
 replicas: 3
 selector:
   matchLabels:
     app: nginx
 template:
   metadata:
     labels:
       app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx:1.14.2
       ports:
       - containerPort: 80
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxUnavailable: 1
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 labels:
   app: nginx
spec:
 replicas: 3
 selector:
   matchLabels:
     app: nginx
 template:
   metadata:
     labels:
       app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx:1.14.2
       ports:
       - containerPort: 80
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxSurge: 1
apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-deployment
 labels:
   app: nginx
spec:
 replicas: 3
 selector:
   matchLabels:
     app: nginx
 template:
   metadata:
     labels:
       app: nginx
   spec:
     containers:
     - name: nginx
       image: nginx:1.14.2
       ports:
       - containerPort: 80
 strategy:
   type: RollingUpdate
   rollingUpdate:
     maxSurge: 1
     maxUnavailable: 1

Progress Deadline Seconds

.spec.progressDeadlineSeconds là một trường tùy chọn, chỉ định số giây bạn muốn chờ Deployment tiến triển trước khi hệ thống báo cáo rằng Deployment đã không tiến triển được - được thể hiện dưới dạng một condition với type: Progressing, status: "False". và reason: ProgressDeadlineExceeded trong trạng thái của tài nguyên. Deployment controller sẽ tiếp tục thử lại Deployment. Giá trị mặc định là 600.

Nếu được chỉ định, trường này cần lớn hơn .spec.minReadySeconds.

Min Ready Seconds

.spec.minReadySeconds là một trường tùy chọn, chỉ định số giây tối thiểu mà một Pod mới được tạo phải ở trạng thái ready mà không có container nào bị crash, để được coi là available (khả dụng). Giá trị mặc định là 0 (Pod sẽ được coi là available ngay khi nó ready). Để tìm hiểu thêm về thời điểm một Pod được coi là ready, xem Container Probes.

Các Pod đang dừng (terminating)

TRẠNG THÁI TÍNH NĂNG: Beta since Kubernetes v1.35; enabled by default

Bạn chỉ có thể thấy các Pod đang dừng nếu DeploymentReplicaSetTerminatingReplicas feature gate được bật trên API server và trên kube-controller-manager

Các Pod chuyển sang trạng thái đang dừng do bị xóa hoặc do scale down có thể mất nhiều thời gian để dừng hẳn, và có thể tiêu tốn thêm tài nguyên trong khoảng thời gian đó. Do đó, tổng số Pod có thể tạm thời vượt quá .spec.replicas. Bạn có thể theo dõi các Pod đang dừng bằng trường .status.terminatingReplicas của Deployment.

Revision History Limit

Lịch sử revision của một Deployment được lưu trữ trong các ReplicaSet mà nó kiểm soát.

.spec.revisionHistoryLimit là một trường tùy chọn, chỉ định số lượng ReplicaSet cũ được giữ lại để cho phép rollback. Các ReplicaSet cũ này tiêu tốn tài nguyên trong etcd và làm rối kết quả của kubectl get rs. Cấu hình của mỗi revision Deployment được lưu trữ trong các ReplicaSet của nó; do đó, một khi ReplicaSet cũ bị xóa, bạn sẽ mất khả năng rollback về revision đó của Deployment. Theo mặc định, 10 ReplicaSet cũ sẽ được giữ lại, tuy nhiên giá trị lý tưởng của trường này phụ thuộc vào tần suất và độ ổn định của các Deployment mới.

Cụ thể hơn, việc đặt trường này bằng 0 có nghĩa là tất cả các ReplicaSet cũ có 0 replica sẽ bị dọn dẹp. Trong trường hợp này, không thể hoàn tác (undo) một rollout mới của Deployment, vì lịch sử revision của nó đã bị dọn dẹp.

Paused

.spec.paused là một trường boolean tùy chọn dùng để tạm dừng và tiếp tục một Deployment. Điểm khác biệt duy nhất giữa một Deployment đang tạm dừng và một Deployment không bị tạm dừng là mọi thay đổi đối với PodTemplateSpec của Deployment đang tạm dừng sẽ không kích hoạt rollout mới chừng nào nó còn đang bị tạm dừng. Theo mặc định, một Deployment không bị tạm dừng khi được tạo.

Tiếp theo là gì

2 - Service

Cung cấp một ứng dụng đang chạy trong cluster của bạn qua một endpoint duy nhất hướng ra bên ngoài, ngay cả khi workload được chia thành nhiều backend.

Trong Kubernetes, Service là một cách để thể hiện ứng dụng đang chạy trong một tập các Pods dưới dạng dịch vụ mạng.

Một mục tiêu chính của Service trong Kubernetes là bạn không cần sửa đổi ứng dụng hiện có để dùng một cơ chế service discovery xa lạ. Bạn có thể chạy code trong các Pod, dù đó là code được thiết kế cho thế giới cloud-native hay một ứng dụng cũ mà bạn đã container hóa. Bạn dùng Service để cung cấp tập Pod đó trên mạng, nhờ vậy các client có thể tương tác với nó.

Nếu bạn dùng một Deployment để chạy ứng dụng, Deployment đó có thể tạo và hủy các Pod một cách linh hoạt. Từ thời điểm này sang thời điểm khác, bạn không biết có bao nhiêu Pod trong số đó đang hoạt động và khỏe mạnh; thậm chí bạn có thể không biết tên của các Pod khỏe mạnh đó. Các Pod trong Kubernetes được tạo và hủy để khớp với trạng thái mong muốn của cluster. Pod là tài nguyên tạm thời (bạn không nên kỳ vọng rằng một Pod riêng lẻ là đáng tin cậy và bền vững).

Mỗi Pod có địa chỉ IP riêng (Kubernetes yêu cầu các plugin mạng đảm bảo điều này). Với một Deployment nhất định trong cluster, tập Pod đang chạy tại một thời điểm có thể khác với tập Pod chạy ứng dụng đó ngay sau đó.

Điều này dẫn đến một vấn đề: nếu một tập Pod (gọi là "backend") cung cấp chức năng cho các Pod khác (gọi là "frontend") bên trong cluster, làm thế nào để các frontend tìm ra và theo dõi được địa chỉ IP cần kết nối tới, để frontend có thể sử dụng phần backend của workload?

Đó là lúc Service xuất hiện.

Service trong Kubernetes

Service API, một phần của Kubernetes, là một lớp trừu tượng giúp bạn cung cấp các nhóm Pod qua mạng. Mỗi đối tượng Service định nghĩa một tập logic các endpoint (thường các endpoint này là Pod) cùng với một chính sách về cách cho phép truy cập các pod đó.

Ví dụ, hãy xem xét một backend xử lý hình ảnh stateless đang chạy với 3 bản sao. Các bản sao đó có thể thay thế cho nhau—frontend không quan tâm chúng dùng backend nào. Mặc dù các Pod thực tế tạo nên tập backend có thể thay đổi, các client frontend không cần biết điều đó, cũng không cần tự theo dõi tập backend.

Lớp trừu tượng Service cho phép sự tách rời (decoupling) này.

Canary deployment là một trường hợp sử dụng phổ biến của Service. Để tìm hiểu những gì cần làm, hãy làm theo hướng dẫn Triển khai một bản phát hành bằng Canary Deployment.

Tập Pod mà một Service nhắm tới thường được xác định bởi một selector mà bạn định nghĩa. Để tìm hiểu các cách khác để định nghĩa endpoint cho Service, xem Service không có selector.

Nếu workload của bạn giao tiếp qua HTTP, bạn có thể chọn dùng một Ingress để kiểm soát cách lưu lượng web đến được workload đó. Ingress không phải là một loại Service, nhưng nó đóng vai trò điểm vào cho cluster của bạn. Ingress cho phép bạn gom các quy tắc định tuyến vào một tài nguyên duy nhất, để bạn có thể cung cấp nhiều thành phần của workload, chạy riêng biệt trong cluster, phía sau một listener duy nhất.

API Gateway dành cho Kubernetes cung cấp thêm các khả năng ngoài Ingress và Service. Bạn có thể thêm Gateway vào cluster - đây là một họ các API mở rộng, được triển khai bằng CustomResourceDefinition - rồi dùng chúng để cấu hình quyền truy cập tới các dịch vụ mạng đang chạy trong cluster.

Service discovery theo kiểu cloud-native

Nếu ứng dụng của bạn có thể dùng Kubernetes API cho service discovery, bạn có thể truy vấn API server để lấy các EndpointSlice tương ứng. Kubernetes cập nhật các EndpointSlice của một Service mỗi khi tập Pod trong Service đó thay đổi.

Với các ứng dụng không phải native, Kubernetes cung cấp các cách để đặt một cổng mạng hoặc bộ cân bằng tải (load balancer) giữa ứng dụng của bạn và các Pod backend.

Dù bằng cách nào, workload của bạn đều có thể dùng các cơ chế service discovery này để tìm đích mà nó muốn kết nối tới.

Định nghĩa một Service

Service là một đối tượng (giống như Pod hay ConfigMap là đối tượng). Bạn có thể tạo, xem hoặc sửa đổi định nghĩa Service bằng Kubernetes API. Thông thường bạn dùng một công cụ như kubectl để thực hiện các lệnh gọi API đó thay bạn.

Ví dụ, giả sử bạn có một tập Pod, mỗi Pod lắng nghe trên cổng TCP 9376 và được gắn label app.kubernetes.io/name=MyApp. Bạn có thể định nghĩa một Service để công bố listener TCP đó:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376

Áp dụng manifest này sẽ tạo một Service mới tên là "my-service" với loại Service mặc định là ClusterIP. Service này nhắm tới cổng TCP 9376 trên bất kỳ Pod nào có label app.kubernetes.io/name: MyApp.

Kubernetes gán cho Service này một địa chỉ IP (cluster IP), mà cơ chế địa chỉ IP ảo sử dụng. Để biết thêm chi tiết về cơ chế đó, hãy đọc IP ảo và Service Proxy.

Controller của Service đó liên tục quét tìm các Pod khớp với selector của nó, rồi thực hiện mọi cập nhật cần thiết cho tập EndpointSlice của Service.

Tên của một đối tượng Service phải là một tên label RFC 1123 hợp lệ.

Lưu ý:

Một Service có thể ánh xạ bất kỳ port đến nào tới một targetPort. Theo mặc định và để thuận tiện, targetPort được đặt cùng giá trị với trường port.

Định nghĩa cổng

Định nghĩa cổng trong Pod có tên, và bạn có thể tham chiếu các tên này trong thuộc tính targetPort của một Service. Ví dụ, ta có thể gắn targetPort của Service với cổng của Pod theo cách sau:

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app.kubernetes.io/name: proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc

---
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc

Cách này vẫn hoạt động ngay cả khi Service có một tập Pod hỗn hợp dùng cùng một tên đã cấu hình, với cùng một giao thức mạng nhưng ở các số cổng khác nhau. Điều này mang lại nhiều sự linh hoạt khi triển khai và phát triển các Service của bạn. Ví dụ, bạn có thể thay đổi số cổng mà các Pod cung cấp trong phiên bản tiếp theo của phần mềm backend mà không làm gián đoạn client.

Giao thức mặc định của Service là TCP; bạn cũng có thể dùng bất kỳ giao thức được hỗ trợ nào khác.

Vì nhiều Service cần cung cấp nhiều hơn một cổng, Kubernetes hỗ trợ nhiều định nghĩa cổng cho một Service. Mỗi định nghĩa cổng có thể có cùng protocol, hoặc khác nhau.

Service không có selector

Service thường trừu tượng hóa việc truy cập tới các Pod Kubernetes nhờ selector, nhưng khi được dùng cùng một tập đối tượng EndpointSlice tương ứng và không có selector, Service có thể trừu tượng hóa các loại backend khác, kể cả những backend chạy bên ngoài cluster.

Ví dụ:

  • Bạn muốn có một cluster cơ sở dữ liệu bên ngoài trong môi trường production, nhưng trong môi trường test bạn dùng các cơ sở dữ liệu của riêng mình.
  • Bạn muốn trỏ Service của mình tới một Service trong một Namespace khác hoặc trên một cluster khác.
  • Bạn đang chuyển (migrate) một workload sang Kubernetes. Trong lúc đánh giá cách tiếp cận, bạn chỉ chạy một phần backend trong Kubernetes.

Trong bất kỳ trường hợp nào ở trên, bạn đều có thể định nghĩa một Service mà không chỉ định selector để khớp với Pod. Ví dụ:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376

Vì Service này không có selector, các đối tượng EndpointSlice tương ứng sẽ không được tạo tự động. Bạn có thể ánh xạ Service tới địa chỉ mạng và cổng nơi nó đang chạy bằng cách thêm thủ công một đối tượng EndpointSlice. Ví dụ:

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: my-service-1 # theo quy ước, dùng tên của Service
                     # làm tiền tố cho tên của EndpointSlice
  labels:
    # Bạn nên đặt label "kubernetes.io/service-name".
    # Đặt giá trị của nó khớp với tên của Service
    kubernetes.io/service-name: my-service
addressType: IPv4
ports:
  - name: http # nên khớp với tên của cổng Service đã định nghĩa ở trên
    appProtocol: http
    protocol: TCP
    port: 9376
endpoints:
  - addresses:
      - "10.4.5.6"
  - addresses:
      - "10.1.2.3"

EndpointSlice tùy chỉnh

Khi bạn tạo một đối tượng EndpointSlice cho một Service, bạn có thể dùng bất kỳ tên nào cho EndpointSlice đó. Mỗi EndpointSlice trong một namespace phải có tên duy nhất. Bạn liên kết một EndpointSlice với một Service bằng cách đặt label kubernetes.io/service-name trên EndpointSlice đó.

Lưu ý:

IP của endpoint không được là: loopback (127.0.0.0/8 với IPv4, ::1/128 với IPv6), hoặc link-local (169.254.0.0/16 và 224.0.0.0/24 với IPv4, fe80::/64 với IPv6).

Địa chỉ IP của endpoint không thể là cluster IP của các Service Kubernetes khác, vì kube-proxy không hỗ trợ IP ảo làm đích đến.

Với một EndpointSlice do chính bạn tạo, hoặc được tạo trong code của bạn, bạn cũng nên chọn một giá trị cho label endpointslice.kubernetes.io/managed-by. Nếu bạn tự viết code controller để quản lý EndpointSlice, hãy cân nhắc dùng một giá trị tương tự như "my-domain.example/name-of-controller". Nếu bạn dùng một công cụ bên thứ ba, hãy dùng tên của công cụ đó viết thường toàn bộ, và thay khoảng trắng cùng các dấu câu khác bằng dấu gạch ngang (-). Nếu mọi người trực tiếp dùng một công cụ như kubectl để quản lý EndpointSlice, hãy dùng một tên mô tả việc quản lý thủ công này, chẳng hạn "staff" hoặc "cluster-admins". Bạn nên tránh dùng giá trị dành riêng "controller", vì giá trị này xác định các EndpointSlice do chính control plane của Kubernetes quản lý.

Truy cập một Service không có selector

Việc truy cập một Service không có selector hoạt động giống như khi nó có selector. Trong ví dụ về Service không có selector, lưu lượng được định tuyến tới một trong hai endpoint được định nghĩa trong manifest EndpointSlice: một kết nối TCP tới 10.1.2.3 hoặc 10.4.5.6, trên cổng 9376.

Lưu ý:

Kubernetes API server không cho phép proxy tới các endpoint không được ánh xạ tới pod. Các thao tác như kubectl port-forward service/<service-name> forwardedPort:servicePort khi service không có selector sẽ thất bại do ràng buộc này. Điều này ngăn Kubernetes API server bị dùng làm proxy tới các endpoint mà bên gọi có thể không có quyền truy cập.

Service ExternalName là một trường hợp đặc biệt của Service, không có selector và dùng tên DNS thay thế. Để biết thêm thông tin, xem phần ExternalName.

EndpointSlice

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.21

EndpointSlice là các đối tượng đại diện cho một tập con (một slice) của các endpoint mạng đứng sau một Service.

Cluster Kubernetes của bạn theo dõi số lượng endpoint mà mỗi EndpointSlice đại diện. Nếu một Service có nhiều endpoint đến mức chạm tới một ngưỡng, thì Kubernetes sẽ thêm một EndpointSlice trống khác và lưu thông tin endpoint mới vào đó. Theo mặc định, Kubernetes tạo một EndpointSlice mới khi tất cả các EndpointSlice hiện có đều chứa ít nhất 100 endpoint. Kubernetes không tạo EndpointSlice mới cho đến khi cần thêm một endpoint nữa.

Xem EndpointSlice để biết thêm thông tin về API này.

Endpoints (không còn được hỗ trợ)

TRẠNG THÁI TÍNH NĂNG: Đã lỗi thời since Kubernetes v1.33

EndpointSlice API là bước phát triển tiếp theo của API Endpoints cũ hơn. Endpoints API không còn được hỗ trợ này có một số vấn đề so với EndpointSlice:

  • Nó không hỗ trợ cluster dual-stack.
  • Nó không chứa thông tin cần thiết để hỗ trợ các tính năng mới hơn, chẳng hạn như trafficDistribution.
  • Nó sẽ cắt bớt danh sách endpoint nếu danh sách quá dài để chứa trong một đối tượng duy nhất.

Vì vậy, tất cả client được khuyến nghị dùng EndpointSlice API thay vì Endpoints.

Endpoint vượt quá sức chứa

Kubernetes giới hạn số lượng endpoint có thể chứa trong một đối tượng Endpoints duy nhất. Khi một Service có hơn 1000 endpoint phía sau, Kubernetes cắt bớt dữ liệu trong đối tượng Endpoints. Vì một Service có thể được liên kết với nhiều hơn một EndpointSlice, giới hạn 1000 endpoint phía sau chỉ ảnh hưởng đến Endpoints API cũ (legacy).

Trong trường hợp đó, Kubernetes chọn tối đa 1000 endpoint backend khả dĩ để lưu vào đối tượng Endpoints, và đặt một chú thích (annotation) trên Endpoints: endpoints.kubernetes.io/over-capacity: truncated. Control plane cũng gỡ bỏ chú thích đó nếu số lượng Pod backend giảm xuống dưới 1000.

Lưu lượng vẫn được gửi tới các backend, nhưng mọi cơ chế cân bằng tải dựa vào Endpoints API cũ chỉ gửi lưu lượng tới tối đa 1000 endpoint trong số các endpoint phía sau hiện có.

Cũng vì giới hạn API đó, bạn không thể cập nhật thủ công một Endpoints để có hơn 1000 endpoint.

Giao thức ứng dụng

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.20

Trường appProtocol cung cấp một cách để chỉ định giao thức ứng dụng cho từng cổng của Service. Trường này được dùng làm gợi ý để các bản triển khai (implementation) cung cấp hành vi phong phú hơn cho các giao thức mà chúng hiểu. Giá trị của trường này được phản ánh sang các đối tượng Endpoints và EndpointSlice tương ứng.

Trường này tuân theo cú pháp label chuẩn của Kubernetes. Giá trị hợp lệ là một trong các loại sau:

  • Tên dịch vụ chuẩn của IANA.

  • Tên có tiền tố do bản triển khai định nghĩa, chẳng hạn mycompany.com/my-custom-protocol.

  • Tên có tiền tố do Kubernetes định nghĩa:

Giao thứcMô tả
kubernetes.io/h2cHTTP/2 qua kết nối không mã hóa (cleartext) như mô tả trong RFC 9113
kubernetes.io/wsWebSocket qua kết nối không mã hóa như mô tả trong RFC 6455
kubernetes.io/wssWebSocket qua TLS như mô tả trong RFC 6455

Service nhiều cổng

Với một số Service, bạn cần cung cấp nhiều hơn một cổng. Kubernetes cho phép bạn cấu hình nhiều định nghĩa cổng trên một đối tượng Service. Khi dùng nhiều cổng cho một Service, bạn phải đặt tên cho tất cả các cổng để chúng không bị nhầm lẫn. Ví dụ:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 9376
    - name: https
      protocol: TCP
      port: 443
      targetPort: 9377

Lưu ý:

Cũng như tên trong Kubernetes nói chung, tên cổng chỉ được chứa ký tự chữ và số viết thường và -. Tên cổng cũng phải bắt đầu và kết thúc bằng một ký tự chữ hoặc số.

Ví dụ, các tên 123-abc và web là hợp lệ, nhưng 123_abc và -web thì không.

Loại Service

Với một số phần của ứng dụng (ví dụ, frontend), bạn có thể muốn cung cấp Service trên một địa chỉ IP bên ngoài, có thể truy cập được từ bên ngoài cluster.

Các loại Service trong Kubernetes cho phép bạn chỉ định loại Service mà bạn muốn.

Các giá trị type hiện có và hành vi của chúng là:

ClusterIP
Cung cấp Service trên một IP nội bộ của cluster. Chọn giá trị này khiến Service chỉ có thể truy cập được từ bên trong cluster. Đây là giá trị mặc định nếu bạn không chỉ định rõ type cho Service. Bạn có thể cung cấp Service ra internet công cộng bằng một Ingress hoặc một Gateway.
NodePort
Cung cấp Service trên IP của mỗi Node tại một cổng tĩnh (NodePort). Để node port sẵn sàng, Kubernetes thiết lập một địa chỉ cluster IP, giống như khi bạn yêu cầu một Service có type: ClusterIP.
LoadBalancer
Cung cấp Service ra bên ngoài bằng một bộ cân bằng tải bên ngoài. Kubernetes không trực tiếp cung cấp thành phần cân bằng tải; bạn phải tự cung cấp, hoặc tích hợp cluster Kubernetes của bạn với một nhà cung cấp dịch vụ đám mây (cloud provider).
ExternalName
Ánh xạ Service tới nội dung của trường externalName (ví dụ, tới hostname api.foo.bar.example). Việc ánh xạ này cấu hình máy chủ DNS của cluster trả về một bản ghi CNAME với giá trị hostname bên ngoài đó. Không có bất kỳ hình thức proxy nào được thiết lập.

Trường type trong Service API được thiết kế theo dạng chức năng lồng nhau - mỗi cấp bổ sung thêm vào cấp trước. Tuy nhiên có một ngoại lệ đối với thiết kế lồng nhau này. Bạn có thể định nghĩa một Service LoadBalancer bằng cách tắt việc cấp phát NodePort cho bộ cân bằng tải.

type: ClusterIP

Loại Service mặc định này gán một địa chỉ IP từ một nhóm (pool) địa chỉ IP mà cluster của bạn đã dành riêng cho mục đích đó.

Một số loại Service khác được xây dựng dựa trên loại ClusterIP làm nền tảng.

Nếu bạn định nghĩa một Service có .spec.clusterIP được đặt thành "None" thì Kubernetes sẽ không gán địa chỉ IP. Xem headless Service để biết thêm thông tin.

Tự chọn địa chỉ IP

Bạn có thể chỉ định địa chỉ cluster IP của riêng mình như một phần của yêu cầu tạo Service. Để làm điều này, hãy đặt trường .spec.clusterIP. Ví dụ, khi bạn đã có sẵn một bản ghi DNS muốn tái sử dụng, hoặc có các hệ thống cũ (legacy) được cấu hình cho một địa chỉ IP cụ thể và khó cấu hình lại.

Địa chỉ IP bạn chọn phải là một địa chỉ IPv4 hoặc IPv6 hợp lệ nằm trong dải CIDR service-cluster-ip-range được cấu hình cho API server. Nếu bạn cố tạo một Service với giá trị địa chỉ clusterIP không hợp lệ, API server sẽ trả về mã trạng thái HTTP 422 để báo rằng có vấn đề.

Đọc tránh xung đột để tìm hiểu cách Kubernetes giúp giảm rủi ro và tác động khi hai Service khác nhau cùng cố dùng một địa chỉ IP.

type: NodePort

Nếu bạn đặt trường type thành NodePort, control plane của Kubernetes cấp phát một cổng từ dải được chỉ định bởi cờ --service-node-port-range (mặc định: 30000-32767). Mỗi node proxy cổng đó (cùng một số cổng trên mọi Node) vào Service của bạn. Service của bạn báo cáo cổng đã được cấp phát trong trường .spec.ports[*].nodePort.

Dùng NodePort cho bạn sự tự do trong việc thiết lập giải pháp cân bằng tải của riêng mình, cấu hình các môi trường mà Kubernetes chưa hỗ trợ đầy đủ, hoặc thậm chí cung cấp trực tiếp địa chỉ IP của một hay nhiều node.

Với một Service node port, Kubernetes cấp phát thêm một cổng (TCP, UDP hoặc SCTP, khớp với giao thức của Service). Mọi node trong cluster tự cấu hình để lắng nghe trên cổng được gán đó và chuyển tiếp lưu lượng tới một trong các endpoint sẵn sàng (ready) liên kết với Service đó. Bạn có thể kết nối tới Service type: NodePort từ bên ngoài cluster bằng cách kết nối tới bất kỳ node nào với giao thức phù hợp (ví dụ: TCP) và cổng phù hợp (cổng đã được gán cho Service đó).

Tự chọn cổng

Nếu bạn muốn một số cổng cụ thể, bạn có thể chỉ định giá trị trong trường nodePort. Control plane sẽ cấp phát cổng đó cho bạn hoặc báo rằng giao dịch API đã thất bại. Điều này có nghĩa là bạn cần tự xử lý các xung đột cổng có thể xảy ra. Bạn cũng phải dùng một số cổng hợp lệ, nằm trong dải được cấu hình cho NodePort.

Dưới đây là một manifest mẫu cho Service có type: NodePort chỉ định một giá trị NodePort (trong ví dụ này là 30007):

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - port: 80
      # Theo mặc định và để thuận tiện, `targetPort` được đặt cùng
      # giá trị với trường `port`.
      targetPort: 80
      # Trường tùy chọn
      # Theo mặc định và để thuận tiện, control plane của Kubernetes
      # sẽ cấp phát một cổng từ một dải (mặc định: 30000-32767)
      nodePort: 30007

Dành riêng dải NodePort để tránh xung đột

Chính sách gán cổng cho các Service NodePort áp dụng cho cả trường hợp gán tự động và gán thủ công. Khi người dùng muốn tạo một Service NodePort dùng một cổng cụ thể, cổng đích có thể xung đột với một cổng khác đã được gán.

Để tránh vấn đề này, dải cổng cho các Service NodePort được chia thành hai băng (band). Việc gán cổng động mặc định dùng băng trên, và có thể dùng băng dưới khi băng trên đã hết. Khi đó người dùng có thể cấp phát từ băng dưới với rủi ro xung đột cổng thấp hơn.

Khi dùng dải NodePort mặc định 30000-32767, các băng được phân chia như sau:

  • Băng tĩnh: 30000-30085
  • Băng động: 30086-32767

Xem Tránh xung đột khi gán cổng cho Service NodePort để biết thêm chi tiết về cách tính các băng tĩnh và động.

Cấu hình địa chỉ IP cho Service type: NodePort

Khi dùng kube-proxy ở chế độ iptables, theo mặc định các Service NodePort khả dụng trên tất cả IP của node. Khi dùng chế độ nftables, theo mặc định chúng chỉ khả dụng trên IP chính của node (hoặc các IP chính dual-stack).

Bạn có thể thay đổi tập IP của node mà các Service NodePort khả dụng bằng cờ --nodeport-addresses của kube-proxy, hoặc trường nodePortAddresses tương đương trong file cấu hình kube-proxy. Cờ này chấp nhận một danh sách các khối IP phân tách bằng dấu phẩy (ví dụ 10.0.0.0/8, 192.0.2.0/25) hoặc một hay nhiều từ khóa sau:

  • primary - địa chỉ IPv4 và/hoặc IPv6 chính của node, theo đối tượng Node. (Đây là giá trị mặc định cho chế độ nftables.)
  • localhost - các địa chỉ loopback của node (127.0.0.0/8, ::1/128).
  • all - tất cả địa chỉ. (Đây là giá trị mặc định cho chế độ iptables và ipvs.)

Ví dụ, nếu bạn khởi động kube-proxy với cờ --nodeport-addresses=192.168.0.0/24, thì kube-proxy sẽ cố tìm một địa chỉ IP cục bộ thuộc subnet đó trên mỗi node, và chỉ phục vụ các Service NodePort qua IP đó.

Service type: NodePort qua localhost

Các cơ chế mà service proxy dùng để triển khai Service NodePort không phải lúc nào cũng hỗ trợ cung cấp Service NodePort trên localhost. Với kube-proxy:

  • Khi dùng chế độ iptables, với giá trị --nodeport-addresses có bao gồm 127.0.0.1, các Service NodePort sẽ khả dụng trên 127.0.0.1. Tuy nhiên, điều này yêu cầu bật một sysctl của kernel (route_localnet) có thể gây ra tác dụng phụ thiếu an toàn trong một số cluster. Có thể tắt NodePort localhost của IPTables bằng cách truyền --iptables-localhost-nodeports false cho kube-proxy, hoặc bằng cách đặt --nodeport-addresses thành một dải không bao gồm 127.0.0.1.

  • Khi dùng chế độ ipvs, hoặc chế độ iptables trong cluster IPv6 single-stack, Service NodePort không khả dụng trên localhost.

TRẠNG THÁI TÍNH NĂNG: Alpha since Kubernetes v1.37; disabled by default
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the KubeProxyNFTablesLocalhostNodePorts feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

  • Khi dùng chế độ nftables, Service NodePort sẽ khả dụng trên localhost khi feature gate KubeProxyNFTablesLocalhostNodePorts được bật, và --nodeport-addresses được đặt thành một giá trị có bao gồm rõ ràng localhost. (Chỉ đặt thành all sẽ không bật Service NodePort trên localhost.) Điều này được triển khai bằng cách chuyển hướng các kết nối NodePort localhost qua một proxy userspace, nên không hiệu quả bằng việc proxy service thông thường.

Các plugin mạng bên thứ ba có bản triển khai service proxy riêng có thể hỗ trợ hoặc không hỗ trợ NodePort trên localhost; hãy tham khảo tài liệu của các plugin đó.

type: LoadBalancer

Trên các nhà cung cấp dịch vụ đám mây hỗ trợ bộ cân bằng tải bên ngoài, việc đặt trường type thành LoadBalancer sẽ cấp phát một bộ cân bằng tải cho Service của bạn. Việc tạo bộ cân bằng tải thực tế diễn ra bất đồng bộ, và thông tin về bộ cân bằng tải được cấp phát sẽ được công bố trong trường .status.loadBalancer của Service. Ví dụ:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
  clusterIP: 10.0.171.239
  type: LoadBalancer
status:
  loadBalancer:
    ingress:
    - ip: 192.0.2.127

Lưu lượng từ bộ cân bằng tải bên ngoài được chuyển tới các Pod backend. Nhà cung cấp dịch vụ đám mây quyết định cách cân bằng tải.

Để triển khai một Service type: LoadBalancer, Kubernetes thường bắt đầu bằng cách thực hiện các thay đổi tương đương với việc bạn yêu cầu một Service type: NodePort. Sau đó, thành phần cloud-controller-manager cấu hình bộ cân bằng tải bên ngoài để chuyển tiếp lưu lượng tới node port đã được gán đó.

Bạn có thể cấu hình một Service có cân bằng tải để bỏ qua việc gán node port, với điều kiện bản triển khai của nhà cung cấp dịch vụ đám mây hỗ trợ điều này.

Một số nhà cung cấp dịch vụ đám mây cho phép bạn chỉ định loadBalancerIP. Trong những trường hợp đó, bộ cân bằng tải được tạo với loadBalancerIP do người dùng chỉ định. Nếu trường loadBalancerIP không được chỉ định, bộ cân bằng tải sẽ được thiết lập với một địa chỉ IP tạm thời. Nếu bạn chỉ định loadBalancerIP nhưng nhà cung cấp dịch vụ đám mây của bạn không hỗ trợ tính năng này, trường loadbalancerIP mà bạn đặt sẽ bị bỏ qua.

Lưu ý:

Trường .spec.loadBalancerIP của Service đã không còn được hỗ trợ từ Kubernetes v1.24.

Trường này được đặc tả chưa đầy đủ và ý nghĩa của nó khác nhau giữa các bản triển khai. Nó cũng không thể hỗ trợ mạng dual-stack. Trường này có thể bị loại bỏ trong một phiên bản API tương lai.

Nếu bạn đang tích hợp với một nhà cung cấp hỗ trợ chỉ định (các) địa chỉ IP của bộ cân bằng tải cho Service thông qua một chú thích (riêng của nhà cung cấp), bạn nên chuyển sang cách đó.

Nếu bạn đang viết code tích hợp bộ cân bằng tải với Kubernetes, hãy tránh dùng trường này. Bạn có thể tích hợp với Gateway thay vì Service, hoặc bạn có thể tự định nghĩa các chú thích (riêng của nhà cung cấp) trên Service để chỉ định thông tin tương đương.

Ảnh hưởng của tình trạng hoạt động (liveness) của node tới lưu lượng bộ cân bằng tải

Kiểm tra sức khỏe (health check) của bộ cân bằng tải đóng vai trò then chốt đối với các ứng dụng hiện đại. Chúng được dùng để xác định máy chủ nào (máy ảo, hoặc địa chỉ IP) mà bộ cân bằng tải nên điều phối lưu lượng tới. Kubernetes API không định nghĩa cách triển khai kiểm tra sức khỏe cho các bộ cân bằng tải do Kubernetes quản lý, thay vào đó các nhà cung cấp dịch vụ đám mây (và những người viết code tích hợp) sẽ quyết định hành vi. Kiểm tra sức khỏe của bộ cân bằng tải được sử dụng rộng rãi trong bối cảnh hỗ trợ trường externalTrafficPolicy của Service.

Bộ cân bằng tải với nhiều loại giao thức

This is a stable feature in Kubernetes, and has been since the 1.26 release. You can no longer toggle this feature (the associated feature gate has been removed).

Theo mặc định, với các Service loại LoadBalancer, khi có nhiều hơn một cổng được định nghĩa, tất cả các cổng phải có cùng giao thức, và giao thức đó phải được nhà cung cấp dịch vụ đám mây hỗ trợ.

Feature gate MixedProtocolLBService (được bật mặc định cho kube-apiserver kể từ v1.24) cho phép dùng các giao thức khác nhau cho các Service loại LoadBalancer khi có nhiều hơn một cổng được định nghĩa.

Lưu ý:

Tập giao thức có thể dùng cho các Service có cân bằng tải do nhà cung cấp dịch vụ đám mây của bạn định nghĩa; họ có thể áp đặt các hạn chế ngoài những gì Kubernetes API quy định.

Tắt cấp phát NodePort cho bộ cân bằng tải

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.24

Bạn có thể tùy chọn tắt việc cấp phát node port cho Service type: LoadBalancer bằng cách đặt trường spec.allocateLoadBalancerNodePorts thành false. Chỉ nên dùng cách này cho các bản triển khai bộ cân bằng tải định tuyến lưu lượng trực tiếp tới pod thay vì dùng node port. Theo mặc định, spec.allocateLoadBalancerNodePorts là true và các Service loại LoadBalancer sẽ tiếp tục cấp phát node port. Nếu spec.allocateLoadBalancerNodePorts được đặt thành false trên một Service hiện có đã được cấp phát node port, các node port đó sẽ không được tự động thu hồi. Bạn phải xóa rõ ràng mục nodePorts trong từng cổng của Service để thu hồi các node port đó.

Chỉ định class cho bản triển khai bộ cân bằng tải

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.24

Với một Service có type được đặt thành LoadBalancer, trường .spec.loadBalancerClass cho phép bạn dùng một bản triển khai bộ cân bằng tải khác với mặc định của nhà cung cấp dịch vụ đám mây.

Theo mặc định, .spec.loadBalancerClass không được đặt và Service loại LoadBalancer sẽ dùng bản triển khai bộ cân bằng tải mặc định của nhà cung cấp dịch vụ đám mây nếu cluster được cấu hình với một nhà cung cấp dịch vụ đám mây thông qua cờ --cloud-provider của thành phần.

Nếu bạn chỉ định .spec.loadBalancerClass, hệ thống giả định rằng có một bản triển khai bộ cân bằng tải khớp với class đã chỉ định đang theo dõi các Service. Mọi bản triển khai bộ cân bằng tải mặc định (ví dụ, bản do nhà cung cấp dịch vụ đám mây cung cấp) sẽ bỏ qua các Service có đặt trường này. spec.loadBalancerClass chỉ có thể được đặt trên Service loại LoadBalancer. Một khi đã đặt, giá trị này không thể thay đổi. Giá trị của spec.loadBalancerClass phải là một định danh dạng label, với tiền tố tùy chọn như "internal-vip" hoặc "example.com/internal-vip". Các tên không có tiền tố được dành riêng cho người dùng cuối.

Chế độ địa chỉ IP của bộ cân bằng tải

Với một Service type: LoadBalancer, controller có thể đặt .status.loadBalancer.ingress.ipMode. .status.loadBalancer.ingress.ipMode chỉ định cách IP của bộ cân bằng tải hoạt động. Trường này chỉ có thể được chỉ định khi trường .status.loadBalancer.ingress.ip cũng được chỉ định.

Có hai giá trị khả dĩ cho .status.loadBalancer.ingress.ipMode: "VIP" và "Proxy". Giá trị mặc định là "VIP", nghĩa là lưu lượng được chuyển tới node với đích được đặt là IP và cổng của bộ cân bằng tải. Có hai trường hợp khi đặt giá trị này thành "Proxy", tùy thuộc vào cách bộ cân bằng tải của nhà cung cấp dịch vụ đám mây chuyển lưu lượng:

  • Nếu lưu lượng được chuyển tới node rồi được DNAT tới pod, đích sẽ được đặt là IP và node port của node;
  • Nếu lưu lượng được chuyển trực tiếp tới pod, đích sẽ được đặt là IP và cổng của pod.

Các bản triển khai Service có thể dùng thông tin này để điều chỉnh việc định tuyến lưu lượng.

Bộ cân bằng tải nội bộ

Trong một môi trường hỗn hợp, đôi khi cần định tuyến lưu lượng từ các Service bên trong cùng một khối địa chỉ mạng (ảo).

Trong môi trường DNS split-horizon, bạn sẽ cần hai Service để có thể định tuyến cả lưu lượng bên ngoài lẫn bên trong tới các endpoint của mình.

Để thiết lập một bộ cân bằng tải nội bộ, hãy thêm một trong các chú thích sau vào Service tùy theo nhà cung cấp dịch vụ đám mây mà bạn đang dùng:

Chọn một trong các tab.

metadata:
  name: my-service
  annotations:
    networking.gke.io/load-balancer-type: "Internal"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internal"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "true"
metadata:
  name: my-service
  annotations:
    service.kubernetes.io/ibm-load-balancer-cloud-provider-ip-type: "private"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
metadata:
  annotations:
    service.kubernetes.io/qcloud-loadbalancer-internal-subnetid: subnet-xxxxx
metadata:
  annotations:
    service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet"
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/oci-load-balancer-internal: true

type: ExternalName

Service loại ExternalName ánh xạ một Service tới một tên DNS, chứ không phải tới một selector thông thường như my-service hay cassandra. Bạn chỉ định các Service này bằng tham số spec.externalName.

Ví dụ, định nghĩa Service sau ánh xạ Service my-service trong namespace prod tới my.database.example.com:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  namespace: prod
spec:
  type: ExternalName
  externalName: my.database.example.com

Lưu ý:

Service type: ExternalName chấp nhận một chuỗi địa chỉ IPv4, nhưng xử lý chuỗi đó như một tên DNS gồm các chữ số, chứ không phải như một địa chỉ IP (tuy nhiên internet không cho phép những tên như vậy trong DNS). Các Service có tên bên ngoài trông giống địa chỉ IPv4 sẽ không được các máy chủ DNS phân giải.

Nếu bạn muốn ánh xạ trực tiếp một Service tới một địa chỉ IP cụ thể, hãy cân nhắc dùng headless Service.

Khi tra cứu host my-service.prod.svc.cluster.local, DNS Service của cluster trả về một bản ghi CNAME với giá trị my.database.example.com. Việc truy cập my-service hoạt động giống như các Service khác nhưng có điểm khác biệt quan trọng là việc chuyển hướng diễn ra ở mức DNS thay vì qua proxy hay chuyển tiếp. Nếu sau này bạn quyết định chuyển cơ sở dữ liệu vào trong cluster, bạn có thể khởi chạy các Pod của nó, thêm các selector hoặc endpoint phù hợp, và thay đổi type của Service.

Chú ý:

Bạn có thể gặp khó khăn khi dùng ExternalName với một số giao thức phổ biến, bao gồm HTTP và HTTPS. Nếu bạn dùng ExternalName thì hostname mà các client bên trong cluster sử dụng sẽ khác với tên mà ExternalName tham chiếu tới.

Với các giao thức sử dụng hostname, sự khác biệt này có thể dẫn đến lỗi hoặc phản hồi không mong muốn. Các yêu cầu HTTP sẽ có header Host: mà máy chủ gốc không nhận ra; các máy chủ TLS sẽ không thể cung cấp chứng chỉ khớp với hostname mà client đã kết nối tới.

Headless Service

Đôi khi bạn không cần cân bằng tải và một IP Service duy nhất. Trong trường hợp này, bạn có thể tạo cái gọi là headless Service, bằng cách chỉ định rõ "None" cho địa chỉ cluster IP (.spec.clusterIP).

Bạn có thể dùng headless Service để giao tiếp với các cơ chế service discovery khác mà không bị ràng buộc vào cách triển khai của Kubernetes.

Với headless Service, cluster IP không được cấp phát, kube-proxy không xử lý các Service này, và nền tảng không thực hiện cân bằng tải hay proxy cho chúng.

Headless Service cho phép client kết nối trực tiếp tới bất kỳ Pod nào mà nó muốn. Các headless Service không cấu hình route và chuyển tiếp gói tin bằng địa chỉ IP ảo và proxy; thay vào đó, headless Service báo cáo địa chỉ IP endpoint của từng pod thông qua các bản ghi DNS nội bộ, được phục vụ qua dịch vụ DNS của cluster. Để định nghĩa một headless Service, bạn tạo một Service với .spec.type được đặt thành ClusterIP (cũng là giá trị mặc định của type), và đặt thêm .spec.clusterIP thành None.

Giá trị chuỗi None là một trường hợp đặc biệt và không giống với việc để trống trường .spec.clusterIP.

Cách DNS được cấu hình tự động phụ thuộc vào việc Service có định nghĩa selector hay không:

Có selector

Với các headless Service có định nghĩa selector, endpoints controller tạo các EndpointSlice trong Kubernetes API, và sửa đổi cấu hình DNS để trả về các bản ghi A hoặc AAAA (địa chỉ IPv4 hoặc IPv6) trỏ trực tiếp tới các Pod đứng sau Service.

Không có selector

Với các headless Service không định nghĩa selector, control plane không tạo các đối tượng EndpointSlice. Tuy nhiên, hệ thống DNS sẽ tìm và cấu hình một trong hai:

  • Bản ghi DNS CNAME cho các Service type: ExternalName.
  • Bản ghi DNS A / AAAA cho tất cả địa chỉ IP của các endpoint sẵn sàng của Service, với mọi loại Service ngoài ExternalName.
    • Với endpoint IPv4, hệ thống DNS tạo bản ghi A.
    • Với endpoint IPv6, hệ thống DNS tạo bản ghi AAAA.

Khi bạn định nghĩa một headless Service không có selector, port phải khớp với targetPort.

Tìm kiếm Service

Với các client chạy bên trong cluster, Kubernetes hỗ trợ hai cách chính để tìm một Service: biến môi trường và DNS.

Biến môi trường

Khi một Pod chạy trên một Node, kubelet thêm một tập biến môi trường cho mỗi Service đang hoạt động. Nó thêm các biến {SVCNAME}_SERVICE_HOST và {SVCNAME}_SERVICE_PORT, trong đó tên Service được viết hoa và dấu gạch ngang được chuyển thành dấu gạch dưới.

Ví dụ, Service redis-primary cung cấp cổng TCP 6379 và đã được cấp phát địa chỉ cluster IP 10.0.0.11 sẽ tạo ra các biến môi trường sau:

REDIS_PRIMARY_SERVICE_HOST=10.0.0.11
REDIS_PRIMARY_SERVICE_PORT=6379
REDIS_PRIMARY_PORT=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_PRIMARY_PORT_6379_TCP_PROTO=tcp
REDIS_PRIMARY_PORT_6379_TCP_PORT=6379
REDIS_PRIMARY_PORT_6379_TCP_ADDR=10.0.0.11

Lưu ý:

Khi bạn có một Pod cần truy cập một Service, và bạn đang dùng phương pháp biến môi trường để công bố cổng và cluster IP cho các Pod client, bạn phải tạo Service trước khi các Pod client được tạo ra. Nếu không, các biến môi trường của những Pod client đó sẽ không được điền giá trị.

Nếu bạn chỉ dùng DNS để tìm cluster IP của một Service, bạn không cần lo lắng về vấn đề thứ tự này.

Kubernetes cũng hỗ trợ và cung cấp các biến tương thích với tính năng "legacy container links" của Docker Engine. Bạn có thể đọc makeLinkVariables để xem cách tính năng này được triển khai trong Kubernetes.

DNS

Bạn có thể (và gần như luôn luôn nên) thiết lập một dịch vụ DNS cho cluster Kubernetes bằng một add-on.

Một máy chủ DNS nhận biết cluster (cluster-aware), chẳng hạn CoreDNS, theo dõi Kubernetes API để phát hiện các Service mới và tạo một tập bản ghi DNS cho mỗi Service. Nếu DNS đã được bật trên toàn bộ cluster thì tất cả các Pod sẽ tự động phân giải được Service theo tên DNS của chúng.

Ví dụ, nếu bạn có một Service tên là my-service trong namespace Kubernetes my-ns, control plane và DNS Service cùng phối hợp tạo một bản ghi DNS cho my-service.my-ns. Các Pod trong namespace my-ns sẽ có thể tìm thấy service bằng cách tra cứu tên my-service (my-service.my-ns cũng hoạt động).

Các Pod trong namespace khác phải dùng tên đầy đủ my-service.my-ns. Những tên này sẽ được phân giải thành cluster IP được gán cho Service.

Kubernetes cũng hỗ trợ bản ghi DNS SRV (Service) cho các cổng có tên. Nếu Service my-service.my-ns có một cổng tên là http với giao thức được đặt thành TCP, bạn có thể thực hiện truy vấn DNS SRV cho _http._tcp.my-service.my-ns để tìm số cổng của http, cũng như địa chỉ IP.

Máy chủ DNS của Kubernetes là cách duy nhất để truy cập các Service ExternalName. Bạn có thể tìm thêm thông tin về việc phân giải ExternalName trong DNS cho Service và Pod.

Cơ chế địa chỉ IP ảo

Trang IP ảo và Service Proxy giải thích cơ chế mà Kubernetes dùng để cung cấp một Service qua một địa chỉ IP ảo.

Chính sách lưu lượng

Bạn có thể đặt các trường .spec.internalTrafficPolicy và .spec.externalTrafficPolicy để kiểm soát cách Kubernetes định tuyến lưu lượng tới các backend khỏe mạnh (“ready”).

Xem Chính sách lưu lượng để biết thêm chi tiết.

Kiểm soát phân phối lưu lượng

Trường .spec.trafficDistribution cung cấp một cách khác để tác động tới việc định tuyến lưu lượng trong một Service Kubernetes. Trong khi chính sách lưu lượng tập trung vào các đảm bảo ngữ nghĩa chặt chẽ, phân phối lưu lượng cho phép bạn thể hiện các ưu tiên (chẳng hạn định tuyến tới các endpoint gần hơn về mặt topo mạng). Điều này có thể giúp tối ưu hiệu năng, chi phí, hoặc độ tin cậy. Trong Kubernetes 1.37, các giá trị sau được hỗ trợ:

PreferSameZone
Thể hiện ưu tiên định tuyến lưu lượng tới các endpoint nằm trong cùng zone với client.
PreferSameNode
Thể hiện ưu tiên định tuyến lưu lượng tới các endpoint nằm trên cùng node với client.
PreferClose (không còn được hỗ trợ)
Đây là một alias cũ của PreferSameZone, có ngữ nghĩa kém rõ ràng hơn.

Nếu trường này không được đặt, bản triển khai sẽ áp dụng chiến lược định tuyến mặc định của nó.

Xem Phân phối lưu lượng để biết thêm chi tiết.

Duy trì phiên (session stickiness)

Nếu bạn muốn đảm bảo rằng các kết nối từ một client cụ thể luôn được chuyển tới cùng một Pod, bạn có thể cấu hình session affinity dựa trên địa chỉ IP của client. Đọc session affinity để tìm hiểu thêm.

IP bên ngoài (External IP)

TRẠNG THÁI TÍNH NĂNG: Đã lỗi thời since Kubernetes v1.36

Tất cả người dùng nên bắt đầu chuyển dần khỏi externalIPs. Thay vào đó, hãy cân nhắc dùng một controller bộ cân bằng tải bên ngoài hoặc một bản triển khai Gateway API.

Nếu có các IP bên ngoài định tuyến tới một hoặc nhiều node của cluster, các Service Kubernetes có thể được cung cấp trên các externalIPs đó. Khi lưu lượng mạng đi vào cluster với IP bên ngoài (là IP đích) và cổng khớp với Service đó, các quy tắc và route mà Kubernetes đã cấu hình sẽ đảm bảo lưu lượng được định tuyến tới một trong các endpoint của Service đó.

Khi định nghĩa một Service, bạn có thể chỉ định externalIPs cho bất kỳ loại Service nào. Trong ví dụ dưới đây, client có thể truy cập Service tên "my-service" qua TCP, tại "198.51.100.32:80" (được tính từ .spec.externalIPs[] và .spec.ports[].port).

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app.kubernetes.io/name: MyApp
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 49152
  externalIPs:
    - 198.51.100.32

Lưu ý:

Kubernetes không quản lý việc cấp phát externalIPs; đây là trách nhiệm của quản trị viên cluster.

Đối tượng API

Service là một tài nguyên cấp cao nhất trong Kubernetes REST API. Bạn có thể tìm thêm chi tiết về đối tượng Service API.

Tiếp theo là gì

Tìm hiểu thêm về Service và vai trò của chúng trong Kubernetes:

  • Làm theo hướng dẫn Kết nối ứng dụng với Service.
  • Đọc về Ingress, thành phần cung cấp các route HTTP và HTTPS từ bên ngoài cluster tới các Service bên trong cluster của bạn.
  • Đọc về Gateway, một phần mở rộng của Kubernetes mang lại nhiều sự linh hoạt hơn Ingress.

Để hiểu thêm bối cảnh, hãy đọc các tài liệu sau:

3 - Persistent Volumes

Trang này đang được dịch. Vui lòng xem phiên bản tiếng Anh để biết thông tin mới nhất.

4 - Tổng quan

4.1 - Kubernetes là gì

Trang tổng quan của Kubernetes.

Kubernetes là một nền tảng nguồn mở, khả chuyển, có thể mở rộng để quản lý các ứng dụng được đóng gói và các service, giúp thuận lợi trong việc cấu hình và tự động hoá việc triển khai ứng dụng. Kubernetes là một hệ sinh thái lớn và phát triển nhanh chóng. Các dịch vụ, sự hỗ trợ và công cụ có sẵn rộng rãi.

Tên gọi Kubernetes có nguồn gốc từ tiếng Hy Lạp, có ý nghĩa là người lái tàu hoặc hoa tiêu. Google mở mã nguồn Kubernetes từ năm 2014. Kubernetes xây dựng dựa trên một thập kỷ rưỡi kinh nghiệm mà Google có được với việc vận hành một khối lượng lớn workload trong thực tế, kết hợp với các ý tưởng và thực tiễn tốt nhất từ cộng đồng.

Quay ngược thời gian

Chúng ta hãy xem tại sao Kubernetes rất hữu ích bằng cách quay ngược thời gian.

Deployment evolution

Thời đại triển khai theo cách truyền thống: Ban đầu, các ứng dụng được chạy trên các máy chủ vật lý. Không có cách nào để xác định ranh giới tài nguyên cho các ứng dụng trong máy chủ vật lý và điều này gây ra sự cố phân bổ tài nguyên. Ví dụ, nếu nhiều ứng dụng cùng chạy trên một máy chủ vật lý, có thể có những trường hợp một ứng dụng sẽ chiếm phần lớn tài nguyên hơn và kết quả là các ứng dụng khác sẽ hoạt động kém đi. Một giải pháp cho điều này sẽ là chạy từng ứng dụng trên một máy chủ vật lý khác nhau. Nhưng giải pháp này không tối ưu vì tài nguyên không được sử dụng đúng mức và rất tốn kém cho các tổ chức để có thể duy trì nhiều máy chủ vật lý như vậy.

Thời đại triển khai ảo hóa: Như một giải pháp, ảo hóa đã được giới thiệu. Nó cho phép bạn chạy nhiều Máy ảo (VM) trên CPU của một máy chủ vật lý. Ảo hóa cho phép các ứng dụng được cô lập giữa các VM và cung cấp mức độ bảo mật vì thông tin của một ứng dụng không thể được truy cập tự do bởi một ứng dụng khác.

Ảo hóa cho phép sử dụng tốt hơn các tài nguyên trong một máy chủ vật lý và cho phép khả năng mở rộng tốt hơn vì một ứng dụng có thể được thêm hoặc cập nhật dễ dàng, giảm chi phí phần cứng và hơn thế nữa. Với ảo hóa, bạn có thể có một tập hợp các tài nguyên vật lý dưới dạng một cụm các máy ảo sẵn dùng.

Mỗi VM là một máy tính chạy tất cả các thành phần, bao gồm cả hệ điều hành riêng của nó, bên trên phần cứng được ảo hóa.

Thời đại triển khai Container: Các container tương tự như VM, nhưng chúng có tính cô lập để chia sẻ Hệ điều hành (HĐH) giữa các ứng dụng. Do đó, container được coi là nhẹ (lightweight). Tương tự như VM, một container có hệ thống tệp (filesystem), CPU, bộ nhớ, process space, v.v. Khi chúng được tách rời khỏi cơ sở hạ tầng bên dưới, chúng có thể khả chuyển (portable) trên cloud hoặc các bản phân phối Hệ điều hành.

Các container đã trở nên phổ biến vì chúng có thêm nhiều lợi ích, chẳng hạn như:

  • Tạo mới và triển khai ứng dụng Agile: gia tăng tính dễ dàng và hiệu quả của việc tạo các container image so với việc sử dụng VM image.
  • Phát triển, tích hợp và triển khai liên tục: cung cấp khả năng build và triển khai container image thường xuyên và đáng tin cậy với việc rollbacks dễ dàng, nhanh chóng.
  • Phân biệt giữa Dev và Ops: tạo các images của các application container tại thời điểm build/release thay vì thời gian triển khai, do đó phân tách các ứng dụng khỏi hạ tầng.
  • Khả năng quan sát không chỉ hiển thị thông tin và các metric ở mức Hệ điều hành, mà còn cả application health và các tín hiệu khác.
  • Tính nhất quán về môi trường trong suốt quá trình phát triển, testing và trong production: Chạy tương tự trên laptop như trên cloud.
  • Tính khả chuyển trên cloud và các bản phân phối HĐH: Chạy trên Ubuntu, RHEL, CoreOS, on-premises, Google Kubernetes Engine và bất kì nơi nào khác.
  • Quản lý tập trung ứng dụng: Tăng mức độ trừu tượng từ việc chạy một Hệ điều hành trên phần cứng ảo hóa sang chạy một ứng dụng trên một HĐH bằng logical resources.
  • Các micro-services phân tán, elastic: ứng dụng được phân tách thành các phần nhỏ hơn, độc lập và thể được triển khai và quản lý một cách linh hoạt - chứ không phải một app nguyên khối (monolithic).
  • Cô lập các tài nguyên: dự đoán hiệu năng ứng dụng
  • Sử dụng tài nguyên: hiệu quả

Tại sao bạn cần Kubernetes và nó có thể làm những gì?

Các container là một cách tốt để đóng gói và chạy các ứng dụng của bạn. Trong môi trường production, bạn cần quản lý các container chạy các ứng dụng và đảm bảo rằng không có khoảng thời gian downtime. Ví dụ, nếu một container bị tắt đi, một container khác cần phải khởi động lên. Điều này sẽ dễ dàng hơn nếu được xử lý bởi một hệ thống.

Đó là cách Kubernetes đến với chúng ta. Kubernetes cung cấp cho bạn một framework để chạy các hệ phân tán một cách mạnh mẽ. Nó đảm nhiệm việc nhân rộng và chuyển đổi dự phòng cho ứng dụng của bạn, cung cấp các mẫu deployment và hơn thế nữa. Ví dụ, Kubernetes có thể dễ dàng quản lý một triển khai canary cho hệ thống của bạn.

Kubernetes cung cấp cho bạn:

  • Service discovery và cân bằng tải
    Kubernetes có thể expose một container sử dụng DNS hoặc địa chỉ IP của riêng nó. Nếu lượng traffic truy cập đến một container cao, Kubernetes có thể cân bằng tải và phân phối lưu lượng mạng (network traffic) để việc triển khai được ổn định.
  • Điều phối bộ nhớ
    Kubernetes cho phép bạn tự động mount một hệ thống lưu trữ mà bạn chọn, như local storages, public cloud providers, v.v.
  • Tự động rollouts và rollbacks
    Bạn có thể mô tả trạng thái mong muốn cho các container được triển khai dùng Kubernetes và nó có thể thay đổi trạng thái thực tế sang trạng thái mong muốn với tần suất được kiểm soát. Ví dụ, bạn có thể tự động hoá Kubernetes để tạo mới các container cho việc triển khai của bạn, xoá các container hiện có và áp dụng tất cả các resource của chúng vào container mới.
  • Đóng gói tự động
    Bạn cung cấp cho Kubernetes một cluster gồm các node mà nó có thể sử dụng để chạy các tác vụ được đóng gói (containerized task). Bạn cho Kubernetes biết mỗi container cần bao nhiêu CPU và bộ nhớ (RAM). Kubernetes có thể điều phối các container đến các node để tận dụng tốt nhất các resource của bạn.
  • Tự phục hồi
    Kubernetes khởi động lại các containers bị lỗi, thay thế các container, xoá các container không phản hồi lại cấu hình health check do người dùng xác định và không cho các client biết đến chúng cho đến khi chúng sẵn sàng hoạt động.
  • Quản lý cấu hình và bảo mật
    Kubernetes cho phép bạn lưu trữ và quản lý các thông tin nhạy cảm như: password, OAuth token và SSH key. Bạn có thể triển khai và cập nhật lại secret và cấu hình ứng dụng mà không cần build lại các container image và không để lộ secret trong cấu hình stack của bạn.

Kubernetes không phải là gì?

Kubernetes không phải là một hệ thống PaaS (Nền tảng như một Dịch vụ) truyền thống, toàn diện. Do Kubernetes hoạt động ở tầng container chứ không phải ở tầng phần cứng, nó cung cấp một số tính năng thường áp dụng chung cho các dịch vụ PaaS, như triển khai, nhân rộng, cân bằng tải, ghi nhật ký và giám sát. Tuy nhiên, Kubernetes không phải là cấu trúc nguyên khối và các giải pháp mặc định này là tùy chọn và có thể cắm được (pluggable).

Kubernetes:

  • Không giới hạn các loại ứng dụng được hỗ trợ. Kubernetes nhằm mục đích hỗ trợ một khối lượng công việc cực kỳ đa dạng, bao gồm cả stateless, stateful và xử lý dữ liệu. Nếu một ứng dụng có thể chạy trong một container, nó sẽ chạy rất tốt trên Kubernetes.
  • Không triển khai mã nguồn và không build ứng dụng của bạn. Quy trình CI/CD được xác định bởi tổ chức cũng như các yêu cầu kỹ thuật.
  • Không cung cấp các service ở mức ứng dụng, như middleware (ví dụ, các message buses), các framework xử lý dữ liệu (ví dụ, Spark), cơ sở dữ liệu (ví dụ, MySQL), bộ nhớ cache, cũng như hệ thống lưu trữ của cluster (ví dụ, Ceph). Các thành phần như vậy có thể chạy trên Kubernetes và/hoặc có thể được truy cập bởi các ứng dụng chạy trên Kubernetes thông qua các cơ chế di động, chẳng hạn như Open Service Broker.
  • Không bắt buộc các giải pháp ghi lại nhật ký (logging), giám sát (monitoring) hoặc cảnh báo (alerting). Nó cung cấp một số sự tích hợp như proof-of-concept, và cơ chế để thu thập và xuất các số liệu.
  • Không cung cấp, không bắt buộc một cấu hình ngôn ngữ/hệ thống (ví dụ: Jsonnet). Nó cung cấp một API khai báo có thể được targeted bởi các hình thức khai báo tùy ý.
  • Không cung cấp cũng như áp dụng bất kỳ cấu hình toàn diện, bảo trì, quản lý hoặc hệ thống tự phục hồi.
  • Ngoài ra, Kubernetes không phải là một hệ thống điều phối đơn thuần. Trong thực tế, nó loại bỏ sự cần thiết của việc điều phối. Định nghĩa kỹ thuật của điều phối là việc thực thi một quy trình công việc được xác định: đầu tiên làm việc A, sau đó là B rồi sau chót là C. Ngược lại, Kubernetes bao gồm một tập các quy trình kiểm soát độc lập, có thể kết hợp, liên tục điều khiển trạng thái hiện tại theo trạng thái mong muốn đã cho. Nó không phải là vấn đề làm thế nào bạn có thể đi được từ A đến C. Kiểm soát tập trung cũng không bắt buộc. Điều này dẫn đến một hệ thống dễ sử dụng hơn, mạnh mẽ hơn, linh hoạt hơn và có thể mở rộng.

Tiếp theo là gì

5 - Kiến Trúc Cluster

5.1 - Leases

Trong các hệ thống phân tán, Kubernetes sử dụng cơ chế leases (giữ quyền tạm thời) để khóa tài nguyên dùng chung và điều phối hoạt động giữa các thành phần trong cụm. Trong Kubernetes, lease được biểu diễn bằng các đối tượng Lease thuộc API group coordination.k8s.io API Group, Những đối tượng này đóng vai trò quan trọng trong hệ thống, ví dụ như theo dõi trạng thái node (heartbeat) và cơ chế bầu chọn leader giữa các thành phần nội bộ.

Node heartbeats

Kubernetes sử dụng Lease API để truyền tín hiệu heartbeat từ kubelet về Kubernetes API server. Với mỗi đối tượng Node, sẽ có một đối tượng Lease tương ứng (có cùng tên) nằm trong namespace kube-node-lease. Ở tầng bên dưới, mỗi lần kubelet gửi heartbeat thực chất là một lệnh update lên đối tượng Lease, cập nhật trường spec.renewTime. Control plane của Kubernetes sẽ dựa vào dấu thời gian trong trường này để xác định node đó còn hoạt động hay không.

Xem Node Lease objects để biết thêm chi tiết.

Leader election

Kubernetes cũng sử dụng Lease để đảm bảo rằng chỉ một phiên bản duy nhất của một thành phần có thể hoạt động tại một thời điểm. Cơ chế này được áp dụng cho các thành phần của control plane như kube-controller-manager và kube-scheduler trong các cấu hình HA (High Availability) — nơi chỉ một phiên bản của thành phần đó được phép hoạt động chính, còn các phiên bản còn lại sẽ ở chế độ chờ.

Đọc thêm tại coordinated leader election để tìm hiểu cách Kubernetes sử dụng Lease API để chọn phiên bản nào sẽ đóng vai trò leader.

API server identity

TRẠNG THÁI TÍNH NĂNG: Beta since Kubernetes v1.26; enabled by default

Từ phiên bản Kubernetes v1.26, mỗi kube-apiserver sẽ sử dụng Lease API để công bố danh tính của mình với phần còn lại của hệ thống.Mặc dù việc này chưa mang lại lợi ích trực tiếp, nhưng nó cung cấp một cơ chế để các thành phần khác có thể phát hiện được có bao nhiêu phiên bản kube-apiserver đang hoạt động trong control plane. Việc tồn tại các Lease của kube-apiserver giúp chuẩn bị cho các tính năng trong tương lai, nơi các kube-apiserver có thể cần phối hợp hoạt động với nhau.

Bạn có thể kiểm tra các Lease này trong namespace kube-system, với tên theo dạng apiserver-<sha256-hash>. Hoặc sử dụng label để lọc apiserver.kubernetes.io/identity=kube-apiserver:

kubectl -n kube-system get lease -l apiserver.kubernetes.io/identity=kube-apiserver
NAME                                        HOLDER                                                                           AGE
apiserver-07a5ea9b9b072c4a5f3d1c3702        apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05        5m33s
apiserver-7be9e061c59d368b3ddaf1376e        apiserver-7be9e061c59d368b3ddaf1376e_84f2a85d-37c1-4b14-b6b9-603e62e4896f        4m23s
apiserver-1dfef752bcb36637d2763d1868        apiserver-1dfef752bcb36637d2763d1868_c5ffa286-8a9a-45d4-91e7-61118ed58d2e        4m43s

Chuỗi SHA256 hash được sử dụng trong tên của đối tượng Lease được tính dựa trên hostname của hệ điều hành mà kube-apiserver nhìn thấy. Do đó, mỗi kube-apiserver cần được cấu hình với một hostname duy nhất trong cụm Kubernetes để tránh xung đột. Khi có một instance mới của kube-apiserver được khởi động và sử dụng cùng hostname với một instance trước đó, nó sẽ tiếp quản Lease đã có sẵn bằng cách ghi đè holder identity, thay vì tạo một Lease mới.Bạn có thể kiểm tra hostname mà kube-apiserver đang sử dụng bằng cách tra nhãn kubernetes.io/hostname trên node hoặc pod tương ứng:

kubectl -n kube-system get lease apiserver-07a5ea9b9b072c4a5f3d1c3702 -o yaml
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  creationTimestamp: "2023-07-02T13:16:48Z"
  labels:
    apiserver.kubernetes.io/identity: kube-apiserver
    kubernetes.io/hostname: master-1
  name: apiserver-07a5ea9b9b072c4a5f3d1c3702
  namespace: kube-system
  resourceVersion: "334899"
  uid: 90870ab5-1ba9-4523-b215-e4d4e662acb1
spec:
  holderIdentity: apiserver-07a5ea9b9b072c4a5f3d1c3702_0c8914f7-0f35-440e-8676-7844977d3a05
  leaseDurationSeconds: 3600
  renewTime: "2023-07-04T21:58:48.065888Z"

Các Lease đã hết hạn từ những kube-apiserver không còn tồn tại sẽ được thu gom tự động (garbage collect) bởi các kube-apiserver mới sau 1 giờ.

Bạn có thể tắt cơ chế Lease định danh của API server bằng cách vô hiệu hóa feature gate APIServerIdentity. Xem thêm tại feature gate.

Workloads

Bạn có thể tự định nghĩa và sử dụng Lease cho chính workload của mình. Ví dụ: bạn có thể chạy một controller tùy chỉnh, trong đó một bản sao chính (primary hoặc leader) sẽ thực hiện các thao tác mà các bản sao còn lại không thực hiện. Bạn định nghĩa một đối tượng Lease để các bản sao của controller có thể chọn hoặc bầu chọn ra leader, sử dụng Kubernetes API để phối hợp hoạt động. Nếu bạn sử dụng Lease, cách thực hiện tốt là đặt tên Lease sao cho dễ liên kết với sản phẩm hoặc thành phần sử dụng nó. Ví dụ: nếu bạn có một thành phần tên là Example Foo, thì nên đặt Lease là example-foo.

Nếu người vận hành cluster hoặc người dùng cuối có thể triển khai nhiều phiên bản của cùng một thành phần, hãy chọn một tiền tố tên riêng biệt và áp dụng một cơ chế (chẳng hạn như hash của tên Deployment) để tránh trùng tên Lease.

Bạn cũng có thể sử dụng một cách tiếp cận khác, miễn là đạt được mục tiêu chung: các phần mềm khác nhau không gây xung đột với nhau khi sử dụng Lease.

5.2 - Các khái niệm nền tảng của Cloud Controller Manager

Khái niệm Cloud Controller Manager (CCM) (để tránh nhầm lẫn với bản binary build cùng tên) được định nghĩa riêng biệt để cho phép các bên cung cấp dịch vụ cloud và thành phần chính của Kubernetes phát triển độc lập với nhau. CCM chạy đồng thời với những thành phần khác thuộc máy chủ của một cluster như Controller Manager của Kubernetes, API server, và Scheduler. Nó cũng có thể đóng vai trò như một addon cho Kubernetes.

Cloud Controller Manager này được thiết kế dựa trên cơ chế plugin nhằm cho phép các bên Cloud Provider có thể tích hợp với Kubernetes một cách dễ dàng thông qua các plugin này. Đã có những bản kế hoạch được thiết kế sẵn nhằm mục đích hỗ trợ những cloud provider thay đổi từ mô hình cũ sang mô hình mới đi chung với CCM.

Tài liệu này thảo luận về những khái niệm đằng sau một CCM và đưa ra những chi tiết về chức năng liên quan của nó.

Dưới đây là kiến trúc của một Kubernetes cluster khi không đi cùng với Cloud Controller Manager:

Kiến trúc CCM Kube trước đây

Thiết kế

Trong sơ đồ trên, Kubernetes và nhà cung cấp dịch vụ cloud được tích hợp thông qua một số thành phần sau:

  • Kubelet
  • Kubernetes Controller Manager
  • Kubernetes API server

CCM hợp nhất tất cả các logic phụ thuộc trên một nền tàng Cloud từ 3 thành phần trên để tạo thành một điểm tích hợp duy nhất với hệ thống Cloud. Sơ đồ kiến trúc khi đi kèm với CCM sẽ trở thành:

Kiến trúc CCM Kube hiện tại

Các thành phần của CCM

Cloud Controller Manager phân nhỏ một số chức năng của Kubernetes controller manager (KCM) và chạy nó như một tiến trình tách biệt. Cụ thể hơn, nó phân nhỏ những controller trong Kubernetes Controller Manager phụ thuộc vào Cloud. Kubernetes Controller Manager sẽ có những controller nhỏ hơn:

  • Node controller
  • Volume controller
  • Route controller
  • Service controller

Tại phiên bản 1.9, CCM thực hiện chạy những controller sau từ trong danh sách trên:

  • Node controller
  • Route controller
  • Service controller

Lưu ý:

Volume controller được bỏ ra khỏi Cloud Controller Manager. Do độ phức tạp lớn ảnh hướng và sẽ tốn nhiều thời gian cũng như nhân lực không đáp ứng đủ cho việc tách hẳn tầng logic liên quan tới Volume từ những bên cung cấp dịch vụ, và quyết định cuối cùng là sẽ không triển khai quản lý Volume như một phần của CCM.

Kết hoạch ban đầu của dự án là hỗ trợ Volume sử dụng Cloud Controller Manager để áp dụng những Flex Volume linh hoạt nhằm dễ dàng tích hợp bổ sung thêm. Tuy nhiên, một giải pháp khác cũng đang được lên kế hoạch để thay thế Flex Volume được biết là CSI.

Sau khi xem xét về khía cạnh này, chúng tôi quyết định sẽ có một khoảng thời gian nghỉ trước khi CSI trở nên sẵn sàng cho việc sử dụng.

Chức năng của Cloud Controller Manager

CCM thừa hưởng những tính năng của nó từ các thành phần trong Kubernetes phụ thuộc vào các Cloud Provider. Phần kế tiếp sẽ giới thiệu những thành phần này.

1. Kubernetes Conntroller Manager

Phần lớn các tính năng của CCM bắt nguồn từ Kubernetes controller manager. Như đã đề cập ở phần trước, CCM bao gồm:

  • Node controller
  • Route controller
  • Service controller

Node controller

Node controller có vai trò khởi tạo một Node bằng cách thu thập thông tin về những Node đang chạy trong cluster từ các cloud provider.

Node controller sẽ thực hiện những chức năng sau:

  1. Khởi tạo một Node với các nhãn region/zone.
  2. Khởi tạo một Node với những thông tin được cung cấp từ cloud, ví dụ như loại máy và kích cỡ.
  3. Thu thập địa chỉ mạng của Node và hostname.
  4. Trong trường hợp một Node không có tín hiệu phản hồi, Node controller sẽ kiểm tra xem Node này có thực sự xóa khỏi hệ thống cloud hay chưa. Nếu Node đó không còn tồn tại trên cloud, controller sẽ xóa Node đó khỏi Kubernetes cluster.

Route controller

Route controller đóng vai trò cấu hình định tuyến trong nằm trong hệ thống cloud để các container trên các Node khác nhau trong Kubernetes cluster có thể giao tiếp với nhau. Route controller hiện chỉ đáp ứng được cho các Google Compute Engine cluster.

Service controller

Service controller lắng nghe các sự kiện tạo mới, cập nhật và xoá bỏ một service. Dựa trên trạng thái hiện tại của các vụ trên Kubernetes, nó cấu hình các dịch vụ cân bằng tải trên cloud (như ELB của AWS, Google Load Balancer, hay Oracle Cloud Infrastructure LB) nhằm phản ánh trạng thái của các Service trên Kubernetes. Ngoài ra, nó đảm bảo những service backends cho các dịch vụ cần bằng tải trên cloud được cập nhật

2. Kubelet

Node controller bao gồm một số tính năng phụ thuộc vào tầng cloud của Kubelet. Trước khi có CCM, Kubelet đảm nhận vai trò khởi tạo một Node với thông tin chi tiết từ cloud như địa chỉ IP, region hay instance type. Với CCM, vai trò này được CCM đảm nhận thay cho Kubelet.

Với mô hình mới này, Kubelet sẽ khởi tạo một Node nhưng không đi kèm với những thông tin từ cloud. Tuy nhiên, nó sẽ thêm vào một dấu Taint để đánh dấu Node sẽ không được lập lịch cho tới khi CCM khởi tạo xong Node này với những thông tin cụ thể cung cấp từ Cloud, sau đó nó sẽ xóa những dấu chờ này.

Cơ chế Plugin

CCM sử dụng interface trong ngôn ngữ Go cho phép triển khai trên bất kì hệ thống cloud nào cũng có thể plugged in. Cụ thể hơn, nó sử dụng CloudProvider Interface được định nghĩa ở đây.

Cách triển khai của bốn controller được nêu ở trên, và một số được thực hiện như giao diện chung cho các bên cung cấp dịch vụ cloud, sẽ ở trong lõi (core) của Kubernetes. Việc triển khai dành riêng cho từng cloud provider sẽ được xây dựng bên ngoài lõi (core) và triển khai các giao diện được xác định bên trong lõi.

Để biết thêm chỉ tiết, xem Cloud Controller Manager.

Phân quyền

Phần này sẽ phân nhỏ quyền truy cập cần có cho các API object cung cấp bởi CCM để thực hiện những hành động của nó.

Node controller

Node controller chỉ hoạt động với các Node. Nó yêu cầu đầy đủ quyền truy cập bao gồm get, list, create, update, patch, watch, và delete một Node.

v1/Node:

  • Get
  • List
  • Create
  • Update
  • Patch
  • Watch
  • Delete

Route controller

Route controller lắng nghe sự kiện tạo ra các Node và cấu hình các Route tương ứng. Nó yêu cầu có quyền truy cập get tới các đối Node.

v1/Node:

  • Get

Service controller

Service controller lắng nghe các sự kiện khởi tạo, cập nhật và xóa bỏ một Service và cấu hình những endpoint phù hợp.

Để truy cập các Service, nó cần quyền list, và watch. Để cập nhật Service, nó sẽ cần patch và update.

Để thiết lập các endpoint cho các Service, nó cần quyền create, list, get, watch, và update.

v1/Service:

  • List
  • Get
  • Watch
  • Patch
  • Update

Các vấn đề khác

Việc triển khai lõi của CCM yêu cầu cần có quyền tạo mới sự kiện và đảm bảo quyền thực thi một số hành động, nó cần có quyền tạo các Service Accounts

v1/Event:

  • Create
  • Patch
  • Update

v1/ServiceAccount:

  • Create

Với RBAC ClusterRole, CCM cần có ClusterRole tối thiểu:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cloud-controller-manager
rules:
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
      - update
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - "*"
  - apiGroups:
      - ""
    resources:
      - nodes/status
    verbs:
      - patch
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - list
      - patch
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - serviceaccounts
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - persistentvolumes
    verbs:
      - get
      - list
      - update
      - watch
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - create
      - get
      - list
      - watch
      - update

Các nhà cung cấp đã triển khai

Sau đây là danh sách các nhà cung cấp dịch vụ cloud đã triển khai CCM:

Quản lý Cluster

Hướng dẫn chi tiết cho việc cấu hình và chạy CCM được cung cấp tại đây.

5.3 - Kubernetes - Khả năng tự phục hồi (Self-Healing)

Kubernetes được thiết kế với khả năng tự phục hồi giúp duy trì tình trạng khỏe mạnh và tính khả dụng của các workload. Nó tự động thay thế các container bị lỗi, lập lịch lại các workload khi node trở nên không khả dụng, và đảm bảo rằng trạng thái mong muốn của hệ thống được duy trì.

Khả năng tự phục hồi

  • Khởi động lại ở cấp độ container: Nếu một container bên trong Pod bị lỗi, Kubernetes sẽ khởi động lại nó dựa trên restartPolicy.

  • Thay thế replica: Nếu một Pod trong Deployment hoặc StatefulSet bị lỗi, Kubernetes sẽ tạo một Pod thay thế để duy trì số lượng replica được chỉ định. Nếu một Pod thuộc DaemonSet bị lỗi, control plane sẽ tạo một Pod thay thế để chạy trên cùng node đó.

  • Phục hồi persistent storage: Nếu một node đang chạy Pod với PersistentVolume (PV) được gắn kết, và node đó bị lỗi, Kubernetes có thể gắn lại volume vào Pod mới trên node khác.

  • Cân bằng tải cho Service: Nếu một Pod đằng sau Service bị lỗi, Kubernetes sẽ tự động loại bỏ nó khỏi endpoint của Service để chỉ định tuyến traffic đến các Pod khỏe mạnh.

Dưới đây là một số thành phần chính cung cấp khả năng tự phục hồi của Kubernetes:

  • kubelet: Đảm bảo rằng các container đang chạy, và khởi động lại những container bị lỗi.

  • ReplicaSet, StatefulSet và DaemonSet controller: Duy trì số lượng replica Pod mong muốn.

  • PersistentVolume controller: Quản lý việc gắn kết và tháo gỡ volume cho các stateful workload.

Những điều cần cân nhắc

  • Lỗi Storage: Nếu một persistent volume trở nên không khả dụng, có thể cần các bước phục hồi thủ công.

  • Lỗi Ứng dụng: Kubernetes có thể khởi động lại container, nhưng các vấn đề ứng dụng cơ bản phải được giải quyết riêng biệt.

Tiếp theo là gì

5.4 - Proxy Đa Phiên Bản (Mixed Version Proxy)

TRẠNG THÁI TÍNH NĂNG: Beta since Kubernetes v1.36; enabled by default

Kubernetes 1.37 giới thiệu một tính năng alpha cho phép API Server proxy các yêu cầu tài nguyên đến các API server ngang hàng khác. Tính năng này đặc biệt hữu ích khi trong một cụm có nhiều API server đang chạy các phiên bản Kubernetes khác nhau (ví dụ: trong quá trình nâng cấp cụm kéo dài sang phiên bản mới).

Điều này giúp quản trị viên của cluster cấu hình các cluster có độ sẵn sàng cao (high availability) có thể được nâng cấp một cách an toàn hơn, bằng cách chuyển hướng các yêu cầu tài nguyên (trong thời gian nâng cấp) đến đúng kube-api server. Cơ chế proxy này giúp người dùng tránh gặp lỗi 404 (Not Found) không mong muốn do quá trình nâng cấp gây ra.

Cơ chế này được gọi là Proxy Đa Phiên Bản.

Bật tính năng Proxy Đa Phiên Bản

Đảm bảo rằng cờ feature gate UnknownVersionInteroperabilityProxy đã được bật khi khởi động API Server:

kube-apiserver \
--feature-gates=UnknownVersionInteroperabilityProxy=true \
# các tham số dòng lệnh bắt buộc cho tính năng này
--peer-ca-file=<đường dẫn đến CA cert của kube-apiserver>
--proxy-client-cert-file=<đường dẫn đến chứng chỉ proxy của aggregator>,
--proxy-client-key-file=<đường dẫn đến khóa proxy của aggregator>,
--requestheader-client-ca-file=<đường dẫn đến CA cert của aggregator>,
# requestheader-allowed-names có thể để trống để chấp nhận mọi Common Name
--requestheader-allowed-names=<các Common Name hợp lệ để xác minh chứng chỉ proxy client>,

# các cờ tuỳ chọn cho tính năng này
--peer-advertise-ip=<Địa chỉ IP của kube-apiserver này để peer sử dụng khi proxy> --peer-advertise-port=<Cổng mà kube-apiserver này mở để peer sử dụng khi proxy>

# …và các cờ khác như thường lệ

Kết nối và xác thực giữa các API server

  • API server nguồn tái sử dụng các cờ xác thực client cho API server hiện có là --proxy-client-cert-file và --proxy-client-key-file để trình bày danh tính của nó, danh tính này sẽ được kube-apiserver đích (destination kube-apiserver) xác minh. API server đích sẽ xác thực kết nối từ phía đối tác dựa trên cấu hình được chỉ định thông qua tham số dòng lệnh --requestheader-client-ca-file.

  • Để xác thực chứng chỉ phục vụ (serving cert) của API server đích, bạn cần cấu hình một gói chứng chỉ CA bằng cách cung cấp tham số dòng lệnh --peer-ca-file cho API server nguồn.

Cấu hình kết nối tới peer API server

Để khai báo địa chỉ mạng của một API server mà các peer sẽ sử dụng để proxy yêu cầu, sử dụng các tham số --peer-advertise-ip và --peer-advertise-port, hoặc khai báo trong tệp cấu hình của API server.

Nếu không khai báo các cờ này, kube-apiserver sẽ mặc định sử dụng giá trị từ --advertise-address hoặc --bind-address. Nếu các tham số này cũng không được cấu hình, địa chỉ giao diện mạng mặc định của máy chủ sẽ được dùng.

Cơ chế proxy giữa các phiên bản khác nhau

Khi proxy đa phiên bản được bật, aggregation layer sẽ tải một bộ lọc đặc biệt với các hành vi sau:

  • Khi một API server nhận được yêu cầu tài nguyên mà nó không thể xử lý (do chưa hỗ trợ API đó hoặc API bị tắt), nó sẽ tìm cách proxy yêu cầu đến một API server ngang hàng có thể xử lý yêu cầu đó.

Việc này được thực hiện bằng cách xác định các nhóm / phiên bản / tài nguyên API mà server hiện tại không nhận biết, rồi chuyển tiếp yêu cầu tới một peer có thể xử lý được.

  • Nếu API server peer gặp lỗi khi phản hồi, API server nguồn sẽ trả về lỗi 503 ("Service Unavailable").

Cách hoạt động bên trong

Khi một API server nhận yêu cầu tài nguyên, nó kiểm tra xem API server nào có thể xử lý yêu cầu đó. Việc kiểm tra này dựa trên API StorageVersion nội bộ.

  • Nếu tài nguyên đã được API server nhận biết (ví dụ: GET /api/v1/pods/some-pod), yêu cầu sẽ được xử lý tại chỗ.

  • Nếu không có đối tượng StorageVersion nào tương ứng với tài nguyên yêu cầu (ví dụ: GET /my-api/v1/my-resource) nhưng APIService tương ứng được cấu hình để proxy đến một API server mở rộng, thì yêu cầu sẽ được xử lý theo luồng proxy mở rộng.

  • Nếu có một đối tượng StorageVersion hợp lệ cho tài nguyên được yêu cầu (ví dụ: GET /batch/v1/jobs) và API server đang cố xử lý yêu cầu đó (gọi là handling API server) đã tắt nhóm API batch, thì handling API server sẽ truy xuất danh sách các API server ngang hàng (peer API servers) có phục vụ nhóm API / phiên bản / tài nguyên tương ứng (trong trường hợp này là api/batch/v1) dựa trên thông tin từ đối tượng StorageVersion đã truy xuất. Sau đó, handling API server sẽ chuyển tiếp (proxy) yêu cầu đến một trong những kube-apiserver ngang hàng phù hợp, vốn có khả năng xử lý tài nguyên được yêu cầu.

    • Nếu không có peer nào khả dụng cho nhóm / phiên bản / tài nguyên đó, API server sẽ trả lại lỗi 404 ("Not Found").

    • Nếu có peer phù hợp nhưng không phản hồi (do lỗi mạng, hoặc thông tin về peer chưa được đăng ký đúng lúc), thì API server sẽ trả về lỗi 503 ("Service Unavailable").

6 - Secrets

Trang này đang được dịch. Vui lòng xem phiên bản tiếng Anh để biết thông tin mới nhất.

7 - Container

Công nghệ đóng gói một ứng dụng cùng với các thành phần phụ thuộc mà ứng dụng cần khi chạy.

Trang này trình bày về container và container image, cũng như cách sử dụng chúng trong vận hành và phát triển giải pháp.

Từ container được dùng với nhiều nghĩa khác nhau. Mỗi khi dùng từ này, bạn hãy kiểm tra xem người nghe có hiểu theo cùng một định nghĩa với bạn hay không.

Mỗi container mà bạn chạy đều có thể tái lập (repeatable); việc chuẩn hóa bằng cách đóng gói kèm các thành phần phụ thuộc giúp bạn nhận được cùng một hành vi ở bất cứ nơi nào bạn chạy container đó.

Container tách rời ứng dụng khỏi hạ tầng máy chủ (host) bên dưới. Điều này giúp việc triển khai trở nên dễ dàng hơn trên các môi trường đám mây hoặc hệ điều hành khác nhau.

Mỗi node trong một cluster Kubernetes chạy các container tạo nên các Pod được gán cho node đó. Các container trong một Pod luôn được đặt cùng vị trí (co-located) và lập lịch cùng nhau (co-scheduled) để chạy trên cùng một node.

Container image

Một container image là một gói phần mềm sẵn sàng để chạy, chứa mọi thứ cần thiết để chạy một ứng dụng: mã chương trình và mọi runtime mà nó cần, các thư viện ứng dụng và thư viện hệ thống, cùng với giá trị mặc định cho mọi thiết lập thiết yếu.

Container được thiết kế theo hướng stateless và bất biến: bạn không nên thay đổi mã của một container đang chạy. Nếu bạn có một ứng dụng đã được container hóa và muốn thay đổi nó, quy trình đúng là build một image mới có chứa thay đổi đó, sau đó tạo lại container để khởi động từ image đã được cập nhật.

Container runtime

Một thành phần cơ bản mà ủy quyền cho Kubernetes chạy các container một cách có hiệu quả. Nó chịu trách nhiệm quản lý việc thực thi và vòng đời của các container trong môi trường Kubernetes.

Kubernetes hỗ trợ các container runtime như containerd, CRI-O, và bất kỳ những triển khai nào của Kubernetes CRI (Container Runtime Interface).

Thông thường, bạn có thể để cluster tự chọn container runtime mặc định cho một Pod. Nếu bạn cần dùng nhiều hơn một container runtime trong cluster, bạn có thể chỉ định RuntimeClass cho Pod để đảm bảo rằng Kubernetes chạy các container đó bằng một container runtime cụ thể.

Bạn cũng có thể dùng RuntimeClass để chạy các Pod khác nhau với cùng một container runtime nhưng với các thiết lập khác nhau.

7.1 - Image

Một container image là dữ liệu nhị phân đóng gói một ứng dụng cùng toàn bộ các thành phần phụ thuộc phần mềm của nó. Container image là các gói phần mềm có thể thực thi, có thể chạy độc lập và có những giả định được xác định rất rõ ràng về môi trường runtime của chúng.

Thông thường, bạn tạo một container image cho ứng dụng của mình và push nó lên một registry trước khi tham chiếu tới nó trong một Pod.

Trang này trình bày tổng quan về khái niệm container image.

Lưu ý:

Nếu bạn đang tìm các container image cho một bản phát hành Kubernetes (chẳng hạn v1.37, bản phát hành minor mới nhất), hãy truy cập Tải xuống Kubernetes.

Tên image

Container image thường được đặt tên như pause, example/mycontainer hoặc kube-apiserver. Image cũng có thể bao gồm hostname của registry; ví dụ: fictional.registry.example/imagename, và có thể có cả số cổng; ví dụ: fictional.registry.example:10443/imagename.

Nếu bạn không chỉ định hostname của registry, Kubernetes sẽ giả định rằng bạn muốn dùng Docker public registry. Bạn có thể thay đổi hành vi này bằng cách thiết lập một image registry mặc định trong cấu hình của container runtime.

Sau phần tên image, bạn có thể thêm tag hoặc digest (giống như khi dùng với các lệnh như docker hoặc podman). Tag giúp bạn phân biệt các phiên bản khác nhau trong cùng một loạt image. Digest là định danh duy nhất cho một phiên bản cụ thể của image. Digest là giá trị băm (hash) của nội dung image, và không thể thay đổi. Tag có thể được chuyển sang trỏ tới các image khác, nhưng digest thì cố định.

Tag của image gồm các chữ cái thường và chữ hoa, chữ số, dấu gạch dưới (_), dấu chấm (.) và dấu gạch ngang (-). Một tag có thể dài tối đa 128 ký tự, và phải tuân theo mẫu regex sau: [a-zA-Z0-9_][a-zA-Z0-9._-]{0,127}. Bạn có thể đọc thêm và tìm regex dùng để kiểm tra tính hợp lệ trong OCI Distribution Specification. Nếu bạn không chỉ định tag, Kubernetes sẽ giả định rằng bạn muốn dùng tag latest.

Digest của image gồm một thuật toán băm (chẳng hạn sha256) và một giá trị băm. Ví dụ: sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07. Bạn có thể tìm thêm thông tin về định dạng digest trong OCI Image Specification.

Một số ví dụ về tên image mà Kubernetes có thể sử dụng:

  • busybox — Chỉ có tên image, không có tag hay digest. Kubernetes sẽ dùng Docker public registry và tag latest. Tương đương với docker.io/library/busybox:latest.
  • busybox:1.32.0 — Tên image kèm tag. Kubernetes sẽ dùng Docker public registry. Tương đương với docker.io/library/busybox:1.32.0.
  • registry.k8s.io/pause:latest — Tên image với registry tùy chỉnh và tag latest.
  • registry.k8s.io/pause:3.5 — Tên image với registry tùy chỉnh và tag không phải latest.
  • registry.k8s.io/pause@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 — Tên image kèm digest.
  • registry.k8s.io/pause:3.5@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 — Tên image kèm tag và digest. Khi pull image, chỉ digest được sử dụng.

Cập nhật image

Khi bạn tạo lần đầu một Deployment, StatefulSet, Pod, hoặc đối tượng khác có chứa PodTemplate, và chính sách pull (pull policy) không được chỉ định rõ ràng, thì theo mặc định, chính sách pull của tất cả các container trong Pod đó sẽ được đặt thành IfNotPresent. Chính sách này khiến kubelet bỏ qua việc pull một image nếu image đó đã tồn tại.

Chính sách pull image

imagePullPolicy của một container và tag của image cùng ảnh hưởng tới thời điểm kubelet cố gắng pull (tải xuống) image được chỉ định.

Dưới đây là danh sách các giá trị bạn có thể đặt cho imagePullPolicy và tác dụng của từng giá trị:

IfNotPresent
image chỉ được pull nếu nó chưa có sẵn ở máy cục bộ.
Always
mỗi khi kubelet khởi chạy một container, kubelet yêu cầu container runtime pull image. Container runtime liên hệ với registry, phân giải tag hoặc tên image thành một digest, và tải xuống các layer chưa được lưu đệm (cache) ở máy cục bộ. Nếu tất cả các layer đã có sẵn, container runtime sẽ dùng image đã được cache mà không tải lại. Bản thân kubelet không kiểm tra xem image đã được cache ở máy cục bộ hay chưa; kubelet luôn giao việc này cho container runtime.
Never
kubelet không cố gắng tải image. Nếu bằng cách nào đó image đã có sẵn ở máy cục bộ, kubelet sẽ cố gắng khởi động container; nếu không, quá trình khởi động sẽ thất bại. Xem image được pull sẵn để biết thêm chi tiết.

Nhờ cơ chế cache của container runtime, ngay cả imagePullPolicy: Always cũng hoạt động hiệu quả, miễn là registry luôn truy cập được ổn định. Container runtime có thể nhận biết rằng các layer của image đã tồn tại trên node nên không cần tải xuống lại.

Lưu ý:

Bạn nên tránh dùng tag :latest khi triển khai container trong môi trường production vì khi đó sẽ khó theo dõi phiên bản nào của image đang chạy hơn và khó rollback đúng cách hơn.

Thay vào đó, hãy chỉ định một tag có ý nghĩa như v1.42.0 và/hoặc một digest.

Để đảm bảo Pod luôn dùng cùng một phiên bản của container image, bạn có thể chỉ định digest của image; thay <image-name>:<tag> bằng <image-name>@<digest> (ví dụ, image@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2).

Khi dùng tag của image, nếu image registry thay đổi phần mã mà tag của image đó đại diện, bạn có thể gặp tình trạng lẫn lộn giữa các Pod chạy mã cũ và mã mới. Digest của image định danh duy nhất một phiên bản cụ thể của image, vì vậy Kubernetes sẽ chạy cùng một mã mỗi khi khởi động một container với tên image và digest đó. Việc chỉ định image bằng digest sẽ cố định mã mà bạn chạy, nhờ đó thay đổi ở registry không thể gây ra tình trạng lẫn lộn phiên bản như vậy.

Có những admission controller của bên thứ ba có thể thay đổi (mutate) các Pod (và PodTemplate) khi chúng được tạo, sao cho workload đang chạy được xác định dựa trên digest của image thay vì tag. Điều đó có thể hữu ích nếu bạn muốn đảm bảo toàn bộ workload của mình đang chạy cùng một mã, bất kể tag ở registry thay đổi thế nào.

Chính sách pull image mặc định

Khi bạn (hoặc một controller) gửi một Pod mới tới API server, cluster của bạn sẽ đặt trường imagePullPolicy khi thỏa mãn các điều kiện cụ thể sau:

  • nếu bạn bỏ qua trường imagePullPolicy và chỉ định digest cho container image, imagePullPolicy sẽ tự động được đặt thành IfNotPresent.
  • nếu bạn bỏ qua trường imagePullPolicy và tag của container image là :latest, imagePullPolicy sẽ tự động được đặt thành Always.
  • nếu bạn bỏ qua trường imagePullPolicy và không chỉ định tag cho container image, imagePullPolicy sẽ tự động được đặt thành Always.
  • nếu bạn bỏ qua trường imagePullPolicy và chỉ định một tag cho container image không phải là :latest, imagePullPolicy sẽ tự động được đặt thành IfNotPresent.

Lưu ý:

Giá trị imagePullPolicy của container luôn được đặt khi đối tượng được tạo lần đầu, và không được cập nhật nếu tag hoặc digest của image thay đổi sau đó.

Ví dụ, nếu bạn tạo một Deployment với image có tag không phải là :latest, và sau đó cập nhật image của Deployment đó sang tag :latest, trường imagePullPolicy sẽ không chuyển thành Always. Bạn phải tự thay đổi chính sách pull của bất kỳ đối tượng nào sau khi nó được tạo lần đầu.

Bắt buộc pull image

Nếu bạn muốn luôn bắt buộc pull, bạn có thể làm theo một trong các cách sau:

  • Đặt imagePullPolicy của container thành Always.
  • Bỏ qua imagePullPolicy và dùng :latest làm tag cho image cần dùng; Kubernetes sẽ đặt chính sách thành Always khi bạn gửi Pod.
  • Bỏ qua imagePullPolicy và tag cho image cần dùng; Kubernetes sẽ đặt chính sách thành Always khi bạn gửi Pod.
  • Bật admission controller AlwaysPullImages.

ImagePullBackOff

Khi kubelet bắt đầu tạo các container cho một Pod bằng container runtime, có thể container sẽ ở trạng thái Waiting do ImagePullBackOff.

Trạng thái ImagePullBackOff có nghĩa là container không thể khởi động vì Kubernetes không thể pull container image (vì những lý do như tên image không hợp lệ, hoặc pull từ một registry riêng tư mà không có imagePullSecret). Phần BackOff cho biết Kubernetes sẽ tiếp tục thử pull image, với độ trễ back-off tăng dần.

Kubernetes tăng độ trễ giữa các lần thử cho tới khi đạt giới hạn được biên dịch sẵn, là 300 giây (5 phút).

Pull image theo runtime class

TRẠNG THÁI TÍNH NĂNG: Alpha since Kubernetes v1.29; disabled by default
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the RuntimeClassInImageCriApi feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Kubernetes có hỗ trợ ở mức alpha cho việc pull image dựa trên RuntimeClass của Pod.

Nếu bạn bật feature gate RuntimeClassInImageCriApi, kubelet sẽ tham chiếu container image bằng một bộ (tuple) gồm tên image và runtime handler thay vì chỉ dùng tên image hoặc digest. Container runtime của bạn có thể điều chỉnh hành vi dựa trên runtime handler được chọn. Pull image dựa trên runtime class hữu ích cho các container chạy trên máy ảo (VM), chẳng hạn container Windows Hyper-V.

Pull image tuần tự và song song

Theo mặc định, kubelet pull các image một cách tuần tự. Nói cách khác, tại mỗi thời điểm kubelet chỉ gửi một yêu cầu pull image tới image service. Các yêu cầu pull image khác phải đợi cho tới khi yêu cầu đang được xử lý hoàn tất.

Mỗi node đưa ra quyết định pull image một cách độc lập. Ngay cả khi bạn dùng chế độ pull image tuần tự, hai node khác nhau vẫn có thể pull cùng một image song song.

Nếu bạn muốn bật pull image song song, bạn có thể đặt trường serializeImagePulls thành false trong cấu hình kubelet. Khi serializeImagePulls được đặt thành false, các yêu cầu pull image sẽ được gửi ngay tới image service, và nhiều image sẽ được pull cùng lúc.

Khi bật pull image song song, hãy đảm bảo rằng image service của container runtime có thể xử lý việc pull image song song.

Kubelet không bao giờ pull nhiều image song song cho cùng một Pod. Ví dụ, nếu bạn có một Pod gồm một init container và một container ứng dụng, việc pull image cho hai container này sẽ không diễn ra song song. Tuy nhiên, nếu bạn có hai Pod dùng các image khác nhau, và tính năng pull image song song được bật, kubelet sẽ pull các image song song cho hai Pod khác nhau đó.

Số lượng pull image song song tối đa

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.35

Khi serializeImagePulls được đặt thành false, theo mặc định kubelet không giới hạn số lượng image tối đa được pull cùng lúc. Nếu bạn muốn giới hạn số lượng pull image song song, bạn có thể đặt trường maxParallelImagePulls trong cấu hình kubelet. Khi maxParallelImagePulls được đặt thành n, chỉ n image có thể được pull cùng lúc, và mọi lượt pull image vượt quá n sẽ phải đợi cho tới khi ít nhất một lượt pull image đang diễn ra hoàn tất.

Việc giới hạn số lượng pull image song song giúp tránh việc pull image tiêu tốn quá nhiều băng thông mạng hoặc I/O ổ đĩa, khi pull image song song được bật.

Bạn có thể đặt maxParallelImagePulls thành một số dương lớn hơn hoặc bằng 1. Nếu bạn đặt maxParallelImagePulls lớn hơn hoặc bằng 2, bạn phải đặt serializeImagePulls thành false. Kubelet sẽ không khởi động được nếu thiết lập maxParallelImagePulls không hợp lệ.

Image đa kiến trúc với image index

Ngoài việc cung cấp các image nhị phân, một container registry cũng có thể cung cấp một container image index. Một image index có thể trỏ tới nhiều image manifest cho các phiên bản dành riêng cho từng kiến trúc của một container. Ý tưởng là bạn có thể có một tên cho image (ví dụ: pause, example/mycontainer, kube-apiserver) và cho phép các hệ thống khác nhau tải về đúng image nhị phân cho kiến trúc máy mà chúng đang sử dụng.

Dự án Kubernetes thường tạo container image cho các bản phát hành của mình với tên có hậu tố -$(ARCH). Để tương thích ngược, hãy tạo các image cũ hơn kèm hậu tố. Ví dụ, một image có tên pause sẽ là một image đa kiến trúc chứa manifest cho tất cả các kiến trúc được hỗ trợ, trong khi pause-amd64 sẽ là phiên bản tương thích ngược cho các cấu hình cũ hơn, hoặc cho các file YAML có tên image kèm hậu tố được viết cứng (hardcode).

Sử dụng registry riêng tư

Registry riêng tư (private registry) có thể yêu cầu xác thực thì mới khám phá và/hoặc pull được image từ đó. Thông tin xác thực (credential) có thể được cung cấp theo nhiều cách:

Các tùy chọn này được giải thích chi tiết hơn ở bên dưới.

Chỉ định imagePullSecrets trên một Pod

Lưu ý:

Đây là cách được khuyến nghị để chạy các container dựa trên image trong registry riêng tư.

Kubernetes hỗ trợ chỉ định khóa của container image registry trên một Pod. Tất cả imagePullSecrets phải là các Secret tồn tại trong cùng Namespace với Pod. Các Secret này phải có kiểu kubernetes.io/dockercfg hoặc kubernetes.io/dockerconfigjson.

Cấu hình node để xác thực với registry riêng tư

Hướng dẫn cụ thể về cách thiết lập thông tin xác thực phụ thuộc vào container runtime và registry mà bạn chọn sử dụng. Bạn nên tham khảo tài liệu của giải pháp mà bạn dùng để có thông tin chính xác nhất.

Để xem ví dụ về cách cấu hình một container image registry riêng tư, hãy xem tác vụ Pull một Image từ Registry riêng tư. Ví dụ đó sử dụng một registry riêng tư trên Docker Hub.

Kubelet credential provider cho việc pull image có xác thực

Bạn có thể cấu hình kubelet để gọi một plugin dạng tệp nhị phân (binary) nhằm lấy thông tin xác thực registry cho một container image một cách động. Đây là cách mạnh mẽ và linh hoạt nhất để lấy thông tin xác thực cho registry riêng tư, nhưng cũng đòi hỏi phải cấu hình ở cấp kubelet để bật.

Kỹ thuật này đặc biệt hữu ích khi chạy các static Pod cần container image được lưu trữ trong registry riêng tư. Bạn không thể dùng ServiceAccount hoặc Secret để cung cấp thông tin xác thực cho registry riêng tư trong đặc tả của một static Pod, vì static Pod không thể tham chiếu tới các tài nguyên API khác trong đặc tả của mình.

Xem Cấu hình kubelet image credential provider để biết thêm chi tiết.

Cách diễn giải config.json

Cách diễn giải config.json khác nhau giữa bản triển khai Docker gốc và cách diễn giải của Kubernetes. Trong Docker, các key trong auths chỉ có thể chỉ định URL gốc, trong khi Kubernetes cho phép cả URL dạng glob lẫn đường dẫn khớp theo tiền tố. Hạn chế duy nhất là các mẫu glob (*) phải bao gồm dấu chấm (.) cho mỗi subdomain. Số lượng subdomain được khớp phải bằng số lượng mẫu glob (*.), ví dụ:

  • *.kubernetes.io sẽ không khớp với kubernetes.io, nhưng sẽ khớp với abc.kubernetes.io.
  • *.*.kubernetes.io sẽ không khớp với abc.kubernetes.io, nhưng sẽ khớp với abc.def.kubernetes.io.
  • prefix.*.io sẽ khớp với prefix.kubernetes.io.
  • *-good.kubernetes.io sẽ khớp với prefix-good.kubernetes.io.

Điều này có nghĩa là một config.json như sau là hợp lệ:

{
    "auths": {
        "my-registry.example/images": { "auth": "…" },
        "*.my-registry.example/images": { "auth": "…" }
    }
}

Các thao tác pull image sẽ chuyển thông tin xác thực tới CRI container runtime cho mọi mẫu hợp lệ. Ví dụ, các tên container image sau sẽ được khớp thành công:

  • my-registry.example/images
  • my-registry.example/images/my-image
  • my-registry.example/images/another-image
  • sub.my-registry.example/images/my-image

Tuy nhiên, các tên container image sau sẽ không được khớp:

  • a.sub.my-registry.example/images/my-image
  • a.b.sub.my-registry.example/images/my-image

Kubelet thực hiện pull image tuần tự với từng thông tin xác thực tìm được. Điều này có nghĩa là config.json cũng có thể có nhiều mục cho các đường dẫn khác nhau:

{
    "auths": {
        "my-registry.example/images": {
            "auth": "…"
        },
        "my-registry.example/images/subpath": {
            "auth": "…"
        }
    }
}

Khi đó, nếu một container chỉ định image my-registry.example/images/subpath/my-image cần được pull, kubelet sẽ thử tải image bằng cả hai nguồn xác thực nếu một trong hai nguồn thất bại.

Image được pull sẵn

Lưu ý:

Cách này phù hợp nếu bạn có thể kiểm soát cấu hình node. Nó sẽ không hoạt động ổn định nếu nhà cung cấp đám mây của bạn quản lý các node và tự động thay thế chúng.

Theo mặc định, kubelet cố gắng pull từng image từ registry được chỉ định. Tuy nhiên, nếu thuộc tính imagePullPolicy của container được đặt thành IfNotPresent hoặc Never, thì image cục bộ sẽ được sử dụng (tương ứng là ưu tiên dùng hoặc chỉ dùng image cục bộ).

Nếu bạn muốn dựa vào các image được pull sẵn để thay thế cho việc xác thực với registry, bạn phải đảm bảo tất cả các node trong cluster đều có cùng các image được pull sẵn.

Cách này có thể được dùng để tải trước một số image nhằm tăng tốc độ hoặc làm phương án thay thế cho việc xác thực với registry riêng tư.

Tương tự như cách dùng kubelet credential provider, các image được pull sẵn cũng phù hợp để khởi chạy các static Pod phụ thuộc vào image được lưu trữ trong registry riêng tư.

Lưu ý:

TRẠNG THÁI TÍNH NĂNG: Beta since Kubernetes v1.35; enabled by default

Quyền truy cập vào các image được pull sẵn có thể được cấp phép theo cơ chế xác minh thông tin xác thực khi pull image.

Đảm bảo xác minh thông tin xác thực khi pull image

TRẠNG THÁI TÍNH NĂNG: Beta since Kubernetes v1.35; enabled by default

Nếu feature gate KubeletEnsureSecretPulledImages được bật cho cluster của bạn, Kubernetes sẽ kiểm tra thông tin xác thực của mọi image cần thông tin xác thực để pull, ngay cả khi image đó đã có sẵn trên node. Việc kiểm tra này đảm bảo rằng những image trong yêu cầu của Pod chưa từng được pull thành công bằng thông tin xác thực được cung cấp sẽ phải được pull lại từ registry. Ngoài ra, các lượt pull image dùng lại cùng thông tin xác thực đã từng pull image thành công trước đó sẽ không cần pull lại từ registry mà được kiểm tra cục bộ, không cần truy cập registry (với điều kiện image có sẵn ở máy cục bộ). Việc này được điều khiển bởi trường imagePullCredentialsVerificationPolicy trong cấu hình Kubelet.

Cấu hình này quy định khi nào phải xác minh thông tin xác thực pull image nếu image đã có sẵn trên node:

  • NeverVerify: Mô phỏng hành vi khi feature gate này bị tắt. Nếu image có sẵn ở máy cục bộ, thông tin xác thực pull image sẽ không được xác minh.
  • NeverVerifyPreloadedImages: Các image được pull bên ngoài kubelet sẽ không được xác minh, nhưng tất cả các image khác sẽ được xác minh thông tin xác thực. Đây là hành vi mặc định.
  • NeverVerifyAllowListedImages: Các image được pull bên ngoài kubelet và có trong preloadedImagesVerificationAllowlist được chỉ định trong cấu hình kubelet sẽ không được xác minh.
  • AlwaysVerify: Tất cả các image sẽ được xác minh thông tin xác thực trước khi có thể được sử dụng.

Việc xác minh này áp dụng cho image được pull sẵn, image được pull bằng secret ở cấp node, và image được pull bằng secret ở cấp Pod.

Lưu ý:

Trong trường hợp xoay vòng thông tin xác thực (credential rotation), thông tin xác thực trước đây đã dùng để pull image sẽ tiếp tục được xác minh mà không cần truy cập registry. Thông tin xác thực mới hoặc đã được xoay vòng sẽ yêu cầu image phải được pull lại từ registry.

Bật KubeletEnsureSecretPulledImages lần đầu tiên

Khi KubeletEnsureSecretPulledImages được bật lần đầu tiên, dù là do nâng cấp kubelet hay do bật tính năng một cách tường minh, nếu kubelet có thể truy cập bất kỳ image nào vào thời điểm đó, tất cả các image này sẽ được coi là đã được pull sẵn. Điều này xảy ra vì trong trường hợp này kubelet không có bản ghi nào về việc các image đã được pull. Kubelet chỉ có thể bắt đầu tạo bản ghi pull image khi một image bất kỳ được pull lần đầu tiên.

Nếu đây là điều bạn lo ngại, bạn nên dọn khỏi các node tất cả những image không nên được coi là pull sẵn trước khi bật tính năng này.

Lưu ý rằng việc xóa thư mục chứa các bản ghi pull image cũng sẽ có tác dụng tương tự khi kubelet khởi động lại, cụ thể là các image đang được container runtime cache trên các node đều sẽ được coi là đã được pull sẵn.

Tạo Secret bằng cấu hình Docker

Bạn cần biết tên người dùng, mật khẩu registry và địa chỉ email client để xác thực với registry, cũng như hostname của registry đó. Chạy lệnh sau, thay các giá trị giữ chỗ bằng giá trị phù hợp:

kubectl create secret docker-registry <name> \
  --docker-server=<docker-registry-server> \
  --docker-username=<docker-user> \
  --docker-password=<docker-password> \
  --docker-email=<docker-email>

Nếu bạn đã có sẵn file thông tin xác thực Docker thì thay vì dùng lệnh ở trên, bạn có thể import file thông tin xác thực đó thành một Secret của Kubernetes. Tạo Secret dựa trên thông tin xác thực Docker hiện có giải thích cách thiết lập việc này.

Cách này đặc biệt hữu ích nếu bạn đang dùng nhiều container registry riêng tư, vì kubectl create secret docker-registry tạo ra một Secret chỉ hoạt động với một registry riêng tư duy nhất.

Lưu ý:

Pod chỉ có thể tham chiếu tới image pull secret trong namespace của chính nó, vì vậy quy trình này cần được thực hiện một lần cho mỗi namespace.

Tham chiếu imagePullSecrets trên một Pod

Bây giờ, bạn có thể tạo các pod tham chiếu tới secret đó bằng cách thêm phần imagePullSecrets vào định nghĩa Pod. Mỗi mục trong mảng imagePullSecrets chỉ có thể tham chiếu tới một Secret trong cùng namespace.

Ví dụ:

cat <<EOF > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: myregistrykey
EOF

cat <<EOF >> ./kustomization.yaml
resources:
- pod.yaml
EOF

Việc này cần được thực hiện cho mỗi Pod sử dụng registry riêng tư.

Tuy nhiên, bạn có thể tự động hóa quy trình này bằng cách chỉ định phần imagePullSecrets trong tài nguyên ServiceAccount. Xem Thêm ImagePullSecrets vào Service Account để biết hướng dẫn chi tiết.

Bạn có thể dùng cách này kết hợp với file .docker/config.json trên từng node. Các thông tin xác thực sẽ được hợp nhất.

Các trường hợp sử dụng

Có một số giải pháp để cấu hình registry riêng tư. Dưới đây là một số trường hợp sử dụng phổ biến và giải pháp được đề xuất.

  1. Cluster chỉ chạy các image không độc quyền (ví dụ: mã nguồn mở). Không cần ẩn image.
    • Sử dụng image công khai từ registry công khai
      • Không cần cấu hình.
      • Một số nhà cung cấp đám mây tự động cache hoặc mirror các image công khai, giúp cải thiện tính sẵn sàng và giảm thời gian pull image.
  2. Cluster chạy một số image độc quyền cần được ẩn với những người bên ngoài công ty, nhưng hiển thị với tất cả người dùng cluster.
    • Sử dụng một registry riêng tư được lưu trữ (hosted)
      • Có thể cần cấu hình thủ công trên các node cần truy cập registry riêng tư.
    • Hoặc, chạy một registry riêng tư nội bộ phía sau tường lửa với quyền đọc mở.
      • Không cần cấu hình Kubernetes.
    • Sử dụng một dịch vụ container image registry được lưu trữ (hosted) có kiểm soát quyền truy cập image
      • Cách này sẽ hoạt động tốt hơn với tính năng tự động điều chỉnh số lượng Node (Node autoscaling) so với cấu hình node thủ công.
    • Hoặc, trên một cluster mà việc thay đổi cấu hình node gây bất tiện, hãy dùng imagePullSecrets.
  3. Cluster có các image độc quyền, trong đó một vài image yêu cầu kiểm soát truy cập nghiêm ngặt hơn.
    • Đảm bảo admission controller AlwaysPullImages đang hoạt động. Nếu không, tất cả các Pod đều có khả năng truy cập vào mọi image.
    • Chuyển dữ liệu nhạy cảm vào một tài nguyên Secret, thay vì đóng gói nó trong image.
  4. Một cluster nhiều tenant (multi-tenant), trong đó mỗi tenant cần registry riêng tư của riêng mình.
    • Đảm bảo admission controller AlwaysPullImages đang hoạt động. Nếu không, tất cả các Pod của tất cả các tenant đều có khả năng truy cập vào mọi image.
    • Chạy một registry riêng tư có yêu cầu phân quyền.
    • Tạo thông tin xác thực registry cho mỗi tenant, lưu vào một Secret, và phân phối Secret đó tới namespace của mọi tenant.
    • Sau đó, tenant thêm Secret đó vào imagePullSecrets của mỗi namespace.

Nếu bạn cần truy cập nhiều registry, bạn có thể tạo một Secret cho mỗi registry.

Kubelet credential provider tích hợp sẵn kiểu cũ

Trong các phiên bản Kubernetes cũ hơn, kubelet được tích hợp trực tiếp với thông tin xác thực của nhà cung cấp đám mây. Điều này cho phép lấy thông tin xác thực cho các image registry một cách động.

Có ba bản triển khai tích hợp sẵn của kubelet credential provider: ACR (Azure Container Registry), ECR (Elastic Container Registry), và GCR (Google Container Registry).

Kể từ phiên bản 1.26 của Kubernetes, cơ chế cũ này đã bị loại bỏ, vì vậy bạn cần thực hiện một trong hai cách:

  • cấu hình kubelet image credential provider trên mỗi node; hoặc
  • chỉ định thông tin xác thực pull image bằng imagePullSecrets và ít nhất một Secret.

Tiếp theo là gì

7.2 - Các biến môi trường của Container

Trang này mô tả các tài nguyên có sẵn cho các Containers trong môi trường Container.

Môi trường container

Môi trường Container trong Kubernetes cung cấp một số tài nguyên quan trọng cho Container:

  • Một hệ thống tệp tin (filesystem), là sự kết hợp của một image và một hoặc nhiều volumes.
  • Thông tin về chính container đó.
  • Thông tin về các đối tượng (object) khác trong cluster.

Thông tin container

Hostname của một Container là tên của Pod mà Container đang chạy trong đó. Có thể lấy thông tin qua lệnh hostname hoặc lệnh gọi hàm gethostname trong libc.

Tên của Pod và namespace có thể lấy ở các biến môi trường thông qua downward API.

Các biến môi trường do người dùng định nghĩa từ định nghĩa của Pod cũng có trong thông tin của Container, như là mọi biến môi trường khác được xác định tĩnh trong Docker image.

Thông tin cluster

Một danh sách tất cả các services đang chạy khi một Container được tạo đều có trong Container dưới dạng các biến môi trường. Các biến môi trường này đều khớp với cú pháp của các Docker links.

Đối với một service có tên là foo ánh xạ với Container có tên là bar, các biến sau được xác định:

FOO_SERVICE_HOST=<host mà service đang chạy>
FOO_SERVICE_PORT=<port mà service đang chạy>

Các services có địa chỉ IP và có sẵn cho Container thông qua DNS nếu DNS addon được enable. 

Tiếp theo là gì

7.3 - Container Lifecycle Hooks

Trang này mô tả cách mà kubelet quản lý các Container có thể sử dụng framework Container lifecycle hook để chạy mã nguồn được kích hoạt bởi các sự kiện trong lifecycle của nó.

Tổng quan

Tương tự như nhiều framework ngôn ngữ lập trình có thành phần các lifecycle hooks, như là Angular, Kubernetes cung cấp các Container cùng với các lifecycle hook. Các hooks cho phép các Container nhận thức được các sự kiện trong lifecycle của chúng và chạy mã nguồn được triển khai trong một trình xử lý khi lifecycle hook tương ứng được thực thi.

Container hooks

Có 2 hooks được cung cấp cho các Containers:

PostStart

Hook này thực thi ngay sau khi một container được tạo mới. Tuy nhiên, không có gì đảm bảo rằng hook này sẽ thực thi trước container ENTRYPOINT. Không có tham số nào được truyền cho trình xử lý (handler).

PreStop

Hook này được gọi tức thì ngay trước khi một container bị chấm dứt bởi một API request hoặc sự kiện quản lý như liveness/startup probe failure, preemption, tranh chấp tài nguyên và các vấn đề khác. Một lời gọi tới PreStop hook thất bại nếu container ở trạng thái đã chấm dứt (terminated) hoặc đã hoàn thành (completed) và hook phải hoàn thành trước khi tín hiệu TERM được gửi tới để dừng container. Thời gian gia hạn chấm dứt của Pod bắt đầu trước khi hook PreStop được chạy, nên Container cuối cùng sẽ chấm dứt trong thời gian gia hạn chấm dứt của Pod bất kể kết quả của handler là gì. Không có tham số nào được truyền cho trình xử lý (handler).

Xem thêm chi tiết về hành vi chấm dứt (termination behavior) tại Termination of Pods.

Các cách thực hiện Hook handler (Hook handler implementations)

Các Containers có thể truy cập một hook bằng cách thực hiện và đăng ký một handler cho hook đó. Có 3 loại hook handler có thể được triển khai cho các Containers:

  • Exec - Thực thi một lệnh cụ thể, như là pre-stop.sh trong cgroups và namespaces của Container. Tài nguyên được sử dụng bởi lệnh được tính vào Container.
  • HTTP - Thực thi một HTTP request với một endpoint cụ thể trên Container.
  • Sleep - Dừng container trong một khoảng thời gian. Đây là tính năng đang ở beta-level, được tự động bật bởi PodLifecycleSleepAction feature gate.

Lưu ý:

Tính năng beta PodLifecycleSleepActionAllowZero được bật mặc định từ bản v1.33. Tính năng này cho phép bạn thiết lập thời gian sleep là 0 giây (hay không thực hiện hành động nào) cho các hooks Sleep lifecycle của bạn.

Thực thi hook handler (Hook handler execution)

Khi một hook quản lý Container lifecycle được gọi, hệ thống quản lý Kubernetes thực thi trình xử lý (handler) dựa trên hành động của hook. httpGet, tcpSocket (deprecated) và sleep được thực thi bởi tiến trình kubelet, còn exec được thực thi trong container.

Lời gọi hook PostStart handler được khởi tạo khi một container được tạo, nghĩa là container ENTRYPOINT và hook PostStart được kích hoạt đồng thời. Tuy nhiên, nếu hook PostStart mất quá nhiều thời gian để chạy hoặc bị treo, container không thể chuyển sang trạng thái running.

Các hooks PreStop không được thực thi bất đồng bộ với tín hiệu dừng Container; hook phải được hoàn thành thực thi trước khi tín hiệu TERM được gửi. Nếu hook PreStop bị treo khi thực thi, Pod phase ở trạng thái Terminating và tiếp tục cho tới khi bị chấm dứt sau terminationGracePeriodSeconds hết hạn. Khoảng thời gian gia hạn này bằng tổng thời gian mà cả hook PreStop thực thi và Container dừng lại một cách bình thường. Ví dụ, nếu terminationGracePeriodSeconds là 60, và hook mất 55 giây để hoàn thành, và Container mất thêm 10 giây để dừng lại sau khi nhận được tín hiệu, thì Container sẽ bị chấm dứt trước khi nó kịp dừng bình thường, vì terminationGracePeriodSeconds nhỏ hơn tổng thời gian cần thiết để thực hiện hai việc này (55+10).

Nếu hook PostStart hoặc PreStop thất bại, nó sẽ xóa Container.

Người dùng nên làm cho hook handlers nhẹ nhất có thể. Tuy nhiên, có những trường hợp khi những lệnh chạy dài có ý nghĩa, chẳng hạn như khi lưu trạng thái trước khi dừng một container.

Hook delivery guarantees

Hook delivery được trù định ít nhất một lần, điều đó có nghĩa là hook có thể được gọi nhiều lần cho bất kỳ sự kiện cho trước nào, chẳng hạn như PostStart hoặc PreStop. Tùy thuộc vào việc thực hiện hook để xử lý việc này một cách chính xác.

Nhìn chung, chỉ có các deliveries đơn được thực hiện. Ví dụ, nếu một HTTP hook receiver bị down và không thể nhận các traffic, sẽ không được gửi lại. Tuy nhiên, trong một số trường hợp hiếm hoi, delivery kép có thể xảy ra. Chẳng hạn, nếu một kubelet khởi động lại ở giữa quá trình gửi một hook, hook có thể được gửi lại sau khi kubelet quay trở lại.

Debugging Hook handlers

Log cho một Hook handler không được hiển thị trong các Pod events. Nếu một handler thất bại vì lí do nào đó, nó sẽ broadcast một event. Đối với PostStart, đây là event FailedPostStartHook, và đối với PreStop, đây là event FailedPreStopHook. Để tự tạo ra một event lỗi FailedPostStartHook, bạn có thể điều chỉnh file lifecycle-events.yaml để thay đổi lệnh postStart thành "badcommand" và áp dụng nó. Dưới đây là một số ví dụ output của events từ việc chạy lệnh kubectl describe pod lifecycle-demo:

Events:
  Type     Reason               Age              From               Message
  ----     ------               ----             ----               -------
  Normal   Scheduled            7s               default-scheduler  Successfully assigned default/lifecycle-demo to ip-XXX-XXX-XX-XX.us-east-2...
  Normal   Pulled               6s               kubelet            Successfully pulled image "nginx" in 229.604315ms
  Normal   Pulling              4s (x2 over 6s)  kubelet            Pulling image "nginx"
  Normal   Created              4s (x2 over 5s)  kubelet            Created container lifecycle-demo-container
  Normal   Started              4s (x2 over 5s)  kubelet            Started container lifecycle-demo-container
  Warning  FailedPostStartHook  4s (x2 over 5s)  kubelet            Exec lifecycle hook ([badcommand]) for Container "lifecycle-demo-container" in Pod "lifecycle-demo_default(30229739-9651-4e5a-9a32-a8f1688862db)" failed - error: command 'badcommand' exited with 126: , message: "OCI runtime exec failed: exec failed: container_linux.go:380: starting container process caused: exec: \"badcommand\": executable file not found in $PATH: unknown\r\n"
  Normal   Killing              4s (x2 over 5s)  kubelet            FailedPostStartHook
  Normal   Pulled               4s               kubelet            Successfully pulled image "nginx" in 215.66395ms
  Warning  BackOff              2s (x2 over 3s)  kubelet            Back-off restarting failed container

Tiếp theo là gì

7.4 - Runtime Class

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.20

Trang này mô tả tài nguyên RuntimeClass và cơ chế lựa chọn runtime.

RuntimeClass là một tính năng dùng để chọn cấu hình container runtime. Cấu hình container runtime được dùng để chạy các container của một Pod.

Động lực

Bạn có thể đặt RuntimeClass khác nhau cho các Pod khác nhau để cân bằng giữa hiệu năng và bảo mật. Ví dụ, nếu một phần workload của bạn cần mức đảm bảo an toàn thông tin cao, bạn có thể chọn lập lịch các Pod đó sao cho chúng chạy trên một container runtime sử dụng ảo hóa phần cứng. Khi đó bạn sẽ có thêm khả năng cô lập từ runtime thay thế này, đổi lại là phát sinh thêm một số chi phí phụ trội (overhead).

Bạn cũng có thể dùng RuntimeClass để chạy các Pod khác nhau với cùng một container runtime nhưng với các thiết lập khác nhau.

Thiết lập

  1. Cấu hình bản triển khai CRI trên các node (tùy thuộc vào runtime)
  2. Tạo các tài nguyên RuntimeClass tương ứng

1. Cấu hình bản triển khai CRI trên các node

Các cấu hình có thể dùng thông qua RuntimeClass phụ thuộc vào bản triển khai Container Runtime Interface (CRI). Xem tài liệu tương ứng (bên dưới) của bản triển khai CRI mà bạn dùng để biết cách cấu hình.

Lưu ý:

Theo mặc định, RuntimeClass giả định rằng cấu hình node là đồng nhất trên toàn cluster (nghĩa là tất cả các node đều được cấu hình giống nhau về container runtime). Để hỗ trợ cấu hình node không đồng nhất, xem phần Lập lịch bên dưới.

Mỗi cấu hình có một tên handler tương ứng, được RuntimeClass tham chiếu tới. Handler phải là một tên DNS label hợp lệ.

2. Tạo các tài nguyên RuntimeClass tương ứng

Mỗi cấu hình được thiết lập ở bước 1 cần có một tên handler đi kèm để định danh cấu hình đó. Với mỗi handler, hãy tạo một đối tượng RuntimeClass tương ứng.

Tài nguyên RuntimeClass hiện chỉ có 2 trường quan trọng: tên của RuntimeClass (metadata.name) và handler (handler). Định nghĩa đối tượng trông như sau:

# RuntimeClass được định nghĩa trong API group node.k8s.io
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  # Tên dùng để tham chiếu tới RuntimeClass.
  # RuntimeClass là tài nguyên không thuộc namespace nào.
  name: myclass 
# Tên của cấu hình CRI tương ứng
handler: myconfiguration 

Tên của một đối tượng RuntimeClass phải là một tên DNS subdomain hợp lệ.

Lưu ý:

Bạn nên giới hạn các thao tác ghi đối với RuntimeClass (create/update/patch/delete) chỉ cho quản trị viên cluster. Thông thường đây là thiết lập mặc định. Xem Tổng quan về phân quyền để biết thêm chi tiết.

Cách sử dụng

Sau khi các RuntimeClass đã được cấu hình cho cluster, bạn có thể chỉ định runtimeClassName trong đặc tả của Pod để sử dụng. Ví dụ:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  runtimeClassName: myclass
  # ...

Điều này sẽ yêu cầu kubelet dùng RuntimeClass được chỉ định để chạy pod này. Nếu RuntimeClass được chỉ định không tồn tại, hoặc CRI không thể chạy handler tương ứng, pod sẽ chuyển sang pha (phase) kết thúc Failed. Hãy tìm sự kiện (event) tương ứng để xem thông báo lỗi.

Nếu không chỉ định runtimeClassName, RuntimeHandler mặc định sẽ được sử dụng, tương đương với hành vi khi tính năng RuntimeClass bị tắt.

Cấu hình CRI

Để biết thêm chi tiết về cách thiết lập các CRI runtime, xem Cài đặt CRI.

containerd

Các runtime handler được cấu hình thông qua file cấu hình của containerd tại /etc/containerd/config.toml. Các handler hợp lệ được cấu hình trong phần runtimes:

[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.${HANDLER_NAME}]

Xem tài liệu cấu hình của containerd để biết thêm chi tiết:

CRI-O

Các runtime handler được cấu hình thông qua file cấu hình của CRI-O tại /etc/crio/crio.conf. Các handler hợp lệ được cấu hình trong bảng crio.runtime:

[crio.runtime.runtimes.${HANDLER_NAME}]
  runtime_path = "${PATH_TO_BINARY}"

Xem tài liệu cấu hình của CRI-O để biết thêm chi tiết.

Lập lịch

TRẠNG THÁI TÍNH NĂNG: Beta since Kubernetes v1.16

Bằng cách chỉ định trường scheduling cho một RuntimeClass, bạn có thể đặt các ràng buộc để đảm bảo rằng các Pod chạy với RuntimeClass này được lập lịch lên những node hỗ trợ nó. Nếu không đặt scheduling, Kubernetes giả định rằng tất cả các node đều hỗ trợ RuntimeClass này.

Để đảm bảo các pod được đặt lên những node hỗ trợ một RuntimeClass cụ thể, nhóm node đó cần có một label chung để trường runtimeclass.scheduling.nodeSelector chọn ra. nodeSelector của RuntimeClass được hợp nhất với nodeSelector của pod trong bước admission, tức là lấy phần giao giữa các tập node mà mỗi bên chọn. Nếu có xung đột, pod sẽ bị từ chối.

Nếu các node được hỗ trợ đã bị gắn taint để ngăn pod của RuntimeClass khác chạy trên node đó, bạn có thể thêm tolerations vào RuntimeClass. Tương tự như nodeSelector, các toleration được hợp nhất với toleration của pod trong bước admission, tức là lấy phần hợp giữa các tập node mà mỗi bên chấp nhận (tolerate).

Để tìm hiểu thêm về cách cấu hình node selector và toleration, xem Gán Pod cho Node.

Chi phí phụ trội của Pod (Pod Overhead)

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.24

Bạn có thể chỉ định các tài nguyên overhead gắn với việc chạy một Pod. Việc khai báo overhead cho phép cluster (bao gồm cả scheduler) tính đến phần này khi đưa ra quyết định về Pod và tài nguyên.

Pod overhead được định nghĩa trong RuntimeClass thông qua trường overhead. Bằng cách sử dụng trường này, bạn có thể chỉ định overhead khi chạy các pod sử dụng RuntimeClass này và đảm bảo các overhead này được Kubernetes tính đến.

Tiếp theo là gì

7.5 - Container Runtime Interface (CRI)

CRI là một giao diện plugin cho phép kubelet sử dụng nhiều loại container runtime khác nhau mà không cần biên dịch lại các thành phần của cluster.

Bạn cần có một container runtime hoạt động được trên mỗi Node trong cluster, để kubelet có thể khởi chạy các Pod cùng các container của chúng.

Container Runtime Interface (CRI) là giao thức chính cho việc giao tiếp giữa kubelet và Container Runtime.

Container Runtime Interface (CRI) của Kubernetes định nghĩa giao thức gRPC chính cho việc giao tiếp giữa các thành phần node là kubelet và container runtime.

API

TRẠNG THÁI TÍNH NĂNG: GA since Kubernetes v1.23

Kubelet đóng vai trò client khi kết nối tới container runtime thông qua gRPC. Các endpoint của runtime service và image service phải có sẵn trong container runtime; bạn có thể cấu hình riêng các endpoint này trong kubelet bằng cờ dòng lệnh --container-runtime-endpoint.

Với Kubernetes v1.26 trở lên, kubelet yêu cầu container runtime phải hỗ trợ CRI API v1. Nếu một container runtime không hỗ trợ API v1, kubelet sẽ không đăng ký node.

Nâng cấp

Khi nâng cấp phiên bản Kubernetes trên một node, kubelet sẽ khởi động lại. Nếu container runtime không hỗ trợ CRI API v1, kubelet sẽ không thể đăng ký và sẽ báo lỗi. Nếu cần kết nối lại (re-dial) gRPC vì container runtime đã được nâng cấp, runtime phải hỗ trợ CRI API v1 thì kết nối mới thành công. Có thể bạn sẽ cần khởi động lại kubelet sau khi container runtime đã được cấu hình đúng.

Truyền danh sách theo luồng (list streaming)

TRẠNG THÁI TÍNH NĂNG: Alpha since Kubernetes v1.36; disabled by default
More information about this feature

To use this feature, you (or a cluster administrator) will need to enable the CRIListStreaming feature gate for all relevant components in your cluster.

See Enable Or Disable Feature Gates for more information.

Các RPC liệt kê tiêu chuẩn của CRI (ListContainers, ListPodSandbox, ListImages) trả về toàn bộ kết quả trong một phản hồi unary duy nhất. Trên các node có số lượng container lớn (ví dụ, nhiều hơn khoảng 10,000 container, tính cả container đang chạy lẫn đã dừng), các phản hồi này có thể vượt quá giới hạn kích thước thông điệp mặc định 16 MiB của gRPC, khiến kubelet gặp lỗi khi đối chiếu (reconcile) trạng thái với container runtime.

Khi bật feature gate CRIListStreaming, kubelet sử dụng các RPC streaming phía server (chẳng hạn StreamContainers, StreamPodSandboxes, StreamImages), cho phép container runtime chia kết quả ra nhiều thông điệp phản hồi, nhờ đó vượt qua giới hạn kích thước của mỗi thông điệp. Điều này đặc biệt hữu ích cho:

  • Các môi trường mà container được tạo và hủy liên tục (các hệ thống CI/CD)
  • Các workload xử lý theo lô (batch) quy mô lớn

Nếu container runtime không hỗ trợ các RPC streaming, kubelet sẽ tự động quay về dùng các RPC unary tiêu chuẩn để đảm bảo tương thích ngược.

Tiếp theo là gì

8 - Jobs

Trang này đang được dịch. Vui lòng xem phiên bản tiếng Anh để biết thông tin mới nhất.

9 - Quản lý tài nguyên cho Containers

Trang này đang được dịch. Vui lòng xem phiên bản tiếng Anh để biết thông tin mới nhất.

10 - IPv4/IPv6 dual-stack

Kubernetes cho phép bạn cấu hình mạng IPv4 đơn, mạng IPv6 đơn, hoặc mạng kép với cả hai họ mạng hoạt động. Trang này giải thích cách thức hoạt động.

Trang này đang được dịch. Vui lòng xem phiên bản tiếng Anh để biết thông tin mới nhất.

11 - Mở rộng Kubernetes

Các cách khác nhau để thay đổi hành vi của cluster Kubernetes của bạn.

Trang này đang được dịch. Vui lòng xem phiên bản tiếng Anh để biết thông tin mới nhất.

12 - ReplicaSet

Mục đích của ReplicaSet là duy trì một tập hợp ổn định các Pod bản sao đang chạy tại bất kỳ thời điểm nào. Thông thường, bạn định nghĩa một Deployment và để Deployment đó quản lý các ReplicaSet tự động.

Trang này đang được dịch. Vui lòng xem phiên bản tiếng Anh để biết thông tin mới nhất.