스웜에 서비스 배포하기
스웜에 서비스 배포하기
스웜 서비스는 선언적 모델을 사용해요. 쉽게 말하면, 여러분이 "서비스가 이렇게 동작해야 한다"는 원하는 상태만 정의하면, Docker가 그 상태를 계속 유지해 주는 구조랍니다. 여기에는 컨테이너가 사용할 이미지 이름과 태그, 서비스에 참여하는 컨테이너 수, 외부에 노출할 포트, Docker 시작 시 자동 실행 여부, 재시작 시 동작 방식, 그리고 서비스가 실행될 노드의 조건(리소스 제한, 배치 선호도 등) 같은 정보가 포함돼요.
스웜 모드에 대한 개요는 Swarm mode key concepts를, 서비스 동작 방식에 대한 개요는 How services work를 참고하세요.
출처: 공식문서
본문
서비스 생성하기
별도 설정 없이 단일 레플리카 서비스를 만들려면 이미지 이름만 지정하면 됩니다. 아래 명령은 무작위로 생성된 이름과 게시 포트 없이 Nginx 서비스를 시작해요. 다만 이 상태로는 Nginx 서비스와 상호작용할 수 없으니 개념을 보여주는 예시라고 이해하세요.
$ docker service create nginx
서비스는 사용 가능한 노드에 스케줄링됩니다. 서비스가 제대로 생성되고 시작됐는지 확인하려면 docker service ls 명령을 사용하세요.
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
a3iixnklxuem quizzical_lamarr replicated 1/1 docker.io/library/nginx@sha256:41ad9967ea448d7c2b203c699b429abe1ed5af331cd92533900c6d77490e0268
생성된 서비스가 항상 바로 실행되지는 않아요. 이미지를 사용할 수 없거나, 여러분이 구성한 요구사항을 충족하는 노드가 없거나, 기타 다른 이유로 서비스가 pending(대기) 상태에 머물 수 있습니다. 자세한 내용은 Pending services를 확인하세요.
서비스에 이름을 지정하려면 --name 플래그를 사용합니다.
$ docker service create --name my_web nginx
독립 실행형 컨테이너와 마찬가지로, 이미지 이름 뒤에 명령을 추가하면 서비스의 컨테이너가 실행할 명령을 지정할 수 있어요. 아래 예시는 helloworld라는 서비스를 만들고, alpine 이미지를 사용해 ping docker.com 명령을 실행합니다.
$ docker service create --name helloworld alpine ping docker.com
이미지 태그도 지정할 수 있습니다. 다음 예시는 앞선 예시를 alpine:3.6 태그를 사용하도록 수정한 거예요.
$ docker service create --name helloworld alpine:3.6 ping docker.com
이미지 태그 해석에 대한 자세한 내용은 서비스가 사용할 이미지 버전 지정하기를 참고하세요.
스웜용 gMSA
참고
이 예시는 Windows 컨테이너에서만 동작합니다.
이제 Swarm에서는 Docker config를 gMSA 자격 증명 사양(credential spec)으로 사용할 수 있어요. 이는 Active Directory 인증 애플리케이션에 필요한 기능인데, 자격 증명 사양을 각 노드에 일일이 배포해야 하는 부담을 줄여줍니다.
아래 예시는 gMSA와 해당 자격 증명 사양(credspec.json)이 이미 존재하고, 배포 대상 노드가 gMSA에 맞게 올바르게 구성되어 있다고 가정해요.
자격 증명 사양으로 사용할 config를 만들려면 먼저 자격 증명 사양이 담긴 Docker config를 생성합니다.
$ docker config create credspec credspec.json
이제 credspec이라는 Docker config가 준비되었으니, 이 자격 증명 사양을 사용하는 서비스를 만들 수 있어요. --credential-spec 플래그에 config 이름을 지정하면 됩니다.
$ docker service create --credential-spec="config://credspec" <your image>
서비스는 시작할 때 gMSA 자격 증명 사양을 사용하지만, 일반적인 Docker config(--config 플래그로 전달하는 방식)와 달리 자격 증명 사양이 컨테이너에 마운트되지는 않아요.
프라이빗 레지스트리의 이미지로 서비스 생성하기
로그인이 필요한 프라이빗 레지스트리에 이미지가 있다면, 로그인 후 docker service create에 --with-registry-auth 플래그를 사용하세요. 이미지가 프라이빗 레지스트리인 registry.example.com에 저장되어 있다면 다음과 같이 명령을 실행합니다.
$ docker login registry.example.com
$ docker service create \
--with-registry-auth \
--name my_service \
registry.example.com/acme/my_image:latest
이렇게 하면 로컬 클라이언트의 로그인 토큰이 암호화된 WAL 로그를 통해 서비스가 배포되는 스웜 노드로 전달됩니다. 이 정보를 바탕으로 노드들은 레지스트리에 로그인해 이미지를 pull 할 수 있어요.
관리 서비스 계정에 자격 증명 사양 제공하기
Enterprise Edition 3.0에서는 Docker config 기능을 통해 gMSA(Group Managed Service Account) 자격 증명을 중앙 집중식으로 배포하고 관리할 수 있어 보안이 향상되었어요. Swarm은 이제 Docker config를 gMSA 자격 증명 사양으로 사용할 수 있게 되어, 자격 증명 사양을 사용 대상 노드에 배포하는 부담이 줄었습니다.
참고
이 옵션은 Windows 컨테이너를 사용하는 서비스에만 적용됩니다.
자격 증명 사양 파일은 런타임에 적용되므로, 호스트 기반 자격 증명 사양 파일이나 레지스트리 항목이 필요 없어요. gMSA 자격 증명이 작업자 노드의 디스크에 기록되지 않습니다. 컨테이너가 시작되기 전에 swarm kit 작업자 노드에서 실행되는 Docker Engine에 자격 증명 사양을 제공할 수 있고, gMSA 기반 config를 사용해 서비스를 배포하면 자격 증명 사양이 해당 서비스의 컨테이너 런타임에 직접 전달됩니다.
--credential-spec은 다음 형식 중 하나여야 해요.
file://<filename>: 참조하는 파일은 Docker 데이터 디렉터리의CredentialSpecs하위 디렉터리에 있어야 합니다. Windows에서 기본값은C:\ProgramData\Docker\예요. 예를 들어file://spec.json을 지정하면C:\ProgramData\Docker\CredentialSpecs\spec.json을 로드합니다.registry://<value-name>: 자격 증명 사양은 데몬 호스트의 Windows 레지스트리에서 읽습니다.config://<config-name>: config 이름은 CLI에서 자동으로 config ID로 변환됩니다. 지정된config에 포함된 자격 증명 사양이 사용돼요.
다음은 Active Directory(AD) 인스턴스에서 gMSA 이름과 JSON 내용을 가져오는 간단한 예시입니다.
$ name="mygmsa"
$ contents="{...}"
$ echo $contents > contents.json
배포 대상 노드가 gMSA에 맞게 올바르게 구성되어 있는지 확인하세요.
config를 자격 증명 사양으로 사용하려면 credpspec.json이라는 자격 증명 사양 파일로 Docker config를 생성합니다. config의 이름은 원하는 대로 지정할 수 있어요.
$ docker config create --label com.docker.gmsa.name=mygmsa credspec credspec.json
이제 이 자격 증명 사양을 사용하는 서비스를 만들 수 있습니다. --credential-spec 플래그에 config 이름을 지정하세요.
$ docker service create --credential-spec="config://credspec" <your image>
서비스는 시작할 때 gMSA 자격 증명 사양을 사용하지만, 일반적인 Docker config(--config 플래그로 전달하는 방식)와 달리 자격 증명 사양이 컨테이너에 마운트되지는 않아요.
서비스 업데이트하기
docker service update 명령을 사용하면 기존 서비스의 거의 모든 것을 변경할 수 있어요. 서비스를 업데이트하면 Docker는 해당 컨테이너를 중지하고 새 구성으로 다시 시작합니다.
Nginx는 웹 서비스이므로 포트 80을 스웜 외부의 클라이언트에 게시하는 것이 훨씬 좋겠죠. 서비스 생성 시 -p 또는 --publish 플래그로 지정할 수 있고, 기존 서비스를 업데이트할 때는 --publish-add 플래그를 사용해요. 이전에 게시했던 포트를 제거하는 --publish-rm 플래그도 있습니다.
앞선 섹션의 my_web 서비스가 아직 존재한다고 가정하고, 다음 명령으로 포트 80을 게시하도록 업데이트해 봅시다.
$ docker service update --publish-add 80 my_web
제대로 반영됐는지 docker service ls로 확인해 보세요.
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
4nhxl7oxw5vz my_web replicated 1/1 docker.io/library/nginx@sha256:41ad9967ea448d7c2b203c699b429abe1ed5af331cd92533900c6d77490e0268 *:0->80/tcp
포트 게시 방식에 대한 자세한 내용은 포트 게시하기를 참고하세요.
이미지 이름과 태그를 포함해 기존 서비스의 거의 모든 구성 세부 사항을 업데이트할 수 있어요. 생성 후 서비스 이미지 업데이트하기를 참고하세요.
서비스 제거하기
서비스를 제거하려면 docker service remove 명령을 사용합니다. docker service ls 명령의 출력에서 볼 수 있듯이 서비스 ID나 이름으로 제거할 수 있어요. 다음 명령은 my_web 서비스를 제거합니다.
$ docker service remove my_web
서비스 구성 세부 정보
이어지는 섹션들에서는 서비스 구성에 대한 세부 내용을 다룹니다. 모든 플래그나 시나리오를 다루지는 않는다는 점을 참고하세요. 서비스 생성 시 구성을 정의할 수 있는 거의 모든 경우에, 비슷한 방식으로 기존 서비스의 구성도 업데이트할 수 있어요.
명령줄 참조는 docker service create와 docker service update를 확인하거나, 해당 명령을 --help 플래그와 함께 실행해 보세요.
런타임 환경 구성하기
컨테이너의 런타임 환경에 대해 다음 옵션을 구성할 수 있습니다.
--env플래그로 환경 변수 설정--workdir플래그로 컨테이너 내 작업 디렉터리 설정--user플래그로 사용자 이름 또는 UID 설정
다음 서비스의 컨테이너들은 환경 변수 $MYVAR가 myvalue로 설정되고, /tmp/ 디렉터리에서 실행되며, my_user 사용자로 실행됩니다.
$ docker service create --name helloworld \
--env MYVAR=myvalue \
--workdir /tmp \
--user my_user \
alpine ping docker.com
기존 서비스가 실행하는 명령 업데이트하기
기존 서비스가 실행하는 명령을 업데이트하려면 --args 플래그를 사용할 수 있어요. 다음 예시는 helloworld라는 기존 서비스를 업데이트해서, 이전에 실행 중이던 명령 대신 ping docker.com 명령을 실행하도록 만듭니다.
$ docker service update --args "ping docker.com" helloworld
서비스가 사용할 이미지 버전 지정하기
이미지 버전에 대한 세부 정보 없이 서비스를 생성하면 서비스는 latest 태그가 붙은 버전을 사용해요. 원하는 결과에 따라 몇 가지 방법으로 특정 이미지 버전을 강제할 수 있습니다.
이미지 버전은 여러 방식으로 표현할 수 있어요.
- 태그를 지정하는 경우: 매니저(또는 content trust를 사용한다면 Docker 클라이언트)가 해당 태그를 digest로 해석합니다. 작업자 노드에 컨테이너 태스크 생성 요청이 도착하면, 작업자 노드는 태그가 아닌 digest만 보게 돼요.
$ docker service create --name="myservice" ubuntu:16.04
ubuntu:16.04 같은 일부 태그는 특정 릴리스를 나타내며, 시간이 지나도 거의 항상 안정적인 digest로 해석됩니다. 가능하면 이런 종류의 태그를 사용하는 것이 좋아요.
latest나 nightly 같은 다른 유형의 태그는 이미지 작성자가 태그를 얼마나 자주 업데이트하느냐에 따라 digest가 자주 바뀔 수 있어요. 서비스 레플리카 태스크들이 서로 다른 이미지 버전을 사용하지 않도록, 자주 업데이트되는 태그로 서비스를 실행하는 것은 권장하지 않습니다.
- 버전을 전혀 지정하지 않는 경우: 관례적으로 이미지의
latest태그가 digest로 해석됩니다. 작업자 노드는 서비스 태스크를 생성할 때 이 digest의 이미지를 사용해요.
따라서 다음 두 명령은 동일합니다.
$ docker service create --name="myservice" ubuntu
$ docker service create --name="myservice" ubuntu:latest
- digest를 직접 지정하는 경우: 서비스 태스크를 생성할 때 항상 해당 정확한 버전의 이미지가 사용됩니다.
$ docker service create \
--name="myservice" \
ubuntu:16.04@sha256:35bc48a1ca97c3971611dc4662d08d131869daa692acb281c7e9e052924e38b1
서비스를 생성하면 이미지의 태그는 서비스 생성 시점에 태그가 가리키는 특정 digest로 해석됩니다. 해당 서비스의 작업자 노드는 서비스를 명시적으로 업데이트하지 않는 한 그 특정 digest를 계속 사용해요. 이 기능은 latest처럼 자주 변경되는 태그를 사용할 때 특히 중요한데, 모든 서비스 태스크가 동일한 이미지 버전을 사용하도록 보장해 주기 때문입니다.
참고
content trust가 활성화되어 있으면, 클라이언트는 스웜 매니저에 요청하기 전에 이미지가 서명되었는지 확인하기 위해 이미지 태그를 digest로 해석합니다. 따라서 content trust를 사용하면 스웜 매니저는 사전에 해석된 요청을 받아요. 이 경우 클라이언트가 이미지를 digest로 해석하지 못하면 요청은 실패합니다.
매니저가 태그를 digest로 해석할 수 없으면 각 작업자 노드가 태그를 digest로 해석해야 하며, 노드마다 다른 이미지 버전을 사용할 수 있어요. 이런 경우 다음 경고가 기록됩니다. 자리 표시자는 실제 정보로 대체됩니다.
unable to pin image <IMAGE-NAME> to digest: <REASON>
이미지의 현재 digest를 확인하려면 docker inspect <IMAGE>:<TAG> 명령을 실행하고 RepoDigests 줄을 찾아보세요. 다음은 이 문서를 작성할 당시 ubuntu:latest의 digest입니다. 가독성을 위해 출력은 일부 생략했어요.
$ docker inspect ubuntu:latest
"RepoDigests": [
"ubuntu@sha256:35bc48a1ca97c3971611dc4662d08d131869daa692acb281c7e9e052924e38b1"
],
서비스를 생성한 후에는 아래 설명처럼 --image 플래그와 함께 docker service update를 명시적으로 실행하지 않는 한 이미지가 업데이트되지 않아요. 서비스 확장, 네트워크나 볼륨 추가/제거, 서비스 이름 변경 등 다른 업데이트 작업은 서비스 이미지를 업데이트하지 않습니다.
생성 후 서비스 이미지 업데이트하기
각 태그는 Git 해시와 유사하게 digest를 나타냅니다. latest 같은 일부 태그는 새 digest를 가리키도록 자주 업데이트되고, ubuntu:16.04 같은 다른 태그는 릴리스된 소프트웨어 버전을 나타내므로 새 digest로 자주 업데이트되지 않을 것으로 예상됩니다. 서비스를 생성하면 service update를 --image 플래그와 함께 실행하여 업데이트하기 전까지는 특정 이미지 digest를 사용하여 태스크를 생성하도록 제한됩니다.
--image 플래그와 함께 service update를 실행하면, 스웜 매니저가 Docker Hub 또는 프라이빗 Docker 레지스트리에 태그가 현재 가리키는 digest를 조회하고, 서비스 태스크가 해당 digest를 사용하도록 업데이트합니다.
참고
content trust를 사용하면 Docker 클라이언트가 이미지를 해석하고, 스웜 매니저는 태그가 아닌 이미지와 digest를 받아요.
일반적으로 매니저는 태그를 새 digest로 해석할 수 있고, 서비스가 업데이트되면서 각 태스크를 새 이미지로 재배포합니다. 매니저가 태그를 해석할 수 없거나 다른 문제가 발생하면 다음 두 섹션에서 설명하는 상황이 벌어집니다.
매니저가 태그를 해석한 경우
스웜 매니저가 이미지 태그를 digest로 해석할 수 있으면, 작업자 노드에 태스크를 재배포하고 해당 digest의 이미지를 사용하도록 지시합니다.
- 작업자가 해당 digest의 이미지를 캐시하고 있으면 그 이미지를 사용해요.
- 그렇지 않으면 Docker Hub 또는 프라이빗 레지스트리에서 이미지를 pull 하려고 시도합니다.
- 성공하면 새 이미지로 태스크가 배포됩니다.
- 작업자가 이미지 pull에 실패하면 해당 작업자 노드에서는 서비스 배포가 실패해요. Docker는 태스크 배포를 다시 시도하며, 다른 작업자 노드에 배포될 수도 있습니다.
매니저가 태그를 해석하지 못한 경우
스웜 매니저가 이미지를 digest로 해석하지 못해도 모든 것이 끝난 것은 아닙니다.
- 매니저는 작업자 노드에 해당 태그의 이미지를 사용하여 태스크를 재배포하도록 지시합니다.
- 작업자가 해당 태그로 해석되는 로컬 캐시 이미지를 가지고 있으면 그 이미지를 사용해요.
- 작업자에게 해당 태그로 해석되는 로컬 캐시 이미지가 없으면, Docker Hub 또는 프라이빗 레지스트리에 연결해 해당 태그의 이미지를 pull 하려고 시도합니다.
- 성공하면 작업자는 그 이미지를 사용합니다.
- 실패하면 태스크 배포가 실패하고, 매니저는 다른 작업자 노드에 태스크 배포를 다시 시도해요.
포트 게시하기
스웜 서비스를 생성할 때 서비스의 포트를 스웜 외부 호스트에 두 가지 방식으로 게시할 수 있어요.
-
라우팅 메시를 사용하는 방법: 서비스 포트를 게시하면 스웜은 모든 노드의 대상 포트에서 서비스를 접근 가능하게 만듭니다. 해당 노드에서 서비스 태스크가 실행 중인지 여부와 관계없이요. 이 방식은 덜 복잡하고 많은 유형의 서비스에 적합한 선택입니다.
-
서비스 태스크의 포트를 스웜 노드에 직접 게시하는 방법: 서비스가 실행 중인 스웜 노드에 직접 게시하는 방식이에요. 이 방식은 라우팅 메시를 우회하며, 자체 라우팅 프레임워크를 개발할 수 있는 능력을 포함해 최대한의 유연성을 제공합니다. 다만 각 태스크가 어디서 실행 중인지 추적하고, 요청을 태스크로 라우팅하며, 노드 간 부하 분산을 하는 것은 여러분의 책임입니다.
각 방법에 대한 자세한 정보와 사용 사례를 계속 살펴보겠습니다.
라우팅 메시를 사용해 서비스 포트 게시하기
서비스의 포트를 스웜 외부에 게시하려면 --publish <PUBLISHED-PORT>:<SERVICE-PORT> 플래그를 사용하세요. 스웜은 모든 스웜 노드의 게시된 포트에서 서비스를 접근 가능하게 만듭니다. 외부 호스트가 어떤 스웜 노드의 해당 포트에 연결하면, 라우팅 메시가 그 연결을 태스크로 라우팅해요. 외부 호스트는 서비스와 상호작용하기 위해 서비스 태스크의 IP 주소나 내부 포트를 알 필요가 없습니다. 사용자나 프로세스가 서비스에 연결하면 서비스 태스크를 실행 중인 모든 작업자 노드가 응답할 수 있어요. 스웜 서비스 네트워킹에 대한 자세한 내용은 Manage swarm service networks를 참고하세요.
예시: 10노드 스웜에서 3개 태스크 Nginx 서비스 실행하기
10노드 스웜이 있고, 3개의 태스크를 실행하는 Nginx 서비스를 배포한다고 상상해 보세요.
$ docker service create --name my_web \
--replicas 3 \
--publish published=8080,target=80 \
nginx
3개의 태스크는 최대 3개의 노드에서 실행됩니다. 어떤 노드가 태스크를 실행 중인지 알 필요가 없어요. 10개 노드 중 아무 노드의 8080 포트에 연결하면 3개의 nginx 태스크 중 하나에 연결됩니다. curl로 테스트해 볼 수 있어요. 다음 예시는 localhost가 스웜 노드 중 하나라고 가정합니다. 그렇지 않거나 localhost가 호스트의 IP 주소로 해석되지 않으면 호스트의 IP 주소나 해석 가능한 호스트 이름으로 대체하세요.
HTML 출력은 일부 생략되었습니다.
$ curl localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...truncated...
</html>
이후 연결은 같은 스웜 노드나 다른 노드로 라우팅될 수 있어요.
서비스 포트를 스웜 노드에 직접 게시하기
애플리케이션 상태에 따라 라우팅 결정을 내려야 하거나, 서비스 태스크로 요청을 라우팅하는 프로세스를 완전히 제어해야 한다면 라우팅 메시가 적합하지 않을 수 있어요. 서비스 포트를 실행 중인 노드에 직접 게시하려면 --publish 플래그에 mode=host 옵션을 사용하세요.
참고
mode=host로 서비스 포트를 스웜 노드에 직접 게시하면서published=<PORT>도 설정하면, 특정 스웜 노드에서 해당 서비스의 태스크를 하나만 실행할 수 있다는 암시적 제한이 생깁니다.published를 포트 정의 없이 지정하면 Docker가 각 태스크에 임의의 포트를 할당하므로 이 제한을 우회할 수 있어요.
또한 mode=host를 사용하면서 docker service create에 --mode=global 플래그를 사용하지 않으면, 작업을 라우팅하기 위해 어떤 노드가 서비스를 실행 중인지 파악하기 어렵습니다.
예시: 모든 스웜 노드에서 nginx 웹 서버 서비스 실행하기
nginx는 오픈소스 리버스 프록시, 로드 밸런서, HTTP 캐시, 웹 서버입니다. 라우팅 메시를 사용해 nginx를 서비스로 실행하면, 어떤 스웜 노드의 nginx 포트에 연결하든 (사실상) 서비스를 실행 중인 임의의 스웜 노드의 웹 페이지가 표시돼요.
다음 예시는 스웜의 각 노드에서 nginx를 서비스로 실행하고, 각 스웜 노드에 nginx 포트를 로컬로 노출합니다.
$ docker service create \
--mode global \
--publish mode=host,target=80,published=8080 \
--name=nginx \
nginx:latest
모든 스웜 노드의 8080 포트에서 nginx 서버에 접근할 수 있어요. 스웜에 노드를 추가하면 해당 노드에서 nginx 태스크가 시작됩니다. 포트 8080에 바인딩하는 다른 서비스나 컨테이너는 어떤 스웜 노드에서도 시작할 수 없어요.
참고
이 예시는 순전히 설명을 위한 것입니다. 다중 계층 서비스를 위한 애플리케이션 계층 라우팅 프레임워크를 만드는 것은 복잡한 작업이며 이 주제의 범위를 벗어납니다.
서비스를 오버레이 네트워크에 연결하기
오버레이 네트워크를 사용하면 스웜 내의 하나 이상의 서비스를 연결할 수 있어요.
먼저 매니저 노드에서 docker network create 명령을 --driver overlay 플래그와 함께 실행해 오버레이 네트워크를 생성합니다.
$ docker network create --driver overlay my-network
스웜 모드에서 오버레이 네트워크를 생성하면 모든 매니저 노드가 해당 네트워크에 접근할 수 있어요.
새 서비스를 생성하면서 --network 플래그를 전달해 오버레이 네트워크에 서비스를 연결할 수 있습니다.
$ docker service create \
--replicas 3 \
--network my-network \
--name my-web \
nginx
스웜은 서비스를 실행하는 각 노드로 my-network를 확장합니다.
--network-add 플래그를 사용해 기존 서비스를 오버레이 네트워크에 연결할 수도 있어요.
$ docker service update --network-add my-network my-web
실행 중인 서비스를 네트워크에서 분리하려면 --network-rm 플래그를 사용하세요.
$ docker service update --network-rm my-network my-web
오버레이 네트워킹과 서비스 디스커버리에 대한 자세한 내용은 Attach services to an overlay network와 Docker swarm mode overlay network security model을 참고하세요.
서비스에 시크릿 접근 권한 부여하기
Docker 관리 시크릿에 접근할 수 있는 서비스를 만들려면 --secret 플래그를 사용하세요. 자세한 내용은 Manage sensitive strings (secrets) for Docker services를 참고하세요.
서비스 격리 모드 사용자 지정하기
중요
이 설정은 Windows 호스트에만 적용되며 Linux 호스트에서는 무시됩니다.
Docker에서는 스웜 서비스의 격리 모드를 지정할 수 있어요. 격리 모드는 다음 중 하나입니다.
-
default:-exec-opt플래그 또는daemon.json의exec-opts배열로 구성된 Docker 호스트의 기본 격리 모드를 사용합니다. 데몬이 격리 기술을 지정하지 않으면 Windows Server에서는process가 기본값이고, Windows 10에서는hyperv가 기본값(유일한 선택)이에요. -
process: 서비스 태스크를 호스트의 별도 프로세스로 실행합니다.
참고
process격리 모드는 Windows Server에서만 지원됩니다. Windows 10은hyperv격리 모드만 지원해요.
hyperv: 서비스 태스크를 격리된hyperv태스크로 실행합니다. 오버헤드는 증가하지만 더 강력한 격리를 제공합니다.
새 서비스를 생성하거나 업데이트할 때 --isolation 플래그로 격리 모드를 지정할 수 있어요.
서비스 배치 제어하기
스웜 서비스는 다양한 노드에 서비스의 규모와 배치를 제어할 수 있는 몇 가지 방법을 제공합니다.
-
서비스가 특정 수의 레플리카를 실행해야 하는지, 아니면 모든 작업자 노드에서 전역으로 실행되어야 하는지 지정할 수 있어요. 레플리카 또는 글로벌 서비스를 참고하세요.
-
서비스의 CPU 또는 메모리 요구사항을 구성할 수 있으며, 서비스는 이러한 요구사항을 충족할 수 있는 노드에서만 실행됩니다.
-
배치 제약 조건을 사용하면 특정(임의의) 메타데이터가 설정된 노드에서만 서비스가 실행되도록 구성할 수 있고, 적절한 노드가 없으면 배포가 실패하게 만들 수 있어요. 예를 들어 임의의 레이블
pci_compliant가true로 설정된 노드에서만 서비스가 실행되도록 지정할 수 있습니다. -
배치 선호도를 사용하면 각 노드에 값의 범위를 가진 임의의 레이블을 적용하고, 알고리즘을 사용해 해당 노드들에 서비스 태스크를 분산할 수 있어요. 현재 지원되는 유일한 알고리즘은 균등 배치를 시도하는
spread입니다. 예를 들어 각 노드에 1~10의 값을 가진rack레이블을 지정하고,rack을 키로 하는 배치 선호도를 지정하면, 다른 배치 제약 조건, 배치 선호도, 노드별 제한 사항을 고려한 후 서비스 태스크가rack레이블이 있는 모든 노드에 가능한 한 균등하게 배치됩니다.
제약 조건과 달리 배치 선호도는 최선 노력(best-effort) 방식이에요. 선호도를 충족하는 노드가 없어도 서비스 배포가 실패하지 않습니다. 서비스에 배치 선호도를 지정하면, 스웜 매니저가 어떤 노드에서 서비스 태스크를 실행할지 결정할 때 해당 선호도와 일치하는 노드의 순위가 더 높아져요. 서비스의 고가용성 같은 다른 요소도 태스크를 실행할 노드를 결정하는 데 영향을 줍니다. 예를 들어 rack 레이블이 있는 노드가 N개(그 외 다른 노드도 있음)이고 서비스가 N+1개의 레플리카로 구성된 경우, +1 레플리카는 가능하면 아직 서비스가 없는 노드에 스케줄링됩니다. 그 노드에 rack 레이블이 있는지 여부와는 관계없어요.
레플리카 또는 글로벌 서비스
스웜 모드에는 replicated(레플리카)와 global(글로벌) 두 가지 유형의 서비스가 있어요. 레플리카 서비스의 경우 스웜 매니저가 사용 가능한 노드에 스케줄링할 레플리카 태스크 수를 지정합니다. 글로벌 서비스의 경우 스케줄러가 서비스의 배치 제약 조건과 리소스 요구사항을 충족하는 각 사용 가능한 노드에 태스크 하나를 배치해요.
--mode 플래그로 서비스 유형을 제어합니다. 모드를 지정하지 않으면 서비스는 기본적으로 replicated로 설정됩니다. 레플리카 서비스의 경우 --replicas 플래그로 시작할 레플리카 태스크 수를 지정해요. 예를 들어 3개의 레플리카 태스크로 레플리카 nginx 서비스를 시작하려면:
$ docker service create \
--name my_web \
--replicas 3 \
nginx
각 사용 가능한 노드에서 글로벌 서비스를 시작하려면 docker service create에 --mode global을 전달하세요. 새 노드를 사용할 수 있을 때마다 스케줄러가 새 노드에 글로벌 서비스 태스크를 배치합니다. 예를 들어 스웜의 모든 노드에서 alpine을 실행하는 서비스를 시작하려면:
$ docker service create \
--name myservice \
--mode global \
alpine top
서비스 제약 조건을 사용하면 스케줄러가 서비스를 노드에 배포하기 전에 노드가 충족해야 하는 기준을 설정할 수 있어요. 노드 속성, 메타데이터 또는 엔진 메타데이터를 기반으로 서비스에 제약 조건을 적용할 수 있습니다. 제약 조건에 대한 자세한 내용은 docker service create CLI 참조를 확인하세요.
서비스에 메모리 또는 CPU 예약하기
서비스에 특정 양의 메모리나 CPU 수를 예약하려면 --reserve-memory 또는 --reserve-cpu 플래그를 사용하세요. 요구사항을 충족하는 노드가 없으면(예: CPU 4개를 요청했는데 스웜에 CPU 4개를 가진 노드가 없는 경우) 서비스는 태스크를 실행할 적절한 노드를 사용할 수 있을 때까지 pending 상태로 유지됩니다.
서비스가 스웜 노드가 보유한 메모리보다 더 많은 메모리를 사용하려고 하면 OOME(Out Of Memory Exception)가 발생할 수 있고, 컨테이너나 Docker 데몬이 커널 OOM 킬러에 의해 종료될 수 있어요. 이를 방지하려면 애플리케이션이 충분한 메모리를 가진 호스트에서 실행되도록 하고, 메모리 부족의 위험 이해하기를 참고하세요.
스웜 서비스에서는 리소스 제약 조건, 배치 선호도, 레이블을 사용해 서비스가 적절한 스웜 노드에 배포되도록 할 수 있어요.
배치 제약 조건
배치 제약 조건을 사용하면 서비스가 할당될 수 있는 노드를 제어할 수 있어요. 다음 예시에서 서비스는 레이블 region이 east로 설정된 노드에서만 실행됩니다. 적절한 레이블이 지정된 노드를 사용할 수 없으면 태스크는 사용 가능해질 때까지 Pending 상태로 대기해요. --constraint 플래그는 같음 연산자(== 또는 !=)를 사용합니다. 레플리카 서비스의 경우 모든 서비스가 같은 노드에서 실행되거나, 각 노드가 레플리카를 하나만 실행하거나, 일부 노드가 레플리카를 실행하지 않을 수도 있어요. 글로벌 서비스의 경우 배치 제약 조건과 리소스 요구사항을 충족하는 모든 노드에서 서비스가 실행됩니다.
$ docker service create \
--name my-nginx \
--replicas 5 \
--constraint node.labels.region==east \
nginx
compose.yaml 파일에서 서비스 레벨 키 constraint를 사용할 수도 있어요.
여러 배치 제약 조건을 지정하면 서비스는 모든 조건을 충족하는 노드에만 배포됩니다. 다음 예시는 region이 east로 설정되고 type이 devel로 설정되지 않은 모든 노드에서만 서비스가 실행되도록 제한합니다.
$ docker service create \
--name my-nginx \
--mode global \
--constraint node.labels.region==east \
--constraint node.labels.type!=devel \
nginx
배치 제약 조건은 배치 선호도, CPU/메모리 제약 조건과 함께 사용할 수도 있어요. 충족할 수 없는 설정을 사용하지 않도록 주의하세요.
제약 조건에 대한 자세한 내용은 docker service create CLI 참조를 확인하세요.
배치 선호도
배치 제약 조건이 서비스를 실행할 수 있는 노드를 제한하는 반면, 배치 선호도는 알고리즘 방식(현재는 균등 분산만 지원)으로 적절한 노드에 태스크를 배치하려고 시도해요. 예를 들어 각 노드에 rack 레이블을 할당했다면, rack 레이블을 기준으로 값별로 서비스가 노드에 균등하게 분산되도록 배치 선호도를 설정할 수 있습니다. 이렇게 하면 랙 하나를 잃더라도 서비스는 다른 랙의 노드에서 계속 실행됩니다.
배치 선호도는 엄격하게 강제되지 않아요. 선호도에 지정한 레이블을 가진 노드가 없으면 선호도가 설정되지 않은 것처럼 서비스가 배포됩니다.
참고
배치 선호도는 글로벌 서비스에서는 무시됩니다.
다음 예시는 datacenter 레이블 값을 기준으로 노드에 배포를 분산하는 선호도를 설정합니다. 일부 노드에 datacenter=us-east가 있고 다른 노드에 datacenter=us-west가 있다면, 서비스는 두 노드 집합에 가능한 한 균등하게 배포돼요.
$ docker service create \
--replicas 9 \
--name redis_2 \
--placement-pref 'spread=node.labels.datacenter' \
redis:7.4.0
참고
분산에 사용되는 레이블이 없는 노드도 태스크 할당을 받습니다. 이 노드들은 하나의 그룹으로서 특정 레이블 값으로 식별되는 다른 그룹들과 동일한 비율로 태스크를 받아요. 어떤 의미에서 레이블이 없다는 것은 레이블이 null 값으로 붙어 있는 것과 같습니다. 서비스가 분산 선호도에 사용되는 레이블이 있는 노드에서만 실행되어야 한다면, 선호도를 제약 조건과 결합해야 해요.
배치 선호도는 여러 개 지정할 수 있으며, 만난 순서대로 처리됩니다. 다음 예시는 여러 배치 선호도를 가진 서비스를 구성합니다. 태스크는 먼저 다양한 데이터센터에 분산된 다음, 랙(각각의 레이블로 표시)에 분산됩니다.
$ docker service create \
--replicas 9 \
--name redis_2 \
--placement-pref 'spread=node.labels.datacenter' \
--placement-pref 'spread=node.labels.rack' \
redis:7.4.0
배치 선호도는 배치 제약 조건이나 CPU/메모리 제약 조건과 함께 사용할 수도 있어요. 충족할 수 없는 설정을 사용하지 않도록 주의하세요.
이 다이어그램은 배치 선호도가 어떻게 동작하는지 보여줍니다.
docker service update로 서비스를 업데이트할 때, --placement-pref-add는 기존 배치 선호도 뒤에 새 배치 선호도를 추가하고, --placement-pref-rm은 인수와 일치하는 기존 배치 선호도를 제거합니다.
서비스 업데이트 동작 구성하기
서비스를 생성할 때 docker service update를 실행할 때 스웜이 서비스에 변경 사항을 적용하는 방식에 대한 롤링 업데이트 동작을 지정할 수 있어요. 이러한 플래그를 업데이트의 일부로 docker service update의 인수로 지정할 수도 있습니다.
--update-delay 플래그는 서비스 태스크 또는 태스크 집합에 대한 업데이트 사이의 시간 지연을 구성합니다. 시간 T는 초 Ts, 분 Tm, 시 Th의 조합으로 표현할 수 있어요. 따라서 10m30s는 10분 30초 지연을 의미합니다.
기본적으로 스케줄러는 한 번에 1개의 태스크를 업데이트합니다. --update-parallelism 플래그를 전달하면 스케줄러가 동시에 업데이트하는 최대 서비스 태스크 수를 구성할 수 있어요.
개별 태스크에 대한 업데이트가 RUNNING 상태를 반환하면 스케줄러는 모든 태스크가 업데이트될 때까지 다른 태스크로 계속 진행합니다. 업데이트 중 언제라도 태스크가 FAILED를 반환하면 스케줄러는 업데이트를 일시 중지합니다. docker service create 또는 docker service update의 --update-failure-action 플래그로 이 동작을 제어할 수 있어요.
아래 예시 서비스에서 스케줄러는 한 번에 최대 2개의 레플리카에 업데이트를 적용합니다. 업데이트된 태스크가 RUNNING 또는 FAILED를 반환하면, 스케줄러는 다음 태스크를 중지하고 업데이트하기 전에 10초를 기다립니다.
$ docker service create \
--replicas 10 \
--name my_web \
--update-delay 10s \
--update-parallelism 2 \
--update-failure-action continue \
alpine
--update-max-failure-ratio 플래그는 업데이트 전체가 실패한 것으로 간주되기 전에 업데이트 중 실패할 수 있는 태스크의 비율을 제어합니다. 예를 들어 --update-max-failure-ratio 0.1 --update-failure-action pause를 사용하면 업데이트 중인 태스크의 10%가 실패한 후 업데이트가 일시 중지됩니다.
개별 태스크 업데이트는 태스크가 시작되지 않거나, --update-monitor 플래그로 지정된 모니터링 기간 내에 실행이 중지되면 실패한 것으로 간주됩니다. --update-monitor의 기본값은 30초이며, 이는 태스크가 시작된 후 처음 30초 내에 실패하면 서비스 업데이트 실패 임계값에 포함되고, 그 이후의 실패는 포함되지 않는다는 뜻이에요.
이전 버전의 서비스로 롤백하기
업데이트된 서비스 버전이 예상대로 작동하지 않는 경우, docker service update의 --rollback 플래그를 사용해 수동으로 이전 버전의 서비스로 롤백할 수 있어요. 이렇게 하면 가장 최근의 docker service update 명령 이전에 있던 구성으로 서비스가 되돌아갑니다.
--rollback은 다른 옵션과 결합할 수 있습니다. 예를 들어 --update-delay 0s를 함께 사용하면 태스크 간 지연 없이 롤백을 실행할 수 있어요.
$ docker service update \
--rollback \
--update-delay 0s
my_web
서비스 업데이트 배포에 실패하면 서비스가 자동으로 롤백되도록 구성할 수도 있어요. 업데이트 실패 시 자동 롤백을 참고하세요.
수동 롤백은 서버 측에서 처리되므로, 수동으로 시작된 롤백이 새 롤백 매개변수를 따를 수 있어요. --rollback은 docker service update의 다른 플래그와 함께 사용할 수 없다는 점에 유의하세요.
업데이트 실패 시 자동 롤백
서비스 업데이트로 인해 재배포가 실패할 경우 서비스가 자동으로 이전 구성으로 롤백되도록 서비스를 구성할 수 있어요. 이는 서비스 가용성을 보호하는 데 도움이 됩니다. 서비스 생성 또는 업데이트 시 다음 플래그 중 하나 이상을 설정할 수 있으며, 값을 설정하지 않으면 기본값이 사용됩니다.
| 플래그 | 기본값 | 설명 |
|---|---|---|
--rollback-delay |
0s |
태스크 하나를 롤백한 후 다음 태스크를 롤백하기 전에 대기하는 시간. 0은 첫 번째 롤백된 태스크가 배포된 직후 두 번째 태스크를 롤백한다는 뜻입니다. |
--rollback-failure-action |
pause |
태스크 롤백이 실패할 때 다른 태스크 롤백을 계속 시도할지(continue) 일시 중지할지(pause) 여부. |
--rollback-max-failure-ratio |
0 |
롤백 중 허용되는 실패율. 0과 1 사이의 부동 소수점 숫자로 지정합니다. 예를 들어 태스크 5개가 주어졌을 때 실패율 .2는 태스크 1개의 롤백 실패를 허용한다는 뜻입니다. 0은 실패를 허용하지 않고, 1은 어떤 수의 실패도 허용합니다. |
--rollback-monitor |
5s |
각 태스크 롤백 후 실패를 모니터링하는 기간. 이 기간이 경과하기 전에 태스크가 중지되면 롤백이 실패한 것으로 간주됩니다. |
--rollback-parallelism |
1 |
병렬로 롤백할 최대 태스크 수. 기본적으로 한 번에 하나의 태스크가 롤백됩니다. 0은 모든 태스크를 병렬로 롤백하게 합니다. |
다음 예시는 docker service update 배포에 실패하면 자동으로 롤백되도록 redis 서비스를 구성합니다. 두 개의 태스크를 병렬로 롤백할 수 있고, 롤백 후 20초 동안 태스크가 종료되지 않는지 모니터링하며, 최대 20%의 실패율이 허용됩니다. --rollback-delay와 --rollback-failure-action은 기본값이 사용됩니다.
$ docker service create --name=my_redis \
--replicas=5 \
--rollback-parallelism=2 \
--rollback-monitor=20s \
--rollback-max-failure-ratio=.2 \
redis:latest
서비스에 볼륨 또는 바인드 마운트 접근 권한 부여하기
최상의 성능과 이식성을 위해 중요한 데이터를 컨테이너의 쓰기 가능한 레이어에 직접 쓰지 않는 것이 좋아요. 대신 데이터 볼륨이나 바인드 마운트를 사용해야 합니다. 이 원칙은 서비스에도 동일하게 적용됩니다.
스웜의 서비스에는 volume 마운트와 bind 마운트 두 가지 유형의 마운트를 만들 수 있어요. 어떤 유형의 마운트를 사용하든, 서비스 생성 시 --mount 플래그로 구성하거나 기존 서비스 업데이트 시 --mount-add 또는 --mount-rm 플래그로 구성합니다. 유형을 지정하지 않으면 기본값은 데이터 볼륨이에요.
데이터 볼륨
데이터 볼륨은 컨테이너와 독립적으로 존재하는 스토리지입니다. 스웜 서비스에서 데이터 볼륨의 수명 주기는 컨테이너에서의 수명 주기와 유사해요. 볼륨은 태스크와 서비스보다 오래 지속되므로, 제거는 별도로 관리해야 합니다. 볼륨은 서비스를 배포하기 전에 생성할 수 있고, 특정 호스트에 태스크가 스케줄링될 때 볼륨이 없으면 서비스의 볼륨 사양에 따라 자동으로 생성됩니다.
서비스에서 기존 데이터 볼륨을 사용하려면 --mount 플래그를 사용하세요.
$ docker service create \
--mount src=<VOLUME-NAME>,dst=<CONTAINER-PATH> \
--name myservice \
<IMAGE>
태스크가 특정 호스트에 스케줄링될 때 <VOLUME-NAME>이라는 이름의 볼륨이 없으면 볼륨이 생성됩니다. 기본 볼륨 드라이버는 local이에요. 이 생성-요청 시 생성 패턴에서 다른 볼륨 드라이버를 사용하려면 --mount 플래그로 드라이버와 해당 옵션을 지정하세요.
$ docker service create \
--mount type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=<DRIVER>,volume-opt=<KEY0>=<VALUE0>,volume-opt=<KEY1>=<VALUE1>
--name myservice \
<IMAGE>
데이터 볼륨 생성 방법과 볼륨 드라이버 사용에 대한 자세한 내용은 Use volumes를 참고하세요.
바인드 마운트
바인드 마운트는 스케줄러가 태스크의 컨테이너를 배포하는 호스트의 파일 시스템 경로입니다. Docker는 해당 경로를 컨테이너에 마운트합니다. 파일 시스템 경로는 스웜이 태스크의 컨테이너를 초기화하기 전에 존재해야 해요.
다음 예시는 바인드 마운트 구문을 보여줍니다.
- 읽기-쓰기 바인드를 마운트하려면:
$ docker service create \
--mount type=bind,src=<HOST-PATH>,dst=<CONTAINER-PATH> \
--name myservice \
<IMAGE>
- 읽기 전용 바인드를 마운트하려면:
$ docker service create \
--mount type=bind,src=<HOST-PATH>,dst=<CONTAINER-PATH>,readonly \
--name myservice \
<IMAGE>
중요
바인드 마운트는 유용할 수 있지만 문제를 일으킬 수도 있어요. 대부분의 경우 호스트에서 경로를 마운트할 필요가 없도록 애플리케이션을 설계하는 것이 좋습니다. 주요 위험은 다음과 같습니다.
- 호스트 경로를 서비스의 컨테이너에 바인드 마운트하면 해당 경로는 모든 스웜 노드에 존재해야 합니다. Docker 스웜 모드 스케줄러는 리소스 가용성 요구사항을 충족하고 여러분이 지정한 모든 제약 조건과 배치 선호도를 만족하는 모든 머신에 컨테이너를 스케줄링할 수 있어요.
- Docker 스웜 모드 스케줄러는 실행 중인 서비스 컨테이너가 비정상 상태가 되거나 연결할 수 없게 되면 언제든지 다시 스케줄링할 수 있습니다.
- 호스트 바인드 마운트는 이식성이 없어요. 바인드 마운트를 사용하면 애플리케이션이 개발 환경과 프로덕션 환경에서 동일하게 실행된다는 보장이 없습니다.
템플릿을 사용하여 서비스 생성하기
service create의 일부 플래그에는 Go의 text/template 패키지가 제공하는 구문을 사용한 템플릿을 적용할 수 있어요.
지원되는 플래그는 다음과 같습니다.
--hostname--mount--env
Go 템플릿에 사용할 수 있는 유효한 자리 표시자는 다음과 같습니다.
| 자리 표시자 | 설명 |
|---|---|
.Service.ID |
서비스 ID |
.Service.Name |
서비스 이름 |
.Service.Labels |
서비스 레이블 |
.Node.ID |
노드 ID |
.Node.Hostname |
노드 호스트 이름 |
.Task.Name |
태스크 이름 |
.Task.Slot |
태스크 슬롯 |
템플릿 예시
이 예시는 서비스 이름과 컨테이너가 실행 중인 노드의 ID를 기반으로 생성된 컨테이너의 호스트 이름 템플릿을 설정합니다.
$ docker service create --name hosttempl \
--hostname="{{.Node.ID}}-{{.Service.Name}}"\
busybox top
템플릿 사용 결과를 확인하려면 docker service ps와 docker inspect 명령을 사용하세요.
$ docker service ps va8ew30grofhjoychbr6iot8c
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
wo41w8hg8qan hosttempl.1 busybox:latest@sha256:29f5d56d12684887bdfa50dcd29fc31eea4aaf4ad3bec43daf19026a7ce69912 2e7a8a9c4da2 Running Running about a minute ago
$ docker inspect --format="{{.Config.Hostname}}" hosttempl.1.wo41w8hg8qanxwjwsg4kxpprj