มีเวลาว่าง เลยมาเขียนบทความเกี่ยวกับพื้นฐาน Kubernetes ไว้สักหน่อย

Kubernetes แบบสั้นๆ

มัน Container Orchestration System คือ ระบบจัดการ Container อัตโนมัติ ซึ่งภายใน Container จะมี Application รันอยู่โดนจะทำงานร่วมกัน


Kubernetes Cluster

ภายใน K8S Cluster จะประกอบไปด้วย ส่วนที่เป็น Worker Node และ Master Node

Kubernetes Cluster

Worker Node

กลุ่มของ Worker Node นึกถึง Computer 1 ตัว หรือมากกว่านั้น ที่ติดตั้ง Kubernetes ไว้

โดยภายในจะประกอบด้วย 2 ส่วน คือ

  1. Kube Management: ที่ทำหน้าที่ในการบริหารจัดการภายใน Node นั้นๆ โดยจะมีการทำงานย่อยๆ อยู่ 3 ตัว คือ
    1. Kubelet (ทำหน้าที่ติดต่อกับ APIs หลัก)
    2. Kube Proxy (ทำหน้าที่จัดการกับ Networks)
    3. Docker (ทำหน้าที่ Run Container)
  2. Application: เป็นส่วนการทำงานของระบบที่เราต้องการติดตั้ง​ ซึ่งจะประกอบไปด้วย Pod หลายๆ Pod ขึ้นอยู่กับว่าเราใช้งานกับอะไร และต้องการใช้งานแบบใน ภายใน Pod ก็จะถูกรันผ่าน Container อีกทีนึง
    1. ใน Node สามารถมี Pod ได้มากกว่า 1 Pod

Master Node

มีหน้าที่บริหารจัดการ Worker Node โดยเราในฐานะที่เป็น Developer ก็จะทำงานผ่านการใส่คำสั่งและส่งไปที่ Master Node ให้มันทำงาน โดยใน Master Node จะมีส่วนประกอบ 3 ส่วนด้วยกัน คือ

  1. APIs Server: ทำหน้าที่รับคำสั่งจาก Developer และ API Server จะติดต่อกับ Kubelet เพื่อบอกว่าต้องการให้ทำอะไร
  2. Scheduler: ทำหน้าที่ตรวจสอบและจัดการ Worker Node เช่น ถ้าเราต้องการเพิ่ม Application ใหม่เข้าไป Scheduler จะทำการตรวจสอบว่า Pod ที่จะเพิ่มใหม่เข้าไป จะเพิ่มเข้าไปใน Worker Node ตัวไหน อาจจะไว้ที่เดียวกัน หรือแยก Node ใหม่เข้าไปก็ได้
  3. Controller Managements: ทำหน้าที่สอดส่องทรัพยากร เช่น ดูว่าระบบยังทำงานได้เหมือนเดิมหรือเปล่า มี Pod หายหรือไม่ หรือ Resource ไม่เพียงพอ ถ้าหากตัว Controller Manager พบ ก็จะแจ้งไปยัง Scheduler เพื่อทำการสร้าง/ลด Pod ใหม่

ลองเรียนรู้และสร้างมันไปด้วยกัน

เรามาลองสร้าง Kubernetes ไปด้วยกัน ซึ่งบทความนี้ขอข้ามเรื่องของการติดตั้ง Docker กับ Kubernetes ไปเลย

สมมติว่าเราออกแบบระบบไว้ดังนี้


kubectl

ก่อนจะเริ่มสร้าง มาเรียนรู้คำสั่งพื้นฐานของ Kubectl กันก่อน

#ดู Resource ทั้งหมด
kubectl get all

#ดู Pods ทั้งหมด
kubectl get pods

#ใช้งานไฟล์
kubectl apply -f myfile.yml

#ดู Services ทั้งหมด
kubectl get services

#ดู Log ของ Pod
kubectl logs mypod

#ลบ Pod
kubectl delete pod mypod

#ลบ Service
kubectl delete service myservice

#ลบ Pod/Service ทั้งหมด
kubectl delete pod/service --all
kubectl delete all --all

สามารถเข้าไปดูทั้งหมดได้ที่ https://kubernetes.io/docs/reference/kubectl/quick-reference


Pods

เป็นหน่วยที่เล็กที่สุดใน K8S ซึ่งภายในก็จะมี Container ประกอบอยู่ เริ่มสร้าง Pod สำหรับ Web App กัน

