Sunday, January 19, 2025

Làm sao để cân bằng tải Kubernetes Cluster với NGINX Ingress

-

Thế nào là Ingress?

Ingress là resource Kubernetes cho phép định tuyến cấu hình Loadbalancer HTTP cho các ứng dụng chạy trên Kubernetes, được đại diện bởi một hoặc nhiều Dịch vụ. Một bộ loadbalancer như vậy là cần thiết để cung cấp các ứng dụng đó cho các máy khách như browser trình duyệt chúng ta truy cập bên ngoài cụm Kubernetes.

Thế nào là Ingress Controller?

Ingress Controller là một ứng dụng chạy trong một cluster và sử dụng cấu hình LoadBalancer HTTP theo tài nguyên Ingress. Loadbalancer này có thể là chạy bằng phần mềm trong cluster, Loadbalancer phần cứng hoặc là Loadbalancer dịch vụ cloud bên ngoài. Với mỗi LoadBalancer khác nhau đòi hỏi phải thực hiện Ingress Controller khác nhau.

Sơ đồ triển khai như sau:

Trong trường hợp này, Ingress Controller được triển khai theo dạng phần mềm.

Bạn hãy xem lại bài “Cách triển khai…” trước khi xem tiếp bài này nhé.

Hãy vào/etc/haproxy/haproxy.cfg và thêm 2 khối như sau

Khối đầu tiên có nội dung như dưới, khối này sẽ chuyển các traffic chạy port 30100 ở trong service của NGINX Ingress. Port 30100 sẽ được Forwarding ra ngoài từ port 80 bằng Service Type là NodePort

frontend http_frontend
    bind *:80
    mode tcp
    option tcplog
    default_backend http_backend

backend http_backend
    mode tcp
    balance roundrobin
    server k8s-worker1 192.168.13.214:30100 check
    server k8s-worker2 192.168.13.215:30100 check
    server k8s-worker3 192.168.13.216:30100 check

Khối thứ 2 có nội dung như dưới, khối này sẽ chuyển các traffic chạy port 30101 ở trong service của NGINX Ingress. Port 30101 sẽ được Forwarding ra ngoài từ port 443 bằng Service Type là NodePort

frontend https_frontend
    bind *:443
    mode tcp
    option tcplog
    default_backend https_backend

backend https_backend
    mode tcp
    balance roundrobin
    server k8s-worker1 192.168.13.214:30101 check
    server k8s-worker2 192.168.13.215:30101 check
    server k8s-worker3 192.168.13.216:30101 check

Như vậy file /etc/haproxy/haproxy.cfg sẽ có nội dung như dưới

frontend stats
    bind *:8080
    mode http
    stats enable
    stats uri /stats
    stats refresh 10s
    stats admin if LOCALHOST

frontend fe-apiserver
    bind 0.0.0.0:6443
    mode tcp
    option tcplog
    default_backend be-apiserver

backend be-apiserver
    mode tcp
    option tcplog
    option tcp-check
    balance roundrobin
    default-server inter 10s downinter 5s rise 2 fall 2 slowstart 60s maxconn 250 maxqueue 256 weight 100
    server k8s-master1 192.168.13.211:6443 check
    server k8s-master2 192.168.13.212:6443 check
    server k8s-master3 192.168.13.213:6443 check

frontend http_frontend
    bind *:80
    mode tcp
    option tcplog
    default_backend http_backend

backend http_backend
    mode tcp
    balance roundrobin
    server k8s-worker1 192.168.13.214:30100 check
    server k8s-worker2 192.168.13.215:30100 check
    server k8s-worker3 192.168.13.216:30100 check

frontend https_frontend
    bind *:443
    mode tcp
    option tcplog
    default_backend https_backend

backend https_backend
    mode tcp
    balance roundrobin
    server k8s-worker1 192.168.13.214:30101 check
    server k8s-worker2 192.168.13.215:30101 check
    server k8s-worker3 192.168.13.216:30101 check

Sau khi config xong bạn hãy khởi động lại dịch vụ Haproxy

$ sudo systemctl restart haproxy

Sau khi khởi động xong Haproxy, hãy vào url http://<vip_ipaddr_loadbalancer>:8080/stats. Ta sẽ thấy xuất hiện 2 khối mới là http_backend, https_backend nhưng chúng đang trạng thái down vì lý do NGINX Ingress chưa được triển khai nên các worker node chưa listen port 30100 và 30101.

