세 대륙에 걸친 Docker Swarm: 100% 업타임을 목표로 설계한 고가용성

게시일 읽는 시간 37분

데이터센터 한 곳은 단일 장애 지점입니다. 이 글에서는 위치 하나가 통째로 멈춰도 되는, 세 대륙에 걸친 Docker Swarm을 구축합니다. 매니저 쿼럼, WireGuard, 리전별 진입점, Geo-DNS 페일오버, 데이터베이스 복제, 운영까지 다룹니다.

하드웨어와 네트워크가 아무리 좋아도, 데이터센터 한 곳에 있는 서버는 단일 장애 지점입니다. 전력이나 네트워크에 장애가 생기거나 단순히 유지보수 중에 실수가 나서 그 위치가 멈추면 애플리케이션도 함께 멈춥니다. 여러 데이터센터에 걸친 Docker Swarm은 바로 이 문제를 해결합니다. 컨테이너가 서로 독립된 세 위치, 이상적으로는 세 대륙에서 실행되며, 그중 한 곳이 완전히 멈추면 나머지 두 곳이 이어받기 때문에 사용자는 이를 알아차리지 못합니다.

이 글에서는 100% 업타임을 목표로 설계된 Docker Swarm 클러스터를 구축하는 방법을 단계별로 보여 드립니다. 북미, 유럽, 아시아에 서버를 한 대씩 두고, 노드 사이를 암호화된 WireGuard 네트워크로 연결하고, 대륙 하나 전체가 멈춰도 버티는 매니저 쿼럼을 구성하고, 리전마다 진입점을 하나씩 두고, 사용자를 가장 가까운 정상 위치로 자동 안내하는 DNS 페일오버를 설정합니다. 또한 이런 아키텍처에서도 여전히 장애가 날 수 있는 부분은 무엇이고, 그 위험까지 어떻게 막을 수 있는지 솔직하게 설명합니다.

Docker Swarm으로 100% 업타임을 달성할 수 있을까요?

세 대륙에 걸친 Docker Swarm은 100% 업타임을 목표로 설계됩니다. 서버 한 대, 데이터센터 한 곳, 대륙 하나 중 어느 것도 혼자서는 애플리케이션을 멈출 수 없습니다. 그렇더라도 절대적인 가용성은 누구도 보장할 수 없습니다. 대형 클라우드 제공업체조차 마찬가지이며, 이들이 약속하는 최고 수준도 99.99~99.999%입니다. 그 원인은 데이터센터가 아니라 모든 위치가 공통으로 가진 요소에 있습니다. 이 글은 바로 그 요소까지 함께 다루어, 기술적으로 가능한 한 100%에 가깝게 다가가실 수 있도록 돕습니다.

숫자로 보는 가용성

가용성연간 다운타임월간 다운타임
99%87.6시간7.3시간
99.9%8.76시간43.8분
99.99%52.6분4.4분
99.999%5.3분26초

1년은 8,760시간, 1개월은 730시간으로 계산했습니다. 9가 하나 늘어날 때마다 허용되는 다운타임은 10분의 1로 줄어들며, 바로 여기서부터 서버 한 대로는 더 이상 감당할 수 없는 일이 시작됩니다.

세 대륙이 큰 차이를 만드는 이유

가용성이 각각 99.9%인 서로 독립된 세 위치가 동시에 멈추는 것은, 계산상 세 곳 모두에 같은 시각에 장애가 났을 때뿐입니다. 0.1% × 0.1% × 0.1%는 0.0000001%입니다. 위치가 서로 멀리 떨어져 있을수록 실제로도 더 독립적입니다. 전력망, 네트워크 연결, 기상 조건, 유지보수 시간대가 저마다 다르기 때문입니다. 그래서 북미, 유럽, 아시아로 나누어 배치하는 것이 서버로 구현할 수 있는 가장 강력한 형태의 장애 대비입니다.

세 대륙에 걸쳐도 여전히 장애가 날 수 있는 것

남은 위험은 모든 위치가 공통으로 의존하는 요소이며, 각각에 대한 대응책이 있습니다.

  • 결함이 있는 업데이트도 정상적인 업데이트와 마찬가지로 클러스터가 모든 위치에 확실하게 배포합니다. 대응책: 헬스 체크와 자동 롤백, 그리고 리전별로 차례차례 진행하는 업데이트.
  • DNS는 모든 사용자가 거쳐 오는 단 하나의 지점입니다. 대응책: 전 세계에 분산된 네트워크와 페일오버를 갖춘 DNS 제공업체, 짧은 TTL.
  • 데이터베이스는 모든 위치에서 같은 데이터를 가지고 있어야 합니다. 대응책: 자동 전환을 갖춘 복제. 데이터 섹션을 참고하세요.
  • 만료된 인증서와 도메인은 모든 위치에 동시에 영향을 줍니다. 대응책: 자동 갱신과 만료일 모니터링.
  • 전환 과정 자체도 헬스 체크와 DNS가 반응할 때까지 시간이 걸립니다. 대개 1~2분이며, 그동안 일부 요청은 실패할 수 있습니다. 대응책: 짧은 점검 주기, 짧은 TTL, 실패한 요청을 다시 시도하는 클라이언트.

