1 คะแนน โดย GN⁺ 2023-10-13 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • The Twelve-Factor App คือแนวทางสำหรับการดำเนินงานและขยายเว็บแอป·SaaS ในระยะยาว โดยครอบคลุมทั้งการทำงานอัตโนมัติของการตั้งค่า ความสามารถในการพกพา การดีพลอยบนคลาวด์ และการดีพลอยอย่างต่อเนื่อง
  • ไม่ยึดติดกับการผสมผสานของ ภาษาโปรแกรม ใดภาษาโปรแกรมหนึ่ง หรือ บริการสนับสนุน เช่น ฐานข้อมูล คิว หรือเมมโมรีแคช จึงสามารถนำไปใช้กับแอปพลิเคชันแบบบริการได้อย่างหลากหลาย
  • ตั้งอยู่บนพื้นฐานของประสบการณ์จากการมีส่วนร่วมโดยตรงในการพัฒนา·ดีพลอยแอปหลายร้อยตัวบนแพลตฟอร์ม Heroku และการสังเกตการพัฒนา·การดำเนินงาน·การขยายตัวของแอปหลายแสนตัวโดยอ้อม
  • ประเด็นสำคัญคือการมอบคำศัพท์ร่วมที่ช่วยลดต้นทุนการทำงานร่วมกันและการสึกกร่อนของซอฟต์แวร์เมื่อแอป เติบโตแบบเป็นธรรมชาติ
  • ไม่เพียงแต่นักพัฒนาที่สร้างแอปพลิเคชันแบบบริการเท่านั้น แต่ยังใช้เป็นมาตรฐานเชิงปฏิบัติสำหรับวิศวกรปฏิบัติการที่นำไปดีพลอยและดูแลจัดการได้ด้วย

หลักการดำเนินงาน 12 ข้อสำหรับแอป SaaS

  • ซอฟต์แวร์สมัยใหม่มักให้บริการในรูปแบบ เว็บแอป หรือ SaaS และ Twelve-Factor App คือแนวทางสำหรับการสร้างแอปพลิเคชันลักษณะนี้
  • เป้าหมายคือทำให้กระบวนการพัฒนา·ดีพลอย·ดำเนินงานของแอปคาดการณ์ได้มากขึ้น
    • ใช้ การทำงานอัตโนมัติของการตั้งค่าแบบประกาศชัดเจน เพื่อลดเวลาและต้นทุนเมื่อมีนักพัฒนาใหม่เข้าร่วมโปรเจกต์
    • สร้างข้อตกลงที่ชัดเจนกับระบบปฏิบัติการพื้นฐาน เพื่อเพิ่ม ความสามารถในการพกพา ระหว่างสภาพแวดล้อมการรัน
    • ออกแบบให้ลดภาระของการดูแลเซิร์ฟเวอร์และระบบ และเหมาะกับการดีพลอยบน คลาวด์แพลตฟอร์ม สมัยใหม่
    • ลดความแตกต่างระหว่างสภาพแวดล้อมพัฒนาและโปรดักชัน เพื่อให้เกิด การดีพลอยอย่างต่อเนื่อง
    • ทำให้สามารถขยายได้โดยไม่ต้องเปลี่ยนเครื่องมือ สถาปัตยกรรม หรือแนวปฏิบัติการพัฒนาอย่างมาก
  • ขอบเขตการใช้งานไม่ได้จำกัดอยู่กับเทคโนโลยีสแตกเฉพาะ
    • ใช้ได้กับแอปที่เขียนด้วยภาษาโปรแกรมใดก็ได้
    • บริการสนับสนุนครอบคลุมฐานข้อมูล คิว เมมโมรีแคช เป็นต้น

ภูมิหลังที่สรุปจากประสบการณ์ของ Heroku

  • ผู้มีส่วนร่วมได้มีส่วนเกี่ยวข้องโดยตรงกับการพัฒนา·ดีพลอยแอปหลายร้อยตัวบนแพลตฟอร์ม Heroku และสังเกตการพัฒนา·การดำเนินงาน·การขยายตัวของแอปหลายแสนตัวโดยอ้อม
  • ได้สรุปแนวปฏิบัติอันเหมาะสมสำหรับการพัฒนาแอปจากประสบการณ์และการสังเกตที่ได้จากแอป SaaS จริง
  • รูปแบบได้รับแรงบันดาลใจจาก Patterns of Enterprise Application Architecture และ Refactoring ของ Martin Fowler

