Skip to content

Latest commit

 

History

History
135 lines (95 loc) · 7.22 KB

File metadata and controls

135 lines (95 loc) · 7.22 KB

19 — Production Hygiene (ให้โปรแกรม “รันจริง” ได้แบบนิ่งและดูแลง่าย)

TOC · Prev

Keywords: tooling, config, logging, concurrency

อ่านแบบคน Python:

  • ถ้าอยากเอา “ภาพรวม” ก่อน: คิดเป็น 4 แกน: deps / secrets / config / observability
  • ถ้าอยาก “ลงมือทำ”: หยิบ checklist ท้ายบท แล้วไล่ทีละข้อกับโปรเจกต์ของคุณ
  • ถ้าติด: เปิด 12-learning-playbook.md

บทนี้เป็น “บทเสริม” ที่เอาหลักการจากบทก่อน ๆ มารวมเป็นชุด เพื่อให้โปรแกรมของคุณ รันซ้ำแล้วเหมือนเดิม, พังแล้ว debug ได้, และ ไม่ต้องแก้ไฟดับทุกครั้งที่ย้ายเครื่อง

ขอบเขตของบทนี้ (ตั้งใจให้ปลอดภัย):

  • เน้นแนวทางเชิงป้องกัน (defensive) และความทนทานของระบบ
  • ไม่สอนเทคนิคโจมตี/หลบการตรวจจับ/ทำลายระบบ

1) Dependencies: ห้าม “ติดตั้งตอน runtime”

กรณีที่เจอบ่อยใน Python คือโค้ดไปเรียก pip install ... ตอนรันจริง

  • ปัญหา: พังยาก, ทำซ้ำไม่ได้, และกลายเป็นพื้นที่เสี่ยงด้าน supply chain

Python: แนวทางที่นิ่งกว่า

  • ตรึง dependency ไว้ล่วงหน้า (เช่น requirements.txt หรือ lock file ของเครื่องมือที่ใช้)
  • แยกขั้นตอน install ออกจากการรันโปรแกรม

Rust: แนวทางตามธรรมชาติของ ecosystem

  • dependencies อยู่ใน Cargo.toml
  • Cargo.lock ช่วยล็อกเวอร์ชันให้ reproducible (โดยเฉพาะ binary)

แนวคิด:

  • “รันโปรแกรม” กับ “ติดตั้ง dependency” เป็นคนละเฟส

2) Secrets: แยก secret ออกจาก code (และจาก logs)

หลักง่าย ๆ:

  • secret ไม่ควรอยู่ใน git
  • secret ไม่ควรไปโผล่ใน query string/URL
  • secret ไม่ควรหลุดไปใน logs หรือ error messages

2.1 Python mindset (สิ่งที่ควรระวัง)

  • เผลอ print(config) แล้วมี token/secret ติดไปด้วย

2.2 Rust mindset

  • ปล่อยให้ config loader คืน error ที่ “มีบริบทพอ” แต่ไม่พ่น secret
  • log เป็น event/fields แล้วเลือก field ที่จะ render (ช่วยกันหลุด)

แนวทาง:

  • ใส่ request_id/job_id เพื่อ debug แต่หลีกเลี่ยงการใส่ secret ลงใน fields

3) Config: อ่านง่าย พังแล้วดัง และเขียนกลับแบบปลอดภัย

3.1 โครงคิด 4 ชั้น (ทวนจากบท 09)

  • I/O → JSON syntax → schema → semantics

3.2 Atomic write (ทำไมต้องทำ)

ถ้าต้องเขียนไฟล์ config/state ระหว่างรัน:

  • ห้ามเขียนทับไฟล์เดิมตรง ๆ เพราะ crash กลางทาง = ไฟล์ครึ่งก้อน

แนวทาง (platform-agnostic):

  1. เขียนไฟล์ใหม่ (.tmp)
  2. replace ด้วย rename/replace (atomic ในระดับ filesystem ส่วนใหญ่)

ตัวอย่าง pseudo-flow (ไม่ลงรายละเอียด OS-specific):

write endpoint.json.tmp
fsync (optional)
rename endpoint.json.tmp -> endpoint.json

3.3 ใส่ version เมื่อ schema เริ่มเปลี่ยน

  • ตอนแรกไม่จำเป็น
  • แต่พอเริ่มเพิ่ม/เปลี่ยน field บ่อย ๆ ให้ใส่ version เพื่อ migrate อย่างมีจังหวะ

4) Observability: logs ที่ไล่เหตุการณ์ได้จริง

ถ้า log เป็น “ประโยคยาว ๆ” อย่างเดียว:

  • search ยาก
  • สรุปสถิติยาก
  • correlation ยาก (เหตุการณ์ชุดเดียวกันกระจัดกระจาย)

แนวคิดที่คุ้มที่สุด:

  • structured log (event + fields)
  • ใส่ request_id/trace_id/job_id

ตัวอย่าง fields ที่พอสำหรับโปรเจกต์เล็ก:

  • level: info/warn/error
  • category: auth/config/api
  • job_id: UUID
  • action: load_config / start_worker / shutdown

5) Concurrency hygiene: คุม lifecycle ของ background jobs

ถ้าคุณมีงาน background:

  • อย่าปล่อยให้มัน “เกิดเอง” หลายจุดจนคุณไม่รู้ว่ามีกี่ตัวรันอยู่

แนวคิดที่ใช้ได้จริง:

  • มี owner ชัด (สร้าง/หยุด)
  • สั่งหยุดได้ (cancellation)
  • รอให้จบได้ (join) เพื่อ shutdown อย่างสะอาด

หมายเหตุ:

  • ในบทนี้ไม่ลงรายละเอียด ops/process control; แนะนำให้พึ่ง supervisor ของระบบ (เช่น container/system service) มากกว่าการไป pkill จากในแอป

6) Mini checklist (เอาไปใช้กับโปรเจกต์จริงได้ทันที)

  1. deps
  • ไม่มี pip install/ดาวน์โหลด dependency ตอน runtime
  • มี lock/วิธีทำซ้ำได้
  1. config
  • โหลด config ที่จุดเดียว (เช่น main)
  • error บอก path/ชั้นที่พัง
  • ถ้าต้องเขียนไฟล์: ใช้ atomic write
  1. logs
  • log เป็น event-first
  • มี request_id/job_id
  • ไม่ log secrets
  1. concurrency
  • background jobs มี start/stop/join
  • lock scope แคบ และไม่ถือ lock ตอนทำ I/O หนัก