7 คะแนน โดย GN⁺ 2023-07-17 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • บริการส่วนตัวรันเองบน DigitalOcean VM เดือนละ $5 กับ Debian 10 และสำหรับบริการขนาดเล็ก วิธีติดตั้งซอฟต์แวร์เซิร์ฟเวอร์ลงบน VM ก็ยังเรียบง่ายและสมเหตุสมผลเพียงพอ
  • แอปเซิร์ฟเวอร์สร้างเป็น ไบนารีแบบ static ของ Rust และรวม HTML, CSS, การตั้งค่า ไปจนถึงค่าลับไว้ด้วย ทำให้ artifact สำหรับ deploy คงอยู่เป็นไฟล์เดียว
  • การจัดการการรันใช้ systemd ส่วน HTTPS ให้ nginx reverse proxy กับ Let’s Encrypt/certbot จัดการ เพื่อไม่ให้แอปต้องแตะ TLS โดยตรง
  • บริการที่ต้องใช้ข้อมูลใช้ SQLite ไฟล์เดียว และใช้ทั้งการสำรองข้อมูลรายวันด้วย Tarsnap กับการสำรองแบบสตรีมไปยัง DigitalOcean Spaces ของ Litestream
  • เมื่อต้องวางหลายบริการบน VM เดียวกัน จะแยกด้วย Unix user แยกตามบริการ และถ้าต้องการความปลอดภัยที่แข็งแรงขึ้นก็เลือกใช้ VM แยก, systemd-nspawn หรือ firejail

โครงสร้างพื้นฐานของเซิร์ฟเวอร์และการ deploy

  • บริการหลายตัว เช่น thoughts.page, hanabi.site, cgmserver, phonebridge ถูกดูแลด้วยโครงสร้างที่ใกล้เคียงกับแบบนี้
  • แอปรันบน DigitalOcean VM โดยใช้แพ็กเกจระดับ $5/เดือน และระบบปฏิบัติการเป็น Debian 10
    • บางบริการแชร์ VM เดียวกัน และบางบริการถูกแยกไปอยู่ VM อื่น
  • ซอฟต์แวร์เซิร์ฟเวอร์เขียนด้วย Rust
    • ลิงก์แบบ static
    • HTML, CSS, การตั้งค่า, ค่าลับ ฯลฯ ถูกคอมไพล์เข้าไปในไบนารี
    • ใช้ rust-musl-builder และ rust-embed
  • วิธีนี้ทำให้การ deploy ง่ายขึ้นเหลือแค่การคัดลอก ไฟล์เดียว ไปยังเซิร์ฟเวอร์
    • มองว่าสามารถใช้วิธีคล้ายกันได้กับ Go, C++ เป็นต้น
    • สำหรับภาษาที่ deploy เป็นไบนารีไฟล์เดียวได้ไม่ง่าย ทางเลือกคือใช้ Docker container เป็น artifact จากการ build

การรัน พร็อกซี และการเก็บรักษาข้อมูล

  • การจัดการการเริ่มบริการให้ systemd รับผิดชอบ
    • ทำให้ไบนารีถูกรันพร้อมกันเมื่อเซิร์ฟเวอร์เริ่มทำงาน
    • ไฟล์ systemd unit ส่วนใหญ่เป็น ไฟล์เรียบง่าย 9 บรรทัด
    • ตัว systemd เองซับซ้อน แต่ในการใช้งานเพื่อเริ่มเซิร์ฟเวอร์ตอนบูต แทบไม่ต้องเจอความซับซ้อนนั้นส่วนใหญ่
  • การ deploy จัดการด้วย simple deploy script
    • คัดลอกไบนารีไปยังเซิร์ฟเวอร์และรีสตาร์ตเซิร์ฟเวอร์
    • สามารถ rollback ได้ และทำให้มีเวอร์ชันที่ใช้งานได้รันอยู่เสมอ แม้การเชื่อมต่อจะหลุดระหว่าง deploy
  • โปรแกรมที่ต้องใช้ฐานข้อมูลใช้ SQLite
    • สถานะทั้งหมดของแอปอยู่ใน ไฟล์เดียว
    • สร้าง backup ทุกวันด้วย คำสั่ง SQLite .backup และเก็บไว้ใน Tarsnap
    • สคริปต์ backup รันด้วย cron
    • ใช้ Litestream สตรีมสำเนาฐานข้อมูลไปยังสตอเรจ DigitalOcean Spaces ในระดับวินาที และสร้าง snapshot ทุก 6 ชั่วโมง
  • เซิร์ฟเวอร์ทั้งหมดรันอยู่หลัง nginx reverse proxy
    • nginx จัดการ TLS termination ดังนั้นแอปไม่ต้องสนใจ HTTPS
    • ใบรับรอง HTTPS ออกโดย Let’s Encrypt และ certbot พร้อมต่ออายุอัตโนมัติ
    • ตัวอย่างการตั้งค่า nginx ของ hanabi.site อยู่ใน gist แยกต่างหาก
    • ไฟล์ static สามารถให้ nginx serve ได้ และคัดลอกไปยังเซิร์ฟเวอร์ด้วย scp หรือ rsync

