Giải quyết lỗi Kubernetes Load Balancer External IP luôn ở trạng thái Pending

Vấn đề:

  • Trạng thái IP external của Kubernetes Load Balancer bị giữ ở trạng thái Pending là một vấn đề thường gặp trong các standalone clusters khi triển khai load balancer service.

  • Thực ra, đây là tính năng chứ không phải là bug. Khi sử dụng nhà cung cấp cloud như GCP, AWS hay Azure thì chính nhà cung cấp Cloud sẽ chỉ định external ip cho load balancer service. Nhưng cứ mỗi ip họ sẽ tính thêm phí

Cách giải quyết:

MetalLB có thể được sử dụng để giải quyết vấn đề này. MetalLB là một triển khai cân bằng tải cho các cụm bare metal Kubernetes clusters, sử dụng các giao thức định tuyến tiêu chuẩn. Metal LB sẽ được định cấu hình bên trong cụm Kubernetes nơi chúng ta đang làm việc.

Thông tin thêm về metallb: metallb.universe.tf

Cách thực hiện:

Deploy MetalLB Load Balancer trong K8s cluster:

Trước tiên, đảm bảo rằng không có vấn đề gì với việc sử dụng K8s cluster API và kubectl:

kubectl cluster-info

Cài đặt wget và curl:

### Debian / Ubuntu ###
sudo apt update && sudo apt install wget curl -y

### CentOS / RHEL / Fedora ###
sudo yum -y install wget curl

Download MetalLB installation manifest:

Lấy release tag mới nhất:

