Istio Service Mesh คืออะไร พร้อมวิธีการติดตั้ง และใช้งาน Istio เบื้องต้น

settakit
Sirisoft

--

by Settakit

สวัสดีครับ ห่างหายกันไปนานหลังจากเขียนซีรี่ย์ยาวอย่าง Kubernetes Zero-2-Hero วันนี้ได้มีโอกาสกลับมาเขียนบล็อกอีกครั้งนึง และในครั้งนี้ก็ยังหนีไม่พ้นในเรื่องของ Kubernetes ในวันนี้เราจะมาแนะนำเครื่องมือที่จะมาช่วยเราจัดการ Microservices บน Kubernetes ได้ง่ายมากยิ่งขึ้น โดยเครื่องมือนั้นมีนามว่า “Istio”

ก่อนจะเข้าสู่เนื้อหาในวันนี้สำหรับคนที่ยังไม่รู้ว่า Kubernetes คืออะไร สามารถเข้าไปอ่านบล็อกที่ผมเคยเขียนไว้ก่อนนี้ได้ที่ลิงก์ด้านล่างนี้เลย เพื่อที่จะสามารถเข้าใจเนื้อหาในส่วนนี้ได้ง่ายมากขึ้น ส่วนคนที่เคยอ่านหรือรู้จัก Kubernetes อยู่แล้วก็สามารถข้ามส่วนนี้แล้วเข้าสู่เนื้อหาได้เลยนะครับ

และเนื้อหาที่เราจะมาพูดในวันนี้มีดังนี้

1. What is a Service Mesh?

2. What is Istio?

3. Install “Istio” on Kubernetes

4. Try “Istio”

What is a Service Mesh?

Service Mesh คือ เครือข่ายการเชื่อมต่อสื่อสารระหว่าง Microservices ในรูปแบบของ Container โดย Service Mesh จะทำงานในรูปแบบของการติดตั้ง Sidecar Proxy ที่เป็น Container เข้าเกาะอยู่กับ Container ที่เป็น Service ของเรา โดย Sidecar Proxy จะทำหน้าที่จัดการในส่วนของ Network ทั้งขาเข้าและขาออก โดย Container ที่เป็น Service ของเราจะไม่ได้คุยกันโดยตรง แต่จะคุยผ่าน Sidecar Proxy

ด้วยเหตุนี้จึงทำให้เราสามารถจัดการในเรื่องของการทำ Circuit Breakers, Timeouts และ Retries หรือไม่ว่าจะเป็นเรื่องของการ Authentication ระหว่าง Service หรือ จาก End-User โดยที่เราไม่จำเป็นจะต้องเขียนโค้ดเพิ่มเข้าไปในตัว Service ของเราเลย หรือถ้าหากเรามี Service เดิมอยู่แล้วเราก็สามารถนำมันมาใช้งานร่วมกับ Service Mesh ได้เลย

What is Istio?

source: https://www.techtalkthai.com/istio-microservice-mesh-ibm-google-lyft/

Istio คือ Open Source Framework สำหรับจัดการ Microservices บน Kubernetes โดย Istio จะเป็นตัวจัดการการเชื่อมต่อสื่อสารกันระหว่าง Service และเพิ่มความปลอดภัยให้กับ Microservices ที่เอาไป Deploy อยู่บน Kubernetes โดย Concept ของ Istio จะประกอบไปด้วย 3 เรื่องดังนี้

source: https://istio.io/latest/
  • Traffic Management: จัดการในเรื่องของการเชื่อมต่อกันระหว่าง Service ทั้งใน Cluster เดียวกัน หรือคนละ Cluster สามารถเพิ่มกฏสำหรับการเชื่อมต่อกันของแต่ละ Service ได้ สามารถจัดการในเรื่องของการทำ Circuit Breakers, Timeouts และ Retries โดยที่ไม่ต้องเขียนโค้ดเพิ่มเข้าไปในตัว Service และยังสามารถจัดการในเรื่องของการทำ Canary Deploy, A/B Testing ได้ง่ายมากขึ้น
  • Observability: จัดการในส่วนของการ Monitor การไหลของ Traffic ระหว่าง Service
  • Security: จัดการในเรื่องของการทำ Authentication ระหว่าง Service หรือจาก End-User

โดยการทำงานของ Istio จะแบ่งส่วนการทำงานออกเป็นสองส่วนคือ Control Plane และ Data Plane