Nếu cụm Kubernetes của bạn chưa có helm thì hãy cài nó vào bằng lệnh dưới

$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
$ chmod 700 get_helm.sh && ./get_helm.sh

Để chắc chắn helm đã được cài, bạn hãy verify lại

$ helm version
version.BuildInfo{Version:"v3.10.1", GitCommit:"9f88ccb6aee40b9a0535fcc7efea6055e1ef72c9", GitTreeState:"clean", GoVersion:"go1.18.7"}

Sau khi cài xong helm chúng ta hãy tạo 1 namespace mới có tên nginx-ingress để tạo không gian riêng cho NGINX Ingress

$ kubectl create namespace nginx-ingress
namespace/nginx-ingress created

Thêm repo của NGINX Ingress vào helm

$ helm repo add nginx-stable https://helm.nginx.com/stable
"nginx-stable" has been added to your repositories

Sau khi thêm repo NGINX Ingress vào helm xong, bạn hãy triển khai NGINX Ingress bằng lệnh dưới

$ helm install ingress nginx-stable/nginx-ingress --namespace nginx-ingress
NAME: ingress
LAST DEPLOYED: Thu Oct 27 04:06:45 2022
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.

Clone source code của NGINX Ingress về trên Github

$ git clone https://github.com/nginxinc/kubernetes-ingress.git
Cloning into 'kubernetes-ingress'...
remote: Enumerating objects: 45985, done.
remote: Counting objects: 100% (121/121), done.
remote: Compressing objects: 100% (86/86), done.
remote: Total 45985 (delta 73), reused 70 (delta 35), pack-reused 45864
Receiving objects: 100% (45985/45985), 60.77 MiB | 2.38 MiB/s, done.
Resolving deltas: 100% (27378/27378), done.

Verify lại kết quả sau của việc clone ta sẽ có 12 file yaml như dưới

$ tree ./kubernetes-ingress/deployments/helm-chart/crds/
./kubernetes-ingress/deployments/helm-chart/crds/
├── appprotect.f5.com_aplogconfs.yaml
├── appprotect.f5.com_appolicies.yaml
├── appprotect.f5.com_apusersigs.yaml
├── appprotectdos.f5.com_apdoslogconfs.yaml
├── appprotectdos.f5.com_apdospolicy.yaml
├── appprotectdos.f5.com_dosprotectedresources.yaml
├── externaldns.nginx.org_dnsendpoints.yaml
├── k8s.nginx.org_globalconfigurations.yaml
├── k8s.nginx.org_policies.yaml
├── k8s.nginx.org_transportservers.yaml
├── k8s.nginx.org_virtualserverroutes.yaml
└── k8s.nginx.org_virtualservers.yaml

0 directories, 12 files

Hãy chỉnh sửa file values.yaml đổi cách triển khai từ deployment sang daemonset

$ sed -i 's|kind: deployment|kind: daemonset|g' ./kubernetes-ingress/deployments/helm-chart/values.yaml

Verify lại file values.yaml

$ cat ./kubernetes-ingress/deployments/helm-chart/values.yaml | grep kind:
  kind: daemonset

Tiến hành update lại NGINX Ingress sau khi đã thay đổi giá trị trong file values.yaml

$ helm upgrade ingress nginx-stable/nginx-ingress -f ./kubernetes-ingress/deployments/helm-chart/values.yaml -n nginx-ingress
coalesce.go:220: warning: cannot overwrite table with non table for nginx-ingress.controller.serviceMonitor.endpoints (map[])
Release "ingress" has been upgraded. Happy Helming!
NAME: ingress
LAST DEPLOYED: Thu Oct 27 04:09:28 2022
NAMESPACE: nginx-ingress
STATUS: deployed
REVISION: 2
TEST SUITE: None
NOTES:
The NGINX Ingress Controller has been installed.

Sau khi update xong bạn đã có 3 pod chạy trên 3 worker node, lý do là chúng ta đã đổi cách triển khai pod từ deployment sang daemonset

$ kubectl get po -n nginx-ingress
NAME                          READY   STATUS    RESTARTS   AGE
ingress-nginx-ingress-76j7l   1/1     Running   0          30m
ingress-nginx-ingress-s6hsh   1/1     Running   0          30m
ingress-nginx-ingress-x4g52   1/1     Running   0          30m

