
ถ้าเคยใช้ Docker มาบ้าง คงรู้แล้วว่ามันแพ็กแอปใส่ "ตู้" ที่ยกไปรันบนเครื่องไหนก็ได้ ปัญหาเริ่มตอนที่ตู้มีเยอะขึ้น ใครจะคอยดูว่าตู้ไหนดับ ใครจะเปิดตู้เพิ่มตอนคนเข้าเว็บเยอะ งานแบบนี้แหละที่ Kubernetes เข้ามารับช่วงต่อ
บทความนี้จะพาสร้างคลัสเตอร์ Kubernetes ของจริงบนเครื่องตัวเองด้วย minikube แล้วลองให้ครบวงจร ตั้งแต่รันแอป เปิดให้เข้าจากเบราว์เซอร์ เพิ่มเป็น 3 ตัว ไปจนถึงแกล้งลบทิ้งแล้วดูมันซ่อมตัวเอง ไม่ต้องเช่าคลาวด์และไม่เสียเงินครับ
จบบทความนี้คุณจะ:
- อธิบายได้ว่า Cluster, Node, Pod, Deployment และ Service ทำงานร่วมกันยังไง
- เปิดคลัสเตอร์บนเครื่องตัวเองด้วย minikube
- ใช้
kubectlสร้างแอป เปิดให้เข้าจากเบราว์เซอร์ และเพิ่มจำนวน Pod - เข้าใจว่าทำไม Pod ที่ถูกลบถึงกลับมาเองได้
- อ่านไฟล์ YAML ของ Deployment ออก
ดูภาพรวมแบบเร็วๆ จากคลิปนี้ก่อนได้เลยครับ
Docker คือตู้ Kubernetes คือนายท่า
ลองนึกภาพท่าเรือขนส่งสินค้า ของทุกอย่างถูกบรรจุลงตู้คอนเทนเนอร์ขนาดมาตรฐาน จะยกขึ้นเรือลำไหนก็ได้ Docker คือเครื่องมือที่แพ็กแอปของเราลงตู้แบบนั้น ตู้หนึ่งใบมีทั้งตัวแอปและทุกอย่างที่แอปต้องใช้
แต่ท่าเรือใหญ่มีตู้เป็นพันใบ ต้องมีคนคุมว่าตู้ไหนขึ้นเรือลำไหน ตู้ไหนเสียต้องเปลี่ยน Kubernetes คือ "นายท่า" คนนั้น เราไม่ต้องสั่งทีละตู้ แค่บอกว่าอยากให้ผลออกมาเป็นแบบไหน แล้วมันจะคอยจัดการให้เป็นแบบนั้นตลอดเวลา
| เรื่อง | Docker | Kubernetes |
|---|---|---|
| ทำอะไร | แพ็กแอปลงตู้ แล้วรันตู้ | คุมตู้จำนวนมากให้เป็นไปตามที่สั่ง |
| เทียบกับ | ตู้คอนเทนเนอร์ | นายท่าที่คุมทั้งท่าเรือ |
| ตู้ดับ | ต้องไปเปิดใหม่เอง | สร้างตู้ใหม่แทนให้ |
ชื่อ Kubernetes มาจากภาษากรีก แปลว่า คนถือหางเสือเรือ โลโก้จึงเป็นพวงมาลัยเรือ ส่วนชื่อย่อ K8s มาจากตัว K ตามด้วยตัวอักษรอีก 8 ตัว แล้วปิดด้วย s
ยังไม่คุ้นกับ Docker ดูคลิปนี้ปูพื้นก่อนได้ บทความนี้ไม่ต้องสร้างตู้เอง เพราะจะใช้ image (ต้นแบบของตู้ที่มีคนทำไว้แล้ว)
ศัพท์ 5 คำที่ต้องรู้
| คำ | เทียบกับ | หน้าที่ |
|---|---|---|
| Cluster / Node | ท่าเรือ / เรือแต่ละลำ | Cluster คือทั้งระบบ Node คือเครื่องที่รันงานจริง |
| Pod | ตู้คอนเทนเนอร์ | หน่วยเล็กสุดที่ K8s ดูแล มี container 1 ตัวขึ้นไป |
| Deployment | ใบสั่งงาน | บอกว่าอยากได้ Pod แบบไหน กี่ตัว |
| Service | ประตูทางเข้า | ทางเข้าคงที่จากข้างนอก ส่งต่อไปหา Pod |
| kubectl | วิทยุสื่อสาร | ใช้ส่งคำสั่งให้คลัสเตอร์ |
งานจริงคลัสเตอร์มีหลาย Node เหมือนท่าเรือที่มีเรือหลายลำ ส่วน minikube ย่อให้เหลือ Node เดียวในเครื่องเรา พอสำหรับฝึก