source: https://istio.io/latest/about/service-mesh/
  • Data Plane คือ เป็นการติดต่อสื่อสารกันของ Service โดยจะสื่อสารกันผ่าน Envoy Proxy ที่เป็น Sidecar ซึ่งติดตั้งเข้าไปยังตัว Service ของเรา โดยจะถูกกำหนด Rule หรือ Policy ต่าง ๆ มาจาก Control Plane โดย Traffic ที่จะเข้าหรือออกจาก Service จะต้องผ่าน Proxy ตัวนี้ก่อน
  • Control Plane เป็นตัวควบคุมการทำงานของตัว Envoy Proxy หากมีการปรับเปลี่ยน Config จากผู้ใช้งาน Control Plane จะนำ Config นั้นไปปรับใช้งานเข้ากับ Envoy Proxy ที่กำลังทำงานเป็น Sidecar อยู่

Install Istio on Kubernetes (Docker Desktop)

ต่อไปเราจะมาติดตั้งตัว Istio บน Kubernetes กัน โดยก่อนอื่นเราต้องติดตั้ง Docker Desktop และเปิดใช้งาน Kubernetes โดยสามารถดูวิธีการติดตั้งได้จากลิงก์นี้ได้เลยนะครับ

หลังจากติดตั้ง Docker และเปิดใช้งาน Kubernetes แล้วให้เพิ่ม Resource สำหรับ Docker ตามภาพด้านล่างนี้

source: https://istio.io/latest/docs/setup/platform-setup/docker/

ต่อมาเราจะติดตั้งตัว “Istio” เข้าไปยัง Cluster ของเรา โดยวิธีการติดตั้ง istio จริง ๆ แล้วมีอยู่หลายวิธี แต่วันนี้ที่เราเลือกมาเป็นวิธีการติดตั้งโดยใช้ Command “istioctl” สามารถทำตามขั้นตอนได้ดังนี้

  1. Download istioctl เพื่อใช้ในการติดตั้ง Istio
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.13.4 TARGET_ARCH=x86_64 sh -

2. เข้าไปยัง Folder ที่ Download มา

cd istio-1.13.4

3. จากนั้นเพิ่ม istioctl ไปยัง PATH

export PATH=$PWD/bin:$PATH

4. จากนั้น Run Command ด้านล่างนี้เพื่อติดตั้ง Istio ไปยัง Kubernetes Cluster ของเรา โดยเราจะใช้ profile ในการติดตั้งเป็น demo

istioctl install --set profile=demo -y# OUTPUT
✔ Istio core installed
✔ Istiod installed
✔ Egress gateways installed
✔ Ingress gateways installed
✔ Installation complete

6. ตรวจสอบว่า Pod ของ Istio ทำงานแล้วตาม Commnad ด้านล่างนี้ จะเห็นว่า Pod ของ Istio ทำงานอยู่

kubectl get po -n istio-system# OUTPUT
NAME READY STATUS RESTARTS AGE
istio-egressgateway-cc4fb665c-jmzs4 1/1 Running 0 2m34s
istio-ingressgateway-5c77ccb95c-d9xv7 1/1 Running 0 2m34s
istiod-694fc8b75c-xlzjc 1/1 Running 0 2m51s

Namespace ชื่อ istio-system จะถูกสร้างขึ้นมาอัตโนมัติเมื่อติดตั้ง Istio

Try “Istio”

หลังจากติดตั้ง Istio ไปที่ Kubernetes แล้ว ต่อมาเราจะมาทดลองใช้งานตัว Istio กัน โดย Application ที่จะมาใช้ทดลองในวันนี้มีชื่อว่า Bookinfo ซึ่งมีรูปแบบเป็น Micro Service โดยมี Architecture ดังนี้

source: https://istio.io/latest/docs/examples/bookinfo/
  • Product Page จะเรียก Reviews Service ที่แสดงความคิดเห็น และคะแนนความนิยมของหนังสือ และ Detail Service เป็นข้อมูลรายละเอียดของหนังสือ
  • Detail Service มีข้อมูลรายละเอียดของหนังสือ
  • Reviews Service แสดงความคิดเห็น และคะแนนความนิยม โดย Review Service จะมี 3 เวอร์ชัน โดย เวอร์ชัน 1 จะไม่มีการเรียก Rating เพื่อแสดงคะแนนความนิยม เวอร์ชัน 2 จะคะแนนจาก Rating Service เป็นสีดำ และ เวอร์ชัน 3 จะแสดงเป็นสีแดง
  • Rating Service จัดเก็บข้อมูลคะแนนความนิยมของหนังสือ