สร้างไฟล์ sample-pod.yml ของเราขึ้นมา

apiVersion: v1
# ประเภท Pod
kind: Pod 
# ชื่อ Pod
metadata:
  name: client-pods
  labels:
    app: client
# ตั้งค่า Pod
spec:
  containers:
    - name: client
      image: dekcode/todo-app:v4

sample-pod.yml

ลอง run ไฟล์ sample-pod.yml ในการสร้าง Pod

kubectl apply -f sample-pod.yml

create new pod by pod file

เมื่อสร้าง pod แล้ว ลองดูว่ามีการสร้าง pod ใหม่ขึ้นมาไหมด้วย

kubectl get pods

show all pod

โดยตัว application ของเราจะ run อยู่บน pod :80 ถ้าหากเราลองเข้าไปดู http://localhost:80 ก็จะเข้าไม่ได้ เนื่องจากตัว pod นั้นเป็นระบบปิด จะต้องไปเปิดให้สามารถเข้าถึงจากภายนอกได้ก่อน

ถ้าหากเราอยากดูว่า pod ของเราทำงานได้ไหม ก็ให้ใช้คำสั่ง logs เข้าไปดูก่อนก็ได้

kubectl logs client-pod

show logs of client-pod


Deployment

เป็นการบริหาร pod โดยให้ระบบ Kubernetes ทำการควบคุมให้ โดยการสร้าง deployment ขึ้นมา และให้ deployment จัดการให้เรา

สร้างไฟล์ client-deployment.yml แล้วเขียน spec ลงไป

apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: client
  template:
    metadata:
      name: client-pods
      labels:
        app: client
    spec:
      containers:
        - name: client
          image: dekcode/todo-app:v4

client-deployment.yml

อธิบายคำสั่ง:

  • replicas: จำนวน pod ที่เราต้องการให้สร้างขึ้น ในไฟล์ตัวอย่างนี้ให้สร้าง pods ขึ้นมา 3 ตัว
  • selector: สำหรับทำงานร่วมกับ service โดยภายในจะใช้ matchLabels เพื่อ match เข้าหา pod หลายๆ ตัว
  • template: เป็นการกำหนด spec สำหรับ pod ที่เราต้องการสร้าง (สังเกตว่าจะเป็นตัวเดียวกับตัว sample-pod.yml ที่เข้าเขียนขึ้นมา)

แต่ก่อน run ไฟล์นี้ ให้ลบ pods เก่าที่เราเคยสร้างขึ้นมาก่อน

kubectl delete all --all

delete all

จากนั้นลอง สร้าง Pod ใหม่กัน

kubectl apply -f client-deploy.yml

create new pod by deployment

เท่านี้ pod ของเราก็จะถูกสร้างขึ้นแล้ว ถ้าลง get pod ดู ก็จะพบว่ามี Pod ใหม่เกิดขึ้นมา 3 ตัว

kubectl get pods

get all pods

NAME                                 READY   STATUS    RESTARTS   AGE
client-deployment-666d95b674-25lfq   1/1     Running   0          22s
client-deployment-666d95b674-jkmwx   1/1     Running   0          22s
client-deployment-666d95b674-rjjrg   1/1     Running   0          22s

result of pod lists

ทีนี้ลอง get all ดู

kubectl get all

ก็จะได้ผลลัพธ์ประมาณนี้

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2m31s

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/client-deployment   3/3     3            3           2m19s

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/client-deployment-666d95b674   3         3         3       2m19s

result of get all

  • service/kubernetes เป็น service ของ Kubernetes ที่สร้างขั้นมาเอง
  • deployment.apps/client-deployment เป็น deployment ที่เราสร้างขึ้นมา บอกว่า มีทั้งหมดกี่ Pod และ Available อยู่เท่าไหร่
  • replicaset.apps/client-deployment-666d95b674 บอกถึง replicas set ที่สร้างขึ้นมา

อย่างที่บอกไว้ว่า ตัว deployment จะเป็นตัวจัดการ pod ทั้งหมดให้

หากเราลองลบ pod ออกสักตัวนึงที่ชื่อ client-deployment-666d95b674-rjjrg

kubectl delete pods client-deployment-666d95b674-rjjrg

delete pod

