서버 한 대면 충분하다
왜 우리는 프로덕션 사이트 수십 개를 전용 서버 한 대에서 굴리는가, 그리고 그 한 대를 버티게 하는 config 블록 세 개.
3 분 읽기서울과 나트랑에서 씁니다
다들 우리가 클라우드 인스턴스를 잔뜩 띄워 굴린다고 생각한다. 실제로는 팔켄슈타인 데이터센터에 놓인 전용 서버 한 대가 전부이고, 우리가 운영하는 모든 프로덕션 사이트와 데이터베이스, 봇이 이 한 대 위에 올라가 있다.
옛날 감성 때문이 아니다. 강제 장치다. 모든 게 한 대에 몰려 있으면 허술한 엔지니어링을 오토스케일링 뒤에 숨길 수가 없다. 메모리 누수는 한 시간 안에 옆집 문제가 된다. 폭주하는 크론 하나가 모두의 지연에 그대로 찍힌다. 그래서 규율은 세 곳에 박혀 있어야 하고, 세 군데 모두 약속이 아니라 config로 강제한다.
1. 서비스마다 천장을 둔다
# systemd override, applied to every unit, no exceptions
[Service]
MemoryMax=512M
CPUQuota=80%
Restart=on-failure
RestartSec=5
천장은 부딪히라고 있는 게 아니다. 뭔가 잘못됐을 때 OOM 킬러가 마흔 개 중 아무나 하나를 골라 죽이는 대신, 커널이 딱 한 서비스만 끝내도록 두려고 있는 장치다.
2. 예약 작업마다 락, 타임아웃, 하트비트를 붙인다
*/5 * * * * flock -n /run/lock/sync.lock timeout 240 /opt/jobs/sync.sh \\
&& date > /opt/health/sync.ok
락은 느리게 도는 작업이 제 뒤에 쌓이는 걸 막는다. 타임아웃은 멈춰버린 작업이 락을 영원히 쥐고 있는 걸 막는다. 재밌는 건 하트비트 파일이다. 별도의 워치독이 모든 .ok 파일의 mtime을 확인하고, 값이 오래됐다는 사실 자체를 경보로 본다. 프로덕션에서 작업은 요란하게 실패하지 않는다. 안 돌면서 실패한다.
3. 라우팅은 눈으로 읽히는 파일 하나
example.com {
encode zstd gzip
root * /srv/sites/example
file_server
handle /api/* { reverse_proxy 127.0.0.1:3160 }
}
Caddy가 모든 도메인의 TLS를 처리하고 호스트명으로 라우팅한다. 그 뒤의 앱은 컨테이너 아니면 정적 디렉터리다. 데이터베이스는 평범한 Postgres와 MySQL, 엔진당 인스턴스 하나에 스키마는 여럿이다. 별난 건 하나도 없고, 전부 표준 도구로 들여다볼 수 있다.
한 대로 얻는 것. 머릿속으로 따져볼 수 있는 지연(내부 홉이 전부 루프백이다), 단 하나의 진실이 담긴 파일시스템(디버깅은 분산 추적 세미나가 아니라 grep이다), 그리고 정직한 용량 계획(대시보드는 htop이다). 함정은 이 한 대가 귀해진다는 것이고, 귀한 물건에는 편집증이 필요하다. 그 편집증, 그러니까 실제로 복구까지 해보는 백업과 침묵 감지, 폭발 반경 제한 같은 것들이 앞으로 이어질 노트 대부분의 주제다.