THEN
PART 5 · ดูแล

ความปลอดภัย + อัปเกรดและแก้ปัญหา

Security and monitoring · upgrade runbook and troubleshooting

TL;DR — สรุปใน 30 วินาทีtldr

สรุปหลักความปลอดภัยของ BOMLLM ในหนึ่งประโยค: ทุกอย่าง bind อยู่ที่ localhost หรือ Tailscale ของที่เปิดสู่โลกคือ Cloudflare Tunnel ประตูเดียว ทุก API มีกุญแจ ทุกกุญแจมีงบ และไม่มีความลับถูก commit ลง repo เลย

บทสุดท้ายคือ "คู่มือผู้รอดชีวิต" — ทุกบทอื่นชี้กลับมาที่นี่เมื่อมีปัญหา มีสองส่วน: วินัยการอัปเกรดที่ทำให้ระบบไม่พังตอนอัป และ สูตรแก้เมื่อมันพังตอนตี 2

เมื่อไหร่ควรใช้ / When to usewhen to use

บทนี้ไม่ใช่ "อ่านผ่าน" — กลับมาอ่านซ้ำทุกครั้งที่ (1) เพิ่มช่องทางใหม่ (2) เปิดพอร์ต/บริการใหม่ (3) ก่อนอัปเกรดใหญ่ นะครับ

กลับมาที่บทนี้ทุกครั้งที่: จะอัปเกรดส่วนไหนก็ตาม, เจออาการผิดปกติ, หรืออ่านเจอคำว่า "ถ้าติดตรงไหน" ในบทอื่นนะครับ

วิธีทำ / How-tohow to

ตาราง bind ของทุก service (นโยบายจริงจาก ss -tlnp):

Servicebind ที่เหตุผล
LiteLLM :4000127.0.0.1 (+ Tailscale ผ่าน forward สำหรับ probe)เส้นทางสาธารณะคือ tunnel เท่านั้น
Open WebUI :3000127.0.0.1อยู่หลัง tunnel
SearXNG :8888127.0.0.1ภายในเท่านั้น
Qdrant :6333127.0.0.1ไม่มีเวกเตอร์สาธารณะ
Postgres / Valkeyเครือข่าย container เท่านั้นไม่เปิด host port เลย
Ollama (Mac) :11434Tailscale IP เท่านั้นmesh เท่านั้น + firewall เปิด
SSHพอร์ตไม่มาตรฐาน + key เท่านั้นufw ตั้ง DROP เป็นค่าตั้งต้น

คุณสมบัติที่ตรวจซ้ำทุกรอบ audit:

# ตรวจเบื้องต้นด้วยตัวเองได้ (รันบน VPS)
curl -o /dev/null -w "%{http_code}" -X POST https://llm.example.com/v1/chat/completions
# ต้องได้ 401 — ไม่มีกุญแจห้ามผ่าน (ตรวจทุกรอบ)

curl -o /dev/null -w "%{http_code}" https://chat.example.com/api/models
# ต้องได้ 401 — ไม่มี session ห้ามเห็นโมเดล

ss -tlnp | grep -v 127.0.0.1   # ดูว่ามีอะไร Listen นอก localhost ที่ไม่ควรมี

ถ้าผลใดผิดจากนี้ = หยุดทุกอย่าง แก้ให้กลับมาถูกก่อนครับ

สุขลักษณะความลับ (ทบทวนจากบทที่ ๔): master key อยู่ใน .env บน VPS เท่านั้น, กุญแจรายช่องทางหมุนเวียนได้โดยไม่แตะ master, .env chmod 600 + gitignore, ใน log/รายงานพูดแค่ "โหลดคีย์แล้ว ยาว N ตัว", n8n เก็บความลับใน credential store เท่านั้น

การเฝ้าระวัง: Uptime Kuma บน NAS probe ทุก 5 นาที (ยิง GET /api/version ผ่าน mesh) และยิงแจ้งเตือน Telegram เมื่อสถานะพลิก — สถานะเฝ้าระวังสรุปไว้ในบอร์ดภายใน

ถ้าติดตรงไหน → บทที่ ๙

กฎ 6 ข้อของการอัปเกรด (ไม่มีข้อไหนต่อรอง):

  1. ปักเวอร์ชันทุกอย่าง — ห้าม latest เด็ดขาด (เคยโดน latest เปลี่ยนพฤติกรรมใต้มือ)
  2. หนึ่งองค์ประกอบต่อหนึ่งหน้าต่างเวลา — อัปสองอย่างพร้อมกันแล้วพัง = เรียนรู้อะไรไม่ได้เลย
  3. สำรองก่อนแตะ — dump Postgres + snapshot config และต้อง "กู้กลับได้จริง" (แบ็กอัพที่ไม่เคยกู้ = ข่าวลือ)
  4. เขียนแผนถอยก่อนพิมพ์คำสั่งอัป
  5. รันชุดตรวจ 6 ข้อหลังทุกการเปลี่ยน — เขียวทั้ง 6 หรือถอยกลับ
  6. นอกชั่วโมงเร่ง — หลัง 23:00 ของทุกวัน เว้นหน้าต่างรีสตาร์ทอัตโนมัติของเราเอง

