티스토리 뷰
도커 스웜을 사용하는 이유
보통 현업에서는 지금까지 한 것 처럼 호스트 하나로만 이루어져 있지 않습니다. 이유는 간단합니다. CPU나 메모리, 디스크 용량같이 자원이 부족한 경우에 스케일 아웃을 하여 서버를 병렬로 쭉 늘려나가는 것입니다. 그러나 여러 대의 서버를 하나의 자원 풀로 만드는 것은 쉬운 작업이 아닙니다. 새로운 서버나 컨테이너가 추가 됐을 때, 이를 발견 하는 작업부터 어떤 서버에 컨테이너를 할 것인가에 대한 스케줄러와 로드밸런서 문제, 클러스터 내의 서버가 다운 됐을 때 고가용성을 어떻게 보장할지 등이 문제로 남아있습니다.
이 때 문제를 해결할 솔루션 중 도커에서 공식하는 스웜이 있습니다.
스웜 클래식과 도커 스웜 모드
스웜 클래식과 스웜 모드는 여러 대의 도커 서버를 하나의 클러스터로 만들어 컨테이너를 생성하는 여러 기능을 제공합니다.
도커 스웜에는 위 처럼 두 가지 종류가 있습니다.
- 도커 버전 1.6 부이후 부터 사용할 수 있는 컨테이너로서의 스웜
- 도커 버전 1.12 이후부터 사용할 수 있는 스웜모드
스웜 클래식과 스웜 모드로 각각 명명하겠습니다.
둘 의 가장 큰 차이점은 목적입니다. 스웜 클래식은 여러 대의 서버를 하나의 지점에서 사용하도록 단일 접근점을 제공한다면, 스웜모드는 마이크로서비스 아키텍처의 컨테이너를 다루기 위한 클러스터링 기능에 초점을 맞추고 있습니다.
스웜 클래식은 docker run, ps 등 일반적인 도커 명령어와 도커 API로 클러스터의 서버를 제어하고 관리할 수 있는 기능을 제공하는데 반에 스웜 모드는 같은 컨테이너로 연결을 분산하는 로드밸런싱을 자체 지원하며 필요에 따라 오토 스케일링도 가능합니다.
또 다른 차이점은 분산 코디네이터, 에이전트와 같은 클러스터 툴이 별도로 구동되느냐 입니다. 스웜 클래식은 분산 코디네이터, 에이전트 등이 별도로 실행되야 하지만 스웜 모드는 클러스터링을 위한 모든 도구가 도커 엔진 자체에 내장돼있기 때문에 더욱 쉽게 서버 클러스터를 구축할 수 있습니다.
스웜 모드
도커 스웜 모드는 별도의 설치 과정이 필요하지 않습니다. docker info 명령어를 통해 도커 엔진의 스웜 모드 클러스터 정보를 확인할 수 있습니다.
docker info | grep Swarm
Swarm: inactive
현재는 사용 중이 아니므로 비활성 상태입니다.
도커 스웜 모드의 구조
스웜 모드는 매니저 노드와 워커 노드로 구성돼 있습니다. 워커 노드는 실제로 컨테이너가 생성되고 관리되는 도커 서버이고 매니저 노드는 워커노드를 관리하기 위한 서버입니다. 매니저 노드에도 컨테이너가 생성될 수 있으며 기본적으로 워커노드의 역할을 포함합니다.
매니저 노드는 1개 이상이 필수입니다. 반면에 워커노드는 없을 수도 있습니다. 매니저 노드가 워커 노드의 역할도 포함하고 있기 때문에 매니저 노드만으로 스웜 클러스터를 구성할 수 있기 때문입니다. 그러나 워커노드와 매니저 노드는 구분하는 것을 권장합니다.
또한 매니저 노드는 안정성을 위해 한 개 이상으로 다중화하는 것을 권장합니다. (만약 매니저 노드에 문제가 생겨 작동하지 않으면 매니저 노드가 복구 될 때 까지 클러스터의 운영을 중단하기 때문에 치명적인 문제가 발생하는 것을 방지해야하기 때문입니다.)
만약 네트워크 파티셔닝과 같은 현상이 발생했을 때에는 짝수 개의 매니저로 구성한 클러스터는 운영이 중단될 수 있기 때문에 홀 수개로 구성하여 과반 수 이상이 유지되는 쿼럼 매니저에서 운영을 계속할 수 있습니다. 그렇기 때문에 홀 수 개로 구성하는 것이 권장됩니다.
뗏목 알고리즘이란?
뗏목 합의 알고리즘을 이해하기 위해서는 다음 용어들을 먼저 이해해야 합니다.
합의 알고리즘(Consensus Algorithm)
다수의 참여자들이 동일된 의사 결정을 하기 위해 사용되는 알고리즘입니다.
한 서버에서 명령을 실행 하기 위해 다른 서버에게 합의를 요청하게 된다. 다른 서버들은 자신의 서버에 문제가 없는지 판단하고, 해당 명령을 실행하여 모두 동일한 상태를 유지하게 됩니다.
장애 허용 분산 시스템(fault-tolerance distributed system)
시스템 중 일부에 오류가 발생해도 정상적으로 작동할 수 있도록 하는 시스템입니다.뗏목의 일부 나무가 손상되어도 제 기능을 유지하는 것과 같습니다.
먼저 한 서버가 명령을 실행할 때 명령을 실행해도 되는지 다른 서버에게 합의를 구합니다.
합의를 한 서버는 같은 명령을 실행하고 동일한 상태를 유지하게 됩니다. 이미 합의를 한 서버는 명령 실행을 위해 다시 합의를 구할 필요가 없습니다.
스웜 모드 서비스
지금 까지 계속 사용해온 도커 명령어의 제어 단위는 컨테이너입니다. 하지만 스웜에서는 Service 단위로 제어합니다.
서비스는 같은 이미지에서 생성된 컨테이너의 집합이며, 서비스를 제어하면 해당 서비스 내의 컨테이너에 같은 명령이 수행 됩니다. 서비스 내에 컨테이너는 1개 이상 존재할 수 있으며, 이러한 컨테이너들을 태스크라고 합니다.
예를 들어, 위 그림 처럼 컨테이너 수를 3개로 설정했다고 가정해보겠습니다. 스웜 스케줄러는 서비스의 정의에 따라 컨테이너를 할당할 적합한 노드를 선정하고, 해당 노드에 컨테이너를 분산해서 할당합니다.
이 처럼 함께 생성된 컨테이너를 레플리카라고 하며, 서비스에 설정된 레플리카 수 만큼의 컨테이너가 스웜 클러스터 내에 존재해야합니다. 스웜은 서비스의 컨테이너들에 대한 상태를 계속 확인하고 있다가 서비스 내에 정의된 레플리카의 수 만큼 컨테이너가 스웜 클러스터에 존재하지 않으면 새로운 컨테이너 레플리카를 생성합니다.
또한 서비스 내의 컨테이너 중 일부가 작동을 멈춰 정지한 상태로 있다면 이 또한 레플리카의 수를 충족하지 못하는 것으로 판단해 스웜 매니저는 새로운 컨테이너를 클러스터에 새롭게 생성합니다.
이걸 그림으로 보면 다음과 같습니다.
서비스는 롤링 업데이트도 가능하여 지속적으로 컨테이너를 무중단 배포로 운영이 가능합니다.
도커 스웜 네트워크
스웜 모드는 도커 일반 네트워크랑은 조금 다른 구조로 사용합니다. 스웜 모드는 여러개의 도커 엔진에 같은 컨테이너를 분산해서 할당하기 때문에 각 도커 데몬의 네트워크가 하나로 묶인 이른바 네트워크 풀이 필요합니다. 이뿐만 아니라 서비스를 외부로 노출했을 때 어느 노드로 접근하더라도 해당 서비스의 컨테이너에 접근할 수 있게 라우팅 기능이 필요합니다. 이러한 네트워크 기능은 스웜모드가 자체적으로 지원하는 네트워크 드라이버를 통해 사용할 수 있습니다.
다음 명령어를 확인해봅시다.
docker network ls
NETWORK ID NAME DRIVER SCOPE
2c6fa542ee50 bridge bridge local
1b46b248a58a cw_network bridge local
386a9ac376cc docker_gwbridge bridge local
b94b21500103 host host local
7x9awuna7d4g ingress overlay swarm
6366c0aa0d74 none null local
기존 네트워크에 docker_gwbridge와 ingress 네트워크가 생성된 것을 볼 수 있습니다. docker_gwbridge 네트워크는 스웜에서 오버레이 네트워크를 사용할 때 사용되고 ingress 네트워크는 로드 밸런싱과 라우팅 메시에 사용됩니다.
ingress 네트워크
ingress 네트워크는 스웜 클러스터를 생성하면 자동으로 등록되는 네트워크로서, 스웜모드를 사용할 때만 유효합니다.
docker network ls 명령어를 입력하면 SCOPE를 확인할 수 있습니다.
docker network ls | grep ingress
7x9awuna7d4g ingress overlay swarm
ingress 네트워크는 어떤 스웜 노드에 접근하더라도 서비스 내의 컨테이너에 접근할 수 있게 설정하는 라우팅 메시를 구성하고, 서비스 내의 컨테이너에 대한 접근을 라운드 로빈 방식으로 분산하는 로드밸런싱을 담당합니다.
예를 들어 하나의 앱을 실행했다고 합시다.
docker service create --name hostname \
-p 80:80 \
--replicas=4 \
alicek106/book:hostname
4개의 컨테이너가 있는 환경에서 docker ps 명령어로 컨테이너의 ID를 확인하면 다음과 같습니다.
manager: docker ps --format "table {{.ID}}\t{{.Status}}\t{{.Image}}"
CONTAINER ID. STATUS. IMAGE
anything1 3 a minute aliceK106/book@sha256:b3d3ac ...
worker1: docker ps --format "table {{.ID}}\t{{.Status}}\t{{.Image}}"
CONTAINER ID. STATUS. IMAGE
anything2 3 a minute aliceK106/book@sha256:b3d3ac ...
anything3 3 a minute aliceK106/book@sha256:b3d3ac ...
worker2: docker ps --format "table {{.ID}}\t{{.Status}}\t{{.Image}}"
CONTAINER ID. STATUS. IMAGE
anything4 3 a minute aliceK106/book@sha256:b3d3ac ...
각각의 컨테이너는 매니저에 1개 worker1 2개 그리고 worker2 1개씩 할당 되었습니다.
브라우저에 접근한다면 호스트의 이름이 각각 다르다는걸 알 수 있습니다. 계속 접근을 시도하다 보면 4개 모두 알 수 있습니다.
하지만 이렇게 ingress로만 네트워크를 외부 노출할 수 있는 것이 아닙니다.
docker service create \
--publish mode=host, target=80, published=8080, protocol=tcp \
--name web\
nginx
다음과 같이 직접 컨테이너의 80번 포트에 연결할 수 있습니다.
그러나 ingress 네트워크를 사용하지 않고 노출할 경우 어느 호스트에서 컨테이너가 생성될지 알 수 없어 포트 및 서비스 관리가 어렵다는 단점이 있습니다. 따라서 가급적 ingress 네트워크를 이용하는 것이 좋습니다.
오버레이 네트워크
스웜 클러스터 내의 컨테이너가 할당 받는 IP주소는 어떻게 되어있는지 살펴보겠습니다.
ingress 네트워크는 오버레이 네트워크 드라이버를 사용합니다. 오버레이 네트워크는 여러개의 도커 데몬을 하나의 네트워크 풀로 만드는 네트워크 가상화 기술의 하나로서, 도커에 오버레이 네트워크를 적용하면 여러 도커 데몬에 존재하는 컨테이너가 서로 통신할 수 있습니다.
즉, 여러 개의 스웜 노드에 할당된 컨테이너는 오버레이 네트워크의 서브넷에 해당하는 IP 대역을 할당받고 이 IP를 통해 서로 통신할 수 있는 것입니다.
사용자 정의 오버레이 네트워크
스웜 모드는 자체 키-값 저장소를 갖고 있으므로 별도의 구성 없이 사용자 정의 오버레이 네트워크를 생성할 수 있습니다.
docker network create \
--subnet 10.0.0.9.0/24 \
-d overlay \
myoverlay
docker network ls 명령어로 확인 가능합니다.
서비스 디스커버리
같은 컨테이너가 여러개 있을 때 가장 쟁점이 되는 부분은 새로 생성된 컨테이너의 발견 없어진 컨테이너 감지입니다. 스웜 모드는 이를 주키퍼 같은 분산 코디네이터 없이 자체적으로 지원합니다.
예를 들어 A 서비스가 B 서비스의 컨테이너를 사용할 때 scale out을 하여 컨테이너를 2개에서 3개로 늘렸을 때 A는 어떻게 찾을까요 ?
결론 부터 말하면 스웜모드에서는 B라는 이름으로 서비스 B의 컨테이너에 모두 접근할 수 있습니다. 즉, 알 필요 없이 서비스의 이름만 알면 되는 것입니다.
각 서비스 이름은 그러면 어떻게 각 서비스의 IP로 변환 된 것일까요? 서비스 이름이 3개의 IP를 가지는 것이 아니라 서비스의 VIP(가상 IP)를 가지는 것입니다. VIP는 다음 명령어로 확인할 수 있습니다.
docker service inspect --format {{.Endpoint.VirtualIPs}} server
[{74mgwebwciysddx3rns0poam9a 10.0.1.2/24}]
server 서비스의 VIP는 10.0.1.2 입니다. 스웜 모드가 활성화된 도커 엔진의 내장 DNS 서버는 server라는 호스트 이름을 10.0.1.2라는 IP로 변환합니다. 그리고 이 IP는 컨테이너의 네트워크 네임스페이스 내부에서 실제 server 서비스의 컨테이너의 IP로 포워딩 됩니다.
VIP 방식이 아닌 도커 내장 DNS 서버를 기반으로 라운드 로빈을 사용할 수 있지만 이는 캐시 문제로 인해 서비스 발견이 정상적으로 동작하지 않을 때가 있으니 권장하진 않습니다.
스웜 모드 볼륨
도커 데몬 명령어 중 run 명령어에서 -v 옵션을 사용할 때 호스트와 디렉터리를 공유하는 경우와 볼륨을 사용하는 경우에 대한 구분은 딱히 없었습니다.
# 호스트와 디렉터리를 공유하는 경우
docker run -i -t --name host_dri_case -v /root:/root ubuntu:14.04
# 도커 볼륨을 사용하는 경우
docker run -i -t --name volume_case -v myvolume:/root ubuntu:14.04
이러한 run의 -v 옵션과 같은 기능을 스웜 모드에서도 사용할 수 있습니다. 그러나 스웜 모드에서는 도커 볼륨을 사용할지, 호스트 외 디렉터리를 공유할지를 좀 더 명확히 해 볼륨을 사용합니다.
volume 타입의 볼륨 생성
스웜 모드에서 도커 볼륨을 사용하는 서비스를 생성하려면 서비스를 생성할 때 —mount 옵션의 type 값에 volume을 지정합니다.
docker service create --name ubuntu \
--mount type=volume,source=myvol,target=/root \
ubuntu:14.04
ping docker.com
도커 데몬에 이미 source에 해당하는 이름의 볼륨이 있으면 해당 볼륨을 사용하지만, 없으면 새로 생성합니다. 이 때 source 옵션을 명시하지 않으면 임의의 16진수로 구성된 익명의 이름을 가진 볼륨을 생성합니다.
docker volume ls
DRIVER. VOLUME NAME
local 5f84312312fd5dfffArfwaer...
서비스의 컨테이너에서 볼륨에 공유할 디렉터리에 파일이 이미 존재한다면 이 파일은 볼륨에 복사되고, 호스트에서 별도의 공간을 차지하게 됩니다. 그러나 서비스를 생성할 때 volume-nocopy 옵션을 추가하면 컨테이너의 파일들이 볼륨에 복사되지 않도록 설정이 가능합니다.
bind 타입의 볼륨 생성
바인드 타입은 호스트와 디렉터리를 공유할 때 사용됩니다. 볼륨 타입과는 달리 공유될 호스트의 디렉터리를 설정해야 하므로 source 옵션이 필수 입니다.
docker service create --name ubuntu \
--mount type=bind,source=/root/host,target=/root/container \
ubuntu:14.04 \
ping docker.com
스웜 모드에서 볼륨의 한계점
스웜 클러스터에서 볼륨을 사용하기란 까다롭습니다. 서비스를 할당받을 수 있는 모든 노드가 볼륨 데이터를 가지고 있어야 하기 때문입니다. 스웜 매니저에 내장된 스케줄러가 컨테이너를 할당할 때 어느 노드에 할당해도 서비스에 정의된 볼륨을 사용할 수 있어야 합니다. 따라서 여러 개의 도커 데몬을 관리해야하는 스웜모드에서는 도커 볼륨, 또는 호스트와의 볼륨 사용이 적합하지 않은 기능일 수 있습니다.
서비스의 컨테이너가 각 노드에 할당될 때 퍼스스턴트 스토리지를 마운트해 사용하면 노드에 볼륨을 생성하지 않아도 되며, 컨테이너가 어느 노드에 할당되는 컨테이너에 필요한 파일을 읽고 쓸 수 있습니다.
또는 각 노드에 라벨을 붙여 서비스에 제한을 설정하는 방법도 있습니다. 노드에 라벨을 설정해 특정 서비스의 동작에 필요한 볼륨이 존재하는 노드에만 컨테이너를 할당할 수 있게 설정하는 것입니다. 하지만 근본적인 해결책은 아닙니다.
노드 AVAILABILITY 변경하기
구축한 클러스터의 노드를 확인하면 다음과 같은 결과를 볼 수 있습니다.
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANGER STATUS
18cnfsafi32da swarm-worker1 Ready Active
723dadddja34b swarm-worker2 Ready Active
31casfd94advx swarm-worker3 Ready Active
54dasdawi432c swarm-manager Ready Active Leader
Active
Active 상태는 새로운 노드가 스웜 클러스터에 추가되면 기본적으로 설정되는 상태로서 노드가 서비스의 컨테이너를 할당받을 수 있음을 의미합니다.
docker node update \
--availability active \
swarm-worker1
위 명령어로 변경이 가능합니다.
Drain
이 상태에선 스웜 매니저의 스케줄러는 컨테이너를 해당 노드에 할당하지 않습니다. 일반적으로 매니저 노드에 설정하는 상태지만, 노드에 문제가 생겨 일시적으로 사용하지 않는 상태로 설정해야 할 때도 자주 사용됩니다.
docker node update \
--availability drain \
swarm-worker1
위 명령어를 사용하여 바꿀 수 있습니다.
Pause
Pause 상태는 서비스의 컨테이너를 더는 할당받지 않습니다. Drain과 다르게 실행 중인 컨테이너가 중지되지 않습니다.
docker node update \
--availability pause \
swarm-worker1
노드 라벨 추가
라벨은 키 값 형태로 존재합니다. 특정 노드에 라벨을 추가하면 서비스를 할당할 때 컨테이너를 생성할 노드의 그룹을 선택하는 것이 가능합니다.
ex) swarm-worker1은 HDD스토리지를 사용하고 있어 storage=hdd라는 라벨을, swarm-worker2는 SSD 스토리지를 사용하고 있어 storage=sdd라는 라벨을 설정할 수 있습니다.
이러한 라벨을 이용해서 서비스를 생성할 때 storage=ssd로 설정해 swarm-worker1에만 컨테이너를 할당할 수 있습니다.
서비스 제약 설정
- node.labels 제약 조건
docker service create --name label_test \
--constraint 'node.labels.storage == ssd' \
--replicas=5 \
ubuntu:14.04 \
ping docker.com
위 명령어로 storage 키의 값이 ssd로 설정된 노드에 서비스 컨테이너를 할당합니다. LABEL key를 찾지 못하면 서비스는 생성되지 않습니다.
- node.id 제약 조건
docker node ls | grep swarm-worker2
2fkljs3dawk231s swarm-worker2 Ready Active
docker service create --name label_test2 \
--constraint 'node.id == 2fkljs3dawk231s' \
--replicas=5 \
ubuntu:14.04 \
ping docker.com
노드의 ID를 명시에 서비스의 컨테이너를 할당할 노드를 선택합니다.
그 외에도 hostname이나 role로도 제약조건을 설정할 수 있습니다.
'docker' 카테고리의 다른 글
[Docker swarm으로 고가용성 서버 구축하기 - 2] Docker swarm 배포하기 (0) | 2023.08.05 |
---|---|
[Docker swarm으로 고가용성 서버 구축하기 - 1] 클러스터와 부하분산 (0) | 2023.07.30 |
[Docker 3주차] Docker Engine 2부 (1) | 2023.02.18 |
[도커 스터디 2주차] Docker Engine 1부 (1) | 2023.02.17 |
docker란? (0) | 2022.06.17 |
- Total
- Today
- Yesterday
- Spring
- 취준
- 백엔드
- IT
- 면접준비
- 프로그래밍
- 게시판
- DevOps
- 동시성
- 개발
- 코딩
- 취업준비
- DB
- 개발자
- CS
- 면접 준비
- 프로젝트
- 취업
- Redis
- Kotlin
- 코드
- MySQL
- docker
- 인터뷰
- JPA
- 면접
- thread
- java
- 자바
- swarm
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |