Basic Kubernetes Part 6 - Ingress

Basic Kubernetes Part 6 - Ingress

ในการติดต่อกันระหว่าง Client Server ในชั้น Application Layer นั้นเราจะใช้ IP กับ Port ซึ่ง Protocol HTTP ใช้ port 80 , HTTPS ใช้ port 443 ทุกอย่างเหมือนไม่มีปัญหาอะไร แต่ปัญหาเกิดขึ้นเมื่อเครื่องเรา deploy application หลายๆ appliaction ซึ่งหลายๆ application นั้นใช้ protocol http , https ซึ่งเราไม่สามารถให้ 2 pod ใช้ Port เดียวกันได้ จะให้ใช้ Port แบบ 32010 , 32020 ก็ไม่สะดวกเพราะ คนใช้งานต้องกำหนด Port มาใน URL ซึ่งน่าจะสร้างความลำบากให้ผู้ใช้น่าดู

รูปภาพ App หลายๆตัวอยู่บนเครื่องเดียวกัน

ถ้าเป็นการ Deploy แบบไม่ใช่ k8s นั้นเรามีวิธีทำคือ เราก็สร้าง Reverse proxy (nginx , apache) ขึ้นมาแล้ว Run บน Port 80 , 443 จากนั้นก็ให้มันรับ Request แล้ว routing ตามกฏไปหา Application ที่ต้องการ

รูปภาพ App ที่มี nginx อยู่ที่ port 80, 443

คราวนี้ถ้าเราอยากทำแบบนี้บน k8s ล่ะจะทำยังไง จากความรู้ที่เรามีเราไม่สามารถทำได้ เพราะตอนเราสร้าง service แบบ NodePort ไม่สามารถตั้งค่า Port เป็น 80 , 443 ได้ ดังนั้นเราไม่สามารถเอา nginx หรือ apache ไปวางได้ แล้วเราจะทำยังไงล่ะ

จากความต้องการแบบนี้ตัว k8s ได้กำหนดสิ่งที่เรียกว่า Ingress ขึ้นมามาให้เราเพื่อทำตามความต้องการที่อยากได้ โดย Ingress นี้เป็นแค่ Spec ของ Configfile เท่านั้นไม่มีตัวที่ทำงานจริง ตัวที่ทำงานจริงคือ Ingress controller ซึ่ง Ingress controller มีให้เราใช้มากมายตัวอย่างเช่น

โดยใครอยากใช้ Ingress controller ตัวไหนก็ลองเข้าไปดูวิธี Install ที่ Document ของแต่ละเจ้าได้เลย ส่วนใครที่ใช้ VM ของผมนั้น ตัว k3s เขาลงตัว Traefik Ingress controller มาให้แล้ว ดังนั้นไม่ต้องลงเพิ่มนะครับ

ลองสร้าง Ingress กัน

ตัวอย่างไฟล์ Config สามารถได้ดูได้ที่ Link

เตรียม Application (Pod)

อันดับเแรกมาทำการสร้าง Application (Pod) บน k8s กันก่อน โดยสร้างไฟล์ : nginx-whoami-app.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
template:
metadata:
name: nginx-pod
labels:
app: nginx
spec:
containers:
- name: nginx-container
image: nginx:1.25.1
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami-deployment
spec:
selector:
matchLabels:
app: whoami
template:
metadata:
name: whoami-pod
labels:
app: whoami
spec:
containers:
- name: whoami-container
image: traefik/whoami
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 32010
type: NodePort
---
apiVersion: v1
kind: Service
metadata:
name: whoami-service
spec:
selector:
app: whoami
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 32020
type: NodePort

จากนั้นทดสอบเปิด Browser แล้วเข้าไปที่ URL

จากภาพจะเห็นว่าเราสามารถเข้าถึง nginx ผ่าน port 32010 และ whoami ผ่าน port 32020

Set file host

เนื่องจากเราไม่ได้จด Domain name : nginx-application.com และ whoami-application.com ไว้ (ต่อให้จดได้ก็จะไม่ได้ ip ตรงกันเพราะแต่ละเครื่องที่ run k8s มี ip ไม่เหมือนกัน) ดังนั้นจะไม่สามารถ resolve domain name ได้ แต่เราสามารถแก้ปัญหานี้ได้โดยการกำหนดค่าในไฟล์ : hosts ซึ่งไฟล์ host นี้แต่ละ os อยู่ไม่เหมือนกัน โดยแต่ละ os ไฟล์จะอยู่ที่

  • windows : c:\Windows\System32\Drivers\etc\hosts
  • linux : /etc/hosts
  • macOs : /private/etc/hosts

เมื่อเปิดไฟล์ให้และเพิ่มข้อมูลลงไปคือ

1
2
<ip เครื่องที่ run k8s> nginx-application.com
<ip เครื่องที่ run k8s> whoami-application.com

ซึ่งของผมจะได้เป็น

1
2
192.168.156.101 nginx-application.com
192.168.156.101 whoami-application.com

ทำการทดสอบลองใช้คำสั่ง ping ไปที่ nginx-application.com , whoami-application.com ว่าทำการ resolve ip เป็น ip ที่ตั้งไว้หรือไม่

1
2
ping nginx-application.com
ping whoami-application.com

สร้าง Ingress

สร้างไฟล์ basic-ingress.yml จากนั้นสั่ง apply file

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: basic-ingress
spec:
rules:
- host: "nginx-application.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
- host: "whoami-application.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: whoami-service
port:
number: 80

จากนั้นลอง get และ describe ingress ดูครับ

จากภาพจะเห็นว่ามี Ingress ชื่อ basic-ingress ขึ้นมาและมีการบอก host และ path ซึ่งตรงกับที่เรากำหนดในไฟล์ อีกทั้งมีชื่อ service คือ nginx-service , whoami-service

