docker service create

docker service create

docker service create는 Swarm에서 새 서비스를 만드는 명령이에요. 지정한 이미지와 매개변수로 서비스를 정의하고 싶을 때 써요.

출처: 문서

본문

지정된 매개변수로 설명되는 서비스를 만들어요.

Swarm: 이 명령은 Swarm 오케스트레이터와 함께 작동해요.

참고: 이 명령은 클러스터 관리 명령이므로 Swarm 매니저 노드에서 실행해야 해요. 매니저와 워커에 대해 알아보려면 문서의 Swarm mode 섹션을 참고하세요.

사용법 (Usage)

docker service create [OPTIONS] IMAGE [COMMAND] [ARG...]

옵션 (Options)

옵션 기본값 설명
--cap-add API 1.41+ Linux capabilities를 추가해요
--cap-drop API 1.41+ Linux capabilities를 제거해요
--config API 1.30+ 서비스에 노출할 구성을 지정해요
--constraint 배치 제약(placement constraints)이에요
--container-label 컨테이너 레이블이에요
--credential-spec API 1.29+ 관리 서비스 계정용 자격 증명 사양이에요 (Windows 전용)
-d, --detach API 1.29+ 서비스가 수렴될 때까지 기다리지 않고 즉시 종료해요
--dns API 1.25+ 사용자 지정 DNS 서버를 설정해요
--dns-option API 1.25+ DNS 옵션을 설정해요
--dns-search API 1.25+ 사용자 지정 DNS 검색 도메인을 설정해요
--endpoint-mode vip 엔드포인트 모드(vip 또는 dnsrr)예요
--entrypoint 이미지의 기본 ENTRYPOINT를 덮어써요
-e, --env 환경 변수를 설정해요
--env-file 환경 변수 파일을 읽어요
--generic-resource 사용자 정의 리소스예요
--group API 1.25+ 컨테이너에 대한 하나 이상의 보조 사용자 그룹을 설정해요
--health-cmd API 1.25+ 상태 확인을 실행할 명령이에요
--health-interval API 1.25+ 검사 실행 사이의 시간이에요 (`ms
--health-retries API 1.25+ 비정상(unhealthy)으로 보고하는 데 필요한 연속 실패 횟수예요
--health-start-interval API 1.44+ 시작 기간 동안 검사 실행 사이의 시간이에요 (`ms
--health-start-period API 1.29+ 재시도를 불안정으로 계산하기 전에 컨테이너가 초기화할 시작 기간이에요 (`ms
--health-timeout API 1.25+ 한 번의 검사가 실행될 최대 시간이에요 (`ms
--host API 1.25+ 하나 이상의 사용자 지정 host-to-IP 매핑을 설정해요 (host:ip)
--hostname API 1.25+ 컨테이너 호스트네임이에요
--init API 1.37+ 각 서비스 컨테이너 안에서 init을 사용해 시그널을 전달하고 프로세스를 거둬들여요
--isolation API 1.35+ 서비스 컨테이너 격리 모드예요
-l, --label 서비스 레이블이에요
--limit-cpu CPU를 제한해요
--limit-memory 메모리를 제한해요
--limit-pids API 1.41+ 최대 프로세스 수를 제한해요 (기본 0 = 무제한)
--log-driver 서비스용 로깅 드라이버예요
--log-opt 로깅 드라이버 옵션이에요
--max-concurrent API 1.41+ 동시에 실행할 작업 태스크 수예요 (기본은 --replicas와 같음)
--memory-swap API 1.52+ Swap 바이트예요 (-1은 무제한)
--memory-swappiness -1 API 1.52+
--mode replicated 서비스 모드여요 (replicated, global, replicated-job, global-job)
--mount 서비스에 파일시스템 마운트를 연결해요
--name 서비스 이름이에요
--network 네트워크 연결이에요
--no-healthcheck API 1.25+ 컨테이너에 지정된 HEALTHCHECK를 비활성화해요
--no-resolve-image API 1.30+ 이미지 digest와 지원 플랫폼을 확인하기 위해 레지스트리를 조회하지 않아요
--oom-score-adj API 1.46+ 호스트의 OOM 기본 설정을 조정해요 (-1000~1000)
--placement-pref API 1.28+ 배치 선호도를 추가해요
-p, --publish 포트를 노드 포트로 게시해요
-q, --quiet 진행 출력을 억제해요
--read-only API 1.28+ 컨테이너의 루트 파일시스템을 읽기 전용으로 마운트해요
--replicas 태스크 수예요
--replicas-max-per-node API 1.40+ 노드당 최대 태스크 수예요 (기본 0 = 무제한)
--reserve-cpu CPU를 예약해요
--reserve-memory 메모리를 예약해요
--restart-condition any 조건이 충족되면 재시작해요 (none, on-failure, any)
--restart-delay 5s 재시작 시도 사이의 지연이에요 (`ns
--restart-max-attempts 포기 전 최대 재시작 횟수예요
--restart-window 재시작 정책을 평가하는 데 사용하는 기간이에요 (`ns
--rollback-delay 0s API 1.28+
--rollback-failure-action pause API 1.28+
--rollback-max-failure-ratio 0 API 1.28+
--rollback-monitor 5s API 1.28+
--rollback-order stop-first API 1.29+
--rollback-parallelism 1 API 1.28+
--secret API 1.25+ 서비스에 노출할 시크릿을 지정해요
--stop-grace-period 10s 컨테이너를 강제 종료하기 전에 기다리는 시간이에요 (`ns
--stop-signal API 1.28+ 컨테이너를 중지할 시그널이에요
--sysctl API 1.40+ Sysctl 옵션이에요
-t, --tty API 1.25+ 의사-TTY를 할당해요
--ulimit API 1.41+ Ulimit 옵션이에요
--update-delay 0s 업데이트 사이의 지연이에요 (`ns
--update-failure-action pause 업데이트 실패 시 동작이에요 (pause, continue, rollback)
--update-max-failure-ratio 0 API 1.25+
--update-monitor 5s API 1.25+
--update-order stop-first API 1.29+
--update-parallelism 1 동시에 업데이트할 최대 태스크 수예요 (0은 한 번에 모두 업데이트)
-u, --user 사용자 이름 또는 UID예요 (형식: `<name
--with-registry-auth 레지스트리 인증 정보를 Swarm 에이전트로 보내요
-w, --workdir 컨테이너 안의 작업 디렉터리예요

예제 (Examples)

서비스 만들기

$ docker service create --name redis redis:7.4.1

dmu1ept4cxcfe8k8lhtux3ro3

$ docker service create --mode global --name redis2 redis:7.4.1

a8q9dasaafudfs8q8w32udass

$ docker service ls

ID            NAME    MODE        REPLICAS  IMAGE
dmu1ept4cxcf  redis   replicated  1/1       redis:7.4.1
a8q9dasaafud  redis2  global      1/1       redis:7.4.1

개인 레지스트리의 이미지로 서비스 만들기 (--with-registry-auth)

로그인이 필요한 개인 레지스트리에서 이미지를 사용한다면, 로그인 후 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 로그를 통해 서비스가 배포되는 Swarm 노드로 전달돼요. 이 정보로 노드들은 레지스트리에 로그인해 이미지를 가져올 수 있어요.

5개 복제 태스크로 서비스 만들기 (--replicas)

--replicas 플래그를 사용해 복제 서비스의 복제 태스크 수를 설정해요. 다음 명령은 5개의 복제 태스크를 가진 redis 서비스를 만들어요.

$ docker service create --name redis --replicas=5 redis:7.4.1

4cdgfyky7ozwh3htjfw0d12qv

위 명령은 서비스의 원하는 태스크 수를 설정해요. 명령이 즉시 반환되지만 실제 서비스 확장에는 시간이 걸릴 수 있어요. REPLICAS 열은 서비스의 실제 및 원하는 복제 태스크 수를 모두 보여 줘요. 다음 예제에서 원하는 상태는 5개 복제본이지만 현재 RUNNING 태스크 수는 3개예요.

$ docker service ls

ID            NAME   MODE        REPLICAS  IMAGE
4cdgfyky7ozw  redis  replicated  3/5       redis:7.4.1

모든 태스크가 만들어지고 RUNNING이 되면 실제 태스크 수는 원하는 수와 같아져요.

$ docker service ls

ID            NAME   MODE        REPLICAS  IMAGE
4cdgfyky7ozw  redis  replicated  5/5       redis:7.4.1

시크릿으로 서비스 만들기 (--secret)

--secret 플래그를 사용해 컨테이너에 시크릿에 대한 접근을 부여해요. 시크릿을 지정해 서비스를 만들어요.

$ docker service create --name redis --secret secret.json redis:7.4.1

4cdgfyky7ozwh3htjfw0d12qv

시크릿의 source, target, user/group ID, mode를 지정해 서비스를 만들어요.

$ docker service create --name redis \
    --secret source=ssh-key,target=ssh \
    --secret source=app-key,target=app,uid=1000,gid=1001,mode=0400 \
    redis:7.4.1

4cdgfyky7ozwh3htjfw0d12qv

여러 시크릿에 접근하려면 --secret 플래그를 여러 번 사용해요. target을 지정하지 않으면 시크릿은 컨테이너의 /run/secrets에 위치해요. target을 지정하지 않으면 시크릿 이름이 컨테이너의 메모리 내 파일 이름으로 사용돼요. target을 지정하면 그 값이 파일 이름으로 사용돼요. 위 예제에서 각 시크릿 target에 대해 /run/secrets/ssh/run/secrets/app 두 파일이 만들어져요.

config로 서비스 만들기 (--config)

--config 플래그를 사용해 컨테이너에 config에 대한 접근을 부여해요. config로 서비스를 만들어요. config는 redis-config로 마운트되고, 컨테이너 안에서 명령을 실행하는 사용자(보통 root)가 소유하며, 파일 모드는 0444 또는 전 세계가 읽을 수 있는 상태가 돼요. uid와 gid를 숫자 ID 또는 이름으로 지정할 수 있어요. 이름을 사용할 때 지정한 group/user 이름은 컨테이너에 미리 존재해야 해요. mode는 0755 같은 4자리 숫자 시퀀스로 지정돼요.

$ docker service create --name=redis --config redis-conf redis:7.4.1

config를 지정하고 target 위치와 파일 모드를 지정해 서비스를 만들어요.

$ docker service create --name redis \
  --config source=redis-conf,target=/etc/redis/redis.conf,mode=0400 redis:7.4.1

여러 config에 접근하려면 --config 플래그를 여러 번 사용해요. target을 지정하지 않으면 config는 컨테이너의 /에 위치해요. target을 지정하지 않으면 config 이름이 컨테이너의 파일 이름으로 사용돼요. target을 지정하면 그 값이 파일 이름으로 사용돼요.

롤링 업데이트 정책으로 서비스 만들기

$ docker service create \
  --replicas 10 \
  --name redis \
  --update-delay 10s \
  --update-parallelism 2 \
  redis:7.4.1

service update를 실행하면 스케줄러는 한 번에 최대 2개의 태스크를 업데이트하고, 업데이트 사이에 10초를 둬요. 자세한 내용은 롤링 업데이트 튜토리얼을 참고하세요.

환경 변수 설정하기 (-e, --env)

이 옵션은 서비스의 모든 태스크에 대한 환경 변수를 설정해요. 예를 들어:

$ docker service create \
  --name redis_2 \
  --replicas 5 \
  --env MYVAR=foo \
  redis:7.4.1

여러 환경 변수를 지정하려면 각각 별도의 key-value 쌍으로 --env 플래그를 여러 번 지정해요.

$ docker service create \
  --name redis_2 \
  --replicas 5 \
  --env MYVAR=foo \
  --env MYVAR2=bar \
  redis:7.4.1

특정 호스트네임으로 서비스 만들기 (--hostname)

이 옵션은 docker service 컨테이너의 호스트네임을 특정 문자열로 설정해요. 예를 들어:

$ docker service create --name redis --hostname myredis redis:7.4.1

서비스에 메타데이터 설정하기 (-l, --label)

레이블은 서비스에 메타데이터를 적용하는 key=value 쌍이에요. 두 개의 레이블로 서비스를 표시하려면:

$ docker service create \
  --name redis_2 \
  --label com.example.foo="bar" \
  --label bar=baz \
  redis:7.4.1

레이블에 대한 자세한 내용은 사용자 지정 메타데이터 적용을 참고하세요.

바인드 마운트, 볼륨 또는 메모리 파일시스템 추가하기 (--mount)

Docker는 컨테이너가 호스트 운영체제 또는 메모리 파일시스템의 파일이나 디렉터리를 읽거나 쓸 수 있게 하는 세 가지 종류의 마운트를 지원해요. 그 유형은 데이터 볼륨(보통 볼륨이라 부름), 바인드 마운트, tmpfs, 그리고 named pipe예요.

바인드 마운트는 호스트의 파일 또는 디렉터리를 마운트된 컨테이너에서 사용할 수 있게 해 줘요. 바인드 마운트는 읽기 전용 또는 읽기-쓰기일 수 있어요. 예를 들어 컨테이너는 호스트의 /etc/resolv.conf를 바인드 마운트해 호스트의 DNS 정보를 공유하거나, 호스트의 /var/log/myContainerLogs 디렉터리에 로그를 쓸 수 있어요. 바인드 마운트를 사용할 때 호스트와 컨테이너의 권한·접근 제어 등에 대한 개념이 다르면 이식성 문제가 발생할 수 있어요.

named volume은 컨테이너가 필요한 영구 데이터를 이미지·호스트 머신과 분리하는 메커니즘이에요. named volume은 Docker가 만들고 관리하며, 현재 사용 중인 컨테이너가 없어도 유지돼요. named volume의 데이터는 컨테이너와 호스트 머신 사이는 물론 여러 컨테이너 간에도 공유할 수 있어요. Docker는 볼륨 드라이버를 사용해 볼륨을 만들고 관리하고 마운트해요. Docker 명령으로 볼륨을 백업하거나 복원할 수 있어요.

tmpfs는 휘발성 데이터를 위해 컨테이너 안에 tmpfs를 마운트해요. npipe는 호스트의 named pipe를 컨테이너로 마운트해요.

이미지가 가벼운 웹 서버를 시작한다고 생각해 보세요. 그 이미지를 기본 이미지로 사용하고, 웹사이트의 HTML 파일을 복사해 다른 이미지로 패키징할 수 있어요. 웹사이트가 바뀔 때마다 새 이미지를 업데이트하고 웹사이트를 제공하는 모든 컨테이너를 재배포해야 해요. 더 나은 해결책은 웹사이트를 각 웹 서버 컨테이너가 시작할 때 연결되는 named volume에 저장하는 거예요. 웹사이트를 업데이트하려면 named volume만 업데이트하면 돼요.

named volume에 대한 자세한 내용은 Data Volumes를 참고하세요.

다음 표는 서비스에서 바인드 마운트와 named volume 모두에 적용되는 옵션을 설명해요.

옵션 필수 설명
type 마운트의 유형이에요. volume, bind, tmpfs, npipe 중 하나예요. type을 지정하지 않으면 volume이 기본이에요. volume: 관리되는 볼륨을 컨테이너에 마운트해요. bind: 호스트의 디렉터리 또는 파일을 컨테이너에 바인드 마운트해요. tmpfs: 컨테이너에 tmpfs를 마운트해요. npipe: 호스트의 named pipe를 컨테이너에 마운트해요 (Windows 컨테이너 전용)
src 또는 source type=bindtype=npipe의 경우 type=volume: src는 볼륨의 이름을 지정하는 선택 옵션이에요 (예: src=my-volume). named volume이 없으면 자동으로 만들어져요. src를 지정하지 않으면 볼륨에 호스트에서 고유함이 보장되지만 클러스터 전체에서는 고유하지 않을 수 있는 임의 이름이 할당돼요. 임의 이름의 볼륨은 컨테이너와 같은 수명을 가지며, 컨테이너가 파괴되면(서비스 업데이트, 확장 또는 재조정 시) 함께 파괴돼요. type=bind: src는 필수이며 바인드 마운트할 파일 또는 디렉터리의 절대 경로를 지정해요 (예: src=/path/on/host/). 파일 또는 디렉터리가 없으면 오류가 발생해요. type=tmpfs: src는 지원되지 않아요
dst 또는 destination 또는 target 컨테이너 안의 마운트 경로예요. 예: /some/path/in/container/. 경로가 컨테이너 파일시스템에 없으면 Engine은 볼륨 또는 바인드 마운트를 마운트하기 전에 지정된 위치에 디렉터리를 만들어요
readonly 또는 ro Engine은 readonly 옵션이 주어지지 않으면 바인드와 볼륨을 읽기-쓰기로 마운트해요. 바인드 마운트에 readonly를 설정해도 커널 버전에 따라 하위 마운트가 읽기 전용이 되지 않을 수 있다는 점을 유의하세요. bind-recursive도 참고하세요. true 또는 1 또는 값 없음: 바인드 또는 볼륨을 읽기 전용으로 마운트해요. false 또는 0: 바인드 또는 볼륨을 읽기-쓰기로 마운트해요

바인드 마운트 옵션

다음 옵션은 바인드 마운트(type=bind)에서만 사용할 수 있어요.

옵션 설명
bind-propagation 바인드 전파 섹션을 참고하세요
consistency 마운트의 일관성 요구 사항이에요. 하나: default: consistent와 동일해요. consistent: 완전한 일관성이에요. 컨테이너 런타임과 호스트가 마운트의 동일한 뷰를 항상 유지해요. cached: 호스트의 마운트 뷰가 권위적이에요. 호스트에서 이뤄진 업데이트가 컨테이너 안에 보이기까지 지연이 있을 수 있어요. delegated: 컨테이너 런타임의 마운트 뷰가 권위적이에요. 컨테이너에서 이뤄진 업데이트가 호스트에 보이기까지 지연이 있을 수 있어요
bind-recursive 기본적으로 하위 마운트도 재귀적으로 바인드 마운트돼요. 하지만 바인드 마운트가 readonly 옵션으로 구성되면 커널 버전에 따라 하위 마운트가 읽기 전용으로 마운트되지 않을 수 있어 혼란스러울 수 있어요. bind-recursive를 설정해 재귀 바인드 마운트 동작을 제어해요. 값은 하나예요: enabled: 재귀 바인드 마운트를 활성화해요. 커널이 v5.12 이상이면 읽기 전용 마운트가 재귀적으로 읽기 전용으로 만들어져요. 그렇지 않으면 재귀적으로 읽기 전용으로 만들어지지 않아요. disabled: 재귀 바인드 마운트를 비활성화해요. writable: 재귀 바인드 마운트를 활성화해요. 읽기 전용 마운트는 재귀적으로 읽기 전용으로 만들어지지 않아요. readonly: 재귀 바인드 마운트를 활성화해요. 커널이 v5.12 이상이면 읽기 전용 마운트가 재귀적으로 읽기 전용으로 만들어져요. 그렇지 않으면 Engine이 오류를 발생시켜요
bind-create-src 기본적으로 바인드 마운트는 소스 경로가 데몬 호스트에 존재해야 해요. 이는 소스 경로가 없으면 만들어 주는 -v 플래그와 크게 다르다는 점이에요. bind-create-src를 설정하면 소스 경로가 없을 때 데몬 호스트에 만들어 줘요. 값은 선택적이에요: true 또는 1: 데몬 호스트에 경로가 없으면 만들어요. false 또는 0: 기본 동작이에요. 데몬 호스트에 소스 경로가 없으면 오류를 발생시켜요

바인드 전파 (Bind propagation)

바인드 전파는 특정 바인드 마운트 또는 named volume 안에서 만들어진 마운트가 그 마운트의 복제본(replica)으로 전파될 수 있는지 여부를 말해요. 마운트 지점 /mnt/tmp에도 마운트되어 있다고 생각해 보세요. 전파 설정은 /tmp/a의 마운트가 /mnt/a에서도 사용 가능한지 제어해요. 각 전파 설정에는 재귀적 대응 설정이 있어요. 재귀의 경우 /tmp/a/foo로 마운트되어 있다고 생각해 보세요. 전파 설정은 /mnt/a 및/또는 /tmp/a가 존재할지 제어해요.

bind-propagation 옵션은 바인드 마운트와 볼륨 마운트 모두에서 기본적으로 rprivate이며, 바인드 마운트에서만 구성할 수 있어요. 즉 named volume은 바인드 전파를 지원하지 않아요.

  • shared: 원본 마운트의 하위 마운트가 복제본 마운트에 노출되고, 복제본 마운트의 하위 마운트도 원본 마운트로 전파돼요.
  • slave: shared 마운트와 비슷하지만 한 방향으로만 동작해요. 원본 마운트가 하위 마운트를 노출하면 복제본 마운트가 그것을 볼 수 있어요. 하지만 복제본 마운트가 하위 마운트를 노출하면 원본 마운트는 그것을 볼 수 없어요.
  • private: 마운트가 개인적이에요. 그 안의 하위 마운트는 복제본 마운트에 노출되지 않고, 복제본 마운트의 하위 마운트도 원본 마운트에 노출되지 않아요.
  • rshared: shared와 같지만 전파가 원본 또는 복제본 마운트 지점 어디에든 중첩된 마운트 지점까지 확장돼요.
  • rslave: slave와 같지만 전파가 원본 또는 복제본 마운트 지점 어디에든 중첩된 마운트 지점까지 확장돼요.
  • rprivate: 기본값이에요. private와 같으며, 원본 또는 복제본 마운트 지점 안의 어떤 마운트 지점도 어느 방향으로도 전파되지 않음을 의미해요.

바인드 전파에 대한 자세한 내용은 Linux 커널 문서의 shared subtree를 참고하세요.

named volume 옵션

다음 옵션은 named volume(type=volume)에서만 사용할 수 있어요.

옵션 설명
volume-driver 볼륨에 사용할 volume-driver 플러그인의 이름이에요. 볼륨이 없을 때 볼륨을 만들기 위해 기본적으로 "local"(로컬 볼륨 드라이버)로 설정돼요
volume-label 생성 시 볼륨에 적용할 하나 이상의 사용자 지정 메타데이터("labels")예요. 예: volume-label=mylabel=hello-world,my-other-label=hello-mars. 레이블에 대한 자세한 내용은 사용자 지정 메타데이터 적용을 참고하세요
volume-nocopy 기본적으로 빈 볼륨을 컨테이너에 연결하고 컨테이너의 마운트 경로(dst)에 파일이나 디렉터리가 이미 존재하면 Engine은 그 파일과 디렉터리를 볼륨에 복사해 호스트가 접근할 수 있게 해요. volume-nocopy를 설정하면 컨테이너 파일시스템에서 볼륨으로 파일을 복사하지 않고 빈 볼륨을 마운트해요. 값은 선택적이에요: true 또는 1: 값을 제공하지 않을 때 기본이에요. 복사를 비활성화해요. false 또는 0: 복사를 활성화해요
volume-opt 특정 볼륨 드라이버에 해당하는 옵션이에요. 볼륨을 만들 때 드라이버에 전달돼요. 옵션은 key/value 쌍의 쉼표 구분 목록으로 제공돼요. 예: volume-opt=some-option=some-value,volume-opt=some-other-option=some-other-value. 주어진 드라이버의 사용 가능한 옵션은 그 드라이버의 문서를 참고하세요

tmpfs 옵션

다음 옵션은 tmpfs 마운트(type=tmpfs)에서만 사용할 수 있어요.

옵션 설명
tmpfs-size 바이트 단위의 tmpfs 마운트 크기예요. Linux에서 기본은 무제한이에요
tmpfs-mode 8진수 tmpfs 파일 모드예요. (예: "700" 또는 "0700".) Linux에서 기본은 "1777"이에요

"--mount"와 "--volume"의 차이점

--mount 플래그는 docker run-v 또는 --volume 플래그가 지원하는 대부분의 옵션을 지원하지만 몇 가지 중요한 예외가 있어요.

  • --mount 플래그는 볼륨 드라이버와 볼륨 드라이버 옵션을 볼륨별로 지정할 수 있어요. 사전에 볼륨을 만들 필요가 없어요. 반대로 docker run--volume-driver 플래그로 모든 볼륨이 공유하는 단일 볼륨 드라이버를 지정할 수 있어요.
  • --mount 플래그는 볼륨이 만들어지기 전에 볼륨에 대한 사용자 지정 메타데이터("labels")를 지정할 수 있어요.
  • --mounttype=bind와 함께 사용하면 host-path는 호스트에 이미 존재하는 경로를 가리켜야 해요. 경로가 만들어지지 않으므로, 경로가 없으면 서비스가 오류로 실패해요. bind-create-src를 사용하면 호스트 경로가 없을 때 만들어 줄 수 있어요.
  • --mount 플래그는 selinux 레이블 지정에 사용되는 Z 또는 z 플래그로 볼륨의 레이블을 다시 지정할 수 없어요.

named volume을 사용하는 서비스 만들기

다음 예제는 named volume을 사용하는 서비스를 만들어요.

$ docker service create \
  --name my-service \
  --replicas 3 \
  --mount type=volume,source=my-volume,destination=/path/in/container,volume-label="color=red",volume-label="shape=round" \
  nginx:alpine

서비스의 각 복제본에 대해 engine은 태스크가 배포되는 기본("local") 볼륨 드라이버에서 "my-volume"이라는 볼륨을 요청해요. 볼륨이 없으면 engine은 새 볼륨을 만들고 "color"와 "shape" 레이블을 적용해요. 태스크가 시작되면 볼륨은 컨테이너 안의 /path/in/container/에 마운트돼요.

기본("local") 볼륨은 로컬 범위의 볼륨 드라이버라는 점에 유의하세요. 즉 태스크가 어디에 배포되느냐에 따라 그 태스크는 "my-volume"이라는 새 볼륨을 얻거나, 같은 서비스의 다른 태스크와 동일한 "my-volume"을 공유할 수 있어요. 여러 컨테이너가 단일 공유 볼륨에 쓰면, 컨테이너 안에서 실행되는 소프트웨어가 동시 프로세스의 같은 위치 쓰기를 처리하도록 설계되지 않았다면 데이터 손상이 발생할 수 있어요. 또한 컨테이너가 Swarm 오케스트레이터에 의해 재스케줄링되어 다른 노드에 배포될 수 있다는 점도 고려해야 해요.

익명 볼륨을 사용하는 서비스 만들기

다음 명령은 /path/in/container에 익명 볼륨이 있는 3개 복제본의 서비스를 만들어요.

$ docker service create \
  --name my-service \
  --replicas 3 \
  --mount type=volume,destination=/path/in/container \
  nginx:alpine

이 예제에서는 볼륨에 이름(source)이 지정되지 않으므로 각 태스크마다 새 볼륨이 만들어져요. 이렇게 하면 각 태스크가 자체 볼륨을 가지게 되고, 볼륨이 태스크 간에 공유되지 않아요. 익명 볼륨은 태스크가 완료된 후 제거돼요.

바인드 마운트된 호스트 디렉터리를 사용하는 서비스 만들기

다음 예제는 서비스를 뒷받침하는 컨테이너의 /path/in/container에 호스트 디렉터리를 바인드 마운트해요.

$ docker service create \
  --name my-service \
  --mount type=bind,source=/path/on/host,destination=/path/in/container \
  nginx:alpine

서비스 모드 설정하기 (--mode)

서비스 모드는 이것이 복제 서비스인지 전역 서비스인지 결정해요. 복제 서비스는 지정된 만큼 많은 태스크를 실행하는 반면, 전역 서비스는 swarm의 각 활성 노드에서 하나의 태스크를 실행해요. 배치 --constraint 표현식은 여전히 적용되며, 전역 서비스는 모든 노드가 아닌 조건에 맞는 노드에만 태스크를 스케줄링해요.

다음 명령은 전역 서비스를 만들어요.

$ docker service create \
 --name redis_2 \
 --mode global \
 redis:7.4.1

서비스 제약 지정하기 (--constraint)

제약 표현식을 정의해 태스크가 스케줄링될 수 있는 노드 집합을 제한할 수 있어요. 제약은 복제·전역 서비스(그리고 job) 모두에 적용돼요. 제약 표현식은 일치(==) 또는 제외(!=) 규칙을 사용할 수 있어요. 여러 제약은 모든 표현식을 충족하는 노드를 찾아요 (AND 일치). 제약은 노드 또는 Docker Engine 레이블을 다음과 같이 매칭할 수 있어요.

노드 속성 일치 예제
node.id 노드 ID node.id==2ivku8v2gvtg4
node.hostname 노드 호스트네임 node.hostname!=node-2
node.role 노드 역할 (manager/worker) node.role==manager
node.platform.os 노드 운영체제 node.platform.os==windows
node.platform.arch 노드 아키텍처 node.platform.arch==x86_64
node.labels 사용자 정의 노드 레이블 node.labels.security==high
engine.labels Docker Engine의 레이블 engine.labels.operatingsystem==ubuntu-24.04

engine.labels는 운영체제, 드라이버 등 Docker Engine 레이블에 적용돼요. Swarm 관리자는 docker node update 명령을 사용해 운영 목적으로 node.labels를 추가해요.

예를 들어 다음은 redis 서비스의 태스크를 노드 유형 레이블이 queue와 같은 노드로 제한해요.

$ docker service create \
  --name redis_2 \
  --constraint node.platform.os==linux \
  --constraint node.labels.type==queue \
  redis:7.4.1

서비스 제약이 클러스터의 모든 노드를 제외하면 적합한 노드를 찾지 못했다는 메시지가 출력되지만, 스케줄러는 조정(재조정) 루프를 시작해 적합한 노드가 생기면 서비스를 배포해요. 아래 예제에서 제약을 충족하는 노드가 없어 서비스가 원하는 상태와 조정되지 않았어요.

$ docker service create \
  --name web \
  --constraint node.labels.region==east \
  nginx:alpine

lx1wrhhpmbbu0wuk0ybws30bc
overall progress: 0 out of 1 tasks
1/1: no suitable node (scheduling constraints not satisfied on 5 nodes)

$ docker service ls
ID                  NAME     MODE         REPLICAS   IMAGE               PORTS
b6lww17hrr4e        web      replicated   0/1        nginx:alpine

클러스터의 노드에 region=east 레이블을 추가하면 서비스가 조정되고 원하는 수의 복제본이 배포돼요.

$ docker node update --label-add region=east yswe2dm4c5fdgtsrli1e8ya5l
yswe2dm4c5fdgtsrli1e8ya5l

$ docker service ls
ID                  NAME     MODE         REPLICAS   IMAGE               PORTS
b6lww17hrr4e        web      replicated   1/1        nginx:alpine

서비스 배치 선호도 지정하기 (--placement-pref)

서비스가 서로 다른 범주의 노드에 태스크를 고르게 분배하도록 설정할 수 있어요. 유용한 예로 데이터센터나 가용성 영역 집합에 태스크를 균형 있게 배치하는 것이 있어요. 아래 예제가 이를 보여 줘요.

$ docker service create \
  --replicas 9 \
  --name redis_2 \
  --placement-pref spread=node.labels.datacenter \
  redis:7.4.1

이것은 spread 전략(현재 유일하게 지원되는 전략)으로 --placement-pref를 사용해 datacenter 노드 레이블의 값에 태스크를 고르게 분배해요. 이 예제에서 모든 노드에 datacenter 노드 레이블이 붙어 있다고 가정해요. swarm의 노드에 이 레이블의 값이 세 가지 다르면, 태스크의 1/3은 각 값에 해당하는 노드에 배치돼요. 이는 한 값에 다른 값보다 더 많은 노드가 있더라도 마찬가지예요. 예를 들어 다음과 같은 노드 집합을 생각해 보세요.

  • node.labels.datacenter=east인 노드 3개
  • node.labels.datacenter=south인 노드 2개
  • node.labels.datacenter=west인 노드 1개

datacenter 레이블의 값에 걸쳐 분배하고 서비스에 9개 복제본이 있으므로 각 데이터센터에 3개 복제본이 배치돼요. east 값과 연결된 노드 3개이므로 각각 이 값에 예약된 복제본 3개 중 하나씩 받아요. south 값의 노드 2개가 있고, 이 값의 복제본 3개는 노드 사이에 나뉘어 하나는 2개, 다른 하나는 1개를 받아요. 마지막으로 west는 단일 노드가 west에 예약된 3개 복제본을 모두 받아요.

한 범주(예: node.labels.datacenter=south)의 노드가 제약이나 리소스 제한 때문에 공정한 몫을 처리하지 못하면, 가능하면 추가 태스크는 다른 노드에 할당돼요.

배치 선호도는 engine 레이블과 노드 레이블 모두를 지원해요. 위 예제는 node.labels.datacenter로 레이블을 참조하므로 노드 레이블을 사용해요. engine 레이블의 값에 분배하려면 --placement-pref spread=engine.labels.<labelname>을 사용해요.

서비스에 여러 배치 선호도를 추가할 수 있어요. 이렇게 하면 선호도 계층이 만들어져 태스크가 먼저 한 범주로 나뉘고, 추가 범주로 더 나뉘어요. 데이터센터 간에 태스크를 공정하게 나누고, 각 데이터센터 안에서 태스크를 랙 선택에 따라 분할하는 것이 유용한 예일 수 있어요. 여러 배치 선호도를 추가하려면 --placement-pref 플래그를 여러 번 지정해요. 순서가 중요하며, 스케줄링 결정을 내릴 때 배치 선호도는 주어진 순서대로 적용돼요.

다음 예제는 여러 배치 선호도로 서비스를 설정해요. 태스크는 먼저 여러 데이터센터에 분배되고, 그다음 랙(각 레이블이 나타내는 대로)에 분배돼요.

$ docker service create \
  --replicas 9 \
  --name redis_2 \
  --placement-pref 'spread=node.labels.datacenter' \
  --placement-pref 'spread=node.labels.rack' \
  redis:7.4.1

docker service update로 서비스를 업데이트할 때 --placement-pref-add는 기존 배치 선호도 뒤에 새 선호도를 추가해요. --placement-pref-rm은 인자와 일치하는 기존 배치 선호도를 제거해요.

서비스의 메모리 요구 사항과 제약 지정하기 (--reserve-memory 및 --limit-memory)

서비스가 올바르게 실행되려면 최소한의 메모리가 필요하다면 --reserve-memory를 사용해 이만큼의 메모리를 예약할 수 있는 노드에만 서비스가 스케줄링되도록 지정할 수 있어요. 기준을 충족하는 노드가 없으면 태스크는 스케줄링되지 않고 pending 상태로 남아요.

다음 예제는 서비스를 해당 노드에 스케줄링하기 전에 주어진 노드에 4GB의 메모리가 사용 가능하고 예약 가능해야 한다고 요구해요.

$ docker service create --reserve-memory=4GB --name=too-big nginx:alpine

매니저는 예약의 합이 해당 노드에서 사용 가능한 메모리를 초과하는 컨테이너 집합을 단일 노드에 스케줄링하지 않아요. 태스크가 스케줄링되어 실행된 후에는 --reserve-memory가 메모리 제한을 강제하지 않아요. --limit-memory를 사용해 태스크가 노드에서 주어진 메모리보다 많이 사용하지 않도록 보장해요. 이 예제는 태스크가 사용하는 메모리를 4GB로 제한해요. --limit-memory는 상한이므로 각 노드에 2GB만 있더라도 태스크는 스케줄링돼요.

$ docker service create --limit-memory=4GB --name=too-big nginx:alpine

--reserve-memory--limit-memory를 사용해도 Docker가 호스트에서 원하는 것보다 더 많은 메모리를 사용하지 않는다는 보장은 없어요. 예를 들어 메모리 사용량의 합이 사용 가능한 메모리를 소진시킬 수 있는 서비스를 여러 개 만들 수 있어요.

호스트에서 실행되는 다른 (컨테이너화되지 않은) 소프트웨어도 고려하면 이 시나리오가 사용 가능한 메모리를 소진하는 것을 막을 수 있어요. --reserve-memory--limit-memory보다 크거나 같으면 Docker는 메모리가 충분하지 않은 호스트에 서비스를 스케줄링하지 않아요. --limit-memory는 서비스의 메모리를 그 한도 내로 제한하므로, 모든 서비스에 메모리 예약과 한도가 설정되어 있으면 Docker 서비스가 호스트를 포화시킬 가능성이 낮아져요. Docker 호스트에서 직접 실행되는 다른 비서비스 컨테이너나 애플리케이션은 여전히 메모리를 소진할 수 있어요.

이 접근 방식에는 단점이 있어요. 메모리를 예약한다는 것은 노드에서 사용 가능한 메모리를 최적으로 사용하지 못할 수도 있다는 뜻이에요. 정상 상황에서 100MB를 사용하지만 로드에 따라 500MB까지 "피크"를 칠 수 있는 서비스를 생각해 보세요. 그 서비스에 500MB를 예약하면(그 "피크"를 위해 500MB를 보장) 대부분의 시간 동안 400MB의 메모리가 낭비돼요.

요약하면 더 보수적이거나 더 유연한 접근 방식을 취할 수 있어요.

  • 보수적(Conservative): 500MB를 예약하고 500MB로 제한해요. 기본적으로 서비스 컨테이너를 VM처럼 취급하는 것이며, 컨테이너의 큰 장점 중 하나인 호스트당 더 높은 서비스 밀도를 잃을 수 있어요.
  • 유연(Flexible): 서비스가 500MB 이상을 필요로 하면 오작동이라고 가정하고 500MB로 제한해요. 100MB "정상" 요구 사항과 500MB "피크" 요구 사항 사이 어딘가를 예약해요. 이 서비스가 "피크"일 때 다른 서비스나 비컨테이너 워크로드는 아마 그렇지 않을 것이라고 가정해요.

취하는 접근 방식은 워크로드의 메모리 사용 패턴에 크게 의존해요. 접근 방식을 결정하기 전에 정상 및 피크 조건에서 테스트해야 해요. Linux에서는 cgroups 또는 기타 관련 운영체제 도구를 사용해 호스트 운영체제 수준에서 주어진 호스트의 서비스 전체 메모리 사용량을 제한할 수도 있어요.

노드당 최대 복제본 지정하기 (--replicas-max-per-node)

--replicas-max-per-node 플래그를 사용해 노드에서 실행할 수 있는 최대 복제 태스크 수를 설정해요. 다음 명령은 2개의 복제 태스크를 가진 nginx 서비스를 만들지만 노드당 복제 태스크는 하나만 만들어요. --placement-pref와 함께 데이터센터 집합에 태스크를 균형 있게 배치하고 --replicas-max-per-node 설정으로 유지보수나 데이터센터 장애 중 복제본이 다른 데이터센터로 마이그레이션되지 않도록 하는 것이 유용한 예가 될 수 있어요. 아래 예제가 이를 보여 줘요.

$ docker service create \
  --name nginx \
  --replicas 2 \
  --replicas-max-per-node 1 \
  --placement-pref 'spread=node.labels.datacenter' \
  nginx

기존 네트워크에 서비스 연결하기 (--network)

오버레이 네트워크를 사용해 swarm 안의 하나 이상의 서비스를 연결할 수 있어요. 먼저 매니저 노드에서 docker network create 명령으로 오버레이 네트워크를 만들어요.

$ docker network create --driver overlay my-network

etjpu59cykrptrgw0z0hk5snf

swarm 모드에서 오버레이 네트워크를 만들면 모든 매니저 노드가 네트워크에 접근할 수 있어요. 서비스를 만들 때 --network 플래그를 전달해 서비스를 오버레이 네트워크에 연결해요.

$ docker service create \
  --replicas 3 \
  --network my-network \
  --name my-web \
  nginx

716thylsndqma81j6kkkb5aus

swarm은 my-network를 서비스를 실행하는 각 노드로 확장해요. 같은 네트워크의 컨테이너는 서비스 디스커버리를 사용해 서로 접근할 수 있어요. --network의 긴 형식 구문은 별칭과 드라이버 옵션 목록을 지정할 수 있게 해 줘요: --network name=my-network,alias=web1,driver-opt=field1=value1

서비스 포트를 swarm에 외부로 게시하기 (-p, --publish)

--publish 플래그를 사용해 서비스 포트를 게시해 swarm에 외부로 사용할 수 있게 만들 수 있어요. --publish 플래그는 두 가지 스타일의 인자를 받을 수 있어요. 짧은 버전은 위치 기반이며, 콜론(:)으로 구분된 게시 포트와 대상 포트를 지정할 수 있어요.

$ docker service create --name my_web --replicas 3 --publish 8080:80 nginx

또한 읽기 쉽고 더 많은 옵션을 지정할 수 있는 긴 형식도 있어요. 긴 형식이 선호돼요. 짧은 형식으로는 서비스의 mode를 지정할 수 없어요. 위와 같은 서비스를 긴 형식으로 표현한 예는 다음과 같아요.

$ docker service create --name my_web --replicas 3 --publish published=8080,target=80 nginx

지정할 수 있는 옵션은 다음과 같아요.

옵션 짧은 구문 긴 구문 설명
publishedtarget 포트 --publish 8080:80 --publish published=8080,target=80 컨테이너 안의 대상 포트와 라우팅 메시(ingress) 또는 호스트 수준 네트워킹을 사용해 노드에 매핑할 포트예요. 이 표 뒤쪽에 더 많은 옵션이 있어요. key-value 구문은 어느 정도 자명하므로 선호돼요
mode 짧은 구문으로는 설정 불가 --publish published=8080,target=80,mode=host 포트를 바인딩하는 데 사용할 모드예요. ingress 또는 host예요. 기본은 라우팅 메시를 사용하는 ingress예요
protocol --publish 8080:80/tcp --publish published=8080,target=80,protocol=tcp 사용할 프로토콜이에요. tcp, udp, 또는 sctp예요. 기본은 tcp예요. 두 프로토콜 모두에 포트를 바인딩하려면 -p 또는 --publish 플래그를 두 번 지정해요

ingress 모드로 서비스 포트를 게시하면 swarm 라우팅 메시는 노드에 서비스 태스크가 실행 중인지 여부와 관계없이 게시된 포트에서 서비스를 모든 노드에서 접근 가능하게 해요. host 모드를 사용하면 포트는 서비스가 실행 중인 노드에서만 바인딩되고, 노드의 특정 포트는 한 번만 바인딩할 수 있어요. 게시 모드는 긴 구문으로만 설정할 수 있어요. 자세한 내용은 Use swarm mode routing mesh를 참고하세요.

관리 서비스 계정용 자격 증명 사양 제공하기 (--credential-spec)

이 옵션은 Windows 컨테이너를 사용하는 서비스에서만 사용돼요. --credential-specfile://<filename> 또는 registry://<value-name> 형식이어야 해요.

file://<filename> 형식을 사용할 때 참조된 파일은 docker 데이터 디렉터리(Windows에서는 기본적으로 C:\ProgramData\Docker\)의 CredentialSpecs 하위 디렉터리에 있어야 해요. 예를 들어 file://spec.json을 지정하면 C:\ProgramData\Docker\CredentialSpecs\spec.json이 로드돼요.

registry://<value-name> 형식을 사용할 때 자격 증명 사양은 데몬 호스트의 Windows 레지스트리에서 읽혀요. 지정된 레지스트리 값은 다음 위치에 있어야 해요.

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Virtualization\Containers\CredentialSpecs

템플릿을 사용해 서비스 만들기

Go의 text/template 패키지가 제공하는 구문을 사용해 service create의 일부 플래그에 템플릿을 사용할 수 있어요. 지원되는 플래그는 다음과 같아요.

  • --hostname
  • --mount
  • --env

Go 템플릿의 유효한 플레이스홀더는 아래에 나열돼 있어요.

플레이스홀더 설명
.Service.ID 서비스 ID
.Service.Name 서비스 이름
.Service.Labels 서비스 레이블
.Node.ID 노드 ID
.Node.Hostname 노드 호스트네임
.Task.ID 태스크 ID
.Task.Name 태스크 이름
.Task.Slot 태스크 슬롯

템플릿 예제

이 예제에서 생성된 컨테이너의 템플릿을 서비스의 이름, 노드의 ID와 호스트네임을 기반으로 설정할 거예요.

$ docker service create \
    --name hosttempl \
    --hostname="{{.Node.Hostname}}-{{.Node.ID}}-{{.Service.Name}}"\
    busybox top

va8ew30grofhjoychbr6iot8c

$ 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}}" 2e7a8a9c4da2-wo41w8hg8qanxwjwsg4kxpprj-hosttempl

x3ti0erg11rjpg64m75kej2mz-hosttempl

Windows에서 격리 모드 지정하기 (--isolation)

기본적으로 Windows 노드에 스케줄링된 태스크는 해당 노드에 구성된 기본 격리 모드를 사용해 실행돼요. 특정 격리 모드를 강제하려면 --isolation 플래그를 사용할 수 있어요.

$ docker service create --name myservice --isolation=process microsoft/nanoserver

Windows에서 지원되는 격리 모드는 다음과 같아요.

  • default: 태스크를 실행하는 노드에 지정된 기본 설정을 사용해요
  • process: 프로세스 격리를 사용해요 (Windows server 전용)
  • hyperv: Hyper-V 격리를 사용해요

Generic Resources를 요청하는 서비스 만들기 (--generic-resource)

--generic-resource 플래그를 사용해(노드가 이 리소스를 광고하는 경우) 태스크가 배치될 수 있는 노드의 종류를 좁힐 수 있어요.

$ docker service create \
    --name cuda \
    --generic-resource "NVIDIA-GPU=2" \
    --generic-resource "SSD=1" \
    nvidia/cuda

job으로 실행하기

job은 작업을 완료까지 실행한 다음 멈추도록 설계된 특별한 종류의 서비스예요. 장기 실행 데몬과는 반대예요. job에 속한 태스크가 성공적으로 종료되면(반환값 0) 태스크는 "Completed"로 표시되고 다시 실행되지 않아요.

job은 두 가지 모드 중 하나로 시작돼요: replicated-job 또는 global-job

$ docker service create --name myjob \
                        --mode replicated-job \
                        bash "true"

이 명령은 하나의 태스크를 실행하며, 태스크는 bash 이미지를 사용해 true 명령을 실행하고 0을 반환한 다음 종료돼요.

job은 결국 다른 종류의 서비스이지만 다른 서비스에 비해 몇 가지 주의 사항이 있어요.

  • 업데이트 또는 롤백 구성 옵션 중 어느 것도 유효하지 않아요. job은 업데이트할 수 있지만 펼치거나 롤백할 수 없으므로 이러한 구성 옵션은 무의미해요.
  • job은 Complete 상태에 도달하면 결코 재시작되지 않아요. 즉 job의 경우 --restart-conditionany로 설정하는 것은 on-failure로 설정하는 것과 같아요.

job은 복제 모드와 전역 모드 모두에서 사용할 수 있어요.

복제 job (Replicated Jobs)

복제 job은 복제 서비스와 비슷해요. --replicas 플래그를 설정하면 실행할 job 반복의 총 수를 지정해요. 기본적으로 복제 job의 모든 복제본은 동시에 시작돼요. 한 번에 동시에 실행되는 복제본의 총 수를 제어하려면 --max-concurrent 플래그를 사용할 수 있어요.

$ docker service create \
    --name mythrottledjob \
    --mode replicated-job \
    --replicas 10 \
    --max-concurrent 2 \
    bash "true"

위 명령은 총 10개의 태스크를 실행하지만 한 번에 2개만 실행돼요.

전역 job (Global Jobs)

전역 job은 전역 서비스와 비슷해요. 배치 제약과 일치하는 각 노드에 태스크가 한 번 실행돼요. 전역 job은 global-job 모드로 표현돼요. 전역 job이 만들어진 후 클러스터에 추가된 새 노드에도 그 job의 태스크가 시작된다는 점을 유의하세요. 전역 job은 job의 제약을 충족하는 모든 노드가 Completed 태스크를 가질 때를 제외하고 전체적으로는 "done" 상태가 없어요.

더 알아보기 (Learn more)