MetalLB_RTAG=$(curl -s https://api.github.com/repos/metallb/metallb/releases/latest|grep tag_name|cut -d '"' -f 4|sed 's/v//')

Kiểm tra release tag:

echo $MetalLB_RTAG

Tạo thư mục để download manifest:

mkdir ~/metallb
cd ~/metallb

Tải manifest mới nhất:

wget https://raw.githubusercontent.com/metallb/metallb/v$MetalLB_RTAG/config/manifests/metallb-native.yaml

Có 2 thành phần chính trong file manifest là:

  • metallb-system/controller deployment: Bộ điều khiển toàn cụm xử lý việc gán địa chỉ IP.

  • metallb-system/speaker daemonset

Cài đặt MetalLB Load Balancer trên cụm K8s:

kubectl apply -f metallb-native.yaml

Command trên sẽ giúp chúng ta deploy MetalLB trên cụm K8s trong namespace metallb-system

watch kubectl get all -n metallb-system
kubectl get pods -n metallb-system --watch

Đợi một lúc để mọi thứ chuyển sang trạng thái running. Lúc này, bạn có thể list các running Pods:

kubectl get pods  -n metallb-system

Để list tất cả các service:

kubectl get all  -n metallb-system

installation manifest không bao gồm tệp cấu hình cần thiết để sử dụng MetalLB. Tất cả các thành phần MetalLB đều được khởi động nhưng sẽ vẫn ở trạng thái không hoạt động cho đến khi bạn hoàn tất các cấu hình cần thiết.

Tạo Load Balancer services IP address pool:

MetalLB cần một nhóm địa chỉ IP để gán cho các dịch vụ khi nhận được yêu cầu đó. Chúng ta phải hướng dẫn MetalLB thực hiện việc đó thông qua IPAddressPool CR.

Hãy tạo một tệp có cấu hình cho IP mà MetalLB sử dụng để gán IP cho các dịch vụ. Trong cấu hình nhóm có dải IP 192.168.1.30-192.168.1.50.

vim ~/metallb/ipaddress_pools.yaml

Nội dung file:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: production
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.30-192.168.1.50

Địa chỉ IP có thể được xác định bằng CIDR, theo phạm vi và có thể chỉ định cả địa chỉ IPV4 và IPV6.

...
spec:
  addresses:
  - 192.168.1.0/24

Bạn có thể xác định nhiều IPAddressPool definition. Xem ví dụ dưới đây:

...
spec:
  addresses:
  - 192.168.1.0/24
  - 172.20.20.30-172.20.20.50
  - fc00:f853:0ccd:e799::/124

Announce service IPs sau khi tạo sau:

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2-advert
  namespace: metallb-system

Sự quảng bá (advertisement) cũng có thể được giới hạn ở một Pool cụ thể. Trong ví dụ, giới hạn là đối với production pool.

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: pool1-advert
  namespace: metallb-system
spec:
  ipAddressPools:
  - production

Cấu hình hoàn chỉnh với cả IP Address Pool và L2 advertisement:

vim ~/metallb/ipaddress_pools.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: production
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.30-192.168.1.50
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: l2-advert
  namespace: metallb-system
kubectl apply -f  ~/metallb/ipaddress_pools.yaml

kubectl get ipaddresspools.metallb.io -n metallb-system
kubectl get l2advertisements.metallb.io -n metallb-system

Xem chi tiết hơn:

kubectl describe ipaddresspools.metallb.io production -n metallb-system

Deploy services sử dụng MetalLB Load Balancer:

Với MetalLB được cài đặt và định cấu hình, chúng ta có thể kiểm tra bằng cách tạo service với spec.type được đặt thành LoadBalancer và MetalLB sẽ thực hiện phần còn lại. Điều này expose ra bên ngoài một service.

MetalLB attaches informational events vào các service mà nó đang kiểm soát. Nếu LoadBalancer của bạn hoạt động sai, hãy chạy kubectl describe service <tên service> và kiểm tra event log.

Chúng ta tạo một service test:

vim web-app-demo.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: web
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
  namespace: web
spec:
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: httpd
        image: httpd:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-server-service
  namespace: web
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer
 kubectl apply -f web-app-demo.yaml

Kiểm tra địa chỉ IP được assigned bởi Load Balancer cho service:

kubectl get svc -n web

Test:

telnet 192.168.1.30 80

Request địa chỉ IP cụ thể cho 1 service:

MetalLB tôn trọng tham số spec.loadBalancerIP và tự động gán địa chỉ IP nhàn rỗi tiếp theo trong Pool. Nếu bạn muốn service của mình được thiết lập với một địa chỉ cụ thể, bạn có thể yêu cầu địa chỉ đó bằng cách đặt tham số này.

Xóa các resources cũ để thay thế bằng resources mới:

kubectl delete -f web-app-demo.yaml

vim web-app-demo.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: web
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-server
  namespace: web
spec:
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: httpd
        image: httpd:alpine
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-server-service
  namespace: web
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer
  loadBalancerIP: 192.168.1.35

Apply configure để tạo ra Pods và Service endpoints:

kubectl apply -f web-app-demo.yaml

Kiểm tra IP được assigned:

kubectl get svc -n web

Test service:

curl http://192.168.1.35

Chọn pool địa chỉ IP cụ thể:

Khi tạo một service type là LoadBalancer, bạn có thể yêu cầu một nhóm địa chỉ IP cụ thể. Đây là một tính năng được MetalLB hỗ trợ.

Bạn có thể yêu cầu một nhóm cụ thể IP address assignment bằng cách thêm annotation metallicb.universe.tf/address-pool vào service, với tên của nhóm địa chỉ làm annotation value. Ví dụ:

apiVersion: v1
kind: Service
metadata:
  name: web-server-service
  namespace: web
  annotations:
    metallb.universe.tf/address-pool: production
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: LoadBalancer

Kiểm soát automatic address allocation:

Đây là một ứng dụng hợp lý dành cho nhóm IP “đắt tiền” nhỏ hơn (ví dụ: địa chỉ IPv4 public được thuê).

Theo mặc định, MetalLB phân bổ địa chỉ IP nhàn rỗi từ bất kỳ nhóm địa chỉ nào được configured. Hành vi này có thể được ngăn chặn bằng cách vô hiệu hóa phân bổ tự động cho một nhóm bằngflag autoAssign được đặt thành false:

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: expensive
  namespace: metallb-system
spec:
  addresses:
  - 42.175.26.64/30
  autoAssign: false

Các địa chỉ vẫn có thể được phân bổ cụ thể từ nhóm “đắt tiền” bằng các phương pháp được mô tả trong phần “Chọn pool địa chỉ IP cụ thể”.

Chia sẻ địa chỉ IP:

Theo mặc định, các service Kubernetes không chia sẻ địa chỉ IP. Đối với mọi nhu cầu định vị các dịch vụ trên một IP duy nhất, hãy thêm annotation metallicb.universe.tf/allow-shared-ip để cho phép chia sẻ IP có chọn lọc cho các service của bạn.

Giá trị của annotation này là “sharing key”. Để Services chia sẻ địa chỉ IP, phải đáp ứng các điều kiện sau:

- Cả hai services nên chia sẻ cùng một sharing key.
- Các services nên sử dụng các cổng khác nhau (ví dụ: tcp/80 cho một cổng và tcp/443 cho cổng còn lại).
- Hai services nên sử dụng Cluster external traffic policy hoặc cả hai đều trỏ đến cùng một nhóm các pods (tức là pod selectors giống hệt nhau).

Bằng cách sử dụng spec.loadBalancerIP, điều đó có nghĩa là hai service chia sẻ một địa chỉ cụ thể. Xem cấu hình ví dụ bên dưới của hai service chung địa chỉ IP:

apiVersion: v1
kind: Service
metadata:
  name: dns-service-tcp
  namespace: demo
  annotations:
    metallb.universe.tf/allow-shared-ip: "key-to-share-192.168.1.36"
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.36
  ports:
    - name: dnstcp
      protocol: TCP
      port: 53
      targetPort: 53
  selector:
    app: dns
---
apiVersion: v1
kind: Service
metadata:
  name: dns-service-udp
  namespace: demo
  annotations:
    metallb.universe.tf/allow-shared-ip: "key-to-share-192.168.1.36"
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.1.36
  ports:
    - name: dnsudp
      protocol: UDP
      port: 53
      targetPort: 53
  selector:
    app: dns

Xem thêm các cấu hình L2 nâng cao:

metallb.universe.tf/configuration/_advanced..

Đặt Nginx sử dụng MetalLB:

Nếu bạn đang sử dụng Nginx Ingress Controller trong cluster, bạn có thể định cấu hình nó để sử dụng MetalLB làm Bộ cân bằng tải.

Deploy Nginx Ingress Controller trên K8s sử dụng Helm chart:

Sử dụng service là cách thông thường để expose các ứng dụng đang chạy trên một nhóm Pod trong Cụm Kubernetes. Trong Kubernetes, mỗi Pod có địa chỉ IP riêng nhưng một nhóm Pod có thể chia sẻ tên DNS. Kubernetes có thể truyền bá dữ liệu ra khắp các Pod mà không cần thay đổi lớp ứng dụng. Theo mặc định, một service được cấp một địa chỉ IP (đôi khi được gọi là “cluster IP“), được sử dụng bởi các proxy của Service. Label selectors cho phép Service tìm ra Pod nào trong một nhóm.

Ingress Controller là gì?

Theo tài liệu chính thống của Kubernetes:

An API object that manages external access to the services in a cluster, typically HTTP.

Ingress may provide load balancing, SSL termination and name-based virtual hosting.

Một Ingress trong Kubernetes expose các HTTP và HTTPS routes từ bên ngoài cụm đến các service chạy trong cụm. Tất cả việc định tuyến lưu lượng truy cập được kiểm soát bởi các quy tắc được xác định trên Ingress resource. Một Ingress có thể được cấu hình để:

  • Cung cấp Service với các URL có thể truy cập được từ bên ngoài

  • Cân bằng tải traffic đi vào các cluster services

  • Terminate SSL / TLS traffic

  • Cung cấp name-based virtual hosting trong Kubernetes

Ingress controller là thứ fulfils Ingress, thường là với bộ cân bằng tải. Dưới đây là ví dụ về cách Ingress gửi tất cả lưu lượng truy cập của máy khách đến Service trong Cụm Kubernetes:

Với các traffic HTTP và HTTPS tiêu chuẩn, một Ingress Controller sẽ được configured để listen ở các port 80 và 443. Nó sẽ bind với một địa chỉ IP mà cluster sẽ nhận được lưu lượng truy cập từ đó. Wildcard DNS đại diện cho domain được sử dụng cho các Ingress routes sẽ trỏ đến (các) địa chỉ IP mà Ingress routes listen.

Thực tế là Kubernetes áp dụng cách tiếp cận BYOS (Bring-Your-Own-Software) cho hầu hết các addons của nó và nó không cung cấp phần mềm thực hiện các chức năng Ingress. Bạn có thể chọn từ rất nhiều Ingress Controllers có sẵn.

Step 1: Cài đặt Nginx Ingress Controller:

Option 1: Install không dùng Helm

Với cách này, bạn sẽ cần phải download và chạy tay deployment manifest sử dụng câu lệnh kubectl.

Cài đặt git, curl và wget:

# CentOS / RHEL / Fedora / Rocky
sudo yum -y install wget curl git

# Debian / Ubuntu
sudo apt update
sudo apt install wget curl git

Apply Nginx Ingress Controller manifest:

Bài viết này tập trung vào sử dụng Bare-metal Nginx Ingress deployment, nếu sử dụng các managed cluster, xem các tài liệu sau:

Phương pháp Bare-metal áp dụng cho mọi cụm Kubernetes được triển khai trên bare-metal với bản phân phối Linux chung (chẳng hạn như CentOS, Ubuntu, Debian, Rocky Linux), v.v.

Download Nginx controller deployment:

controller_tag=$(curl -s https://api.github.com/repos/kubernetes/ingress-nginx/releases/latest | grep tag_name | cut -d '"' -f 4)

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/${controller_tag}/deploy/static/provider/baremetal/deploy.yaml
mv deploy.yaml nginx-ingress-controller-deploy.yaml
kubectl apply -f nginx-ingress-controller-deploy.yaml

kubectl config set-context --current --namespace=ingress-nginx
kubectl get pods -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --watch

Nếu bạn muốn chạy nhiều Nginx Ingress Pods, bạn có thể mở rộng quy mô bằng lệnh bên dưới:

kubectl -n ingress-nginx scale deployment ingress-nginx-controller --replicas 2

Option 3: Sử dụng Helm:

Cài đặt helm 3:

cd ~/
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh
helm version

Download latest stable release of Nginx Ingress Controller:

controller_tag=$(curl -s https://api.github.com/repos/kubernetes/ingress-nginx/releases/latest | grep tag_name | cut -d '"' -f 4)

wget https://github.com/kubernetes/ingress-nginx/archive/refs/tags/${controller_tag}.tar.gz

Giải nén:

tar xvf ${controller_tag}.tar.gz

Vào thư mục:

cd ingress-nginx-${controller_tag}

cd charts/ingress-nginx/

Tạo namespace:

kubectl create namespace ingress-nginx

Deploy nginx ingress control:

helm install -n ingress-nginx ingress-nginx  -f values.yaml .

Sample output:

NAME: ingress-nginx
LAST DEPLOYED: Thu Nov  4 02:50:28 2021
NAMESPACE: ingress-nginx
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The ingress-nginx controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace ingress-nginx get services -o wide -w ingress-nginx-controller'

An example Ingress that makes use of the controller:

  apiVersion: networking.k8s.io/v1
  kind: Ingress
  metadata:
    annotations:
      kubernetes.io/ingress.class: nginx
    name: example
    namespace: foo
  spec:
    ingressClassName: example-class
    rules:
      - host: www.example.com
        http:
          paths:
            - path: /
              pathType: Prefix
              backend:
                service:
                  name: exampleService
                  port: 80
    # This section is only required if TLS is to be enabled for the Ingress
    tls:
      - hosts:
        - www.example.com
        secretName: example-tls

If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided:

  apiVersion: v1
  kind: Secret
  metadata:
    name: example-tls
    namespace: foo
  data:
    tls.crt: <base64 encoded cert>
    tls.key: <base64 encoded key>
  type: kubernetes.io/tls

Check status:

kubectl get all -n ingress-nginx

kubectl get pods -n ingress-nginx

kubectl -n ingress-nginx  logs deploy/ingress-nginx-controller

kubectl -n ingress-nginx  logs deploy/ingress-nginx-controller -f

Set replica count of the controller pods to 2:

$ vim values.yaml 
controller:
  replicaCount: 3

Confirm hiện tại chỉ có 1 pod:

kubectl -n ingress-nginx  get deploy

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
ingress-nginx-controller   1/1     1            1           43m

Update ingress – nginx release:

helm upgrade -n ingress-nginx ingress-nginx -f values.yaml .

Check số lượng pod sau khi upgrade:

kubectl -n ingress-nginx  get deploy

Uninstall the chart:

Để xóa nginx ingress controller và tất cả các tài nguyên liên quan mà chúng ta đã triển khai bởi Helm:

helm -n ingress-nginx uninstall ingress-nginx

Step 2: Configure Nginx Ingress Controller

Option 1: Sử dụng Load Balancer:

Setting Nginx Ingress dùng MetalLB:

Check nginx service:

kubectl get svc -n ingress-nginx

Nếu cụm Kubernetes của bạn là cụm “real” hỗ trợ các dịch vụ thuộc loại LoadBalancer, thì cụm đó sẽ phân bổ địa chỉ IP external hoặc FQDN cho bộ ingress controller. Sử dụng lệnh sau để xem địa chỉ IP hoặc FQDN đó:

kubectl get service ingress-nginx-controller --namespace=ingress-nginx

NAME                       TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller   NodePort   10.108.187.12   <none>        80:31320/TCP,443:31124/TCP   25m

Patch ingress-nginx-controller service bằng cách đặt loại service thành LoadBalancer:

kubectl -n ingress-nginx patch svc ingress-nginx-controller --type='json' -p '[{"op":"replace","path":"/spec/type","value":"LoadBalancer"}]'

kiểm tra lại:

kubectl get service ingress-nginx-controller --namespace=ingress-nginx

NAME                       TYPE           CLUSTER-IP    EXTERNAL-IP    PORT(S)                      AGE
ingress-nginx-controller   LoadBalancer   10.108.4.75   192.168.1.30   80:30084/TCP,443:30540/TCP   10m

Service được tự động gán một địa chỉ IP từ Nhóm địa chỉ như được định cấu hình trong MetalLB.

Mapping DNS name cho Nginx Ingress từ LB IP

Chúng ta có thể tạo tên miền, tốt nhất là wildcard để sử dụng khi tạo các tuyến Ingress trong Kubernetes. Trong cụm k8s, chúng ta có k8s.example.com làm tên miền cơ sở. Chúng ta sẽ sử dụng miền wildcard duy nhất *.k8s.example.com cho Ingress.

Mapping là *.k8s.example.com trỏ đến địa chỉ IP 192.168.1.30 (IP Nginx Ingress LB).

Option 2: Sử dụng nodes để chạy nginx Ingress Pod (không được khuyến khích)

  1. Label nodes sẽ sử dụng để chạy Ingress Controller Pods:

node selector được sử dụng khi chúng ta phải triển khai một nhóm hoặc một nhóm pods trên một nhóm nodes cụ thể đã vượt qua các tiêu chí được xác định trong tệp cấu hình.

Danh sách các nodes:

kubectl get nodes

NAME                         STATUS   ROLES                  AGE   VERSION
k8smaster01.example.com      Ready    control-plane,master   14h   v1.23.5
k8smaster02.example.com      Ready    control-plane,master   14h   v1.23.5
k8sworker01.example.com      Ready    <none>                 13h   v1.23.5
k8sworker02.example.com      Ready    <none>                 13h   v1.23.5
k8sworker03.example.com      Ready    <none>                 13h   v1.23.5

2. Sửa ingress-nginx-controller service và set externalIPs

Trong quá trình thiết lậpprivate Infrastructure Kubernetes deployment, bạn khó có thể không có Load Balancer service support.

kubectl get svc  -n ingress-nginx ingress-nginx-controller

NAME                       TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE
ingress-nginx-controller   NodePort   10.101.4.21   <none>        80:30248/TCP,443:30773/TCP   3m53s

Bạn sẽ nhận thấy service thuộc loại NodePort. Chúng ta sẽ cập nhật service bằng cách bind Ingress với địa chỉ IP cụ thể bằng cách sử dụng IP external.

Kubernetes hỗ trợ gán địa chỉ IP external cho trường Service spec.externalIPs thông qua ExternalIP facility. Điều này sẽ expose một địa chỉ IP ảo bổ sung, được gán cho Nginx Ingress Controller Service. Điều này cho phép chúng ta hướng lưu lượng truy cập đến một local node để cân bằng tải.

Trong Cụm Kubernetes của tôi, tôi có hai node control Plane nodes có địa chỉ IP chính bên dưới:

Địa chỉ IP của các nodes có thể được kiểm tra bằng cách chạy lệnh:

kubectl get nodes -o wide

Tôi sẽ tạo một tệp chứa sửa đổi để thêm External IPs vào service.

$ vim external-ips.yaml
spec:
  externalIPs:
  - 192.168.42.245
  - 192.168.42.246

apply patch cho service:

kubectl -n ingress-nginx patch svc ingress-nginx-controller --patch "$(cat external-ips.yaml)"
kubectl get svc  ingress-nginx-controller -n ingress-nginx

NAME                       TYPE       CLUSTER-IP    EXTERNAL-IP                     PORT(S)                      AGE
ingress-nginx-controller   NodePort   10.101.4.21   192.168.42.245,192.168.42.246   80:30248/TCP,443:30773/TCP   8m31

3. Chạy ingress-nginx-controller Pods trên Control Plane (Master) Nodes

Bạn có thể cân nhắc việc chạy Ingress Controller Pods trên các master nodes. Để đạt được điều này, chúng ta sẽ gắn nhãn các master nodes, sau đó sử dụng node selector để chỉ định các pods trong Ingress controller deployment cho các Control Plane nodes.

kubectl get nodes -l node-role.kubernetes.io/control-plane

NAME                         STATUS   ROLES                  AGE   VERSION
k8smaster01.example.com      Ready    control-plane,master   16d   v1.23.5
k8smaster02.example.com      Ready    control-plane,master   16d   v1.23.5

Thêm label runress=nginx vào các master nodes:

kubectl label node k8smaster01.example.com runingress=nginx
kubectl label node k8smaster02.example.com runingress=nginx

Labels đã add vào nodes có thể được check bằng câu lệnh:

kubectl describe node <node-name>

Ví dụ:

kubectl describe node k8smaster01.example.com

Name:               k8smaster01.example.com
Roles:              control-plane,master
Labels:             beta.kubernetes.io/arch=amd64
                    beta.kubernetes.io/os=linux
                    kubernetes.io/arch=amd64
                    kubernetes.io/hostname=k8smaster01.example.com
                    kubernetes.io/os=linux
                    node-role.kubernetes.io/control-plane=
                    node-role.kubernetes.io/master=
                    node.kubernetes.io/exclude-from-external-load-balancers=
                    runingress=nginx # Label added
.....

Tạo patch file để chạy các pods trên nodes với label runingress=nginx:

vim node-selector-patch.yaml
spec:
  template:
    spec:
      nodeSelector:
        runingress: nginx
kubectl get deploy  -n ingress-nginx
kubectl -n ingress-nginx patch deployment/ingress-nginx-controller --patch "$(cat node-selector-patch.yaml)"

4. Thêm tolerations cho phép Nginx ingress Pods chạy trên Control Plane nodes

Trong Kubernetes, default setting disable pods trong master nodes. Để các Ingress pods chạy trong các master nodes, bạn sẽ phải thêm tolerations.

Hãy tạo một file patch để áp dụng tolerations khi triển khai Ingress:

vim master-node-tolerations.yaml
spec:
  template:
    spec:
      tolerations:
        - key: node-role.kubernetes.io/master
          operator: Equal
          value: "true"
          effect: NoSchedule
        - key: node-role.kubernetes.io/master
          operator: Equal
          effect: NoSchedule
        - key: node-role.kubernetes.io/control-plane
          operator: Equal
          value: "true"
          effect: NoSchedule
        - key: node-role.kubernetes.io/control-plane
          operator: Equal
          effect: NoSchedule

Apply the patch:

kubectl -n ingress-nginx patch deployment/ingress-nginx-controller --patch "$(cat master-node-tolerations.yaml)"

Confirm pod mới tạo có Node Selector configured:

kubectl get pods -n ingress-nginx

NAME                                        READY   STATUS      RESTARTS   AGE
ingress-nginx-admission-create--1-hpkzp     0/1     Completed   0          3d
ingress-nginx-admission-patch--1-qnjlj      0/1     Completed   1          3d
ingress-nginx-controller-57b46c846b-8n28t   1/1     Running     0          1m47s

$ kubectl describe pod ingress-nginx-controller-57b46c846b-8n28t
...omitted_output...
QoS Class:                   Burstable
Node-Selectors:              kubernetes.io/os=linux
                             run-nginx-ingress=true
                             runingress=nginx
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:

5. Update values.yml để thay đổi parameters (Chỉ khi sử dụng Helm deployment)

vim values.yaml

Thêm tolerations cho Master nodes

controller:
  tolerations:
    - key: node-role.kubernetes.io/master
      operator: Equal
      value: "true"
      effect: NoSchedule
    - key: node-role.kubernetes.io/master
      operator: Equal
      effect: NoSchedule
    - key: node-role.kubernetes.io/control-plane
      operator: Equal
      value: "true"
      effect: NoSchedule
    - key: node-role.kubernetes.io/control-plane
      operator: Equal
      effect: NoSchedule

Set controller.service.externalIPs:

controller:
  service:
    externalIPs: ["192.168.42.245","192.168.42.246"]

Để set số lượng replicas Ingress controller deployment trong controller.replicaCount:

controller:
  replicaCount: 1

Nếu sử dụng node selector cho pod assignment cho Ingress controller pods set on controller.nodeSelector

controller:
  nodeSelector:
    kubernetes.io/os: linux
    runingress: "nginx"

Tạo namespace:

kubectl create namespace ingress-nginx

Deploy Nginx Ingress Controller:

helm install -n ingress-nginx ingress-nginx  -f values.yaml .

Step 3: Deploy services test để test chức năng Nginx Ingress:

Tạo một namespace có tên là demo:

kubectl create namespace demo

Tạo test pod và services bằng file YAML:

cd ~/
vim demo-app.yml
kind: Pod
apiVersion: v1
metadata:
  name: apple-app
  labels:
    app: apple
spec:
  containers:
    - name: apple-app
      image: hashicorp/http-echo
      args:
        - "-text=apple"
---

kind: Service
apiVersion: v1
metadata:
  name: apple-service
spec:
  selector:
    app: apple
  ports:
    - port: 5678 # Default port for image
---
kind: Pod
apiVersion: v1
metadata:
  name: banana-app
  labels:
    app: banana
spec:
  containers:
    - name: banana-app
      image: hashicorp/http-echo
      args:
        - "-text=banana"
---

kind: Service
apiVersion: v1
metadata:
  name: banana-service
spec:
  selector:
    app: banana
  ports:
    - port: 5678 # Default port for image
kubectl apply -f demo-app.yml -n demo

Test hoạt động:

kubectl get pods -n demo

kubectl -n demo logs apple-app

Tạo Ubuntu pod sử dụng để test service connection:

cat <<EOF | kubectl -n demo apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: ubuntu
  labels:
    app: ubuntu
spec:
  containers:
  - name: ubuntu
    image: ubuntu:latest
    command: ["/bin/sleep", "3650d"]
    imagePullPolicy: IfNotPresent
  restartPolicy: Always
EOF

Test services connectivity trong namespace:

kubectl -n demo exec -ti ubuntu -- bash
root@ubuntu:/# apt update && apt install curl -y
root@ubuntu:/# curl apple-service:5678
apple
root@ubuntu:/# curl banana-service:5678
banana

Tạo ingress route:

Cách tạo Ingress definition có thể được xem bằng command sau:

kubectl explain ingress

Bây giờ, định nghĩa một Ingress để route request tới /apple kết nối tới service thứ nhất, và /banana tới service thứ hai. Kiểm tra Ingress’ rules field định nghĩa các request đi qua

vim webapp-app-ingress.yml

Với k8s cluster > 1.19:

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: webapp-ingress
spec:
  ingressClassName: nginx
  rules:
  - host: webapp.k8s.example.com
    http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: web-server-service
              port:
                number: 80
kubectl -n web apply -f  webapp-app-ingress.yml

List configured ingress:

kubectl get ingress -n web

Exec vào nginx ingress controller để xem nginx configuration đã được injected hay chưa

kubectl get pods -n ingress-nginx

NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx-controller-6f5844d579-hwrqn   1/1     Running   0          53m
ingress-nginx-controller-6f5844d579-kvgtd   1/1     Running   0          25m
ingress-nginx-controller-6f5844d579-lcrrt   1/1     Running   0
kubectl exec -n ingress-nginx  -it ingress-nginx-controller-6f5844d579-hwrqn  -- /bin/bash

bash-5.1$ less /etc/nginx/nginx.conf

Test service sử dụng curl:

curl http://webapp.k8s.example.com/

<html><body><h1>It works!</h1></body></html>

Note:

Trong service phải thiết định spec thêm :

externalTrafficPolicy: Local

Thì app bên trong mới get được IP thật , vì bản chất thằng MetalLB này nó chỉ làm việc ở L2 nên việc get IP thật TCP HTTP hơi khoai