จากนั้นลอง kubectl get all มาดูใหม่ ก็จะพบว่า pods ยังมีอยู่ 3 ตัวเท่าเดิม แต่ตัว pod ที่ชื่อ client-deployment-666d95b674-rjjrg หายไปแล้ว และมี pod ใหม่เข้ามาแทน pod/client-deployment-666d95b674-jlf8q

NAME                                     READY   STATUS    RESTARTS   AGE
pod/client-deployment-666d95b674-25lfq   1/1     Running   0          8m53s
pod/client-deployment-666d95b674-jkmwx   1/1     Running   0          8m53s
pod/client-deployment-666d95b674-jlf8q   1/1     Running   0          9s

result show all pod after pod deleted

นี่คือความสามารถของ deployment ของ Kubernetes


Services

ใน services จะประกอบไปด้วย 4 ตัว

  • Node Port: ทำหน้าที่เปิด port ให้สามารถเข้าใช้งานจากภายนอกได้ user สามารถเข้ามาใช้งาน application ของเราผ่าน port ที่เราเปิดไว้
  • Cluster IP: จะคล้ายๆ node port แต่จะเปิดให้ใช้ภายใน Kubernetes Clusters ของเราเท่านั้น จะไม่สามารถเข้าใช้งานจากภายนอกได้
  • Load Balancer: มีหน้าที่ทำการ load balance ระหว่าง cluster ซึ่งส่วนนี้ตอนใช้งานจริงอาจจะต้องดูหน่อยว่า cloud service ที่เราใช้งานอยู่รองรับหรือเปล่า (ตัวขอข้ามไปก่อน ไม่เอาเข้ามาใส่ไว้ใน)
  • External (Ingress): เป็นการจัดการ traffic ที่เรียกใช้จากภายนอก (เดี๋ยวค่อยลงรายละเอียดในหัวข้อ Ingress อีกทีหนึ่ง)

Node Port

ใช้สำหรับเปิด pod ที่ง่ายที่สุด เพื่อให้ผู้ใช้หรือ application อื่นๆ เข้าถึง pod ภายใน cluster ของเราได้

แต่จะสามารถเปิดได้แค่ 1 pod ต่อ 1 application เท่าไหร่

โดย node port นั้นมีคุณสมบัติคล้ายกับ Load balance

เช่น เรามีผู้ใช้งาน เข้ามาใช้งานผ่านทาง node port ของเรา มันก็จะดูว่าเปิด port นั้นไว้หรือเปล่า ถ้าเปิดไว้แล้วมันจะให้เราเข้าถึงได้ และถ้าหากเรามีหลายๆ pod ตัว node port ก็จะ route ไปหา pod ทุกๆ ตัวที่เราสร้างไว้ เป็นต้น

มาลองกันเลยดีกว่า... เริ่มจากสร้างไฟล์ client-service.yml

apiVersion: v1
kind: Service
metadata:
  name: client-service
spec:
  type: NodePort
  selector:
    app: client
  ports:
    - protocol: TCP
      # port ภายใน container
      port: 80
      # application port ที่ใช้
      targetPort: 80
      # ใช้ได้ตั้งแต่ 30000 - 32767
      nodePort: 30001

client-service.yml

อธิบายคำสั่ง

  • selector -> app: เป็นการอ้างอิงถึงตัว labels ของ app ที่เราตั้งชื่อไว้ใน client-deployment.yml ซึ่งในตัวอย่างชื่อ client
  • type : เป็นตัวบอกว่าเรากำลังจำใช้ service ประเภทไหน ซึ่งในตัวอย่างนี้เราจะใช้ NodePort
  • port: เป็น port ภายในของตัว container
  • targetPort: เป็น port ภายในของ Application
  • nodePort: เป็น port ภายนอกที่อยากให้เข้าถึง ซึ่งเราสามารถใช้งานได้ ตั้งแต่ 30000 - 32767

จากนั้นลอง run คำสั่งกันดู โดยจะใช้วิธีการสร้างไฟล์หลายๆ ไฟล์ พร้อมกัน โดยการระบบ . แทนการอ้างอิงไฟล์แบบไฟล์เดียว

kubectl apply -f .

create deployment and service

ซึ่งเมื่อใช้คำสั่งนี้แล้วจะเป็นการเรียกใช้งานไฟล์ทุกไฟล์ที่อยู่ภายใต้ directory นั้น

deployment.apps/client-deployment created
service/client-service created
pod/client-pods created

result of create all file in directory