อ่านภาพจากบนลงล่าง เราใช้ kubectl ยื่นใบสั่งงานให้คลัสเตอร์ คลัสเตอร์วาง Pod ลงบน Node ให้ครบตามจำนวน แล้วเมื่อมีคนเปิดเว็บ คำขอจะเข้าทาง Service ซึ่งส่งต่อให้ Pod ตัวใดตัวหนึ่ง
ทำไมไม่ต่อตรงไปที่ Pod เลย Pod เกิดและตายได้ตลอด และทุกครั้งที่เกิดใหม่จะได้ที่อยู่ (IP) ใหม่ Service จึงเป็นประตูที่ที่อยู่ไม่เปลี่ยน และคอยดูเองว่าตอนนี้มี Pod ตัวไหนพร้อมรับงาน
เตรียมเครื่อง
minikube จะสร้าง Node เป็นตู้ Docker หนึ่งตู้ในเครื่องเรา (เหมือนเอาเรือจำลองไปลอยในอ่าง) แล้วติดตั้ง Kubernetes ไว้ข้างใน ตามเอกสารของ minikube เครื่องต้องมีอย่างน้อย
- CPU 2 คอร์ขึ้นไป, RAM ว่าง 2 GB, ดิสก์ว่าง 20 GB และอินเทอร์เน็ต
- Docker Desktop ที่ติดตั้งแล้ว และ เปิดค้างไว้ตลอด ระหว่างทำ
docker --versionDocker version 2x.x.x, build xxxxxxxเช็กว่าได้แล้ว: ขึ้นเลขเวอร์ชัน แต่คำสั่งนี้บอกแค่ว่าติดตั้งแล้ว ให้ดูด้วยว่าโปรแกรม Docker Desktop เปิดอยู่และขึ้นว่ากำลังทำงาน
ติดตั้ง minikube
winget install Kubernetes.minikubebrew install minikubeLinux ให้ดูคำสั่งตามรุ่นที่หน้า minikube start ติดตั้งเสร็จแล้ว ปิด Terminal แล้วเปิดใหม่ เพราะตัวติดตั้งเพิ่ม minikube ลงใน PATH (รายชื่อโฟลเดอร์ที่ Terminal ใช้หาคำสั่ง) แต่หน้าต่างเดิมอ่าน PATH แค่ตอนเปิด
minikube versionminikube version: v1.xx.xเช็กว่าได้แล้ว: ขึ้นเลขที่ขึ้นต้นด้วย v1. ตัวเลขหลังจุดต่างกันไปตามวันที่ติดตั้ง
เปิดคลัสเตอร์แรก
minikube startคำสั่งเดียวนี้ทำหลายอย่าง คือสร้างตู้ Docker ชื่อ minikube ขึ้นมาเป็น Node, ติดตั้ง Kubernetes ข้างใน แล้วตั้งค่าให้ kubectl คุยกับคลัสเตอร์นี้ ครั้งแรกต้องโหลดของเยอะ จึงอาจใช้เวลาหลายนาที
Automatically selected the docker driver
Creating docker container (CPUs=2, Memory=xxxxMB) ...
Preparing Kubernetes v1.xx.x on Docker ...
Done! kubectl is now configured to use "minikube" cluster and "default" namespace by defaultของจริงมีไอคอนเล็กๆ นำหน้าแต่ละบรรทัด ที่สำคัญคือ Done! บรรทัดสุดท้าย ต่อไปเช็กสุขภาพคลัสเตอร์
minikube statusminikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configuredtype: Control PlaneNode นี้เป็น "หอบังคับการ" ด้วย ในคลัสเตอร์จริงมักแยกเครื่องกัน แต่ minikube รวมไว้ที่เดียวhostตู้ Docker ที่เป็น Node เปิดอยู่kubeletผู้ช่วยประจำ Node ที่คอยเปิดปิด Pod ตามคำสั่งapiserverช่องรับคำสั่ง ทุกคำสั่งจาก kubectl วิ่งมาที่นี่kubeconfigไฟล์ตั้งค่าของ kubectl ชี้มาที่คลัสเตอร์นี้แล้ว
เช็กว่าได้แล้ว: ทุกบรรทัดเป็น Running และบรรทัดสุดท้ายเป็น Configured
คุยกับคลัสเตอร์ผ่าน kubectl
ลองใช้วิทยุครั้งแรก ถามว่าท่าเรือมีเรือกี่ลำ
minikube kubectl -- get nodesNAME STATUS ROLES AGE VERSION
minikube Ready control-plane 2m v1.xx.xครั้งแรก minikube จะโหลด kubectl รุ่นที่เข้ากับคลัสเตอร์มาให้ก่อน ผลบอกว่ามี Node เดียวชื่อ minikube สถานะ Ready คือพร้อมรับงาน
พิมพ์ kubectl เฉยๆ ได้ไหม ได้ ถ้าเครื่องมี
kubectlอยู่แล้ว แต่ถ้าขึ้นว่าไม่รู้จักคำสั่ง ให้เติมminikube kubectl --นำหน้าทุกคำสั่งkubectlในบทความนี้ เครื่องหมาย--บอก minikube ว่าที่เหลือเป็นของ kubectl ต่อจากนี้บทความจะเขียนแค่kubectlเพื่อให้อ่านง่าย
minikube ยังมีหน้าเว็บให้ดูภาพรวมด้วย
minikube dashboardเบราว์เซอร์จะเปิดหน้า Kubernetes Dashboard ขึ้นมาเอง ทริกคือเปิดค้างไว้ แล้วพิมพ์คำสั่งต่อใน Terminal หน้าต่างใหม่ จะเห็นของโผล่บนหน้าเว็บทุกครั้งที่สั่ง (อยากปิด dashboard ก็กด Ctrl+C)
สร้างแอปแรกด้วย Deployment
แอปที่ใช้คือ kicbase/echo-server:1.0 เว็บเซิร์ฟเวอร์ตัวจิ๋วที่ทีม minikube ทำไว้สาธิต มันแค่ "สะท้อน" คำขอที่ได้รับกลับมาให้ดู ชื่ออ่านว่า เจ้าของ kicbase / image echo-server / เวอร์ชัน 1.0
kubectl create deployment hello-minikube --image=kicbase/echo-server:1.0deployment.apps/hello-minikube createdเท่ากับยื่นใบสั่งงานชื่อ hello-minikube ว่า "ช่วยรัน Pod จาก image นี้" เราไม่ได้บอกจำนวน K8s จึงใช้ค่าเริ่มต้นคือ 1 ตัว มาดูกัน
kubectl get podsNAME READY STATUS RESTARTS AGE
hello-minikube-6c5d4b8f7d-x2k8p 1/1 Running 0 40sNAMEชื่อ Deployment ต่อด้วยรหัสสุ่ม 2 ท่อน เพราะ Deployment เดียวอาจมีหลาย Pod ชื่อจึงต้องไม่ซ้ำ (ของคุณจะต่างจากในบทความ)READYcontainer พร้อมกี่ตัวจากทั้งหมด1/1คือครบSTATUSช่วงแรกจะเป็นContainerCreating(กำลังโหลด image) แล้วเปลี่ยนเป็นRunningRESTARTSจำนวนครั้งที่ถูกเริ่มใหม่ ถ้าขึ้นเรื่อยๆ แปลว่าแอปพังซ้ำAGEอายุของ Pod เดี๋ยวคอลัมน์นี้จะเป็นตัวจับผิดตอนเราลบ Pod
เช็กว่าได้แล้ว: READY เป็น 1/1 และ STATUS เป็น Running ถ้ายังไม่ใช่ รอแล้วสั่งซ้ำ หรือใช้ kubectl get pods -w ให้ตารางอัปเดตเอง (กด Ctrl+C เพื่อออก)
เปิดประตูด้วย Service
Pod ทำงานแล้ว แต่ยังอยู่ข้างในท่าเรือ คนข้างนอกเข้าไม่ได้ ต้องสร้างประตูให้มัน
kubectl expose deployment hello-minikube --type=NodePort --port=8080
kubectl get services hello-minikubeservice/hello-minikube exposed
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hello-minikube NodePort 10.96.52.110 <none> 8080:31234/TCP 1s--port=8080 คือพอร์ตของประตู ตั้งให้ตรงกับพอร์ตที่แอปข้างในรอรับอยู่ ส่วน --type=NodePort คือให้เปิดช่องบนตัว Node ด้วย ช่อง PORT(S) จึงอ่านว่า พอร์ต 8080 ถูกเปิดออกมาเป็นช่อง 31234 บน Node (เลขอยู่ในช่วง 30000 ถึง 32767 และของคุณจะต่างไป) แทนที่จะประกอบที่อยู่เอง ให้ minikube เปิดให้เลย
minikube service hello-minikubeminikube จะขึ้นตาราง URL แล้วเปิดเบราว์เซอร์ให้ หน้าเว็บเป็นข้อความธรรมดาแบบนี้
Request served by hello-minikube-6c5d4b8f7d-x2k8p
HTTP/1.1 GET /
Host: 127.0.0.1:52817บรรทัดแรกบอกว่า Pod ตัวไหนเป็นคนตอบ ที่เหลือคือคำขอของเบราว์เซอร์ที่แอปสะท้อนกลับมา
ระวัง อย่าเพิ่งปิด Terminal นี้ บน Windows และ Mac คำสั่งนี้ต้องเปิด "อุโมงค์" จากเครื่องเราเข้าไปหา Node และจะบอกให้เปิด Terminal ค้างไว้ ถ้าปิด หน้าเว็บจะเข้าไม่ได้ทันที ใช้เสร็จค่อยกด Ctrl+C ส่วนคำสั่งต่อไปให้พิมพ์ในอีกหน้าต่าง
เช็กว่าได้แล้ว: เบราว์เซอร์ขึ้น Request served by ตามด้วยชื่อ Pod
เพิ่มเป็น 3 ตัวด้วย scale
สมมติคนเข้าเว็บเยอะขึ้น เราแค่แก้ใบสั่งงานจาก 1 เป็น 3 (replicas แปลว่าจำนวนสำเนา)
kubectl scale deployment/hello-minikube --replicas=3รอไม่กี่วินาที แล้วสั่ง kubectl get pods อีกครั้ง
NAME READY STATUS RESTARTS AGE
hello-minikube-6c5d4b8f7d-9vqtz 1/1 Running 0 12s
hello-minikube-6c5d4b8f7d-m4wlr 1/1 Running 0 12s
hello-minikube-6c5d4b8f7d-x2k8p 1/1 Running 0 6mตัวเดิม (x2k8p อายุ 6 นาที) ยังอยู่ และมีตัวใหม่เพิ่ม 2 ตัว Service จะกระจายคำขอให้ทั้ง 3 ตัวเอง ลองรีเฟรชหน้าเว็บ ชื่อ Pod อาจเปลี่ยน (ถ้าเห็นชื่อเดิมตลอดก็ไม่ได้พัง เบราว์เซอร์แค่ใช้การเชื่อมต่อเดิมซ้ำ)
เช็กว่าได้แล้ว: มี Pod 3 แถว และทุกแถวเป็น Running
ลบ Pod ทิ้ง แล้วดูมันกลับมา
ส่วนที่สนุกที่สุดครับ ก๊อปชื่อ Pod ตัวไหนก็ได้ (ใช้ชื่อในเครื่องคุณ) แล้วลบทิ้ง
kubectl delete pod hello-minikube-6c5d4b8f7d-x2k8p
kubectl get podspod "hello-minikube-6c5d4b8f7d-x2k8p" deleted
NAME READY STATUS RESTARTS AGE
hello-minikube-6c5d4b8f7d-9vqtz 1/1 Running 0 3m
hello-minikube-6c5d4b8f7d-hb7sn 1/1 Running 0 4s
hello-minikube-6c5d4b8f7d-m4wlr 1/1 Running 0 3mยังมี 3 ตัวเหมือนเดิม ตัวที่ลบหายไปจริง แต่มี hb7sn โผล่มาแทน ดู AGE แค่ไม่กี่วินาทีก็รู้ว่าเพิ่งเกิด

