Hello các bạn lại là mình đây 👋👋👋
Tiếp tục trở lại với series học Kubernetes, ở bài trước ta đã tìm hiểu về giao tiếp trên K8S với Service.
Ở bài này ta sẽ tiếp tìm hiểu về Deployment, thứ mà chúng ta sẽ gặp và làm việc nhiều nhất khi làm thật. Ta sẽ cùng xem:
- cách tạo deployment thế nào
- scale app ra sao
- zero downtime deployment trên K8S là thế nào
- ....
Thôi không luyên thuyên nữa, ta bắt đầu thôi nào 🚀🚀🚀
Lấy K8S Session
Vẫn như thường lệ, trước khi bắt đầu các bạn đảm giúp mình là đã lấy được K8S Session để truy cập vào cluster của mình đẻ lát nữa ta thực hành nhé. Xem lại bài cũ giúp mình nhé
Kiến thức vỡ lòng
Ở bài mở đầu với Pod, ta đã biết rằng app của chúng ta khi được deploy trên K8S thì bản chất là nó chạy ở trong Pod, ta có thể viết manifest để trực tiếp deploy 1 hoặc nhiều Pod tuỳ ta mong muốn.
Các Pod đó là naked Pod
(pod trần chuồng 🤣🤣🤣), ý là nó sẽ không được deploy lại nếu xảy ra lỗi. Và ta nên hạn chế dạng Pod này
Khi làm thật, production, thì thường ta sẽ không deploy trực tiếp Pod như vậy mà làm qua Deployment (cùng với Service). Với Deploy ta có thể tạo và quản lý Pod và Replicaset. Ta định nghĩa state
ta mong muốn cho app của chúng ta (bao nhiêu instance, như nào thì ok cho traffic đi vào pod, khi nào thì cần restart pod,...) và K8S Deployment sẽ giúp ta "đảm bảo" việc đưa trạng thái app của chúng ta về state đó.
Một số ưu điểm của Deployment:
- tự động deploy lại pod nếu xảy ra lỗi
- zero downtime deployment: mỗi khi ta update deployment (scale, đổi image với code mới, đổi cpu/ram limit,....), thì k8s sẽ thực hiện re-deploy 1 cách "từ từ" đảm bảo là app của chúng ta vẫn luôn chạy để user sử dụng
- rollout (deploy) mới hoặc rollback về version cũ bất kì lúc nào
- ta có thể pause tạm dừng deployment khi ta muốn
- ....
Tạo deployment
Bây giờ ta cùng nhau bắt đầu tạo deployment và cả Service để có thể truy cập vào app của chúng ta nhé
Đầu tiên ta tạo file manifest tên là myapp.yml
với nội dung như sau nhé:
apiVersion: apps/v1
kind: Deployment
metadata: name: myapp-deployment labels: app: myapp
spec: replicas: 2 selector: matchLabels: app: myapp-pod template: metadata: labels: app: myapp-pod spec: containers: - name: myapp-container image: nginx:1.14.2 ports: - containerPort: 80 name: http resources: requests: memory: "64Mi" cpu: "64m" limits: memory: "128Mi" cpu: "128m" ---
apiVersion: v1
kind: Service
metadata: name: myapp-svc
spec: type: LoadBalancer ports: - name: first-port protocol: TCP port: 8000 targetPort: http selector: app: myapp-pod
Ố ồ, dừnggggggggg, sao lại viết chung cả Service chung vào với Deployment thế kia??? 🤔🤔🤔
À thì đơn giản là ngoài tách riêng từng object ra các file (deployment, service) thì ta cũng có thể gộp tất cả chúng lại, các objects phân tách với nhau bởi dấu ---
, bạn có thể viết gộp bao nhiêu objects vào 1 file
Vọc vạch chút trước khi apply
nhé những người anh em thiện lành:
- để tạo Deployment thì ta phải định nghĩa
kind=Deployment
, đặt cho nó labelapp: myapp
. - bên trong spec thì ta có
replicas=2
ý là ta muốn tạo 2 pod, khi taapply
thì sẽ có 1 Replicaset được tạo ra để quản lý 2 pod này, nếu ta không nóireplicas
bằng bao nhiêu thì mặc định là 1. Các bạn có thể setreplicas=0
cũng được - tiếp đó ta có
selector
đây là label selector cho Pod. KHÔNG phải là label selector cho deploy ta định nghĩa bên trên nhé. Ở đây ta muốn nói rằng "deployment" này sẽ đượcapply
cho các pod có labelmatch
với giá trịapp: myapp-pod
- bên dưới đó là ta có
template
(cái này người ta cũng gọi làPodTemplateSpec
), nơi ta định nghĩaspec
cho pod - bên trong template thì đầu tiên ta phải định nghĩa
metadata
vớilabels
giống với giá trị cho pod label selector ở trên (matchLabels
) - tiếp đó cũng giống như ở bài viết manifest cho naked Pod, thì ta có
spec
nơi ta định nghĩa container cho Pod: tên container, image là gì, expose port 80 (ta cũng đặt tên "phụ" cho nó làhttp
), và thêm resource request.
Bên dưới cùng thì ta có định nghĩa cho Service, giống như bài trước mình đã trình bày rồi nhé.
apply
triển thôi nào anh em ơi 😎😎:
kubectl apply -f myapp.yml --kubeconfig=kubernetes-config
Sau đó ta sẽ get pod
xem trạng thái ra sao rồi nhé:
kubectl get po --kubeconfig=kubernetes-config
Ta thấy in ra như sau:
NAME READY STATUS RESTARTS AGE
myapp-deployment-55f998b87b-g7qtv 1/1 Running 0 4s
myapp-deployment-55f998b87b-n6pjq 1/1 Running 0 4s
Như trên ta đã có 2 Pod
Running
ok
Check trạng thái chung của cả deployment thì ta cũng get deployment
là ra:
kubectl get deploy --kubeconfig=kubernetes-config
Và ở terminal in ra:
NAME READY UP-TO-DATE AVAILABLE AGE
myapp-deployment 2/2 2 2 8s
Ở trên ta thấy một số thông tin như sau:
- READY: đã có bao nhiêu pod đã "ready" cho user gọi vào, như ta thấy thì cả 2/2 pod đã ready
- UP-TO-DATE: bao nhiêu pod đã được update để đạt trạng thái mong muốn (2)
- AVAILABLE giống READY
Ở trên mình viết tắt là "get deploy", ta có thể viết là "get deployment" hay "get deployments" cũng được
Mỗi 1 lần mà ta apply và Deployment chạy lại, hay ta update trực tiếp deployment từ terminal (đổi image, đổi volume,....) thì deployment cũng chạy lại. Mỗi lần "chạy lại" như thế thì ta gọi là 1 lần "rollout".
Ta thử "watch" trạng thái rollout của deployment này xem nhé:
kubectl rollout status -w deployment/myapp-deployment --kubeconfig=kubernetes-config --- deployment "myapp-deployment" successfully rolled out
Bên trên K8S báo là deployment của chúng ta đã rollout thành công.
Ta thử check rollout history của deployment của chúng ta xem nhé:
kubectl rollout history deploy/myapp-deployment --kubeconfig=kubernetes-config ----- deployment.apps/myapp-deployment REVISION CHANGE-CAUSE
1 <none>
bên trên mình vẫn viết tắt là "deploy/...", các bạn có thể viết đầy đủ ra cũng được nhé
Ta thấy output ra lịch sử rollout của deployment này, có REVISION
(phiên bản, tăng dần từ 1,2,3 ...), và CHANGE-CAUSE
thay đổi nào đã tác động tới lần rollout đó. Bởi vì ta mới apply lần đầu tiên nên cause=none
.
Ở đầu bài mình có nói là Deployment sẽ tạo ra 1 Replicate set tương ứng để quản lý pod. Ta thử get
nó xem có gì nhé:
kubectl get rs --kubeconfig=kubernetes-config ------>> NAME DESIRED CURRENT READY AGE
myapp-deployment-55f998b87b 2 2 2 3m6s
Ở trên ta lại thấy các thông số như sau: (lắm thông số quá 😥😥😥😥)
- DESIRED: số Pod mà ta mong muốn, ta mong muốn nhé các bạn. Cái mà ta định nghĩa ở file manifest (replicas=2)
- CURRENT: hiện tại đang có bằng này pod RUNNING (nhưng chưa chắc đã READY cho user gọi vào)
- READY: số Pod thực tế ready để nhận traffic từ user
Giờ ta test thử xem là app của chúng ta chạy oke chưa đã nhé, các bạn get service
để lấy load balancer trước nhé:
kubectl get svc --kubeconfig=kubernetes-config --- NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-svc LoadBalancer 10.245.136.215 146.190.7.21 8000:30623/TCP 44m
Như ở trên các bạn thấy External-IP của mình là 146.190.7.21
, của các bạn sẽ khác đó nhé.
Và giờ ta mở trình duyệt truy cập ở địa chỉ ExternalIP:8000
sẽ thấy như sau:
Để ý version hiện tại của nginx là 1.14.2 giống với tag của image mà ta đang dùng
Tiếp theo ta sẽ update lại deployment để chạy với image nginx phiên bản mới hơn nhé:
kubectl set image deploy/myapp-deployment myapp-container=nginx:1.16.1 --kubeconfig=kubernetes-config --- deployment.apps/myapp-deployment image updated
Ở trên ta thay đổi trực tiếp image từ terminal, đổi image của container my-container
lên nginx:1.16.1
Ngay sau đó nếu ta thử get pod
và watch rollout status
thì sẽ thấy như sau:
Ở trên ta thấy ngay sau khi set image
thì K8S sẽ ngay lập tức triển khai pod mới, nhưng các pod cũ vẫn giữ nguyên, đảm bảo trong thời gian đó app của chúng ta không có downtime, user vẫn có thể truy cập thoải mái.
Sau khi deploy pod mới thành công thì K8S sẽ teardown, terminate
dần pod cũ đi.
Chú ý rằng trong quá trình deploy, khi những pod mới chưa ready hoàn toàn, thì K8S vẫn đẩy traffic vào pod cũ.
Nếu bây giờ ta quay lại trình duyệt truy cập thử thì sẽ thấy trogn response trả về, nginx đã được update lên phiên bản mới:
Ngoài việc update deploy trực tiếp từ terminal như trên (set image
), thì ta cũng có thể edit lại file manifest và chạy lại apply
, đây là cách phổ biến hơn cả, và cũng là cách mình thường dùng.
Các bạn mở file myapp.yml
và đổi lại image thành:
image: nginx:1.19.10
Sau đó ta thực hiện apply:
kubectl apply -f myapp.yml --kubeconfig=kubernetes-config --- deployment.apps/myapp-deployment configured
service/myapp-svc unchanged
như bên trên K8S báo là deployment đã được "cấu hình" (thay đổi), service thì không đổi gì cả (unchanged)
Ta lại get pod thì lại thấy kết quả tương tự, K8S đang bắt đầu triển khai pod mới:
kubectl get po --kubeconfig=kubernetes-config --->> NAME READY STATUS RESTARTS AGE
myapp-deployment-5c5946dc8f-cvkg2 1/1 Running 0 15m
myapp-deployment-5c5946dc8f-j87wt 1/1 Running 0 15m
myapp-deployment-7749c46bcb-k9d8l 0/1 ContainerCreating 0 4s
Chờ 1 lúc cho deploy xong, get lại pod thấy RUNNING là oke nhé :
NAME READY STATUS RESTARTS AGE
myapp-deployment-7749c46bcb-9gs86 1/1 Running 0 10s
myapp-deployment-7749c46bcb-k9d8l 1/1 Running 0 14s
Quay lại trình duyệt, F5 kiểm tra sẽ thấy phiên bản nginx được update:
ngoài việc trực tiếp edit file thì ta cũng có thể chạy command sau để edit deployment na ná như edit file luôn
kubectl edit deploy myapp-deployment --kubeconfig=kubernetes-config
Vọc vạch sâu sâu hơn tí
Scale
Hiện tại ở trong file manifest myapp.yml
ta đang để replicas=2
tức là sẽ có 2 pod chạy cho app của chúng ta, giờ ta thử scale lên 4 pod nhé.
Các bạn mở file myapp.yml
, và sửa replicas=4
nhé:
replicas: 4
Sau đó ta apply
tiếp:
kubectl apply -f myapp.yml --kubeconfig=kubernetes-config
Thử get
lại pod và ta sẽ thấy:
kubectl get po --kubeconfig=kubernetes-config --- NAME READY STATUS RESTARTS AGE
myapp-deployment-7749c46bcb-7jksl 1/1 Running 0 45s
myapp-deployment-7749c46bcb-h9fcl 1/1 Running 0 8m42s
myapp-deployment-7749c46bcb-kqvf9 1/1 Running 0 8m40s
myapp-deployment-7749c46bcb-qzd59 1/1 Running 0 45s
Quay lại trình duyệt thử F5 thấy không có gì khác biệt (từ phía end user), nhưng thực tế đằng sau thì app đã được scale từ 2 thành 4 instances, Service sẽ làm nhiệm vụ điều phối traffic vào các pod cho chúng ta, hoàn toàn tự động, rất đơn giản phải không các bạn
Ở thực tế nếu ta có thể ước lượng được lượng traffic thì ta có thể chọn trước 1 con số replicas
cho phù hợp
Ở bài auto scale ta sẽ xem cách scale tự động khi có traffic tăng cao nhé
Cá nhân mình thấy "ấn tượng" với K8S khi mình biết tới deployment, mọi thứ đơn giản hơn vô cùng nhiều: deploy, update image mới, scale,....
So với cái thời ngày xửa ngày xưa khi mọi thứ phải làm bằng tay. Lúc deploy mới bằng Docker hay cái Deployer của PHP thì phải "down" cái cũ đi, bị downtime 1 tí (đôi khi là nhiều tí ). Xong lúc scale lên thì phải tự add thêm instance mới vào Load balancer để LB nó biết để đẩy traffic vào. Nhìn chung là vất vả 😪😪😪
Chú ý là namespace của các bạn mình đã limit 750m CPU/1Gi RAM, nên các bạn không scale được nhiều quá đâu nhé, mỗi instance hiện tại có thể ăn tối đa (
limits: 128m CPU / 128Mi
--> không có quá 750/128 = 5 Pod tại bất kì thời điểm nào)
ReplicaSet
Mỗi 1 lần mà ta apply
deployment, thì tương ứng nó sẽ tạo ra 1 ReplicaSet mới để quản lý số Pod của chúng ta. Bây giờ ta chạy command sau để get replicaset
nhé:
kubectl get rs --kubeconfig=kubernetes-config
Ta thấy in ra như sau:
NAME DESIRED CURRENT READY AGE
myapp-deployment-55f998b87b 0 0 0 43m
myapp-deployment-5c5946dc8f 0 0 0 35m
myapp-deployment-7749c46bcb 4 4 4 34m
Như bên trên ta thấy có tổng cộng 3 replicaset tương ứng với 3 lần ta apply
, trừ lần cuối khi ta scale từ 2->4 pod
Rồi rồi, nói riết Replicaset, Replicaset, mà chẳng hiểu gì, thế bản chất nó là cái gì????? 😒😒😒
Thì ReplicaSet là 1 K8S object mà nhiệm vụ của nó là đảm bảo sự ổn định của 1 số lượng Pod đang chạy ở bất kì thời điểm nào. Kiểu mình bảo nó là: "ê ReplicaSet, tôi muốn có 4 pod luôn chạy ổn định ở mọi thời điểm, đảm bảo điều đó cho tôi, lúc nào mà tôi get pod
mà thấy không đủ 4 thì liệu hồn" 😘😜
Ví dụ trong trường hợp của chúng ta thì K8S tạo ra ReplicaSet như sau:
apiVersion: apps/v1
kind: ReplicaSet
metadata: name: frontend labels: app: myapp
spec: replicas: 4 selector: matchLabels: app: myapp-pod template: metadata: labels: app: myapp-pod spec: containers: - name: myapp-container image: nginx:1.19.10
Ủa nom giống manifest của Deployment thế nhỉ, thay mỗi kind: ReplicaSet
🤔🤔🤔 Đúng rồi đó các bạn 🤣
Thường thì ta rất ít khi, hay cụ thể là gần như không bao giờ phải tự tay tạo ReplicaSet, thay vào đó ta nên dùng Deployment, và Deployment sẽ tự làm điều đó và quản lý nó thay ta
Rollback về version cũ
Giả sử giờ ta vừa deploy ra bản mới và đồng đội của ta thảng thốt: ông ơi bỏ mịa quên tôi lại đang log user password ra console, nãy quên dev ở local log ra để debug cho dễ, ông rollback lại version cũ hộ tôi tí không lát Leader nhìn thấy thì tháng này thiếu tiền mua sữa cho con bú ngoài 🙃🙃
Lúc là những lúc ta cần K8S giúp ta rollback về version cũ, 1 tính năng cũng rất đáng tiền của K8S nữa, vì đây cũng là 1 vấn đề khá phổ biến khi chạy production: vừa rollout bản mới phát thâý failed, muốn nhanh chóng đưa lại bản cũ stable.
Oke huyên thuyên đủ rồi, giờ ta cùng check rollout history xem từ đầu tới giờ ta đã deploy mấy lần nhé:
kubectl rollout history deploy/myapp-deployment --kubeconfig=kubernetes-config ---- REVISION CHANGE-CAUSE
1 <none>
2 <none>
3 <none>
Ở trên ta đã deploy 3 lần, mỗi lần ta set image với 1 tag khác nhau: v1.14, v.1.16 và v1.19 (hiện tại)
Quay lại trình duyệt kiểm tra trước đảm bảo ta đang chạy bản 1.19 nhé:
Giờ ta sẽ tiến hành rollback lại bản cũ v1.16 (REVISISON=2):
kubectl rollout undo deploy/myapp-deployment --kubeconfig=kubernetes-config
Ngay lập tức như 1 cơn gió, K8S sẽ tiến hành tạo các pod mới với image v1.16 và sau đó Terminate các pod v1.19:
Và sau khi tất cả mọi thứ xong xuôi ta get
pod sẽ thấy đủ 4 pod v1.16 RUNNING:
kubectl get pod --kubeconfig=kubernetes-config ---- myapp-deployment-5c5946dc8f-9n76q 1/1 Running 0 2m45s
myapp-deployment-5c5946dc8f-bzpbf 1/1 Running 0 2m41s
myapp-deployment-5c5946dc8f-wnzkl 1/1 Running 0 2m31s
myapp-deployment-5c5946dc8f-wr6z6 1/1 Running 0 2m44s
Quay lại trình duyệt kiểm tra cho chắc chắn đảm bảo nginx đã về phiên bản cũ nhé:
Command undo
ta chạy bên trên sẽ đưa deployment về lần rollout ngay trước đó, nếu ta muốn nhảy cóc 1 phát về 1 REVISION bất kì thì ta thêm --to-revision=2
vào là được:
kubectl rollout undo deploy/myapp-deployment --kubeconfig=kubernetes-config --to-revision=2
Pause/Resume deployment
Ta thậm chí có thể Pause Deployment để dừng việc rollout của nó, cái này ta có thể làm khi ta muốn vọc vạch deploy các kiểu nhưng không muốn rollout.
Nhưng cái này mình thấy khá ít khi dùng, các bạn có nhu cầu tự tìm hiểu thêm giúp mình nhé
Các câu hỏi liên quan
Oke vậy là ta nên dùng Deployment thay vì tạo Pod trực tiếp (naked)?
Đúng vậy, ta nên luôn dùng Deployment để deploy thay vì dùng naked pod nhé, sẽ tiện hơn rất nhiều cho chúng ta, Deployment cũng cho ta thêm rất nhiều tính năng nữa mà naked Pod không có
Nên update Deployment thế nào?
Như trong bài có đoạn ta set image
cho deployment để rollout bản mới trực tiếp từ terminal.
Nhưng các mình khuyên dùng là tất cả mọi thứ, mọi update, ta đều làm qua file manifest, như vậy sau này khi đẩy manifest lên Git thì ta có thể track được thay đổi theo thời gian, và người khác cũng có thể biết chính xác ta đã đổi những gì
Viết Deployment và Service chung hay riêng?
Tuỳ các bạn
Nhưng mình thì hay viết chung luôn. Deployment + Service như kiểu cặp bài trùng luôn đi cùng với nhau. 1 cái để deploy pod, 1 cái để expose Pod để nơi khác có thể gọi vào.
Pull image từ private registry như thế nào?
Như các bạn thấy trong bài ta lấy image từ public Dockerhub Registry. Khi muốn pull image từ private registry, ví dụ như từ gitlab như các bài trong series học Docker thì ta cần dùng tới imagePullSecret
. Cái này ta sẽ thảo luận ở các bài tới nhé
Apply từ 1 url hay 1 folder?
K8S cho phép ta có thể apply trực tiếp từ 1 link nào đó thay vì phải có file manifest từ local.
Ví dụ bên dưới ta apply
file manifest tạo ingress nginx trực tiếp từ Github:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.4.0/deploy/static/provider/do/deploy.yaml
Còn nếu ta muốn apply cả 1 folder thì ta thêm option -R
nhé:
kubectl -R -f .....
Xoá Deployment
Để xoá Deploy thì ta có 2 cách: xoá trực tiếp hoặc xoá qua file manifest
- Xoá trực tiếp
kubectl delete deploy myapp-deployment --kubeconfig=kubernetes-config
- Xoá qua file manifest:
kubectl delete -f myapp.yml --kubeconfig=kubernetes-config
Chú ý rằng khi xoá qua file manifest thì K8S sẽ xoá tất cả mọi thứ trong đó luôn, như bài này là sẽ xoá cả Service trong đó
Chào thân ái
Lại hết bài ồiiiii 😆😆
Qua bài này hi vọng rằng các bạn đã biết về Deployment, biết về cách create/update , 1 thứ mà chắc chắn khi làm việc với Kubernetes ta sẽ phải đụng tới vô cùnggggggggggggg nhiều và luôn luôn
Thân ái chào tạm biệt. Hẹn gặp lại các bạn ở những bài sau 👋👋