아키텍처 개요

여러 대륙에 걸친 Docker Swarm은 여섯 가지 구성 요소로 이루어집니다. 각 구성 요소는 특정한 장애 지점을 하나씩 없애 줍니다.

구성 요소역할막아 주는 장애
세 대륙에 둔 매니저 노드 세 개Raft 합의로 클러스터 상태를 유지함위치 하나 또는 대륙 하나 전체의 장애
리전마다 두는 워커 용량사용자 가까이에서 컨테이너를 실행함개별 서버의 장애
모든 노드를 잇는 WireGuard 네트워크인터넷을 오가는 클러스터 트래픽 전체를 암호화함데이터센터 사이에서 일어나는 도청과 조작
리전별 진입점(리버스 프록시)사용자의 요청을 받아 로컬에서 응답함한 리전의 진입점 장애
헬스 체크를 갖춘 Geo-DNS사용자를 가장 가까운 정상 위치로 보냄접속할 수 없는 위치
복제된 데이터 저장과 백업데이터베이스와 파일을 여러 곳에 보관함위치 장애로 인한 데이터 손실

매니저는 몇 개를, 어디에 둘까요?

Swarm의 매니저 노드는 Raft 합의 알고리즘으로 클러스터 상태를 관리합니다. 모든 변경에는 매니저 과반수, 이른바 쿼럼의 동의가 필요합니다. 과반수를 잃으면 기존 컨테이너는 계속 실행되지만, 클러스터는 재스케줄링도, 장애 보완도, 업데이트 배포도 할 수 없습니다.

매니저과반수견딜 수 있는 장애
321
532
743

Docker는 매니저를 홀수로 두고 최소 세 개의 가용 영역에 분산하도록 권장합니다. 매니저가 세 개라면 1-1-1, 다섯 개라면 2-2-1 비율입니다. 여기서 이 글의 가장 중요한 규칙이 나옵니다. 위치 두 곳으로는 부족합니다. 위치가 두 곳이면 어느 한쪽에 매니저가 더 많을 수밖에 없고, 바로 그쪽이 멈추면 과반수가 사라집니다. 위치가 세 곳이 되어야 비로소 쿼럼이 어느 데이터센터의 장애든 견딜 수 있으며, 세 대륙이라면 대륙 하나 전체의 장애도 견딥니다.

세 대륙인가, 한 대륙인가: 장단점 비교

클러스터의 모든 변경, 모든 배포, 컨테이너의 모든 재스케줄링은 매니저 과반수의 확인을 기다립니다. 유럽, 북미, 아시아 사이에서는 이 확인 하나하나에 약 80~250밀리초의 패킷 지연 시간이 걸립니다. 사용자의 요청은 로컬에서 응답되므로 사용자에게는 문제가 되지 않습니다. 다만 배포와 재스케줄링은 노드 사이의 거리가 짧은 클러스터보다 눈에 띄게 오래 걸립니다.

방식장점대가
세 대륙(미국, 유럽, 아시아)최대한의 독립성, 전 세계 사용자가 서버와 가까움, 대륙 하나 전체가 멈춰도 됨더 느린 클러스터 관리, 장거리 구간의 데이터베이스 복제, 요청이 로컬에 머물러야 함
한 대륙 안의 세 위치(예: 프랑크푸르트, 스트라스부르, 바르샤바)빠른 클러스터 관리, 간단한 동기 복제대륙 전역에 걸친 대규모 사고가 모든 위치에 영향을 줌, 멀리 있는 사용자는 경로가 길어짐

사용자가 여러 대륙에 있고 가능한 한 높은 가용성을 목표로 하는 애플리케이션이라면 세 대륙 방식이 맞으며, 이 가이드에서도 이 방식을 구축합니다. 이때 모든 단계에 걸쳐 지켜야 할 중요한 규칙이 하나 있습니다. 모든 요청은 자기 리전에서 응답되며, 절대 대륙 사이를 오가지 않습니다.

적합한 KernelHost 위치

KernelHost는 프랑크푸르트암마인의 maincubes 데이터센터에서 서버를 운영하며, 유럽, 북미, 아시아 태평양의 여러 위치에서도 가상 서버를 제공합니다. 미국 내 세 곳, 캐나다, 런던, 스트라스부르, 바르샤바, 헬싱키, 싱가포르, 일본, 시드니, 뭄바이 등이 그 예입니다. 지도가 포함된 전체 목록은 서버 위치 페이지에서 보실 수 있습니다. 이 글의 예시에서는 유럽에 프랑크푸르트암마인, 북미에 미국 동부 해안, 아시아에 싱가포르를 사용합니다.

가이드: 세 대륙에 걸친 Docker Swarm 구축하기

예시에서는 서버 세 대를 사용합니다. 프랑크푸르트암마인의 swarm-eu, 미국 동부 해안의 swarm-us, 싱가포르의 swarm-asia입니다. 공인 주소는 문서화용 네트워크 203.0.113.0/24에서 가져왔고, WireGuard 네트워크는 10.10.0.0/24를 사용합니다. 둘 다 실제 값으로 바꾸세요. 세 노드 모두 매니저이자 워커입니다. 성능이 더 필요하면 나중에 리전마다 워커 전용 노드를 추가하세요.