12 องค์ประกอบ

  • I. Codebase: มีโค้ดเบสเดียวที่อยู่ภายใต้การควบคุมเวอร์ชันและมีหลายการดีพลอย
  • II. Dependencies: ประกาศและแยกการพึ่งพาอย่างชัดเจน
  • III. Config: เก็บการตั้งค่าไว้ใน environment
  • IV. Backing services: ปฏิบัติต่อบริการสนับสนุนเสมือนเป็นทรัพยากรที่เชื่อมต่ออยู่
  • V. Build, release, run: แยกขั้นตอน build กับขั้นตอน run ออกจากกันอย่างเคร่งครัด
  • VI. Processes: รันแอปเป็นหนึ่งหรือหลายโปรเซสแบบไร้สถานะ
  • VII. Port binding: ให้บริการแก่ภายนอกผ่านการ bind พอร์ต
  • VIII. Concurrency: scale-out ด้วยโมเดลโปรเซส
  • IX. Disposability: เพิ่มความทนทานด้วยการเริ่มต้นที่รวดเร็วและการปิดระบบอย่างนุ่มนวล
  • X. Dev/prod parity: รักษาให้ development, staging, production ใกล้เคียงกันมากที่สุด
  • XI. Logs: ปฏิบัติต่อ log เสมือนเป็นสตรีมของเหตุการณ์
  • XII. Admin processes: รันงานดูแลระบบเป็นโปรเซสแบบครั้งเดียว

