Kubernetes ตั้งแต่ศูนย์ถึงระดับมืออาชีพ: 100 ตัวอย่างด้วย kubectl, Manifest, Helm และ GitOps
Kubernetes รันคอนเทนเนอร์บนกลุ่มเครื่องที่เรียกว่าคลัสเตอร์ มันรีสตาร์ตคอนเทนเนอร์ที่ล้มเหลว กระจายคอนเทนเนอร์ไปยังโหนดต่างๆ ปล่อยเวอร์ชันใหม่โดยไม่หยุดให้บริการ และปรับขนาดตามโหลด บทความนี้พาคุณจาก Pod แรกไปจนถึงการตั้งค่าที่คำนึงถึงโปรดักชันใน 100 ตัวอย่าง คุณจะได้เรียน kubectl และ context, Pod, Deployment และการอัปเดตแบบค่อยเป็นค่อยไป, Service และ Ingress, ConfigMap และ Secret, ที่เก็บข้อมูลถาวร, Job และ CronJob, StatefulSet และ DaemonSet, การปรับขนาดอัตโนมัติ, การขอทรัพยากรและ probe, การจัดตาราง, RBAC และความปลอดภัยของ Pod, Helm และ Kustomize, การสังเกตการณ์และการดีบัก, GitOps และการสำรองข้อมูล หมายเหตุตามจริง: เครื่องที่ใช้เตรียมบทความนี้ไม่ได้ติดตั้งคลัสเตอร์ Kubernetes หรือ kubectl จึงไม่ได้รันตัวอย่างทั้ง 100 ข้อที่นี่ manifest เขียนตาม Kubernetes API ปัจจุบัน แต่คุณควรนำไปใช้กับคลัสเตอร์ทดสอบด้วยตัวเองก่อนพึ่งพา
สิ่งที่ต้องเตรียม
Docker Desktop ที่มี Kubernetes ในตัว หรือ kind หรือ minikube สำหรับคลัสเตอร์ฝึกบนเครื่อง พร้อม kubectl และ Helm ความรู้พื้นฐานเรื่องคอนเทนเนอร์จะช่วยได้ บทความ Docker ในชุดนี้เป็นจุดเริ่มต้นที่ดี บางตัวอย่างใช้ผู้ให้บริการคลาวด์อย่าง Azure Kubernetes Service และ ingress controller ให้ใช้โฮสต์ตัวอย่างเช่น shop.example.com และรหัสผ่านตัวอย่างเช่น <strong-password> ห้ามใส่ข้อมูลรับรองจริงลงใน manifest หรือคำสั่ง
วิธีใช้ตัวอย่าง
ตัวอย่าง bash ใช้กับ kubectl, Helm และ Kustomize ตัวอย่าง YAML ให้บันทึกเป็นไฟล์ .yaml แล้วใช้ kubectl apply -f ตามชื่อไฟล์ที่ระบุ ตัวอย่าง Python ใช้ไลบรารี kubernetes ของ Python
หัวข้อที่ 1: เริ่มต้นกับ Kubernetes
Kubernetes คือลูปควบคุม คุณอธิบายสถานะที่ต้องการด้วย YAML แล้วคลัสเตอร์จะทำงานต่อจนสภาพจริงตรงกับที่ต้องการ เรียนรู้องค์ประกอบของคลัสเตอร์ ไคลเอนต์ kubectl context และ namespace
control plane และโหนด
control plane เก็บสถานะที่ต้องการและตัดสินใจ ส่วนโหนดของ worker รันคอนเทนเนอร์ของคุณ คุณคุยกับคลัสเตอร์ผ่าน API server และ kubectl คือไคลเอนต์มาตรฐานที่ส่งคำขอของคุณ
context และ namespace
context เก็บว่าคำสั่งของคุณใช้คลัสเตอร์ ผู้ใช้ และ namespace ใด ตรวจด้วย kubectl config current-context ก่อนเปลี่ยนแปลงอะไร เพื่อไม่ให้ลบทรัพยากรผิดคลัสเตอร์
manifest และการเปลี่ยนแปลงแบบ declarative
manifest มี apiVersion, kind, metadata และ spec คำสั่ง kubectl apply เทียบไฟล์ของคุณกับคลัสเตอร์และเปลี่ยนเฉพาะส่วนที่ต่างกัน ทำให้การเปลี่ยนแปลงทำซ้ำได้และตรวจสอบใน Git ได้ง่าย
ภาพประกอบ
ตัวอย่างที่ 1: ตรวจสอบ client และคลัสเตอร์
ยืนยันว่า kubectl คุยกับคลัสเตอร์ได้
kubectl version --client
kubectl cluster-info
kubectl get nodes -o wide
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 2: สร้างคลัสเตอร์ฝึกด้วย kind
คลัสเตอร์บนเครื่องของคุณสำหรับการเรียนรู้
kind create cluster --name learn
kubectl config current-context
kubectl get nodes
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 3: สลับ context
เลือกว่าคำสั่งจะไปที่คลัสเตอร์ไหน
kubectl config get-contexts
kubectl config use-context kind-learn
kubectl config view --minify --output "jsonpath={.current-context}"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 4: สร้าง namespace
แยกทีมและแอปภายในคลัสเตอร์เดียว
kubectl create namespace shop
kubectl config set-context --current --namespace=shop
kubectl get pods
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 5: อ่าน manifest
ฟิลด์ระดับบนสุดสี่ตัวที่ทุกออบเจกต์มี
apiVersion: v1
kind: Namespace
metadata:
name: shop
labels:
team: platform
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 6: apply manifest
ทำการเปลี่ยนแปลงแบบ declarative ด้วย kubectl apply
kubectl apply -f namespace.yaml
kubectl get namespace shop --show-labels
kubectl describe namespace shop
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 2: Pod: หน่วยที่เล็กที่สุด
Pod คือคอนเทนเนอร์หนึ่งตัวหรือมากกว่าที่ใช้ที่อยู่เครือข่ายและที่เก็บข้อมูลร่วมกัน Pod ส่วนใหญ่มีคอนเทนเนอร์เดียว แต่ Pod ยังบรรจุตัวช่วยที่ต้องทำงานคู่กับแอปหลักได้
Pod คืออะไร
คอนเทนเนอร์ทั้งหมดใน Pod ใช้ IP เดียวกันและเข้าถึงกันผ่าน localhost ได้ Pod เป็นของชั่วคราว เมื่อตัวใดตาย คลัสเตอร์จะสร้างตัวใหม่ที่มีชื่อและที่อยู่ใหม่ ดังนั้นอย่าพึ่งที่อยู่ของ Pod
init และ sidecar container
init container ทำงานจนเสร็จก่อนคอนเทนเนอร์หลักจะเริ่ม เหมาะกับการรอฐานข้อมูล sidecar ทำงานคู่กับคอนเทนเนอร์หลักเพื่องานบันทึก พร็อกซี หรือซิงก์ไฟล์ และใช้วอลุ่มร่วมกัน
log, event และ exec
kubectl logs แสดงผลลัพธ์ kubectl describe แสดง event เช่นข้อผิดพลาดการดึงอิมเมจและการจัดตารางที่ล้มเหลว kubectl exec เปิด shell อ่าน describe ก่อนเสมอ เพราะอธิบายความล้มเหลวตอนเริ่มส่วนใหญ่ได้
ตัวอย่างที่ 7: รัน Pod อย่างรวดเร็ว
คำสั่งแบบ imperative ใช้ทดสอบเร็วเท่านั้น
kubectl run hello --image=nginx:1.27 --port=80
kubectl get pod hello -o wide
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 8: Pod แบบ declarative
หน่วยที่เล็กที่สุดที่คุณ deploy
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 9: อ่าน log และ event
หาว่าเกิดอะไรขึ้นกับ Pod
kubectl logs web
kubectl logs web --previous
kubectl get events --sort-by=.lastTimestamp
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 10: รันคำสั่งภายใน Pod
ดีบักจากข้างใน
kubectl exec -it web -- sh
kubectl exec web -- nginx -v
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 11: port-forward ไปยัง Pod
เข้าถึง Pod จากคอมพิวเตอร์ของคุณเพื่อทดสอบ
kubectl port-forward pod/web 8080:80
curl http://localhost:8080
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 12: คอนเทนเนอร์สองตัวใช้วอลุ่มร่วมกัน
รูปแบบ sidecar
apiVersion: v1
kind: Pod
metadata:
name: sidecar-demo
spec:
volumes:
- name: logs
emptyDir: {}
containers:
- name: app
image: busybox:1.36
command: ["sh", "-c", "while true; do date >> /logs/app.log; sleep 5; done"]
volumeMounts:
- name: logs
mountPath: /logs
- name: log-reader
image: busybox:1.36
command: ["sh", "-c", "tail -F /logs/app.log"]
volumeMounts:
- name: logs
mountPath: /logs
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 13: init container รอให้พร้อม
รันขั้นตอนเตรียมก่อนที่แอปจะเริ่ม
apiVersion: v1
kind: Pod
metadata:
name: init-demo
spec:
initContainers:
- name: wait-for-db
image: busybox:1.36
command: ["sh", "-c", "until nslookup db; do sleep 2; done"]
containers:
- name: app
image: nginx:1.27
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 14: ลบ Pod สำหรับทดสอบ
ล้างออบเจกต์ที่ไม่ต้องใช้แล้ว
kubectl delete pod hello web sidecar-demo init-demo --ignore-not-found
kubectl get pods
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 3: Deployment และการอัปเดตแบบค่อยเป็นค่อยไป
คุณแทบไม่เคยสร้าง Pod โดยตรง Deployment จัดการ ReplicaSet ซึ่งรักษาจำนวน Pod ที่ต้องการให้ทำงานอยู่ และทำการอัปเดตแบบค่อยเป็นค่อยไปเพื่อให้เวอร์ชันใหม่แทนที่เวอร์ชันเก่าทีละน้อย
จำนวนเรพลิกาและ selector
ฟิลด์ replicas กำหนดจำนวน Pod ที่ต้องการ selector ต้องตรงกับ label ของ Pod เพื่อให้ Deployment รู้ว่าเป็นเจ้าของ Pod ใด หากไม่ตรงจะถูกปฏิเสธตอนสร้าง
กลยุทธ์ RollingUpdate
maxSurge กำหนดจำนวน Pod ส่วนเกินที่อนุญาตระหว่างอัปเดต และ maxUnavailable กำหนดจำนวนที่อาจขาดหายไป การตั้ง maxUnavailable เป็น 0 จะรักษาความจุเต็มระหว่างการปล่อยเวอร์ชัน
ประวัติ rollout และการย้อนกลับ
การเปลี่ยนแปลงแต่ละครั้งสร้าง ReplicaSet ใหม่และหมายเลขรีวิชัน kubectl rollout undo กลับไปเวอร์ชันก่อนหน้า และ kubectl rollout history แสดงรายการรีวิชันให้คุณเลือกได้
ตัวอย่างที่ 15: Deployment รักษาจำนวนเรพลิกาให้ทำงาน
การอัปเดตแบบค่อยเป็นค่อยไปที่จัดการให้
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 16: สร้างและติดตาม Deployment
ตรวจสถานะการ rollout
kubectl apply -f deployment.yaml
kubectl rollout status deployment/web
kubectl get deploy,rs,pods -l app=web
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 17: ปรับจำนวนด้วยมือ
เปลี่ยนจำนวนเรพลิกา
kubectl scale deployment web --replicas=5
kubectl get pods -l app=web -o wide
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 18: rollout อิมเมจใหม่
อัปเดตเวอร์ชันของแอป
kubectl set image deployment/web web=nginx:1.27.2
kubectl rollout status deployment/web
kubectl rollout history deployment/web
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 19: ย้อนการปล่อยที่มีปัญหา
ยกเลิก rollout ที่ผิดพลาด
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=1
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 20: ควบคุมการอัปเดตแบบค่อยเป็นค่อยไป
maxSurge และ maxUnavailable
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 21: หยุดชั่วคราวและกลับมาทำต่อ
รวมการเปลี่ยนแปลงหลายอย่างเข้าด้วยกัน
kubectl rollout pause deployment/web
kubectl set resources deployment/web -c web --limits=memory=256Mi
kubectl rollout resume deployment/web
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 22: readiness probe
ทราฟฟิกรอจนกว่าแอปจะพร้อม
containers:
- name: web
image: nginx:1.27
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 3
periodSeconds: 5
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 4: Service และเครือข่าย
Pod มาๆ ไปๆ ดังนั้นไคลเอนต์ต้องการชื่อและที่อยู่ที่คงที่ Service เลือก Pod ด้วย label และให้ IP เสมือนหนึ่งตัวกับชื่อ DNS ที่ไม่เปลี่ยน
ชนิดของ Service
ClusterIP เข้าถึงได้เฉพาะภายในคลัสเตอร์ NodePort เปิดพอร์ตบนทุกโหนด LoadBalancer ขอโหลดบาลานเซอร์ภายนอกจากผู้ให้บริการคลาวด์ เลือกชนิดที่เล็กที่สุดที่ตอบโจทย์
DNS และ endpoint
ทุก Service มีชื่อ DNS เช่น web-svc.shop.svc.cluster.local และภายใน namespace เดียวกันใช้ชื่อสั้นได้ kubectl get endpoints แสดงที่อยู่ของ Pod ที่อยู่หลัง Service ซึ่งเป็นสิ่งแรกที่ควรตรวจเมื่อ Service ไม่ตอบสนอง
Service แบบ headless
เมื่อกำหนด clusterIP เป็น None DNS จะคืนที่อยู่ของ Pod แต่ละตัวแทนที่จะเป็น IP เสมือนตัวเดียว ฐานข้อมูลและระบบแบบคลัสเตอร์ใช้วิธีนี้เพื่อให้ไคลเอนต์คุยกับสมาชิกที่ระบุได้
ตัวอย่างที่ 23: Service แบบ ClusterIP
ที่อยู่คงที่ภายในคลัสเตอร์
apiVersion: v1
kind: Service
metadata:
name: web
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 24: expose ด้วย kubectl
วิธีรวดเร็วในการสร้าง Service
kubectl expose deployment web --port=80 --target-port=80 --name=web-svc
kubectl get svc web-svc
kubectl get endpoints web-svc
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 25: เรียก Service จาก Pod ชั่วคราว
ทดสอบ DNS ภายในคลัสเตอร์
kubectl run tmp-client --rm -it --image=busybox:1.36 --restart=Never -- wget -qO- http://web-svc
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 26: Service แบบ NodePort
เปิดพอร์ตบนทุกโหนด
apiVersion: v1
kind: Service
metadata:
name: web-nodeport
spec:
type: NodePort
selector:
app: web
ports:
- port: 80
targetPort: 80
nodePort: 30080
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 27: Service แบบ LoadBalancer
โหลดบาลานเซอร์ของคลาวด์สำหรับเข้าถึงสาธารณะ
apiVersion: v1
kind: Service
metadata:
name: web-public
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "false"
spec:
type: LoadBalancer
selector:
app: web
ports:
- port: 80
targetPort: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 28: หาที่อยู่ภายนอก
อ่านที่อยู่ที่คลาวด์กำหนดให้
kubectl get svc web-public -o jsonpath="{.status.loadBalancer.ingress[0].ip}"
kubectl get svc web-public -w
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 29: พอร์ตที่มีชื่อ
ใช้ชื่อพอร์ตเพื่อเปลี่ยนหมายเลขได้อย่างปลอดภัย
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: api
ports:
- name: http
port: 8000
targetPort: http
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 30: Service แบบ headless
รับที่อยู่ของ Pod แต่ละตัว
apiVersion: v1
kind: Service
metadata:
name: db-headless
spec:
clusterIP: None
selector:
app: db
ports:
- port: 5432
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 5: การตั้งค่าด้วย ConfigMap และ Secret
เก็บการตั้งค่าไว้นอกอิมเมจ ConfigMap เก็บการตั้งค่าทั่วไป ส่วน Secret เก็บค่าที่ละเอียดอ่อน ทั้งสองอย่างกลายเป็นตัวแปรสภาพแวดล้อมหรือไฟล์ภายใน Pod ได้ จึงเปลี่ยนการตั้งค่าได้โดยไม่ต้อง build อิมเมจใหม่
ConfigMap
ConfigMap เก็บคู่คีย์-ค่าหรือทั้งไฟล์ ใช้ envFrom เพื่อโหลดทุกคีย์เป็นตัวแปรสภาพแวดล้อม หรือเมานต์ ConfigMap เป็นไดเรกทอรีเมื่อแอปอ่านจากไฟล์
Secret
Secret เก็บค่าที่เข้ารหัส base64 base64 เป็นการเข้ารหัสเท่านั้น ไม่ใช่การเข้ารหัสที่ปกป้องข้อมูล จึงควรเปิดการเข้ารหัสขณะจัดเก็บในคลัสเตอร์ จำกัดผู้ที่อ่าน Secret ได้ด้วย RBAC และเมานต์เป็นไฟล์เมื่อทำได้
การโหลดการเปลี่ยนแปลงใหม่
ตัวแปรสภาพแวดล้อมถูกอ่านเพียงตอนเริ่ม ดังนั้น ConfigMap ที่เปลี่ยนต้องมีการ rollout ใหม่ ไฟล์ที่เมานต์จะอัปเดตหลังดีเลย์ แต่แอปต้องอ่านไฟล์นั้นใหม่ วางแผนการโหลดใหม่ทุกครั้งที่เปลี่ยนการตั้งค่า
ตัวอย่างที่ 31: ConfigMap จากค่าโดยตรง
เก็บการตั้งค่าที่ไม่ใช่ความลับ
kubectl create configmap app-config --from-literal=LOG_LEVEL=info --from-literal=FEATURE_X=on
kubectl get configmap app-config -o yaml
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 32: manifest ของ ConfigMap
การตั้งค่าเขียนเป็น YAML
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
LOG_LEVEL: info
FEATURE_X: "on"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 33: ConfigMap เป็นตัวแปรสภาพแวดล้อม
โหลดทุกคีย์ด้วย envFrom
containers:
- name: api
image: sample-api:1.4.2
envFrom:
- configMapRef:
name: app-config
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 34: เมานต์ ConfigMap เป็นไฟล์
ไฟล์การตั้งค่าภายในคอนเทนเนอร์
containers:
- name: web
image: nginx:1.27
volumeMounts:
- name: site-config
mountPath: /etc/nginx/conf.d
readOnly: true
volumes:
- name: site-config
configMap:
name: nginx-conf
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 35: สร้าง Secret
เก็บรหัสผ่านโดยไม่เขียนลงไฟล์
kubectl create secret generic db-secret --from-literal=DB_PASSWORD='<strong-password>'
kubectl get secret db-secret -o jsonpath="{.type}"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 36: manifest ของ Secret
stringData ให้เขียนข้อความธรรมดาได้
apiVersion: v1
kind: Secret
metadata:
name: db-secret
type: Opaque
stringData:
DB_PASSWORD: <strong-password>
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 37: อ่านค่าใน Secret
ตรวจค่าที่ถอดรหัสแล้วและเก็บเป็นความลับ
kubectl get secret db-secret -o jsonpath="{.data.DB_PASSWORD}" | base64 --decode; echo
# Do not print secrets in shared logs or screenshots.
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 38: เมานต์ Secret เป็นไฟล์
ไฟล์ข้อมูลรับรองแบบอ่านอย่างเดียว
containers:
- name: api
image: sample-api:1.4.2
volumeMounts:
- name: db-creds
mountPath: /var/run/secrets/db
readOnly: true
volumes:
- name: db-creds
secret:
secretName: db-secret
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 6: ที่เก็บข้อมูลถาวร
คอนเทนเนอร์จะสูญเสียไฟล์เมื่อรีสตาร์ต PersistentVolumeClaim ขอที่เก็บข้อมูลที่อยู่รอดกว่าอายุของ Pod และ StorageClass บอกคลัสเตอร์ว่าจะสร้างที่เก็บนั้นอย่างไร
PV, PVC และ StorageClass
PersistentVolume คือที่เก็บข้อมูลจริง PersistentVolumeClaim คือคำขอใช้ที่เก็บนั้น และ StorageClass กำหนดวิธีสร้างวอลุ่มตามต้องการ คลัสเตอร์ส่วนใหญ่มี StorageClass เริ่มต้น ดังนั้น claim มักต้องระบุเพียงขนาด
โหมดการเข้าถึง
ReadWriteOnce เมานต์วอลุ่มบนโหนดเดียวในแต่ละครั้ง เหมาะกับฐานข้อมูลส่วนใหญ่ ReadWriteMany ให้หลายโหนดเขียนได้ แต่มีที่เก็บเพียงบางชนิดที่รองรับ เลือกโหมดที่ที่เก็บของคุณรองรับ
นโยบายการเรียกคืน
Delete ลบที่เก็บเมื่อลบ claim ส่วน Retain เก็บข้อมูลไว้ให้กู้คืนด้วยมือ ใช้ Retain กับข้อมูลที่ต้องรอดจากการลบโดยไม่ตั้งใจ และสำรองข้อมูลสำคัญไว้เสมอ
ตัวอย่างที่ 39: PersistentVolumeClaim
ขอพื้นที่จัดเก็บจากคลัสเตอร์
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-pvc
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 40: Pod ที่ใช้ claim
ข้อมูลอยู่รอดแม้ Pod จะรีสตาร์ต
apiVersion: v1
kind: Pod
metadata:
name: writer
spec:
containers:
- name: writer
image: busybox:1.36
command: ["sh", "-c", "echo hello >> /data/out.txt; sleep 3600"]
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: data-pvc
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 41: ตรวจพื้นที่ที่ผูกแล้ว
อ่านสถานะของ claim และวอลุ่ม
kubectl get pvc data-pvc
kubectl get pv
kubectl describe pvc data-pvc
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 42: เลือก StorageClass
เลือกชนิดของดิสก์
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fast-pvc
spec:
storageClassName: managed-premium
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 43: ดูรายการ StorageClass
หาคลาสเริ่มต้น
kubectl get storageclass
kubectl get storageclass -o custom-columns=NAME:.metadata.name,DEFAULT:.metadata.annotations."storageclass\.kubernetes\.io/is-default-class"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 44: ขยาย claim
เพิ่มพื้นที่ในคลาสที่รองรับ
kubectl patch pvc data-pvc -p '{"spec":{"resources":{"requests":{"storage":"2Gi"}}}}'
kubectl get pvc data-pvc
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 45: นโยบาย reclaim
เก็บข้อมูลไว้หลังลบ claim
apiVersion: v1
kind: PersistentVolume
metadata:
name: archive-pv
spec:
capacity:
storage: 5Gi
accessModes: ["ReadWriteOnce"]
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /srv/archive
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 46: จำกัด ephemeral storage
หยุดไฟล์ชั่วคราวที่โตไม่หยุด
containers:
- name: worker
image: busybox:1.36
resources:
limits:
ephemeral-storage: 1Gi
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 7: Ingress และการกำหนดเส้นทางทราฟฟิก
Ingress เปิดเส้นทาง HTTP และ HTTPS จากภายนอกคลัสเตอร์ไปยัง Service มันกำหนดเส้นทางตามชื่อโฮสต์และ path และจบการเข้ารหัส TLS ทำให้ที่อยู่สาธารณะเดียวให้บริการแอปได้หลายตัว
Ingress และ controller
ทรัพยากร Ingress ทำงานไม่ได้เพียงลำพัง ingress controller เช่น ingress-nginx อ่านออบเจกต์ Ingress และตั้งค่าพร็อกซี ติดตั้ง controller หนึ่งตัวต่อคลัสเตอร์ และกำหนด ingressClassName ในแต่ละ Ingress
ใบรับรอง TLS
ส่วน tls ชี้ไปยัง Secret ที่เก็บใบรับรองและคีย์ cert-manager ขอและต่ออายุใบรับรองให้อัตโนมัติ คุณจึงไม่ต้องจัดการวันหมดอายุด้วยมือ
การกำหนดเส้นทางตาม path และโฮสต์
กฎจับคู่ชื่อโฮสต์และ path prefix path ที่เจาะจงที่สุดชนะ เช่น /api ไป API ขณะที่ / ไปเว็บไซต์ ควรให้เส้นทางเรียบง่ายเพื่อให้ตารางการกำหนดเส้นทางตรวจได้ง่าย
ตัวอย่างที่ 47: Ingress ตามชื่อโฮสต์
กำหนดเส้นทาง HTTP ไปยัง Service
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 48: Ingress กับ TLS
HTTPS ด้วยใบรับรองที่เก็บใน Secret
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-tls
spec:
ingressClassName: nginx
tls:
- hosts: [shop.example.com]
secretName: shop-tls
rules:
- host: shop.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 49: ติดตั้ง ingress controller
ติดตั้งตัวควบคุมด้วย Helm
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress ingress-nginx/ingress-nginx --namespace ingress --create-namespace
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 50: กำหนดเส้นทางตาม path
ส่ง /api และ / ไปยัง Service คนละตัว
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: shop-routes
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 8000
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 51: ความเป็นไปได้ของ annotation สำหรับการอัปโหลด
เพิ่มขีดจำกัดขนาดการอัปโหลดและเวลาหมดอายุสำหรับเว็บไซต์หนึ่ง
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: upload-ingress
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "20m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
spec:
ingressClassName: nginx
rules:
- host: upload.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: uploader
port:
number: 80
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 52: ClusterIssuer ของ cert-manager
ขอใบรับรองจาก Let's Encrypt โดยอัตโนมัติ
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: [email protected]
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
ingressClassName: nginx
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 53: ขอใบรับรองจาก Ingress
ใช้ annotation เพื่อให้ระบบจัดการ TLS ให้
metadata:
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts: [shop.example.com]
secretName: shop-tls
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 54: NetworkPolicy สำหรับ API
อนุญาตเฉพาะชั้นเว็บให้เข้าถึง API ได้
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-allow-web
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: web
ports:
- port: 8000
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 8: Job และ CronJob
ไม่ใช่ทุกงานต้องทำงานตลอดเวลา Job รันงานจนสำเร็จ ส่วน CronJob เริ่ม Job ตามตารางเวลา เหมือนตาราง cron ที่ทำงานภายในคลัสเตอร์
Job และจำนวนที่สำเร็จ
Job สร้าง Pod จนกว่าจำนวน completions ที่ต้องการจะสำเร็จ backoffLimit จำกัดจำนวนครั้งที่ลองใหม่ ใช้ restartPolicy เป็น Never หรือ OnFailure เพราะ Job ไม่อนุญาต Always
งานขนาน
parallelism กำหนดว่ามี Pod กี่ตัวทำงานพร้อมกัน และ completions กำหนดจำนวนการรันที่สำเร็จที่ต้องการ รูปแบบนี้ประมวลผลชุดของรายการด้วย worker หลายตัว
CronJob และประวัติ
CronJob ใช้ไวยากรณ์ cron ในตารางเวลา concurrencyPolicy เป็น Forbid ป้องกันการรันซ้อนทับกัน และ history limits ลบ Job เก่าเพื่อให้คลัสเตอร์เป็นระเบียบ
ตัวอย่างที่ 55: Job ทำงานจนเสร็จ
งานครั้งเดียว เช่น การย้ายฐานข้อมูล
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
spec:
backoffLimit: 2
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: sample-api:1.4.2
command: ["python", "manage.py", "migrate"]
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 56: รัน Job และรอผล
รอจนงานเสร็จ แล้วอ่าน log
kubectl apply -f job.yaml
kubectl wait --for=condition=complete job/db-migrate --timeout=120s
kubectl logs job/db-migrate
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 57: Job แบบขนาน
ประมวลผลหลายรายการด้วยผู้ทำงานหลายตัว
apiVersion: batch/v1
kind: Job
metadata:
name: thumbnails
spec:
completions: 10
parallelism: 3
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: thumbnailer:2.0
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 58: CronJob ตามตารางเวลา
รันรายงานทุกคืนเวลา 02:00
apiVersion: batch/v1
kind: CronJob
metadata:
name: nightly-report
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report
image: reporter:1.1
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 59: ตรวจและสั่ง CronJob
ดูการรันที่ผ่านมาและเริ่มเองด้วยมือ
kubectl get cronjob nightly-report
kubectl get jobs --sort-by=.metadata.creationTimestamp
kubectl create job --from=cronjob/nightly-report manual-report-1
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 60: จำกัดประวัติของ Job
ไม่ให้ Job ที่เสร็จแล้วสะสมมากเกินไป
spec:
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
startingDeadlineSeconds: 300
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 9: StatefulSet และ DaemonSet
บางงานต้องการตัวตนที่คงที่หรือหนึ่ง Pod ต่อหนึ่งโหนด StatefulSet ให้ Pod มีชื่อเรียงลำดับและที่เก็บข้อมูลของตัวเอง ส่วน DaemonSet รันสำเนาของ Pod หนึ่งตัวบนทุกโหนดที่ตรงเงื่อนไข
ตัวตนของ StatefulSet
Pod ถูกตั้งชื่อ name-0, name-1 และต่อไป แต่ละตัวเก็บ claim ของตัวเองจาก volumeClaimTemplates ชื่อยังคงเดิมเมื่อ Pod รีสตาร์ต เหมาะกับฐานข้อมูลและ message broker
การทำงานตามลำดับ
โดยค่าเริ่มต้น StatefulSet เริ่มและหยุด Pod ตามลำดับ การอัปเดตแบบ partition ให้เปลี่ยนเฉพาะ Pod ที่หมายเลขสูงสุดก่อน ซึ่งเป็นการทดสอบแบบ canary ที่ควบคุมได้สำหรับระบบที่มีสถานะ
DaemonSet
DaemonSet รักษา Pod หนึ่งตัวบนแต่ละโหนด เหมาะกับตัวเก็บ log เอเจนต์ตรวจสอบ และปลั๊กอินเครือข่าย nodeSelector หรือ tolerations จำกัดให้ทำงานเฉพาะบางโหนด
ตัวอย่างที่ 61: StatefulSet
ชื่อและที่เก็บข้อมูลที่คงที่สำหรับฐานข้อมูล
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 1Gi
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 62: ตรวจ StatefulSet
ชื่อตามลำดับและ claim หนึ่งตัวต่อ Pod
kubectl get statefulset redis
kubectl get pods -l app=redis
kubectl get pvc
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 63: DaemonSet
Pod หนึ่งตัวบนทุกโหนด สำหรับ agent
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-agent
spec:
selector:
matchLabels:
app: node-agent
template:
metadata:
labels:
app: node-agent
spec:
containers:
- name: agent
image: busybox:1.36
command: ["sh", "-c", "sleep 3600"]
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 64: DaemonSet บนโหนดที่มีป้ายกำกับ
ใช้ nodeSelector เพื่อเลือกโหนด
spec:
template:
spec:
nodeSelector:
workload: edge
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 65: partition ของ StatefulSet
อัปเดต Pod บางส่วนก่อน
spec:
updateStrategy:
type: RollingUpdate
rollingUpdate:
partition: 2
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 66: ปรับขนาด StatefulSet อย่างระมัดระวัง
ตรวจ claim หลังลดจำนวน
kubectl get statefulset redis -o jsonpath="{.spec.replicas}"; echo
kubectl scale statefulset redis --replicas=2
kubectl get pvc -l app=redis
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 10: การปรับขนาดอัตโนมัติ
โหลดเปลี่ยนไปตลอดทั้งวัน การปรับขนาดอัตโนมัติเพิ่ม Pod เมื่อความต้องการสูง และลดเมื่อต่ำลง คุณจึงจ่ายเฉพาะสิ่งที่ใช้ และผู้ใช้ยังได้การตอบสนองที่รวดเร็ว
Horizontal Pod Autoscaler
HPA เทียบตัวชี้วัด เช่น การใช้ CPU เฉลี่ย กับเป้าหมาย แล้วปรับจำนวนเรพลิกา ต้องใช้ metrics server และการขอ CPU ใน Pod เพราะการใช้งานวัดเทียบกับค่าที่ขอ
พฤติกรรมการปรับขนาด
ฟิลด์ behavior ควบคุมความเร็วในการเพิ่มและลดขนาด stabilization window ตอนลดขนาดช่วยป้องกันการแกว่งเร็วหลังมีการพุ่งขึ้นชั่วคราว
การปรับขนาดตามเหตุการณ์
KEDA ปรับขนาดตามสัญญาณภายนอก เช่น ความยาวของคิวหรือความล่าช้าของข้อความ และลดเหลือศูนย์ได้เมื่อไม่มีงาน ใช้กับ worker ที่อ่านคิว
ตัวอย่างที่ 67: การใช้ทรัพยากรแบบเรียลไทม์
kubectl top ต้องใช้ metrics server
kubectl top nodes
kubectl top pods -l app=web
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 68: ปรับขนาดตาม CPU อัตโนมัติ
เพิ่ม Pod เมื่อโหลดเฉลี่ยสูง
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: web
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 69: สร้าง HPA จากบรรทัดคำสั่ง
ปรับขนาดอัตโนมัติและดูการตอบสนอง
kubectl autoscale deployment web --min=2 --max=10 --cpu-percent=70
kubectl get hpa web -w
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 70: ปรับขนาดตามหน่วยความจำ
ใช้มากกว่าหนึ่งตัวชี้วัด
spec:
metrics:
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 75
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 71: จำกัดความเร็วในการลดขนาดของ HPA
หลีกเลี่ยงการแกว่งด้วยช่วงรอเสถียรภาพ
spec:
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 1
periodSeconds: 60
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 72: ปรับขนาดตามเหตุการณ์
ปรับขนาด worker ตามความยาวคิวด้วย KEDA
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
name: worker-scaler
spec:
scaleTargetRef:
name: worker
minReplicaCount: 0
maxReplicaCount: 20
triggers:
- type: rabbitmq
metadata:
queueName: orders
mode: QueueLength
value: "50"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 11: ทรัพยากร probe และการจัดตาราง
บอก Kubernetes ว่าแต่ละคอนเทนเนอร์ต้องการ CPU และหน่วยความจำเท่าไร และจะรู้ได้อย่างไรว่าคอนเทนเนอร์สุขภาพดี ตัวจัดตารางใช้ requests เพื่อวาง Pod ส่วน probe ตัดสินว่าเมื่อใดควรรีสตาร์ตหรือส่งทราฟฟิก
requests และ limits
request คือปริมาณที่ตัวจัดตารางสงวนให้คอนเทนเนอร์ limit คือปริมาณสูงสุดที่ใช้ได้ หน่วยความจำที่เกิน limit ทำให้คอนเทนเนอร์ถูกฆ่า ส่วน CPU ที่เกินจะถูกจำกัดความเร็ว ตั้งค่าทั้งสองจากการใช้งานที่วัดได้
probe
liveness probe รีสตาร์ตคอนเทนเนอร์ที่ค้าง readiness probe เอาคอนเทนเนอร์ออกจาก endpoint ของ Service ขณะที่ยังไม่พร้อม startup probe ระงับ probe อื่นสำหรับการเริ่มที่ช้า
การกระจายและการหยุดชะงัก
topology spread constraints วางเรพลิกาไว้คนละโหนดหรือโซน PodDisruptionBudget จำกัดจำนวน Pod ที่การกระทำโดยสมัครใจ เช่น drain จะลบพร้อมกันได้
ตัวอย่างที่ 73: requests และ limits
ตัวจัดตารางและเคอร์เนลใช้ค่าเหล่านี้อย่างไร
containers:
- name: api
image: sample-api:1.4.2
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 74: liveness probe
รีสตาร์ตคอนเทนเนอร์ที่ค้าง
livenessProbe:
httpGet:
path: /healthz
port: 8000
initialDelaySeconds: 10
periodSeconds: 15
failureThreshold: 3
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 75: startup probe
ให้แอปที่เริ่มช้ามีเวลาเริ่มทำงาน
startupProbe:
httpGet:
path: /healthz
port: 8000
failureThreshold: 30
periodSeconds: 10
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 76: PodDisruptionBudget
รักษาจำนวนเรพลิกาให้พอระหว่างการบำรุงรักษา
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: web-pdb
spec:
minAvailable: 2
selector:
matchLabels:
app: web
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 77: กระจายเรพลิกาไปยังหลายโหนด
ไม่วางทุกเรพลิกาไว้บนเครื่องเดียว
spec:
topologySpreadConstraints:
- maxSkew: 1
topologyKey: kubernetes.io/hostname
whenUnsatisfiable: ScheduleAnyway
labelSelector:
matchLabels:
app: web
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 78: PriorityClass
ให้ Pod สำคัญได้รับการจัดตารางแม้คลัสเตอร์เต็ม
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
name: business-critical
value: 100000
globalDefault: false
description: "Keep these Pods running when the cluster is full."
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 12: ความปลอดภัย: RBAC และความปลอดภัยของ Pod
หลักสิทธิ์น้อยที่สุดใช้กับทั้งคนและแอปพลิเคชัน RBAC กำหนดว่าใครทำอะไรได้ ส่วนการตั้งค่าความปลอดภัยของ Pod จำกัดสิ่งที่คอนเทนเนอร์ทำได้บนโหนด
ServiceAccount, Role และ binding
ServiceAccount คือตัวตนของ Pod Role ระบุ verb ที่อนุญาตบนทรัพยากรภายใน namespace หนึ่ง และ RoleBinding ผูกเข้ากับบัญชี ให้สิทธิ์เฉพาะ verb ที่แอปต้องใช้ และหลีกเลี่ยง cluster-admin สำหรับแอป
การตรวจสิทธิ์
kubectl auth can-i ทดสอบสิทธิ์ โดยระบุผู้ใช้หรือ ServiceAccount อื่นได้ ควรรันในขั้นตอนตรวจทานเพื่อให้พบข้อผิดพลาดก่อนการ deploy ไม่ใช่หลังเหตุการณ์
Pod Security
label ของ namespace บังคับใช้ Pod Security Standards เช่น restricted ซึ่งบล็อกคอนเทนเนอร์ที่รันเป็น root และการยกระดับสิทธิ์ ใช้ร่วมกับ securityContext ที่ตัด capability และตั้งระบบไฟล์รากเป็นอ่านอย่างเดียว
ตัวอย่างที่ 79: ServiceAccount
ตัวตนของ Pod โดยปิดการใช้โทเค็น
apiVersion: v1
kind: ServiceAccount
metadata:
name: api-sa
automountServiceAccountToken: false
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 80: Role ที่มีสิทธิ์น้อยที่สุด
อ่าน ConfigMap ได้ในเนมสเปซเดียวเท่านั้น
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-config
namespace: shop
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 81: RoleBinding
ผูก Role เข้ากับ ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: api-read-config
namespace: shop
subjects:
- kind: ServiceAccount
name: api-sa
namespace: shop
roleRef:
kind: Role
name: read-config
apiGroup: rbac.authorization.k8s.io
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 82: ตรวจสิทธิ์ด้วย auth can-i
ยืนยัน RBAC ก่อนนำไป deploy
kubectl auth can-i list configmaps --as=system:serviceaccount:shop:api-sa -n shop
kubectl auth can-i delete pods --as=system:serviceaccount:shop:api-sa -n shop
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 83: Pod Security Standards
บังคับใช้โปรไฟล์ restricted กับเนมสเปซ
kubectl label namespace shop pod-security.kubernetes.io/enforce=restricted
kubectl label namespace shop pod-security.kubernetes.io/warn=restricted
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 84: security context ที่เสริมความแข็งแรง
ไม่ใช่ root อ่านอย่างเดียว และไม่มี capability เพิ่ม
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
seccompProfile:
type: RuntimeDefault
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 13: Helm และ Kustomize
แอปจริงมี manifest จำนวนมากและหลายสภาพแวดล้อม Helm รวมเป็น chart พร้อมค่า values ส่วน Kustomize เก็บ base ชุดเดียวและใช้ overlay เล็กๆ ของแต่ละสภาพแวดล้อม
chart และ release ของ Helm
chart คือแพ็กเกจเทมเพลตที่มีค่าเริ่มต้น release คือสำเนาที่ติดตั้งหนึ่งชุด ติดตั้ง อัปเกรด และย้อนกลับด้วย helm และใช้ --atomic เพื่อให้การอัปเกรดที่ล้มเหลวย้อนกลับเองโดยอัตโนมัติ
values ตามสภาพแวดล้อม
เก็บไฟล์ values หนึ่งไฟล์ต่อสภาพแวดล้อมและส่งด้วย -f ตรวจผลลัพธ์ด้วย helm template ก่อนติดตั้ง เพื่อรู้แน่ชัดว่าจะมีอะไรไปถึงคลัสเตอร์
overlay ของ Kustomize
Kustomize รวม base กับ overlay ที่เปลี่ยนจำนวนเรพลิกา อิมเมจ หรือ label โดยไม่ใช้เทมเพลต kubectl apply -k นำผลไปใช้ และ kubectl diff แสดงการเปลี่ยนแปลงก่อน
ตัวอย่างที่ 85: ติดตั้งแอปด้วย Helm
ใช้ chart จากรีโพซิทอรี
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm install shop-cache bitnami/redis --namespace shop --set architecture=standalone
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 86: แสดงผลและตรวจ chart
ดูตัวอย่าง YAML และตรวจกับเซิร์ฟเวอร์
helm template shop-web ./chart --values values-prod.yaml > rendered.yaml
kubectl apply --dry-run=server -f rendered.yaml
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 87: ไฟล์ค่า values ของ Helm
การตั้งค่าสำหรับสภาพแวดล้อมหนึ่ง
replicaCount: 3
image:
repository: registry.example.com/team/web
tag: "1.4.2"
resources:
limits:
memory: 512Mi
ingress:
enabled: true
host: shop.example.com
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 88: อัปเกรดและย้อนกลับด้วย Helm
อัปเกรดแบบ atomic ที่ย้อนกลับเองเมื่อล้มเหลว
helm upgrade shop-web ./chart -n shop -f values-prod.yaml --atomic --wait
helm history shop-web -n shop
helm rollback shop-web 1 -n shop
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 89: overlay ของ Kustomize
เปลี่ยนค่าตามสภาพแวดล้อมโดยไม่ใช้เทมเพลต
# overlays/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
replicas:
- name: web
count: 5
images:
- name: web
newTag: "1.4.2"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 90: apply overlay ของ Kustomize
ดู diff ก่อน แล้วค่อย apply
kubectl kustomize overlays/prod | kubectl diff -f -
kubectl apply -k overlays/prod
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 14: การสังเกตการณ์และการดีบัก
เมื่อ Pod ทำงานผิดปกติ คำตอบอยู่ในสถานะ event log และ metric ของมัน กิจวัตรที่ใจเย็นและเป็นลำดับจะหาข้อผิดพลาดส่วนใหญ่ได้เร็ว และสัญญาณเดียวกันนี้ป้อนให้แดชบอร์ดและการแจ้งเตือน
สถานะ event และ describe
kubectl get pod แสดงเฟสและจำนวนการรีสตาร์ต kubectl describe แสดง event เช่น ImagePullBackOff, CrashLoopBackOff หรือ FailedScheduling และอธิบายว่าทำไม Pod ติดอยู่
log ข้าม Pod
เลือก Pod ด้วย label ด้วย kubectl logs -l และใช้ --prefix เพื่อดูว่า Pod ใดเขียนแต่ละบรรทัด ใช้ --previous เพื่ออ่าน log ของคอนเทนเนอร์ที่ล่มและรีสตาร์ต
คอนเทนเนอร์ดีบักชั่วคราว
kubectl debug แนบคอนเทนเนอร์ชั่วคราวเข้ากับ Pod ที่กำลังทำงาน ทำให้ใช้เครื่องมือที่ไม่มีในอิมเมจของแอปได้ โดยไม่ต้องรีสตาร์ตแอป
ตัวอย่างที่ 91: log จากหลาย Pod
เลือก Pod ด้วยป้ายกำกับ
kubectl logs -l app=web --all-containers --prefix --tail=20
kubectl logs -l app=web -f --max-log-requests=10
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 92: ทำไม Pod จึงไม่เริ่ม
อ่านผล describe และสถานะของคอนเทนเนอร์
kubectl describe pod web-6d4cf56db6-abcde
kubectl get pod web-6d4cf56db6-abcde -o jsonpath="{.status.containerStatuses[0].state}"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 93: คอนเทนเนอร์ดีบักชั่วคราว
ดีบัก Pod ที่กำลังทำงานโดยไม่รีสตาร์ต
kubectl debug -it web-6d4cf56db6-abcde --image=busybox:1.36 --target=web -- sh
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 94: event และแรงกดดันของโหนด
หาปัญหาความจุและการขับไล่ Pod
kubectl get events -A --sort-by=.lastTimestamp | tail -20
kubectl describe node worker-1 | grep -A5 "Conditions"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
หัวข้อที่ 15: การดำเนินงานโปรดักชันและ GitOps
โปรดักชันหมายถึงการปล่อยที่ทำซ้ำได้ การบำรุงรักษาที่ปลอดภัย และการกู้คืน GitOps ทำให้คลัสเตอร์ตรงกับรีโพซิทอรี Git และการสำรองข้อมูลปกป้องออบเจกต์และข้อมูลที่สร้างใหม่ไม่ได้
GitOps
เครื่องมือ GitOps เช่น Argo CD เฝ้าดูรีโพซิทอรีและนำ manifest ไปใช้กับคลัสเตอร์ ทุกการเปลี่ยนแปลงผ่าน pull request และ selfHeal ย้อนการแก้ด้วยมือที่เบี่ยงจาก Git
การบำรุงรักษาโดยไม่หยุดให้บริการ
kubectl drain ย้าย Pod ออกจากโหนดก่อนอัปเกรด และ PodDisruptionBudget รักษาเรพลิกาให้ทำงานต่อ เมื่อโหนดพร้อมอีกครั้ง รัน kubectl uncordon
การสำรองและการกู้คืน
Velero สำรองออบเจกต์ของคลัสเตอร์และวอลุ่มถาวรไปยัง object storage ทดสอบการกู้คืนในคลัสเตอร์แยก เพราะการสำรองที่ไม่เคยกู้คืนได้ยังไม่ได้รับการพิสูจน์
ตัวอย่างที่ 95: GitOps ด้วย Argo CD
ทำให้คลัสเตอร์ตรงกับรีโพซิทอรี Git เสมอ
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: shop
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/example/shop-deploy
targetRevision: main
path: overlays/prod
destination:
server: https://kubernetes.default.svc
namespace: shop
syncPolicy:
automated:
prune: true
selfHeal: true
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 96: Deployment สำหรับโปรดักชัน
probe, limits และการ rollout ที่ปลอดภัยรวมกัน
apiVersion: apps/v1
kind: Deployment
metadata:
name: api
labels:
app: api
spec:
replicas: 3
revisionHistoryLimit: 5
selector:
matchLabels:
app: api
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api
spec:
containers:
- name: api
image: registry.example.com/team/api:1.4.2
ports:
- containerPort: 8000
resources:
requests: {cpu: 250m, memory: 256Mi}
limits: {memory: 512Mi}
readinessProbe:
httpGet: {path: /ready, port: 8000}
livenessProbe:
httpGet: {path: /healthz, port: 8000}
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 97: สำรองข้อมูลด้วย Velero
บันทึกออบเจกต์และวอลุ่มของคลัสเตอร์ลงที่เก็บข้อมูล
velero install --provider azure --plugins velero/velero-plugin-for-microsoft-azure:v1.10.0 --bucket k8s-backups
velero backup create shop-daily --include-namespaces shop
velero backup describe shop-daily
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 98: drain และ uncordon โหนด
บำรุงรักษาโดย Pod ไม่หยุดทำงาน
kubectl cordon worker-1
kubectl drain worker-1 --ignore-daemonsets --delete-emptydir-data
kubectl uncordon worker-1
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 99: ตรวจจำนวนเรพลิกาจาก Python
ใช้ไลบรารี Kubernetes อย่างเป็นทางการ
from kubernetes import client, config
config.load_kube_config()
apps = client.AppsV1Api()
for d in apps.list_namespaced_deployment("shop").items:
ready = d.status.ready_replicas or 0
print(d.metadata.name, f"{ready}/{d.spec.replicas}")
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
ตัวอย่างที่ 100: สคริปต์เช็กลิสต์ก่อนปล่อยเวอร์ชัน
หยุดการปล่อยเมื่อการตรวจสอบใดล้มเหลว
#!/usr/bin/env bash
set -euo pipefail
NS="$1"
kubectl -n "$NS" rollout status deployment --timeout=180s
kubectl -n "$NS" get pdb
kubectl -n "$NS" auth can-i --list --as=system:serviceaccount:"$NS":default | grep -q "\*" && { echo "default account has wildcard rights"; exit 1; }
kubectl -n "$NS" get pods -o jsonpath="{range .items[*]}{.metadata.name}{' runAsRoot='}{.spec.securityContext.runAsNonRoot}{'\n'}{end}"
echo "release checks passed for namespace $NS"
*ตัวอย่างนี้ยังไม่ได้รันที่นี่ เพราะเครื่องที่เตรียมบทความไม่มีคลัสเตอร์ Kubernetes หรือ kubectl*
แล็บ: deploy, เปิดเผย และปล่อยแอปเว็บขนาดเล็ก
รวมบทความเข้าด้วยกันในคลัสเตอร์ฝึกเดียว: Deployment พร้อม probe และ limits, Service, Ingress, ConfigMap และ Secret, การอัปเดตแบบค่อยเป็นค่อยไปพร้อมการย้อนกลับ และการตรวจก่อนปล่อย
1. สร้างคลัสเตอร์ฝึกด้วย kind จากนั้นสร้าง namespace ชื่อ shop และตั้งเป็นค่าเริ่มต้น
2. apply Deployment ที่มีสามเรพลิกา ค่า requests และ limits ของทรัพยากร readiness probe และ liveness probe
3. เพิ่ม Service แบบ ClusterIP และตรวจ endpoint จากนั้นเรียกใช้จาก Pod ชั่วคราว
4. สร้าง ConfigMap สำหรับการตั้งค่า และ Secret สำหรับรหัสผ่านตัวอย่าง แล้วเมานต์ทั้งคู่เข้ากับ Pod
5. rollout อิมเมจเวอร์ชันใหม่ ดูการ rollout แล้วย้อนด้วย kubectl rollout undo
6. เพิ่ม Ingress สำหรับ shop.example.com จากนั้นจำกัด API ด้วย NetworkPolicy
7. รันเช็กลิสต์ก่อนปล่อยจากตัวอย่างที่ 100 กับ namespace และหยุดหากมีการตรวจข้อใดล้มเหลว
ขั้นต่อไป
หลังแล็บ ให้ฝึกบนคลัสเตอร์ที่ผู้ให้บริการจัดการให้ในบัญชีทดลอง เรียน Helm chart สำหรับแอปของคุณเอง และตั้งค่า GitOps เพื่อให้ทุกการเปลี่ยนแปลงถูกตรวจทานใน Git เก็บตัวอย่างไว้ในรีโพซิทอรีและรันซ้ำเมื่ออัปเกรดคลัสเตอร์ เพราะ API ของ Kubernetes เปลี่ยนระหว่างเวอร์ชัน
สรุป
Kubernetes ทำให้คอนเทนเนอร์ของคุณทำงานต่อเนื่อง กระจายไปตามโหนด อัปเดตโดยไม่หยุดให้บริการ และปรับขนาดตามความต้องการ คุณได้เรียน kubectl และ context, Pod, Deployment และการย้อนกลับ, Service และ Ingress, การตั้งค่าและความลับ, ที่เก็บข้อมูล, Job และ CronJob, StatefulSet และ DaemonSet, การปรับขนาดอัตโนมัติ, การขอทรัพยากรและ probe, RBAC และความปลอดภัยของ Pod, Helm และ Kustomize, การดีบัก และ GitOps พร้อมการสำรองข้อมูลสำหรับโปรดักชัน