Chào các bạn trên hành trình “Roadmap Docker”! Nếu bạn đã đồng hành cùng chúng tôi qua các bài viết trước, từ việc hiểu Container Là Gì, sự khác biệt giữa Container, VM và Bare Metal, đến việc làm quen với Docker và Tiêu chuẩn OCI, cũng như các kỹ năng nền tảng về Linux, Shell Script, quản lý Lưu trữ dữ liệu bền vững, viết Dockerfile hay quản lý ứng dụng đa-container với Docker Compose, thì giờ là lúc chúng ta nhìn xa hơn. Docker đã giúp việc đóng gói và chạy ứng dụng trở nên dễ dàng hơn bao giờ hết. Nhưng điều gì xảy ra khi bạn cần chạy ứng dụng đó ở quy mô lớn, đảm bảo nó luôn hoạt động, tự phục hồi khi gặp sự cố, và dễ dàng cập nhật mà không làm gián đoạn dịch vụ?
Đây chính là lúc các nền tảng điều phối (orchestration) container như Kubernetes tỏa sáng. Đối với những người đã thành thạo Docker, Kubernetes là bước tiếp theo tự nhiên để đưa các ứng dụng container hóa của bạn lên một tầm cao mới, đặc biệt là trong môi trường sản phẩm phức tạp và đòi hỏi tính sẵn sàng cao. Bài viết này sẽ là cầu nối giúp bạn chuyển từ tư duy quản lý container đơn lẻ hoặc với Docker Compose sang thế giới của Kubernetes.
Mục lục
Từ Docker Run Đơn Lẻ Đến Hệ Sinh Thái Phân Tán
Khi bắt đầu với Docker, bạn thường làm quen với lệnh docker run
để khởi chạy một container từ một image. Điều này rất tuyệt vời cho môi trường phát triển cục bộ, kiểm thử, hoặc chạy các tác vụ đơn giản. Bạn có thể chạy một container web server, một database, hoặc một công cụ dòng lệnh đã được đóng gói với Docker. Bạn có thể cấu hình các tùy chọn runtime, quản lý volumes cho dữ liệu bền vững, và kết nối các container lại với nhau bằng Docker network.
Với Docker Compose, bạn nâng cấp khả năng quản lý lên một bước nữa. Bạn định nghĩa một ứng dụng gồm nhiều service (mỗi service thường là một container hoặc nhóm container cùng loại) trong một file docker-compose.yml
. Compose giúp bạn dễ dàng khởi tạo, dừng, và quản lý toàn bộ stack ứng dụng cục bộ chỉ với một vài lệnh. Điều này là bước tiến lớn khi làm việc với các kiến trúc ứng dụng phức tạp hơn, chẳng hạn như microservices.
Tuy nhiên, Docker và Docker Compose (ở chế độ standalone) vẫn còn những hạn chế đáng kể khi triển khai ứng dụng ở môi trường production quy mô lớn:
- Khả năng mở rộng (Scaling): Làm sao để tự động tăng hoặc giảm số lượng container khi tải tăng giảm?
- Tính sẵn sàng cao (High Availability): Nếu một máy chủ vật lý hoặc một container gặp sự cố, làm sao để ứng dụng tự động khởi động lại container đó hoặc chuyển sang máy chủ khác mà không cần can thiệp thủ công?
- Tự phục hồi (Self-healing): Nếu một container chết, ai sẽ khởi động lại nó?
- Cập nhật (Rolling Updates & Rollbacks): Làm sao để triển khai phiên bản mới của ứng dụng một cách mượt mà, từ từ thay thế các container cũ mà không gây gián đoạn dịch vụ, và có khả năng quay lại phiên bản cũ nếu có lỗi?
- Phát hiện dịch vụ (Service Discovery) & Cân bằng tải (Load Balancing): Làm sao để các service trong ứng dụng tìm thấy nhau, và lưu lượng truy cập được phân phối đều giữa các bản sao (replica) của service?
- Quản lý cấu hình và bí mật (Configuration & Secrets Management): Làm sao để quản lý cấu hình ứng dụng và các thông tin nhạy cảm (mật khẩu, khóa API) một cách an toàn và hiệu quả trên nhiều máy chủ?
Đây chính là những bài toán mà các nền tảng điều phối container như Kubernetes được thiết kế để giải quyết. Kubernetes không thay thế Docker; ngược lại, nó sử dụng Docker (hoặc các runtime container tương thích OCI khác) làm nền tảng để chạy các container. Kubernetes thêm vào một lớp trừu tượng và tự động hóa mạnh mẽ ở phía trên để quản lý toàn bộ vòng đời của ứng dụng container hóa trên một cụm (cluster) các máy chủ.
Kubernetes: Nền Tảng Điều Phối Container
Kubernetes (thường gọi tắt là K8s) là một hệ thống mã nguồn mở mạnh mẽ để tự động hóa việc triển khai, mở rộng và quản lý các ứng dụng container hóa. Được Google giới thiệu và sau đó được chuyển giao cho Cloud Native Computing Foundation (CNCF), Kubernetes đã nhanh chóng trở thành tiêu chuẩn vàng trong lĩnh vực điều phối container.
Điểm khác biệt cốt lõi giữa việc chạy container với Docker standalone/Compose và Kubernetes là tư duy khai báo (declarative) so với tư duy mệnh lệnh (imperative). Với Docker, bạn thường ra lệnh trực tiếp: “chạy container X”, “dừng container Y”, “kết nối Z”. Với Kubernetes, bạn khai báo trạng thái mong muốn (desired state) của hệ thống: “Tôi muốn chạy 3 bản sao của ứng dụng A sử dụng image X phiên bản Y, kết nối với database B”. Kubernetes sẽ tự động thực hiện các hành động cần thiết để đưa hệ thống từ trạng thái hiện tại đến trạng thái mong muốn đó, và liên tục giám sát để duy trì trạng thái này.
Để hiểu Kubernetes, chúng ta sẽ xem xét các khái niệm chính của nó và so sánh chúng với những gì bạn đã biết về Docker.
Các Khái Niệm Chính Của Kubernetes Qua Lăng Kính Docker
Pod: Đơn vị Triển khai Nhỏ nhất
- Trong Docker: Đơn vị cơ bản bạn làm việc là Container. Một container chạy một tiến trình (process) chính. Để chạy nhiều container liên quan (ví dụ: ứng dụng web và sidecar agent), bạn dùng Docker Compose để nhóm chúng lại, nhưng mỗi container vẫn là một thực thể độc lập có vòng đời riêng.
- Trong Kubernetes: Đơn vị triển khai nhỏ nhất không phải là Container, mà là Pod. Một Pod là một nhóm gồm một hoặc nhiều container cùng chia sẻ tài nguyên mạng (địa chỉ IP, cổng) và lưu trữ (Volumes). Các container trong cùng một Pod luôn được lên lịch chạy cùng nhau trên cùng một Node (máy chủ).
Hãy nghĩ về Pod như một “máy ảo siêu nhẹ” được tùy chỉnh để chạy các container cụ thể, hoặc đơn giản hơn, là một “ngôi nhà” cho một nhóm các container liên quan chặt chẽ với nhau. Mô hình Pod rất hữu ích cho các ứng dụng có nhiều tiến trình cần làm việc cùng nhau và chia sẻ tài nguyên, ví dụ: ứng dụng chính và container sidecar để ghi log, giám sát hoặc đồng bộ dữ liệu. Hầu hết thời gian, bạn sẽ chỉ chạy một container chính trong một Pod, nhưng mô hình Pod vẫn cung cấp các lợi ích như chia sẻ network namespace và volume.
- Ví dụ YAML cho một Pod đơn giản:
apiVersion: v1 kind: Pod metadata: name: my-nginx-pod spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80
Deployment: Quản lý Vòng Đời Ứng dụng
- Trong Docker: Bạn chạy container bằng
docker run
, hoặc quản lý nhiều service vớidocker-compose up
. Việc cập nhật thường yêu cầu dừng container cũ và chạy container mới. Mở rộng số lượng container phải làm thủ công hoặc dựa vào script. - Trong Kubernetes: Bạn sử dụng Deployment để định nghĩa cách bạn muốn chạy ứng dụng của mình. Deployment mô tả trạng thái mong muốn: bao nhiêu bản sao (replicas) của Pod bạn muốn chạy, sử dụng image nào, cách thức cập nhật (rolling update), v.v.
Deployment sẽ tạo và quản lý các ReplicaSet (đảm bảo số lượng Pod replicas luôn như mong muốn) và Pods. Khi bạn muốn cập nhật ứng dụng, bạn chỉ cần thay đổi image trong file YAML của Deployment và áp dụng nó. Kubernetes sẽ tự động thực hiện chiến lược rolling update: tạo Pod mới với image mới, chờ chúng sẵn sàng, và từ từ xóa bỏ Pod cũ. Nếu có lỗi, Deployment có thể tự động hoặc thủ công rollback về phiên bản trước. Đây là một trong những lợi ích lớn nhất của Kubernetes so với quản lý container thủ công hoặc chỉ với Docker Compose.
- Ví dụ YAML cho một Deployment:
apiVersion: apps/v1 kind: Deployment metadata: name: my-web-deployment spec: replicas: 3 # Tôi muốn 3 bản sao của Pod này selector: matchLabels: app: my-web-app template: metadata: labels: app: my-web-app spec: containers: - name: my-web-container image: my-docker-registry/my-web-app:1.0.0 # Sử dụng image của bạn ports: - containerPort: 80
Service: Phát hiện Dịch vụ và Cân bằng Tải
- Trong Docker: Bạn có thể map cổng từ container ra host (
docker run -p
) hoặc sử dụng Docker network để các container trong cùng network có thể giao tiếp với nhau bằng tên service (với Docker Compose). Tuy nhiên, việc cân bằng tải giữa nhiều bản sao container (khi không dùng Swarm mode) hay phát hiện IP động khi container khởi động lại có thể phức tạp. - Trong Kubernetes: Service là một layer trừu tượng định nghĩa một tập hợp các Pods và policy để truy cập chúng. Service cung cấp một địa chỉ IP và tên DNS ổn định cho một nhóm Pods (thường được chọn bằng selector label), ngay cả khi các Pods đó bị thay thế, khởi động lại hoặc scale up/down.
Service tự động cân bằng tải giữa các Pods backend của nó. Có nhiều loại Service khác nhau (ClusterIP, NodePort, LoadBalancer, ExternalName) tùy thuộc vào việc bạn muốn truy cập Service từ bên trong cluster hay từ bên ngoài. Service giải quyết hiệu quả bài toán phát hiện dịch vụ và cân bằng tải trong môi trường phân tán, nơi các Pods có thể sinh ra và chết đi liên tục.
Node: Máy chủ Chạy Container
- Trong Docker: Đây là máy chủ (physical hoặc VM) mà bạn cài Docker Engine và chạy các lệnh
docker
. - Trong Kubernetes: Node là một máy chủ worker (Physical hoặc Virtual Machine) trong cluster Kubernetes. Mỗi Node có các thành phần cần thiết để chạy Pods, bao gồm Kubelet (agent giao tiếp với Control Plane), container runtime (như Docker, containerd, CRI-O) và Kube-proxy (quản lý network rules cho Services). Kubernetes Control Plane quản lý các Node và lên lịch chạy Pods trên chúng.
Cluster: Tập hợp các Máy chủ
- Trong Docker: Bạn thường làm việc với Docker Engine trên một máy chủ đơn lẻ hoặc sử dụng Docker Swarm để kết nối nhiều máy chủ thành một swarm (mặc dù Swarm ít phổ biến hơn Kubernetes).
- Trong Kubernetes: Cluster là tập hợp các Nodes được quản lý bởi Kubernetes Control Plane. Control Plane là bộ não của cluster, chịu trách nhiệm duy trì trạng thái mong muốn (Scheduler, Controller Manager, API Server, etcd). Các Node worker chạy các Pods chứa ứng dụng của bạn. Kubernetes Cluster là môi trường phân tán nơi các ứng dụng container hóa được triển khai, mở rộng và quản lý một cách tự động.
Namespace: Cô lập Tài nguyên Logic
- Trong Docker: Bạn có thể sử dụng Docker network để cô lập mạng giữa các nhóm container hoặc sử dụng các công cụ quản lý dự án để phân chia ứng dụng.
- Trong Kubernetes: Namespace cung cấp một cách để phân chia cluster thành các khu vực riêng biệt (logic). Tài nguyên trong một Namespace được cô lập khỏi tài nguyên ở Namespace khác. Điều này rất hữu ích cho việc tổ chức các môi trường (dev, staging, production), các team khác nhau, hoặc các ứng dụng khác nhau trong cùng một cluster mà không lo ngại xung đột tên. Bạn có thể đặt giới hạn tài nguyên (CPU, RAM) cho từng Namespace.
Volume: Lưu trữ Bền vững
- Trong Docker: Bạn sử dụng Docker Volumes hoặc Bind Mounts (như đã tìm hiểu) để lưu trữ dữ liệu bên ngoài vòng đời của container, đảm bảo dữ liệu không bị mất khi container bị xóa.
- Trong Kubernetes: Volume trong Kubernetes phức tạp hơn và mạnh mẽ hơn. Kubernetes Volumes được gắn vào Pod (chứ không phải container) và có thể được chia sẻ giữa các container trong cùng một Pod. Kubernetes hỗ trợ rất nhiều loại Volume khác nhau (PersistentVolume, PersistentVolumeClaim, EmptyDir, ConfigMap, Secret, hostPath, các loại volume cho cloud storage như AWS EBS, GCE Persistent Disks, Azure Disk, v.v.).
Khái niệm PersistentVolume (PV) và PersistentVolumeClaim (PVC) đặc biệt quan trọng. PV biểu diễn một phần tài nguyên lưu trữ trong cluster, còn PVC là yêu cầu sử dụng tài nguyên lưu trữ đó của người dùng/Pod. Kubernetes tự động gắn PV phù hợp vào Pod khi Pod yêu cầu PVC. Điều này giúp tách biệt việc quản lý lưu trữ (do admin cluster thực hiện) và việc sử dụng lưu trữ (do người dùng ứng dụng thực hiện). Đây là một bước tiến lớn so với việc quản lý volume thủ công trên từng Docker host.
Docker CLI vs. Kubectl: Thế giới Lệnh Mới
Nếu bạn đã quen thuộc với việc sử dụng Docker CLI để quản lý container, image, network và volume, bạn sẽ cần làm quen với công cụ dòng lệnh chính của Kubernetes: kubectl
. kubectl
cho phép bạn giao tiếp với Kubernetes Control Plane để triển khai ứng dụng, kiểm tra trạng thái của cluster và tài nguyên, xem logs, v.v.
Mặc dù cú pháp khác biệt, nhiều lệnh kubectl
có chức năng tương tự như các lệnh Docker bạn đã biết. Sự khác biệt chính là thay vì thao tác trực tiếp trên container hoặc image, bạn thao tác trên các đối tượng (objects) của Kubernetes như Pods, Deployments, Services, v.v., thường bằng cách áp dụng các file cấu hình định dạng YAML.
Dưới đây là bảng so sánh một số khái niệm và lệnh cơ bản giữa Docker và Kubernetes:
Khái niệm/Tác vụ | Trong Docker | Trong Kubernetes |
---|---|---|
Đơn vị tính toán nhỏ nhất | Container | Pod (có thể chứa 1 hoặc nhiều Container) |
Định nghĩa ứng dụng/Service | Dockerfile, docker run , docker-compose.yml |
YAML Manifests (Deployment, StatefulSet, etc.) |
Chạy ứng dụng | docker run , docker-compose up |
kubectl apply -f <file.yaml> |
Liệt kê các thực thể đang chạy | docker ps |
kubectl get pods , kubectl get deployments , kubectl get services , v.v. |
Xem log của ứng dụng | docker logs <container_id/name> |
kubectl logs <pod_name> [<container_name>] |
Thực thi lệnh bên trong container | docker exec -it <container_id/name> <command> |
kubectl exec -it <pod_name> [<container_name>] -- <command> |
Dừng/Xóa ứng dụng | docker stop/rm , docker-compose down |
kubectl delete -f <file.yaml> hoặc kubectl delete <type> <name> |
Tải image | docker pull <image_name> |
Không cần lệnh riêng; Kubernetes Control Plane tự động pull image khi lên lịch Pod trên Node. |
Xây dựng image | docker build -t <tag> . |
Sử dụng Docker CLI hoặc công cụ khác bên ngoài Kubernetes. K8s chỉ sử dụng image đã được build. |
Lưu trữ bền vững | Docker Volume, Bind Mount | Volume (nhiều loại), PersistentVolume (PV), PersistentVolumeClaim (PVC) |
Mạng | Docker Network | Service (ClusterIP, NodePort, LoadBalancer), Ingress |
Điều phối ứng dụng đa-container (đơn máy) | Docker Compose | Pod, Deployment, Service |
Điều phối ứng dụng đa-container (đa máy, production) | Docker Swarm (ít phổ biến) | Kubernetes Cluster (tiêu chuẩn công nghiệp) |
Việc chuyển từ tư duy lệnh sang tư duy khai báo bằng file YAML là một trong những thay đổi lớn nhất khi bắt đầu với Kubernetes. Bạn sẽ dành nhiều thời gian hơn để viết và quản lý các file cấu hình này. Tối ưu Docker image và gắn tag đúng cách vẫn là những kỹ năng quan trọng, vì Kubernetes sẽ sử dụng chính những image đó từ các Registry.
Khi Nào Bạn Cần Kubernetes?
Nếu bạn chỉ đang phát triển ứng dụng trên máy cục bộ, chạy một vài service nhỏ, hoặc triển khai ứng dụng lên một máy chủ đơn lẻ mà không yêu cầu khả năng tự phục hồi hay mở rộng động, thì Docker và Docker Compose có thể đã đủ.
Tuy nhiên, bạn nên bắt đầu tìm hiểu Kubernetes khi:
- Bạn cần triển khai ứng dụng lên môi trường production với yêu cầu tính sẵn sàng cao.
- Ứng dụng của bạn cần khả năng tự động mở rộng dựa trên tải.
- Bạn có một kiến trúc microservices phức tạp với hàng chục hoặc hàng trăm service cần giao tiếp và quản lý.
- Bạn cần khả năng cập nhật ứng dụng mượt mà (rolling updates) và rollback dễ dàng.
- Bạn cần quản lý cấu hình và bí mật một cách tập trung và an toàn trên toàn bộ cluster.
- Bạn muốn tối ưu hóa việc sử dụng tài nguyên phần cứng bằng cách chạy các container của nhiều ứng dụng khác nhau trên cùng một nhóm các máy chủ một cách hiệu quả.
- Bạn muốn áp dụng các thực hành CI/CD nâng cao (như đã đề cập) để tự động hóa quy trình build, test và deploy.
Nói cách khác, Kubernetes là bước tiếp theo khi bạn chuyển từ việc quản lý container ở mức “cá nhân” hoặc “ứng dụng đơn lẻ” sang quản lý ở mức “cụm máy chủ” và “toàn bộ hệ thống”.
Bước Tiếp Theo Trên Con Đường
Việc chuyển từ Docker sang Kubernetes có thể giống như chuyển từ lái một chiếc xe hơi đơn lẻ sang quản lý cả một đội xe. Có nhiều khái niệm mới, nhiều công cụ mới (chủ yếu là kubectl
và các file YAML), và một cách suy nghĩ khác về cách triển khai và quản lý ứng dụng.
Để bắt đầu, bạn không nhất thiết phải thiết lập một cluster Kubernetes phức tạp ngay lập tức. Có nhiều cách để trải nghiệm Kubernetes trên máy cục bộ:
- Minikube: Chạy một cluster Kubernetes đơn Node bên trong một máy ảo trên máy tính của bạn.
- Kind (Kubernetes in Docker): Chạy một cluster Kubernetes sử dụng các Docker container làm Node.
- Docker Desktop: Phiên bản mới của Docker Desktop tích hợp sẵn một cluster Kubernetes đơn Node, rất tiện lợi cho việc phát triển và thử nghiệm cục bộ.
Ngoài ra, các nhà cung cấp dịch vụ đám mây lớn như Google Cloud (GKE), Amazon Web Services (EKS), Azure (AKS) đều cung cấp các dịch vụ Kubernetes được quản lý hoàn toàn, giúp bạn dễ dàng bắt đầu mà không cần lo lắng về việc cài đặt và quản lý Control Plane phức tạp.
Hãy bắt đầu bằng cách cài đặt một trong các công cụ cục bộ đó, làm quen với kubectl
và thử triển khai ứng dụng Docker “hello-world” đầu tiên của bạn dưới dạng Pod và Deployment. Sau đó, khám phá cách tạo Service để truy cập ứng dụng đó. Dần dần, bạn sẽ mở rộng kiến thức sang các khái niệm nâng cao hơn như Ingress (cho truy cập từ bên ngoài cluster), ConfigMaps và Secrets (cho cấu hình và bí mật), StatefulSets (cho ứng dụng có trạng thái như database), Helm (quản lý package cho Kubernetes), v.v.
Con đường phía trước rất rộng mở, và việc nắm vững Kubernetes sẽ là một kỹ năng vô cùng giá trị cho bất kỳ kỹ sư DevOps hay nhà phát triển nào trong bối cảnh hiện đại. Docker là nền tảng vững chắc, và Kubernetes là lớp kiến trúc giúp bạn xây dựng và vận hành các ứng dụng phức tạp, đáng tin cậy ở quy mô lớn.
Chúc mừng bạn đã tiến thêm một bước nữa trên “Roadmap Docker”! Hãy tiếp tục khám phá và thực hành nhé.