1 ความคิดเห็น

 
GN⁺ 2023-10-13
ความคิดเห็นจาก Hacker News
  • 12-Factor App เป็นแนวทางแนะนำที่สร้างขึ้นในปี 2011 โดยอิงกับ Heroku และข้อจำกัดของโครงสร้างพื้นฐานแบบคอนเทนเนอร์ในยุคนั้นมากกว่า ไม่ได้ดูเหมือนเอกสารที่ตั้งอยู่บนหลักวิศวกรรมอย่างลึกซึ้ง
    เช่น ข้อเสนอที่ว่าให้ใส่การตั้งค่าไว้ใน environment variables นั้นเป็นเพราะผู้เขียนทำงานที่ Heroku และ Heroku ใช้วิธีเติม environment variables ผ่านช่องอินพุตของเว็บแอป
    ถ้าคุณอยากติดตามประวัติการตั้งค่าด้วย version control, ใช้ GitOps, ใช้ k8s ConfigMap หรือวางไฟล์ตั้งค่าไว้บน mounted volume ทั้งหมดนี้โดยรวมก็เป็นตัวเลือกที่ใช้ได้ เพราะช่วยแยกสถานะของการตั้งค่าออกจากสถานะการ deploy ของแอป
    ผมมองว่าเอกสารนี้สับสนระหว่างภาพรวมกับรายละเอียด และแนะนำสิ่งที่เข้ากับฟีเจอร์ของผลิตภัณฑ์ของบริษัทผู้เขียนมากกว่าหลักวิศวกรรมจริง ๆ จึงเป็น แนวทางที่เป็นอันตราย

    • จริงอยู่ที่ได้รับอิทธิพลจากวิธีทำงานของ Heroku อยู่ระดับหนึ่ง แต่ ConfigMap และ GitOps ไม่ได้ตอบโจทย์ด้านความปลอดภัยและความสะดวกในการใช้งานได้เหมือนกับการตั้งค่า/environment variables ของ Heroku
      ถ้าต้องการจัดเก็บการตั้งค่าอย่างปลอดภัยใน Kubernetes สุดท้ายก็ต้องใช้ Secrets ซึ่งมีรูปแบบ key-value คล้าย environment variables หากต้องการความปลอดภัยแบบเดียวกันใน Git ก็ต้องมีชั้นการเข้ารหัส และนั่นทำให้การเปรียบเทียบ diff พังลง รวมถึงต้องใช้เครื่องมือเพิ่มเติม
      สุดท้ายก็วนกลับมาที่เหตุผลว่าทำไมเครื่องมือ deploy ระดับสูงอย่าง Heroku ถึงถูกสร้างขึ้นมา
    • ตอนนี้ไม่มีใครเรียกมันว่า 12 factor แล้วก็จริง แต่เพราะมันเรายังทำตามหลักการทั่วไปเหล่านั้นอยู่ 12-Factor เป็นเอกสารก่อนที่ Docker และ Kubernetes จะกลายเป็นกระแสหลัก
      หลักการที่ว่าควรจัดการ log เป็น stream ยังถูกต้องอยู่ ให้เขียน log ไปที่ STDOUT ไม่ใช่ไฟล์ แล้วให้ออร์เคสเตรเตอร์อ่านและจัดเก็บ
      การตั้งค่ามาจาก environment แอปมักอ่านการตั้งค่าจากแหล่งที่ต่างกันตามวิธี deploy เช่น ในเครื่องใช้ .env ส่วน production ใช้ secret store
      port binding ก็เช่นกัน แอปเปิดพอร์ต แล้ววางของอย่าง nginx ไว้ด้านหน้าเพื่อทำ reverse proxy ส่วน K8S service และ ingress ทำหน้าที่นั้น
      คำวิจารณ์ใหญ่ที่สุดที่พูดได้กับ 12-Factor ในปัจจุบันคือ เอกสารถูกเขียนไว้ไม่ดีนัก และเหมือนสมมติว่าผู้อ่านรู้อยู่แล้วอย่างแม่นยำว่ากำลังพูดถึงอะไร
    • Heroku ไม่ได้เป็นผู้ประดิษฐ์ environment variables และไม่ได้เป็นคนแรกที่คิดว่าจะใช้กับการตั้งค่าแอป วิธีนี้ใช้กันมานานก่อน Heroku แล้ว
      แต่ก็ยอมรับว่า Heroku มีเครดิตในการทำให้แนวคิดนี้เป็นที่รู้จักกว้างขวางและแพร่หลาย
    • คำว่า “เก็บการตั้งค่าไว้ใน environment” ไม่ได้จำเป็นต้องหมายถึงให้ใส่การตั้งค่าไว้ใน environment variables เสมอไป แต่หมายถึงการตั้งค่ามาจาก สภาพแวดล้อมโฮสติ้ง ไม่ใช่จากตัวแอปเอง
      ไม่ใช่วิธีเพิ่ม settings.json ลงในเครื่องก่อนเริ่มแอป แต่เป็นแนวทางที่ว่า ถ้า source เดียวกันถูก deploy ไปยังคลัสเตอร์ AKS ของ Azure EU north ก็ใช้ค่าที่ตั้งไว้ในคลัสเตอร์นั้น และถ้า deploy ไปยังคลัสเตอร์ RPi Zero Docker Swarm ในกรอบรูป Ikea ก็ใช้การตั้งค่าของคลัสเตอร์นั้น
      ชื่อคลัสเตอร์นั้นเรียกว่า Gibson
    • แอป 12-Factor ที่ใส่การตั้งค่าไว้ใน environment variables นั้นย้ายไปใช้ ConfigMap ได้ง่ายมาก ในทางกลับกัน แอปธุรกิจทุกวันนี้ที่มี layout ของ ConfigMap ซับซ้อน หรือกรณีแย่สุดคือพึ่งพา k8s API โดยตรง จะกลายเป็นนรกเมื่อต้อง migrate ไปสู่สิ่งที่มาหลัง k8s
  • ผมรู้สึกว่าแต่ละข้อนี้สามารถโต้แย้งได้ค่อนข้างสมเหตุสมผล
    ข้อแรก หลักการที่ว่าแอปหนึ่งตัวต่อ repository หนึ่งตัวไม่ได้ผิดในระดับพื้นฐาน ถ้ามีหลายแอปที่ผูกกันแน่นในเชิงฟังก์ชันและใช้ release cycle ร่วมกัน แต่ต้อง deploy แยกกันเพื่อให้ได้ข้อดีของการเป็น process แยกและ scale ได้อิสระ ก็ไม่ได้มีปัญหาอะไรที่จะพัฒนาแอปเหล่านั้นใน repository เดียวกัน ตัวอย่างที่นึกถึงคือการแยก public API กับ worker process เช่น Sidekiq ของ Ruby, Celery ของ Python หรือ consumer ของ Kafka ทั่วไป
    ข้อสอง คำว่า “12-Factor app ไม่พึ่งพาการมีอยู่โดยนัยของแพ็กเกจระดับระบบ” นั้นในทางปฏิบัติทำได้ยากมากถ้าไม่ได้ใช้ของอย่าง Nix dependency ต่อ API ของ system call ใน kernel ก็ยังรั่วออกมา และแอป Rust ส่วนใหญ่ก็พึ่งพา glibc โดยนัย ยกเว้นกรณีใช้ musl ซึ่งมีอยู่ใน Linux distribution หลัก ๆ ถ้าไม่นับ distribution แบบ slim อย่าง Alpine ผมมองว่าเครื่องค้ำอย่าง Docker ก็จำเป็นเพื่อบรรเทาปัญหานี้
    ข้อสาม การเก็บการตั้งค่าไว้ใน environment variables ดูเปราะบางกว่า อาจลดความปลอดภัยเพราะบังคับให้ใส่ secret ไว้ใน environment และทำให้ต้องยอมทิ้ง config แบบไฟล์ที่มีโครงสร้าง ซึ่งได้ประโยชน์จาก type safety, autocomplete ใน IDE และการ parse อัตโนมัติ ใน environment variables ต้อง implement parser สำหรับค่าการตั้งค่าซับซ้อนที่ไม่ใช่ string เอง ในความเป็นจริงก็มีหลายกรณีที่ถูกเก็บเป็นไฟล์ .env แล้ว commit เข้า repository ทำให้ประเด็นเรื่องความปลอดภัยของการ commit แทบไม่มีความหมาย

    • นั่นเป็นการตีความแนวทางนั้นผิด ต้นฉบับพูดถึง support libraries ที่ติดตั้งผ่านระบบแพ็กเกจเฉพาะภาษาอย่าง CPAN หรือ Rubygems และอธิบายว่า library เหล่านั้นอาจถูกติดตั้งเป็น “site packages” ระดับระบบได้
      ไม่ได้หมายความว่าอย่าพึ่งพา operating system รวมถึง glibc แต่หมายความว่าอย่าทำให้ต้องมีแพ็กเกจจาก package manager ของภาษาหนึ่ง ๆ ติดตั้งอยู่บนเครื่องก่อนเท่านั้น
      ส่วนข้อชี้แนะที่เหลือเห็นด้วย โดยเฉพาะเรื่อง environment variables นั้นดูใกล้เคียงกับการที่ผู้เขียนนำวิธีที่พบได้บ่อยในการพัฒนา Ruby ตอนนั้นมาเขียนโดยสมมติว่าเป็นวิธีที่ดีที่สุด มากกว่าจะเป็นคำแนะนำที่มีหลักฐานจริงรองรับ
    • ถ้า deploy แยกกัน ก็ไม่ได้ใช้ release cycle ร่วมกัน สุดท้ายสักวันจะมีชุดเวอร์ชันที่ไม่ตรงกันถูกเรียกใช้งานได้ เช่น deploy เพียงชิ้นเดียวอาจล้มเหลว
      ดังนั้นต้องเตรียมรับมือสถานการณ์แบบนั้น และถ้าต้องการทดสอบชุดเวอร์ชันต่าง ๆ ร่วมกันได้ง่าย ผมมองว่าใช้ repository แยก ดีกว่า
      ภาษา high-level ส่วนใหญ่ไม่ได้พึ่งพา glibc เวอร์ชันเฉพาะ ถ้า runtime ของภาษานั้นทำงานถูกต้อง แอปก็ทำงานบน runtime นั้นได้ แน่นอนว่าบางกรณีก็จะใช้ของอย่าง Docker ยากไม่ได้แปลว่าไม่มีคุณค่า
    • ถ้านั่นคือประเด็นพื้นฐาน ผมไม่เห็นด้วย ผมมองว่าคนส่วนใหญ่ของโลกเห็นด้วยกับผมและ หลักการ 12-Factor มากกว่า
    • ผมเห็นด้วยกับมุมมองต่อกฎข้อแรก และยินดีจะใส่หลายแอปที่เกี่ยวข้องกันไว้ใน repository เดียวกัน เพราะไม่ได้มีการให้เหตุผลว่าทำไม repository หนึ่งถึงควรมีได้แค่แอปเดียว ดังนั้นกฎข้อนั้นละไว้ได้
      ภาษาแบบ dynamic หรือ managed มักละความยุ่งยากเกี่ยวกับ system package ไปได้เป็นส่วนใหญ่
    • ประโยชน์ของ monorepo เป็นรูปตัวยู โปรเจกต์ส่วนใหญ่อยู่ตรงกลางที่ไหนสักแห่ง โดยเฉพาะโปรเจกต์ที่ดูแลโดยทีมวิศวกรรมอิสระซึ่งไม่มีทีมแพลตฟอร์ม/DevOps แบบรวมศูนย์ หรือทุกวันนี้จะเรียกอะไรก็ตาม
  • โดยรวมแล้วผมชอบนะ แต่มีหลายครั้งเกินไปที่คนที่ไม่ใช่สายเทคนิคหรือเป็นสายเทคนิคครึ่ง ๆ กลาง ๆ หยิบ “12 factor” ขึ้นมาเหมือนเป็นใบเหลืองสารพัดประโยชน์เพื่อทำให้การปล่อยรีลีสช้าลง จนแทบจะเลิกสนใจมันไปโดยสิ้นเชิง
    จริง ๆ แล้ว “agile” ก็คล้ายกัน ผมเข้าใจเจตนาของแนวทางพวกนี้ แต่คุณค่าจริง ๆ ของมันดูจะเป็นประโยชน์กับคนที่ให้ได้แค่ ภาวะผู้นำทางเทคนิคแบบหอคอยงาช้าง มากกว่ามาก

    • โดยหลักการแล้ว การเมินมันโดยสิ้นเชิงก็แย่พอ ๆ กับคนที่ปฏิบัติต่อมันเหมือนหลักคำสอนบังคับ
      เคยเจอกรณีที่วิศวกรจูเนียร์ที่ไฟแรงเกินไป หรือคนที่อยากเป็นสถาปนิก ใช้บทความ 12-Factor เหมือนเป็นข้อกำหนดบังคับสำหรับทุกการปล่อยผลิตภัณฑ์
      สิ่งเหล่านี้เป็นเป้าหมายที่ดีและควรพยายามไปให้ถึง แต่ในโลกจริงต้องอธิบายอย่างหนักแน่นและสม่ำเสมอว่า เพื่อให้ปล่อยได้ เราต้องประนีประนอม และต้องเลือกว่าจะชะลอหรือเลื่อนส่วนไหนออกไป
    • เราเรียกแบบนั้นว่า หลักคำสอนที่ถูกทำให้เป็นอาวุธ ถึงอย่างนั้นก็ยังคิดว่าดีกว่าคนที่ยึด DRY จนสร้างฟังก์ชัน helper แบบ generic ที่อ่านไม่รู้เรื่อง เพียงเพื่อจะลดโค้ดซ้ำ 3–4 บรรทัด
    • ต้องยกตัวอย่างให้เห็น เหมือนเรื่องส่วนใหญ่ มันขึ้นกับบริบทและมีพื้นที่สีเทา แต่ผมคิดว่านักพัฒนาส่วนใหญ่ใช้ 12-Factor เหมือนเป็น ดาวเหนือ
      คงไม่ถึงกับบล็อกรีลีสเพราะการเบี่ยงเบนเล็กน้อย แต่ถ้าโดยรวมไม่สอดคล้องกัน อย่างน้อยก็ควรถูกจัดเป็นหนี้ทางเทคนิค หากรีลีสใดถอยหลังอย่างมากในข้อใดข้อหนึ่ง การบล็อกไว้ หรืออย่างน้อยบังคับให้มีการทบทวนละเอียดขึ้นว่าทำไมจึงเห็นว่าการแลกเปลี่ยนนั้นคุ้มค่า ก็ถือว่ายุติธรรม
    • หลักคำสอนใด ๆ ไม่ควรขัดขวาง การ deploy ในขอบเขต MVP หลังจากพ้น MVP ไปแล้ว ทุกอย่างที่ยังไม่ได้จัดการตามมุมมองแนวปฏิบัติที่ดีจะกลายเป็นหนี้ทางเทคนิค
      ถ้าองค์กรนำ 12FA มาใช้เป็นแนวปฏิบัติที่ดี ก็ต้องทำให้ได้ตามนั้น แต่ไม่ควรนำมาใช้บล็อกการ deploy
      12FA ไม่ใช่ช่องเดียวที่ต้องติ๊กให้ครบ สามารถ และส่วนใหญ่ก็ควร จะนำแต่ละข้อมาใช้กับผลิตภัณฑ์ทีละข้อ และถ้าจำเป็นก็แตกย่อยเพิ่ม เมื่อผลิตภัณฑ์เติบโตเต็มที่ขึ้น
      ถ้าทำวิศวกรรมที่ดีไว้ตั้งแต่ต้น กล่าวคือมี abstraction และ interface ที่เหมาะสม และไม่ได้ hardcode ทุกอย่างไว้ ก็ไม่ควรเป็นปัญหา
      YAGNI ก็ถูกใช้ผิด ๆ มากพอ ๆ กับ 12FA สิ่งที่ 12FA ขาดไปมากที่สุดคือ ตัวอย่างที่เป็นรูปธรรม ให้จูเนียร์วิศวกรใช้อ้างอิง
    • เคยเจอปัญหาแบบนั้นมาก่อน และวิธีแก้คือมี กระบวนการที่จัดทำเอกสารไว้อย่างชัดเจน แล้วชี้ไปที่กระบวนการนั้น
      ผมเรียนรู้ที่จะตีความปัญหาแบบนี้ด้วยความเชื่อในเจตนาดี ต้องดูว่าทำไมเขาถึงหยิบประเด็นขึ้นมา กระบวนการเดิมไม่ชัดเจนหรือไม่ มีข้อบกพร่องหรือไม่ หรือขาดความน่าเชื่อถือหรือไม่
      ถ้าพบข้อกังวลที่สมเหตุสมผล ก็ปรับกระบวนการและอัปเดตเอกสารก็พอ
  • Twelve-Factor App บอกให้ใช้ environment สำหรับ config ส่วน Docker บอกว่าอย่าใช้ environment สำหรับ config เพราะไม่ปลอดภัย
    ผมชอบและใช้แพตเทิร์นหลายอย่างของ 12-Factor แต่บางข้อถูกเขียนขึ้นในบริบทของ VPS ตอนนั้น environment มีเสถียรภาพ ปลอดภัย และคงที่มากกว่า แต่ใน container นั้น environment อาจถูกพ่วงติดไปกับเลเยอร์ใดเลเยอร์หนึ่งได้
    ในยุค container ข้อนี้โดยเฉพาะเป็นเรื่องชวนปวดหัวพอสมควร Docker secrets ก็ไม่ได้เข้ากันได้ดีเสมอไป ต้องทำท่ากายกรรมหลายอย่างถึงจะทำให้ทำงานได้

    • ช่วยอธิบายละเอียดขึ้นได้ไหม? สงสัยว่า environment variable ไม่ปลอดภัยอย่างไร ถ้าค่าลับต้องมีอยู่ตลอดอายุการทำงานของแอป ผมก็ยังไม่รู้ว่าทางเลือกที่ดีกว่าคืออะไร
      การฉีดค่าลับเข้าแอปผ่าน environment variable ไม่ได้หมายความว่าการจัดการภายนอกทั้งหมดจะไม่ปลอดภัย ตัวอย่างเช่น container ของ AWS ECS มีการรองรับในตัวสำหรับดึงค่าลับจาก Secret Manager ตอนเริ่มต้น แล้วส่งต่อเป็น environment variable: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/...
      ค่าลับจะถูกดึงมาตอน container เริ่มต้น และดึงจาก Secret Manager ด้วย IAM credential ของแอปพลิเคชันที่กำลังรันอยู่ ดังนั้นจึงต้องมีสิทธิ์ต่อค่าลับนั้น
      ข้อดีของการใช้ environment variable ดูเหมือนจะขึ้นอยู่ทั้งหมดว่าสุดท้ายแล้วมันถูกตั้งค่าอย่างไร ถ้าเป็นกลไกแบบนี้ ก็ไม่ค่อยเห็นข้อเสียใหญ่ ๆ
      ข้อเสียหลักที่เห็นคือมัลแวร์ทั่วไปที่พยายาม dump environment variable อาจจับค่านั้นได้ แต่ถ้าไม่ได้ถึงขั้นไม่เก็บค่าลับไว้ในหน่วยความจำแบบถาวร การป้องกันภัยคุกคามแบบนั้นโดยมากก็ใกล้เคียงกับการทำให้สับสนมากกว่า
    • ในทางกลับกัน ถ้าไม่มีการรองรับจากแพลตฟอร์ม ก็ไม่มีทางเลือกง่าย ๆ มากนัก อย่างน้อยมันก็ส่งเสริม การแยก config ออกจาก code และช่วยให้เก็บค่าลับไว้นอกระบบ version control
    • ด้วยเหตุผลนี้ ผมไม่เคยชอบวิธีใส่ค่าลับไว้ใน environment variable เลย debug console ที่หละหลวมเผลอเปิดเผย environment variable บ่อยจนน่าตกใจ
      การที่ private key ถูกเปิดเผยแบบนั้นเป็นสิ่งที่ควรหลีกเลี่ยงจริง ๆ
      วิธีที่ k8s mount ค่าลับเข้าใน file system นั้นไม่ได้มีปัญหาอะไรเป็นพิเศษ แน่นอนว่าทั้งหมดนี้ขึ้นอยู่กับสภาพแวดล้อมการ deploy
      บางครั้ง environment variable ก็เป็นตัวเลือกที่แย่น้อยกว่า เพราะค่าลับเป็นเรื่องยากเสมออยู่แล้ว
    • อาจโต้แย้งได้ว่าไม่ควรมอง input ใด ๆ ว่า “ปลอดภัยที่จะบริโภค” ถ้าเขียนโค้ดแอปแบบ defensive ก็ไม่ควรสันนิษฐานเด็ดขาดว่า ENV ปลอดภัยหรืออยู่ในรูปแบบที่คาดไว้ และควรทำความสะอาดก่อนใช้
      ถ้าเชื่อ input อย่างมืดบอดเพียงเพราะเป็น single-tenant วันหนึ่งเมื่อเงื่อนไขเปลี่ยนเพราะการ pivot ทางธุรกิจตามอำเภอใจ ก็จะเจอบั๊กที่ตามยากและค่ำคืนอันยาวนาน
    • มองย้อนกลับไป ชื่อ section นั้นควรเป็น แยก config ออกจาก code ตามที่เขียนไว้ในย่อหน้าแรกมากกว่า
  • ในช่วงไม่กี่ปีที่ผ่านมา มีการพูดคุยเรื่องแอป 12-Factor กันมาก และก็เห็นความสับสนอยู่ไม่น้อย เว็บไซต์ 12-Factor นั้นยอดเยี่ยม แต่โดยหลักแล้วเหมาะกับคนที่เข้าใจอยู่แล้วว่าทำไมแต่ละข้อถึงสำคัญ
    สำหรับคนที่ไม่รู้เหตุผลเบื้องหลังกฎเหล่านั้น จำเป็นต้องมีคำอธิบายที่ลึกกว่านี้ ดังนั้นจึงทำวิดีโอ “What are 12 Factor Apps and Why Should You Care?”[1] ขึ้นมา และได้ยินมาว่าบางบริษัทนำไปใช้ในการอบรมวิศวกร/DevOps ที่เพิ่งเข้ามาใหม่แล้วช่วยได้มาก
    ไม่ว่าจะเรียนจากที่ไหน แอป 12-Factor ก็คุ้มค่าที่จะใช้เวลาสักหนึ่งหรือสองชั่วโมงศึกษา “กฎ” ส่วนใหญ่เป็นเรื่องที่ต้องอาศัยการตระหนักรู้ และไม่ได้ชัดเจนในทันทีจนกว่าจะได้ทำพลาดเองและเจ็บเอง
    [1] https://youtu.be/REbM4BDeua0

    • คล้ายกับ unit test แนวคิดเรื่องการเขียนโค้ดเพื่อทดสอบโค้ดนั้นชัดเจนมากสำหรับผมตั้งแต่เริ่มเขียนโปรแกรม จนแปลกใจว่าทำไมคนอื่นถึงใช้เวลานานขนาดนั้นกว่าจะยอมรับมัน
      แนวคิด “unit test” เริ่มได้รับ traction ราวปลายยุค 90 และรู้สึกโล่งใจที่มีคนมีอิทธิพลออกมาสนับสนุน
      เหตุผลที่ผมไม่ได้ทำเองจนกระทั่ง Kent Beck เปิดตัว JUnit คือโค้ดที่ทำอยู่ไม่ได้มีโครงสร้างที่เหมาะให้โค้ดอื่นมาควบคุมได้ ตัวแปร global, state ที่กระจายไปทั่ว, การพึ่งพาระบบภายนอกและ layout ของ file system เฉพาะ, รวมถึงการไม่สนใจ modularity ทำให้ไม่สามารถรันอะไรนอกบริบทที่มันถูกออกแบบไว้ได้เลย
      ทั้งหมดนี้คือ “การออกแบบที่แย่” แต่ก็ส่งงานทันกำหนด และทุกคนจึงทำแบบนั้นกัน หวังว่าเมื่อ unit test ได้ traction โปรแกรมเมอร์จะหลุดพ้นจากการออกแบบแบบก้อนเดียว
      หลังจาก 25 ปีที่ได้เห็น unit test สำหรับ getter/setter และ unit test ขนาดมหึมาเพียงตัวเดียวที่สร้างฐานข้อมูล in-memory เพราะทุกฟังก์ชันในแอปต้องใช้ฐานข้อมูลจริงแค่เพื่อจะพยายามรัน แล้วสุดท้ายก็ล้มเหลวและถูกคอมเมนต์ทิ้ง ผมก็หมดศรัทธาว่า unit test จะเป็นอะไรมากกว่าช่อง checkbox ที่ไร้ความหมาย ทุกคนกรอกมันเพราะเป็น “best practice” แต่ไม่หยุดคิดว่าทำไปทำไม
  • ผมไม่เคยเห็นด้วยกับคำแนะนำเรื่องการตั้งค่ามากที่สุดเสมอมา configuration มักถูกกำหนดโดยหลายฝ่าย และบ่อยครั้งนักพัฒนาก็เป็นคนกำหนด ดังนั้นการแพ็กค่า default ที่สมเหตุสมผลมากับแอปพลิเคชัน แล้วให้ไฟล์ตาม environment และ environment variable override เอา มักเป็นวิธีที่ดีที่สุด
    สำหรับแอปพลิเคชันฝั่งเซิร์ฟเวอร์ส่วนใหญ่ วิธีนี้ยืดหยุ่นที่สุด ในหลายกรณีเรารู้อยู่แล้วว่าควรมี configuration แบบไหน และควรอยู่ใน source control แต่ค่า secret ควรถูก inject ตอนรัน
    ถ้าไม่อยากเสียเวลาตั้งค่าอย่างมหาศาลใน dev, test และ production configuration จำเป็นต้องมี hierarchical override

    • ปัญหาคือ “ค่า default ที่สมเหตุสมผล” นั้นเป็นค่า default สำหรับ development หรือ production
      ถ้าบอกว่าเป็นของ development สุดท้ายก็จะทำ production พัง และค่า default ของ production ก็อาจไม่มีความหมายเลยใน development
    • บริบทที่ 12-Factor App ถูกเขียนขึ้นมานั้น มีเรื่องที่สามารถ toggle โหมดด้วย environment variable อย่าง RAILS_ENV=test ได้
      วิธีหนึ่งในการคิดถึงหมวด configuration คือ “กลยุทธ์การตั้งค่านี้เข้ากับ container ได้ดีไหม?” เมื่อ build image แล้ว จะมีสถานะดิสก์แบบ static ที่การเปลี่ยนแปลงจะไม่คงอยู่ เว้นแต่จะสร้าง image ใหม่
      ถ้า configuration เป็นแบบอิงไฟล์เท่านั้น ก็ต้อง build image ใหม่ทั้งหมดเพื่อสลับพฤติกรรมระหว่าง test กับ production
      การทำให้เปลี่ยน configuration ได้โดยไม่ขึ้นกับดิสก์พื้นฐานช่วยแยกการเปลี่ยนแปลงออกจากกัน ต้องแยกให้ได้ว่าเหตุที่แอปพังคือ deployment หรือการสร้าง image พัง หรือเป็นเพราะ configuration ผิด
      ถ้าแยกการสร้าง image กับการเปลี่ยน configuration ออกจากกัน คำถามนั้นก็หายไปเอง
    • ถ้าระบบป้องกันไม่ให้ instance ของ QA ไปเชื่อมกับ resource ของ production ได้โดยโครงสร้าง การ deploy configuration ส่วนใหญ่ไปพร้อมกับแอปก็อาจโอเค
      เช่น ไม่ควรปล่อยให้เกิดความผิดพลาดของมนุษย์ที่คาดเดาได้ง่ายอย่างการให้ QA ยิง retry การปฏิเสธการยืนยันตัวตน 100,000 ครั้งไปยังคิวส่งงานของ production
      อย่างไรก็ตาม configuration จำนวนมากไม่ได้เกี่ยวกับ infrastructure แต่เป็นเรื่องอย่างเช่นจะ wire bean ของ ResolverStrategy ตัวไหนในแต่ละ environment
    • สามารถมีค่า default ที่สมเหตุสมผลได้ และก็น่าจะควรมี
      configuration ควรถูกจัดการด้วย version control แต่ควรแยกจาก source code เพราะ configuration อธิบาย ตัว deployment เอง ไม่ใช่ image ที่ใช้ deploy
    • ผมไม่เห็นว่ามีเหตุผลอะไรที่ค่า default ก็ไม่ควรถูกแสดงเป็น configuration ถ้านั่นคือความหมาย ก็คิดว่าไม่ขัดกับ 12-Factor
  • มันเป็น บรรทัดฐานทางวิศวกรรม ที่มีอิทธิพลอย่างชัดเจน ตอนนี้มี abstraction ที่ใช้ง่ายจาก hosting อย่าง Render หรือ Vercel อยู่มากมาย แต่เมื่อคิดว่าเอกสารนี้เขียนในปี 2012 และตอนนั้นเว็บแอปยังเหมือนยุคบุกเบิกตะวันตกมากกว่านี้มากในแง่แนวปฏิบัติร่วมที่เป็นที่ยอมรับ ก็ให้ความรู้สึกแปลกอยู่เหมือนกัน

  • สิ่งที่ขาดไปมากในเอกสารนี้คือ การให้เหตุผลรองรับ กฎเกือบทั้งหมดเป็นแค่กฎ
    ยากที่จะตัดสินว่ากฎนั้นดีหรือไม่ และเอกสารนี้ก็ไม่ได้ช่วยให้เข้าใจเรื่องนั้น

    • ถ้าคลิกที่กฎ จะพาไปยังหน้าที่มีเนื้อหาตามที่ต้องการ เช่น https://12factor.net/backing-services
  • เห็นแค่ชื่อเรื่องแล้วนึกว่าเป็นบทวิจารณ์เกี่ยวกับการยืนยันตัวตนสองขั้นตอน หมายถึงแอปประเภทที่ต้องขอรูปพาสปอร์ต, สแกนใบหน้า, ใบขับขี่, SMS, Google Authenticator, ลิงก์อีเมล, รหัสผ่าน และลายนิ้วมือทั้งหมดเพื่อจะล็อกอินสักครั้ง

    • น่าจะเหมือน เว็บเทรดคริปโตแบบรวมศูนย์ โดยเฉลี่ยนั่นแหละ
    • โดยคร่าว ๆ ก็คล้ายกับสิ่งที่เว็บเทรดหุ้นหรือคริปโตโดยเฉลี่ยเรียกร้องก่อนจะอนุญาตให้เทรด
  • ในยุคแรกของ Docker ผมทำงานค่อนข้างเยอะเพื่อให้ WordPress ทำตัวเหมือน Twelve-Factor App
    ตามธรรมเนียมแล้ว WordPress ไม่ได้ทำงานแบบนั้น และในระดับหนึ่งก็เป็นเรื่องสมเหตุสมผล เพราะ WordPress เติบโตมาในโลกที่เซิร์ฟเวอร์อายุยาวซึ่งมีดิสก์ local ที่เขียนได้และคงอยู่ถาวรเป็นเรื่องปกติ
    หลังจากนั้นสถานการณ์คงเปลี่ยนไปมากแล้ว นี่เป็นเรื่องราวราวปี 2016 แต่เป็นความท้าทายที่สนุกจริง ๆ

    • จำได้ตอนที่ได้เรียนรู้วิธีทำให้เซิร์ฟเวอร์เป็น stateless เช่น เก็บข้อมูล session ไว้ในฐานข้อมูลและไม่เขียนลงดิสก์
      แปลกใจมากว่าสิ่งต่าง ๆ ง่ายขึ้นแค่ไหน และทำให้ load balance หลาย node ได้โดยไม่ต้องกังวลเรื่อง session stickiness
      แน่นอนว่ามันก็ยากขึ้นในอีกแบบหนึ่ง เช่น ต้องมี DB แยกสำหรับ session