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

Return to the regular view of this page.

Học kiến thức cơ bản về Kubernetes

Kiến thức cơ bản về Kubernetes

Hướng dẫn này cung cấp những kiến thức cơ bản về một cụm Kubernetes. Mỗi mô-đun chứa một số thông tin cơ bản về các tính năng cũng như khái niệm chính của Kubernetes, đồng thời bao gồm một hướng dẫn tương tác trực tuyến. Các hướng dẫn tương tác này giúp bạn quản lý một cluster đơn giản và các ứng dụng được đóng gói của bạn.

Bằng các hướng dẫn tương tác, bạn có thể học cách:

  • Triển khai một ứng dụng container trong một cluster.
  • Thay đổi quy mô triển khai.
  • Cập nhật ứng dụng container.
  • Debug ứng dụng container.

Những hướng dẫn này dùng Katacoda để chạy một terminal ảo trên trình duyệt web của bạn chạy Minikube. Không cần phải cài đặt và cấu hình bất kỳ phần mềm nào; mỗi hướng dẫn tương tác chạy trực tiếp từ trình duyệt web của bạn.


Kubernetes có thế làm những gì?

Với các dịch vụ web hiện đại, người dùng mong muốn các ứng dụng luôn sẵn sàng hoạt động 24/7 và các lập trình viên muốn triển khai các phiên bản của ứng dụng đó nhiều lần trong ngày. Việc đóng gói ứng dụng vào container giúp giải quyết mục tiêu này, cho phép các ứng dụng được phát hành và cập nhật một cách dễ dàng, nhanh chóng mà không có downtime. Kubernetes giúp bạn đảm bảo các ứng dụng container chạy ở bất kì đâu và bất kì lúc nào bạn muốn, đồng thời giúp chúng tìm thấy các tài nguyên và công cụ cần thiết để chạy. Kubernetes là nền tảng mã nguồn mở, chạy được trong môi trường production, được thiết kế và phát triển bởi Google, kết hợp với những ý tưởng tốt nhất từ cộng đồng.


1 - Tạo một Cluster

1.1 - Sử dụng Minikube để tạo một Cluster

Mục tiêu

  • Tìm hiểu Kubernetes cluster là gì?
  • Tìm hiểu Minikube là gì?
  • Khởi tạo một Kubernetes cluster sử dụng terminal trực tuyến.

Kubernetes Clusters

Kubernetes kết nối và điều phối các máy tính trong một cluster để chúng có thể hoạt động như một đơn vị thống nhất (unit). Nó cho phép bạn triển khai các ứng dụng trên Container mà không cần phải bận tâm chúng sẽ được khởi chạy trên chiếc máy tính cụ thể nào trong cluster. Để sử dụng mô hình triển khai của Kubernetes, các ứng dụng cần được đóng gói theo một cách linh động và không phụ thuộc vào từng máy tính cụ thể (host): tức là các ứng dụng được Container hóa. Các ứng dụng dạng Container có được sự khả chuyển và sẵn sàng cao hơn các mô hình triển khai được sử dụng trong quá khứ, ở đó chúng được cài đặt trực tiếp trên các máy tính cụ thể và gắn chặt với các bộ thư viện trên đó. Kubernetes phân bổ và điều phối các ứng dụng hoàn toàn tự động xuyên suốt cluster theo một cách hiệu quả. Ngoài ra Kubernetes là mã nguồn mở và sẵn sàng để sử dụng trong môi trường triển khai thực tế (production).

Một Kubernetes cluster bao gồm 2 loại tài nguyên:

  • Node Master làm nhiệm vụ quản lý toàn cluster.
  • Các Node còn lại khởi chạy các ứng dụng trực tiếp trên đó là các Worker.

Tổng kết:

  • Kubernetes cluster
  • Minikube

Kubernetes là một bộ công cụ mã nguồn mở, đáp ứng tiêu chuẩn triển khai thực tế, làm nhiệm vụ điều phối và khởi chạy các ứng dụng dạng Container bên trong một cluster hoặc thậm chí xuyên suốt nhiều cluster.


Mô hình Cluster


Node Master chịu trách nhiệm quản lý cluster. Nó quản lý toàn bộ các hoạt động bên trong cluster, như là việc khởi chạy các ứng dụng, kiểm soát chúng để chắc chắn chúng luôn ở các trạng thái như mong muốn, thay đổi khả năng đáp ứng của chúng (scaling), hoặc triển khai các phiên bản nâng cấp theo thời gian.