มาเริ่มกันเลยดีกว่านะครับ ก่อนอื่น สร้าง Namespace “bookinfo” เพื่อเตรียม Deploy Application Bookinfo

kubectl create ns bookinfo# OUTPUT
namespace/bookinfo created

จากนั้นกำหนด Label ให้กับ Namespace “bookinfo” เพื่อที่เป็นการกำหนดให้ Istio นั้นแนบ Sidecar ให้ Pods ใน Namespace

kubectl label namespace bookinfo istio-injection=enabled# OUTPUT
namespace/bookinfo labeled

ใช้ Command เพื่อตรวจสอบว่าเพิ่ม Label ให้กับ Namespace เรียบร้อยแล้ว จะพบว่ามี Label ชื่อ “istio-injection=enabled” อยู่

kubectl get ns bookinfo --show-labels# OUTPUT
NAME STATUS AGE LABELS
bookinfo Active 4m19s istio-injection=enabled,kubernetes.io/metadata.name=bookinfo

จากนั้น Deploy Application Bookinfo ไปที่ Namespace bookinfo

kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -n bookinfo# OUTPUT
service/details created
serviceaccount/bookinfo-details created
deployment.apps/details-v1 created
service/ratings created
serviceaccount/bookinfo-ratings created
deployment.apps/ratings-v1 created
service/reviews created
serviceaccount/bookinfo-reviews created
deployment.apps/reviews-v1 created
deployment.apps/reviews-v2 created
deployment.apps/reviews-v3 created
service/productpage created
serviceaccount/bookinfo-productpage created
deployment.apps/productpage-v1 created

ตรวจสอบว่า Service และ Pod นั้นทำงานอยู่

kubectl get service -n bookinfo# OUTPUT
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
details ClusterIP 10.96.253.155 <none> 9080/TCP 2m30s
productpage ClusterIP 10.97.189.17 <none> 9080/TCP 2m30s
ratings ClusterIP 10.103.59.231 <none> 9080/TCP 2m30s
reviews ClusterIP 10.108.83.40 <none> 9080/TCP 2m30s
kubectl get pod -n bookinfo# OUTPUT
NAME READY STATUS RESTARTS AGE
details-v1-586577784f-kfzxb 2/2 Running 0 2m38s
productpage-v1-589b848cc9-xm54v 2/2 Running 0 2m38s
ratings-v1-679fc7b4f-sjvl8 2/2 Running 0 2m38s
reviews-v1-7b76665ff9-lqc4l 2/2 Running 0 2m38s
reviews-v2-6b86c676d9-z4xpp 2/2 Running 0 2m38s
reviews-v3-b77c579-4xc4r 2/2 Running 0 2m38s

จากการตรวจสอบจะพบว่าสถานะของ Pods จะเป็น 2/2 เนื่องจากภายใน Pods จะมีอยู่ 2 Container นั้นคือ Envoy Proxy ที่เป็น Sidecar และ Service Container โดย Sidecar จะถูกแนบเข้ามาโดยอัตโนมัติถ้าหากเรากำหนด Label ไว้ที่ Namespace

ต่อมาสร้าง Gateway และ VirtualService เพื่อที่จะให้ End-User สามารถเข้ามาใช้งาน Application Bookinfo ของเราได้ แต่ก่อนอื่นเรามาทำความรู้จัก Gateway และ VirtualService กันก่อนนะครับ

Gateway เป็นตัวกำหนดทางเข้าหรือทางออกของ Service ภายใน Cluster

VirtualService เป็นตัวกำหนดปลายทางของ Request ที่เข้ามาผ่าน Gateway นั้นจะ Route ไปที่ปลายทางที่ Service ไหน โดยสามารถกำหนดได้จาก Path, Host, Header ต่าง ๆ

Gateway, VirtualService เป็น Custom Resource Definition (CRD) ของ Istio

สร้าง Gateway และ VirtualService โดยเนื้อหาของ Configuration จะมีหน้าตาแบบนี้

