สแตกสถาปัตยกรรมของเทคสตาร์ตอัปแบบคนเดียว
(anthonynsimon.com)-
คำอธิบายสถาปัตยกรรมของนักพัฒนาเดี่ยวที่ดูแล SaaS แบบค่อยเป็นค่อยไปโดยไม่เครียด
-
ได้จัดวางโครงสร้างพื้นฐานสำหรับการรันหลายโปรเจ็กต์พร้อมกัน
-
อธิบายโดยยึดจาก SaaS ที่เพิ่งสร้างล่าสุดชื่อ PanelBear
→ เริ่มจาก VPS ขนาดเล็กที่สุดพร้อม SQLite + Django
→ หลังทำซ้ำและปรับปรุงตลอด 6 เดือน กลายเป็น Django monolith + Postgres + ClickHouse (analytics) + Redis (caching) + Celery (scheduled jobs) บน EKS
→ ทำงานอัตโนมัติเป็นส่วนใหญ่: autoscaling, ingress, ใบรับรอง TLS, failover, logging, monitoring ฯลฯ
→ เนื่องจากใช้ชุดคอนฟิกนี้กับหลายโปรเจ็กต์ จึงลดค่าใช้จ่ายและเริ่มการทดลองใหม่ ๆ ได้ง่ายมาก
→ แทบไม่ต้องใช้เวลากับการดูแลอินฟราเลย (เดือนละ 0~2 ชั่วโมง)
→ เวลาส่วนใหญ่จึงใช้ไปกับการพัฒนาฟีเจอร์ ซัพพอร์ตลูกค้า และขยายธุรกิจ -
ใช้ Kubernetes บน AWS แต่ไม่ได้หมายความว่าจำเป็นต้องใช้แบบนี้เสมอไป เพียงแต่ผู้เขียนคุ้นเคยจึงคิดว่าสามารถดูแลให้เสถียรได้
→ ได้เรียนรู้จากการใช้เครื่องมือนี้หลายปีในทีมที่มีความอดทน (ช่วยกันทนและช่วยกันแก้ปัญหา)
→ “Kubernetes ทำให้เรื่องง่ายกลายเป็นเรื่องซับซ้อน แต่ก็ทำให้เรื่องซับซ้อนกลายเป็นเรื่องง่ายได้เช่นกัน” -
DNS, SSL และ load balancing แบบอัตโนมัติ
→ ส่งทราฟฟิกทั้งหมดจาก CloudFlare Proxy ไปยัง AWS L4 NLB (Network Load Balancer)
→ เมื่อมี request เข้ามา LB จะ forward ไปยังหนึ่งในโหนดของคลัสเตอร์ k8s
→ โหนดเหล่านี้อยู่ใน private subnet ที่ครอบคลุมหลาย AZ (Availability Zone)
→ ส่วนที่ให้ k8s รู้ว่าต้องส่งคำขอไปบริการไหนคือ ingress-nginx (Nginx cluster)
→ nginx จะใช้กฎ Rate Limiting และ traffic shaping ก่อนส่งต่อทราฟฟิก
→ สำหรับ PanelBear คอนเทนเนอร์แอปคือ Django ที่เสิร์ฟด้วย Uvicorn
→ มีไฟล์คอนฟิกไม่กี่ไฟล์ระหว่าง Terraform/K8s และโปรเจ็กต์ส่วนใหญ่ใช้ร่วมกัน
→ เวลา deploy โปรเจ็กต์ใหม่ ใช้แค่คอนฟิก ingress ราว 20 บรรทัดก็พอ -
rollout และ rollback อัตโนมัติ
→ ทุกครั้งที่ push ไปยัง master จะรัน CI pipeline ด้วย GitHub Actions
→ ตรวจสอบ codebase และสร้างสภาพแวดล้อมสมบูรณ์ด้วย Docker-Compose เพื่อทดสอบ E-to-E
→ หากผ่านการตรวจสอบ จะ build Docker image ใหม่แล้ว push ไปยัง ECR (Docker Registry ของ AWS)
→ จากนั้น flux component ( https://fluxcd.io/ ) ในคลัสเตอร์ k8s จะ sync image ภายในคลัสเตอร์ให้อัตโนมัติ
→ Flux จะทำ incremental rollout ให้อัตโนมัติ -
Horizontal Autoscaling
→ autoscaling ตามการใช้ CPU/หน่วยความจำ
→ หากมี Pod ต่อโหนดในคลัสเตอร์มากเกินไป ระบบจะสร้างเซิร์ฟเวอร์เพิ่มอัตโนมัติเพื่อเพิ่มความจุของคลัสเตอร์และลดโหลด และจะลดขนาดลงเมื่อไม่มีงาน
→ สำหรับ PanelBear ระบบจะปรับจำนวน replica ของ API Pod อัตโนมัติจาก 2 ถึง 8 ตัว -
แคช static asset ด้วย CDN
→ ตั้งค่า CloudFlare ที่ DNS เพื่อจัดการทุก request และช่วยป้องกัน DDoS
→ ใช้ Whitenoise ( https://github.com/evansd/whitenoise ) สำหรับเสิร์ฟไฟล์ static จึงไม่ต้องอัปโหลดไฟล์ไปยัง NGinx/Cloudfront/S3
→ สำหรับเว็บไซต์แบบ static บางส่วน เช่น landing page ของ Panelbear ใช้ NextJS -
แคชข้อมูลแอปพลิเคชัน
→ ในบางส่วนใช้ in-memory LRU cache ที่ Python มีให้
→ endpoint ส่วนใหญ่ใช้ Redis ภายในคลัสเตอร์ -
Rate Limiting ราย endpoint
→ แม้ nginx-ingress จะทำ global rate limit แต่บางครั้งจำเป็นต้องกำหนดลิมิตเฉพาะราย endpoint/เมธอด
→ ใช้ไลบรารี Django Ratelimit เพื่อประกาศข้อจำกัดในแต่ละ Django view ได้
→ ใช้ Redis เป็น backend เพื่อติดตามไคลเอนต์ที่เรียกแต่ละ endpoint (hash ตาม client key ไม่ใช่ตาม IP) -
การดูแลระบบแอป
→ Admin Panel ของ Django รองรับความสามารถพื้นฐานในการดูและแก้ไขข้อมูลอยู่แล้ว
→ เพิ่มฟังก์ชันอย่างบล็อกการเข้าถึงของบัญชีที่น่าสงสัย / ส่งอีเมลประกาศ / จัดการคำขอลบบัญชี (เริ่มจาก soft delete ก่อน แล้วลบถาวรภายใน 72 ชั่วโมง) -
การรันงานตามกำหนดเวลา
→ ใน SaaS มีงานตามกำหนดเวลาหลายประเภท: รายงานประจำวันให้ลูกค้า, คำนวณสถิติการใช้งานทุก 15 นาที, อีเมลตัวชี้วัดที่ส่งให้พนักงาน ฯลฯ
→ รัน Celery worker หลายตัวและ Celery beat scheduler ภายในคลัสเตอร์ โดยใช้ Redis เป็น task queue
→ ใช้ HealthChecks.io เพื่อรับการแจ้งเตือนผ่าน SMS/Slack/Email เมื่อ scheduled job ทำงานผิดพลาด -
การตั้งค่าแอป
→ การตั้งค่าทั้งหมดใช้ environment variables แม้จะเป็นวิธีดั้งเดิมแต่พกพาได้และรองรับอย่างดี -
การเก็บความลับ
→ ใช้ kubeseal โดยอาศัยการเข้ารหัสแบบอสมมาตรเพื่อเข้ารหัส Secret ต่าง ๆ มีเพียงคลัสเตอร์ที่มีสิทธิ์เข้าถึงคีย์ถอดรหัสเท่านั้นที่ถอดรหัสได้
→ ใช้คีย์เข้ารหัสของ AWS KMS เพื่อปกป้อง Secret ภายในคลัสเตอร์ -
ข้อมูลเชิงสัมพันธ์: Postgres
→ สำหรับการทดลอง จะรันคอนเทนเนอร์ Postgres แบบวานิลลาภายในคลัสเตอร์ และทำแบ็กอัปไป S3 ทุกวันด้วย K8s Cronjob
→ เมื่อโปรเจ็กต์เติบโต จะย้ายฐานข้อมูลจากในคลัสเตอร์ไปยัง RDS เพื่อให้ AWS จัดการแบ็กอัปแบบเข้ารหัสและอัปเดตด้านความปลอดภัย
→ เพื่อเสริมความปลอดภัย ฐานข้อมูลของ AWS จะเข้าถึงได้เฉพาะจาก Private Network เท่านั้น -
ข้อมูลแบบคอลัมน์: ClickHouse
→ ใช้ ClickHouse เพื่อจัดเก็บและ query ข้อมูล analytics ของ PanelBear อย่างมีประสิทธิภาพแบบเรียลไทม์
→ เป็นฐานข้อมูลแบบ columnar ที่ยอดเยี่ยม เร็วมาก และถ้าออกแบบโครงสร้างดีจะได้อัตราการบีบอัดสูง (ใช้ storage น้อยลง = กำไรมากขึ้น)
→ โฮสต์ ClickHouse instance เองภายในคลัสเตอร์ K8s
→ สร้าง CronJob สำหรับแบ็กอัปข้อมูลแบบ columnar ไปยัง S3 เป็นระยะ
→ มีสคริปต์บางตัวสำหรับแบ็กอัปและกู้คืนข้อมูลจาก S3 แบบแมนนวลในกรณีเกิดภัยพิบัติ -
service discovery แบบอิง DNS
→ K8s จะจัดการ DNS record ภายในคลัสเตอร์โดยอัตโนมัติเพื่อ route ทราฟฟิกไปยัง service ที่เกี่ยวข้อง
→ แม้ระหว่าง autoscaling ก็จะ sync DNS record อัตโนมัติเพื่อให้เชื่อมต่อกับ pod ที่ยัง healthy ได้ -
Infrastructure ที่ควบคุมเวอร์ชันได้
→ จัดการ Docker, Terraform และ K8s manifest ใน repository เดียว (Infra Mono-Repo)
→ สามารถสร้างและลบอินฟราได้ด้วยคำสั่งง่าย ๆ และทำซ้ำได้ผ่านการควบคุมเวอร์ชัน -
Terraform สำหรับ Cloud Resource
→ cloud resource ส่วนใหญ่จัดการด้วย Terraform
→ ทำให้สามารถจัดทำเอกสารและติดตามทรัพยากรอินฟราและคอนฟิกได้ -
K8s manifest สำหรับ deploy แอป
→ K8s Manifest ถูกเขียนไว้ในไฟล์ YAML ของ infra mono repo
→ แบ่งเป็นสองโฟลเดอร์คือ cluster และ apps
→ ใน cluster จะมีคอนฟิกสำหรับบริการระดับทั้งคลัสเตอร์ เช่น nginx-ingress, secret ที่เข้ารหัสแล้ว, Prometheus scraper
→ ส่วน apps จะเก็บข้อมูลไว้หนึ่ง namespace ต่อหนึ่งโปรเจ็กต์ -
การสมัครสมาชิกและการชำระเงิน
→ ใช้ Stripe Checkout จัดการการชำระเงินทั้งหมด
→ จึงไม่ต้องเข้าไปยุ่งกับข้อมูลการชำระเงินโดยตรง และโฟกัสกับตัวผลิตภัณฑ์ได้
→ เพียงสร้าง customer session แล้ว redirect ไปยังหน้าของ Stripe จากนั้นรับผลผ่าน WebHook ก็เสร็จ -
Logging
→ ไม่ใช้ logging agent แค่พิมพ์ log ไปที่ stdout อย่างง่าย ๆ แล้ว k8s จะเก็บ log และทำ rotate ให้อัตโนมัติ
→ จะส่งต่อไปยัง Elasticsearch/Kibana ผ่าน FluentBit เป็นต้นก็ได้ แต่ยังไม่ทำเพื่อให้ระบบคงความเรียบง่าย
→ สำหรับการตรวจสอบ log ใช้เครื่องมือ CLI ชื่อ stern -
การมอนิเตอร์และการแจ้งเตือน
→ ตอนแรกโฮสต์ Prometheus / Grafana เอง แต่เมื่อคลัสเตอร์มีปัญหา ระบบแจ้งเตือนก็หยุดไปด้วยจึงไม่สะดวก
→ จึงเปลี่ยนไปใช้ New Relic
→ ทุกบริการมี Prometheus Integration ที่เก็บ metrics อัตโนมัติและส่งต่อไปยัง Datadog, New Relic, Grafana Cloud ฯลฯ ได้ ดังนั้นตอนย้ายไป New Relic จึงทำได้เพียงใช้ Prometheus Docker image ที่พวกเขาให้มา -
การติดตามข้อผิดพลาด
→ ใช้ Sentry เพื่อเก็บข้อผิดพลาดของแอปพลิเคชัน
→ ใช้ช่อง Slack #alerts เพื่อรวมศูนย์การแจ้งเตือนทั้งหมด เช่น downtime, cron job failures, security alerts, performance regressions, application exceptions -
Profiling และของดีอื่น ๆ
→ เมื่อต้องการวิเคราะห์เชิงลึก จะใช้เครื่องมืออย่าง cProfile หรือ snakeviz
→ บนเครื่อง local ใช้ Django Debug Toolbar
11 ความคิดเห็น
ขอบคุณครับ
ความสามารถของ Sentry กับ New Relic ต่างกันมากไหม?
ก่อนหน้านี้คิดว่าน่าจะทำงานคล้ายกัน แต่ยังไม่เคยลองใช้เลยครับ
โอ้ บริษัทของเราก็กำลังพิจารณานำ k8s มาใช้เหมือนกัน แม้จะไม่ใช่สตาร์ทอัพที่มีวิศวกรคนเดียวก็ยังเป็นบทความที่ดีมากเลย
ขอบคุณสำหรับบทความดีๆ ครับ ได้แรงบันดาลใจกลับไปเลย
ถึงจะไม่ใช่สตาร์ตอัปคนเดียวก็เป็นบทความที่ดีนะ
มีคำพิมพ์ผิดเล็กน้อย,,
→ ใช้ Sentry เพื่อรวบรวมข้อผิดพลาดของแอปพลิเคชัน
=> น่าจะเป็นคำว่า รวบรวม ค่ะ
ขอบคุณครับ แก้ไขแล้ว~!
ผมหวังว่าในประเทศเราจะมีนักพัฒนาเดี่ยวหรือทีมขนาดเล็กที่สร้างรายได้จากบริการของตัวเองเพิ่มขึ้นอีกมาก
และหวังว่า GeekNews แห่งนี้จะเติบโตเป็นพื้นที่ที่บริการเหล่านั้นได้แนะนำตัวเองและได้รับฟีดแบ็กที่สร้างสรรค์
บันทึกทบทวนการดำเนินงานสตาร์ตอัป SaaS แบบคนเดียวตลอด 6 เดือน https://th.news.hada.io/topic?id=2415
การบริหารสตาร์ตอัปซอฟต์แวร์ด้วยความพยายามให้น้อยที่สุด https://th.news.hada.io/topic?id=1534
รายงานสถานะของ Independent SaaS ปี 2021 [สไลด์ 63 หน้า] https://th.news.hada.io/topic?id=3728
เรื่องราวประสบการณ์ล้มเหลวในการพยายามสร้างบริษัทมูลค่า 1 ล้านล้านวอน https://th.news.hada.io/topic?id=2
ผมขายหัวหอมบนอินเทอร์เน็ต https://th.news.hada.io/topic?id=3
สิ่งที่ซีอีโอสตาร์ตอัปอดีตพนักงาน Samsung ได้เรียนรู้หลังสูญเงินไป 1.2 พันล้านวอน https://th.news.hada.io/topic?id=3015
บริหารสตาร์ตอัปด้วยเงินปีละ 6 ดอลลาร์ https://th.news.hada.io/topic?id=1621
ขอบคุณครับ
เห็นด้วยครับ ขอบคุณ :)
👍