GitOps với ArgoCD: Hướng dẫn toàn tập từ cơ bản đến nâng cao
Tìm hiểu GitOps là gì và cách triển khai ArgoCD trên Kubernetes. Hướng dẫn chi tiết cài đặt, cấu hình Application, App of Apps, RBAC và Monitoring.
Mục lục
- GitOps là gì?
- Kiến trúc của ArgoCD
- Hướng dẫn cài đặt ArgoCD
- Tìm hiểu về Application CRDs
- Mô hình App of Apps (Ứng dụng của các ứng dụng)
- Chiến lược Đồng bộ (Sync strategies)
- Custom Health checks (Kiểm tra sức khỏe hệ thống)
- Các mô hình Rollback
- Quản lý đa cụm (Multi-cluster) với ApplicationSets
- Tích hợp Kustomize và Helm
- Phân quyền RBAC và SSO
- Thiết lập Notifications
- Theo dõi hệ thống (Monitoring) nội bộ của ArgoCD
- Lời kết
GitOps là gì?
GitOps là một mô hình vận hành trong đó trạng thái mong muốn của hạ tầng và ứng dụng được khai báo và lưu trữ trong Git. Một bộ điều khiển (controller) chạy trong cluster của bạn sẽ theo dõi Git repository và đảm bảo trạng thái thực tế khớp với trạng thái đã khai báo. Nếu có bất kỳ sự sai lệch (drift) nào, controller sẽ tự động điều chỉnh lại cho đúng.
Các nguyên tắc cốt lõi của GitOps bao gồm:
- Cấu hình khai báo (Declarative configuration): Mọi thứ được mô tả dưới dạng YAML hoặc JSON manifests trong Git. Không có các script thao tác thủ công (imperative scripts) hay các bước làm bằng tay.
- Git là nguồn chân lý duy nhất (Single source of truth): Repository Git là nơi duy nhất mọi thay đổi được thực hiện. Những gì có trong Git chính là những gì đang chạy trong cluster.
- Đồng bộ hóa dựa trên cơ chế Pull (Pull-based reconciliation): Thay vì hệ thống CI đẩy (push) thẳng vào cluster, một controller bên trong cluster sẽ tự động kéo (pull) trạng thái mong muốn từ Git. Cách này bảo mật hơn vì thông tin xác thực của cluster không bao giờ lọt ra ngoài.
- Đồng bộ hóa liên tục (Continuous reconciliation): Controller không chỉ áp dụng thay đổi một lần. Nó liên tục so sánh trạng thái thực tế với trạng thái mong muốn và tự động khắc phục mọi sự sai lệch.
Điều này hoàn toàn khác biệt với mô hình CI/CD truyền thống (dựa trên Push), nơi pipeline chạy kubectl apply sau khi build. Với Push-based CD, nếu ai đó thay đổi cấu hình trực tiếp trên cluster, hệ thống CI của bạn sẽ không hề hay biết. Với GitOps, controller phát hiện sự sai lệch và sửa chữa nó tự động.
# Push-based CI/CD (truyền thống):
Developer → Git push → CI builds → CI chạy kubectl apply → Cluster
(CI cần có chứng chỉ của cluster)
(Sai lệch cấu hình không bị phát hiện)
# Pull-based GitOps:
Developer → Git push → Controller phát hiện thay đổi → Controller apply → Cluster
(Controller nằm trong cluster, liên tục theo dõi Git)
(Sai lệch được phát hiện và khắc phục tự động)
Kiến trúc của ArgoCD
ArgoCD là một GitOps controller phổ biến nhất dành cho Kubernetes. Đây là một dự án graduated của CNCF với một kiến trúc được thiết kế rất chặt chẽ:
- API Server: Máy chủ gRPC/REST cung cấp sức mạnh cho giao diện web (UI), CLI và các API tích hợp bên ngoài. Nó xử lý việc xác thực, phân quyền (RBAC) và cung cấp trạng thái của ứng dụng.
- Repository Server: Thực hiện clone các Git repositories và tạo ra các Kubernetes manifests. Nó hỗ trợ YAML thuần túy, Kustomize, Helm, Jsonnet và các plugin tùy chỉnh.
- Application Controller: Bộ não của ArgoCD. Nó theo dõi các tài nguyên Application, so sánh trạng thái mong muốn (từ Git) với trạng thái hiện tại (từ cluster) và thực hiện các thao tác đồng bộ (sync) khi có sự khác biệt.
- Redis: Lớp bộ nhớ đệm (caching layer) dành cho repository server và application controller.
- ApplicationSet Controller: Quản lý tài nguyên ApplicationSet - cho phép tạo và quản lý nhiều Application từ một định nghĩa duy nhất.
Vòng lặp reconciliation của ArgoCD (mặc định chạy mỗi 3 phút):
- Application Controller đọc Application CRD.
- Yêu cầu Repo Server lấy và render các manifests từ Git.
- Controller so sánh manifests đã render với trạng thái thực tế trên cluster.
- Nếu có sự khác biệt:
- Nếu bật auto-sync: Controller tự động áp dụng các thay đổi.
- Nếu không bật auto-sync: Controller đánh dấu ứng dụng là OutOfSync.
- Controller cập nhật trạng thái Application, chu kỳ lặp lại.
Hướng dẫn cài đặt ArgoCD
Cách cài đặt được khuyến nghị là sử dụng Helm. Bạn hãy tạo namespace và cài đặt bằng các lệnh sau:
kubectl create namespace argocd
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
Dưới đây là một file cấu hình values.yaml tham khảo cho môi trường production:
# argocd-values.yaml
configs:
params:
server.insecure: true
timeout.reconciliation: 180s
cm:
statusbadge.enabled: "true"
kustomize.buildOptions: "--enable-helm"
server:
replicas: 2
ingress:
enabled: true
ingressClassName: nginx
hostname: argocd.example.com
tls: true
controller:
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
memory: 1Gi
repoServer:
replicas: 2
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 512Mi
Chạy lệnh cài đặt:
helm install argocd argo/argo-cd \
--namespace argocd \
--values argocd-values.yaml \
--wait
# Lấy mật khẩu admin ban đầu
kubectl -n argocd get secret argocd-initial-admin-secret \
-o jsonpath="{.data.password}" | base64 -d
# Đăng nhập và đổi mật khẩu
argocd login argocd.example.com --username admin --password <your-password>
argocd account update-password
Tìm hiểu về Application CRDs
Application CRD là thành phần cơ bản nhất trong ArgoCD. Nó xác định những gì cần triển khai, ở đâu, và làm thế nào để giữ cho hệ thống luôn đồng bộ:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-app
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/techcoban/my-app-manifests
targetRevision: main
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true # Xóa các resource không còn tồn tại trong Git
selfHeal: true # Tự động revert các thay đổi thủ công trên cluster
syncOptions:
- CreateNamespace=true
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # Bỏ qua nếu có HPA đang quản lý số lượng replicas
source: Nơi chứa các manifests bao gồmrepoURL,targetRevision(nhánh/tag), vàpath(thư mục trong repo).destination: Nơi sẽ deploy lên vớiserverlà endpoint của Kubernetes API, vànamespacelà đích đến.syncPolicy: Cách cấu hình đồng bộ.automatedsẽ bật tính năng tự động đồng bộ (auto-sync),pruneđể dọn dẹp các tài nguyên đã bị xóa trên Git,selfHealhoàn tác các thay đổi thủ công.ignoreDifferences: Các trường cần bỏ qua khi so sánh (rất hữu ích với các trường được set tự động bởi cluster).
Mô hình App of Apps (Ứng dụng của các ứng dụng)
Khi bạn có nhiều ứng dụng, việc quản lý từng resource Application riêng lẻ sẽ trở nên tẻ nhạt và khó bảo trì. Mô hình App of Apps tạo ra một Application “cha” (parent) để quản lý các manifests của các Application “con” (child).
Cấu trúc Repository ví dụ:
gitops-repo/
├── apps/ # Ứng dụng cha sẽ trỏ về đây
│ ├── my-app.yaml # Các file manifests của ứng dụng con
│ ├── monitoring.yaml
│ ├── cert-manager.yaml
│ └── ingress-nginx.yaml
├── my-app/
│ ├── base/
│ └── overlays/
│ ├── staging/
│ └── production/
└── monitoring/
Application “cha” sẽ có cấu trúc như sau:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: app-of-apps
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/techcoban/gitops-repo
targetRevision: main
path: apps
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
Các ứng dụng con sẽ sử dụng annotations là sync-wave để kiểm soát thứ tự deploy. Các thành phần hạ tầng (infrastructure) thường nằm ở wave 0, còn các workload của ứng dụng nằm ở wave 2:
# apps/cert-manager.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cert-manager
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "0" # Triển khai phần hạ tầng trước
spec:
project: default
source:
repoURL: https://charts.jetstack.io
chart: cert-manager
targetRevision: v1.16.3
helm:
releaseName: cert-manager
values: |
installCRDs: true
destination:
server: https://kubernetes.default.svc
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
Chiến lược Đồng bộ (Sync strategies)
ArgoCD cung cấp quyền kiểm soát cực kỳ chi tiết về cách thức và thời điểm đồng bộ hóa (sync) diễn ra. Một mô hình phổ biến là dùng auto-sync cho môi trường staging và manual (thủ công) cho môi trường production:
# Auto-sync: Các thay đổi được áp dụng tự động
syncPolicy:
automated:
prune: true # Xóa các resource không còn trong Git
selfHeal: true # Hoàn tác các thay đổi bằng tay trên cluster
# Manual sync: Chỉ cần bỏ đi phần "automated"
syncPolicy:
syncOptions:
- CreateNamespace=true
Các chính sách xử lý retry khi gặp lỗi mạng tạm thời:
syncPolicy:
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
Các cửa sổ đồng bộ (Sync windows) hạn chế thời gian ArgoCD có thể thực hiện sync (rất hữu ích để “đóng băng” hệ thống vào ban đêm hoặc các dịp lễ Tết):
# Định nghĩa trong cấu hình AppProject spec
spec:
syncWindows:
- kind: allow
schedule: "0 9 * * 1-5" # T2 - T6 vào 9 giờ sáng
duration: 8h
applications: ["*"]
- kind: deny
schedule: "0 0 20 12 *" # Block trong dịp lễ
duration: 336h
applications: ["*"]
clusters: ["production"]
Custom Health checks (Kiểm tra sức khỏe hệ thống)
ArgoCD có sẵn các tính năng health check cho các tài nguyên mặc định của Kubernetes. Đối với các tài nguyên tùy chỉnh (Custom Resources - CRDs), bạn có thể viết các script health check bằng ngôn ngữ Lua.
Các trạng thái cơ bản bao gồm:
- Healthy: Tài nguyên đang hoạt động bình thường.
- Progressing: Chưa sẵn sàng nhưng đang trong tiến trình xử lý.
- Degraded: Tài nguyên gặp lỗi.
- Suspended: Tài nguyên đang bị tạm dừng.
- Missing: Tài nguyên không tồn tại.
Ví dụ Custom health check cho cert-manager Certificate (cấu hình trong argocd-cm ConfigMap):
resource.customizations.health.cert-manager.io_Certificate: |
hs = {}
if obj.status ~= nil then
if obj.status.conditions ~= nil then
for i, condition in ipairs(obj.status.conditions) do
if condition.type == "Ready" and condition.status == "False" then
hs.status = "Degraded"
hs.message = condition.message
return hs
end
if condition.type == "Ready" and condition.status == "True" then
hs.status = "Healthy"
hs.message = condition.message
return hs
end
end
end
end
hs.status = "Progressing"
hs.message = "Waiting for certificate"
return hs
Các mô hình Rollback
Một trong những ưu điểm lớn nhất của GitOps là việc rollback đơn giản chỉ là thực hiện một thao tác git revert.
1. Cách chuẩn GitOps: revert commit trên Git
git revert HEAD --no-edit
git push
# ArgoCD sẽ tự động phát hiện sự thay đổi này và đồng bộ lại trạng thái cũ
2. Rollback sử dụng tính năng History của ArgoCD (Trường hợp khẩn cấp)
argocd app history my-app
argocd app rollback my-app 2
# LƯU Ý: Lệnh này KHÔNG revert code trên Git, nên nếu auto-sync đang bật nó sẽ tự động ghi đè lại.
# Bạn cần vô hiệu hóa tính năng auto-sync trước hoặc là đồng thời revert luôn trên Git.
Quản lý đa cụm (Multi-cluster) với ApplicationSets
ApplicationSets giúp bạn tạo ra nhiều Applications từ một template mẫu duy nhất bằng cách sử dụng các Generators (Bộ tạo). Thay vì phải tạo thủ công một Application cho từng cluster riêng biệt, bạn định nghĩa một template chung và một generator để sản sinh ra các biến thể tương ứng.
List generator: Cung cấp danh sách parameters cụ thể cho từng môi trường:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: my-app
namespace: argocd
spec:
generators:
- list:
elements:
- cluster: staging
url: https://staging-api.example.com
- cluster: production
url: https://production-api.example.com
template:
metadata:
name: "my-app-{{cluster}}"
spec:
project: default
source:
repoURL: https://github.com/techcoban/gitops-repo
targetRevision: main
path: "my-app/overlays/{{cluster}}"
destination:
server: "{{url}}"
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true
Cluster generator: Tự động tạo ra các Applications cho mọi cluster thỏa mãn điều kiện matchLabels:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: monitoring-stack
namespace: argocd
spec:
generators:
- clusters:
selector:
matchLabels:
environment: production
template:
metadata:
name: "monitoring-{{name}}"
spec:
project: monitoring
source:
repoURL: https://github.com/techcoban/gitops-repo
targetRevision: main
path: monitoring
destination:
server: "{{server}}"
namespace: monitoring
Git generator: Tạo Applications dựa trên cấu trúc thư mục (directory structure) hoặc theo file cấu hình:
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: team-apps
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/techcoban/gitops-repo
revision: main
directories:
- path: "teams/*/apps/*"
template:
metadata:
name: "{{path.basename}}"
spec:
project: default
source:
repoURL: https://github.com/techcoban/gitops-repo
targetRevision: main
path: "{{path}}"
destination:
server: https://kubernetes.default.svc
namespace: "{{path.basename}}"
Tích hợp Kustomize và Helm
ArgoCD hỗ trợ gốc (native support) cho cả Kustomize và Helm. Nó sẽ tự động biên dịch (render) các manifests tại thời điểm sync, vì vậy bạn hoàn toàn không cần phải chạy lại các công cụ này trong các pipeline CI của mình nữa.
Đối với Kustomize, bạn chỉ cần trỏ thư mục source trong Application đến thư mục overlay mong muốn:
# overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- target:
kind: Deployment
name: my-app
patch: |
- op: replace
path: /spec/replicas
value: 3
images:
- name: techcoban/my-app
newTag: v1.2.3
Đối với các Helm charts từ một Helm chart repository:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: prometheus
namespace: argocd
spec:
source:
repoURL: https://prometheus-community.github.io/helm-charts
chart: kube-prometheus-stack
targetRevision: 67.9.0
helm:
releaseName: prometheus
values: |
prometheus:
prometheusSpec:
retention: 30d
storageSpec:
volumeClaimTemplate:
spec:
resources:
requests:
storage: 50Gi
grafana:
enabled: true
destination:
server: https://kubernetes.default.svc
namespace: monitoring
syncPolicy:
syncOptions:
- ServerSideApply=true
Phân quyền RBAC và SSO
Projects là cơ chế quan trọng nhất để giới hạn các quyền truy cập trong ArgoCD. Mỗi project sẽ xác định chính xác repository, cluster và namespace nào mà một ứng dụng được phép sử dụng:
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: payments-team
namespace: argocd
spec:
description: "Dự án của team Payments"
sourceRepos:
- "https://github.com/techcoban/payments-*"
destinations:
- server: https://kubernetes.default.svc
namespace: "payments-*"
namespaceResourceWhitelist:
- group: "apps"
kind: Deployment
- group: ""
kind: Service
- group: ""
kind: ConfigMap
- group: ""
kind: Secret
roles:
- name: developer
policies:
- p, proj:payments-team:developer, applications, get, payments-team/*, allow
- p, proj:payments-team:developer, applications, sync, payments-team/*, allow
groups:
- payments-developers
- name: admin
policies:
- p, proj:payments-team:admin, applications, *, payments-team/*, allow
groups:
- payments-admins
Để thiết lập SSO với OIDC:
# Trong file argocd-cm ConfigMap
oidc.config: |
name: Keycloak
issuer: https://keycloak.example.com/realms/engineering
clientID: argocd
clientSecret: $oidc.keycloak.clientSecret
requestedScopes: ["openid", "profile", "email", "groups"]
# Trong file argocd-rbac-cm ConfigMap
policy.default: role:readonly
policy.csv: |
g, platform-admins, role:admin
g, payments-developers, proj:payments-team:developer
p, role:readonly, applications, get, */*, allow
Thiết lập Notifications
ArgoCD Notifications gửi các thông báo hoặc cảnh báo cho hệ thống dựa trên các sự kiện sync. Tính năng này đã được tích hợp sẵn mặc định trong bản Helm chart kể từ ArgoCD bản 2.6 trở lên.
# argocd-notifications-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
service.slack: |
token: $slack-token
template.app-sync-succeeded: |
slack:
attachments: |
[{"color": "#18be52", "title": "{{.app.metadata.name}} đã sync thành công!",
"fields": [
{"title": "Revision", "value": "{{.app.status.sync.revision}}", "short": true},
{"title": "Namespace", "value": "{{.app.spec.destination.namespace}}", "short": true}
]}]
template.app-sync-failed: |
slack:
attachments: |
[{"color": "#E96D76", "title": "{{.app.metadata.name}} sync THẤT BẠI",
"fields": [
{"title": "Error", "value": "{{range .app.status.conditions}}{{.message}}{{end}}"}
]}]
trigger.on-sync-succeeded: |
- when: app.status.operationState.phase in ['Succeeded']
send: [app-sync-succeeded]
trigger.on-sync-failed: |
- when: app.status.operationState.phase in ['Error', 'Failed']
send: [app-sync-failed]
Để đăng ký nhận thông báo cụ thể cho một ứng dụng, bạn thêm cấu hình annotations:
metadata:
annotations:
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: deployments
notifications.argoproj.io/subscribe.on-sync-failed.slack: deployments-alerts
Theo dõi hệ thống (Monitoring) nội bộ của ArgoCD
ArgoCD public sẵn các số liệu Prometheus (Prometheus metrics) ra ngoài (out-of-the-box). Dưới đây là các metrics quan trọng nhất mà bạn nên theo dõi:
argocd_app_info: Cung cấp trạng thái sync (đồng bộ) và health (sức khỏe) cho từng ứng dụng.argocd_app_sync_total: Tổng số lượt sync operations.argocd_app_reconcile_bucket: Theo dõi thời gian xử lý quá trình reconciliation.argocd_git_request_total: Tổng số lượt Git requests (Nếu có lỗi nghĩa là ArgoCD không truy cập được vào Git repository của bạn).argocd_cluster_api_resource_objects: Theo dõi số object API trên mỗi cluster (Hữu ích khi bạn lên kế hoạch sử dụng memory).
Ví dụ tạo một file rule cảnh báo dựa trên metrics:
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: argocd-alerts
namespace: monitoring
spec:
groups:
- name: argocd.rules
rules:
- alert: ArgoCDAppOutOfSync
expr: argocd_app_info{sync_status="OutOfSync"} == 1
for: 30m
labels:
severity: warning
annotations:
summary: "ArgoCD app {{ $labels.name }} bị mất đồng bộ (out of sync) quá 30 phút!"
- alert: ArgoCDAppUnhealthy
expr: argocd_app_info{health_status!~"Healthy|Progressing"} == 1
for: 15m
labels:
severity: critical
annotations:
summary: "ArgoCD app {{ $labels.name }} đang ở trạng thái {{ $labels.health_status }}"
- alert: ArgoCDSyncFailing
expr: increase(argocd_app_sync_total{phase!="Succeeded"}[1h]) > 3
labels:
severity: critical
annotations:
summary: "Có nhiều hơn 3 lượt sync thất bại trong vòng 1 giờ cho ứng dụng {{ $labels.name }}"
- alert: ArgoCDGitFetchErrors
expr: increase(argocd_git_request_total{request_type="fetch", result="error"}[10m]) > 5
labels:
severity: warning
annotations:
summary: "ArgoCD không thể fetch source code từ các Git repositories"
Bạn cũng đừng quên thiết lập cho Prometheus thu thập (scrape) metrics bằng một ServiceMonitor:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: argocd-metrics
namespace: monitoring
spec:
selector:
matchLabels:
app.kubernetes.io/part-of: argocd
namespaceSelector:
matchNames: [argocd]
endpoints:
- port: metrics
interval: 30s
Lời kết
GitOps với ArgoCD cung cấp cho bạn một luồng quy trình triển khai có khả năng kiểm soát, có thể lặp lại chính xác và hoàn toàn có thể tự phục hồi. Bằng cách coi Git là Nguồn chân lý duy nhất và cho phép controller của hệ thống tự động quá trình reconciliation, bạn đã loại bỏ đi toàn bộ các nguy cơ liên quan đến việc sai lệch cấu hình và các bước deploy thủ công rườm rà.
Sự kết hợp hoàn hảo giữa Application CRDs, mô hình App of Apps, ApplicationSets và khả năng phân quyền RBAC tuyệt vời của ArgoCD tạo nên một nền tảng vững chắc để giúp bạn quản lý tất cả, từ việc làm chủ một cụm (cluster) Kubernetes đơn lẻ cho đến cả một hạm đội các clusters trong nhiều môi trường khác nhau. Hy vọng bài viết này mang lại cho bạn những kiến thức bổ ích. Chúc các bạn thực hành thành công!
Bình luận
Bài viết liên quan
Kubernetes là gì? Hệ sinh thái quản lý Container mạnh mẽ nhất
Tại sao Kubernetes lại trở thành tiêu chuẩn công nghiệp (de-facto) cho việc điều phối Container? Cùng tìm hiểu K8s cơ bản.
Hướng Dẫn Triển Khai Cài Đặt RKE2 Chuẩn Production On-Premises
Bài viết hướng dẫn chi tiết từng bước cách cài đặt và cấu hình cụm Kubernetes với RKE2 (Rancher Kubernetes Engine 2) chuẩn Production, đảm bảo High Availability (HA) và an toàn bảo mật.
Triển khai Zero-port với Cloudflare Tunnels trên Kubernetes
Khám phá cách triển khai ứng dụng Kubernetes an toàn ra Internet mà không cần mở Port (Zero-trust) bằng Cloudflare Tunnel, thay thế cho Ingress Controller và Load Balancer truyền thống.