apiVersion: networking.istio.io/v1alpha3
kind: Gateway # ประเภทของ Resource
metadata:
name: bookinfo-gateway # ชื่อของ Gateway
spec:
selector:
istio: ingressgateway # กำหนด Ingress Gateway
servers:
- port:
number: 80 # กำหนด Port สำหรับการเข้าใช้งาน
name: http # กำหนดชื่อของ Port
protocol: HTTP # กำหนด Protocol
hosts:
- "*" # ระบุ Host สำหรับการเข้าใช้งาน Gateway (เราสามารถกำหนดเป็นชื่อได้เช่น bookinfo.com)
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService # ประเภทของ Resource
metadata:
name: bookinfo # ชื่อของ VirtualService
spec:
hosts:
- "*" # กำหนด host เพื่อเข้าใข้งาน VirtualService
gateways:
- bookinfo-gateway # กำหนดชื่อของ Gateway
http:
- match: # กำหนด Rule สำหรับการเข้ามาใช้งาน VirtualService
- uri:
exact: /productpage
- uri:
prefix: /static
- uri:
exact: /login
- uri:
exact: /logout
- uri:
prefix: /api/v1/products
route: # กำหนด Route ปลายทาง
- destination:
host: productpage # Host ของ Service ที่จะให้ Route
port:
number: 9080 # เลขของ Port ที่ Service ทำงานอยู่

สร้าง Gateway และ VirtualService ด้วย Command ดังนี้

kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml -n bookinfo# OUTPUT
gateway.networking.istio.io/bookinfo-gateway created
virtualservice.networking.istio.io/bookinfo created

ตรวจสอบว่า Gateway และ VirtualService ถูกสร้างขึ้นมาแล้ว

kubectl get gateways,virtualservice -n bookinfo# OUTPUT
NAME AGE
gateway.networking.istio.io/bookinfo-gateway 2m8s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/bookinfo ["bookinfo-gateway"] ["*"] 2m8s

ทดลองเข้าใช้งาน Bookinfo ผ่าน Browser ที่ http://localhost/productpage จะได้ผลลัพธ์ดังนี้

โดยเมื่อเราลองกด Refesh ดูจะพบว่าในส่วนของ Book Reviews จะมีการเปลี่ยนการแสดงผลเป็นทั้งแบบดาวสีแดง ดาวสีดำ และไม่มีดาว เนื่องจากโดย Default การ Route ของ Review Service ไปที่ Rating Service จะเป็นการ Route แบบ Round Robin เนื่องจากว่าตอนนี้เรายังไม่ได้ใช้ Istio ในการจัดการเรื่องของการ Routing

ต่อไปมาลองใช้ Istio กำหนดให้ Request ที่เข้ามาให้ Route ไปที่ Review เวอร์ชัน 1 เท่านั้น

ก่อนอื่นสร้าง DestinationRule เพื่อกำหนดว่าแต่ละ Service ปลายทางมีเวอร์ชันให้เลือกใช้งานกี่เวอร์ชัน โดย Configuration จะหน้าตาแบบนี้

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule # ประเภทของ Resource
metadata:
name: productpage # ชื่อของ DestinationRule
spec:
host: productpage # host ของ Service
subsets: # กำหนด Subset ของแต่ละ Service ว่ามีกี่เวอร์ชัน
- name: v1 # ชื่อของ Subset
labels:
version: # label เวอร์ชันที่ถูกกำหนดไว้ในไฟล์ deployment ของ service
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: ratings
spec:
host: ratings
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v2-mysql
labels:
version: v2-mysql
- name: v2-mysql-vm
labels:
version: v2-mysql-vm
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: details
spec:
host: details
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---

DestinationRule เป็นตัวกำหนดสิ่งที่จะเกิดขึ้นต่อไปเมื่อจะ Request ไปยังปลายทาง โดยสามารถกำหนดในเรื่องของการทำ Load balancing, Circuit Breaking ได้ที่ DestinationRule

DestinationRule ก็เป็นหนึ่งใน Custom Resource Definition (CRD) ของ Istio

รัน Command เพื่อนำ DestinationRule ไปปรับใช้ภายใน Cluster

kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml -n bookinfo# OUTPUT
destinationrule.networking.istio.io/productpage created
destinationrule.networking.istio.io/reviews created
destinationrule.networking.istio.io/ratings created
destinationrule.networking.istio.io/details created

ต่อมารัน Command เพื่อสร้าง VirtualService เพื่อให้ Route ไปยัง Service เวอร์ชัน 1 ทั้งหมด

kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml -n bookinfo# OUTPUT
virtualservice.networking.istio.io/productpage created
virtualservice.networking.istio.io/reviews created
virtualservice.networking.istio.io/ratings created
virtualservice.networking.istio.io/details created

โดยในส่วนของ Configuration เป็นดังนี้

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService # ประเภทของ Resource
metadata:
name: productpage # ชื่อของ VirtualService
spec:
hosts:
- productpage # Host ของ Service ที่ถูกเรียกเข้ามา
http:
- route:
- destination:
host: productpage # Host ปลายทางของ Service (ต้องตรงกับ Host ใน DestinationRule)
subset: v1 # ชื่อของ Subset ใน DestinationRule
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: details
spec:
hosts:
- details
http:
- route:
- destination:
host: details
subset: v1
---