1단계: 세 대륙에 서버 준비하기

세 리전에 Debian 12 또는 13을 설치한 서버를 한 대씩 주문하고 기본 보안 설정을 적용하세요. 키로만 로그인하는 SSH, 자동 보안 업데이트, 별도의 사용자 계정이 여기에 해당합니다. 새 루트 서버 체크리스트에서 이 내용을 모두 다룹니다. docker node ls에서 어느 노드가 어디에 있는지 바로 알아볼 수 있도록 의미가 드러나는 호스트 이름을 붙이세요.

hostnamectl set-hostname swarm-eu

2단계: 모든 노드에 Docker 설치하기

Debian과 Ubuntu에 Docker 설치하기 글에서 설명한 대로 세 서버 모두에 공식 Docker 저장소의 Docker Engine을 설치하세요. 그런 다음 노드마다 버전을 확인합니다. 세 노드 모두 메이저 버전이 같아야 합니다.

docker version --format '{{.Server.Version}}'

3단계: 대륙 사이에 WireGuard 네트워크 구축하기

노드들은 공용 인터넷을 통해 서로 통신합니다. 클러스터 트래픽 전체를 암호화하고 Swarm 포트에 외부에서 절대 접근할 수 없도록 WireGuard 네트워크로 세 서버를 연결합니다. 먼저 각 노드에서 키 쌍을 만듭니다.

apt-get install -y wireguard
umask 077
wg genkey | tee /etc/wireguard/private.key | wg pubkey > /etc/wireguard/public.key

그다음 각 노드에 /etc/wireguard/wg0.conf 파일을 만듭니다. swarm-eu에서는 아래와 같으며, 나머지 두 노드는 이와 대칭되게 구성합니다.

[Interface]
Address = 10.10.0.1/24
ListenPort = 51820
PrivateKey = SWARM_EU_개인_키
MTU = 1420

[Peer]
PublicKey = SWARM_US_공개_키
Endpoint = 203.0.113.12:51820
AllowedIPs = 10.10.0.2/32
PersistentKeepalive = 25

[Peer]
PublicKey = SWARM_ASIA_공개_키
Endpoint = 203.0.113.13:51820
AllowedIPs = 10.10.0.3/32
PersistentKeepalive = 25
systemctl enable --now wg-quick@wg0
ping -c 3 10.10.0.2

모든 노드가 10.10.0.x 주소로 응답하면 네트워크가 완성된 것입니다. PersistentKeepalive는 상태 기반 방화벽 뒤에서도 연결이 끊기지 않게 유지합니다. 지금 ping이 보여 주는 대륙 간 지연 시간이 바로 클러스터를 변경할 때마다 드는 대기 시간입니다.

4단계: 방화벽으로 Swarm 노드만 허용하기

Docker Swarm은 노드 사이에 클러스터 관리용 2377/TCP 포트, 노드 간 통신용 7946/TCP 및 UDP 포트, 오버레이 네트워크용 4789/UDP 포트가 필요합니다. 이 포트들은 WireGuard 인터페이스에서만 허용하고, 외부에는 WireGuard 자체만, 그것도 다른 노드에게만 열어 둡니다. ufw를 쓰면 swarm-eu에서는 다음과 같습니다.

ufw allow from 203.0.113.12 to any port 51820 proto udp
ufw allow from 203.0.113.13 to any port 51820 proto udp
ufw allow in on wg0 to any port 2377 proto tcp
ufw allow in on wg0 to any port 7946
ufw allow in on wg0 to any port 4789 proto udp

중요: Docker가 컨테이너용으로 공개하는 포트는 ufw를 우회합니다. Docker가 자체 iptables 규칙을 설정하기 때문입니다. 따라서 진입점의 포트(80과 443)만 공개하고, 데이터베이스 포트나 관리 포트는 절대 공개하지 마세요.

5단계: Swarm 초기화와 매니저 추가

swarm-eu에서 Swarm을 초기화하고, 관리 트래픽과 데이터 트래픽이 모두 WireGuard를 거치도록 합니다.

docker swarm init --advertise-addr 10.10.0.1 --data-path-addr 10.10.0.1
docker swarm join-token manager

두 번째 명령은 다른 매니저가 참가할 때 쓸 조인 명령을 출력합니다. swarm-us와 swarm-asia에서 각자의 WireGuard 주소를 넣어 이 명령을 실행합니다. swarm-us의 예는 다음과 같습니다.

docker swarm join --token SWMTKN-1-... --advertise-addr 10.10.0.2 --data-path-addr 10.10.0.2 10.10.0.1:2377
docker node ls

이제 docker node ls에 노드 세 개가 표시되며, 하나는 Leader 상태이고 나머지 둘은 Reachable 상태입니다. 조인 토큰은 비밀 정보입니다. 이 토큰을 아는 사람은 자신의 매니저를 여러분의 클러스터에 몰래 끼워 넣을 수 있습니다. 구축이 끝나면 docker swarm join-token --rotate manager로 토큰을 새로 발급하세요.