ลอง kubectl get all ดู ก็จะพบว่า มีตัว service/client-service ที่เป็น NodePort ถูกสร้างขึ้นมาแล้ว


NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/client-service   NodePort    10.111.17.240   <none>        80:30001/TCP   79s
service/kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP        18m

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/client-deployment   3/3     3            3           79s

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/client-deployment-666d95b674   3         3         3       79s

result of get all that show new service

NodePort ที่ถูกสร้างมานั้นจะมี Cluster IP ที่เป็น IP ที่ใช้ภายใน Node ของ Kubernetes และมี port ที่เพิ่มให้ผู้ใช้ได้เข้ามาใช้งานคือ 30001ลองเข้าไปดูได้ผ่านทาง http://localhost:30001/

Cluster IP

ใช้สำหรับเปิดการสื่อสารระหว่าง Pod โดยการสื่อสารจะเป็นเฉพาะภายใน Node เท่านั้น โดยทั้งระบบจะมี DNS Service เป็นผู้บริหาร IP ภายในให้อัตโนมัติ

เราจะสร้างตามนี้

เราจะใช้ตัว Cluster IP ในการสื่อสารระหว่าง API กับตัว Mongo DB

สร้างไฟล์ server-deployment
เริ่มต้นที่เราจะสร้างไฟล์ server-deployment.yml ใหม่ขึ้นมาก่อน ซึ่งไฟล์นี้จะเป็นไฟล์สำหรับ api ของเรา

apiVersion: apps/v1
kind: Deployment
metadata:
  name: server-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: server
  template:
    metadata:
      name: server-pods
      labels:
        app: server
    spec:
      containers:
        - name: server
          image: dekcode/todo-api:v5
---
apiVersion: v1
kind: Service
metadata:
  name: server-service-nodeport
spec:
  type: NodePort
  selector:
    app: server
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30002

server-deployment.yml

จากไฟล์ได้บนถ้าหากเราต้องการสร้าง NodePort หรืออื่นๆ แทนทีจะสร้างไฟล์แยก เราสามารถรวมกันไว้ในไฟล์เดียวกันได้เลย โดยขั้นด้วย ---

ปรับไฟล์ client-deployment
ก่อนที่จะไปทำตัว mongo ผมขอปรับไฟล์ client-deployment.yml ใหม่ก่อน โดยรวมไฟล์ service มาไว้ในไฟล์ deployment ด้วย ก็จะได้แบบนี้

apiVersion: apps/v1
kind: Deployment
metadata:
  name: client-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: client
  template:
    metadata:
      name: client-pods
      labels:
        app: client
    spec:
      containers:
        - name: client
          image: dekcode/todo-app:v4
---
apiVersion: v1
kind: Service
metadata:
  name: client-service
spec:
  type: NodePort
  selector:
    app: client
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 30001

client-deployment.yml

เมื่อเรียบร้อยแล้วลองสร้างกัน

kubectl apply -f .

create api and client deployment

เมื่อลอง kubectl get all ก็จะได้ประมาณนี้

NAME                                     READY   STATUS    RESTARTS   AGE
pod/client-deployment-666d95b674-2wswc   1/1     Running   0          3s
pod/client-deployment-666d95b674-6hdqb   1/1     Running   0          3s
pod/client-deployment-666d95b674-7phbm   1/1     Running   0          3s
pod/server-deployment-5d9649d564-j64xr   1/1     Running   0          3s
pod/server-deployment-5d9649d564-lqlfb   1/1     Running   0          3s
pod/server-deployment-5d9649d564-mc9xg   1/1     Running   0          3s

NAME                              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
service/client-service            NodePort    10.97.23.171     <none>        80:30001/TCP   3s
service/kubernetes                ClusterIP   10.96.0.1        <none>        443/TCP        7m17s
service/server-service-nodeport   NodePort    10.103.217.152   <none>        80:30002/TCP   3s

NAME                                READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/client-deployment   3/3     3            3           3s
deployment.apps/server-deployment   3/3     3            3           3s

NAME                                           DESIRED   CURRENT   READY   AGE
replicaset.apps/client-deployment-666d95b674   3         3         3       3s
replicaset.apps/server-deployment-5d9649d564   3         3         3       3s

result of all services and pods of server and client

ซึ่งถ้าลองเข้าไปที่ http://localhost:30001/ กับ http://localhost:30002/api/health ถ้าแสดง ready แสดงว่ามันสามารถทำงานได้แล้ว