เมื่อทดสอบเข้า Browser ที่ http://localhost/productpage อีกครั้งจะพบว่าส่วนของ Book Reviews นั้นจะไม่ดาวให้เห็นเลยเนื่องจากตอนนี้เราได้ทำการ Routing Request ทั้งหมดไปที่ Service เวอร์ชัน 1 ทั้งหมด

ต่อมาเราจะมาแบ่ง Traffic Request ไปที่เวอร์ชัน 3 ด้วย โดยกำหนด Configuration ใน VirtualService ดังนี้

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 50
- destination:
host: reviews
subset: v3
weight: 50

เพิ่ม Destination เป็นสองปลายทาง และกำหนดการแบ่ง Traffic ไว้ที่ 50%

รัน Command เพื่อนำ Config ไปปรับใช้งาน

kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-50-v3.yaml -n bookinfo# OUTPUT
virtualservice.networking.istio.io/reviews configured

จากนั้นถ้าลองทดสอบเข้า Browser ที่ http://localhost/productpage อีกครั้ง และลอง Refesh หน้าดูจะพบว่าส่วนของ Book Reviews นั้นจะมีการแสดงดาวเป็นสีแดง และไม่แสดงดาวสลับกันไป

แล้วเราจะทำแบบนี้ไปทำไม การทำแบบนี้เหมือนเป็นการอยากลองทดสอบ Service เวอร์ชัน 3 กับผู้ใช้งานดูก่อนว่าถ้าหากนำไปใช้งานจริง ๆ แล้วจะเกิดปัญหาอะไรขึ้นหรือไม่ โดยที่ก็ยังมี Service เวอร์ชันเก่าที่ทำงานสมบูรณ์ควบคู่กันไปด้วย โดยถ้าหากทดสอบมาระยะนึงพบว่า Service เวอร์ชัน 3 นั้นไม่มีปัญหาการใช้งานก็สามารถปรับให้ Traffic เข้ามาใช้งานเวอร์ชัน 3 แบบ 100% เลยก็ได้

โดยเราสามารถกำหนด Config ได้ดังนี้ โดยการลบ Distination ในส่วนของ เวอร์ชัน 1 ออก

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v3

จากนั้นรัน Command เพื่อนำ Config ไปปรับใช้งาน

kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-v3.yaml -n bookinfo# OUTPUT
virtualservice.networking.istio.io/reviews configured

ลองเข้าหน้าเว็บอีกครั้งจะพบว่า Book Reviews จะแสดงดาวสีแดงตลอด

ต่อมาเราจะลองในเรื่องของการทำ Authentication ระหว่าง Service กันนะครับ โดยเริ่มแรกทดลองสร้าง Namespace และ Pods ตาม Command ดังนี้

kubectl create ns foo
kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo
kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo
kubectl create ns bar
kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n bar
kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n bar
kubectl create ns legacy
kubectl apply -f samples/httpbin/httpbin.yaml -n legacy
kubectl apply -f samples/sleep/sleep.yaml -n legacy
# OUTPUT
namespace/foo created
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
namespace/bar created
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created
namespace/legacy created
serviceaccount/httpbin created
service/httpbin created
deployment.apps/httpbin created
serviceaccount/sleep created
service/sleep created
deployment.apps/sleep created

ตอนนี้เราจะมี Namespace foo, bar และ legacy โดยที่ Pods ภายใน Namespace foo และ bar นั้นได้ทำการ Inject Sidecar เข้าไปด้วย ส่วน Legacy นั้นไม่ได้มี Sidecar

kubectl get po -n foo# OUTPUT
NAME READY STATUS RESTARTS AGE
httpbin-ff46b584d-thlk9 2/2 Running 0 2m34s
sleep-57f79f5944-8b6zf 2/2 Running 0 2m33s
kubectl get po -n bar# OUTPUT
NAME READY STATUS RESTARTS AGE
httpbin-ff46b584d-tjbdm 2/2 Running 0 2m38s
sleep-57f79f5944-lm52w 2/2 Running 0 2m37s
kubectl get po -n legacy# OUTPUT
NAME READY STATUS RESTARTS AGE
httpbin-847f64cc8d-j4r5p 1/1 Running 0 2m45s
sleep-5887ccbb67-9c79j 1/1 Running 0 2m44s

