# Kubernetes: Triển khai ứng dụng đầu tiên > Hướng dẫn thực hành để triển khai ứng dụng trên Kubernetes. Từ cài đặt minikube đến Deployments, Services và ConfigMaps với các ví dụ cụ thể. - Published: 2026-02-12 - Updated: 2026-04-28 - Author: SharpSkill - Tags: kubernetes, k8s, container orchestration, devops, deployment - Reading time: 16 min --- Kubernetes (K8s) đã trở thành tiêu chuẩn de facto cho việc điều phối container. Được thiết kế bởi Google và hiện được CNCF duy trì, Kubernetes tự động hóa việc triển khai, mở rộng và quản lý các ứng dụng được container hóa. Hướng dẫn này đi qua việc thiết lập một cluster cục bộ và triển khai ứng dụng đầu tiên. > **Yêu cầu trước** > > Kiến thức cơ bản về Docker được khuyến nghị trước khi đi sâu vào Kubernetes. Container là các khối nền tảng mà Kubernetes điều phối. Đọc trước hướng dẫn Docker giúp việc hiểu các khái niệm được trình bày ở đây dễ dàng hơn rất nhiều. ## Hiểu về kiến trúc Kubernetes Kubernetes dựa trên kiến trúc master-worker. Control Plane đưa ra các quyết định toàn cục về cluster, trong khi các Node chạy các workload. ``` # Simplified Kubernetes Architecture ┌─────────────────────────────────────────────────────────────┐ │ CONTROL PLANE │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │ │ API Server │ │ Scheduler │ │ Controller Manager │ │ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │ ┌─────────────────────────────────────────────────────────┐│ │ │ etcd ││ │ └─────────────────────────────────────────────────────────┘│ └─────────────────────────────────────────────────────────────┘ │ ┌────────────────────┼────────────────────┐ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ NODE 1 │ │ NODE 2 │ │ NODE 3 │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ ┌───────────┐ │ │ │ kubelet │ │ │ │ kubelet │ │ │ │ kubelet │ │ │ ├───────────┤ │ │ ├───────────┤ │ │ ├───────────┤ │ │ │ kube-proxy│ │ │ │ kube-proxy│ │ │ │ kube-proxy│ │ │ ├───────────┤ │ │ ├───────────┤ │ │ ├───────────┤ │ │ │ Pods │ │ │ │ Pods │ │ │ │ Pods │ │ │ └───────────┘ │ │ └───────────┘ │ │ └───────────┘ │ └─────────────────┘ └─────────────────┘ └─────────────────┘ ``` API Server là điểm vào cho mọi lệnh. etcd lưu trữ trạng thái của cluster. Scheduler gán Pod cho các Node. Controller duy trì trạng thái mong muốn của hệ thống. ## Thiết lập môi trường cục bộ Có nhiều tùy chọn để thử nghiệm Kubernetes cục bộ: minikube, kind, k3d hoặc Docker Desktop. Minikube vẫn là giải pháp phổ biến nhất để học. ```bash # terminal # Install kubectl (Kubernetes client) curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" chmod +x kubectl sudo mv kubectl /usr/local/bin/ # Verify installation kubectl version --client # Client Version: v1.31.0 # Install minikube curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 sudo install minikube-linux-amd64 /usr/local/bin/minikube # Start the local cluster minikube start --driver=docker --cpus=2 --memory=4096 # Check status minikube status # minikube: Running # cluster: Running # kubectl: Configured ``` Minikube tạo một cluster Kubernetes một-node bên trong một máy ảo hoặc một container Docker. Tài nguyên được phân bổ (CPU, bộ nhớ) có thể được điều chỉnh theo nhu cầu. ```bash # terminal # Access the Kubernetes dashboard (web interface) minikube dashboard # Check cluster nodes kubectl get nodes # NAME STATUS ROLES AGE VERSION # minikube Ready control-plane 5m v1.31.0 # Detailed cluster information kubectl cluster-info ``` > **Các lựa chọn thay thế Minikube** > > Kind (Kubernetes in Docker) khởi động nhanh hơn và phù hợp hơn cho việc kiểm thử CI/CD. K3d sử dụng k3s, một bản phân phối Kubernetes nhẹ. Docker Desktop tích hợp Kubernetes trực tiếp nhưng tiêu thụ nhiều tài nguyên hơn. ## Pods: đơn vị cơ bản Pod là đơn vị nhỏ nhất có thể triển khai trong Kubernetes. Một Pod đóng gói một hoặc nhiều container chia sẻ cùng một mạng và bộ lưu trữ. ```yaml # pod-simple.yaml apiVersion: v1 kind: Pod metadata: # Unique Pod name within the namespace name: nginx-pod # Labels for organization and selection labels: app: nginx environment: development spec: containers: # Main container definition - name: nginx # Docker image to use image: nginx:1.25-alpine # Ports exposed by the container ports: - containerPort: 80 # Container resource allocation resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" ``` Manifest YAML này khai báo một Pod chứa một container nginx duy nhất. Các nhãn cho phép xác định và chọn các Pod. Tài nguyên xác định các đảm bảo tối thiểu (requests) và giới hạn tối đa (limits). ```bash # terminal # Create the Pod kubectl apply -f pod-simple.yaml # pod/nginx-pod created # List Pods kubectl get pods # NAME READY STATUS RESTARTS AGE # nginx-pod 1/1 Running 0 30s # Full Pod details kubectl describe pod nginx-pod # Container logs kubectl logs nginx-pod # Execute a command inside the Pod kubectl exec -it nginx-pod -- /bin/sh # Delete the Pod kubectl delete pod nginx-pod ``` Pod có bản chất là tạm thời. Trong trường hợp gặp sự cố hoặc bị xóa, Kubernetes không tự động tạo lại chúng. Deployment giải quyết hạn chế này. ## Deployments: quản lý theo kiểu khai báo Deployment xác định trạng thái mong muốn cho một tập hợp các Pod giống nhau. Kubernetes tự động duy trì trạng thái này bằng cách tạo, cập nhật hoặc xóa các Pod khi cần. ```yaml # deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: # Deployment name name: webapp-deployment labels: app: webapp spec: # Desired number of replicas replicas: 3 # Selector to identify managed Pods selector: matchLabels: app: webapp # Template for Pod creation template: metadata: labels: app: webapp spec: containers: - name: webapp image: nginx:1.25-alpine ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" # Liveness probe: restarts container on failure livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 10 periodSeconds: 10 # Readiness probe: removes Pod from Service on failure readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5 ``` Deployment tạo một ReplicaSet duy trì 3 Pod giống nhau. Các probe kiểm tra trạng thái của các container và cho phép Kubernetes phản ứng tự động với các sự cố. ```bash # terminal # Create the Deployment kubectl apply -f deployment.yaml # deployment.apps/webapp-deployment created # Verify the Deployment kubectl get deployments # NAME READY UP-TO-DATE AVAILABLE AGE # webapp-deployment 3/3 3 3 1m # List Pods created by the Deployment kubectl get pods -l app=webapp # NAME READY STATUS RESTARTS AGE # webapp-deployment-7d9f8b6c4-abc12 1/1 Running 0 1m # webapp-deployment-7d9f8b6c4-def34 1/1 Running 0 1m # webapp-deployment-7d9f8b6c4-ghi56 1/1 Running 0 1m # Manual scaling kubectl scale deployment webapp-deployment --replicas=5 # Deployment history kubectl rollout history deployment webapp-deployment ``` Xóa một Pod sẽ tự động kích hoạt việc tạo một Pod mới để duy trì số lượng bản sao mong muốn. ## Services: phơi bày mạng Các Pod có địa chỉ IP tạm thời. Services cung cấp một địa chỉ ổn định để truy cập một tập hợp các Pod, với cân bằng tải tích hợp. ```yaml # service.yaml apiVersion: v1 kind: Service metadata: name: webapp-service spec: # Service type: ClusterIP (internal), NodePort, LoadBalancer type: ClusterIP # Selector to identify target Pods selector: app: webapp ports: # Port exposed by the Service - port: 80 # Target container port targetPort: 80 # Protocol (TCP by default) protocol: TCP ``` Service ClusterIP này chỉ có thể truy cập được từ bên trong cluster. Các yêu cầu đến `webapp-service:80` được phân phối giữa các Pod có nhãn `app: webapp`. ```bash # terminal # Create the Service kubectl apply -f service.yaml # service/webapp-service created # List Services kubectl get services # NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE # webapp-service ClusterIP 10.96.123.456 80/TCP 30s # Test from a temporary Pod kubectl run curl-test --rm -it --image=curlimages/curl -- curl webapp-service # Detailed Service description kubectl describe service webapp-service ``` Để phơi bày ứng dụng ra ngoài cluster, cần loại NodePort hoặc LoadBalancer. ```yaml # service-nodeport.yaml apiVersion: v1 kind: Service metadata: name: webapp-nodeport spec: type: NodePort selector: app: webapp ports: - port: 80 targetPort: 80 # Port on each Node (30000-32767) nodePort: 30080 ``` Với minikube, lệnh `minikube service webapp-nodeport` tự động mở trình duyệt tại đúng URL. ## ConfigMaps: cấu hình bên ngoài ConfigMap tách cấu hình khỏi mã nguồn. Các giá trị được tiêm dưới dạng biến môi trường hoặc gắn dưới dạng tệp. ```yaml # configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: webapp-config data: # Simple key-value pairs APP_ENV: "production" LOG_LEVEL: "info" MAX_CONNECTIONS: "100" # Multiline configuration (complete file) nginx.conf: | server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html; } location /health { return 200 'OK'; add_header Content-Type text/plain; } } ``` ConfigMap lưu trữ dữ liệu không nhạy cảm. Đối với các bí mật (mật khẩu, token), Secrets của Kubernetes phù hợp hơn. ```yaml # deployment-with-config.yaml apiVersion: apps/v1 kind: Deployment metadata: name: webapp-configured spec: replicas: 2 selector: matchLabels: app: webapp-configured template: metadata: labels: app: webapp-configured spec: containers: - name: webapp image: nginx:1.25-alpine ports: - containerPort: 80 # Inject environment variables envFrom: - configMapRef: name: webapp-config # Or individual variables env: - name: SPECIFIC_VAR valueFrom: configMapKeyRef: name: webapp-config key: LOG_LEVEL # Mount configuration file volumeMounts: - name: nginx-config mountPath: /etc/nginx/conf.d/default.conf subPath: nginx.conf volumes: - name: nginx-config configMap: name: webapp-config ``` Cấu hình này tiêm các biến môi trường và gắn tệp nginx.conf vào bên trong container. ```bash # terminal # Apply resources kubectl apply -f configmap.yaml kubectl apply -f deployment-with-config.yaml # Verify environment variables kubectl exec deployment/webapp-configured -- printenv | grep APP_ENV # APP_ENV=production # Verify mounted file kubectl exec deployment/webapp-configured -- cat /etc/nginx/conf.d/default.conf ``` > **Cập nhật ConfigMap** > > Việc sửa đổi một ConfigMap không tự động khởi động lại các Pod. Để áp dụng thay đổi, cần khởi động lại thủ công: `kubectl rollout restart deployment webapp-configured`. Các công cụ như Reloader tự động hóa quy trình này. ## Secrets: dữ liệu nhạy cảm Secret lưu trữ thông tin nhạy cảm như mật khẩu, token hoặc khóa SSH. Mặc dù được mã hóa base64, chúng không được mã hóa mặc định khi ở trạng thái nghỉ. ```yaml # secret.yaml apiVersion: v1 kind: Secret metadata: name: webapp-secrets type: Opaque # Values must be base64-encoded data: # echo -n 'admin' | base64 username: YWRtaW4= # echo -n 'supersecretpassword' | base64 password: c3VwZXJzZWNyZXRwYXNzd29yZA== --- # Alternative: stringData accepts plain text values apiVersion: v1 kind: Secret metadata: name: webapp-secrets-plain type: Opaque stringData: username: admin password: supersecretpassword ``` Secret được tiêm theo cùng cách như ConfigMap. ```yaml # deployment-with-secrets.yaml apiVersion: apps/v1 kind: Deployment metadata: name: webapp-secure spec: replicas: 1 selector: matchLabels: app: webapp-secure template: metadata: labels: app: webapp-secure spec: containers: - name: webapp image: nginx:1.25-alpine env: - name: DB_USERNAME valueFrom: secretKeyRef: name: webapp-secrets key: username - name: DB_PASSWORD valueFrom: secretKeyRef: name: webapp-secrets key: password ``` ```bash # terminal # Create the Secret kubectl apply -f secret.yaml # List Secrets (values are not displayed) kubectl get secrets # NAME TYPE DATA AGE # webapp-secrets Opaque 2 10s # Decode a value kubectl get secret webapp-secrets -o jsonpath='{.data.password}' | base64 -d # supersecretpassword ``` ## Namespaces: cô lập logic Namespace phân chia một cluster thành các môi trường ảo cô lập. Sự phân chia này cho phép quản lý nhiều nhóm hoặc môi trường trên cùng một cluster. ```yaml # namespace.yaml apiVersion: v1 kind: Namespace metadata: name: development labels: environment: development --- apiVersion: v1 kind: Namespace metadata: name: staging labels: environment: staging ``` Mỗi tài nguyên có thể được tạo trong một namespace cụ thể. ```bash # terminal # Create namespaces kubectl apply -f namespace.yaml # List namespaces kubectl get namespaces # NAME STATUS AGE # default Active 1d # development Active 10s # staging Active 10s # Create a resource in a specific namespace kubectl apply -f deployment.yaml -n development # List Pods in a namespace kubectl get pods -n development # Change default namespace kubectl config set-context --current --namespace=development ``` Tài nguyên trong các namespace khác nhau được cô lập theo mặc định. Giao tiếp giữa các namespace được thực hiện thông qua DNS nội bộ: `service-name.namespace.svc.cluster.local`. ## Ứng dụng hoàn chỉnh: lắp ráp các tài nguyên Dưới đây là một ứng dụng hoàn chỉnh kết hợp tất cả các khái niệm đã trình bày. ```yaml # complete-app.yaml --- # Dedicated Namespace apiVersion: v1 kind: Namespace metadata: name: myapp --- # ConfigMap for configuration apiVersion: v1 kind: ConfigMap metadata: name: myapp-config namespace: myapp data: APP_NAME: "MyApp" LOG_LEVEL: "info" --- # Secret for sensitive data apiVersion: v1 kind: Secret metadata: name: myapp-secrets namespace: myapp type: Opaque stringData: api-key: "sk-1234567890abcdef" --- # Deployment with 3 replicas apiVersion: apps/v1 kind: Deployment metadata: name: myapp namespace: myapp spec: replicas: 3 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp image: nginx:1.25-alpine ports: - containerPort: 80 envFrom: - configMapRef: name: myapp-config env: - name: API_KEY valueFrom: secretKeyRef: name: myapp-secrets key: api-key resources: requests: memory: "64Mi" cpu: "100m" limits: memory: "128Mi" cpu: "200m" livenessProbe: httpGet: path: / port: 80 initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 5 periodSeconds: 5 --- # Service for internal exposure apiVersion: v1 kind: Service metadata: name: myapp-service namespace: myapp spec: type: ClusterIP selector: app: myapp ports: - port: 80 targetPort: 80 --- # NodePort Service for external access (development) apiVersion: v1 kind: Service metadata: name: myapp-nodeport namespace: myapp spec: type: NodePort selector: app: myapp ports: - port: 80 targetPort: 80 nodePort: 30100 ``` Một tệp duy nhất này triển khai một ứng dụng hoàn chỉnh với cấu hình bên ngoài, các bí mật, tính sẵn sàng cao và phơi bày mạng. ```bash # terminal # Deploy the complete application kubectl apply -f complete-app.yaml # Verify all resources kubectl get all -n myapp # NAME READY STATUS RESTARTS AGE # pod/myapp-7d9f8b6c4-abc12 1/1 Running 0 30s # pod/myapp-7d9f8b6c4-def34 1/1 Running 0 30s # pod/myapp-7d9f8b6c4-ghi56 1/1 Running 0 30s # # NAME TYPE CLUSTER-IP PORT(S) AGE # service/myapp-service ClusterIP 10.96.123.456 80/TCP 30s # service/myapp-nodeport NodePort 10.96.123.789 80:30100/TCP 30s # # NAME READY UP-TO-DATE AVAILABLE AGE # deployment.apps/myapp 3/3 3 3 30s # Access the application with minikube minikube service myapp-nodeport -n myapp ``` ## Cập nhật và rollback Kubernetes hỗ trợ các cập nhật tiến triển và rollback một cách dễ dàng. ```bash # terminal # Update the Deployment image kubectl set image deployment/myapp myapp=nginx:1.26-alpine -n myapp # Track deployment in real-time kubectl rollout status deployment/myapp -n myapp # Waiting for deployment "myapp" rollout to finish: 1 out of 3 new replicas updated # Waiting for deployment "myapp" rollout to finish: 2 out of 3 new replicas updated # deployment "myapp" successfully rolled out # Revision history kubectl rollout history deployment/myapp -n myapp # REVISION CHANGE-CAUSE # 1 # 2 # Rollback to previous revision kubectl rollout undo deployment/myapp -n myapp # Rollback to a specific revision kubectl rollout undo deployment/myapp --to-revision=1 -n myapp ``` Chiến lược cập nhật mặc định (RollingUpdate) thay thế dần các Pod cũ bằng các Pod mới, đảm bảo tính sẵn sàng liên tục. ## Các lệnh kubectl thiết yếu ```bash # terminal # ======================================== # General Information # ======================================== kubectl cluster-info # Cluster info kubectl get nodes -o wide # Nodes with details kubectl api-resources # List resource types # ======================================== # Resource Management # ======================================== kubectl get all # All namespace resources kubectl get pods -A # Pods from all namespaces kubectl get pods -o wide # Pods with IP and Node kubectl get pods -w # Watch mode (real-time) # ======================================== # Inspection and Debugging # ======================================== kubectl describe pod # Full details kubectl logs -f # Streaming logs kubectl logs -c # Specific container logs kubectl exec -it -- /bin/sh # Interactive shell kubectl port-forward 8080:80 # Local tunnel to Pod # ======================================== # Editing and Deletion # ======================================== kubectl edit deployment # Live editing (vi) kubectl delete -f manifest.yaml # Delete via file kubectl delete pod --force # Force deletion ``` ## Kết luận Kubernetes biến đổi việc quản lý các ứng dụng được container hóa bằng cách cung cấp một khuôn khổ khai báo, có khả năng phục hồi và mở rộng. Các khái niệm cơ bản được trình bày ở đây tạo nền tảng cho các triển khai sẵn sàng cho production. ### Danh sách kiểm tra cho lần triển khai Kubernetes đầu tiên - ✅ Cluster cục bộ hoạt động (minikube, kind hoặc k3d) - ✅ kubectl đã cài đặt và cấu hình - ✅ Deployment với các bản sao và health probes - ✅ Service để phơi bày mạng - ✅ ConfigMap cho cấu hình bên ngoài - ✅ Secret cho dữ liệu nhạy cảm - ✅ Namespace để cô lập - ✅ Giới hạn tài nguyên đã được xác định (requests/limits) - ✅ Nắm vững chiến lược cập nhật và rollback Thành thạo Kubernetes mở ra cánh cửa tới các kiến trúc cloud-native có khả năng mở rộng. Các bước tiếp theo bao gồm khám phá Ingress Controllers cho định tuyến HTTP, PersistentVolumes cho lưu trữ và Helm cho quản lý gói. Kubernetes trở thành một tài sản quan trọng trong các cuộc phỏng vấn DevOps và SRE. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/vi/blog/devops/kubernetes-deploying-first-application