ชุดตรวจ 6 ข้อ (รันหลังทุกการเปลี่ยนแปลง):

vps — ชุดตรวจมาตรฐาน (จาก docs/upgrade.md, sanitized) · 6-probe smoke
# 1. LiteLLM มีชีวิต + DB ต่ออยู่curl -sf https://llm.example.com/health/liveliness # 200curl -sf https://llm.example.com/health/readiness # 200, db=connected# 2. ประตูห้ามคนไม่มีกุญแจยังยืนอยู่POST /v1/chat/completions แบบไม่แนบกุญแจ # ต้องได้ 401# 3. แชทไทยจริงยิงทะลุทั้งสายพานcurl -sf .../v1/chat/completions -H "Authorization: $SMOKE_KEY" -d @smoke-payload.json# 4. WebUI 200 5. SearXNG local ตอบ 6. Mac: โมเดล resident + swap ไม่โตssh mac 'ollama ps && vm_stat | grep -i swap'

ต้นฉบับเต็มอยู่ใน docs/upgrade.md ของ repo — คัดลอกไปเป็นสคริปต์ของคุณได้เลยครับ

อาการที่พบบ่อยกับทางแก้บนมือ:

อาการสาเหตุที่พบบ่อยทางแก้
บอทตอบว่างเปล่าthink เปิด + num_predict น้อยpin think:false บน route (บทที่ ๔)
ข้อความไหลช้า ๆ ขึ้นเป็นพัก ๆสองโมเดล resident จน swapไล่โมเดลรอง (keep_alive 0) แล้วดูบทที่ ๒
เข้าเว็บแชทไม่ได้ 401 รัว ๆกุญแจ/เซสชันหมดอายุตรวจ master key ใน .env แล้ว up -d ใหม่
คำตอบไทยเริ่มมีตัวจีนชุกsystem prompt หลุด / route ใหม่ไม่ได้ใส่ Config Gกลับไปดู route + บทที่ ๖
ค่าคลาวด์วันนี้สูงผิดปกติMac ล่มนาน จน Tier 3 ทำงานหนักดู spend log รายชั่วโมง + เช็คสาเหตุที่ Mac
อัปเดตแล้วพังเวอร์ชันลอย / param ถูกตัดเงียบถอยกลับแท็กเดิมก่อน แล้วหาสาเหตุทีหลัง

สูตรตี 2 (พังตอนกลางคืน): (1) อย่า debug ลึก ๆ ตอนง่วง — ถอยกลับแท็กเดิมก่อน (2) เก็บ log 30 นาทีล่าสุดใส่โฟลเดอร์เหตุการณ์ (3) นอน (4) ทำ post-mortem หนึ่งหน้าใน 48 ชม. ระบุสาเหตุราก + กฎข้อไหนที่ถ้าทำตามจะกันเหตุนี้ แล้วเพิ่มกฎนั้นเข้าคู่มือ

ตัวอย่างการตั้งค่า / Config exampleconfig example

ตารางภัยคุกคามกับมาตรการ (ฉบับย่อจาก docs/security.md):

ภัยมาตรการที่ระบบทำ
กุญแจรั่ว (git/ภาพ/log)ระบบ gitignore + ตรวจก่อน push + งบต่อกุญแจจำกัดความเสียหาย
สแกนเนอร์อินเทอร์เน็ตเปิดผ่าน tunnel เท่านั้น + 401 ถ้าไม่มีกุญแจ
VPS โดนบุกไม่มีโมเดล/ข้อมูลหนักบน VPS; Mac อยู่ใน mesh; สำรองข้อมูลออกกล่องทุกคืน
Prompt injection ผ่านผลค้นเว็บผลค้นเป็นบริบท ไม่ใช่คำสั่ง + system prompt ตรึงพฤติกรรม + บอทมี post-process
ค่าใช้จ่ายหนีบงบรายวันรายกุญแจ + เพดานรวม $5 + แจ้งเตือน
อุปทาน (image) เปลี่ยนใต้มือปักแท็กเวอร์ชันทุกตัว ห้าม latest

ขั้นตอนอัปเกรดยอดฮิตแบบย่อ (เต็มอยู่ใน docs/upgrade.md) ครับ:

# --- Ollama บน Mac ---
brew upgrade ollama          # แล้ว restart service ครับ
ollama --version             # client == server ต้องตรงกันครับ
ollama ps                    # โมเดลหลักต้องกลับมา resident ครับ
# แชทอุ่นเครื่องแรก < 5 วิ จึงนับว่าเสร็จ

