50 คะแนน โดย xguru 2021-04-12 | 11 ความคิดเห็น | แชร์ทาง WhatsApp
  • คำอธิบายสถาปัตยกรรมของนักพัฒนาเดี่ยวที่ดูแล 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 ความคิดเห็น

 
wellsbabo 2024-08-13

ขอบคุณครับ

 
admin2 2021-04-13

ความสามารถของ Sentry กับ New Relic ต่างกันมากไหม?

ก่อนหน้านี้คิดว่าน่าจะทำงานคล้ายกัน แต่ยังไม่เคยลองใช้เลยครับ

 
kbumsik 2021-04-13

โอ้ บริษัทของเราก็กำลังพิจารณานำ k8s มาใช้เหมือนกัน แม้จะไม่ใช่สตาร์ทอัพที่มีวิศวกรคนเดียวก็ยังเป็นบทความที่ดีมากเลย

 
fortune 2021-04-12

ขอบคุณสำหรับบทความดีๆ ครับ ได้แรงบันดาลใจกลับไปเลย

 
khris 2021-04-12

ถึงจะไม่ใช่สตาร์ตอัปคนเดียวก็เป็นบทความที่ดีนะ

 
yshrust 2021-04-12

มีคำพิมพ์ผิดเล็กน้อย,,

  • การติดตามข้อผิดพลาด

→ ใช้ Sentry เพื่อรวบรวมข้อผิดพลาดของแอปพลิเคชัน

=> น่าจะเป็นคำว่า รวบรวม ค่ะ

 
xguru 2021-04-12

ขอบคุณครับ แก้ไขแล้ว~!

 
xguru 2021-04-12

ผมหวังว่าในประเทศเราจะมีนักพัฒนาเดี่ยวหรือทีมขนาดเล็กที่สร้างรายได้จากบริการของตัวเองเพิ่มขึ้นอีกมาก

และหวังว่า 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

 
wellsbabo 2024-08-13

ขอบคุณครับ

 
reedids 2021-04-12

เห็นด้วยครับ ขอบคุณ :)

 
e1q88 2021-04-12

👍