ทดลองเรียก HTTP Request ไปที่ httpbin Pods จาก sleep Pods จากทั้ง 3 Namespace จะพบว่า Status ที่ได้กลับมาเป็น 200 ทั้งหมด โดยสามารถทำได้ตาม Command ตามด้านล่างนี้

for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name})" -c sleep -n ${from} -- curl -s "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done# OUTPUT
sleep.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.foo to httpbin.legacy: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200
sleep.bar to httpbin.legacy: 200
sleep.legacy to httpbin.foo: 200
sleep.legacy to httpbin.bar: 200
sleep.legacy to httpbin.legacy: 200

โดย Default แล้วหากว่าเรายังไม่ได้ Config เพิ่มเติม Pods ที่ไม่มี Sidecar หรือว่า Service อื่น ๆ ที่เรียกเข้ามาใน Pods ที่มี Sidecar จะสามารถเรียกหากันได้ปกติ

ต่อมาเราจะใช้ Kind ที่มีชื่อว่า PeerAuthentication เพื่อกำหนดว่า Traffic ที่จะเข้ามายัง Pods ที่มี Sidecar นั้น สามารถทำได้หรือไม่ โดย Config จะมีหน้าตาแบบด้านล่างนี้

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: "default"
namespace: "istio-system"
spec:
mtls:
mode: STRICT

PeerAuthentication คือตัวกำหนดว่า Traffic ที่จะผ่าน Sidecar Proxy ต้องทำ mTLS หรือไม่

PeerAuthentication นั้นก็เป็นหนึ่งใน Custom Resource Definition (CRD) ของ Istio

โดย Config ด้านบนนี้จะกำหนดให้ทุก Request ที่เข้ามายัง Pods ที่มี Sidecar จะต้องทำการ mTLS กันก่อน ซึ่งโดยปกติ Pods ที่มี Sidecar อยู่มันจะสามารถทำ mTLS กันเองได้อยู่แล้ว แต่ว่า Pods ที่ไม่มี Sidecar จะไม่สามารถทำ mTLS ได้

สามารถรัน Command ตามด้านล่างนี้เพื่อปรับใช้งาน Config เข้ากับ Cluster

kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: "default"
namespace: "istio-system"
spec:
mtls:
mode: STRICT
EOF
# OUTPUT
peerauthentication.security.istio.io/default created

โดยเมื่อเราสร้าง PeerAuthentication แล้วเมื่อทดลองเรียก HTTP Request ตาม Command เดิมก่อนหน้านี้อีกครั้งจะพบว่า Pods ใน Namespace legacy จะไม่ได้สามารถเรียกเข้าไปยัง Pods ใน Namespace foo และ bar ได้ เนื่องจาก Pods ใน Legacy นั้นไม่มี Sidecar จึงไม่สามารถทำ mTLS ได้

for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name})" -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done# OUTPUT
sleep.foo to httpbin.foo: 200
sleep.foo to httpbin.bar: 200
sleep.foo to httpbin.legacy: 200
sleep.bar to httpbin.foo: 200
sleep.bar to httpbin.bar: 200
sleep.bar to httpbin.legacy: 200
sleep.legacy to httpbin.foo: 000
command terminated with exit code 56
sleep.legacy to httpbin.bar: 000
command terminated with exit code 56

sleep.legacy to httpbin.legacy: 200

โดย Case นี้ถ้าหากจะใช้งานจริงก็จะเหมือนกับว่าเราไม่อยากให้ Service อื่น ๆ ที่เราไม่ได้กำหนดให้เข้ามาใช้งานใน Service ของเราได้ เพื่อช่วยในเรื่องของความปลอดภัยให้กับระบบของเรามากขึ้น

ต่อมาถ้าหากว่าเราไม่ถนัดใช้ Command Line ตัวของ Istio ก็มี Dashboard UI ที่มีชื่อว่า “Kiali” ที่ทำให้เราสามารถจัดการ Configuration ที่เกี่ยวกับ Istio และยังสามารถดู Virtualization ของ Traffic ที่เข้ามาใช้งาน Service ภายใน Cluster ได้

โดยเราสามารถติดตั้งตัว Kiali ได้ตาม Command ดังนี้