6단계: Autolock 활성화하기

매니저는 Raft 로그를 암호화하는 키를 포함해 클러스터 상태를 /var/lib/docker/swarm/에 저장합니다. Autolock을 켜면 이 키 자체가 암호화되며, 재시작한 매니저는 잠금 해제 키를 입력해야 클러스터에 다시 참가합니다.

docker swarm update --autolock=true
docker swarm unlock

첫 번째 명령은 잠금 해제 키를 출력하며, 이 키는 비밀번호 관리자에 보관합니다. 두 번째 명령은 매니저를 재시작할 때마다 필요합니다. 이 키가 없으면 백업에서도 Swarm을 복원할 수 없으므로 서버와 분리된 곳에 보관하세요.

7단계: 리전별로 노드에 레이블 붙이기

레이블은 스케줄러에게 노드가 어디에 있는지 알려 줍니다. 다음 단계의 배치 규칙은 이 레이블을 바탕으로 합니다.

docker node update --label-add region=eu swarm-eu
docker node update --label-add region=us swarm-us
docker node update --label-add region=asia swarm-asia

8단계: 알맞은 MTU로 오버레이 네트워크 만들기

서로 다른 위치에 있는 컨테이너는 오버레이 네트워크로 통신합니다. 이 네트워크는 WireGuard 터널을 통과하므로 MTU가 더 작아야 합니다. WireGuard는 1420바이트로 동작하고, 오버레이 네트워크(VXLAN)가 그중 50바이트를 자체 헤더에 쓰므로 1370바이트가 남습니다.

docker network create --driver overlay --attachable --opt com.docker.network.driver.mtu=1370 appnet

MTU가 너무 크면 증상이 까다롭게 나타납니다. 작은 요청은 잘 되는데 큰 응답은 멈춰 버립니다. WireGuard를 쓰지 않는다면 대신 --opt encrypted로 오버레이 네트워크를 암호화할 수 있습니다. 이 경우 노드 사이에 IP 프로토콜 50(ESP)도 허용해야 하며, Docker는 성능이 눈에 띄게 떨어진다고 분명히 안내합니다. 저희는 WireGuard를 권장합니다. 관리 트래픽을 포함한 전체 트래픽을 보호하기 때문입니다.

9단계: 리전마다 애플리케이션 운영하기

일반적인 Swarm 서비스는 서비스 주소로 들어온 요청을 클러스터의 모든 레플리카에 분산하며, 다른 대륙에 있는 레플리카도 예외가 아닙니다. 대륙이 셋이라면 요청 두세 건 중 한 건은 지구 반 바퀴를 돌아야 합니다. 그래서 리전마다 별도의 서비스를 두고, 배치 규칙으로 그 서비스가 자기 리전 안에 머물게 합니다. 업데이트 설정은 새 버전이 컨테이너 하나씩 차례로 배포되고, 오류가 나면 자동으로 되돌려지도록 합니다.

for r in eu us asia; do
  docker service create --name web-$r --replicas 2 --network appnet \
    --constraint node.labels.region==$r \
    --update-parallelism 1 --update-delay 30s \
    --update-failure-action rollback \
    registry.example.com/web:1.0
done

컨테이너가 그냥 실행 중인 것이 아니라 실제로 제대로 일하고 있는지 Swarm이 알 수 있도록 이미지에 헬스 체크를 넣어야 합니다. 예를 들어 애플리케이션의 Dockerfile에 다음 줄을 넣습니다.

HEALTHCHECK --interval=10s --timeout=3s --retries=3 CMD wget -qO- http://127.0.0.1:8080/health || exit 1

헬스 체크가 세 번 연속 실패한 컨테이너는 교체되며, 업데이트 중에 헬스 체크가 실패하면 배포가 중단됩니다.

10단계: 리전마다 진입점 하나

진입점 역할은 Caddy, Traefik, nginx 같은 리버스 프록시가 맡습니다. 리버스 프록시도 리전마다 별도의 서비스로 실행되며, 요청을 자기 리전의 애플리케이션에만 전달합니다. 호스트 모드에서 80번과 443번 포트를 자기 리전의 서버에 직접 공개합니다. Caddy가 전달 대상을 환경 변수에서 읽으므로 공통 설정 하나로 충분합니다.

example.com {
    reverse_proxy {$UPSTREAM}:80
}
docker config create caddyfile ./Caddyfile
for r in eu us asia; do
  docker service create --name edge-$r --network appnet \
    --constraint node.labels.region==$r \
    --env UPSTREAM=web-$r \
    --config source=caddyfile,target=/etc/caddy/Caddyfile \
    --publish mode=host,target=80,published=80 \
    --publish mode=host,target=443,published=443 \
    caddy:2
done

모든 리전에는 같은 도메인의 TLS 인증서가 필요합니다. 따라서 인증서는 DNS 챌린지로 발급받으세요. DNS 챌린지는 DNS 레코드가 지금 어느 리전을 가리키든 상관없이 작동합니다. 이를 위해 Caddy에는 사용하시는 DNS 제공업체용 모듈이 필요합니다. 프록시의 기초는 nginx를 리버스 프록시로 설정하기 글에서 다룹니다.