Verify lại daemonset NGINX Ingress

$ kubectl get ds -n nginx-ingress
NAME                    DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
ingress-nginx-ingress   3         3         3       3            3           <none>          29m

Verify lại service chúng ta thấy service của NGINX Ingress đang có 2 port chính là 80 và 443

$ kubectl get svc -n nginx-ingress
NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-ingress   LoadBalancer   10.109.246.83   <pending>     80:30701/TCP,443:32153/TCP   3m35s

Tạo file ConfigMap với nội dung như dưới, với:

use-proxy-protocol: đổi từ false thành true

external-status-address: là VIP Address Loadbalancer Node

$ echo """kind: ConfigMap
apiVersion: v1
metadata:
  name: ingress-nginx-ingress
  namespace: nginx-ingress
data:
  use-proxy-protocol: "true"
  proxy-connect-timeout: "10s"
  proxy-read-timeout: "10s"
  client-max-body-size: "2m"
  external-status-address: "192.168.13.238"
""" > ./patch-configmap.yaml

Tiến hành patch configmap

$ kubectl -n nginx-ingress patch configmap ingress-nginx-ingress --patch-file ./patch-configmap.yaml
configmap/ingress-nginx-ingress patched

Show configmap chúng ta thấy namespace nginx-ingress có 3 configmap

$ kubectl -n nginx-ingress get cm
NAME                                    DATA   AGE
ingress-nginx-ingress                   5      28m
ingress-nginx-ingress-leader-election   0      28m
kube-root-ca.crt                        1      29m

Bạn hãy vào configmap ingress-nginx-ingress và verify lại kết quả patch đã chắc chắn chính xác

$ kubectl -n nginx-ingress edit cm/ingress-nginx-ingress

Tiếp theo chúng ta hãy đổi type service từ LoadBalancer sang NodePort để Forwarding 2 port 80 và 443 ra ngoài nhé.

Chạy lệnh dưới để tiến hành patch service ingress-nginx-ingress

$ kubectl -n nginx-ingress patch svc ingress-nginx-ingress --patch '{"spec": { "type": "NodePort", "ports": [ { "port": 80, "nodePort": 30100 }, { "port": 443, "nodePort": 30101 } ] } }'
service/ingress-nginx-ingress patched

Bây giờ service của ingress-nginx-ingress đã đổi sang type là NodePort và port 80 được forwarding ra ngoài bằng port 30100 và port 443 được forwarding ra ngoài bằng port 30101

$ kubectl get svc -n nginx-ingress
NAME                    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-ingress   NodePort   10.109.246.83   <none>        80:30100/TCP,443:30101/TCP   31m

Lúc này khi vào lại http://<vip_ipaddr_loadbalancer>:8080/stats chúng ta thấy 2 khối mới là http_backend, https_backend đã UP màu xanh

Để kiểm tra tính chịu lỗi của cụm, mình sẽ triển khai Prometheus để test. Tạo 1 namespace cho Prometheus

$ kubectl create ns prometheus-monitor
namespace/prometheus-monitor created

Tạo file ./prometheus-Deployment.yaml với nội dung dưới

$ cat > ./prometheus-Deployment.yaml << OEF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: prometheus
  namespace: prometheus-monitor
spec:
  selector:
    matchLabels:
      run: prometheus
  replicas: 5
  template:
    metadata:
      labels:
        run: prometheus
    spec:
      containers:
        - name: prometheus
          image: prom/prometheus
          ports:
            - containerPort: 9090
OEF

Triển khai prometheus-Deployment.yaml

$ kubectl apply -f ./prometheus-Deployment.yaml
deployment.apps/prometheus created

Verify lại kết quả triển khai prometheus-Deployment.yaml chúng ta đã có 5 pods đúng như đã khai báo ở replicas trong deployment

$ kubectl get po -n prometheus-monitor -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
prometheus-578745d58b-59xnq   1/1     Running   0          77s   10.244.194.67    k8s-worker1   <none>           <none>
prometheus-578745d58b-7p69n   1/1     Running   0          77s   10.244.126.3     k8s-worker2   <none>           <none>
prometheus-578745d58b-lf8w6   1/1     Running   0          77s   10.244.126.4     k8s-worker2   <none>           <none>
prometheus-578745d58b-llh27   1/1     Running   0          77s   10.244.100.197   k8s-worker3   <none>           <none>
prometheus-578745d58b-tw4jg   1/1     Running   0          77s   10.244.194.66    k8s-worker1   <none>           <none>

