Skip to main content
레플리카 하나는 모든 컴파일에 노드 하나의 CPU를 사용합니다. vCPU 2개인 테스트 노드에서 동시에 여섯 개를 컴파일하면 하나를 컴파일할 때보다 세 배 오래 걸렸고, 그동안 다른 노드는 유휴 상태였습니다. 여러 레플리카를 사용하면 사용자와 그 컴파일이 노드들에 분산됩니다. 이 구성은 Overleaf의 수평 확장 설계를 Kubernetes에 맞게 옮긴 것입니다.
처음부터 S3를 계획하세요. 이미 볼륨에 파일을 보관하고 있는 인스턴스는 먼저 S3 마이그레이션이 필요합니다. 이 구성은 5단계의 Deployment를 대체합니다. StatefulSet을 적용하기 전에 Deployment를 삭제하세요(kubectl delete -f 40-overleaf.yaml).
1

S3 스토리지

S3에 설명된 대로 사용 중인 S3 서비스나 클러스터 내 Garage로 버킷 세 개와 사용자 두 명을 설정하고, 키 네 개를 Secret overleaf-s3에 넣으세요. 아래 구성은 해당 페이지의 Garage 예시를 사용합니다.
2

구성

이 ConfigMap은 30-config.yaml을 대체합니다. S3와 git-bridge를 추가하고 OVERLEAF_SITE_URL이 로드 밸런서를 가리키도록 합니다.
60-config.yaml
3

레플리카

70-overleaf.yaml
  • Deployment가 아닌 StatefulSet. 각 레플리카는 로드 밸런서 쿠키와 git-bridge가 참조하는 고정된 이름(overleaf-0, overleaf-1, …)을 받으며, OrderedReady는 overleaf-0을 먼저 단독으로 시작하므로 데이터베이스 마이그레이션은 하나의 레플리카만 실행합니다.
  • 노드당 레플리카 하나. 각 레플리카는 자신의 노드에 있는 Docker를 통해 컴파일을 시작하고, 컴파일 결과를 해당 노드의 /srv/overleaf-ha에 보관합니다.
  • 레플리카별 V1_HISTORY_URL. 각 레플리카는 자신의 이름으로 히스토리 링크를 제공하므로 git-bridge가 여기에 접근할 수 있습니다.
4

git-bridge

75-git-bridge.yaml
GIT_BRIDGE_HOST가 고정 IP 주소인 이유는 Git 연동을 참고하세요.
5

로드 밸런서

HAProxy 구성은 Overleaf의 샘플에서 다섯 가지를 변경한 것입니다. /project/<id>/mcp를 /git/처럼 overleaf-0으로 라우팅(MCP 서버 참고), 포트 8080의 일반 HTTP(이미지가 root가 아닌 사용자로 실행되므로 containerd에서 포트 80에 바인딩할 수 없음), 클러스터 DNS를 통한 서버 탐색(파드 IP는 재시작할 때마다 바뀜), 모든 노드가 응답하도록 하는 DaemonSet, 그리고 기본값 roundrobin 대신 balance leastconn(아래 Q&A 참고)입니다.
80-haproxy.yaml
externalTrafficPolicy: Local은 사용자의 IP 주소를 X-Forwarded-For에 유지합니다.
6

TLS

TLS 프록시에 설명된 대로 HAProxy에서 HTTPS를 종료한 다음, 60-config.yaml에서 OVERLEAF_SITE_URL을 https:// 주소로 설정하고 OVERLEAF_SECURE_COOKIE: "true"를 설정하세요.

예상 결과

각각 vCPU 2개와 4 GB 메모리를 가진 노드 세 대에서 레플리카 세 개를 테스트했으며, MongoDB, Redis, Garage, git-bridge, HAProxy도 같은 노드에서 실행했습니다.
  • 사용자 12명이 600페이지 문서를 동시에 컴파일: 12명 모두 컴파일하고 PDF를 가져왔으며, 레플리카당 사용자 4명이었습니다. 한 라운드에 8~18초가 걸렸습니다. 각 노드는 최소 1.3 GB의 사용 가능한 메모리를 유지했습니다.
  • 모든 사용자는 하나의 레플리카에 머무릅니다(쿠키 server-pro-ha). 컴파일한 레플리카에서 가져와야 하는 PDF 다운로드도 마찬가지입니다. 에디터의 WebSocket을 포함하여 HTTPS에서도 동일합니다.
  • 레플리카가 중지되면 HAProxy가 이를 다운으로 표시할 때까지 약 15~20초 동안 해당 사용자에게 오류가 발생합니다. 그 후 사용자는 다른 레플리카로 이동하며, 세션이 Redis에 있으므로 로그인 상태가 유지됩니다.

Q&A

답변: Overleaf의 샘플(roundrobin, 노드당 HAProxy 하나)로 테스트했을 때 신규 사용자 12명이 세 레플리카에 8명, 3명, 1명으로 배분되었고, 가장 바쁜 레플리카 때문에 가장 느린 사용자는 29초를 기다렸습니다. 열린 연결이 가장 적은 레플리카를 선호하는 leastconn(각 에디터는 WebSocket을 열어 둠)으로 같은 테스트를 하자 4명, 4명, 4명으로 배분되었고 18초가 걸렸습니다. 스티키 동작은 쿠키에서 오므로 변하지 않습니다.
답변: 네. 쿠키를 기준으로 고정된 레플리카로 라우팅하고, WebSocket을 지원하며, 10분의 요청 시간과 50 MB 업로드를 허용하고, keep-alive 타임아웃을 65초 미만으로 유지하며, X-Forwarded-For와 X-Forwarded-Proto를 보내고, /git/과 /project/<id>/mcp를 overleaf-0으로 보낸다면 사용할 수 있습니다. 모든 요청을 분산하는 Service가 아니라 레플리카 자체의 이름(overleaf-N.overleaf-hl)으로 라우팅하세요.
답변: Docker와 TeX Live 이미지가 있는 노드를 추가하고, 70-overleaf.yaml에서 replicas: 4로 설정한 다음, HAProxy 구성에 server overleaf-3 ... 줄을 추가하세요(그리고 HAProxy가 재시작되도록 config-hash를 올리세요).
마지막 수정일 2026년 11월 2일