11단계: 페일오버를 갖춘 Geo-DNS 설정하기

마지막 구성 요소는 사용자를 올바른 리전으로 안내합니다. 지역 기반 라우팅과 헬스 체크를 지원하는 DNS 서비스가 유럽 사용자는 프랑크푸르트암마인으로, 미주 사용자는 미국 동부 해안으로, 아시아 사용자는 싱가포르로 보냅니다. 한 리전이 멈추면 헬스 체크가 30~60초 안에 이를 감지하고, 그 리전의 사용자를 가장 가까운 정상 리전으로 보냅니다. 리졸버가 변경을 빨리 반영하도록 레코드의 유효 시간(TTL)을 60초로 설정하세요. 헬스 체크 없이 A 레코드를 여러 개 두는 것은 임시방편일 뿐입니다. 브라우저는 다음 주소를 시도하는 경우가 많지만 모든 클라이언트가 그렇지는 않으며, 멈춘 리전도 응답에 그대로 남습니다.

현실적으로 계산해 보세요. 장애가 난 뒤 전환이 끝나기까지는 점검 주기에 TTL을 더한 시간, 이 예시에서는 1~2분이 걸리고, 그동안 해당 리전 사용자 중 일부는 여전히 멈춘 위치로 접속합니다. 실패한 요청을 잠시 뒤 다시 시도하는 애플리케이션과 앱은 사용자가 거의 눈치채지 못하게 이 시간을 넘깁니다.

12단계: 리전 장애 테스트하기

한 번도 연습하지 않은 페일오버는 실제 상황에서 제대로 작동하는 경우가 드뭅니다. 리전의 노드를 운영에서 빼서 그 리전의 장애를 재현하고, 클러스터와 DNS가 어떻게 반응하는지 지켜보세요.

docker node update --availability drain swarm-asia
docker node ls
docker service ls
docker node update --availability active swarm-asia

그 리전의 서비스는 배치 규칙으로 해당 노드에 묶여 있으므로 다른 곳으로 옮겨 가지 않고 일시 중지됩니다. 그 리전의 사용자는 DNS 페일오버가 넘겨받습니다. 따라서 이 테스트에서는 무엇보다 헬스 체크가 그 리전을 응답에서 빼는지, 인접 리전이 늘어난 부하를 감당하는지 확인하세요. 더 강도 높은 테스트를 하려면 WireGuard를 중지하는 식으로 노드를 네트워크에서 완전히 분리합니다. 큰 변경을 한 뒤에는, 그리고 적어도 분기마다 한 번은 테스트를 반복하세요.

데이터: 고가용성에서 가장 어려운 부분

Docker Swarm은 컨테이너를 복제할 뿐, 데이터는 복제하지 않습니다. 볼륨은 언제나 컨테이너가 실행되는 노드에 있습니다. 그래서 웹 프런트엔드나 API처럼 상태가 없는 서비스는 어느 리전에서든 문제없이 운영할 수 있지만, 데이터를 다루는 모든 것에는 별도의 복제가 필요합니다.

대륙 간 데이터베이스 복제

데이터베이스에는 자체 복제 기능이 있으며, 여러 데이터센터에 걸친 공유 스토리지보다는 언제나 이 기능을 쓰는 편이 낫습니다. 대륙 간에는 한 리전에 프라이머리 인스턴스를 두고 나머지 두 리전에 비동기 복제본을 두는 방식이 검증되어 있습니다. PostgreSQL이라면 스트리밍 복제에 Patroni 같은 자동 전환 도구를 더하고, MariaDB와 MySQL이라면 내장 복제 기능을 씁니다. 이렇게 하면 읽기 요청은 각 리전이 로컬에서 처리하고, 쓰기는 프라이머리 인스턴스로 갑니다. 솔직히 말하면, 비동기라는 것은 프라이머리 리전이 멈출 경우 마지막 몇 초 동안의 쓰기가 유실될 수 있다는 뜻이기도 합니다. 전 세계에서 쓰기를 하면서도 아무것도 잃고 싶지 않다면 CockroachDB나 YugabyteDB처럼 여러 리전을 위해 만들어진 데이터베이스를 선택합니다. Swarm이 데이터베이스 노드를 데이터 없이 옮기는 일이 없도록, 데이터베이스 노드는 배치 규칙으로 자기 리전에 고정하세요.

docker service create --name db-asia --constraint node.labels.region==asia ...

파일과 업로드

업로드된 파일은 로컬 볼륨이 아니라, 두 번째 리전으로 복제되는 S3 호환 오브젝트 스토리지나 직접 운영하는 복제형 스토리지 시스템에 두어야 합니다. 대륙을 가로지르는 NFS 같은 네트워크 파일 시스템은 느리고, 그 자체가 단일 장애 지점이 됩니다.

세션과 캐시

애플리케이션이 세션을 컨테이너의 메모리에 저장하면, 전환될 때 사용자의 로그인이 풀립니다. 세션은 복제되는 데이터베이스나 Redis 같은 복제형 캐시에 두거나, 각 리전이 스스로 검증할 수 있는 서명된 토큰을 사용하세요.

백업은 여전히 필수입니다

