[บันทึกช่วยจำ] ลองเล่น NGINX Ingress ใน K8S Cluster บน On-Premises
ครั้งนี้เรามาต่อยอดจากครั้งที่แล้วจากเรื่อง [บันทึกช่วยจำ] ลองเล่น Terraform + Kubespray + Ansible = K8S Cluster บน VMware โดยที่เราจะลองใช้งาน NGINX มาใช้งานเป็น Ingress บน Service กัน
โดยทางผู้เขียนทำการปรับปรุง code บน Terraform จากครั้งเก่าให้สามารถสร้าง HA Proxy อีกชุดเพื่อทำหน้าที่ NLB เพื่อทำการกระจายโหลดเข้าไปที่ฝั่ง worker ทั้งหมดของ K8S Cluster เพื่อให้สะดวกให้การสร้าง K8S Cluster มากยิ่งขึ้น โดยเมื่อเราทำการสั่งคำสั่งในการสร้าง K8S Cluster จนเสร็จแล้วเราจะได้หน้าตาของ infrastructure ดังนี้
คราวนี้เราก็พร้อมจะไปลองใช้งาน NGINX กันแล้ว แต่ก่อนที่จะไปลองเล่นกันเรามาดูส่วนประกอบต่างๆ กันก่อนว่ามีอะไรบ้าง
Ingress, Ingress Resource เป็น service ประเภทหนึ่งมีหน้าที่บริการ application ต่างๆ ที่อยู่ภายใน cluster ส่งออกไปข้างนอกโดยที่เราสามารถกำหนด rules ต่างๆได้ดัง
Content-based routing: ใช้ในการกำหนดการว่าหากมีการเรียกแบบนี้ให้ไป service ไหน โดยจะมีการแบ่งออกเป็น 2 แบบอีก
- Host-based routing. เป็นการกำหนดใช้ Hostname (FQDN) เป็นตัวกำหนด routing ว่าเรียกแบบนี้เข้ามาให้ไปที่ services อะไร เช่น images.demo.domain
- Path-based routing. เป็นการกำหนดใช้ pathเป็นตัวกำหนด routing ว่าเรียกแบบนี้เข้ามาให้ไปที่ services อะไร เช่น demo.domain/images
TLS/SSL termination: ใช้ในการทำเรื่อง Offload SSL certificate
NGINX Ingress Controller เป็น application ตัวหนึ่งที่จะรันอยู่ใน cluster ทุกๆ worker node ทำหน้าที่ เป็น reverse proxy และ load balancer โดยรองรับทั้ง NGINX และ NGINX Plus
น่าจะพร้อมลองเล่นจริงๆ แล้วละ เราไปลงเมื่อกันเลยดีกว่า
Clone โปรเจค kubernetes-ingress ของ nginxinc จาก github ซะก่อน
ขั้นตอนที่ 1 — ทำการ clone โปรเจค kubernetes-ingress มาที่เครื่องของเรา
dcadmin@k8s-operation:~$ git clone https://github.com/nginxinc/kubernetes-ingress/
dcadmin@k8s-operation:~$ cd kubernetes-ingress/deployments
ตั้งค่าเกี่ยว RBAC
ขั้นตอนที่ 1 — สร้าง namespace และ service account สำหรับ Ingress controller
dcadmin@k8s-operation:~$ kubectl apply -f common/ns-and-sa.yaml
ขั้นตอนที่ 2 — สร้าง cluster role และทำการ binding cluster role ให้กับ service account ที่สร้างขึ้นมา
dcadmin@k8s-operation:~$ kubectl apply -f rbac/rbac.yaml
สร้าง Common Resources
ขั้นตอนที่ 1 — สร้าง secret เพื่เก็บค่า TLS certificate สำหรับ default server
dcadmin@k8s-operation:~$ kubectl apply -f common/default-server-secret.yaml
ขั้นตอนที่ 2— สร้าง config map สำหรับการตั้งค่า NGINX
dcadmin@k8s-operation:~$ kubectl apply -f common/nginx-config.yaml
Deploy Ingress Controller
ขั้นตอนที่ 1 — สร้าง NGINX Controller โดยใช้ DaemonSet ระบบจะทำการสร้าง Ingress Controller ทุกๆ node บน cluster
dcadmin@k8s-operation:~$ kubectl apply -f daemon-set/nginx-ingress.yaml
ขั้นตอนที่ 2— ตรวจสอบสถานะ Ingress Controller
dcadmin@k8s-operation:~$ kubectl get pods --namespace=nginx-ingress
ผลลัพธ์ที่ได้
NAME READY STATUS RESTARTS AGE
nginx-ingress-2h79f 1/1 Running 0 33s
nginx-ingress-85dfh 1/1 Running 0 33s
nginx-ingress-jzhck 1/1 Running 0 33s
ทดลอง Deploy Application
ขั้นตอนที่ 1 — สร้าง deployment สำหรับ main application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-deployment-main.yaml
nginx-deployment-main.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deploy-main
labels:
run: nginx
spec:
replicas: 2
selector:
matchLabels:
run: nginx-main
template:
metadata:
labels:
run: nginx-main
spec:
containers:
- image: nginx
name: nginx
ขั้นตอนที่ 2— สร้าง service สำหรับ main application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-service-main.yaml
nginx-service-main.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service-main
labels:
run: nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: nginx-main
ขั้นตอนที่ 3 — ตรวจสอบ pods และ service
dcadmin@k8s-operation:~$ kubectl get pods,services
ผลลัพธ์ที่ได้
NAME READY STATUS RESTARTS AGE
pod/nginx-deploy-main-7cc547b6f7-8p2pm 1/1 Running 0 104s
pod/nginx-deploy-main-7cc547b6f7-nqx8p 1/1 Running 0 104sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 35m
service/nginx-service-main ClusterIP 10.233.55.99 <none> 80/TCP 104s
ขั้นตอนที่ 4 — สร้าง ingress resource เพื่อกำหนด rule การ routing โดยทางผู้เขียนจะใช้ Hostname (FQDN) เป็นชื่อ k8s.lab.local ทั้งนี้ผู้ที่ลองทำตามต้องไปทำการแก้ไขไฟล์ hosts ในเครื่องตัวเองให้ระบบ k8s.lab.local ไปที่ VIP ของ HA Proxy ด้วย
dcadmin@k8s-operation:~$ kubectl apply -f ingress-resource.yaml
ingress-resource.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-resource
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: k8s.lab.local
http:
paths:
- backend:
serviceName: nginx-service-main
servicePort: 80
ตรวจสอบ ingress resource
dcadmin@k8s-operation:~$ kubectl get ingress
ผลลัพธ์ที่ได้
NAME HOSTS ADDRESS PORTS AGE
ingress-resource k8s.lab.local 80 73s
ดูรายละเอียดของ ingress resource
dcadmin@k8s-operation:~$ kubectl describe ingress
ผลลัพธ์ที่ได้
Name: ingress-resource
Namespace: default
Address:
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
k8s.lab.local
nginx-service-main:80 (10.233.74.3:80,10.233.88.2:80)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"networking.k8s.io/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"ingress-resource","namespace":"default"},"spec":{"rules":[{"host":"k8s.lab.local","http":{"paths":[{"backend":{"serviceName":"nginx-service-main","servicePort":80}}]}}]}}Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal AddedOrUpdated 72s nginx-ingress-controller Configuration for default/ingress-resource was added or updated
Normal AddedOrUpdated 72s nginx-ingress-controller Configuration for default/ingress-resource was added or updated
Normal AddedOrUpdated 72s nginx-ingress-controller Configuration for default/ingress-resource was added or updated
แก้ไข hosts ที่เครื่องเราเองด้วยนะ
172.16.31.78 k8s.lab.local
ขั้นตอนที่ 5— ทดสอบลองเรียกใช้งาน main application
เย้เรียกเข้าใช้งานได้แล้ว
ทดลอง Deploy Application ตัวต่อไป
ขั้นตอนที่ 1 — สร้าง deployment สำหรับ cat application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-deployment-cat.yaml
nginx-deployment-cat.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: nginx
name: nginx-deploy-cat
spec:
replicas: 2
selector:
matchLabels:
run: nginx-cat
template:
metadata:
labels:
run: nginx-cat
spec:
volumes:
- name: webdata
emptyDir: {}
initContainers:
- name: web-content
image: busybox
volumeMounts:
- name: webdata
mountPath: "/webdata"
command: ["/bin/sh", "-c", 'echo "<title>Welcome to nginx!</title><h1>CAT Application<h1/><p> ____ _ ____<br />/ ___| / \ |_ _|<br />| | / _ \ | |<br />| |___ / ___ \ | |<br /> \____/_/ \_\_|</p>" > /webdata/index.html']
containers:
- image: nginx
name: nginx
volumeMounts:
- name: webdata
mountPath: "/usr/share/nginx/html"
ขั้นตอนที่ 2 — สร้าง service สำหรับ cat application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-service-cat.yaml
nginx-service-cat.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service-cat
labels:
run: nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: nginx-cat
ขั้นตอนที่ 3 — ตรวจสอบ pods และ service
dcadmin@k8s-operation:~$ kubectl get pods,services
ผลลัพธ์ที่ได้
NAME READY STATUS RESTARTS AGE
pod/nginx-deploy-cat-c6cdd4b64-4ltvp 1/1 Running 0 22s
pod/nginx-deploy-cat-c6cdd4b64-h7kc5 1/1 Running 0 22s
pod/nginx-deploy-main-7cc547b6f7-8p2pm 1/1 Running 0 24m
pod/nginx-deploy-main-7cc547b6f7-nqx8p 1/1 Running 0 24mNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 58m
service/nginx-service-cat ClusterIP 10.233.36.73 <none> 80/TCP 22s
service/nginx-service-main ClusterIP 10.233.55.99 <none> 80/TCP 24m
ขั้นตอนที่ 4 — แล้วเราจะเข้าใช้ cat application ยังไงนะ ลองเรียก cat.k8s.lab.local
อย่าลืม แก้ไข hosts ที่เครื่องเราเองด้วยนะ
172.16.31.78 cat.k8s.lab.local
ยังเรียกไม่ได้เพราะยังไม่ได้เพิ่ม rule บน ingress resource
ขั้นตอนที่ 5— เพิ่ม rule สำหรับ cat.k8s.lab.local ให้เรียกไปที่ nginx-service-cat
dcadmin@k8s-operation:~$ kubectl apply -f ingress-resource.yaml
ingress-resource.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-resource
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: k8s.lab.local
http:
paths:
- backend:
serviceName: nginx-service-main
servicePort: 80
- host: cat.k8s.lab.local
http:
paths:
- backend:
serviceName: nginx-service-cat
servicePort: 80
ตรวจสอบ ingress resource
dcadmin@k8s-operation:~$ kubectl get ingress
ผลลัพธ์ที่ได้
NAME HOSTS ADDRESS PORTS AGE
ingress-resource k8s.lab.local,cat.k8s.lab.local 80 29m
ดูรายละเอียดของ ingress resource
dcadmin@k8s-operation:~$ kubectl describe ingress
ผลลัพธ์ที่ได้
Name: ingress-resource
Namespace: default
Address:
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
k8s.lab.local
nginx-service-main:80 (10.233.74.3:80,10.233.88.2:80)
cat.k8s.lab.local
nginx-service-cat:80 (10.233.84.3:80,10.233.88.3:80)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"networking.k8s.io/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"ingress-resource","namespace":"default"},"spec":{"rules":[{"host":"k8s.lab.local","http":{"paths":[{"backend":{"serviceName":"nginx-service-main","servicePort":80}}]}},{"host":"cat.k8s.lab.local","http":{"paths":[{"backend":{"serviceName":"nginx-service-cat","servicePort":80}}]}}]}}Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal AddedOrUpdated 4s (x2 over 32m) nginx-ingress-controller Configuration for default/ingress-resource was added or updated
Normal AddedOrUpdated 4s (x2 over 32m) nginx-ingress-controller Configuration for default/ingress-resource was added or updated
Normal AddedOrUpdated 4s (x2 over 32m) nginx-ingress-controller Configuration for default/ingress-resource was added or updated
ขั้นตอนที่ 5 — ทดสอบลองเรียกใช้งาน cat application อีกครั้ง
เย้เข้าได้แล้ว
ทดลอง Deploy Application อีกสัก 2 ตัว
ขั้นตอนที่ 1 — สร้าง deployment สำหรับ dog application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-deployment-dog.yaml
nginx-deployment-dog.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: nginx
name: nginx-deploy-dog
spec:
replicas: 2
selector:
matchLabels:
run: nginx-dog
template:
metadata:
labels:
run: nginx-dog
spec:
volumes:
- name: webdata
emptyDir: {}
initContainers:
- name: web-content
image: busybox
volumeMounts:
- name: webdata
mountPath: "/webdata"
command: ["/bin/sh", "-c", 'echo "<title>Welcome to nginx!</title><h1>DOG Application<h1/><p>____ ___ ____<br />| _ \/ _ \ / ___|<br />| | | | | | | | _<br />| |_| | |_| | |_| |<br />|___/\___/ \___|</p>" > /webdata/index.html']
containers:
- image: nginx
name: nginx
volumeMounts:
- name: webdata
mountPath: "/usr/share/nginx/html"
ขั้นตอนที่ 2 — สร้าง service สำหรับ dog application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-service-dog.yaml
nginx-service-dog.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service-dog
labels:
run: nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: nginx-dog
ขั้นตอนที่ 3— สร้าง deployment สำหรับ rabbit application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-deployment-rabbit.yaml
nginx-deployment-rabbit.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
run: nginx
name: nginx-deploy-rabbit
spec:
replicas: 2
selector:
matchLabels:
run: nginx-rabbit
template:
metadata:
labels:
run: nginx-rabbit
spec:
volumes:
- name: webdata
emptyDir: {}
initContainers:
- name: web-content
image: busybox
volumeMounts:
- name: webdata
mountPath: "/webdata"
command: ["/bin/sh", "-c", 'echo "<title>Welcome to nginx!</title><h1>RABBIT Application<h1/><p>____ _ ___ ___ ___ ____<br />| _ \ / \ | __ )| __ )_ _|_ _|<br />| |_) | / _ \ | _ \| _ \| | | |<br />| _ < / ___ \| |_) | |_) | | | |<br />|_| \_\/_/ \_\___/|___/___||_|</p>" > /webdata/index.html']
containers:
- image: nginx
name: nginx
volumeMounts:
- name: webdata
mountPath: "/usr/share/nginx/html"
ขั้นตอนที่ 4 — สร้าง service สำหรับ rabbit application
dcadmin@k8s-operation:~$ kubectl apply -f nginx-service-rabbit.yaml
nginx-service-rabbit.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service-rabbit
labels:
run: nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: nginx-rabbit
ขั้นตอนที่ 5— ตรวจสอบ pods และ service
dcadmin@k8s-operation:~$ kubectl get pods,services
ผลลัพธ์ที่ได้
NAME READY STATUS RESTARTS AGE
pod/nginx-deploy-cat-c6cdd4b64-4ltvp 1/1 Running 0 18m
pod/nginx-deploy-cat-c6cdd4b64-h7kc5 1/1 Running 0 18m
pod/nginx-deploy-dog-5dd6cb45c7-9qqrn 1/1 Running 0 65s
pod/nginx-deploy-dog-5dd6cb45c7-fz6wn 1/1 Running 0 65s
pod/nginx-deploy-main-7cc547b6f7-8p2pm 1/1 Running 0 42m
pod/nginx-deploy-main-7cc547b6f7-nqx8p 1/1 Running 0 42m
pod/nginx-deploy-rabbit-5557fdf458-vnkxm 1/1 Running 0 61s
pod/nginx-deploy-rabbit-5557fdf458-xsq8l 1/1 Running 0 61sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 76m
service/nginx-service-cat ClusterIP 10.233.36.73 <none> 80/TCP 18m
service/nginx-service-dog ClusterIP 10.233.36.210 <none> 80/TCP 66s
service/nginx-service-main ClusterIP 10.233.55.99 <none> 80/TCP 42m
service/nginx-service-rabbit ClusterIP 10.233.4.255 <none> 80/TCP 61s
ขั้นตอนที่ 6 — แก้ไข hosts ที่เครื่องเราเพื่อเพิ่ม hostname ที่เราจะเรียกเข้าไปใช้งาน service
172.16.31.78 dog.k8s.lab.local
172.16.31.78 rabbit.k8s.lab.local
ขั้นตอนที่ 7— เพิ่ม rule สำหรับ dog.k8s.lab.local และ rabbit.k8s.lab.local ให้เรียกไปที่ nginx-service-dog และ nginx-service-rabbit
dcadmin@k8s-operation:~$ kubectl apply -f ingress-resource.yaml
ingress-resource.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-resource
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: k8s.lab.local
http:
paths:
- backend:
serviceName: nginx-service-main
servicePort: 80
- host: cat.k8s.lab.local
http:
paths:
- backend:
serviceName: nginx-service-cat
servicePort: 80
- host: dog.k8s.lab.local
http:
paths:
- backend:
serviceName: nginx-service-dog
servicePort: 80
- host: rabbit.k8s.lab.local
http:
paths:
- backend:
serviceName: nginx-service-rabbit
servicePort: 80
ตรวจสอบ ingress resource
dcadmin@k8s-operation:~$ kubectl get ingress
ผลลัพธ์ที่ได้
NAME HOSTS ADDRESS PORTS AGE
ingress-resource k8s.lab.local,cat.k8s.lab.local,dog.k8s.lab.local + 1 more... 80 41m
ดูรายละเอียดของ ingress resource
dcadmin@k8s-operation:~$ kubectl describe ingress
ผลลัพธ์ที่ได้
Name: ingress-resource
Namespace: default
Address:
Default backend: default-http-backend:80 (<none>)
Rules:
Host Path Backends
---- ---- --------
k8s.lab.local
nginx-service-main:80 (10.233.74.3:80,10.233.88.2:80)
cat.k8s.lab.local
nginx-service-cat:80 (10.233.84.3:80,10.233.88.3:80)
dog.k8s.lab.local
nginx-service-dog:80 (10.233.74.4:80,10.233.84.4:80)
rabbit.k8s.lab.local
nginx-service-rabbit:80 (10.233.74.5:80,10.233.88.4:80)
Annotations:
kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"networking.k8s.io/v1beta1","kind":"Ingress","metadata":{"annotations":{},"name":"ingress-resource","namespace":"default"},"spec":{"rules":[{"host":"k8s.lab.local","http":{"paths":[{"backend":{"serviceName":"nginx-service-main","servicePort":80}}]}},{"host":"cat.k8s.lab.local","http":{"paths":[{"backend":{"serviceName":"nginx-service-cat","servicePort":80}}]}},{"host":"dog.k8s.lab.local","http":{"paths":[{"backend":{"serviceName":"nginx-service-dog","servicePort":80}}]}},{"host":"rabbit.k8s.lab.local","http":{"paths":[{"backend":{"serviceName":"nginx-service-rabbit","servicePort":80}}]}}]}}Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal AddedOrUpdated 64s (x3 over 42m) nginx-ingress-controller Configuration for default/ingress-resource was added or updated
Normal AddedOrUpdated 64s (x3 over 42m) nginx-ingress-controller Configuration for default/ingress-resource was added or updated
Normal AddedOrUpdated 64s (x3 over 42m) nginx-ingress-controller Configuration for default/ingress-resource was added or updated
ขั้นตอนที่ 8— ทดสอบลองเรียกใช้งาน dog application และ rabbit application
หมายเหตุ ในส่วนของ Path-based routing ทางผู้เขียนได้ทดลองแล้วพบว่าไม่สามารถทำงาน และยังสาเหตุไม่เจอเพราะอะไร หากเจอสาเหตุ และวิธีการแก้ไขแล้วจะนำมาอัพเดตในบทความครับ
สุดท้ายการลองเล่นครั้งก็เป็นเพียงการใช้งานเบื้องต้น ตัว nginx ยังไม่ลูกเล่นอีกมากมายที่ให้เราใช้งาน แต่ต้องเรียนรู้ และหัดใช้งานกันต่อไป
สุดท้ายแล้วหากท่านไหนหลงเข้ามาอ่านแล้วเห็นว่าผมยังเข้าใจผิดในเรื่องใด สามารถให้ข้อมูลเพิ่มเติมได้ครับ
Ref: https://docs.nginx.com/nginx-ingress-controller/overview/