แต่ถ้าเราลองเข้าไปที่ http://localhost:30002/api/todo ดูมันจะเข้าไม่ได้ เพราะว่าเรายังไม่ได้สร้างตัว database ที่เป็น mongo db

สร้างไฟล์ mongo-deployment
ต่อไปเราจะสร้าง Pod สำหรับ Mongo DB เริ่มจากสร้างไฟล์ชื่อ mongo-deployment.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongo-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      name: mongo-pods
      labels:
        app: mongo
    spec:
      containers:
        - name: mongo
          image: mongo
---
apiVersion: v1
kind: Service
metadata:
  name: mongo-service
spec:
  type: ClusterIP
  selector:
    app: mongo
  ports:
    - port: 27017
      targetPort: 27017

mongo-deployment.yml

สำหรับ mongo db ที่เป็น database นั้น เราจะใช้ pod แค่ตัวเดียวเท่านั้น replicas:1 และกำหนดประเภทของ spec -> type เป็น ClusterIP

อีกส่วนหนึ่งที่ต่างจากไฟล์ api-deployment.yml และ client-deployment.yml เราจะไม่มีการกำหนด nodePort ให้แก่ pod ของ mongo db ของเรา เพราะเราไม่อยากให้ pod นี้เข้าถึงได้จากภายนอก

เมื่อเสร็จแล้ว ก็ให้ลองสร้างไฟล์ mongo-deployment.yml ดู

ถ้าหากเราสร้าง client กับ api ไว้แล้ว ก็สร้างแค่ mongo-deployment.yml เท่านั้น

kubectl apply -f mongo-deployment.yml

create mongo-deployment

แต่ถ้ายังไม่ได้สร้างอะไรไว้เลยก็ใช้

kubectl apply -f .

create all

ถ้าอยากดูว่าทุกอย่างถูกสร้างไว้หรือยังก็ให้ใช้ kubectl get all เพื่อดูเหมือนเดิม แต่ผมจะไม่เอา result มาแปะไว้แล้ว เพราะมันเริ่มเยอะแล้ว เดียวจะอ่านยากเปล่าๆ

ทดสอบของเข้าไป http://localhost:30002/api/todo ตอนนี้เราจะได้ array เปล่าๆ กลับมา แสดงว่า เราสามารถสื่อสารกับ mongo db ได้แล้ว

อธิบายการทำงาน ภายใน Kubernetes จะมี DNS service ที่จะเก็บข้อมูล IP ไว้ เมื่อมีการเปลี่ยนแปลง IP ภายใน Cluster ก็จะทำการอัพเดท IP อัตโนมัติ

ในการเข้าถึงได้ผ่านชื่อของ cluster ที่ตั้งไว้ได้เลย เช่น mongo-service, api-service โดยที่ DNS ทำการบริหาร IP ให้อัตโนมัติ

สร้าง Cluster IP สำหรับ Client กับ API
ตอนนี้เรามี Cluster IP ที่ไว้สำหรับสื่อสารกันระหว่าง mongo กับ api แล้ว แต่เรายังไม่มี Cluster IP ที่ไว้สำหรับสื่อสารระหว่าง API กับ Client

ดังนั้นเราจะเข้าไปปรับในไฟล์ server-deployment.yml โดยการเพิ่ม Cluster IP เข้าไปแทน

apiVersion: v1
kind: Service
metadata:
  name: server-service
spec:
  type: ClusterIP
  selector:
    app: server
  ports:
    - port: 80
      targetPort: 80

cluster ip of service

เมื่อเสร็จแล้ว ก็จะได้แบบนี้

apiVersion: apps/v1
kind: Deployment
metadata:
  name: server-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: server
  template:
    metadata:
      name: server-pods
      labels:
        app: server
    spec:
      containers:
        - name: server
          image: dekcode/todo-api:v5
---
apiVersion: v1
kind: Service
metadata:
  name: server-service-nodeport
spec:
  type: NodePort
  selector:
    app: server
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30002
---
apiVersion: v1
kind: Service
metadata:
  name: server-service
spec:
  type: ClusterIP
  selector:
    app: server
  ports:
    - port: 80
      targetPort: 80

server-service.yml

ลองสร้างดู

 kubectl apply -f .

create all

เมื่อทุกอย่าง run ขึ้นมาพร้อมแล้ว เราก็จะสามารถ เพิ่มลด todo ได้ผ่าน http://localhost:30001 และสามารถเรียกดูข้อมูลผ่าน API ได้เหมือนกัน http://localhost:30002