# --- LiteLLM / WebUI บน VPS ---
docker compose pull litellm          # แท็กที่ปักไว้ ครับ
docker compose up -d litellm         # recreate ตัวเดียวครับ
docker compose logs -f litellm       # ดู boot: parse config + DB migrate ครับ
# หลังอัป: diff /model/info กับ snapshot ก่อนอัป + รัน smoke 6 ข้อ

ความเสี่ยงสุด = migration ของฐานข้อมูล ดังนั้น dump Postgres ทุกครั้งก่อน pull ครับ

Pro tip — เคล็ดลับจากสนามจริงpro tip

★ PROD LESSON
จุดอ่อนที่เราเคยเจอเอง: Netdata — รอบตรวจหนึ่งเราเจอ Netdata :19999 ลอยอยู่บน 0.0.0.0 โดยไม่มีใครตั้งใจ — จากวันนั้น "เช็ค bind ของทุกอย่างที่ติดตั้งใหม่" ถูกใส่เข้าไปในเช็กลิสต์ audit ถาวร จงสมมติว่ามี service ที่คุณลืมอยู่เสมอ แล้วไปหามันด้วย ss -tlnp
★ PRO TIP
ข้อมูลอยู่ที่ไหน ตอบให้ได้ทุกเมื่อ — แชท/ผู้ใช้อยู่ใน Postgres (VPS), เวกเตอร์ RAG ใน Qdrant (VPS), สำรองทุกคืนออกไป NAS เก็บ 14 วัน — ถ้าตอบโจทย์นี้ไม่ได้ แปลว่ายังไม่รู้ระบบตัวเองพอจะป้องกันมันได้
★ PROD LESSON
แบ็กอัพที่ไม่เคยกู้ = ไม่มีอยู่ — เราทดสอบกู้จริงอย่างน้อยปีละครั้ง โดยเอา dump ไป restaure บนเครื่องทดสอบ — ครั้งแรกที่ลอง คุณจะพบว่าอะไรขาดหายไปในสคริปต์แบ็กอัพของตัวเองแน่นอน (ทุกคนเจอ)
★ PRO TIP
จดบันทึกการอัปเกรดทุกครั้ง — ตารางง่าย ๆ: วันที่ | อะไร | จาก->ไป | smoke ผล | ต้องถอยไหม — สามเดือนต่อมาเวลาถามว่า "เริ่มพังตั้งแต่เมื่อไหร่" ตารางนี้คือคำตอบที่ถูกที่สุด

คำถามที่พบบ่อย / FAQfaq

ถาม: ข้อมูลออกนอกบ้านไหม? — ไม่ ยกเว้นกรณี Tier 3 คลาวด์ถูกดึงใช้ (สามารถปิดขาดได้ในบทที่ ๔) และการค้นเว็บที่ออนไลน์เพื่อดึงผลลัพธ์กลับ (ตัวค้นตั้งในเครื่องแต่ตัวเว็บไซต์เป้าหมายเห็น IP ของ VPS ตามปกติ)

ถาม: ถ้าอยากล็อกแน่นสุด ๆ? — ปิด signup บน WebUI + ครอบ Cloudflare Access ทุกหน้า admin + ปิด Tier 3 + เปิด ufw DROP ทั้งขาเข้ายกเว้น SSH/Tailscale — ระบบจะเงียบสนิทกับโลกภายนอก

คำถามอื่น ๆ ดูที่ FAQ

ถาม: ควรอัปเดตถี่แค่ไหน? — มี CVE ด่วน = ทันทีแต่ตามกฎ 6 ข้อ, ปกติ = เดือนละครั้งในหน้าต่างกลางคืน, macOS = เลือกสัปดาห์ว่าง ๆ เท่านั้น

ถาม: พังจนไม่รู้จะทำไง? — ถอยทั้ง stack กลับแท็กเดิมทั้งหมด (ทำได้เพราะทุกอย่างปักเวอร์ชัน + มี dump) แล้วค่อยหาสาเหตุบนสำเนา ไม่ใช่บนของจริงที่ลูกค้ากำลังใช้

ดูเพิ่ม / See alsosee also

ภาพประกอบ / Figuresfigures

ประตู Cloudflare Access ที่คุ้มหน้า admin ทุกตัว
ชั้นป้องกันก่อนถึงหน้า login ของแอปจริง — ใช้งานจริงใน production
บันได fallback 3 ชั้น — เหตุผลที่ระบบทนต่อการอัปเกรด
ตอนอัปเกรองค์ประกอบหนึ่ง อีกสองชั้นรับงานแทนชั่วคราว

คลิปหน้าจอจริงตามบทเรียน · คลิกเล่นเพื่อฟังสรุป · ไม่ใช่คำแนะนำการลงทุนครับ

▶ ดูวิดีโอคู่มือ: https://youtu.be/O4hM39cgDCY