นี่คือแนวคิดหัวใจของ Kubernetes คือ desired state (สภาพที่เราต้องการ) กับ actual state (สภาพที่เป็นอยู่จริง) ใบสั่งงานเขียนว่าต้องการ 3 ตัว แล้ว K8s มีตัวคุมที่เทียบสองอย่างนี้วนไปไม่หยุด ของจริงน้อยกว่าก็สร้างเพิ่ม มากกว่าก็ลบออก
เหมือนแอร์ที่ตั้งไว้ 25 องศา เราไม่ได้สั่งให้เป่าแรงหรือเบา แค่บอกอุณหภูมิที่อยากได้ ถ้ามีคนเปิดประตูจนห้องร้อน แอร์ก็เร่งทำงานเองจนกลับมาที่ 25
ตัวที่คอยนับ Pod ให้ Deployment ชื่อ ReplicaSet ชื่อของมันคือท่อนกลางในชื่อ Pod และเราเห็นตัวเลขทั้งสองฝั่งได้ตรงๆ
kubectl get replicasetsNAME DESIRED CURRENT READY AGE
hello-minikube-6c5d4b8f7d 3 3 3 10mDESIRED คือจำนวนที่ใบสั่งงานต้องการ CURRENT คือที่มีอยู่จริง และ READY คือที่พร้อมรับงานแล้ว
ลบ Pod ไม่ได้แปลว่าลบแอป ถ้าอยากให้แอปหายจริง ต้องลบที่ใบสั่งงาน คือลบ Deployment ไม่อย่างนั้นลบ Pod กี่ครั้งก็ถูกสร้างใหม่ทุกครั้ง ข้อนี้คนเพิ่งเริ่มใช้ K8s งงกันบ่อยมาก
แถม ดู Deployment ในรูป YAML
ใบสั่งงานที่เราสร้างด้วย kubectl create ถูกเก็บเป็นเอกสารชิ้นหนึ่ง เราสั่งให้พิมพ์ออกมาดูได้โดยไม่สร้างจริง
kubectl create deployment hello-minikube --image=kicbase/echo-server:1.0 --dry-run=client -o yaml--dry-run=client คือลองทำแค่ในเครื่องเรา ไม่ส่งไปที่คลัสเตอร์ ส่วน -o yaml คือขอผลเป็น YAML รูปแบบข้อมูลที่ใช้การย่อหน้าบอกว่าอะไรอยู่ใต้อะไร
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: hello-minikube
name: hello-minikube
spec:
replicas: 1
selector:
matchLabels:
app: hello-minikube
template:
metadata:
labels:
app: hello-minikube
spec:
containers:
- image: kicbase/echo-server:1.0
name: echo-serverผลจริงมีบรรทัดที่ไม่มีค่าแทรกอยู่ เช่น creationTimestamp: null หรือ status: {} ตัดออกได้ มาดู 3 ส่วนที่ไฮไลต์ไว้
replicasอยากได้ Pod กี่ตัว เป็น 1 เพราะตอน create เราไม่ได้ระบุ คำสั่งkubectl scaleเมื่อกี้ก็คือการแก้เลขนี้selectorบอกว่า Pod แบบไหนนับเป็นของใบสั่งงานนี้ คือ Pod ที่ติดป้าย (label)app: hello-minikubeเหมือนนายท่านับตู้จากสติกเกอร์templateแม่แบบของ Pod ที่จะสร้าง ติดป้ายอะไร ใช้ image ไหน ป้ายตรงนี้ต้องตรงกับselectorไม่อย่างนั้น K8s จะไม่รับใบสั่งงาน เพราะนับตู้ของตัวเองไม่ได้
ในงานจริง ทีมจะเก็บไฟล์ YAML ไว้ใน Git แล้วสั่ง kubectl apply -f ชื่อไฟล์.yaml ให้คลัสเตอร์ปรับตัวให้ตรงกับไฟล์ เรียกว่าแบบ declarative คือเขียนผลที่อยากได้ แทนการสั่งทีละขั้น
เจอปัญหา ลองเช็กตามนี้
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
minikube start error เรื่อง Docker | Docker Desktop ไม่ได้เปิด | เปิด Docker Desktop แล้วสั่งใหม่ |
minikube start พังซ้ำๆ | คลัสเตอร์เก่าเสียค้าง | minikube delete แล้ว start ใหม่ |
ไม่รู้จักคำสั่ง minikube | Terminal ยังไม่เห็น PATH ใหม่ | ปิดแล้วเปิด Terminal ใหม่ |
ไม่รู้จักคำสั่ง kubectl | เครื่องไม่มี kubectl | เติม minikube kubectl -- นำหน้า |
Pod ค้าง ContainerCreating | ยังโหลด image ไม่เสร็จ | รอ 1–2 นาที แล้วดู events |
Pod ขึ้น ErrImagePull | ชื่อ image ผิด หรือเน็ตหลุด | เช็กชื่อ image ทุกตัวอักษร |
ไม่แน่ใจว่าเกิดอะไรขึ้น ให้ดูบันทึกเหตุการณ์
kubectl get eventsLAST SEEN TYPE REASON OBJECT MESSAGE
44s Normal Pulling pod/hello-minikube-6c5d4b8f7d-x2k8p Pulling image "kicbase/echo-server:1.0"เห็น Pulling แต่ยังไม่มี Pulled แปลว่ายังโหลดอยู่ ถ้า TYPE เป็น Warning ให้อ่าน MESSAGE ซึ่งมักบอกสาเหตุตรงๆ ส่วน minikube delete คือไม้ตาย ลบทั้งคลัสเตอร์แล้วเริ่มใหม่
เก็บกวาดเมื่อเลิกใช้
คลัสเตอร์ที่เปิดทิ้งไว้กิน RAM และ CPU ตลอด ใช้เสร็จให้รื้อประตู ยกเลิกใบสั่งงาน (Pod ทั้ง 3 ตัวถูกลบตามไปเอง) แล้วพักท่าเรือ
kubectl delete service hello-minikube
kubectl delete deployment hello-minikube
minikube stopminikube stop คือพักไว้ ของยังอยู่ครบ วันหลังสั่ง minikube start ก็ทำต่อได้ ถ้าเลิกใช้จริงและอยากคืนพื้นที่ดิสก์ ให้ลบทุกคลัสเตอร์ทิ้ง
minikube delete --allเช็กว่าได้แล้ว: minikube status บอกว่าไม่พบคลัสเตอร์ หรือทุกบรรทัดเป็น Stopped
ไปต่อ
- คู่มือทางการ minikube start และบทเรียน Kubernetes Basics
- อ่านเจาะเรื่อง Deployments ต่อ
- อยาก deploy แบบไม่ต้องดูแลเซิร์ฟเวอร์เอง อ่าน Cloudflare Workers: ขึ้นเว็บแรกฟรีบน Cloudflare
สรุป
- Docker แพ็กแอปลงตู้ ส่วน Kubernetes เป็นนายท่าที่คุมตู้จำนวนมากให้เป็นไปตามที่สั่ง
- Cluster คือท่าเรือ Node คือเรือ Pod คือตู้ Deployment คือใบสั่งงาน Service คือประตู kubectl คือวิทยุ
minikube startเปิดคลัสเตอร์create deploymentสร้างแอปexposeสร้างประตู และminikube serviceเปิดในเบราว์เซอร์kubectl scale --replicas=3เพิ่ม Pod และ Pod ที่ถูกลบกลับมาเอง เพราะ K8s รักษา desired state ไว้ตลอด- เลิกใช้แล้วลบ Service กับ Deployment แล้ว
minikube stopหรือminikube delete --all
พร้อมแล้วก็ไปทำ Quiz ท้ายบทด้านล่างเพื่อรับ XP ได้เลยครับ