รัน k3s บน VPS เครื่องเดียวคุ้มไหม? ข้อดีและข้อควรระวัง
เรียนรู้การติดตั้ง k3s บน VPS โหนดเดียวเพื่อใช้งาน Kubernetes API เต็มรูปแบบ วิเคราะห์การใช้ RAM ปัญหาพอร์ต 80 ชนกัน และเหตุผลที่คุณควรเลือกใช้ Docker Compose แทนในบางกรณี
k3s คืออะไร และสิ่งที่คุณจะได้รับจากโหนดเดียว
k3s คือ Kubernetes distribution ฉบับสมบูรณ์ที่รวมอยู่ในไฟล์ binary เดียว การรันบน VPS เพียงเครื่องเดียวจะทำให้คุณได้รับ Kubernetes API ของจริงโดยไม่ต้องมี control plane ถึง 3 เครื่อง k3s เป็น Kubernetes distribution ที่ผ่านการรับรอง ดังนั้น manifest ที่ใช้ในที่นี้จึงสามารถนำไปใช้กับ cluster แบบ managed ในภายหลังได้ การติดตั้งทำได้ด้วยคำสั่งเดียวและใช้เวลาประมาณ 1 นาที สิ่งที่คุณต้องแลกคือหน่วยความจำที่แอปพลิเคชันของคุณจะใช้งานไม่ได้อีกต่อไป รวมถึงรูปแบบความล้มเหลวบางอย่างที่ไม่เคยเกิดขึ้นกับ Docker Compose
SUSE พัฒนา k3s สำหรับ edge site และการติดตั้งขนาดเล็ก โดยความแตกต่างทุกอย่างจาก Kubernetes ต้นฉบับมีไว้เพื่อให้ตัวซอฟต์แวร์มีขนาดเล็กลง datastore เริ่มต้นคือ sqlite ที่ทำงานผ่าน shim ที่เรียกว่า kine ไม่ใช่ etcd จึงไม่มีเรื่อง quorum ของ etcd ให้ต้องดูแล containerd ถูกฝังมาใน binary แทนที่จะต้องติดตั้งแยกต่างหาก binary เดียวกันนี้ยังมาพร้อมกับ CoreDNS สำหรับ cluster DNS, Traefik สำหรับเป็น ingress controller, ServiceLB (หรือที่เรียกว่า klipper-lb) เพื่อให้ LoadBalancer service ทำงานได้โดยไม่ต้องมี cloud provider รองรับ, local-path provisioner สำหรับ persistent volumes, metrics-server และ flannel สำหรับ pod networking ทุกรายการเหล่านี้จะเริ่มทำงานโดยค่าเริ่มต้น นี่คือเหตุผลว่าทำไมปัญหาพอร์ตชนกันที่อธิบายไว้ด้านล่าง จึงเป็นปัญหาแรกที่พบบ่อยที่สุดบน VPS ที่มีการใช้งานอื่นอยู่ก่อนแล้ว
เมื่อใดที่การใช้ k3s แบบโหนดเดียวจึงคุ้มค่า
ให้ใช้กฎนี้: ให้รัน k3s เมื่อสิ่งที่คุณต้องการคือ Kubernetes API เช่น คุณกำลังเรียนรู้ Kubernetes บนเครื่องที่คุณควบคุมเอง หรือซอฟต์แวร์ที่คุณต้องการมีให้ใช้งานเฉพาะในรูปแบบ Helm chart เท่านั้น ความสามารถในการย้าย Manifest ได้ก็เป็นปัจจัยสำคัญ เพราะ Deployment ที่คุณเขียนขึ้นที่นี่สามารถนำไปใช้กับ Managed cluster ได้โดยไม่ต้องแก้ไขอะไร ส่วนการรัน Docker Compose ให้ใช้เมื่อสิ่งที่คุณต้องการคือตัวแอปพลิเคชันเอง Compose จะเริ่มคอนเทนเนอร์ชุดเดียวกันโดยมีส่วนประกอบที่ซับซ้อนน้อยกว่ามาก และ ไฟล์ Compose บน VPS นั้นอ่านเข้าใจได้ง่ายกว่าเมื่อเวลาผ่านไปหนึ่งปี เมื่อเทียบกับไดเรกทอรีที่เต็มไปด้วยไฟล์ Manifest
ต้องเข้าใจให้ชัดเจนว่าการมีโหนดเดียวไม่ได้ให้อะไรคุณบ้าง
- ไม่มีความพร้อมใช้งานสูง (High availability): เมื่อ VPS รีบูต เวิร์กโหลดทุกอย่างจะหยุดทำงาน Kubernetes จะพยายามย้าย Pod ไปยังโหนดอื่น แต่ในกรณีนี้ไม่มีโหนดอื่นให้ย้ายไป
- ไม่มีการอัปเดตแบบ Rolling update ที่ทำให้บริการยังคงทำงานได้ต่อเนื่อง เว้นแต่แอปพลิเคชันจะรองรับการรัน 2 Replica บนเครื่องเดียวโดยใช้ Volume ร่วมกัน
- พื้นที่จัดเก็บข้อมูลจะถูกผูกติดอยู่กับเครื่องนั้นๆ ตามเหตุผลที่อธิบายไว้ในส่วน local-path ด้านล่าง
- Control plane จะใช้ RAM ประมาณ 1 GB ไม่ว่าคุณจะติดตั้งอะไรลงไปหรือไม่ก็ตาม
สิ่งเหล่านี้ไม่ได้ทำให้ k3s เป็นตัวเลือกที่แย่ แต่มันทำให้ k3s เป็นตัวเลือกที่แย่สำหรับเหตุผลที่คนส่วนใหญ่มักอ้างถึง ซึ่งก็คือเรื่องความน่าเชื่อถือ หากสิ่งที่คุณต้องการจริงๆ คือเครื่องหลายเครื่องเพื่อสร้างคลัสเตอร์แบบหลายโหนดที่ใช้งานได้จริง การตัดสินใจนั้นต้องมาก่อน: Proxmox บนฮาร์ดแวร์ของคุณเทียบกับ VPS ที่เช่ามา เป็นตัวกำหนดว่าโหนดจะมาจากที่ใด ก่อนที่ k3s จะเป็นตัวกำหนดว่าสิ่งใดจะรันอยู่บนโหนดเหล่านั้น
ปริมาณการใช้ RAM และ CPU ของ k3s ก่อนเริ่มปรับใช้เวิร์กโหลดใดๆ
โครงการ k3s เผยแพร่ตัวเลขที่วัดผลได้จริงแทนการประมาณการ โปรดอ่านอย่างละเอียด เนื่องจากตัวเลขที่ผู้คนมักอ้างถึงไม่ใช่ค่าสถานะ idle ของ k3s
The data behind this chart
[
{
"label": "Server, sqlite datastore",
"ram_mb": "1,596",
"cpu_percent_of_one_core": 6
},
{
"label": "Server, embedded etcd",
"ram_mb": "1,606",
"cpu_percent_of_one_core": 6
},
{
"label": "Agent node only",
"ram_mb": "275",
"cpu_percent_of_one_core": 3
}
]โหนดเซิร์ฟเวอร์ในการทดสอบนั้นใช้ RAM ไป 1,596 MB ที่เปอร์เซ็นไทล์ที่ 95 และใช้ CPU ประมาณ 6 เปอร์เซ็นต์ของหนึ่งคอร์ ตัวเลขเหล่านี้เป็นตัวเลขที่เผยแพร่อย่างเป็นทางการ ไม่ใช่การวัดผลจากคู่มือนี้ และการทดสอบได้รัน k3s v1.26.5 โดยเปิดใช้งานส่วนประกอบที่มาพร้อมกับแพ็กเกจทั้งหมด รวมถึง stack การตรวจสอบอย่าง Prometheus และ Grafana ดังนั้นตัวเลขดังกล่าวจึงรวมเวิร์กโหลดจริงเข้าไปด้วย ไม่ใช่แค่คลัสเตอร์เปล่าๆ การเปลี่ยนจาก sqlite มาใช้ etcd แบบฝังตัวทำให้การใช้ RAM เพิ่มขึ้นเป็น 1,606 MB ส่วนโหนด agent ซึ่งรัน kubelet และ containerd โดยไม่มี control plane ใช้ RAM ไป 275 MB ข้อกำหนดขั้นต่ำที่ระบุไว้สำหรับเซิร์ฟเวอร์คือ 2 คอร์ และ RAM 2 GB ซึ่งขั้นต่ำนี้ครอบคลุมถึง k3s และส่วนประกอบที่มาพร้อมกับแพ็กเกจก่อนที่คุณจะเริ่มรันเวิร์กโหลดของคุณเอง
ในทางปฏิบัติ: บน VPS ขนาด 2 GB ตัว control plane และส่วนเสริมที่มาพร้อมกันจะเหลือทรัพยากรให้คุณใช้งานน้อยมาก และสิ่งแรกที่จะเกิดขึ้นเมื่อระบบอยู่ภายใต้ความกดดันคือ kubelet จะทำการ evict pod ออก ขนาด 4 GB เป็นจุดเริ่มต้นที่เหมาะสมสำหรับโหนดเดียวที่มีบริการขนาดเล็กไม่กี่รายการ ควรวัดผลบนเครื่องของคุณเองแทนที่จะเชื่อตัวเลขที่เผยแพร่ รวมถึงตัวเลขนี้ด้วย
free -h
sudo k3s kubectl top node
sudo k3s kubectl get pods -Aให้รัน free -h ก่อนที่คุณจะติดตั้ง และรันอีกครั้งเมื่อ pod ทุกตัวใน kube-system มีสถานะเป็น Running ผลต่างที่ได้คือต้นทุนทรัพยากรที่ control plane ใช้บนฮาร์ดแวร์ของคุณ k3s kubectl top node จะส่งค่า error: Metrics API not available กลับมาในช่วงหนึ่งหรือสองนาทีแรกหลังการติดตั้ง เนื่องจาก metrics-server ยังไม่ได้ดึงข้อมูลใดๆ นั่นไม่ใช่ข้อผิดพลาด หากคุณกำลังคำนวณขนาดเครื่องเพื่อรันงานนี้ควบคู่ไปกับงานอื่น การคำนวณใน การกำหนดขนาด RAM และ CPU สำหรับ VPS สามารถนำมาใช้ได้ทันทีโดยไม่ต้องปรับเปลี่ยนใดๆ
การติดตั้ง k3s แบบระบุเวอร์ชันแทนการใช้เวอร์ชันล่าสุด
คำสั่งเริ่มต้นใช้งานด่วนที่ทุกคนคัดลอกไปใช้จะดึงเวอร์ชันล่าสุดจากช่องทาง stable ณ วันที่รันคำสั่ง สำหรับเครื่องที่คุณต้องการใช้งานในระยะยาว ควรระบุเวอร์ชันให้ชัดเจน k3s มีการเผยแพร่ช่องทางแยกตามเวอร์ชันย่อยของ Kubernetes ดังนั้น INSTALL_K3S_CHANNEL=v1.36 จะติดตามเฉพาะการอัปเดตแพตช์ภายใน v1.36 เท่านั้น และจะไม่ข้ามไปยังเวอร์ชันย่อยอื่นโดยที่คุณไม่ต้องการ ณ เดือนสิงหาคม 2026 ช่องทาง stable ชี้ไปที่ v1.36.3+k3s1
ให้สร้างไฟล์คอนฟิกก่อนเริ่มการติดตั้ง k3s จะอ่านค่าจาก /etc/rancher/k3s/config.yaml เมื่อเริ่มทำงาน ดังนั้นการตั้งค่าใดๆ ในไฟล์นี้จะมีผลตั้งแต่การบูตครั้งแรกและทุกครั้งหลังจากนั้น
sudo mkdir -p /etc/rancher/k3s
sudo tee /etc/rancher/k3s/config.yaml >/dev/null <<'EOF'
tls-san:
- k3s.example.com
EOF
curl -sfL https://get.k3s.io | INSTALL_K3S_CHANNEL=v1.36 sh -หากต้องการระบุเวอร์ชันที่แน่นอนแทนการใช้ช่องทาง ให้ใช้ INSTALL_K3S_VERSION=v1.36.3+k3s1 โดยเครื่องหมายบวกเป็นส่วนหนึ่งของแท็กเวอร์ชัน จากนั้นตรวจสอบว่าระบบทำงานได้ปกติ
k3s --version
sudo systemctl status k3s
sudo k3s kubectl get node
sudo k3s kubectl get pods -Akubectl get node ควรแสดงรายการโหนดหนึ่งโหนดที่มีสถานะเป็น Ready ภายในเวลาประมาณสามสิบวินาที และทุกพอดใน kube-system ควรมีสถานะเป็น Running หรือ Completed หากโหนดค้างอยู่ที่สถานะ NotReady มักหมายความว่า container runtime ไม่ได้เริ่มทำงาน ให้ตรวจสอบ sudo journalctl -u k3s -n 100 --no-pager สำหรับ VPS ที่มีการตั้งค่าไม่มาตรฐาน ให้รัน sudo k3s check-config ก่อนเริ่มแก้ไขปัญหาอื่น เพราะคำสั่งนี้จะรายงานฟีเจอร์ของ kernel ที่ขาดหายไป ซึ่งช่วยให้ทราบสาเหตุได้เร็วกว่าการอ่าน log
เหตุใดพอร์ต 80 จึงถูกใช้งานอยู่แล้ว และต้องยอมสละสิ่งใดเพื่อแก้ไข
นี่คือความผิดพลาดที่มักพบใน VPS ที่มีการให้บริการบางอย่างอยู่ก่อนแล้ว การติดตั้งจะเสร็จสมบูรณ์ แต่ Traefik จะไม่ได้รับที่อยู่ IP และเว็บไซต์ที่คุณใช้งานอยู่เดิมก็ยังคงทำงานได้ตามปกติ ทำให้ดูเหมือนไม่มีอะไรเสียหายจนกว่าคุณจะพยายามเข้าถึง ingress
กลไกการทำงาน: Traefik chart ที่มาพร้อมกับระบบจะสร้าง Service ประเภท LoadBalancer บนพอร์ต 80 และ 443 โดย ServiceLB จะตอบสนองคำขอนั้นด้วยการสร้าง DaemonSet ของพอดขนาดเล็กที่มีชื่อขึ้นต้นด้วย svclb- ซึ่งจะจองหมายเลขพอร์ตเหล่านั้นเป็น hostPort บนแต่ละโหนด การใช้ hostPort จะเป็นการเผยแพร่พอร์ตของคอนเทนเนอร์ออกไปยัง network namespace ของโหนดโดยตรง เช่นเดียวกับที่ docker run -p 80:80 ทำ หาก nginx, Caddy, Apache หรือคอนเทนเนอร์อื่นใช้งานพอร์ต 80 อยู่แล้ว เคอร์เนลจะไม่ยอมให้มีการใช้งานซ้ำ ทำให้ตัวจัดตารางงาน (scheduler) ไม่มีที่ว่างสำหรับวางพอด
sudo k3s kubectl -n kube-system get pods
sudo k3s kubectl -n kube-system get svc traefik
sudo ss -lntp '( sport = :80 or sport = :443 )'คุณจะเห็นพอด svclb อยู่ในสถานะ Pending และ service ไม่มี external address:
svclb-traefik-8f2c1a-r6k9x 0/2 Pending 0 3m
traefik LoadBalancer 10.43.62.11 <pending> 80:31480/TCP,443:30219/TCPการรัน kubectl -n kube-system describe pod svclb-traefik-... จะระบุสาเหตุโดยตรง:
0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports.ss -lntp จะบอกคุณว่าโพรเซสใดเป็นผู้ถือครองพอร์ตนั้นอยู่ มีทางออกมากกว่าหนึ่งวิธี และแต่ละวิธีมีสิ่งที่ต้องแลกเปลี่ยน:
มอบพอร์ตให้ k3s: หยุดและปิดการใช้งานเว็บเซิร์ฟเวอร์เดิม จากนั้นปล่อยให้ Traefik เป็นผู้ดูแลพอร์ต 80 และ 443 วิธีนี้เป็นคำตอบที่ถูกต้องเมื่อ VPS จะถูกใช้เป็น k3s box เพียงอย่างเดียว และทุกบริการที่คุณเคยให้บริการจะถูกย้ายไปอยู่หลัง Ingress
ปิดการใช้งาน ServiceLB และใช้พร็อกซีเดิมของคุณต่อไป: ติดตั้งด้วย --disable=servicelb โดย Service ประเภท LoadBalancer จะยังคงจัดสรร NodePort ให้ ทำให้ Traefik ยังคงเข้าถึงได้ผ่านพอร์ตสูง เช่น 31480 และคุณสามารถตั้งค่า nginx หรือ Caddy ให้ทำหน้าที่ proxy ไปยัง 127.0.0.1:31480 ได้ สิ่งที่คุณต้องยอมสละคือ external address: service จะแสดงสถานะเป็น <pending> ตลอดไป ซึ่งดูเหมือนเป็นข้อผิดพลาดทั้งที่เป็นทางเลือกที่คุณตัดสินใจเอง
ปิดการใช้งาน Traefik และกำหนดเส้นทางด้วยพร็อกซีของคุณเอง: ติดตั้งด้วย --disable=traefik ในกรณีนี้คุณจะไม่มี ingress controller ดังนั้น Ingress object จะไม่ทำงานใดๆ เลย โดยจะค้างอยู่ใน API โดยไม่มี controller คอยตรวจสอบ ซึ่งเป็นวิธีที่ยอมรับได้หากคุณกำหนดเส้นทางจาก host proxy ไปยัง NodePorts และเป็นทางเลือกที่ตรงไปตรงมาหากคุณรู้อยู่แล้วว่าต้องการจัดการ HTTP อย่างไร หากคุณยังตัดสินใจไม่ได้ว่าควรใช้อะไรเป็นหน้าด่าน ให้พิจารณา การเลือกระหว่าง nginx, Caddy และ Traefik ในฐานะ reverse proxy ก่อนที่จะปิดการใช้งานสิ่งใด
แฟล็กทั้งสองตัวนี้สามารถใช้กับตัวติดตั้ง หรือใส่ไว้ในไฟล์คอนฟิก:
tls-san:
- k3s.example.com
disable:
- traefik
- servicelbการแก้ไขไฟล์ดังกล่าวหลังการติดตั้งและรัน sudo systemctl restart k3s ก็สามารถทำได้เช่นกัน เพราะ --disable ไม่ได้ทำหน้าที่เพียงแค่ข้ามส่วนประกอบในขณะติดตั้งเท่านั้น แต่ยังลบส่วนประกอบที่ถูกปรับใช้ไปแล้วออกด้วย ดังนั้นการเปลี่ยนแปลงจึงมีผลกับคลัสเตอร์ที่กำลังทำงานอยู่
หากต้องการเก็บ Traefik ไว้แต่เปลี่ยนวิธีการกำหนดค่า chart ห้ามแก้ไข /var/lib/rancher/k3s/server/manifests/traefik.yaml เนื่องจาก k3s จะเขียนทับไฟล์นั้นด้วยค่าเริ่มต้นทุกครั้งที่เริ่มระบบ ให้สร้างไฟล์แยกต่างหากในไดเรกทอรีเดียวกันแทน เพราะทุกอย่างที่อยู่ใน /var/lib/rancher/k3s/server/manifests จะถูกนำไปใช้โดยอัตโนมัติเมื่อเริ่มระบบและทุกครั้งที่มีการเปลี่ยนแปลงบนดิสก์
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
ports:
web:
forwardedHeaders:
trustedIPs:
- 10.0.0.0/8ตัวอย่างนั้นเป็นการตั้งค่า Traefik chart value หนึ่งรายการ คือ trusted proxy addresses กลไกเดียวกันนี้สามารถใช้ตั้งค่าอื่นๆ ที่ chart เปิดให้ปรับแต่งได้ รวมถึงพอร์ตของมันด้วย
การจัดเก็บข้อมูลแบบถาวรบนโหนดเดียว
k3s มาพร้อมกับ StorageClass เริ่มต้นที่ชื่อ local-path ซึ่งทำงานผ่าน local-path provisioner ของ Rancher หาก PersistentVolumeClaim ไม่ได้ระบุ storageClassName ระบบจะใช้ค่านี้โดยอัตโนมัติ ข้อมูลของ Volume จะถูกจัดเก็บไว้ใน /var/lib/rancher/k3s/storage โดยแบ่งเป็นไดเรกทอรีย่อยตามแต่ละ Volume บนดิสก์ของโหนดนั้นๆ
sudo k3s kubectl get storageclass
sudo k3s kubectl get pvc -A
sudo ls -l /var/lib/rancher/k3s/storageการจัดเก็บข้อมูลบนดิสก์ของโหนดโดยตรงส่งผลกระทบ 2 ประการ ซึ่งมักจะกลายเป็นปัญหาในภายหลังมากกว่าในปัจจุบัน
StorageClass นี้ใช้ volumeBindingMode: WaitForFirstConsumer ดังนั้น PVC ใหม่จะยังคงอยู่ในสถานะ Pending จนกว่าจะมี Pod ทำการ mount ข้อมูลจริง คำสั่ง kubectl describe pvc จะแสดงผลดังนี้:
waiting for first consumer to be created before bindingสถานะนี้ถือเป็นเรื่องปกติ ดังนั้นการสร้าง PVC ทิ้งไว้เฉยๆ จะไม่ทำให้สถานะเปลี่ยนเป็น Bound
เมื่อสถานะเป็น Bound แล้ว Volume จะถูกผูกติดกับโหนดที่สร้างมันขึ้นมา (node affinity) ซึ่งจะบังคับให้ทุก Pod ที่ใช้ claim นี้ต้องรันบนโหนดดังกล่าวตลอดอายุการใช้งานของ Volume หากคุณมีเพียงโหนดเดียว คุณจะไม่พบปัญหาใดๆ แต่หากเพิ่มโหนดที่สองในภายหลัง Pod ที่ไม่ยอมย้ายโหนดอาจดูเหมือนเป็นบั๊กของตัวจัดตารางงาน (scheduler) จนกว่าคุณจะรันคำสั่ง kubectl get pv -o yaml และพบชื่อโฮสต์ระบุอยู่ใน nodeAffinity
การสำรองข้อมูลเป็นหน้าที่ของคุณ การสร้าง VPS ใหม่จะทำให้ไดเรกทอรีดังกล่าวถูกลบ รวมถึงการใช้สคริปต์ถอนการติดตั้งที่ระบุไว้ด้านล่างนี้ด้วย คุณควรสำรองข้อมูล /var/lib/rancher/k3s/storage รวมถึงไฟล์ sqlite datastore ที่ /var/lib/rancher/k3s/server/db/state.db โดยต้องหยุดการทำงานของ service ก่อนทำการคัดลอกเนื่องจากเป็นฐานข้อมูลที่ใช้งานอยู่ตลอดเวลา อีกทางเลือกหนึ่งคือการมองว่าคลัสเตอร์นี้เป็นสิ่งที่สร้างใหม่ได้เสมอ (disposable) และเก็บ manifest ทุกอย่างไว้ใน git
Ingress และ TLS
เมื่อเปิดใช้งาน Traefik ไว้ คุณเพียงแค่ต้องสร้าง Ingress object ตามมาตรฐานเท่านั้น
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: hello
annotations:
cert-manager.io/cluster-issuer: letsencrypt
spec:
ingressClassName: traefik
tls:
- hosts:
- hello.example.com
secretName: hello-tls
rules:
- host: hello.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: hello
port:
number: 80ใบรับรองจะไม่ถูกสร้างขึ้นมาเองโดยอัตโนมัติ วิธีการทั่วไปคือการใช้ cert-manager ซึ่งติดตั้งจาก manifest ที่เผยแพร่ไว้ พร้อมกับ ClusterIssuer หนึ่งรายการ โดยเวอร์ชัน v1.21.1 เป็นเวอร์ชันปัจจุบัน ณ เดือนสิงหาคม 2026
sudo k3s kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.21.1/cert-manager.yamlapiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt
spec:
acme:
email: you@example.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-account-key
solvers:
- http01:
ingress:
ingressClassName: traefikการทำ HTTP-01 challenge หมายความว่าเซิร์ฟเวอร์ ACME (automatic certificate management environment) จะเชื่อมต่อเข้ามาที่ http://hello.example.com/.well-known/acme-challenge/... จากอินเทอร์เน็ตสาธารณะ ดังนั้น DNS A record จะต้องชี้มาที่ VPS แล้ว และพอร์ต 80 จะต้องเข้าถึง Traefik ได้ หากคุณปิดการใช้งาน ServiceLB และใช้ proxy ของคุณเองอยู่หน้าสุด proxy นั้นจะต้องส่งต่อ path ของ challenge ด้วย มิฉะนั้น cert-manager จะค้างและแสดงสถานะบน Challenge object ดังนี้:
Waiting for HTTP-01 challenge propagation: wrong status code '404', expected '200'ตรวจสอบสถานะการออกใบรับรองได้ด้วย sudo k3s kubectl describe certificate hello-tls และ sudo k3s kubectl get order,challenge -A
ไฟล์ kubeconfig และเหตุผลที่ API server ควรเป็นส่วนตัว
k3s จะเขียนข้อมูลรับรองผู้ดูแลระบบลงใน /etc/rancher/k3s/k3s.yaml โดยค่าเริ่มต้นไฟล์นี้จะมีเจ้าของเป็น root และกำหนดสิทธิ์แบบ 600 ไฟล์นี้เก็บ client certificate ที่มีสิทธิ์ระดับ cluster-admin ดังนั้นใครก็ตามที่อ่านไฟล์นี้ได้ย่อมถือสิทธิ์ควบคุมคลัสเตอร์ทั้งหมด
sudo k3s kubectl get node
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get nodeหากติดตั้ง kubectl แยกต่างหากโดยไม่ได้ตั้งค่า KUBECONFIG จะเกิดข้อผิดพลาด The connection to the server localhost:8080 was refused - did you specify the right host or port? เนื่องจากโปรแกรมจะหันไปใช้ค่าเริ่มต้นซึ่งไม่เกี่ยวข้องกับ k3s ให้ตั้งค่า KUBECONFIG หรือใช้ sudo k3s kubectl ซึ่งจะอ่านไฟล์ที่ถูกต้องได้โดยอัตโนมัติ
คุณอาจพบคำแนะนำให้ใช้ --write-kubeconfig-mode 644 เพื่อให้ผู้ใช้ทั่วไปสามารถรัน kubectl ได้ โปรดเข้าใจผลกระทบของการกระทำนี้: มันเป็นการทำให้ข้อมูลรับรองระดับ cluster-admin สามารถอ่านได้โดยทุกบัญชีผู้ใช้ในเครื่อง สำหรับเครื่องที่มีผู้ดูแลระบบเพียงคนเดียวอาจเป็นสิ่งที่ยอมรับได้ แต่สำหรับเครื่องที่มีผู้ใช้ร่วมกันนั้นไม่ควรทำ การคัดลอกไฟล์ไปไว้ที่อื่นจะช่วยให้ผู้ใช้รายนั้นเข้าถึงได้โดยไม่ต้องเปิดเผยไฟล์ต่อสาธารณะ:
mkdir -p ~/.kube
sudo install -o $USER -g $USER -m 600 /etc/rancher/k3s/k3s.yaml ~/.kube/configบรรทัด server: ในไฟล์ดังกล่าวระบุค่าเป็น https://127.0.0.1:6443 หากต้องการใช้ kubectl จากแล็ปท็อปของคุณ อย่าเปิดพอร์ต 6443 ออกสู่อินเทอร์เน็ต Kubernetes API ที่เปิดเป็นสาธารณะถือเป็นเป้าหมายที่เสี่ยง และการเปิดทิ้งไว้คือสาเหตุที่ทำให้คลัสเตอร์ขนาดเล็กถูกนำไปใช้ขุดสกุลเงินดิจิทัลโดยผู้อื่น ให้ใช้วิธีทำ tunnel ผ่าน SSH แทน และคงค่าที่อยู่ในไฟล์ไว้ตามเดิม:
ssh -N -L 6443:127.0.0.1:6443 user@your-vps
KUBECONFIG=./k3s.yaml kubectl get nodeหากจำเป็นต้องเข้าถึง API ผ่านที่อยู่เครือข่ายส่วนตัว ให้ติดตั้งโดยใช้ tls-san พร้อมระบุชื่อหรือที่อยู่นั้น จากนั้นแก้ไขบรรทัด server: ในไฟล์ที่คัดลอกมาให้ตรงกัน หากไม่มีรายการ SAN (subject alternative name) โปรแกรม kubectl จะปฏิเสธการเชื่อมต่อ:
x509: certificate is valid for 127.0.0.1, 10.43.0.1, not 203.0.113.10พอร์ตขาเข้าที่ระบุไว้สำหรับคลัสเตอร์คือ TCP 6443 สำหรับ API, UDP 8472 สำหรับ flannel VXLAN ระหว่างโหนด และ TCP 10250 สำหรับ kubelet metrics สำหรับโหนดเดี่ยว ไม่จำเป็นต้องเปิดพอร์ตเหล่านี้ออกสู่อินเทอร์เน็ตเลยแม้แต่พอร์ตเดียว
containerd ไม่ใช่ Docker
k3s ทำงานโดยใช้ containerd ที่ฝังตัวอยู่ภายใน และไม่ได้ใช้ image store ร่วมกับ Docker ดังนั้น image ที่คุณเพิ่งสร้างด้วย docker build จะไม่ปรากฏให้ k3s เห็น ส่งผลให้ pod ล้มเหลวด้วยข้อผิดพลาด ErrImagePull แม้ว่า docker images จะแสดงรายการ image นั้นอยู่ก็ตาม คุณต้องนำเข้า (import) image ดังกล่าวอย่างชัดเจน:
docker save myapp:0.1 | sudo k3s ctr images import -
sudo k3s crictl imagesจากนั้นให้หลีกเลี่ยงการใช้ tag :latest กับ container นั้น เนื่องจาก :latest จะตั้งค่า imagePullPolicy เป็น Always โดยค่าเริ่มต้น ทำให้ kubelet พยายามดึง image จาก registry อยู่ดี สำหรับ tag อื่นๆ จะใช้ค่าเริ่มต้นเป็น IfNotPresent ซึ่งจะเรียกใช้ image ที่คุณนำเข้าไว้ การรันทั้งสองระบบบน VPS เดียวกันสามารถทำได้ และควรทราบว่า การติดตั้ง Docker ปกติบน VPS และ k3s ต่างก็แยกเก็บ image store และกฎ iptables ของตนเองบนเครื่องเดียวกัน
วิธีการลบ k3s
ตัวติดตั้งจะสร้างสคริปต์สำหรับการถอนการติดตั้งไว้ให้ โดยไม่มีตัวเลือกสำหรับการลบเพียงบางส่วนหรือการย้อนกลับการตั้งค่า
sudo /usr/local/bin/k3s-uninstall.shสคริปต์นี้จะหยุดการทำงานและลบ service, ลบ datastore, ลบข้อมูล persistent volume, ลบการตั้งค่า node และลบเครื่องมือต่างๆ ที่ตัวติดตั้งเพิ่มเข้าไป สำหรับ node ที่เป็น agent สคริปต์จะเป็น k3s-agent-uninstall.sh แทน ให้คัดลอกข้อมูลทุกอย่างภายใต้ /var/lib/rancher/k3s/storage ออกจากเครื่องก่อนดำเนินการ เนื่องจากไดเรกทอรีดังกล่าวจะถูกลบไปด้วย หลังจากนั้นให้ตรวจสอบว่าไม่มีกระบวนการใดค้างอยู่โดยการตรวจสอบพอร์ตหรืออินเทอร์เฟซด้วย ip link show และ sudo ss -lntp หากยังมีอินเทอร์เฟซ cni0 หรือ flannel.1 หลงเหลืออยู่ อินเทอร์เฟซเหล่านั้นจะถูกล้างออกเมื่อรีบูตเครื่องในครั้งถัดไป
การตัดสินใจว่าการใช้ Kubernetes node เดียวเป็นภาระเกินความจำเป็นสำหรับงานนั้นถือเป็นเรื่องปกติ ไม่ใช่ความล้มเหลว การย้าย workload เหล่านั้นกลับไปใช้ Compose มักใช้เวลาเพียงช่วงบ่ายเท่านั้น
รูปแบบความล้มเหลวและข้อความที่คุณจะพบ
Node อยู่ในสถานะ NotReady หรือ k3s รีสตาร์ทวนซ้ำ โปรดอ่าน sudo journalctl -u k3s -n 200 --no-pager ก่อน สำหรับ VPS ขนาดเล็ก สาเหตุที่พบบ่อยคือ kernel out-of-memory killer สั่งยุติกระบวนการทำงาน ซึ่งจะปรากฏใน dmesg เป็นบรรทัดที่ระบุชื่อ k3s-server ข้อกำหนดขั้นต่ำที่ 2 GB คือค่าที่ต้องมีจริง
Pod ค้างอยู่ในสถานะ Pending kubectl describe pod จะระบุสาเหตุไว้เสมอ Insufficient memory หรือ Insufficient cpu หมายความว่าโหนดไม่มีทรัพยากรเหลือเพียงพอ didn't have free ports คือปัญหา hostPort ชนกันตามที่กล่าวไว้ข้างต้น waiting for first consumer บน PVC หมายความว่า WaitForFirstConsumer กำลังทำงานตามหน้าที่ของมัน
ImagePullBackOff เกิดจาก tag ไม่มีอยู่ใน registry ใดๆ ที่โหนดเข้าถึงได้ หรือคุณสร้าง image ด้วย Docker แต่ไม่ได้นำเข้า (import) ไปยัง containerd
Traefik ตอบสนอง แต่แอปพลิเคชันไม่ตอบสนอง เนื้อหาการตอบกลับ 404 page not found มาจากตัว Traefik เอง ซึ่งหมายความว่าคำขอมาถึงแล้วแต่ไม่มี router ใดตรงกับเงื่อนไข ตรวจสอบว่า Ingress host ตรงกับชื่อที่คุณระบุ และ ingressClassName มีค่าเป็น traefik
Cluster DNS ล้มเหลวในขณะที่โฮสต์ยัง resolve ได้ปกติ ตรวจสอบ CoreDNS ด้วย sudo k3s kubectl -n kube-system logs -l k8s-app=kube-dns ข้อความเช่น plugin/loop: Loop ... detected for zone "." จะทำให้ CoreDNS ไม่สามารถเริ่มทำงานได้ ปัญหานี้เกิดขึ้นเพราะ CoreDNS กำลังส่งต่อคำขอไปยัง resolver ที่ส่งคำขอกลับมาหาตัวมันเอง ซึ่งเป็นสิ่งที่เกิดขึ้นเมื่อใช้ loopback address ใน /etc/resolv.conf ให้ชี้ k3s ไปยังไฟล์ upstream จริงด้วย --resolv-conf /run/systemd/resolve/resolv.conf
FAQ
การรัน k3s บน VPS เครื่องเดียวคุ้มค่าหรือไม่?
คุ้มค่าหากคุณต้องการใช้งาน Kubernetes API ไม่ว่าจะเป็นเพื่อการเรียนรู้บนเครื่องที่คุณควบคุมเอง, การเก็บ deployment ไว้ในรูปแบบ manifest ที่ย้ายไปที่อื่นได้ง่าย, การรันซอฟต์แวร์ที่มีเฉพาะ Helm chart หรือการสร้างระบบเพื่อเตรียมย้ายไปใช้ managed cluster ในอนาคต แต่จะไม่คุ้มค่าหากคุณเพียงแค่ต้องการรัน container เพราะ Docker Compose ทำงานได้โดยมีภาระในการดูแลรักษาน้อยกว่ามากและประหยัด RAM ได้มากกว่าประมาณ 1 GB การรันเพียงโหนดเดียวไม่ช่วยเรื่อง high availability ดังนั้นความน่าเชื่อถือจึงไม่ใช่เหตุผลในการเลือกใช้
ทำไม service ประเภท LoadBalancer ของ k3s ถึงค้างอยู่ที่สถานะ Pending?
ServiceLB จะสร้าง svclb- pod ที่จองพอร์ตของ service ไว้เป็น hostPort บนโหนด ดังนั้น pod จะถูก schedule เฉพาะในจุดที่พอร์ตเหล่านั้นว่างอยู่เท่านั้น หาก nginx หรือ proxy อื่นใช้งานพอร์ต 80 ไปแล้ว pod จะค้างอยู่ที่สถานะ Pending และ service จะไม่ได้รับ external address kubectl -n kube-system describe pod svclb-... จะรายงานสถานะ 0/1 nodes are available: 1 node(s) didn't have free ports for the requested pod ports ให้คุณปลดล็อกพอร์ตดังกล่าว หรือติดตั้งใหม่ด้วย --disable=servicelb แล้วทำ proxy ไปยัง NodePort ที่ service ยังคงจัดสรรไว้ให้แทน
k3s ต้องการ RAM เท่าไรบน VPS?
ข้อกำหนดขั้นต่ำสำหรับ server node คือ 2 core และ 2 GB ซึ่งครอบคลุมการทำงานของ k3s และส่วนประกอบที่มาพร้อมกันก่อนจะเริ่มรัน workload ของคุณ จากการวัดผลของโครงการพบว่า server node ใช้ RAM อยู่ที่ 1,596 MB เมื่อมีการรัน monitoring stack ไว้ด้วย ดังนั้นให้ถือว่า 2 GB คือค่าต่ำสุด และ 4 GB คือขนาดเริ่มต้นที่ทำให้การรันโหนดเดียวทำงานได้อย่างราบรื่น ให้วัดค่าจากเครื่องของคุณด้วย free -h ก่อนการติดตั้ง และวัดอีกครั้งหลังจากทุก pod ใน kube-system มีสถานะเป็น Running
ฉันสามารถรัน Docker และ k3s บน VPS เครื่องเดียวกันได้หรือไม่?
ได้ ทั้งสองระบบจะทำงานแยกจากกัน k3s ใช้ containerd ที่ฝังมาในตัว ดังนั้น image ที่สร้างด้วย docker build จะไม่ปรากฏใน k3s จนกว่าคุณจะรัน docker save myapp:0.1 | sudo k3s ctr images import - นอกจากนี้ ทั้งสองระบบยังเขียน iptables rules และจัดการ bridge network ของตัวเองแยกกัน ให้เฝ้าระวังการใช้หน่วยความจำรวม เพราะ Docker รวมกับ k3s และ container ของคุณจะไม่สามารถรันบนเครื่องขนาด 2 GB ได้
ฉันจะลบ k3s ออกทั้งหมดได้อย่างไร?
ให้รัน sudo /usr/local/bin/k3s-uninstall.sh บน server node หรือ sudo /usr/local/bin/k3s-agent-uninstall.sh บน agent ระบบจะหยุดการทำงานของ service, ลบ datastore, ลบข้อมูล persistent volume ภายใต้ /var/lib/rancher/k3s/storage และลบเครื่องมือที่มาพร้อมกับชุดติดตั้งออกทั้งหมด ให้คัดลอกข้อมูลที่ต้องการเก็บไว้ออกไปก่อน เพราะการดำเนินการนี้ไม่สามารถย้อนกลับได้ ส่วน network interface อย่าง cni0 หรือ flannel.1 ที่อาจหลงเหลืออยู่จะหายไปเองเมื่อรีบูตเครื่องในครั้งถัดไป