Một Node có thể là một máy ảo (VM) hoặc một máy tính vật lý làm việc với vai trò cung cấp khả năng tính toán cho cluster. Mỗi Node có một chương trình chạy thường trực bên trong tên là Kubelet, làm nhiệm vụ quản lý Node và duy trì kết nối với node Master. Mỗi Node bên cạnh đó còn chạy các chương trình dùng để khởi chạy và quản lý các Container như Docker hay rkt. Mỗi một Kubernetes cluster được triển khai trong thực tế khai thác thường có ít nhất 3 node thuộc 2 loại như bên trên.

Master quản lý cluster và các Node đóng vai trò chạy các ứng dụng Container.

Khi bạn triển khai các ứng dụng trên Kubernetes, bạn yêu cầu node Master phân bổ và khởi chạy các ứng dụng của bạn. Node Master tiếp đó tính toán để tìm ra các Node nào thích hợp cho việc triển khai ứng dụng. Các Node trong cluster kết nối và giao tiếp với nhau theo bộ qui tắc Kubernetes API do node Master đưa ra. Quản trị viên hoặc người sử dụng đầu cuối cũng có thể sử dụng bộ qui tắc này để tương tác trực tiếp với một cluster.

Một Kubernetes cluster có thể được xây dựng trên các máy tính vật lý hoặc các máy ảo. Để bắt đầu việc phát triển cho Kubernetes, bạn có thể sử dụng Minikube. Minikube là một bộ cài đặt Kubernetes bằng cách tạo ra một máy ảo trên máy tính của bạn và triển khai một cluster đơn giản bên trong máy ảo đó chỉ bao gồm một Node. Minikube có cho Linux, macOS, và Windows. Minikube CLI, một bộ công cụ dòng lệnh, cung cấp khả năng điều khiển cluster cho người sử dụng, như chạy, dừng chạy, xem trạng thái, hoặc xóa một thành phần trong cluster. Trong bài hướng dẫn này, bạn sẽ sử dụng một giao diện terminal trực tuyến với Minikube đã được cài đặt sẵn để thao tác.

Giờ bạn đã biết Kubernetes là gì, hãy tiếp tục với phần tương tác và tạo ra cluster đầu tiên!


1.2 - Hướng dẫn tương tác - Tạo một Cluster

Để tương tác với Terminal, hãy dùng phiên bản dành cho desktop/tablet

2 - Triển khai một ứng dụng

2.1 - Sử dụng kubectl để triển khai ứng dụng

Mục tiêu

  • Tìm hiểu về triển khai ứng dụng (Deployment).
  • Triển khai ứng dụng đầu tiên của bạn trên Kubernetes với kubectl.

Kubernetes Deployments

Giả sử bạn đã có một Kubernetes cluster đang hoạt động, bạn có thể triển khai ứng dụng của bạn trên cluster này. Để thực hiện điều đó, bạn cần tạo một kịch bản triển khai (Deployment). Kịch bản này sẽ giúp Kubernetes có thể tạo và cập nhật các phiên bản chạy (instances) của ứng dụng của bạn. Sau khi một kịch bản triển khai được tạo ra trong Kubernetes, node Master sẽ lập lịch để khởi chạy ứng dụng của bạn trên các Node của cluster.

Sau khi ứng dụng của bạn được khởi chạy trên các Node của cluster, Kubernetes sẽ tiếp tục theo dõi các chương trình đang chạy (instances) này. Nếu một Node đang chạy một instance của ứng dụng của bạn gặp trục trặc hoặc bị xóa khỏi cluster, Kubernetes sẽ thay thế instance đó bằng cách khởi chạy một instance mới trên một Node khác của cluster. Đây là cơ chế tự phục hồi (self-healing) khi có lỗi xảy ra hoặc khi một Node nào đó cần được bảo trì.

Ở thời điểm trước Kubernetes, các ứng dụng thường được khởi chạy thông qua các bộ tool cài đặt (installation scripts), nhưng chúng hầu như không thể hỗ trợ việc phục hồi khi có lỗi xảy ra với một máy tính trong cluster. Bằng cách khởi chạy các instances của ứng dụng trên nhiều Node xuyên suốt cluster, Kubernetes về cơ bản đã cho thấy một cách tiếp cận rất khác biệt về việc quản lý các ứng dụng.

