k8s: เสริมพลัง ingress ด้วย gce l7 + nginx

เมื่อเราใช้ Kubernetes ใน GKE ถ้าไม่ใช้ HTTP(S) Load Balancer (L7) ถือว่าบาปมาก

เพราะอะไร ?

ถ้าเราใช้ nginx แล้วเปิด service type=LoadBalancer เราจะได้ TCP Load Balancer ได้ IP ตาม region ที่เราสร้าง cluster ไว้ เช่น เราสร้าง cluster ไว้ที่ asia-southeast1 เราก็จะได้ ip ที่ region asia-southeast1 เวลามีคนเข้าเว็บเรา request จะวิ่งผ่าน internet มาที่ cluster ของเรา (มาที่ TCP Load Balancer ใน region เดียวกับ cluster)

แต่ถ้าเราสร้างเป็น ingress ที่ class=gce เราจะได้เป็น HTTP(S) Load Balancer แทน ข้อดีคือ เราจะได้ Global IP (Anycast) เพราะ Load Balancer จะจัดการเรื่อง http, https ให้ เวลามีคนเข้าเว็บเรา ก็จะวิ่งไปที่ load balancer ที่ใกล้ที่สุด (ไม่ใช่ region ของ cluster) แล้ว request จะวิ่งจาก load balancer มาหา cluster ของเรา ด้วย internal network ของ google ที่เร็วกว่า internet ของคนใช้หลายเท่า ทำให้ latency เว็บเราเวลาเข้าจากต่างประเทศก็จะน้อย และสามารถเปิด CDN เพื่อ cache response ที่ load balancer ได้เลย ทำให้เว็บเราเข้าจากทั่วโลกใช้เวลาไม่ถึง 100ms ถ้ามี cache แต่ถ้าไม่มี cache ก็ยังใช้เวลาน้อยอยู่ดี เพราะวิ่งผ่าน internal network ของ google


แต่ gce-l7 ก็ไม่ได้มีข้อดีเสมอไป ข้อเสียก็มี เช่น

  • สร้างนาน,​ ลบนาน, แก้ path นาน กว่า load balancer จะ update
  • gzip ไม่ได้
  • ใส่ header เพิ่มไม่ได้
  • redirect http => https ไม่ได้

จะเห็นว่าข้อเสียพวกนี้ เราสามารถเอา nginx ingress controller มาใช้ได้เลย เพราะเวลาเราแก้ ingress หรือ config มันจะ reload nginx ให้ทันที!!!

แล้วเราจะสร้าง ingress ยังไงดีหล่ะ ?

Internet
=> gce-ingress l7
=> nginx-ingress (NodePort)
=> backend-service (ClusterIP)


Step 1. เราก็สร้าง nginx-ingress-controller ก่อน

  1. สร้าง ConfigMap เพื่อ config nginx
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-ingress
labels:
app: nginx-ingress
data:
body-size: 5m
enable-vts-status: "true"
hsts: "false" # ถ้าจะใช้ HTTPS อยู่แล้วก็ควรใช้ HSTS
server-tokens: "false"
use-gzip: "true"

2. สร้าง Service สำหรับ nginx

apiVersion: v1
kind: Service
metadata:
name: nginx-ingress
labels:
app: nginx-ingress
spec:
type: NodePort
selector:
app: nginx-ingress
ports:
- name: http
port: 80

3. สร้าง Deployment ของ nginx-ingress-controller

apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: nginx-ingress
labels:
app: nginx-ingress
spec:
replicas: 3
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
spec:
containers:
- name: nginx-ingress-controller
image: gcr.io/google-containers/nginx-ingress-controller:0.9.0-beta.15
ports:
- containerPort: 80
args:
- /nginx-ingress-controller
- --default-backend-service=kube-system/default-http-backend
- --configmap=$(POD_NAMESPACE)/nginx-ingress
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
livenessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 80
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
failureThreshold: 3
httpGet:
path: /healthz
port: 80
scheme: HTTP
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1

4. สร้าง ingress ให้ nginx

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx # ห้ามลืมบรรทัดนี้!!!
kubernetes.io/tls-acme: "true" # ถ้าใช้ kube-lego ให้ไว้ใน nginx
ingress.kubernetes.io/limit-rps: "10" # ใส่ rate-limit ด้วยก็ดี
name: nginx-ingress
spec:
rules:
# เพิ่ม backend services ที่นี่ได้เลย
- host: example.com
http:
paths:
- backend:
serviceName: example
servicePort: 8080
tls:
- secretName: gce-ingress # ค่านี้ให้จำไว้ เพราะจะเอาไปใช้ใน gce-ingress
# ใส่ tls certificate ที่นี้,​ kube-lego ก็ใส่ที่นี่
hosts:
- example.com

5. สร้าง ingress ให้ gce

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: gce # ห้ามลืมบรรทัดนี้!!!
name: gce-ingress
spec:
backend: # ส่งทุก requests เข้าไปที่ nginx
serviceName: nginx-ingress
servicePort: 80
tls:
- secretName: gce-ingress # ตั้งชื่อเดียวกันกับ secretName ใน nginx

แล้วเราก็จะได้ ข้อดีของทั้ง gce l7 กับ nginx มารวมกันแล้ว เย่~~~!!!

ข้อดีที่จะได้

  • ได้ Global IP (Anycast)
  • เปิด CDN ได้
  • reload เร็ว เพราะเราแก้ backend/config ผ่าน nginx ingress
  • gzip ได้ผ่าน nginx
  • ใส่ header เพิ่มได้ผ่าน nginx (เช่น HSTS)
  • nginx จะ redirect http ไป https ให้ด้วย (ดูจาก X-Forwarded-Proto ที่ gce ส่งมา กับดูว่ามี Host ใน tls ไหม)
  • ดู logs ได้ทั้ง HTTP(S) Load Balancer ทั้ง container ของ nginx
  • ไม่มีปัญหาเรื่อง real-ip (เพราะ nginx จะอ่านจาก X-Forwarded-For ที่ gce ส่งมา)
  • ingress ของ kube-lego ไม่มาปนกันกับใน gce ingress

สามารถดู yaml เพิ่มเติมได้จาก https://github.com/acoshift/k8s-app/tree/master/gce-nginx-ingress