Ingress

ใช้สำหรับกระจาย traffic ไปยัง Service ต่างๆ โดยขึ้นอยู่กับ routing rules (ต้นทาง) ที่กำหนด โดยมันจะทำหน้าที่คล้ายๆ กับ Application Load Balancer

เช่น เรามี xxx.com ที่จะมี route path ต่างๆ ไว้ ดังนี้

  • ถ้าเข้าผ่าน xxx.com/api ให้วิ่งไปที่ API Service
  • ถ้าเข้าผ่าน xxx.com/image ให้วิ่งไปที่ Storage Service
  • ถ้าเข้าผ่าน xxx.com ให้วิ่งไปที่ Web Service

กลับมาที่ Pod ที่เรามีอยู่ตอนนี้

ในขั้นต่อไปเราจะใช้ Ingress โดยที่เอาจะเอา NodePort ออกจาก server-deployment.yml และเปลี่ยนไปเป็น Cluster IP อีกตัวหนึ่ง

จากนั้นก็จะไปสร้าง Ingress Controller เข้าไป เมื่อมีการเรียก http://localhost:30001 ก็จะไปยังเว็บ application ของเรา

ถ้าเข้าผ่าน http://localhost:30001/api ก็จะเข้าผ่าน API Service แทน ตามภาพเลย

เรามาเริ่มกันเลย....

ลบ NodePort
เริ่มต้นด้วยการลบ NodePort ของ server-service-nodeport ในไฟล์ server-deployment.yml ออกไปก่อน เพราะเราจะไม่ใช้มันแล้ว

---
apiVersion: v1
kind: Service
metadata:
  name: server-service-nodeport
spec:
  type: NodePort
  selector:
    app: server
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30002

delete node port in server-deployment.yml

ก็จะได้แบบนี้

apiVersion: apps/v1
kind: Deployment
metadata:
  name: server-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: server
  template:
    metadata:
      name: server-pods
      labels:
        app: server
    spec:
      containers:
        - name: server
          image: dekcode/todo-api:v5
---
apiVersion: v1
kind: Service
metadata:
  name: server-service
spec:
  type: ClusterIP
  selector:
    app: server
  ports:
    - port: 80
      targetPort: 80

server-deployment after delete node port

ติดตั้ง Ingress Nginx Controller
ก่อนที่เราจะไปสร้าง Ingress เราต้องติดตั้งตัว Ingress Service ขึ้นมาก่อน โดยในบทความนี้ เราจะใช้ Ingress-Nginx Controller

Installation Guide - Ingress-Nginx Controller

จากนั้นก็เลือกติดตั้งตามเครื่องมือที่เราใช้ได้เลย เช่น Docker Desktop, Minikube, AWS, GCE เป็นต้น

จริงๆ เวลาเราใช้งาน Kubernetes เราจะใช้ Helm เข้ามาช่วย แต่ในบทความนี้ เราใช้ Kubectl กันไปก่อนนะ

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.1/deploy/static/provider/cloud/deploy.yaml

https://kubernetes.github.io/ingress-nginx/deploy/#quick-start

เมื่อกด run เสร็จแล้ว ลองเข้าไปที่ http://localhost มันจะแสดงหน้า nginx ขึ้นมาแล้ว

สร้าง Ingress
สร้างไฟล์ ingress.yml จากนั้นให้ใส่คำสั่งดังนี้

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-service
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
  rules:
    - host: localhost
      http:
        paths:
          - path: /api/
            pathType: Prefix
            backend:
              service:
                name: server-service
                port:
                  number: 80
          - path: /?(.*)
            pathType: Prefix
            backend:
              service:
                name: client-service
                port:
                  number: 80

ingress.yml

  • kubernetes.io/ingress.class ตรงนี้เป็นตัวบอกว่า เวลาที่เราต้องการใช้ ingress จะให้ไปใช้ engine ตัวไหนมา run ซึ่งในตัวอย่างจะใช้ nginx
  • nginx.ingress.kubernetes.io/use-regex: บอกว่าเราจะใช้ regular expression ในการ match path จากต้นทาง

ลอง run ดู

kubectl apply -f .

create all

ถ้าเราลอง get all ดู เราจะไม่เจอ ingress แสดงไว้ในนั้น อย่าพึ่งตกใจไป ให้ลองเข้า http://localhost/ ถ้าทำถูกต้องเราก็จะเข้าเว็บ todo ที่เราสร้างไว้