จากนั้นลองเข้า browser แล้วเปิด URL

  • nginx-application.com
  • whoami-application.com


จากภาพจะเห็นว่าสามารถเปิดหน้าได้ตรงตามความต้องการของเราคือ เข้า nginx-application.com ก็ไปหา nginx เข้า whoami-application.com ก็ไปหา whoami ซึ่งนั่นแปลว่า Ingress ของเราทำงานได้ตรงตามความต้องการของเราแล้ว

อธิบายไฟล์ basic-ingress.yml

ในส่วนของ apiVersion , kind , metadata ไม่ขอลงรายละเอียดเพราะเหมือนกับตัวอื่นๆที่ผ่านมา

rules

ตรงนี้จะเป็นการประกาศเกี่ยวกับ rule (มีได้หลาย rule) ที่ใช้ในการ route ของ Ingress (Reverse Proxy) ถ้าเทียบง่ายๆก็เหมือน file nginx.conf ของ nginx ที่เรากำหนดว่ามาด้วย host นี้จะให้ส่งไปที่ไหนต่อ โดย rule จะเป็นไปตามรายละเอียดต่อไปนี้

1
2
3
4
5
6
7
8
9
10
- host: "nginx-application.com" 
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80

host

ตรงส่วนนี้จะเป็นการบอกกฏว่า request ที่มาต้องมีค่า host ตรงกับ nginx-application.com

http

ตรงนี้เป็นการบอกว่าถ้าเป็น protocol http

paths

ตรงนี้จะเป็นการบอกต่อว่าเมื่อเข้ามาด้วย http แล้วจะใช้ path อะไรในการ map (มีได้หลาย path)

path

1
2
3
4
5
6
7
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80

ตรงนี้จะเป็นการบอกรายละเอียดแล้วว่าต้องตรงด้วย path อะไร ลักษณะของการ map เป็นแบบไหน ถ้า map ตรงแล้วจะให้ยิงไปที่ service ไหน port อะไร

จากตัวอย่างจะเป็นการว่า ถ้า map ที่ path : / การ map เป็นแบบ Prefix คือถ้าขึ้นต้นด้วย / จะถือว่าตรงตามเงื่อนไขหมด ซึ่งถ้าตรงตามเงื่อนไขทั้งหมดเนี่ยให้ route request นี้ไปหา service : nginx-service ที่ port 80

สรุปการ config rule

1
2
3
4
5
6
7
8
9
10
- host: "nginx-application.com" 
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80

rule นี้มีความหมายคือ request ที่ยิงเข้ามามีค่า host == nginx-application.com แล้วมี path ที่ขึ้นต้นด้วย / จะถูก route request ไปที่ nginx-service port 80

สิ่งที่ต้องอ่านเพิ่มเติม

การตั้งค่าเพิ่มเติม Ingress Controller

ที่อยู่ในตัวอย่างทั้งหมดของเรานั้นเป็น Ingress กลางๆที่ k8s กำหนดขึ้นให้ใช้ได้กับทุก IngressController ซึ่ง Rule ที่ตั้งได้นั้นมีจำกัดมากๆแค่ host กับ path จะ route ด้วยเงื่อนไขอื่นๆ เช่น header ที่เรากำหนดเป็นต้น หรือ เวลา Route แล้วให้เราทำการแก้ไข path เพิ่ม header ที่ต้องการ อันนี้ไม่สามารถทำได้ผ่านการประกาศแบบนี้

แต่ถามว่าทำได้ไหม ก็ต้องบอกว่าสามารถทำได้ครับ แต่ต้องเพิ่มการประกาศตาม Ingress controller ที่คุณเลือกใช้ เช่น

  • Nginx : เราสามารถใช้ Feature ต่างๆแบบที่ nginx reverse proxy ทำได้แต่ต้องประกาศในส่วนของ Annotation โดยสามารถดูรายละเอียดการ config ได้ที่ Link
  • Traefik : ถ้าอยากใช้ Feature ที่มากกว่าที่ k8s กำหนดต้องประกาศตัว IngressRoute ของ Traefik เพิ่ม โดยสามารถดูรายละเอียดการ config ได้ที่ Link และดู Annotation ได้ที่ Link
  • Contour : ตัวนี้จะคล้ายๆกับตัว Traefik ที่ต้องทำการประกาศ Ingress ของตัว Contour เองเพื่อใช้ Feature ของ Contour โดยสามารถดูรายละเอียดการ Config ได้ที่ Link

การ Set ssl ให้กับ Ingress

ในตัวอย่างที่เราได้ทดลองนั้น เราทดลองสร้าง Ingress ที่เป็นแบบ http แต่ในการทำงานจริงๆนั้นคนที่ใช้งานจริงๆนั้นใช้งานแบบ https นั้นจะต้อง set อย่างไร โดยสามารถดูรายละเอียดการ setting ได้ที่ : Link

สรุป

สำหรับตอนนี้เราได้ทำความรู้จักกับ Ingress ว่าคืออะไร เอาไปใช้อะไรกับกรณีไหน rule ที่เราพอตั้งให้ได้คืออะไร ซึ่งถ้าใครอ่านมาตั้งแต่ตอนแรกจนถึงตอนนี้ก็น่าจะสามารถ Deploy application ขึ้น k8s ไปใช้งานได้แล้ว (แต่ไม่ได้ดีมาก และไม่ถูกต้องตาม best practice ) แต่อ่านอย่างเดียวอาจจะไม่เข้าใจ ตอนหน้าเรามาลองทำการ Deploy application สักตัวกันว่าถ้าต้องทำโจทย์แบบนี้แล้วเราต้องทำอะไรบ้าง