복제는 위치 장애는 막아 주지만 오류는 막지 못합니다. 실수로 삭제한 레코드는 몇 초 뒤 모든 리전에서 삭제됩니다. 그러므로 서버 백업 전략 글에서 설명한 것처럼, 독립된 장소에 두는 정기적이고 검증된 백업은 모든 고가용성 아키텍처에 반드시 포함되어야 합니다.

운영: 업데이트, 유지보수, 모니터링

리전별 순차 업데이트

새 버전은 모든 리전에 동시에 배포하지 말고 차례로 배포하며, 다음 리전으로 넘어가기 전에 각 리전을 잠시 지켜보세요. 그러면 어떤 헬스 체크도 잡아내지 못하는 오류가 있더라도 영향은 많아야 한 리전에 그치고, 나머지 두 리전이 그 리전의 사용자에게 계속 서비스를 제공합니다.

for r in asia us eu; do
  docker service update --image registry.example.com/web:1.1 web-$r || break
  sleep 300
done

업데이트가 실패하면 9단계의 설정 덕분에 Swarm이 자동으로 되돌리고, 명령이 오류를 보고하는 즉시 || break가 반복문을 끝냅니다. 나중에야 문제가 드러난 업데이트는 docker service rollback web-asia로 직접 되돌립니다.

리전 유지보수

서버를 재시작하거나 업데이트해야 한다면 docker node update --availability drain으로 운영에서 빼되, 그 전에 DNS 페일오버가 해당 사용자들을 인접 리전으로 돌리도록 합니다. 유지보수가 끝나면 --availability active로 다시 투입합니다. 매니저 두 개가 동시에 빠지는 일이 없도록, docker node ls에서 세 개 모두 다시 접속 가능 상태로 표시될 때까지 기다린 뒤 다음 매니저로 넘어가세요.

외부에서 하는 모니터링

각 리전을 개별적으로, 그리고 클러스터 바깥에서 모니터링하세요. 진입점의 접속 가능 여부, 서비스의 헬스 체크, 매니저의 상태, 데이터베이스의 복제 지연, 인증서와 도메인의 만료일을 확인해야 합니다. 리전 하나 전체가 아무 응답이 없을 때에도 알림은 반드시 도착해야 합니다. 개별 서버에서 이를 구성하는 방법은 서버 모니터링 설정하기 글에서 보여 드립니다.

비밀 정보를 안전하게 배포하기

비밀번호와 키는 환경 변수나 Compose 파일이 아니라 Docker Secrets에 두어야 합니다. Docker Secrets는 Raft 로그에 암호화되어 저장되며, 명시적으로 할당한 서비스에만 전달됩니다.

printf '%s' '데이터베이스_비밀번호' | docker secret create db_password -
docker service update --secret-add db_password web-eu

여러 대륙에 걸친 Swarm에 KernelHost가 맞는 이유

여러 대륙에 걸친 클러스터는 서버 한 대를 쓸 때와는 다른 조건을 제공업체에 요구합니다. 실무에서 결정적인 요소는 다음과 같습니다.

요구 사항중요한 이유KernelHost에서는
여러 대륙의 위치쿼럼에는 독립된 위치 세 곳이 필요하고, 사용자는 짧은 경로를 원함프랑크푸르트암마인과 유럽, 북미, 아시아 태평양의 여러 위치를 한 업체에서 제공
무제한 트래픽WireGuard, 오버레이 네트워크, 데이터베이스 복제가 대륙 간 트래픽을 끊임없이 만들어 냄용량 제한 없는 무제한 트래픽 VPS
DDoS 방어모든 진입점은 외부에서 접속할 수 있으므로 공격 대상이 됨모든 위치에 포함, 핵심 위치인 프랑크푸르트암마인에서는 3.2 Tbps Arbor 실시간 필터링, null-routing 없음
완전한 루트 권한WireGuard, 방화벽, Docker Engine에는 완전한 제어권이 필요함모든 KVM 루트 서버와 전용 서버에서 제공
빠른 개통대체 노드와 테스트 리전을 몇 분 안에 준비할 수 있어야 함프랑크푸르트암마인에서 약 30초, 다른 위치에서는 대개 몇 분
노드별 약정 없음필요에 따라 노드를 늘리고 줄임PrePaid, 최소 이용 기간 없음, 설치비 없음
자동화새 노드를 스크립트로 만들 수 있어야 함KernelHost API로 주문과 제어

가격이 포함된 전체 클라우드 요금제 개요와 대형 클라우드 제공업체와의 비용 비교는 클라우드 서버 임대 페이지에서 보실 수 있습니다.