Tổng kết:

  • Deployments
  • Kubectl

Một kịch bản triển khai (Deployment) là một tập hợp các thao tác làm nhiệm vụ khởi chạy ứng dụng của bạn và theo dõi cập nhật các instances của ứng dụng trên phạm vi toàn cluster


Triển khai ứng dụng đầu tiên của bạn trên Kubernetes


Bạn có thể tạo ra và quản lý các kịch bản triển khai (Deployment) thông qua giao diện dòng lệnh của Kubernetes với Kubectl. Kubectl sử dụng Kubernetes API để tương tác với cluster. Ở bài hướng dẫn này, bạn sẽ tìm hiểu các câu lệnh cơ bản nhất của Kubectl cần thiết để triển khai ứng dụng của bạn trên một Kubernetes cluster.

Khi tạo một kịch bản triển khai (Deployment), bạn cần chỉ ra một tệp Container Image chứa ứng dụng của bạn và số lượng bản sao (replicas) của ứng dụng bạn muốn chạy. Bạn hoàn toàn có thể thay đổi các thông số này bằng cách cập nhật kịch bản triển khai sau thông qua Module 56 của tập hướng dẫn cách cập nhật các Deployments.

Các ứng dụng phải được đóng gói trong một định dạng Container được hỗ trợ bởi Kubernetes để có thể triển khai trên hệ thống này

Về ứng dụng dầu tiên của bạn, nó sẽ là một ứng dụng Node.js được đóng gói trong một Docker container. (Nếu bạn chưa biết cách tạo một ứng dụng Node.js và đóng gói dưới dạng Container, vui lòng theo dõi hướng dẫn tại địa chỉ Hello Minikube tutorial).

Giờ bạn đã biết kịch bản triển khai (Deployment) là gì, hãy bắt đầu triển khai ứng dụng đầu tiên của bạn!


2.2 - Hướng dẫn tương tác - Triển khai một ứng dụng


Để tương tác với Terminal, hãy sử dụng phiên bản dành cho desktop/tablet

3 - Khám phá ứng dụng

3.1 - Khám Phá Pods Và Nodes

Mục tiêu

  • Tìm hiểu Kubernetes Pods là gì?
  • Tìm hiểu Kubernetes Nodes là gì?
  • Khắc phục sự cố ứng dụng đã triển khai

Kubernetes Pods

Khi bạn triển khai ứng dụng thông qua Module 2, Kubernetes tạo ra Pod để lưu trữ phiên bản chạy của ứng dụng của bạn. Một Pod là một khái niệm trừu tượng của Kubernetes, đại diện cho một nhóm gồm một hoặc nhiều ứng dụng containers (ví dụ như Docker hoặc rkt) và một số tài nguyên được chia sẻ cho các containers đó. Những tài nguyên đó bao gồm:

  • Lưu trữ được chia sẻ, dưới dạng Volumes
  • Kết nối mạng, như một cluster IP duy nhất
  • Thông tin về cách chạy từng container, chẳng hạn như phiên bản container image hoặc các ports cụ thể để sử dụng

Một Pod mô phỏng một "máy chủ logic" dành riêng cho ứng dụng và có thể chứa các ứng dụng containers khác nhau được liên kết tương đối chặt chẽ. Ví dụ, một Pod có thể bao gồm cả container với ứng dụng Node.js của bạn cũng như một container khác cung cấp dữ liệu hiển thị bởi webserver của Node.js. Các containers trong một Pod chia sẻ một địa chỉ IP và port space, chúng luôn được đặt cùng vị trí, cùng lên lịch trình, và chạy trong ngữ cảnh được chia sẻ trên cùng một Node.

Pods là các đơn vị nguyên tử trên nền tảng Kubernetes. Khi chúng tôi tạo một kịch bản triển khai (Deployment) trên Kubernetes, kịch bản triển khai đó tạo ra các Pods với các containers bên trong chúng (trái ngược với việc tạo các containers trực tiếp). Mỗi Pod được gắn với Node nơi nó được lên lịch trình, và tiếp tục ở đó cho đến khi chấm dứt (theo chính sách khởi động lại). Trong trường hợp có lỗi ở Node, các Pods giống nhau được lên lịch trình trên các Nodes có sẵn khác trong cluster.