Tạo file prometheus-Service.yaml với nội dung dưới

$ cat > ./prometheus-Service.yaml << OEF
apiVersion: v1
kind: Service
metadata:
  name: prometheus
  namespace: prometheus-monitor
  labels:
    run: prometheus
spec:
  ports:
    - name: prometheus
      port: 80
      targetPort: 9090
  selector:
    run: prometheus
OEF

Triển khai prometheus-Service.yaml

$ kubectl apply -f ./prometheus-Service.yaml
service/prometheus created

Chúng ta đã có 1 service mới của prometheus

$ kubectl get svc -n prometheus-monitor -o wide
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE   SELECTOR
prometheus   ClusterIP   10.103.255.158   <none>        80/TCP    68s   run=prometheus

Hãy tạo file prometheus-Ingress.yaml với nội dung dưới với prometheus.hoanghd.com chính là domain của prometheus

$ cat > ./prometheus-Ingress.yaml << OEF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: prometheus
  namespace: prometheus-monitor
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
    - host: prometheus.hoanghd.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: prometheus
                port:
                  number: 80
OEF

Triển khai prometheus-Ingress.yaml

$ kubectl apply -f ./prometheus-Ingress.yaml
ingress.networking.k8s.io/prometheus created

Kiểm tra lại quá trình triển khai nginx-ingress chúng ta thấy domain prometheus-Ingress.yaml đã được trỏ về VIP Address của Loadbalancer node

$ kubectl get ing -n prometheus-monitor -o wide
NAME         CLASS   HOSTS                    ADDRESS          PORTS   AGE
prometheus   nginx   prometheus.hoanghd.com   192.168.13.238   80      3s

Trỏ tên miền cho prometheus.hoanghd.com về VIP Address của Loadbalancer node và kiểm tra kết nối

$ ping prometheus.hoanghd.com
PING prometheus.hoanghd.com (192.168.13.238): 56 data bytes
64 bytes from 192.168.13.238: icmp_seq=0 ttl=64 time=1.526 ms
64 bytes from 192.168.13.238: icmp_seq=1 ttl=64 time=0.468 ms
64 bytes from 192.168.13.238: icmp_seq=2 ttl=64 time=0.563 ms
64 bytes from 192.168.13.238: icmp_seq=3 ttl=64 time=0.500 ms
64 bytes from 192.168.13.238: icmp_seq=4 ttl=64 time=0.452 ms

--- prometheus.hoanghd.com ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.452/0.702/1.526/0.414 ms

Hãy sử dụng trình duyệt web để kiểm tra kết quả, chúng ta đã truy cập được dịch vụ

Kết quả telnet port 80 vào domain prometheus.hoanghd.com

$ telnet prometheus.hoanghd.com 80
Trying 192.168.13.238...
Connected to prometheus.hoanghd.com.
Escape character is '^]'.

Hiện tại ip 192.168.13.238 của VIP Address của Loadbalancer node đang được gắn cho Loadbalancer2 vì Loadbalancer2 đang nắm priority keepalive lớn hơn.

Bây giờ mình sẽ tắt Loadbalancer2 có ip là 192.168.13.218

$ ip addr show ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:ad:78:a2 brd ff:ff:ff:ff:ff:ff
    inet 192.168.13.218/23 brd 192.168.13.255 scope global ens192
       valid_lft forever preferred_lft forever
    inet 192.168.13.238/32 scope global ens192
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:fead:78a2/64 scope link
       valid_lft forever preferred_lft forever

Kết quả 192.168.13.218 đã mất kết nối, bây giờ chúng ta chỉ còn 1 Loadbalancer còn sống đó là Loadbalancer1 (Loadbalancer2 đã bị mình tắt)

$ ping 192.168.13.218
PING 192.168.13.218 (192.168.13.218): 56 data bytes
Request timeout for icmp_seq 0
Request timeout for icmp_seq 1
Request timeout for icmp_seq 2
Request timeout for icmp_seq 3
ping: sendto: No route to host
Request timeout for icmp_seq 4

