The Architecture Behind A One-Person Tech Startup (anthonynsimon.com)
- 스트레스 없이, 느릿하게 SaaS를 운영하는 1인 개발자의 아키텍쳐 설명
- 여러개의 프로젝트를 동시에 운영하는 인프라를 구성했음
- 자신이 최근에 만든 PanelBear 라는 SaaS를 기준으로 설명
ㅤ→ 가장 작은 VPS에서 SQLite + Django로 시작
ㅤ→ 6개월간의 이터레이션을 통해, EKS 위에서 Django 모노리스 + Postgres + ClickHouse(분석) + Redis(캐싱) + Celery(예약 작업)
ㅤ→ 대부분 자동화 : 오토스케일링, ingress, TLS 인증서, failover, 로깅, 모니터링 등
ㅤ→ 이 설정을 여러개의 프로젝트에서 사용하므로, 비용을 줄이고 실험들을 정말 쉽게 시작할 수 있음
ㅤ→ 인프라 관리에는 거의 시간이 들지않음 (한달에 0~2시간).
ㅤ→ 대부분의 시간은 기능 개발, 고객 지원, 비즈니스를 성장시키는데 사용
- AWS에서 Kubernetes를 사용하지만 꼭 이게 필요한 건 아님. 다만, 본인은 익숙하기에 안정적으로 운영할 수 있다고 생각.
ㅤ→ 참을성 있는 팀(에러를 같이 참고 막아줄)에서 이 도구를 몇년간 사용하며 배웠음
ㅤ→ "쿠버네티스는 단순한 걸 복잡하게 만들지만, 복잡한 것을 단순하게 만들기도 함"
- 자동 DNS, SSL 및 로드밸런싱
ㅤ→ CloudFlare Proxy 에서 AWS L4 NLB(Network Load Balancer) 로 모든 트래픽 전달
ㅤ→ Request가 들어오면 LB가 k8s 클러스터 노드중 하나로 포워드
ㅤ→ 이 노드들은 여러개의 AZ(Availability Zone) 에 걸쳐져 있는 프라이빗 서브넷에 있음
ㅤ→ k8s 가 요청할 서비스를 알아내는 것은 ingress-nginx(Nginx 클러스터) 가 함
ㅤ→ nginx가 트래픽 전달전에 RateLimiting 및 트래픽 쉐이핑 규칙을 적용
ㅤ→ PanelBear의 경우 앱 컨테이너는 Uvicorn 에서 서빙되는 Django
ㅤ→ Terraform/K8s 간에 몇개의 설정파일이 있고 그걸 대부분의 프로젝트가 공유
ㅤ→ 새 프로젝트 배포 할때 20 라인 정도의 ingress 설정으로 가능
- 자동 롤아웃 및 롤백
ㅤ→ 마스터에 푸시할때마다 GitHub Actions 로 CI 파이프라인 실행
ㅤ→ 코드베이스 검사, Docker-Compose로 완전환 환경을 만들어 E-to-E 테스트
ㅤ→ 검사 통과시 ECR(AWS의 Docker Registry)로 푸시되는 새 Docker 이미지를 빌드
ㅤ→ k8s 클러스터에 flux 컴포넌트 ( https://fluxcd.io/ ) 가 알아서 클러스터내의 이미지를 동기화
ㅤ→ Flux 가 자동으로 Incremental Rollout 실행
- Horizontal Autoscaling
ㅤ→ CPU/메모리 사용량 기반으로 오토스케일링
ㅤ→ 클러스터의 노드당 Pod가 너무 많은 경우 자동으로 더 많은 서버를 생성해서 클러스터 용량을 늘리고 부하를 줄임. 일이 없을때는 축소
ㅤ→ Panelbear 의 경우 API Pod를 2개에서 8개까지 복제본을 자동 조정
- CDN을 이용해서 Static Asset 캐슁
ㅤ→ CloudFlare를 DNS에 설정해서, 모든 요청을 처리하고 DDoS 방지까지 처리
ㅤ→ Static 파일 서빙에는 Whitenoise ( https://github.com/evansd/whitenoise ) 를 사용해서 NGinx/Cloudfront/S3에 파일 업로드할 필요가 없음
ㅤ→ Panelbear 의 랜딩 같은 몇개의 정적 웹사이트에는 NextJS 를 사용
- 어플리케이션 데이터 캐슁
ㅤ→ 일정부분에서는 파이썬이 제공하는 인메모리 LRU 캐슁을 사용
ㅤ→ 대부분의 엔드포인트는 인 클러스터 Redis를 사용
- EndPoint 당 Rate Limiting
ㅤ→ nginx-ingress 에서 글로벌 레이트 리밋을 하지만, 가끔 엔드포인트/메소드 당 특정 리밋을 줄 필요가 있음
ㅤ→ Django Ratelimit 라이브러리를 사용해서 Django 뷰당 제한을 선언 가능
ㅤ→ Redis 를 백엔드로 사용해서 각 엔드포인트에 요청하는 클라이언트를 추적하도록 구성(IP가 아닌 클라이언트 키 기반 해시)
- App Administration
ㅤ→ Django 의 Admin Panel 이 기본적으로 데이터를 보고 편집하는 기능들을 지원
ㅤ→ 의심스러운 계정 접근 차단 / 공지 이메일 발송 / 계정 삭제 요청 처리등의 기능을 추가(처음엔 소프트 삭제후, 72시간내에 완전 삭제)
- 예약된 작업 실행
ㅤ→ SaaS에선 다양한 예약작업이 실행 : 고객에 대한 일일 보고서, 15분마다 사용 통계 계산, 직원들에게 보내는 지표 이메일 등
ㅤ→ Celery 워커 몇개와 Celery beat 스케줄러를 클러스터내에서 실행중. Redis를 태스크 큐로 사용
ㅤ→ 예약된 작업이 제대로 실행되지 않을때 SMS/Slack/Email 등으로 공지 받기 위해 HealthChecks.io 를 사용
- App Configuration
ㅤ→ 모든 설정은 환경변수를 이용. 구식이지만 이식 가능하고 잘 지원됨
- Keeping Secrets
ㅤ→ kubeseal 을 이용. 비대칭 암호화를 사용해서 Secret들을 암호화함. 암호 해독 키에 접근할 권한이 있는 클러스터만 암호 해독 가능
ㅤ→ 클러스터내의 Secret 들을 보호하기 위해 AWS KMS의 암호화 키를 이용
- 관계형 데이터 : Postgres
ㅤ→ 실험들을 위해 클러스터내에서 바닐라 Postgres 컨테이너를 운영하고, K8s Cronjob 으로 S3에 매일 백업을 수행
ㅤ→ 프로젝트가 성장하면 데이터베이스를 클러스터 내에서 RDS로 옮기고, AWS가 암호화된 백업 및 보안 업데이트등을 처리하게 함
ㅤ→ 보안을 강화하기 위해, AWS의 DB는 Private Network 에서만 접근 가능
- 컬럼 데이터 : ClickHouse
ㅤ→ PanelBear의 분석데이터를 효율적으로 저장하고 실시간 쿼리하기 위해 ClickHouse 를 사용
ㅤ→ 훌륭한 Columnar 데이터 베이스로, 엄청 빠르고 구조만 잘 잡으면 높은 압축률을 보임(스토리지 감소 = 수익 증가)
ㅤ→ K8s 클러스터내에서 ClickHouse 인스턴스를 자체 호스팅
ㅤ→ S3에 Columnar 데이터를 주기적으로 백업하는 CronJob 생성
ㅤ→ 재해시 S3에서 데이터를 수동으로 백업하고 복원하는 몇개의 스크립트
- DNS기반 서비스 디스커버리
ㅤ→ K8s 가 클러스터내의 DNS 레코드를 자동으로 관리해서 트래픽을 해당 서비스로 라우팅
ㅤ→ 오토스케일링중에도 건강한 pod 들과 연결되도록 자동으로 DNS 레코드를 동기화
- Version-Controlled Infrastructure
ㅤ→ Docker, Terraform, K8s manifest 를 단일 저장소(Infra Mono-Repo)에서 관리
ㅤ→ 간단한 명령으로 인프라 생성 및 삭제가 가능하고 버전 관리를 통해 재현가능
- Cloud Resource를 위한 Terraform
ㅤ→ 대부분의 클라우드 리소스는 Terraform 으로 관리
ㅤ→ 이를 통해 인프라 자원과 설정을 문서화 하고 추적이 가능
- App 배포를 위한 K8s manifest
ㅤ→ 인프라 모노 레포의 YAML 파일에 K8s Manifest 가 적혀있음
ㅤ→ cluster 와 apps 두개의 폴더로 분할
ㅤ→ cluster 에는 nginx-ingress, 암호화된 secret들, Prometheus 스크래퍼 처럼 클러스터 전체 서비스 관련 설정을 포함
ㅤ→ apps 에는 프로젝트당 하나의 네임스페이스에 정보들 저장
- 구독 및 결제
ㅤ→ Stripe Checkout 을 이용해서 모든 결제 처리
ㅤ→ 결제 정보 자체에 관여할 필요 없어서 제품에 집중 가능
ㅤ→ 고객 세션 만들고 Stripe의 페이지로 리디렉션 한뒤 WebHook 으로 결과받으면 끝
- Logging
ㅤ→ 로깅 에이전트를 쓰지 않고 간단히 stdout 에 로그출력하면 k8s 가 자동으로 로그를 수집하고 rotate도 수행
ㅤ→ FluentBit 등을 통해서 Elasticsearch/Kibana 등으로 보낼수 있겠지만, 간단히 유지하기 위해 아직은 하지 않음
ㅤ→ 로그 검사를 위해서는 CLI 도구인 stern 사용
- 모니터링 및 경고
ㅤ→ 처음에는 Prometheus / Grafana 를 자체호스팅 했지만, 클러스터 문제 발생시 경고시스템도 같이 중단 되어서 불편
ㅤ→ 그래서 New Relic 으로 변경
ㅤ→ 모든 서비스에는 자동으로 지표를 수집하고 Datadog, New Relic, Grafana Cloud 등으로 보낼수 있는 Prometheus Integration 이 있어서, New Relic 으로 이관시 그들이 제공하는 Prometheus Docker 이미지를 사용하는 것만으로 가능
- 오류 추적
ㅤ→ Sentry 를 이용해서 애플리케이션 오류를 수집
ㅤ→ Slack #alerts 채널을 이용해서 downtime, cron job failures, security alerts, performance regressions, application exceptions 등 모든 경고를 중앙 집중화
- Profiling 및 다른 좋은 것들
ㅤ→ 심층 분석이 필요할때는 cProfile 이나 snakeviz 같은 도구를 사용
ㅤ→ 로컬 머신에서는 Django Debug Toolbar 이용
Detect language Afrikaans Albanian Amharic Arabic Armenian Azerbaijani Basque Belarusian Bengali Bosnian Bulgarian Catalan Cebuano Chichewa Chinese (Simplified) Chinese (Traditional) Corsican Croatian Czech Danish Dutch English Esperanto Estonian Filipino Finnish French Frisian Galician Georgian German Greek Gujarati Haitian Creole Hausa Hawaiian Hebrew Hindi Hmong Hungarian Icelandic Igbo Indonesian Irish Italian Japanese Javanese Kannada Kazakh Khmer Korean Kurdish Kyrgyz Lao Latin Latvian Lithuanian Luxembourgish Macedonian Malagasy Malay Malayalam Maltese Maori Marathi Mongolian Myanmar (Burmese) Nepali Norwegian Pashto Persian Polish Portuguese Punjabi Romanian Russian Samoan Scots Gaelic Serbian Sesotho Shona Sindhi Sinhala Slovak Slovenian Somali Spanish Sundanese Swahili Swedish Tajik Tamil Telugu Thai Turkish Ukrainian Urdu Uzbek Vietnamese Welsh Xhosa Yiddish Yoruba Zulu
Afrikaans Albanian Amharic Arabic Armenian Azerbaijani Basque Belarusian Bengali Bosnian Bulgarian Catalan Cebuano Chichewa Chinese (Simplified) Chinese (Traditional) Corsican Croatian Czech Danish Dutch English Esperanto Estonian Filipino Finnish French Frisian Galician Georgian German Greek Gujarati Haitian Creole Hausa Hawaiian Hebrew Hindi Hmong Hungarian Icelandic Igbo Indonesian Irish Italian Japanese Javanese Kannada Kazakh Khmer Korean Kurdish Kyrgyz Lao Latin Latvian Lithuanian Luxembourgish Macedonian Malagasy Malay Malayalam Maltese Maori Marathi Mongolian Myanmar (Burmese) Nepali Norwegian Pashto Persian Polish Portuguese Punjabi Romanian Russian Samoan Scots Gaelic Serbian Sesotho Shona Sindhi Sinhala Slovak Slovenian Somali Spanish Sundanese Swahili Swedish Tajik Tamil Telugu Thai Turkish Ukrainian Urdu Uzbek Vietnamese Welsh Xhosa Yiddish Yoruba Zulu
Text-to-speech function is limited to 200 characters