단일 서버 운영의 자리
단일 서버 운영의 자리
서비스가 시작 단계라면 한 대의 VPS 에 docker compose 로 모든 것을 띄우는 구성이 종종 가장 합리적입니다. 운영 인지 부하가 작고 비용이 예측 가능. 이 모델이 어디까지 가능한지 · 언제 한계에 닿는지 · k8s 를 비롯한 다음 단계의 자리.
1. 단일 서버 + Compose 의 일반적 구성
- 한 대의 리눅스 호스트 (Ubuntu · Debian · Rocky 등).
- Caddy 또는 nginx 가 80 / 443 에서 종단.
- 애플리케이션 · DB · Redis · 큐가 같은 호스트의 컨테이너로 공존.
- 로그 · 메트릭은 호스트 또는 한정된 외부 서비스로.
- 정기 백업이 별도 디스크 또는 오브젝트 스토리지로.
수직 확장 (스펙 큰 머신) 으로 상당 거리까지 갈 수 있습니다. 적절히 만들어진 단일 서버가 하루 수백만 요청을 다루는 사례가 종종 보고 (작업 성격에 크게 좌우).
2. 수직 확장 vs 수평 확장
- 수직 (scale up) — 더 큰 단일 머신. 단순. 한계는 머신 스펙 · 실패 도메인 단일.
- 수평 (scale out) — 여러 머신으로 분산. 가용성 · 확장성. 비용은 운영 복잡도.
수직 확장은 코드 변경이 거의 없음. 수평 확장은 상태 공유 · 세션 · 캐시 · DB 라우팅 · 로깅 집계 같은 결정이 줄줄이 따라옴.
3. 운영 도구
compose.yaml— 서비스 정의 +restart: unless-stopped.systemd— Compose 자체를 부팅 시 자동 시작 (docker-compose@.service같은 unit).- 로그 회전 — Docker 로그 드라이버 옵션 또는 logrotate.
- 백업 —
pg_dump/pg_basebackup의 cron + 오브젝트 스토리지 업로드. - 모니터링 — Prometheus + Grafana 컨테이너, 또는 외부 SaaS (uptime · APM).
4. 가용성의 기본
자동 재시작 — compose restart: unless-stopped, systemd 의 Restart=always.
헬스체크 — 컨테이너 healthcheck + 외부 상태 모니터.
이 둘만으로 단순 장애의 대부분은 자동 회복. 호스트 자체가 죽는 경우는 별개의 이야기.
5. 다른 길
| 도구 | 첫 등장 | 메모 |
|---|---|---|
| Docker Swarm | 2014–2015 | Docker 가 만든 오케스트레이터. 비교적 단순. 활발한 발전은 둔화. |
| Nomad (HashiCorp) | 2015 | 단일 바이너리. 컨테이너 외 작업도 다룸. 운영 복잡도 중간. |
| Kubernetes | 2014 (Google) → CNCF 1.0 (2015) | 사실상의 컨테이너 오케스트레이션 표준. 거대한 생태계. |
| Nomad + Consul + Vault | — | 분산 시스템 빌딩 블록의 다른 조합. |
k8s 는 강력하지만 인지 부하가 크다는 평이 일반적. 작은 팀이 k8s 를 직접 운영하면 본업보다 클러스터 유지에 시간이 더 들 수 있음. 매니지드 (EKS / GKE / AKS) 가 그 부담을 줄이지만 여전히 학습 · 운영 비용.
6. k8s 가 과한 자리
다음은 단일 호스트로 충분한 경우가 많음:
- 팀 규모가 작고 (수 명) 서비스 수가 적음 (< 10).
- 트래픽이 한 호스트로 처리 가능.
- 무중단 배포의 엄격함이 비즈니스 임계가 아님.
- 운영 인력의 시간이 제한.
반대로 다음이 누적되면 단일 호스트는 한계:
- 단일 실패 지점이 사업 위험.
- 트래픽이 여러 머신으로 나뉘어야 함.
- 여러 팀이 독립 배포를 원함.
- 다중 리전 · 지연 민감 사용자.
7. 부팅 자동화 예
# /etc/systemd/system/myapp.service
[Unit]
Description=My App via Docker Compose
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
[Install]
WantedBy=multi-user.target
8. 백업 잡 (PostgreSQL 예)
# /etc/cron.daily/db-backup
#!/usr/bin/env bash
set -euo pipefail
TS=$(date +%Y%m%d-%H%M)
docker exec db pg_dump -U postgres app | gzip > /backup/db-$TS.sql.gz
find /backup -name 'db-*.sql.gz' -mtime +14 -delete
# 오프사이트 업로드
rclone copy /backup remote:backup/
데이터 손실 위험을 가장 잘 줄이는 것은 백업 자체가 아니라 복원 리허설. 테스트 호스트에 복원해 보는 절차를 정기적으로 돌리는 편이 안전.
9. 단일 호스트 무중단 배포
- 새 컨테이너를 다른 포트로 띄움.
- 헬스체크가 통과하면 프록시 (Caddy / nginx) 의 업스트림을 새 포트로 전환.
- 구 컨테이너 종료.
Caddy 의 동적 설정 또는 traefik 의 라벨 기반 디스커버리가 이 흐름을 돕습니다.
10. 자주 걸리는 자리
단일 디스크의 SPOF — RAID · 정기 백업 · 오프사이트 복사가 없다면 디스크 한 번에 모든 것이 사라질 수 있음.
호스트 OS 업데이트 시 다운타임 — 커널 업데이트 등은 재부팅 필요. 사전 공지 또는 별도 호스트로 임시 이전.
메모리 한도 — 컨테이너가 늘어나면 OOM 이 잦아짐. 컨테이너별 mem_limit · DB 의 shared_buffers 튜닝.
로그 · 메트릭의 디스크 점유 — 로그 회전 · Prometheus retention 설정을 빠뜨리면 디스크가 차서 서비스가 멈춤.
운영 호스트에서의 즉흥 변경 — 서버에 직접 SSH 로 들어가 패키지를 깐 뒤 그 사실을 잊음. 변경은 가능한 한 코드 (Compose · Ansible) 에 남김.
하고픈 말
단일 서버 + Compose 는 작은 팀의 가장 합리적인 출발점입니다. 코드의 95% 를 비즈니스 로직에 쓸 수 있고, k8s 학습 곡선과 인프라 운영 부담에서 자유. 한계가 보이기 시작하면 그때 수평 확장의 한 단계만 (DB 분리 → 매니지드 k8s) 신중하게 올라갈 만한 자리.
Next
- local-https-mkcert
- cloud-emulator-stack
Compose 운영 가이드 · Restart policies (Docker) · Kubernetes 공식 · Nomad 공식 · PostgreSQL 백업 문서 를 참고합니다.