Khi triển khai K8S on premise, để các ứng dụng có thể truy cập từ bên ngoài, một cách phổ biến là expose service bằng NodePort type, cho phép bạn truy cập qua địa chỉ IP của node và một port trong khoảng từ 30000 đến 32767.
Tuy nhiên, NodePort có một số hạn chế như số lượng port bị giới hạn, gây khó khăn khi có nhiều service; đồng thời, đường dẫn truy cập (ví dụ: http://node_ip:port) trông không chuyên nghiệp so với tên miền chuẩn như git.example.com.
Ngoài ra, khi sử dụng NodePort type thì entry point sẽ là địa chỉ IP của node. Trong trường hợp có nhiều node, để đảm bảo các request được chia đều tới các node, bạn cần cấu hình load balancing để thực hiện nhiệm vụ đó.
Thông thường ta sẽ sử dụng HA Proxy hoặc Nginx để cấu hình, Trong bài hướng dẫn này, chúng ta sẽ sử dụng HAProxy trên một server riêng biệt để làm load balancer, kết hợp với Nginx Ingress Controller để quản lý định tuyến request trong cluster và Cert-Manager để tự động hóa cấu hình SSL. Hướng dẫn cấu hình High Availability (HA) cho HAProxy sẽ được trình bày trong một bài viết riêng.
Yêu cầu
- Đã cài đặt K8S Cluster.
- Một server riêng để cài đặt HA Proxy (trong bài thực hành mình đang sử dụng Ubuntu 22.04, 1GB ram, 1 cpu, 10GB SSD)
- Có hiểu biết về Load Balance HA Proxy, Ingress, Nginx và một số workload trong K8S.
Trong phần hướng dẫn này, mình đã cài đặt K8S Cluster với 2 worker với IP lần lượt là 100.64.0.1, 100.64.0.2 và load balancer server với IP 100.64.0.4.
Mô hình
Mô tả các thành phần:
Client: người dùng gửi request https đến hệ thống.
Load Balancer (HAProxy):
- Nhận traffic từ client qua HTTPS.
- Không giải mã SSL → chỉ chuyển tiếp request đến Ingress Controller, sử dụng mode tcp trong HAProxy → TCP passthrough.
- Client → HA Proxy: HTTPS, HA Proxy → Ingress Controller: HTTPS (giữ nguyên vẹn gói tin).
Ingress Controller Service:
- Là một pod trong cluster.
- Xử lý SSL Termination: nhận HTTPS từ LB → giải mã ra HTTP.
- Đọc domain/path trong request → áp dụng Ingress Rules để route.
Ingress Rule:
Thực hiện routing theo quy định:
- Theo domain (ví dụ: hello.manhtx.click)
- Theo path (ví dụ: /login, /shop)
Cert Manager:
- Tự động tạo SSL cert (qua Let’s Encrypt, Self Sign, …).
- Gắn cert vào đúng Ingress (bằng cách quản lý Secret).
- Gia hạn chứng chỉ tự động.
Service:
- Là Kubernetes Service route request đến đúng Pod backend (Deployment).
- Mỗi Service có thể đại diện cho một microservice khác nhau.
Deployment:
- Các Pod chạy ứng dụng backend thật sự.
- Mỗi Deployment có nhiều Pod, quản lý tự động bởi Kubernetes.
Luồng xử lý yêu cầu
- Client gửi request:
hello.manhtx.click
- HAProxy Load Balancer:
- Nhận gói HTTPS.
- Dùng mode tcp, truyền thẳng đến Ingress Controller (không giải mã).
- Ingress Controller Service:
- Terminate SSL (giải mã HTTPS → HTTP).
- Đọc domain hello.manhtx.click, path /cart.
- So khớp với Ingress Rule.
- Ingress Rule chọn đúng Service (ví dụ: service-cart).
- Service route đến đúng Pod trong Deployment để xử lý.
Hướng dẫn cấu hình
Cài đặt Nginx Ingress:
Trong hướng dẫn này mình sẽ sử dụng helm để cài nginx ingress lên k8s cluster, ngoài ra bạn có thể cài trực tiếp bằng file manifest (phần cài helm các bạn có thể xem các bài hướng dẫn khác nhé):
- Tạo namespace để dễ dàng hơn trong việc quản lý các resource
kubectl create namespace ingress-nginx
- Thêm repo của ingress-nginx
helm repo add ingress-nginx <https://kubernetes.github.io/ingress-nginx>
helm repo update
- Tải về chart và file cấu hình
values.yaml
helm pull ingress-nginx/ingress-nginx --untar
cd ingress-nginx
Sau đó bạn sẽ thấy thư mục có sẵn file values.yaml
. Bạn nên copy ra 1 file mới và update value trong file đó.
- Chỉnh sửa file
values.new.yaml
copy từ values.yaml
nano values.new.yaml
- Tìm đến controller block, do cluster đang cài on premise, cập nhật lại service type từ LoadBalancer (chỉ support cho cloud provider) sang NodePort để có thể truy cập vào nginx ingress qua IP của node
- Chỉ định port cho cần expose với http chạy port 31080 và https chạy port 31443
- Chạy lệnh sau để cài ingress nginx lên cluster với file values mới
helm install ngix-ingress ingress-nginx -f values.new.yaml -n ingress-nginx
- Sau khi chạy xong, kiểm tra các resource được tạo ra.
kubectl get all -n ingress-nginx
Ta thấy đã có service type là NodePort đang expose 2 port 31080 với 31443 ra ngoài.
Cài đặt Cert Manager:
Ta cũng dùng helm để cài đặt
- Thêm Helm repo của Jetstack
helm repo add jetstack <https://charts.jetstack.io>
helm repo update
- Cài đặt cert manager
helm install cert-manager jetstack/cert-manager \\
--namespace cert-manager \\
--create-namespace \\
--version v1.14.5 \\
--set installCRDs=true
- Kiểm tra các resouce được khởi tạo
kubectl get all -n cert-manager
Cài đặt HA Proxy:
- Cập nhật hệ thống
sudo apt update
sudo apt upgrade -y
- Cài đặt HA Proxy
sudo apt install haproxy -y
- Kiểm tra phiên bản
haproxy -v
Hiện tại mình đang sử dụng phiên bản 2.4.24-0ubuntu0.22.04.2
- Cấu hình HA Proxy
sudo nano /etc/haproxy/haproxy.cfg
- Sử dụng cấu hình sau
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
stats timeout 30s
user haproxy
group haproxy
daemon
maxconn 20000 # Điều chỉnh nếu cần
defaults
log global
mode tcp # Quan trọng: Sử dụng mode tcp cho NodePort
option tcplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend http_frontend
bind 100.64.0.4:80 # Lắng nghe trên IP thật của server LB và port 80
mode tcp
default_backend k8s_workers_http
frontend https_frontend
bind 100.64.0.4:443 # Lắng nghe trên IP thật của server LB và port 443
mode tcp
default_backend k8s_workers_https
backend k8s_workers_http
mode tcp
balance roundrobin # Thuật toán cân bằng tải
# Server worker 1, trỏ đến NodePort HTTP
server worker1 100.64.0.1:31080 check port 31080 inter 2000 rise 2 fall 3 send-proxy-v2
# Server worker 2, trỏ đến NodePort HTTP
server worker2 100.64.0.2:31080 check port 31080 inter 2000 rise 2 fall 3 send-proxy-v2
backend k8s_workers_https
mode tcp
balance roundrobin
# Server worker 1, trỏ đến NodePort HTTPS
server worker1 100.64.0.1:31443 check port 31443 inter 2000 rise 2 fall 3 send-proxy-v2
# Server worker 2, trỏ đến NodePort HTTPS
server worker2 100.64.0.2:31443 check port 31443 inter 2000 rise 2 fall 3 send-proxy-v2
Hãy thay thế IP và port đúng với cấu hình của bạn, nếu server của bạn có cấu hình firewall thì hãy mở các port này ra tránh trường hợp không kết nối đươc nhé.
- Kiểm tra cấu hình
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
- Khởi động lại dịch vụ HAProxy
sudo systemctl restart haproxy
sudo systemctl enable haproxy
- Kiểm tra trạng thái
sudo systemctl status haproxy
- Kiểm tra xem HA Proxy đã chuyển tiếp request với nginx ingress chưa, nếu trỏ về trang 404 Not Found của Nginx thì chính xác
curl -L <http://100.64.0.4/>
- Cũng có thể kiểm tra trên trình duyệt
Như thế ta đã cấu hình xong HA Proxy để forward request về các worker node.
Triển khai ứng dụng
Sau khi đã cấu hình xong, ta sẽ triển khai demo 1 ứng dụng nhỏ để xem các cài đặt của chúng ta hoạt động đúng không.
- Đầu tiên, tạo 1 ClusterIssuer sử dụng self-signed certificate (do đang dựng lab ở local) với file selfsigned-issuer.yaml có nội dung sau:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: selfsigned-cluster-issuer
spec:
selfSigned: {}
- Apply file
kubectl apply -f selfsigned-issuer.yaml
- Kiểm tra ClusterIssuer được tạo chưa
kubectl get clusterissuer
Nếu cột READY
là True
thì issuer đã sẵn sàng dùng.
- Tạo file demo_app.yaml với nội dung sau
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-deployment
spec:
replicas: 2
selector:
matchLabels:
app: hello-app
template:
metadata:
labels:
app: hello-app
spec:
containers:
- name: hello-container
image: nginxdemos/hello
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
selector:
app: hello-app
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello-ingress
annotations:
cert-manager.io/cluster-issuer: selfsigned-cluster-issuer
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls: # lấy https
- hosts:
- hello.manhtx.click
secretName: domain-tls-self-signed-attacks-hello.manhtx.click
rules:
- host: hello.manhtx.click
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello-service
port:
number: 80
- Apply file
kubectl create ns demo
kubectl apply -f demo_app.yaml -n demo
- Cấu hình domain để trỏ về load balancer IP hoặc bạn có thể config local dns trong /etc/hosts.
- Sau khi cấu hình xong, ta có thể truy cập bằng domain đã config trước đó.
Ta có thể truy cập vào trang web với https, nó sẽ hiện cảnh báo do mình đang sử dụng self sign cert. HA Proxy sẽ tự động điều hướng các request lần lượt vào các worker node theo đúng thuật toán roundrobin được cấu hình trong trước đó.