ทีนี้เราลองเข้าผ่าน http://localhost/api/todo ก็จะได้ ข้อมูลกลับมาเช่นเดียวกัน


Storage (Persistent Volume)

ตอนนี้เรามี mongo db อยู่ใน pod แล้ว ภายใน pod จะมี volume อยู่ในตัวของมันเองด้วย ซึ่งในที่นี้จะเก็บไว้นใน data/db

จากที่เราลองสร้างและลบ เราจะเห็นว่าตัวข้อมูลมันหายไปด้วย เมื่อเราลบ pod นั้นทิ้ง ซึ่งในการใช้งานจริงๆ เวลาที่ pod หายไป ข้อมูลก็จะหายไปด้วยเช่นกัน

ดังนั้นเราจำเป็นต้องเปลี่ยนที่เก็บข้อมูลใหม่ เพื่อให้ข้อมูลที่เราต้องการเก็บไว้ยังอยู่

มาออกแบบกันใหม่ โดยการเพิ่ม Persistent Volume Claim (PVC) เข้าไป

สร้าง pvc
เริ่มที่เราจะสร้างไฟล์ mongo-pvc.yml ขึ้นมา

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mongo-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

mongo-pvc.yml

  • accessModes: กำหนดสิทธิ์ในการเข้าถึง PVC ซึ่งในตัวอย่างนี้จะกำหนดให้ใครเข้าไปอ่านก็ได้ ReadWriteMany สามารถเข้าดูเพิ่มเติมได้ที่ https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes
  • requests: เป็นการบอกว่าต้องการ resource เท่าไหร่

เสร็จแล้วก็สร้าง PVC ของเรา

 kubectl apply -f .\mongo-pvc.yml

create mongo pvc

ถ้าอยากเข้าไปดูให้ใช้คำสั่ง

kubectl get pvc

show pvc

เราก็จะเห็น PVC ทั้งหมดที่สร้างขึ้นมา

NAME        STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
mongo-pvc   Bound    pvc-839a0c1c-03c4-4a20-ac9b-7d6119fafb3e   1Gi        RWX            hostpath       <unset>                 50s

เชื่อมต่อ PVC กับ Containers
ไปที่ไฟล์ mongo-development.yml จากนั้น ไปเพิ่ม volumeMounts เข้ากับตัวไหน แต่เราจะไม่สามารถต่อเข้า PVC ได้โดยตรง จำเป็นต้องระบุ volume กันก่อน

ตั้งชื่อให้กับ volume ใน mongo โดยเราจะตั้งชื่อว่า mongo-storage และระบุ mountPath เป็น /data/db

จากนั้น เพิ่มข้อมูล volumes เข้าไป โดย name เป็นตัวเดียวกับที่พึ่งตั้งชื่อไป (mongo-storage)

เพิ่ม PersistentVolumeClaim เข้าไปที่ volumes เพื่อต่อเข้าไปหา PVC ที่เราได้สร้างไว้

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongo-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mongo
  template:
    metadata:
      name: mongo-pods
      labels:
        app: mongo
    spec:
      containers:
        - name: mongo
          image: mongo
          volumeMounts:
            - name: mongo-storage
              mountPath: /data/db
      volumes:
        - name: mongo-storage
          persistentVolumeClaim:
            claimName: mongo-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: mongo-service
spec:
  type: ClusterIP
  selector:
    app: mongo
  ports:
    - port: 27017
      targetPort: 27017

mongo-deployment.yml

ลอง run ดู

kubectl apply -f .

create all

เมื่อ run ผ่านแล้ว เท่านี้เราก็สามารถเก็บข้อมูลโดยที่ไม่หายได้แล้ว

หากต้องการลด PVC ทิ้งทั้งหมด ให้ใช้คำสั่งนี้

kubectl delete pvc --all

delete all pvc


เรื่องพื้นๆ ของ Kubernetes จบไว้เท่านี้ก่อน ไว้มีเวลาจะมาเขียนเพิ่มเติมต่อไป

หรือถ้าอยากลองเล่น ผมมี repo ของ Kubernetes อยู่ ลองไป clone แล้วลองเล่นกันได้

GitHub - Thammasok/k8s-training
Contribute to Thammasok/k8s-training development by creating an account on GitHub.