kubectl apply -f samples/addons -n istio-system# OUTPUT
serviceaccount/grafana created
configmap/grafana created
service/grafana created
deployment.apps/grafana created
configmap/istio-grafana-dashboards created
configmap/istio-services-grafana-dashboards created
deployment.apps/jaeger created
service/tracing created
service/zipkin created
service/jaeger-collector created
serviceaccount/kiali created
configmap/kiali created
clusterrole.rbac.authorization.k8s.io/kiali-viewer created
clusterrole.rbac.authorization.k8s.io/kiali created
clusterrolebinding.rbac.authorization.k8s.io/kiali created
role.rbac.authorization.k8s.io/kiali-controlplane created
rolebinding.rbac.authorization.k8s.io/kiali-controlplane created
service/kiali created
deployment.apps/kiali created
serviceaccount/prometheus created
configmap/prometheus created
clusterrole.rbac.authorization.k8s.io/prometheus created
clusterrolebinding.rbac.authorization.k8s.io/prometheus created
service/prometheus created
deployment.apps/prometheus created

ต่อมาเราจะ Expose Service ของ Kiali ออกมาเป็น NodePort เพื่อที่เราจะสามารถเข้าใช้งานได้ผ่านหน้า Browser

kubectl expose svc kiali --name kiali-nodeport --target-port 20001 --type NodePort -n istio-system# OUTPUT
service/kiali-nodeport exposed

จากนั้นเรียกดู Service เพื่อตรวจสอบเลขของ NodePort เพื่อเข้าใช้งาน

kubectl get svc kiali-nodeport -n istio-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# OUTPUT
kiali-nodeport NodePort 10.107.233.10 <none> 20001:32107/TCP,9090:30723/TCP 4m43s

โดย Port ที่เราจะเข้าใช้งานหน้าเว็บคือ 20001 เราจะใช้ NodePort ที่ Mapping กับ Port 20001 นั้นคือ 32107 (ซึ่งของแต่ละคนอาจจะไม่เหมือนกันนะครับในส่วนของเลข NodePort)

เมื่อทดลองเข้าใช้งานหน้าเว็บบน Browser ด้วย http://localhost:<NodePort> จะได้หน้าตาแบบนี้

เราสามารถสร้าง Gateway และ VirtualService ได้ผ่านหน้า Dashboard โดยเราจะสร้าง Gateway และ VirtualService สำหรับ httpbin ใน Namespace foo

โดยเราสามารถทำได้ตามวิธีดังนี้

  1. เข้าไปที่เมนู Services
  2. จากนั้นเปลี่ยน Namespace เป็น foo

3. จากนั้นเลือกที่ httpbin

4. คลิกที่ Action จากนั้นเลือก Request Routing

5. ใส่ค่า Config ตามภาพด้านล่างเพื่อสร้าง VirtualService และ Gateway จากนั้นกด Create

6. จากนั้นกลับไปที่หน้า Service จะพบว่า Gateway, VirtualService และ DestinationRule ถูกสร้างขึ้นมาแล้ว

เพื่อทดสอบว่า Gateway และ VirtualService ที่เราสร้างนั้นสามารถใช้งานได้ด้วย Command ตามด้านล่างนี้

curl --resolve 'httpbin.local.com:80:127.0.0.1' http://httpbin.local.com/ip# OUTPUT
{
"origin": "192.168.65.3"
}

จะเห็นว่าเราสามารถใช้งาน Host จาก Gateway เรียกเข้าไปที่ httpbin Service ของเราได้

และประโยชน์ของ Kiali นั้นไม่ได้มีแค่จัดการเรื่อง Configuration ผ่าน UI เท่านั้น มันยังสามารถใช้เป็นตัว Virtualization Traffic ที่เข้ามายัง Service ของเรา แล้ว Traffic เหล่านั้นถูกส่งต่อไปที่ Service ไหนบ้าง

โดยเราสามารถกดไปที่เมนู Graph จะได้รูปดังนี้

แต่ตอนนี้ยังไม่มี Traffic เข้ามาจึงยังไม่มีการแสดงผลของ Graph ขึ้นมา เพื่อแสดง Graph เราจะส่ง Request ไปที่ Bookinfo ฉะนั้นเราจะเปลี่ยน Namespace ที่ใช้แสดงเป็น bookinfo จากนั้นในส่วนของ Display ให้เลือก Response Time และ Traffic Animation เพื่อดูการไหลของ Traffic และ Response Time ของแต่ละ Service ว่าเป็นเท่าไหร่

โดยเราสามารถรัน Command ด้านล่างนี้เพื่อให้เกิด Traffic ขึ้นมา

for i in {1..100}; do curl "http://localhost/productpage" | grep -o "<title>.*</title>"; done;# OUTPUT
100 5179 100 5179 0 0 81014 0 --:--:-- --:--:-- --:--:-- 99k
<title>Simple Bookstore App</title>
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5179 100 5179 0 0 99241 0 --:--:-- --:--:-- --:--:-- 117k
<title>Simple Bookstore App</title>
.
.
.