การบำรุงรักษาและการแยกบริการ

  • โครงสร้างนี้มุ่งไปที่เส้นทางการดำเนินงานที่เรียบง่ายและ ทนทาน
    • ซอฟต์แวร์ในเส้นทางการให้บริการ นอกเหนือจากตัวแอปเอง เป็นองค์ประกอบที่พิสูจน์แล้วจากการใช้งานมาหลายสิบปี
    • ตราบใดที่ยังจ่ายค่า DigitalOcean ต่อไป การดูแลไซต์แทบไม่มีงานบำรุงรักษาโดยพื้นฐาน
    • ปัญหาที่ monitoring ตรวจพบมีเพียงปัญหาเครือข่าย DigitalOcean ชั่วคราวเท่านั้น
  • ระบบปฏิบัติการและอัปเดตความปลอดภัยยังจำเป็นเป็นครั้งคราว
    • Debian release ให้การสนับสนุน 5 ปี ดังนั้นจะต้องอัปเกรดเป็น Debian 11 ในอีกราว 2 ปีครึ่ง
    • หากเกิดเหตุการณ์แบบ Heartbleed อีก ก็ต้อง patch
    • เหตุการณ์แบบนี้ค่อนข้างพบได้ไม่บ่อย
  • ค่าใช้จ่าย VM เดือนละ $5 ต่อบริการอาจเป็นภาระได้ แต่สามารถรันหลายบริการบน VM เดียวกันได้
    • การแยกกันทำโดยสร้าง บัญชี Unix user แยกต่างหาก ให้แต่ละบริการ
    • วิธีแยกแบบนี้มีมาตั้งแต่ยุคแรกของ Unix จึงดูแข็งแรง
    • ถ้าต้องการการแยกที่แข็งแรงขึ้น สามารถใช้ systemd-nspawn หรือ firejail ได้
    • ถ้าสำคัญด้านความปลอดภัยจริง ๆ ก็จ่ายเพิ่มอีก $5/เดือน แล้วรันบน VM แยก
  • ขั้นตอนตั้งค่าโปรเจกต์ใหม่สั้นมาก
    • สร้าง user ใหม่
    • เพิ่ม nginx virtual host และออกใบรับรอง HTTPS ด้วย certbot
    • เพิ่ม systemd unit
    • commit deploy script ลงใน repository แล้วรัน
  • ตอนแรกมีหลายอย่างที่ต้องเรียนรู้ แต่ infrastructure แบบนี้ เปลี่ยนช้ากว่า cloud infrastructure มาก
    • รูปแบบการตั้งค่า nginx แทบไม่เปลี่ยนในสาระสำคัญตลอด 10 ปีที่ผ่านมา
    • การเปลี่ยนแปลงใหญ่ครั้งล่าสุดของการดูแลระบบ Debian คือการย้ายไปใช้ systemd เมื่อเกือบ 10 ปีก่อน
    • เผชิญกับสถานการณ์ที่ผู้ให้บริการ cloud ยกเลิกบริการหรือแอบเปลี่ยนพฤติกรรมน้อยกว่า
    • dependency มีเพียงผู้ให้บริการ VPS และเซิร์ฟเวอร์เป็นสินค้าทั่วไป จึงสามารถย้ายไปผู้ให้บริการรายอื่นได้

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

 
GN⁺ 2023-07-17
ความคิดเห็นจาก Hacker News
  • หากรันแต่ละบริการด้วยผู้ใช้ Unix แยกกันเพื่อการแยกส่วน ฟีเจอร์ DynamicUser ของ systemd สามารถช่วยประหยัดเวลาได้
    มันจะจัดสรร UID และสร้างไดเรกทอรีสำหรับล็อก/สถานะด้วยสิทธิ์ที่ถูกต้องให้
    https://0pointer.net/blog/dynamic-users-with-systemd.html

    • หากต้องการให้ UID คงอยู่ถาวร การสร้างผู้ใช้ระบบใหม่ด้วย systemd-sysusers ก็ทำได้ง่ายมากเช่นกัน
      เพียงใส่ไฟล์ข้อความขนาดเล็กที่มีข้อมูลอย่างชื่อผู้ใช้และโฮมไดเรกทอรีไว้ใน /etc/sysusers.d/ แล้วรันคำสั่งหรือบริการ sysusers
    • การใช้ บัญชีบริการ แยกสำหรับแต่ละบริการ/แอปถือเป็นแนวทางที่ค่อนข้างมาตรฐาน และการแยกเซิร์ฟเวอร์ก็ควรทำเช่นกัน
  • ช่วงนี้ชอบ HTTP-triggered cloud functions มากที่สุด
    ถ้าระวังหลีกเลี่ยงกับดักเฉพาะของแต่ละผู้ให้บริการได้ มันช่วยลดความซับซ้อนได้มาก และเป็น abstraction แบบ cloud-native อย่างเดียวที่ให้ความรู้สึกเหมือนเป็น “รูปแบบสุดท้าย”
    ในแอปเดียวมีการ deploy ไปแล้วมากกว่า 2000 ครั้ง และไม่เคยมีสักครั้งที่ environment พังจนต้องติดต่อซัพพอร์ตหรือพิมพ์คำสั่งคอนโซลประหลาด ๆ
    ประสิทธิภาพบน Azure แบบ isolated App Service plan ก็ยอดเยี่ยม และความรู้สึกต่อต้านเชิงอุดมการณ์ต่อการคิดเงินตามจำนวนคำขอก็หายไปแล้ว
    ถ้าถึงขั้นเป็นเจ้าของอสังหาริมทรัพย์ที่มีเซิร์ฟเวอร์ของตัวเองอยู่จริง ๆ ก็อาจทำให้ถูกกว่าได้ แต่ถ้าจะสร้างคุณสมบัติอย่าง compliance และ audit ที่ HTTP function แบบง่าย ๆ ให้มาเองด้วยมือตัวเอง ในทางปฏิบัติก็คงต้องตั้งบริษัทระดับยักษ์และจ้างคนจำนวนมาก
    เหตุผลเรื่อง vendor lock-in ก็ไม่ได้รู้สึกหนักแน่นอีกต่อไป อินเทอร์เฟซที่รับ HTTP request แล้วส่ง HTTP response กลับนั้นเป็นธรรมชาติมาก และความต่างของแต่ละผู้ให้บริการก็มีแค่บริบทเสริมอย่าง method signature ของ trigger หรือ claims ของ OIDC/SAML ดังนั้นถ้าจะทำให้มัน refactor ไปผู้ให้บริการอื่นภายในหนึ่งสัปดาห์ไม่ได้ ก็คงต้องพยายามทำให้ผูกติดเองมากกว่า
    ถ้าเป็นบล็อกส่วนตัวก็เข้าใจได้ว่าการซื้อ VM จาก Hetzner มาขัดเกลาแบบงานคราฟต์จะสนุกกว่า ถ้าอยู่ในอุตสาหกรรมที่ไม่มีข้อกำกับเลย ก็พอจะโต้แย้งได้หนักแน่นกว่าว่าควรคิดเรื่องมาร์จินและความซับซ้อนอย่างละเอียดแทนการเอาเซิร์ฟเวอร์ไป outsource ทั้งหมด

    • สะดุดกับประโยคที่ว่า “ถ้าจะทำเองต้องสร้างบริษัทมูลค่า 1 พันล้านดอลลาร์และจ้างเพิ่มอีก 1,000 คน” มาก
      ไม่แน่ใจว่าคิดว่าก่อน การย้ายขึ้นคลาวด์ คนเขาทำกันอย่างไร
      ตอนนี้ผมดูแลหลายทีมในบริษัทอุตสาหกรรมขนาดใหญ่และกำลังขยาย/ปล่อยแอปนวัตกรรมบน Azure ซึ่งค่าใช้จ่ายคลาวด์มหาศาลเกินเหตุเมื่อเทียบกับจำนวนผู้ใช้
      เมื่อก่อนเราเคยรันแอปที่มีผู้ใช้มากกว่านี้ 10 เท่า ด้วยค่าใช้จ่ายเพียง 1/100 และยังซับซ้อนน้อยกว่าอีก
      ค่าใช้จ่ายนี้ถูกโยนไปให้แผนกอื่นที่เลือกแนวทางน่าสงสัยแบบนี้อยู่แล้ว ดังนั้นส่วนตัวผมไม่ได้เดือดร้อน แต่ก็เข้าใจยากว่าทำไมถึงเชื่อคลาวด์แบบไร้ข้อกังขา
    • ผมทำงานในอุตสาหกรรมที่ถูกกำกับอย่างเข้มงวดมาก แต่ไม่เข้าใจประโยคสุดท้าย
      สำหรับเรา การดูแลฮาร์ดแวร์เอง คือทางเลือกที่ดีที่สุด และเราอยู่ในระดับที่แม้แต่ถ่ายรูปในออฟฟิศก็ไม่ได้ ดังนั้นโอกาสที่จะฝากอะไรไว้กับผู้ให้บริการคลาวด์จึงเป็น 0%
    • ฟังก์ชันเหมาะพอตัวสำหรับการโฮสต์ API ที่เรียบง่าย
      แต่ถ้าแอปจะมีประโยชน์จริงก็ยังต้องมีองค์ประกอบอื่นอย่างฐานข้อมูลด้วย และตรงนั้นค่าใช้จ่ายก็ไม่ได้เพิ่มขึ้นหรือ?
    • ไม่แน่ใจว่าสิ่งที่เรียกว่า HTTP-triggered cloud functions ตรงนี้หมายถึงอะไรกันแน่
      สับสนว่าหมายถึง request handler ของ HTTP server, cloud computing แบบ function-as-a-service หรือแค่ RPC แบบเก่า
    • สิ่งสำคัญคือฟังก์ชันนั้นทำอะไรจริง ๆ
      ถ้ามี script kiddie คนไหนเริ่มยิง request 10 ครั้งต่อนาที ต้องใช้เวลานานแค่ไหนกว่าค่าใช้จ่ายของฟังก์ชันจะแพงกว่า VPS?
  • ผมใช้สแตกคล้ายกันกับทั้งเว็บไซต์ส่วนตัวและเว็บไซต์โปรเจกต์
    ใช้ Linode VM ราคา 5 ดอลลาร์ต่อเดือน, ใช้ Debian GNU/Linux และเขียนซอฟต์แวร์ด้วย Common Lisp
    เว็บไซต์ส่วนตัวหรือบล็อกจะสร้างเป็น static site ด้วยโปรแกรม Common Lisp ส่วนบริการออนไลน์หรือเว็บแอปก็ทำเป็นโปรแกรม Common Lisp ที่รับและตอบ HTTP request ด้วย Hunchentoot
    ใช้ไฟล์ยูนิต systemd เพื่อให้เว็บไซต์/บริการเริ่มทำงานอัตโนมัติเมื่อ VM บูตหรือรีสตาร์ต โดยส่วนใหญ่ยาวแค่ราว 10~15 บรรทัด
    การตั้งค่าเริ่มต้นของ VM ถูกทำเป็นโค้ดไว้ในเชลล์สคริปต์: https://github.com/susam/dotfiles/blob/main/linode.sh
    การตั้งค่าแยกตามโปรเจกต์และบริการจัดการด้วย Makefile ของแต่ละตัว: https://github.com/susam/susam.net/blob/main/Makefile, https://github.com/susam/mathb/blob/main/Makefile
    ไม่ใช้คอนเทนเนอร์ เว็บไซต์เหล่านี้รันมากันหลายปีตั้งแต่ก่อนคอนเทนเนอร์จะฮิต และจนถึงตอนนี้แค่มีสคริปต์เริ่มต้นกับ Makefile ก็เพียงพอแล้ว
    ใช้ Nginx ด้วยเช่นกัน มันใช้เสิร์ฟไฟล์แบบ static และเมื่อมี backend service ก็ทำหน้าที่เป็น reverse proxy นอกจาก terminate TLS แล้ว ยังมีข้อดีอย่างการจำกัดอัตราคำขอและการตั้ง allowlist ของ HTTP header เพื่อปกป้อง backend
    ในเพลย์บุ๊กส่วนตัวขนาดเล็กมีคำสั่งไม่กี่ตัวอย่าง curl LINK -o linode.sh && sh linode.sh, git clone LINK && cd PROJECT && sudo make setup https
    target ของ make จะจัดการงานที่จำเป็นต่อการเปิดเว็บไซต์ เช่น ติดตั้งเครื่องมืออย่าง Nginx, certbot, sbcl, ตั้งค่า Nginx, ตั้งค่าใบรับรอง และเมื่อคำสั่งจบ เว็บไซต์ก็จะออนไลน์ทันที

    • อยากรู้ว่าใช้ Common Lisp implementation ตัวไหน
      ดูจาก Makefile เหมือนจะใช้ SBCL เลยสงสัยว่าเคยมีปัญหาเรื่องการใช้หน่วยความจำหรือไม่
      ผมใช้ VPS ที่มี RAM 512MB และ SBCL instance หนึ่งกินประมาณ 100MB เลยรันหลายบริการพร้อมกันได้ไม่กี่ตัว
      เคยคิดจะย้ายบริการที่มีทราฟฟิกต่ำที่สุดไป CLISP แต่ฟีเจอร์หนึ่งที่ใช้อยู่คือ package-local aliases ไม่มีให้ใช้
  • หลังจากขัดเกลาการตั้งค่ามาเรื่อย ๆ หลายปี ตอนนี้ลงตัวกับการรันทุกอย่างด้วย Docker containers
    ตัว orchestrator ใช้ docker-compose แทน systemd และ proxy ใช้ Caddy แทน nginx
    เหมือนกับบทความต้นฉบับ ผมเขียน deployment script แยกสำหรับแต่ละโปรเจกต์ที่ต้องรัน เลยถือว่าภาพรวมค่อนข้างคล้ายกัน
    ข้อดีอย่างหนึ่งจากหลาย ๆ ข้อของ Docker คือสามารถรันซอฟต์แวร์ third-party ด้วยการตั้งค่าแบบเดียวกันได้
    ใช้การตั้งค่านี้มาหลายปีแล้วและมันดีมาก ทั้งแข็งแรงเหมือนบทความต้นฉบับ และก็ยืดหยุ่นพอจะปรับตามต้องการเมื่อจำเป็น
    ตอนนี้จุดที่เจ็บปวดอยู่เรื่องเดียวคือ rolling deployment พอซอฟต์แวร์ใหญ่ขึ้น การที่มี downtime หลายวินาทีทุกครั้งที่ deploy ก็เริ่มเป็นปัญหา และแม้จะยังไม่มีวิธีแก้ที่ง่าย แต่ docker swarm อาจเป็นทางที่ใช่

    • ผมก็ใช้งานคล้าย ๆ กันโดยใช้ Caddy
      ถ้าอยากลด downtime ลองใช้ health_uri /health, lb_try_duration 30s ได้
      ตัวอย่างเช่นถ้าตั้งค่าเป็น reverse_proxy api:8089 { health_uri /health lb_try_duration 30s } ระหว่าง deploy เวอร์ชันใหม่ Caddy จะบัฟเฟอร์คำขอไว้และให้เวลา 30 วินาทีจนกว่าบริการใหม่จะขึ้นมา
      ตามอุดมคติแล้ว Caddy ควรเริ่มส่งทราฟฟิกไปยังเวอร์ชันใหม่หลังจากมันพร้อมใช้งาน แล้วค่อยหยุดคอนเทนเนอร์เดิม
      ผมดู https://github.com/Wowu/docker-rollout กับ https://github.com/lucaslorentz/caddy-docker-proxy ไว้แล้ว แต่ยังหาเวลาให้มันเป็นลำดับความสำคัญไม่ได้
    • ถ้ามี load balancer อย่าง Caddy อยู่หน้า pod ก็สามารถตั้งค่าให้มันค้างคำขอไว้ระหว่างที่ pod ใหม่กำลังขึ้นได้: https://twitter.com/bradleyjkemp/status/1486756361845329927
      มันไม่สมบูรณ์แบบ แต่แทนที่เบราว์เซอร์จะเจอ connection error ผู้ใช้ก็จะเห็นแค่โหลดค้างอยู่ไม่กี่วินาที
      https://mrsk.dev/ ก็ใช้เทคนิคเดียวกัน
    • ตอนสร้างซอฟต์แวร์สแตกของสตาร์ตอัปที่ผมทำงานอยู่ตั้งแต่ต้น เราแพ็กแอปพลิเคชันด้วย Docker ตั้งแต่แรกเลย
      ฝั่งโปรดักชันเริ่มจาก compose แล้วภายหลังก็พัฒนาเป็น continuous deployment pipeline ที่อัปเกรดสแตกอัตโนมัติ
      พอบริษัทและฐานผู้ใช้โตขึ้น ปัญหา downtime ตอนรีสตาร์ตหรือ deploy ก็เริ่มโผล่มา และทุกครั้งที่อยากรันแอปเพิ่มก็ต้องเตรียมสภาพแวดล้อม deploy ใหม่
      ผมกลัวว่าวันหนึ่งจะต้องใช้ Kubernetes เพราะเคยเห็นความซับซ้อนของมันมากับตา และไม่อยากใช้เวลาส่วนใหญ่ของวันไปกับการกล่อมคลัสเตอร์
      เลยเลือกใช้ Swarm mode ซึ่งเป็นการเดินทางแบบบางทีก็ Jekyll บางทีก็ Hyde
      มีทั้งบั๊กที่ไม่มีใครคิดจะแก้ ส่วนของ Docker spec ที่ไม่ได้ implement แต่ก็ไม่บอกกันตรง ๆ การตัดสินใจด้าน implementation ที่ชวนดึงผมตัวเอง และความรู้สึกเหมือนพนักงาน Docker Inc คุยกันไม่รู้เรื่องหรือโฟกัสไม่จบ
      ถึงอย่างนั้นมันก็มีด้านที่สวยงามมาก compose stack เดิมยังใช้ต่อได้และค่อย ๆ ขยายเมื่อถึงจุดจำเป็น และถ้าตั้งค่าดี ๆ ก็ทำ zero-downtime deploy, upgrade, load balancing และ rollback ได้ดี
      Raft ก็เชื่อถือได้สำหรับการดูแลคลัสเตอร์เหมือนในที่อื่น ๆ และถ้าลงแรงอีกนิด คุณก็สร้างแพลตฟอร์ม self-service ที่ยืดหยุ่น ปลอดภัย และกระจายงานอัตโนมัติได้ โดยใช้งบเพียงเศษเสี้ยวของค่าบำรุงรักษา K8s
      แต่ก็ต้องพร้อมจะทำ deployment script ให้ดี ผมใช้เวลาไปเยอะพอสมควรกับการทำเครื่องมือ Python สำหรับแปลง compose file ที่เป็น Docker spec ถูกต้องให้เป็น Swarm spec, รีเฟรชและเก็บกวาด secret, และขยาย environment variables
      และขึ้นอยู่กับผู้ให้บริการ VPS คุณต้องตั้งค่า network MTU ให้ถูกต้องด้วย เรื่องนี้น่าจะทำให้อายุผมสั้นลงไปพอสมควร
    • ถ้ามี compose file อยู่แล้ว Swarm ก็เป็นเส้นทางต่อไปได้ แต่โดยส่วนตัวผมมองว่ามันยังไม่คุ้มค่า
      อย่างน้อยก็จนกว่าจะมีลูกค้าหรือผู้ใช้มากพอจนปัญหาเรื่องการขยายระบบเกิดขึ้นจริง
    • ผมก็ทำการตั้งค่าคล้ายกัน แต่ไม่ชอบวิธีใช้ docker save กับ docker import เพื่อดันอิมเมจผ่าน SSH
      เลยสงสัยว่าคุณรัน registry เองหรือเปล่า
  • บนเซิร์ฟเวอร์จริง ผมใส่ฐานข้อมูล PostgreSQL กับแอปไว้ใน Podman Pod แล้วรันทุกอย่างบนพอร์ต localhost ที่มากกว่า 5000 โดยให้ Caddy ทำหน้าที่เป็น reverse proxy ที่พอร์ต 443
    ใช้ systemd Timer ดัมพ์ฐานข้อมูลทั้งหมดไปไว้ในไดเรกทอรีเดียวทุกวันเวลา 16:55
    จากนั้น DejaDup [1] จะสำรองข้อมูล $HOME ไปยัง HDD ภายนอกอัตโนมัติทุกวันเวลา 17:00 โดยไม่รวมไฟล์แคช แต่รวม database dump
    OS คือ Debian พร้อม GNOME Core [2] และกฎของ firewalld อนุญาตเฉพาะ 80, 443 และพอร์ต SSH แบบกำหนดเอง
    SSH ใช้ key-based และปิด password authentication
    มันเป็นวิธีที่น่าเบื่อที่สุด แต่ก็ใช้งานได้ดีเฉย ๆ
    1 - https://flathub.org/apps/org.gnome.DejaDup
    2 - https://packages.debian.org/bookworm/gnome-core

    • สงสัยว่าทำไมถึงรัน GNOME บนเซิร์ฟเวอร์
    • สงสัยว่ามีเหตุผลอะไรที่ใช้ systemd timers แทน cron jobs ไหม
  • ช่วงนี้ผมใช้ Dokku ลงบนเซิร์ฟเวอร์เครื่องเดียว
    จัดการง่าย นักพัฒนาสามารถ deploy ได้แบบ Heroku-style แค่ git push และมีปลั๊กอินเยอะมากจนเพิ่มฐานข้อมูลหรือตั้งค่า HTTPS ได้ภายในไม่เกิน 10 วินาที

    • ผมก็ใช้วิธีเดียวกัน และชอบมากที่ deploy ได้ด้วย git push
      ทั้งการเพิ่มแอป เพิ่มฐานข้อมูล จัดการ environment variables และจัดการโดเมน ล้วนตรงไปตรงมาและเข้าใจง่ายมาก
  • ชอบโครงสร้างที่เรียบง่าย เลยรู้สึกว่าวิธีแบบนี้น่าจะทำงานด้วยได้อย่างเพลิดเพลิน
    มีโอกาสสูงว่าน่าจะเพียงพอสำหรับ 99% ของบริการ
    สำหรับการตั้งค่าเซิร์ฟเวอร์และสคริปต์ deploy น่าจะใช้ Ansible
    แบบนั้นก็อาจช่วยให้ตัวเซิร์ฟเวอร์ถูกจัดทำเป็นเอกสารไปด้วย และสคริปต์ deploy ก็เรียบง่ายขึ้น
    แต่ก็คงไม่ได้หลีกเลี่ยงการเข้าไปที่เซิร์ฟเวอร์โดยตรงเพื่อดีบักหรือตรวจสอบ

    • แม้จะใช้ Ansible อยู่ แต่ก็กำลังคิดทบทวนอีกครั้งว่ามันคุ้มค่าหรือไม่
      สคริปต์ของฉันแทบไม่เปลี่ยนเลยตลอด 7 ปีที่ผ่านมา แต่ Ansible ช้าและมีแนวโน้มต้องคอยดูแลหลายไฟล์อยู่เรื่อย ๆ
      ในแง่ของการดีบัก อาจเปิดเผย app log ผ่าน endpoint ที่ป้องกันด้วยรหัสผ่าน บน nginx ก็ได้
  • บางทีก็ดูเหมือนเราจะลืมไปว่า แม้ก่อนย้ายขึ้นคลาวด์ CI/CD และการจัดการเซิร์ฟเวอร์อย่างมีประสิทธิภาพก็เป็นแนวปฏิบัติที่พบได้ทั่วไปอยู่แล้ว

  • เกี่ยวกับแนวทางการเขียนซอฟต์แวร์เซิร์ฟเวอร์ด้วย Rust แล้วทำ static link จากนั้นคอมไพล์ทุกอย่างอย่าง HTML, CSS, config, secret ฯลฯ ใส่เข้าไปในไบนารีทั้งหมด ช่วงนี้ฉันก็ทำแบบนี้ใน Go อยู่ และชอบมากเพราะมันทำให้ทั้งการเขียนและ deploy ซอฟต์แวร์ที่พึ่งพา static file ง่ายขึ้นมาก

    • การใส่ secret ไว้ในไบนารีที่คอมไพล์แล้วก็ยังน่ากังขาอยู่
      การใช้ environment variable หรือ config สำหรับ secret น่าจะใกล้เคียงกับมาตรฐานมากกว่า และแม้จะเพิ่มขั้นตอนก่อนรันมาอีกหนึ่งอย่าง แต่ก็ช่วยเลี่ยงสถานการณ์ที่แชร์ไบนารีไปแล้วลืมว่ามี secret อยู่ข้างใน
      การแตกไบนารีแล้วค้นหาสตริงทำได้ค่อนข้างง่าย
      แต่การใส่ static frontend asset และค่า config เริ่มต้นเข้าไปด้วยกันถือเป็นข้อดีมาก
    • ไม่ได้ชอบ Go เป็นพิเศษ แต่ก็ลงเอยด้วยการใช้มันอยู่เรื่อย ๆ เพราะเหตุผลนี้
      การ cross-compile ไปเป็น Rust โดยเฉพาะจาก Mac ไป Linux ค่อนข้างเจ็บปวด ในขณะที่ Go เป็นเรื่องเล็กน้อยและมีมาให้ในเครื่องมือ go อยู่แล้ว
      มันช่วยลดแรงเสียดทานเวลาจะปิดงาน side project แล้วเอาไป deploy ได้ง่ายมาก
    • https://play.clickhouse.com/play?user=play ก็ทำแบบเดียวกัน
      แต่มีคำถามอยู่อย่างหนึ่ง ในบทความบอกว่า “รับใบรับรอง Let’s Encrypt ด้วย certbot และมีการต่ออายุอัตโนมัติด้วย” แต่ฉันใช้ โครงสร้างข้ามรีเจียน ที่มีเซิร์ฟเวอร์สองเครื่องและ geo-DNS ทำให้ certbot ทำงานได้แค่บนเซิร์ฟเวอร์ในสหรัฐฯ และต้องคัดลอกใบรับรองไปยังเซิร์ฟเวอร์ยุโรปด้วยตนเอง
      มีวิธีแก้เรื่องนี้ไหม?
      คำอธิบายของ ClickHouse Playground อยู่ที่นี่: https://ghe.clickhouse.tech/
    • ฉันก็ใช้วิธีเดียวกัน
      ให้บริการเว็บแอปจากบริการที่คอมไพล์แบบ static ตัวเดียวกันกับที่ให้ backend API
      หลังจากรัน npm build ใน CI แล้วฝังผลลัพธ์เข้าไป ก็ทำให้การทดสอบบนเครื่องหรือเปิด demo instance ทำได้ง่ายมาก