--- 192.168.13.218 ping statistics ---
6 packets transmitted, 0 packets received, 100.0% packet loss

Vậy bây giờ nếu mình ssh vào 192.168.13.238 thì 192.168.13.238 sẽ kết nối vào ip 192.168.13.217 của Loadbalancer1 như ở dưới

$  ssh root@192.168.13.238
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-194-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

  System information as of Thu Oct 27 06:17:21 UTC 2022

  System load:  0.0               Processes:             105
  Usage of /:   3.8% of 48.27GB   Users logged in:       1
  Memory usage: 7%                IP address for ens192: 192.168.13.217
  Swap usage:   0%

 * Super-optimized for small spaces - read how we shrank the memory
   footprint of MicroK8s to make it the smallest full K8s around.

   https://ubuntu.com/blog/microk8s-memory-optimisation

6 updates can be applied immediately.
5 of these updates are standard security updates.
To see these additional updates run: apt list --upgradable

New release '20.04.5 LTS' available.
Run 'do-release-upgrade' to upgrade to it.


Last login: Thu Oct 27 06:15:42 2022 from 192.168.12.63

Hãy show ip của 192.168.13.238 chúng ta thấy ip 192.168.13.238 của VIP Address của Loadbalancer node đã được gắn cho 192.168.13.217 của Loadbalancer1

$ ip addr show ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:50:56:ad:f9:96 brd ff:ff:ff:ff:ff:ff
    inet 192.168.13.217/23 brd 192.168.13.255 scope global ens192
       valid_lft forever preferred_lft forever
    inet 192.168.13.238/32 scope global ens192
       valid_lft forever preferred_lft forever
    inet6 fe80::250:56ff:fead:f996/64 scope link
       valid_lft forever preferred_lft forever

Mình tiếp tục tắt các node k8s-master2, k8s-worker2, k8s-worker3 chúng ta có thể thấy các node đã bị tắt chuyển sang trạng thái NotReady đông thời các pod của prometheus trên các node bị chết đdãđược khởi tạo lại (trạng thái ContainerCreating), các pod của prometheus trên các node bị chết sẽ bị xoá đi (trạng thái Terminating)

$ kubectl get no,po,svc,ing -n prometheus-monitor
NAME               STATUS     ROLES                  AGE   VERSION
node/k8s-master1   Ready      control-plane,master   14h   v1.23.8
node/k8s-master2   NotReady   control-plane,master   14h   v1.23.8
node/k8s-master3   Ready      control-plane,master   14h   v1.23.8
node/k8s-worker1   Ready      <none>                 14h   v1.23.8
node/k8s-worker2   NotReady   <none>                 14h   v1.23.8
node/k8s-worker3   NotReady   <none>                 14h   v1.23.8

NAME                              READY   STATUS              RESTARTS   AGE
pod/prometheus-578745d58b-59xnq   1/1     Running             0          90m
pod/prometheus-578745d58b-6mgwg   0/1     ContainerCreating   0          3s
pod/prometheus-578745d58b-7p69n   1/1     Terminating         0          90m
pod/prometheus-578745d58b-lf8w6   1/1     Terminating         0          90m
pod/prometheus-578745d58b-llh27   1/1     Running             0          90m
pod/prometheus-578745d58b-smbcl   0/1     ContainerCreating   0          3s
pod/prometheus-578745d58b-tw4jg   1/1     Running             0          90m

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/prometheus   ClusterIP   10.103.255.158   <none>        80/TCP    89m

NAME                                   CLASS   HOSTS                    ADDRESS          PORTS   AGE
ingress.networking.k8s.io/prometheus   nginx   prometheus.hoanghd.com   192.168.13.238   80      86m

Khi vào http://<vip_ipaddr_loadbalancer>:8080/stats để check lại thì các node k8s-master2, k8s-worker2, k8s-worker3 đã bị down chuyển sang màu đỏ

Kết quả telnet cho ta thấy mặc dù chúng ta đã bị chết 1 Loadbalancer, 1 master, 2 worker nhưng dịch vụ của chúng ta vẫn còn chạy được

$ telnet prometheus.hoanghd.com 80
Trying 192.168.13.238...
Connected to prometheus.hoanghd.com.
Escape character is '^]'.

Chúc các bạn thành công.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

4,956FansLike
256FollowersFollow
223SubscribersSubscribe
spot_img

Related Stories