TL;DR
- Services Overview: service เป็น object หนึ่งที่ทำหน้าที่เป็น load balancer แบบง่ายๆ ของ kubernetes จุดประงสงค์ คือ share load ระหว่าง pods ที่ทำงานแบบเดียวกับ โดยอาศัย Pods Label ในการระบุ Pods ที่เป็นตัวรับ load
- Service Update Pattern: ควรระบุ application version ใน label selector ของ service เพื่อเวลา update application ใน deployment จะได้ไม่มี traffic เข้าไปยัง pods ใหม่ก่อนที่จะพร้อม เมื่อทุกอย่างพร้อมค่อย Update label เพื่อชี้ไปยัง pods ใหม่
- Accessing an Application with a Service: เป็นการลองสร้าง service แบบ NodePort แล้วลอง เรียกใช้งาน server จากภายนอก
- Service Types: มี 4 ชนิด คือ ClusterIP, NodePort, LoadBalancer และ ExternalName
- Services Diagram: พูดถึง service แต่ละชนิด ได้แก่ user space proxy, iptables proxy, ipvs proxy
- Local Proxy for Development: เป็นการ run local proxy เชื่อเครื่องเรากับ cluster เพื่อใช้ในการ test service ที่เรายัง develope อยู่
- DNS: Cluster DNS ของ Kuberbenetes ซึ่งตอนนี้ใช้ CoreDNS
- Verifying DNS Registration: การทำสอบ และ troubleshoot DNS services
Services Overview
จากที่เราทราบกันแล้วว่าหลักสำคัญของ kubernetes คือ "transient" และ "decoupled" นั่นหมายความว่า แต่ละ app ต้องแยกออกจากกันโดยอิสระ และ เกิดตายได้ตลอดเวลา รวมถึงการเพิ่มและลดจำนวน pod แบบอัตโนมัติ เพื่อให้เหมาะสมกับ traffic จึงเป็นเรื่องที่ยุ่งยากและซ้ำซ้อนมากหากเราจะต้องเขียน logic เอง เพื่องานนี้
kubernetes จึงมีสิ่งที่เรียกว่า "service" เพื่อมาช่วยเราในเรื่องนี้ service ทำหน้าที่เป็นคล้ายๆ load balancer แบบง่ายๆ ที่คอยตรวจสอบสถานะของ pod ที่มี label ตรงกับที่กำหนดไว้ เพื่อทำการ share load ไปยัง pod ต่างๆ ที่อยู่ในสภาวะพร้อมใช้งาน
โดยเบื้องหลัง service จะมี kube-proxy เป็นตัวช่วยตรวจสอบสถานะของ pod ที่มี label ตามที่กำหนดจาก kubernetes API และรวบรวม pods ที่อยู่ในสถานะพร้อมใช้งานเป็น Endpoint
จากนั้นจึงสั่งให้ iptables พา traffic ไปยัง endpoint นั้นๆ โดย 1 service จะมี 1 IP คล้ายๆ กับ VIP ของ load balancer และ IP ดังกล่าวจะถูกเก็บไว้ที่ etcd database เพื่อให้ core dns (internal dns ของ kubernetes) นำไปใช้งาน ทำให้เราสามารถเรียกใช้งานด้วยชื่อของ service
service สามารถชี้ไปยัง Endpoint
ทั้งภายในและภายนอก cluster เช่น third party database
ใน Google มีการใช้พัฒนาและใช้งาน EPS
(Extensible Service Proxy) ซึ่งมีพื้นฐานมาจาก nginx reverse proxy โดยมันมีความยืดหยุ่นและมีประสิทธิภาพมากกว่า Endpoint
แต่ยังไม่ค่อยได้รับความสนใจจากภายนอกมากนัก
Service Update Pattern
จากที่เรารู้แล้วว่า Service
จะแจก load ไปยัง pods ที่มี Label
ตรงตามที่กำหนดไว้ และ Label
นั้นสามารถ update ได้ตลอดเวลาโดยไม่มีผลกระทบอะไรกับ pods ทำให้เป็นไปได้ง่ายมากที่เราจะพลาด deploy application version ใหม่แล้ว มี traffic ไหลเข้าไปโดยทันที และเนื่องด้วย depfault update strategy ของ Kubernetes คือ "Rolling Update" ดังนั้น ระหว่างที่ update จะมี application version ใหม่ และ version เก่า run ไปพร้อมๆ กันอยู้ช่วงเวลาหนึ่ง ซึ่งถ้าอาจทำให้เกิดปัญหากับผู้เรียกใช้งานได้
วิธีที่แนะนำคือให้ระบุ version ของ application ลงไปใน Label
ของ pods ใน Deployment
และ ที่ Service
ก็ระบุ Label
ของ Endpoint
ด้วย Label
เดียวกันที่มี version ของ application ดังนั้น เมื่อทำการ update application version ให้ create Deployment
ใหม่ขึ้นมา เมื่อเราตรวจสอบว่าทุกอย่างเรียบร้อยดี ค่อย update Label
ของ Endpoint
ของ Service
ไปยัง version ใหม่ ตาม Deployment
ใหม่ของเรา จากนั้นค่อย delete Deployment
เก่าให้หมด
Accessing an Application with a Service
เราสามารถใช้ kubectl expose ...
เพื่อทำการสร้าง Services
ได้ โดยจะเป็นการสร้าง Service
ประเภท NodePort
ที่มีการ assign random port ขึ้นมาเพื่อรับ traffic จากภายนอก แล้ว map กับ port ของ pod ที่เป็น Endpoint
เราสามารถระบุ port
และ targetPort
ได้ด้วย ตอนสร้าง Service
เพื่อลดความยุ่งยากหากเราต้องการเปลี่ยน port ของ Pods เราสามารถกำหนดชื่อของ port ใน deployment แล้ว ระบุ port
ด้วย ชื่อที่เรากำหนดไว้ได้
range ของ random port นี้ สามารถกำหนดได้โดยระบุในตัวแปร NodePort
ตอนสร้าง cluster
Service สามารถชี้ข้าม Namespace ได้ด้วย
ตัวอย่างการใช้งาน Service แบบง่ายๆ
- สร้าง Pods และ Service ที่ชี้ไปยัง Pods นั้นๆ
# kubectl create deployment nginx --image=nginx
deployment.apps/nginx created
# kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
nginx 0/1 1 0 9s
# kubectl expose deployment/nginx --port=80 --type=NodePort
service/nginx exposed
- ตรวจสอบความสัมพันธ์
# kubectl get pod
NAME READY STATUS RESTARTS AGE
nginx-86c57db685-qrkm2 1/1 Running 0 48m
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 58d
nginx NodePort 10.110.137.109 <none> 80:32285/TCP 18s #<-- Service ที่เราสร้างขึ้น
# kubectl get pod nginx-86c57db685-qrkm2 -o yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
cni.projectcalico.org/podIP: 192.168.32.49/32
cni.projectcalico.org/podIPs: 192.168.32.49/32
creationTimestamp: "2020-03-14T22:39:35Z"
generateName: nginx-86c57db685-
labels:
app: nginx #<-- label ของ Pod
pod-template-hash: 86c57db685
name: nginx-86c57db685-qrkm2
namespace: default
ownerReferences:
: :
# kubectl get svc nginx -o yaml
apiVersion: v1
kind: Service
metadata:
creationTimestamp: "2020-03-14T22:39:48Z"
labels:
app: nginx
name: nginx
namespace: default
resourceVersion: "12379326"
selfLink: /api/v1/namespaces/default/services/nginx
uid: 31b30266-157c-4ffb-8830-52613f712eaa
spec:
clusterIP: 10.110.137.109
externalTrafficPolicy: Cluster
ports:
- nodePort: 32285 #<-- Random port ที่ถูก assign เพื่อใช้รับ traffic จากภายนอก
port: 80 #<-- Port ของ Pod ที่เป็น Endpoint
protocol: TCP
targetPort: 80 #<-- ตาม default จะเป็นค่าเดียวกับ port:
selector:
app: nginx #<-- label ของ Pod ที่ Service ชี้ไป
sessionAffinity: None
type: NodePort
status:
loadBalancer: {}
- ลอง curl ไปยัง IP ของ server ใดก็ได้ ใน cluster จะเห็นว่าสามารถทำงานได้
# curl 127.0.0.1:32285
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
Service Types
Service
ของ kubernetes มี 4 ประเภท คือ
-
ClusterIP:
เป็น service ที่สามารถใช้ภายใน cluster เท่านั้น เป็น default type
-
NodePort:
เป็น service ที่จะ listen random port ที่กำหนดไว้ที่ทุกๆ server ใน cluster เราสามารถกำหนด range ของ port โดยระบุในตัวแปร NodePort หรือ option --service-node-port-range ตอนสร้าง cluster (default = 30000-32767) ในการใช้งานต้องมี static IP และต้อง allow Firewall ให้ครบทุก servers
-
LoadBalancer:
เป็นการ expose service ให้ภายนอกใช้ ผ่าน load balancer ของ Cloud Providers โดยจะต้องมีการระบุ Cloud Providers ตอน สร้าง cluster เพื่อให้ kubernertes ไปคุยกับ API ของ Cloud Provider ได้ นั่นหมายความว่า จะต้องเป็น Cloud Provider ที่ Support ด้วย
-
ExternalName:
ไม่มีการกำหนด selector โดยจะเป็นการ map service กับ External Domain name โดยใช้ CNAME ของ cluster DNS
Services Diagram
การทำงานของ Service
ที่ไม่ใช่ ExternameName
จะมี kube-proxy
เป็นคนคอยจัดการตรวจสอบสถานะของ resources จาก API server และจัดการเรื่อง virtual server ให้ โดย technic ที่ kube-proxy
ใช้มีการเปลี่ยนแปลงไปเรื่อยๆ ในแต่ละ version
ใน v1.0, Service
จะทำงานใน mode user space proxy คือ จะทำงานเป็น Layer 4 proxy ใน mode นี้สามารถทำ SessionAffinity ได้ และ default algoritm คือ round-robin เบื้องหลังการทำงานก็คือ "iptables rules" นั่นเอง
ใน v1.1, mode iptables proxy ถูกเพิ่มเข้ามาและกลายเป็น default mode ใน v1.2 เป็นต้นไป โดย ใน mode นี้ยังคงทำงานโดยอาศัย iptable rules เหมือนเดิม แต่เปลี่ยนมาใช้ "Linux Nerfilter" แทน user space ทำให้ไม่ต้อง switch ไปมาระหว่าง user space และ kernel space เป็นผลให้ทำงานได้ขึ้น
นอกจากนั้นใน mode นี้ยังเพิ่มการ retry ไปยัง pod อื่น ให้อัตโนมัติ หาก pod ส่ง request ไปไม่ตอบสนอง
และเพื่อลดการ retry ใน mode นี้ ยังมีการตรวจสอบ status ของ readiness probe ของ pods ด้วย โดยจะส่ง request ไปยัง pods ที่ readiness status มีค่าเป็น healthy เท่านั้น
ใน v1.9, mode IPVS proxy ถูกเพิ่มในเข้ามา และ stable ใน v1.11 โดยใน mode นี้เป็นจะการแก้ปัญหาเรื่อง overhead ของ iptables rule ใน cluster ที่ใหญ่มากๆ (5000 nodes ขึ้นไป) เพราะ iptable ทำงานแบบ rule-base ที่ check จากบนลงล่าง ดังนั้นเมื่อ rule เยอะขึ้น overhead ก็มากขึ้น kubernetes จึงเปลี่ยนมาใช้ IPVS ของ Linux ในการสร้าง virtual server และ share load ไปยัง Endpoint
ด้วย feature ของ IPVS ทำให้ ใน mode นี้ share load ได้หลาย algorithm เช่น rr
: round-robin, lc
: least connection, dh
: destination hashing, sh
: source hashing, sed
: shortest expected delay และ nq
: never queue
Local Proxy for Development
จากในการทำงาน หากจะ test service ที่เรากำลัง develope อยู่ ตัวช่วยที่ง่ายที่สุดคือ local proxy
Local proxy จะสร้าง encryption tunnel ระหว่างเครื่องเราและ cluster โดยทำให้เราสามารถเรียก localhost ของเราเพื่อต่อไปยัง Kubernetes API server ของ cluster เราได้
จากนั้นเราสามารถ curl เข้าไปยัง Service
ของเราได้ โดยใช้ URL ดังนี้
http://localhost:8001/api/v1/namespaces/<namespace_name>/services/<service_name>:<port>/proxy/
# kubectl proxy &
Starting to serve on 127.0.0.1:8001
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 58d
nginx NodePort 10.110.137.109 <none> 80:32285/TCP 6h37m
# curl http://localhost:8001/api/v1/namespaces/default/services/nginx:80/proxy/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
DNS
ตั้งแต่ v1.13, Cluster DNS ที่ kubernetes ใช้โดย default คือ CodeDNS ซึ่งสามารถเพิ่ม functionality ได้โดยการเพิ่ม plugin
เราสามารเข้าไปเลือก plugin ที่เราต้องการได้จาก CoreDNS Plugins โดย Plugins ที่ใช้บ่อยๆ ตัวอย่างเช่น การ export metrics ให้ prometheus, error logging, health reporting และ TLS เพื่อ configure certificate และ gRPC servers
Verifying DNS Registration
เมื่อเราทำการ set up cluster แล้ว จะรู้ได้อย่างไรว่า CoreDNS ใช้งานได้ ? วิธีการง่ายๆ ก็คือ run pod ขึ้นมา แล้วลง nslookup จากในนั้น
# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 58d
nginx NodePort 10.110.137.109 <none> 80:32285/TCP 7h5m
# kubectl run --generator=run-pod/v1 -it --rm busybox --image=busybox
If you don't see a command prompt, try pressing enter.
/ # cat /etc/resolv.conf #<-- check that DNS server is properly configure
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local openstacklocal novalocal
options ndots:5
/ # nslookup nginx #<-- Test nslookup for the service nginx
Server: 10.96.0.10
Address: 10.96.0.10:53
Name: nginx.default.svc.cluster.local
Address: 10.110.137.109
/ # exit
ถ้าพบปัญหา อาจลองดู Log ของ pods ที่มีชื่อขึ้นต้นด้วย coredns โดยหาบรรทัดที่มีคำว่า W (warning), E (error) และ F (failure)
Top comments (0)