Tổng Kết:

  • Pods
  • Nodes
  • Những lệnh chính của Kubectl

Một Pod là một nhóm gồm một hoặc nhiều ứng dụng containers (như Docker hoặc rkt) và bao gồm lưu trữ được chia sẻ (dưới dạng volumes), địa chỉ IP và thông tin về cách chạy chúng.


Tổng quan về Pods


Nodes

Một Pod luôn chạy trên một Node. Một Node là một máy worker trong Kubernetes và có thể là máy ảo hoặc máy vật lý, tuỳ thuộc vào cluster. Mỗi Node được quản lí bởi Master. Một Node có thể chứa nhiều Pods và Kubernetes master tự động xử lí việc lên lịch trình các Pods thuộc các Nodes ở trong cluster. Việc lên lịch trình tự động của Master sẽ tính đến các tài nguyên có sẵn trên mỗi Node.

Mỗi Node ở Kubernetes chạy ít nhất:

  • Kubelet, một quy trình chịu trách nhiệm liên lạc giữa Kubernetes Master và Node; quản lí các Pods và các containers đang chạy trên cùng một máy.
  • Một container runtime (như Docker, rkt) chịu trách nhiệm lấy container image từ registry, giải nén container và chạy ứng dụng. Các containers chỉ nên được lên lịch trình cùng nhau trong một Pod duy nhất nếu chúng được liên kết chặt chẽ.

Các containers chỉ nên được lên lịch trình cùng nhau trong một Pod duy nhất nếu chúng được liên kết chặt chẽ và cần chia sẻ tài nguyên như disk.


Tổng quan về Node


Khắc phục sự cố với kubectl

Ở Module 2, bạn đã sử dụng giao diện dòng lệnh Kubectl. Bạn sẽ tiếp tục sử dụng nó trong Module 3 để nhận thông tin về các ứng dụng đã triển khai và môi trường của chúng. Các hoạt động phổ biến nhất có thể được thực hiện với các lệnh kubectl sau:

  • kubectl get - liệt kê các tài nguyên
  • kubectl describe - hiển thị thông tin chi tiết về một tài nguyên
  • kubectl logs - in các bản ghi (logs) từ một container trong một pod
  • kubectl exec - thực hiện một lệnh trên một container trong một pod

Bạn có thể sử dụng các lệnh này để xem khi nào các ứng dụng được triển khai, trạng thái hiện tại của chúng là gì, nơi chúng đang chạy và cấu hình của chúng là gì.

Bây giờ chúng ta đã biết thêm về các thành phần cluster và dòng lệnh, hãy khám phá ứng dụng của chúng tôi.

Một Node là một máy worker trong Kubernetes và có thể là máy ảo hoặc máy vật lý, tuỳ thuộc vào cluster. Nhiều Pods có thể chạy trên cùng một Node.


3.2 - Hướng dẫn tương tác - Khám phá ứng dụng


Để tương tác với Terminal, hãy sử dụng phiên bản dành cho desktop/tablet

4 - Công khai ứng dụng ra bên ngoài

4.1 - Sử dụng Service để công khai ứng dụng

Mục tiêu

  • Tìm hiểu về Service trong Kubernetes
  • Hiểu mối quan hệ giữa labels và selectors với Service
  • Công khai một ứng dụng ra bên ngoài Kubernetes cluster

Tổng quan về Service trong Kubernetes

Pods trong Kubernetes có tính tạm thời. Pods có một vòng đời nhất định. Khi một worker node ngừng hoạt động, các Pod đang chạy trên Node đó cũng bị mất. Một ReplicaSet có thể tự động điều chỉnh cluster về trạng thái mong muốn bằng cách tạo các Pod mới để giữ cho ứng dụng tiếp tục hoạt động. Ví dụ khác, hãy xem xét một backend xử lý container image với 3 bản sao. Những bản sao này có thể thay thế lẫn nhau; hệ thống frontend không cần quan tâm đến các bản sao backend hoặc việc một Pod bị mất và được tạo lại. Tuy nhiên, mỗi Pod trong Kubernetes cluster đều có một địa chỉ IP duy nhất, kể cả các Pod trên cùng một Node, vì vậy cần có cơ chế tự động điều phối các thay đổi giữa các Pod để ứng dụng có thể tiếp tục hoạt động.