흔한 실수와 피하는 방법

  • 매니저를 두 위치에만 둔다. 과반수가 있는 위치가 멈추면 클러스터가 정지합니다. 해결책: 위치 세 곳, 1-1-1 또는 2-2-1 분산.
  • 매니저 수가 짝수다. 매니저 네 개는 세 개보다 더 많은 장애를 견디지 못하면서 합의에 드는 부담만 늘립니다. 해결책: 3, 5 또는 7개.
  • 요청이 대륙 사이를 오간다. 모든 리전에 걸친 서비스 주소 하나가 사용자를 지구 반대편까지 보냅니다. 해결책: 리전마다 서비스 하나와 진입점 하나.
  • Swarm 포트가 외부에서 접근 가능하다. 2377번 포트와 오버레이 네트워크는 절대 공개 인터넷에 노출되어서는 안 됩니다. 해결책: WireGuard를 통해서만 허용하고, 외부에는 80, 443, 그리고 다른 노드를 위한 WireGuard만 엽니다.
  • MTU를 잊었다. 큰 응답은 멈추고 작은 응답은 잘 됩니다. 해결책: WireGuard MTU가 1420이면 오버레이 MTU는 1370.
  • 컨테이너 포트 보호를 ufw에 맡긴다. Docker는 공개된 포트에 대해 ufw를 우회합니다. 해결책: 진입점만 공개합니다.
  • 데이터베이스를 복제 없이 볼륨에 둔다. 리전이 멈추면 데이터에 접근할 수 없습니다. 해결책: 데이터베이스 복제, 리전에 고정된 배치.
  • 모든 리전을 동시에 업데이트한다. 그러면 오류 하나가 전 세계 모든 사용자에게 영향을 줍니다. 해결책: 리전별로 차례로 배포합니다.
  • DNS TTL이 길다. TTL이 하루면 페일오버가 다음 날에야 적용됩니다. 해결책: 60초.
  • 백업 대신 복제에 기댄다. 오류도 정상 데이터와 똑같이 복제됩니다. 해결책: 독립된 장소에 검증된 백업을 추가로 둡니다.

핵심 요약

  • 세 대륙에 걸친 Docker Swarm은 100% 업타임을 목표로 설계됩니다. 위치 하나나 대륙 하나 전체가 멈춰도 애플리케이션은 계속 동작합니다.
  • 누구도 가용성을 절대적으로 보장할 수는 없습니다. 남은 위험은 DNS, 결함 있는 업데이트, 데이터베이스, 인증서이며, 각각에 대한 대응책이 있습니다.
  • 세 위치에 매니저 세 개가 최소 조건이며, 위치 두 곳으로는 장애에 견디는 쿼럼을 만들 수 없습니다.
  • 모든 요청은 자기 리전 안에 머뭅니다. 리전마다 서비스 하나와 진입점 하나를 두고, 헬스 체크와 짧은 TTL을 갖춘 Geo-DNS를 더합니다.
  • WireGuard가 클러스터 트래픽을 암호화하고, Swarm 포트는 외부에서 보이지 않으며, 오버레이 MTU는 1370으로 낮춥니다.
  • Swarm은 컨테이너를 복제할 뿐 데이터는 복제하지 않습니다. 데이터베이스에는 자체 복제가 필요하고, 백업은 여전히 필수입니다.
  • KernelHost는 유럽, 북미, 아시아 태평양의 위치, 무제한 트래픽, 모든 위치의 DDoS 방어, 최소 이용 기간 없는 PrePaid 정산을 제공합니다.

자주 묻는 질문