จากนั้นกลับไปที่ Dashboard รอซักพักนึงจะมี Graph ขึ้นมาพร้อมแสดงการ Traffic ไหลไปที่ Service ใดบ้าง และยังแสดงผล Response Time ของแต่ละ Service และยังบอก Success และ Error Rate ว่าเป็นสัดส่วนเท่าไหร่อีกด้วย

จากหน้า Graph นี้มันทำให้เรานั้นสามารถ Monitor การทำงานของ Traffic ได้ว่าสถานะปัจจุบันล่าสุดเป็นยังไง มี Error เกิดขึ้นระหว่าง Service ไหนรึเปล่า ถ้าหากไม่มี Graph แสดงเราก็คงต้องไปเปิดหา Arhitechture ของ Service เราแล้วต้องคอย Debug ไปทีละจุดทำให้เสียเวลามากขึ้น และยังไม่พอหาก Service เรามีจำนวนมากขึ้นการเชื่อมต่อกันของแต่ละ Service ก็จะยิ่งเยอะขึ้นทำให้ Debug ยากเข้าไปอีก สิ่งนี้ก็จะเข้ามาช่วยลดเวลาในส่วนนั้นได้

Conclusion

  1. Service Mesh คือเครือข่ายการเชื่อมต่อสื่อสารระหว่าง Microservice ในรูปแบบของ Container โดยจะมี Sidecar Proxy ค่อยเป็นตัวกลางในการติดต่อสื่อสารกันระหว่าง Service
  2. Istio คือ Open Source Framework สำหรับจัดการ Microservices บน Kubernetes โดย Istio จะทำหน้าที่ติดตั้ง Envoy Proxy ที่เป็น Sidecar เข้าไปยัง Pods เพื่อเป็นตัวขั้นกลางระหว่างการสื่อสารกันของ Service แต่ละตัว และเรายังสามารถใช้ Istio จัดการในเรื่องของการ Circuit Breakers, Timeouts, Retries, Canary Deploy, A/B Testing หรือไม่ว่าจะเป็นการทำ Authentication ระหว่าง Service หรือ End-User และยังสามารถ Monitor การไหลของ Traffic ได้อีกด้วย
  3. วิธีการติดตั้ง Istio ด้วย istioctl Command
  4. รู้จัก Gateway ที่เป็นตัวกำหนดทางเข้าและออกให้กับ Service, VirtualService เป็นตัวกำหนดปลายทางของ Request ที่เข้ามาผ่าน Gateway ว่าจะให้ Route ไปที่ Service ใด, DestinationRule เป็นตัวกำหนดสิ่งที่จะเกิดขึ้นต่อไปเมื่อจะ Request ไปยังปลายทาง และ PeerAuthentication เป็นตัวกำหนดว่า Traffic จะผ่าน Sidecar Proxy ได้หรือไม่ด้วยการทำ mTLS
  5. ทดลองการใช้งาน Istio เบื้องต้น การเปิดใช้งาน Inject Sidecar ไปยัง Pods การสร้าง Gateway, VirtualService และ DestinationRule ผ่าน Command และ Kiali Dashboard การทำ Traffic Shifting เพื่อแบ่ง Traffic ไป Service ที่มีหลายเวอร์ชัน การเปิดใช้งานการ Authentication ระหว่าง Service และการใช้ Kiali Dashboard สร้าง Configuration และ Monitor Traffic ที่เข้ามายัง Service

และในบล็อกนี้ก็ขอจบลงเพียงเท่านี้ หวังว่าคงมีประโยชน์ต่อผู้ที่สนใจอยากจะศึกษา Istio ไม่มากก็น้อยนะครับ

บล็อกนี้เป็นแค่ข้อมูลส่วนน้อยของ Istio สำหรับใครที่อยากศึกษาเพิ่มเติมว่า Istio สามารถทำอะไรได้อีกบ้าง ผมจะแนบลิงก์ Reference ไว้ให้ด้านล่างสามารถตามไปอ่านกันได้เลย หากมีข้อผิดพลาดประการใดก็ขออภัยมา ณ ที่นี้ด้วยนะครับ

และเพื่อนๆสามารถติดตามกันได้ที่ https://www.facebook.com/sirisoft แล้วพบกันใหม่ ขอบคุณครับ 👋🏻👋🏻👋🏻

Reference

--

--