Service trong Kubernetes là một lớp trừu tượng định nghĩa một tập hợp logic các Pod và chính sách truy cập cho chúng. Service cho phép các Pod phụ thuộc nhau có thể kết nối với nhau một cách "lỏng lẻo". Service được định nghĩa bằng YAML hoặc JSON, giống như tất cả các manifest đối tượng khác của Kubernetes. Tập hợp các Pod mà Service nhắm đến thường được xác định bằng một label selector (xem phần dưới để hiểu tại sao đôi khi bạn có thể muốn một Service mà không cần selector trong đặc tả).

Mặc dù mỗi Pod có một địa chỉ IP duy nhất, những IP này không được công khai ra bên ngoài cluster nếu không có Service. Service cho phép ứng dụng của bạn nhận lưu lượng truy cập từ bên ngoài. Service có thể được công khai theo nhiều cách khác nhau bằng cách chỉ định type trong phần spec của Service:

  • ClusterIP (mặc định) - Công khai Service trên một IP nội bộ trong cluster. Kiểu này chỉ cho phép Service có thể truy cập được từ bên trong cluster.

  • NodePort - Công khai Service trên cùng một port của mỗi Node được chọn trong cluster sử dụng NAT. Cho phép Service có thể truy cập được từ bên ngoài cluster thông qua <NodeIP>:<NodePort>. Là tập mở rộng của ClusterIP.

  • LoadBalancer - Tạo một bộ cân bằng tải bên ngoài trong cloud hiện tại (nếu được hỗ trợ) và gán một IP cố định bên ngoài cho Service. Là tập mở rộng của NodePort.

  • ExternalName - Ánh xạ Service với nội dung của trường externalName (ví dụ: foo.bar.example.com), bằng cách trả về một bản ghi CNAME với giá trị tương ứng. Không thiết lập bất kỳ proxy nào. Kiểu này yêu cầu phiên bản v1.7 trở lên của kube-dns hoặc CoreDNS phiên bản 0.0.8 trở lên.

Thông tin chi tiết hơn về các kiểu Service khác nhau có thể được tìm thấy trong hướng dẫn Sử dụng Source IP. Đồng thời xem thêm Kết nối ứng dụng với Services.

Ngoài ra, lưu ý rằng có một số trường hợp sử dụng Service mà không cần định nghĩa selector trong đặc tả. Service được tạo không có selector sẽ không tạo đối tượng Endpoints tương ứng. Điều này cho phép người dùng tự ánh xạ Service đến các endpoints cụ thể. Một trường hợp khác là khi bạn chỉ sử dụng type: ExternalName.

Service và Labels

Service định tuyến lưu lượng qua một tập hợp các Pod. Service là lớp trừu tượng cho phép các Pod có thể được tạo và xóa trong Kubernetes mà không ảnh hưởng đến ứng dụng. Việc service discovery và định tuyến giữa các Pod phụ thuộc nhau (như các thành phần frontend và backend trong một ứng dụng) được xử lý bởi Kubernetes Services.

Service khớp với một tập hợp các Pod sử dụng labels và selectors, một cơ chế nhóm cho phép thao tác logic trên các đối tượng trong Kubernetes. Labels là các cặp key/value được gắn vào đối tượng và có thể được sử dụng theo nhiều cách:

  • Chỉ định đối tượng cho môi trường development, test và production
  • Gắn thẻ phiên bản
  • Phân loại đối tượng bằng tags

Labels có thể được gắn vào đối tượng khi tạo hoặc sau đó và có thể được sửa đổi bất cứ lúc nào. Bây giờ hãy công khai ứng dụng của chúng ta bằng Service và áp dụng một số labels.

Bước 1: Tạo một Service mới

Đầu tiên, hãy kiểm tra xem ứng dụng của chúng ta có đang chạy không. Chúng ta sẽ sử dụng lệnh kubectl get để xem các Pod hiện có:

kubectl get pods

Nếu không có Pod nào đang chạy, điều đó có nghĩa là các đối tượng từ bài hướng dẫn trước đã được dọn dẹp. Trong trường hợp này, hãy quay lại và tạo lại deployment từ bài hướng dẫn Sử dụng kubectl để tạo Deployment. Vui lòng đợi vài giây và liệt kê lại các Pod. Bạn có thể tiếp tục khi thấy một Pod đang chạy.