Docker Swarm으로 100% 업타임을 보장할 수 있나요?
세 대륙에 걸친 Docker Swarm은 100% 업타임을 목표로 설계됩니다. 서버 한 대, 데이터센터 한 곳, 대륙 하나 중 어느 것도 혼자서는 애플리케이션을 멈출 수 없기 때문입니다. 그래도 누구도 가용성을 절대적으로 보장할 수는 없으며, 대형 클라우드 제공업체도 예외가 아닙니다. 남은 위험은 DNS, 결함 있는 업데이트, 데이터베이스, 인증서 같은 공통 의존 요소입니다. 이에 대해서는 자동 롤백을 갖춘 헬스 체크, 리전별 순차 업데이트, 데이터베이스 복제, 모니터링이 도움이 됩니다.
고가용성 Docker Swarm에는 매니저 노드가 몇 개 필요한가요?
최소 세 개이며, 서로 독립된 세 위치에 나누어 둡니다. 매니저는 Raft 합의로 클러스터 상태를 유지하며, 모든 변경에 과반수가 필요합니다. 매니저 세 개는 장애 하나를, 다섯 개는 둘을, 일곱 개는 셋을 견딥니다. Docker는 홀수 개를 두고 최소 세 개의 가용 영역에 분산하도록 권장하며, 매니저가 세 개라면 1-1-1 비율입니다.
Docker Swarm에 데이터센터 두 곳으로는 왜 부족한가요?
위치가 두 곳이면 어느 한쪽에 매니저가 더 많을 수밖에 없기 때문입니다. 바로 그 위치가 멈추면 매니저 과반수가 사라지고, 클러스터는 재스케줄링도, 장애 보완도, 업데이트 배포도 할 수 없습니다. 실행 중인 컨테이너는 계속 동작하지만 클러스터는 아무 조치도 취할 수 없는 상태가 됩니다. 위치가 세 곳이 되어야 비로소 쿼럼이 어느 데이터센터의 장애든 견딥니다.
Swarm 매니저를 여러 대륙에 나누어 둘 수 있나요?
네. 북미, 유럽, 아시아에 매니저를 하나씩 둔 Swarm은 대륙 하나 전체의 장애를 견딥니다. 대신 클러스터 관리가 느려집니다. 모든 변경이 지연 시간 80~250밀리초의 장거리 구간을 거쳐 오는 확인을 기다리기 때문입니다. 각 요청이 자기 리전에서 응답되는 한, 즉 리전마다 서비스 하나와 진입점 하나를 두는 한 사용자에게는 문제가 되지 않습니다.
Docker Swarm에는 어떤 포트가 필요한가요?
Docker Swarm은 노드 사이에 클러스터 관리용 2377/TCP 포트, 노드 간 통신용 7946/TCP 및 UDP 포트, 오버레이 네트워크용 4789/UDP 포트가 필요합니다. 오버레이 네트워크의 내장 암호화를 사용한다면 IP 프로토콜 50(ESP)도 허용해야 합니다. 이 포트들은 어느 것도 공개 인터넷에 노출되어서는 안 되며, 노드 사이의 WireGuard 네트워크를 통해서만 오가게 하는 것이 가장 안전합니다.
WireGuard가 필요한가요, 아니면 암호화된 오버레이 네트워크로 충분한가요?
암호화된 오버레이 네트워크(--opt encrypted)는 컨테이너의 데이터 트래픽만 보호하며, Docker에 따르면 성능이 눈에 띄게 떨어집니다. 반면 WireGuard는 관리 트래픽과 노드 간 통신을 포함해 노드 사이의 모든 트래픽을 암호화하고, 모든 Swarm 포트를 인터넷에서 차단합니다. 여러 데이터센터에 걸친 클러스터에는 오버레이 MTU 1370바이트와 함께 WireGuard를 사용하시기를 권장합니다.
대륙 간 페일오버는 어떻게 작동하나요?
지역 기반 라우팅과 헬스 체크를 지원하는 DNS 서비스가 각 사용자를 가장 가까운 리전으로 보내고, 30~60초마다 그 리전의 진입점이 응답하는지 확인합니다. 한 리전이 멈추면 그 주소를 더 이상 내주지 않고 사용자를 가장 가까운 정상 리전으로 안내합니다. TTL이 60초라면 전환은 대개 1~2분 안에 끝납니다.
위치 장애가 나도 데이터베이스를 계속 쓸 수 있게 하려면 어떻게 하나요?
Docker가 아니라 데이터베이스 자체의 복제로 해결합니다. Swarm은 컨테이너를 복제할 뿐 볼륨은 복제하지 않습니다. 한 리전에 프라이머리 인스턴스를 두고 다른 리전에 복제본을 두는 방식이 검증되어 있으며, PostgreSQL이라면 자동 전환에 Patroni 등을 사용합니다. 대륙 간 복제는 비동기로 이루어지므로 비상시에는 마지막 몇 초 동안의 쓰기가 유실될 수 있습니다. 전 세계에서 손실 없이 써야 한다면 CockroachDB나 YugabyteDB처럼 여러 리전을 위한 데이터베이스를 사용합니다.
여러 위치에는 Docker Swarm과 Kubernetes 중 무엇이 좋을까요?
Docker Swarm은 구축과 운영이 훨씬 간단하고, 많은 애플리케이션에 충분합니다. Kubernetes는 자동화, 확장성, 생태계 면에서 더 많은 가능성을 제공하지만 더 많은 지식이 필요합니다. 여러 대륙에 걸칠 때 Kubernetes는 보통 리전마다 클러스터를 하나씩 두고 GitOps로 함께 배포하는 방식으로 운영하는 반면, Swarm은 클러스터 하나를 여러 대륙에 걸쳐 펼칠 수 있습니다.
세 대륙에 걸친 Swarm에는 어떤 KernelHost 위치가 적합한가요?
KernelHost는 프랑크푸르트암마인을 비롯해 유럽, 북미, 아시아 태평양의 여러 위치에서 서버를 제공합니다. 미국 내 세 곳, 캐나다, 런던, 스트라스부르, 바르샤바, 헬싱키, 싱가포르, 일본, 시드니, 뭄바이가 여기에 포함됩니다. 세 대륙을 위한 검증된 조합은 프랑크푸르트암마인, 미국 동부 해안, 싱가포르입니다. 모든 위치를 DDoS 방어, PrePaid 정산과 함께 한 업체에서 이용하실 수 있습니다.
세 대륙에 걸친 Docker Swarm의 비용은 얼마인가요?
기본적으로 리전마다 한 대씩 서버 세 대와 헬스 체크를 지원하는 DNS 서비스가 필요합니다. WireGuard, 오버레이 네트워크, 데이터베이스 복제가 위치 사이에 끊임없이 트래픽을 만들어 내므로 무제한 트래픽 요금제가 결정적입니다. 많은 대형 클라우드 제공업체에서는 바로 이 트래픽에 기가바이트당 추가 요금이 붙습니다. KernelHost의 무제한 트래픽 VPS는 용량 제한 없이, 최소 이용 기간과 설치비가 없는 PrePaid 방식으로 이용하실 수 있습니다.

Docker Swarm 고가용성 멀티 리전 WireGuard Geo-DNS 페일오버 Docker 클라우드