Tiếp theo, hãy liệt kê các Service hiện có trong cluster:

kubectl get services

Bây giờ chúng ta có một Service đang chạy tên là kubernetes-bootcamp. Service này đã nhận được một cluster-IP duy nhất, một port nội bộ và một external-IP (IP của Node).

Để tìm hiểu port nào được mở ra bên ngoài (cho Service kiểu NodePort), chúng ta sẽ chạy lệnh con describe service:

kubectl describe services/kubernetes-bootcamp

Tạo một biến môi trường NODE_PORT với giá trị là Node port được gán:

export NODE_PORT="$(kubectl get services/kubernetes-bootcamp -o go-template='{{(index .spec.ports 0).nodePort}}')"
echo "NODE_PORT=$NODE_PORT"

Bây giờ chúng ta có thể kiểm tra xem ứng dụng đã được công khai ra ngoài cluster chưa bằng cách sử dụng curl, địa chỉ IP của Node và port được công khai ra bên ngoài:

curl http://"$(minikube ip):$NODE_PORT"

Lưu ý:

Nếu bạn đang chạy minikube với Docker Desktop làm container driver, bạn cần một minikube tunnel. Điều này là do các container trong Docker Desktop bị cô lập với máy host của bạn.

Trong một cửa sổ terminal riêng biệt, thực thi:

minikube service kubernetes-bootcamp --url

Kết quả sẽ như sau:

http://127.0.0.1:51082
!  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

Sau đó sử dụng URL được cung cấp để truy cập ứng dụng:

curl 127.0.0.1:51082

Và chúng ta nhận được phản hồi từ server. Service đã được công khai.

Bước 2: Sử dụng labels

Deployment đã tự động tạo một label cho Pod của chúng ta. Với lệnh con describe deployment, bạn có thể thấy tên (khóa) của label đó:

kubectl describe deployment

Hãy sử dụng label này để truy vấn danh sách Pod của chúng ta. Chúng ta sẽ sử dụng lệnh kubectl get pods với tham số -l, theo sau là giá trị của label:

kubectl get pods -l app=kubernetes-bootcamp

Bạn có thể làm tương tự để liệt kê các Service hiện có:

kubectl get services -l app=kubernetes-bootcamp

Lấy tên của Pod và lưu vào biến môi trường POD_NAME:

export POD_NAME="$(kubectl get pods -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')"
echo "Name of the Pod: $POD_NAME"

Để áp dụng một label mới, chúng ta sử dụng lệnh con label theo sau bởi loại đối tượng, tên đối tượng và label mới:

kubectl label pods "$POD_NAME" version=v1

Điều này sẽ áp dụng một label mới cho Pod của chúng ta (chúng ta đã gắn phiên bản ứng dụng vào Pod), và chúng ta có thể kiểm tra nó bằng lệnh describe pod:

kubectl describe pods "$POD_NAME"

Chúng ta thấy rằng label đã được gắn vào Pod của chúng ta. Và bây giờ chúng ta có thể truy vấn danh sách pod sử dụng label mới:

kubectl get pods -l version=v1

Và chúng ta thấy Pod đó.

Bước 3: Xóa một service

Để xóa Service, bạn có thể sử dụng lệnh con delete service. Labels cũng có thể được sử dụng ở đây:

kubectl delete service -l app=kubernetes-bootcamp

Xác nhận rằng Service đã bị xóa:

kubectl get services

Điều này xác nhận rằng Service của chúng ta đã được gỡ bỏ. Để xác nhận rằng route không còn được công khai nữa, bạn có thể thử curl tới IP và port đã được công khai trước đó:

curl http://"$(minikube ip):$NODE_PORT"

Điều này chứng minh rằng ứng dụng không còn có thể truy cập được từ bên ngoài cluster nữa. Bạn có thể xác nhận rằng ứng dụng vẫn đang chạy bằng cách curl từ bên trong pod:

kubectl exec -ti $POD_NAME -- curl http://localhost:8080

Chúng ta thấy rằng ứng dụng vẫn đang hoạt động. Điều này là do Deployment đang quản lý ứng dụng. Để tắt ứng dụng, bạn cần xóa cả Deployment.

Tiếp theo là gì