docker run
docker run
docker run은 이미지로 새 컨테이너를 만들고 실행하는 명령이에요. 필요하면 이미지를 가져오고 컨테이너를 시작해서, Docker에서 가장 자주 쓰는 핵심 명령이에요.
출처: 문서
본문
docker run 명령은 새 컨테이너에서 명령을 실행하고, 필요하면 이미지를 가져오며 컨테이너를 시작해요. 정지된 컨테이너는 docker start로 이전 변경 사항을 모두 보존한 채 다시 시작할 수 있어요. 정지된 것을 포함한 모든 컨테이너 목록을 보려면 docker ps -a를 사용해요.
사용법 (Usage)
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
별칭 (Aliases)
별칭은 긴 명령 대신 쓸 수 있는 짧고 기억하기 쉬운 대안이에요.
docker container run
옵션 (Options)
| 옵션 | 기본값 | 설명 |
|---|---|---|
--add-host |
사용자 지정 host-to-IP 매핑을 추가해요 (host:ip) |
|
--annotation |
API 1.43+ 컨테이너에 annotation을 추가해요 (OCI 런타임으로 전달됨) | |
-a, --attach |
STDIN·STDOUT·STDERR에 연결해요 | |
--blkio-weight |
블록 IO (상대 가중치)를 10에서 1000 사이로 정해요. 0이면 비활성화 (기본 0) | |
--blkio-weight-device |
블록 IO 가중치 (상대 디바이스 가중치) | |
--cap-add |
Linux 기능(capabilities)을 추가해요 | |
--cap-drop |
Linux 기능을 제거해요 | |
--cgroup-parent |
컨테이너의 선택적 부모 cgroup이에요 | |
--cgroupns |
API 1.41+ 사용할 cgroup namespace예요 (host|private) |
|
--cidfile |
컨테이너 ID를 파일에 써요 | |
--cpu-count |
CPU 개수 (Windows만) | |
--cpu-percent |
CPU 비율 (Windows만) | |
--cpu-period |
CPU CFS(Completely Fair Scheduler) 주기를 제한해요 | |
--cpu-quota |
CPU CFS(Completely Fair Scheduler) 할당량을 제한해요 | |
--cpu-rt-period |
API 1.25+ CPU 실시간 주기를 마이크로초 단위로 제한해요 | |
--cpu-rt-runtime |
API 1.25+ CPU 실시간 런타임을 마이크로초 단위로 제한해요 | |
-c, --cpu-shares |
CPU 공유 (상대 가중치) | |
--cpus |
API 1.25+ CPU 수예요 | |
--cpuset-cpus |
실행을 허용할 CPU (0-3, 0,1) |
|
--cpuset-mems |
실행을 허용할 MEM (0-3, 0,1) |
|
-d, --detach |
컨테이너를 백그라운드에서 실행하고 컨테이너 ID를 출력해요 | |
--detach-keys |
컨테이너에서 분리할 때 쓸 키 시퀀스를 덮어써요 | |
--device |
컨테이너에 호스트 디바이스를 추가해요 | |
--device-cgroup-rule |
cgroup 허용 디바이스 목록에 규칙을 추가해요 | |
--device-read-bps |
디바이스의 읽기 속도(초당 바이트)를 제한해요 | |
--device-read-iops |
디바이스의 읽기 속도(초당 IO)를 제한해요 | |
--device-write-bps |
디바이스에 쓰기 속도(초당 바이트)를 제한해요 | |
--device-write-iops |
디바이스에 쓰기 속도(초당 IO)를 제한해요 | |
--dns |
사용자 지정 DNS 서버를 설정해요 | |
--dns-option |
DNS 옵션을 설정해요 | |
--dns-search |
사용자 지정 DNS 검색 도메인을 설정해요 | |
--domainname |
컨테이너 NIS 도메인 이름이에요 | |
--entrypoint |
이미지의 기본 ENTRYPOINT를 덮어써요 | |
-e, --env |
환경 변수를 설정해요 | |
--env-file |
환경 변수 파일을 읽어요 | |
--expose |
포트 또는 포트 범위를 노출해요 | |
--gpus |
API 1.40+ 컨테이너에 추가할 GPU 디바이스예요 (all이면 모든 GPU 전달) |
|
--group-add |
추가로 참여할 그룹을 추가해요 | |
--health-cmd |
상태를 확인하기 위해 실행할 명령이에요 | |
--health-interval |
확인 사이의 시간이에요 (ms|s|m|h) (기본 0s) | |
--health-retries |
unhealthy로 보고하기 위해 필요한 연속 실패 횟수예요 | |
--health-start-interval |
API 1.44+ 시작 기간 동안 확인 사이의 시간이에요 (ms|s|m|h) (기본 0s) | |
--health-start-period |
API 1.29+ health-retries 카운트다운을 시작하기 전에 컨테이너가 초기화할 시작 기간이에요 (ms|s|m|h) (기본 0s) | |
--health-timeout |
한 번의 확인이 실행될 최대 시간이에요 (ms|s|m|h) (기본 0s) | |
--help |
사용법을 출력해요 | |
-h, --hostname |
컨테이너 호스트 이름이에요 | |
--init |
API 1.25+ 신호를 전달하고 프로세스를 수거하는 init을 컨테이너 안에서 실행해요 | |
-i, --interactive |
연결되지 않아도 STDIN을 열어 둬요 | |
--io-maxbandwidth |
시스템 드라이브의 최대 IO 대역폭 제한이에요 (Windows만) | |
--io-maxiops |
시스템 드라이브의 최대 IOps 제한이에요 (Windows만) | |
--ip |
IPv4 주소예요 (예: 172.30.100.104) |
|
--ip6 |
IPv6 주소예요 (예: 2001:db8::33) |
|
--ipc |
사용할 IPC 모드예요 | |
--isolation |
컨테이너 격리 기술이에요 | |
-l, --label |
컨테이너에 메타데이터를 설정해요 | |
--label-file |
줄 구분 라벨 파일을 읽어요 | |
--link |
다른 컨테이너에 링크를 추가해요 | |
--link-local-ip |
컨테이너 IPv4/IPv6 링크-로컬 주소예요 | |
--log-driver |
컨테이너의 로깅 드라이버예요 | |
--log-opt |
로그 드라이버 옵션이에요 | |
--mac-address |
컨테이너 MAC 주소예요 (예: 92:d0:c6:0a:29:33) |
|
-m, --memory |
메모리 제한이에요 | |
--memory-reservation |
메모리 소프트 제한이에요 | |
--memory-swap |
메모리+스왑과 같은 스왑 제한이에요. -1이면 무제한 스왑 활성화 |
|
--memory-swappiness |
-1 |
컨테이너 메모리 swappiness를 조절해요 (0에서 100) |
--mount |
컨테이너에 파일시스템 마운트를 연결해요 | |
--name |
컨테이너에 이름을 지정해요 | |
--network |
컨테이너를 네트워크에 연결해요 | |
--network-alias |
컨테이너에 네트워크 범위 별칭을 추가해요 | |
--no-healthcheck |
컨테이너가 지정한 HEALTHCHECK를 비활성화해요 | |
--oom-kill-disable |
OOM Killer를 비활성화해요 | |
--oom-score-adj |
호스트의 OOM 선호도를 조절해요 (-1000에서 1000) | |
--pid |
사용할 PID namespace예요 | |
--pids-limit |
컨테이너 pids 제한을 조절해요 (무제한은 -1) | |
--platform |
API 1.32+ 서버가 멀티플랫폼을 지원하면 플랫폼을 설정해요 | |
--privileged |
이 컨테이너에 확장 권한을 부여해요 | |
-p, --publish |
컨테이너의 포트를 호스트에 게시해요 | |
-P, --publish-all |
모든 노출 포트를 임의의 포트에 게시해요 | |
--pull |
missing |
실행 전에 이미지를 가져올지 정해요 (always, missing, never) |
-q, --quiet |
pull 출력을 숨겨요 | |
--read-only |
컨테이너의 루트 파일시스템을 읽기 전용으로 마운트해요 | |
--restart |
no |
컨테이너가 종료될 때 적용할 재시작 정책이에요 |
--rm |
컨테이너가 종료되면 컨테이너와 연결된 익명 볼륨을 자동으로 제거해요 | |
--runtime |
이 컨테이너에 사용할 런타임이에요 | |
--security-opt |
보안 옵션이에요 | |
--shm-size |
/dev/shm의 크기예요 |
|
--sig-proxy |
true |
받은 신호를 프로세스에 프록시해요 |
--stop-signal |
컨테이너를 중지할 신호예요 | |
--stop-timeout |
API 1.25+ 컨테이너를 중지할 제한 시간(초)이에요 | |
--storage-opt |
컨테이너의 스토리지 드라이버 옵션이에요 | |
--sysctl |
Sysctl 옵션이에요 | |
--tmpfs |
tmpfs 디렉터리를 마운트해요 | |
-t, --tty |
pseudo-TTY를 할당해요 | |
--ulimit |
Ulimit 옵션이에요 | |
--umask |
API 1.56+ 컨테이너의 umask를 설정해요 | |
--use-api-socket |
Docker API 소켓과 필요한 인증을 바인드 마운트해요 | |
-u, --user |
사용자 이름 또는 UID예요 (형식: <name|uid>[:<group|gid>]) |
|
--userns |
사용할 사용자 namespace예요 | |
--uts |
사용할 UTS namespace예요 | |
-v, --volume |
볼륨을 바인드 마운트해요 | |
--volume-driver |
컨테이너의 선택적 볼륨 드라이버예요 | |
--volumes-from |
지정한 컨테이너의 볼륨을 마운트해요 | |
-w, --workdir |
컨테이너 안의 작업 디렉터리예요 |
예제 (Examples)
이름 지정하기 (--name)
--name 플래그로 컨테이너의 사용자 지정 식별자를 지정할 수 있어요. 다음 예제는 nginx:alpine 이미지로 detached 모드에서 test라는 컨테이너를 실행해요.
$ docker run --name test -d nginx:alpine
4bed76d3ad428b889c56c1ecc2bf2ed95cb08256db22dc5ef5863e1d03252a19
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4bed76d3ad42 nginx:alpine "/docker-entrypoint.…" 1 second ago Up Less than a second 80/tcp test
다른 명령에서 컨테이너를 이름으로 참조할 수 있어요. 예를 들어 다음 명령은 test라는 컨테이너를 중지하고 제거해요.
$ docker stop test
test
$ docker rm test
test
--name 플래그로 사용자 지정 이름을 지정하지 않으면 데몬은 vibrant_cannon 같은 임의로 생성된 이름을 컨테이너에 지정해요. 사용자 지정 이름을 쓰면 기억하기 쉬운 ID를 갖는 이점이 있어요. 게다가 컨테이너를 사용자 정의 bridge 네트워크에 연결하면 같은 네트워크의 다른 컨테이너가 DNS로 컨테이너를 이름으로 참조할 수 있어요.
$ docker network create mynet
cb79f45948d87e389e12013fa4d969689ed2c3316985dd832a43aaec9a0fe394
$ docker run --name test --net mynet -d nginx:alpine
58df6ecfbc2ad7c42d088ed028d367f9e22a5f834d7c74c66c0ab0485626c32a
$ docker run --net mynet busybox:latest ping test
PING test (172.18.0.2): 56 data bytes
64 bytes from 172.18.0.2: seq=0 ttl=64 time=0.073 ms
64 bytes from 172.18.0.2: seq=1 ttl=64 time=0.411 ms
64 bytes from 172.18.0.2: seq=2 ttl=64 time=0.319 ms
64 bytes from 172.18.0.2: seq=3 ttl=64 time=0.383 ms
...
컨테이너 ID 캡처하기 (--cidfile)
자동화를 돕기 위해 Docker가 컨테이너 ID를 원하는 파일에 쓰게 할 수 있어요. 이는 일부 프로그램이 프로세스 ID를 파일에 쓰는 것과 비슷해요(PID 파일을 봤을 수도 있어요).
$ docker run --cidfile /tmp/docker_test.cid ubuntu echo "test"
이 명령은 컨테이너를 만들고 콘솔에 test를 출력해요. cidfile 플래그는 Docker가 새 파일을 만들고 그 안에 컨테이너 ID를 쓰게 해요. 파일이 이미 있으면 Docker는 오류를 반환해요. docker run이 종료되면 Docker는 이 파일을 닫아요.
PID 설정 (--pid)
--pid="" : Set the PID (Process) Namespace mode for the container,
'container:<name|id>': joins another container's PID namespace
'host': use the host's PID namespace inside the container
기본적으로 모든 컨테이너는 PID namespace가 활성화돼 있어요. PID namespace는 프로세스의 분리를 제공해요. PID namespace는 시스템 프로세스의 뷰를 제거하고, PID 1을 포함한 프로세스 ID의 재사용을 허용해요. 어떤 경우엔 컨테이너가 호스트의 프로세스 namespace를 공유해서 컨테이너 안의 프로세스가 시스템의 모든 프로세스를 볼 수 있게 하고 싶을 수 있어요. 예를 들어 strace나 gdb 같은 디버깅 도구로 컨테이너를 만들고 싶지만, 이 도구들을 컨테이너 안의 프로세스를 디버깅할 때 사용하고 싶은 경우예요.
예제: 컨테이너 안에서 htop 실행하기
호스트의 프로세스 namespace를 공유하는 컨테이너에서 htop을 실행하려면, --pid=host 옵션으로 alpine 컨테이너를 실행해요.
$ docker run --rm -it --pid=host alpine
컨테이너에 htop을 설치해요.
/ # apk add --quiet htop
htop 명령을 실행해요.
/ # htop
예제: 다른 컨테이너의 PID namespace에 조인하기
다른 컨테이너의 PID namespace에 조인하는 건 그 컨테이너를 디버깅할 때 유용할 수 있어요. Redis 서버를 실행하는 컨테이너를 시작해요.
$ docker run --rm --name my-nginx -d nginx:alpine
my-nginx 컨테이너에 --pid namespace를 연결하는 Alpine 컨테이너를 실행해요.
$ docker run --rm -it --pid=container:my-nginx \
--cap-add SYS_PTRACE \
--security-opt seccomp=unconfined \
alpine
Alpine 컨테이너에 strace를 설치해요.
/ # apk add strace
my-nginx 컨테이너의 프로세스 ID인 프로세스 1에 연결해요.
/ # strace -p 1
strace: Process 1 attached
컨테이너의 namespace 리매핑 비활성화하기 (--userns)
데몬에서 사용자 namespace를 활성화하면 모든 컨테이너가 기본적으로 사용자 namespace로 시작돼요. 특정 컨테이너의 사용자 namespace 리매핑을 비활성화하려면 --userns 플래그를 host로 설정해요.
docker run --userns=host hello-world
host는 --userns 플래그의 유일한 유효한 값이에요. 자세한 내용은 사용자 namespace로 컨테이너 격리하기 문서를 참고해요.
UTS 설정 (--uts)
--uts="" : Set the UTS namespace mode for the container
'host': use the host's UTS namespace inside the container
UTS namespace는 그 안에서 실행되는 프로세스에 보이는 hostname과 도메인을 설정하기 위한 것이에요. 기본적으로 --network=host를 쓰는 컨테이너를 포함해 모든 컨테이너는 자신만의 UTS namespace가 있어요. --uts를 host로 설정하면 컨테이너가 호스트와 같은 UTS namespace를 사용하게 돼요.
참고: Docker는
--hostname·--domainname플래그와--uts=host를 함께 쓰는 걸 허용하지 않아요. 이는 호스트의 UTS namespace에서 실행되는 컨테이너가 호스트 구성 변경을 시도하지 못하게 하기 위해서예요.
컨테이너의 hostname이 호스트의 hostname과 함께 바뀌길 원한다면 UTS namespace를 호스트와 공유하고 싶을 수 있어요. 더 고급 사용 사례는 컨테이너에서 호스트의 hostname을 바꾸는 거예요.
IPC 설정 (--ipc)
--ipc="MODE" : Set the IPC mode for the container
--ipc 플래그는 다음 값을 받아요.
| 값 | 설명 |
|---|---|
"" |
데몬의 기본값을 사용해요 |
"none" |
/dev/shm이 마운트되지 않은 자체 private IPC namespace예요 |
"private" |
자체 private IPC namespace예요 |
"shareable" |
다른 컨테이너와 공유할 수 있는 자체 private IPC namespace예요 |
"container:<name-or-ID>" |
다른 ("shareable") 컨테이너의 IPC namespace에 조인해요 |
"host" |
호스트 시스템의 IPC namespace를 사용해요 |
지정하지 않으면 데몬 기본값이 사용되는데, 데몬 버전·구성에 따라 "private" 또는 "shareable"일 수 있어요. System V 프로세스 간 통신(IPC) namespace는 이름 있는 공유 메모리 세그먼트·세마포어·메시지 큐의 분리를 제공해요. 공유 메모리 세그먼트는 파이프나 네트워크 스택을 통하지 않고 메모리 속도로 프로세스 간 통신을 가속하는 데 쓰여요. 공유 메모리는 데이터베이스와 과학 컴퓨팅·금융 서비스 분야의 고성능 애플리케이션에서 흔히 쓰여요. 이런 애플리케이션을 여러 컨테이너로 나눈다면, 메인("donor") 컨테이너에는 "shareable" 모드로, 다른 컨테이너에는 "container:<donor-name-or-ID>"로 IPC 메커니즘을 공유해야 할 수 있어요.
컨테이너 권한 높이기 (--privileged)
--privileged 플래그는 컨테이너에 다음 기능을 부여해요.
- 모든 Linux 커널 기능을 활성화
- 기본 seccomp 프로필 비활성화
- 기본 AppArmor 프로필 비활성화
- SELinux 프로세스 라벨 비활성화
- 모든 호스트 디바이스에 접근 허용
/sys를 읽기-쓰기로 만들기- cgroups 마운트를 읽기-쓰기로 만들기
즉, 컨테이너는 호스트가 할 수 있는 거의 모든 것을 할 수 있게 돼요. 이 플래그는 Docker 안에서 Docker를 실행하는 같은 특수 사용 사례를 허용하기 위해 존재해요.
경고:
--privileged플래그는 주의해서 사용해요.--privileged컨테이너는 안전하게 샌드박스 처리된 프로세스가 아니에요. 이 모드의 컨테이너는 호스트에서 root 셸을 얻어 시스템을 제어할 수 있어요. 대부분의 사용 사례에서 이 플래그는 선호되는 해결책이 아니에요. 컨테이너에 상승된 권한이 필요하면--cap-add로 개별 커널 기능을 추가하는 식으로 필요한 권한을 명시적으로 부여하는 걸 선호해요.
다음 예제는 기본적으로 Docker가 CAP_SYS_ADMIN(파일시스템 마운트에 필요)를 포함한 대부분의 잠재적으로 위험한 커널 기능을 버리기 때문에 동작하지 않아요.
$ docker run -t -i --rm ubuntu bash
root@bc338942ef20:/# mount -t tmpfs none /mnt
mount: permission denied
--privileged 플래그를 추가하면 동작해요.
$ docker run -t -i --privileged ubuntu bash
root@50e3f57e16e6:/# mount -t tmpfs none /mnt
root@50e3f57e16e6:/# df -h
Filesystem Size Used Avail Use% Mounted on
none 1.9G 0 1.9G 0% /mnt
작업 디렉터리 설정 (-w, --workdir)
$ docker run -w /path/to/dir/ ubuntu pwd
-w 옵션은 지정한 디렉터리 안에서 명령을 실행해요. 이 예제에선 /path/to/dir/이에요. 경로가 존재하지 않으면 Docker가 컨테이너 안에서 그 경로를 만들어요.
컨테이너별 스토리지 드라이버 옵션 설정 (--storage-opt)
$ docker run -it --storage-opt size=120G fedora /bin/bash
이(size)는 생성 시점에 컨테이너 파일시스템 크기를 120G로 제한해요. 이 옵션은 btrfs, overlay2, windowsfilter, zfs 스토리지 드라이버에서만 사용할 수 있어요. overlay2 스토리지 드라이버의 경우 backing 파일시스템이 xfs이고 pquota 마운트 옵션으로 마운트된 경우에만 size 옵션을 쓸 수 있어요. 이러한 조건에서 backing 파일시스템 크기보다 작은 어떤 크기든 전달할 수 있어요. windowsfilter, btrfs, zfs 스토리지 드라이버에서는 기본 BaseFS 크기보다 작은 크기를 전달할 수 없어요.
tmpfs 마운트 (--tmpfs)
--tmpfs 플래그로 tmpfs 마운트를 만들 수 있어요. --tmpfs에 전달할 수 있는 옵션은 Linux mount -t tmpfs -o 명령과 동일해요. 다음 예제는 rw, noexec, nosuid, size=65536k 옵션으로 빈 tmpfs를 컨테이너에 마운트해요.
$ docker run -d --tmpfs /run:rw,noexec,nosuid,size=65536k my_image
볼륨 마운트 (-v)
$ docker run -v $(pwd):$(pwd) -w $(pwd) -i -t ubuntu pwd
위 예제는 -v 플래그로 현재 디렉터리를 같은 경로에 컨테이너에 마운트하고, 작업 디렉터리로 설정한 뒤 컨테이너 안에서 pwd 명령을 실행해요. Docker Engine 23부터 호스트에서 상대 경로를 쓸 수 있어요.
$ docker run -v ./content:/content -w /content -i -t ubuntu pwd
위 예제는 -v 플래그로 현재 디렉터리의 content 디렉터리를 /content 경로에 컨테이너에 마운트하고, 작업 디렉터리로 설정한 뒤 컨테이너 안에서 pwd 명령을 실행해요.
$ docker run -v /doesnt/exist:/foo -w /foo -i -t ubuntu bash
바인드 마운트된 볼륨의 호스트 디렉터리가 존재하지 않으면 Docker가 호스트에 이 디렉터리를 자동으로 만들어줘요. 위 예제에서 Docker는 컨테이너를 시작하기 전에 /doesnt/exist 폴더를 만들어요.
볼륨 읽기 전용 마운트 (--read-only)
$ docker run --read-only -v /icanwrite busybox touch /icanwrite/here
--read-only 플래그와 함께 볼륨을 써서 컨테이너가 파일을 쓰는 위치를 제어할 수 있어요. --read-only 플래그는 컨테이너의 루트 파일시스템을 읽기 전용으로 마운트해서, 지정한 볼륨이 아닌 위치에 대한 쓰기를 금지해요.
$ docker run -t -i -v /var/run/docker.sock:/var/run/docker.sock -v /path/to/static-docker-binary:/usr/bin/docker busybox sh
Docker Unix 소켓과 정적으로 링크된 Docker 바이너리를 바인드 마운트하면 컨테이너에게 호스트의 Docker 데몬을 만들고 조작할 완전한 접근을 줘요. Windows에서는 Windows 스타일 경로 시맨틱으로 경로를 지정해야 해요.
PS C:\> docker run -v c:\foo:c:\dest microsoft/nanoserver cmd /s /c type c:\dest\somefile.txt
Contents of file
PS C:\> docker run -v c:\foo:d: microsoft/nanoserver cmd /s /c type d:\somefile.txt
Contents of file
다음 예제는 Windows 기반 컨테이너에서 실패해요. 컨테이너 안의 볼륨이나 바인드 마운트 대상은 비어 있지 않은·빈 디렉터리이거나 C:가 아닌 드라이브여야 하기 때문이에요. 게다가 바인드 마운트의 소스는 로컬 디렉터리여야 하며 파일이 아니어야 해요.
net use z: \\remotemachine\share
docker run -v z:\foo:c:\dest ...
docker run -v \\uncpath\to\directory:c:\dest ...
docker run -v c:\foo\somefile.txt:c:\dest ...
docker run -v c:\foo:c: ...
docker run -v c:\foo:c:\existing-directory-with-contents ...
--mount 플래그로 바인드 마운트·볼륨 추가하기
--mount 플래그는 컨테이너에 볼륨·호스트 디렉터리·tmpfs 마운트를 마운트할 수 있게 해요. --mount 플래그는 -v 또는 --volume 플래그가 지원하는 대부분의 옵션을 지원하지만 다른 구문을 사용해요. --volume은 폐기할 계획이 없지만 --mount 사용이 권장돼요.
$ docker run --read-only --mount type=volume,target=/icanwrite busybox touch /icanwrite/here
$ docker run -t -i --mount type=bind,src=/data,dst=/data busybox sh
포트 게시 또는 노출 (-p, --expose)
$ docker run -p 127.0.0.1:80:8080/tcp nginx:alpine
이 명령은 컨테이너의 8080 포트를 호스트의 127.0.0.1의 TCP 80 포트에 바인드해요. udp와 sctp 포트도 지정할 수 있어요. Networking overview 페이지가 Docker로 포트를 게시하는 방법을 자세히 설명해요.
참고: 컨테이너의 포트를 게시할 때 IP 주소를 지정하지 않으면(즉
-p 80:80대신-p 127.0.0.1:80:80), Docker는 기본적으로 모든 인터페이스(주소 0.0.0.0)에 포트를 게시해요. 이 포트는 외부에서 접근 가능해요. UFW로 이 특정 포트를 차단하도록 구성했어도 Docker는 자체 iptables 규칙을 관리하므로 마찬가지예요.
$ docker run --expose 80 nginx:alpine
이 명령은 포트를 호스트 시스템 인터페이스에 게시하지 않고 컨테이너의 80 포트를 노출해요.
모든 노출 포트 게시하기 (-P, --publish-all)
$ docker run -P nginx:alpine
-P(또는 --publish-all) 플래그는 모든 노출 포트를 호스트에 게시해요. Docker는 각 노출 포트를 호스트의 임의의 포트에 바인드해요. -P 플래그는 Dockerfile EXPOSE 지시문 또는 docker run의 --expose 플래그로 명시적으로 표시된 포트 번호만 게시해요. 포트 범위는 /proc/sys/net/ipv4/ip_local_port_range가 정의한 임시 포트 범위 안이에요. 단일 포트나 포트 범위를 명시적으로 매핑하려면 -p 플래그를 써요.
pull 정책 설정 (--pull)
--pull 플래그로 (실행과 함께) 컨테이너를 만들 때 이미지 pull 정책을 설정해요. --pull 플래그는 다음 값 중 하나를 받아요.
| 값 | 설명 |
|---|---|
missing (기본) |
이미지가 이미지 캐시에 없으면 가져오고, 그렇지 않으면 캐시된 이미지를 사용해요 |
never |
이미지가 없어도 가져오지 않아요. 이미지 캐시에 이미지가 없으면 오류를 생성해요 |
always |
컨테이너를 만들기 전에 항상 pull을 수행해요 |
이미지에서 컨테이너를 만들(고 실행) 때 데몬은 이미지가 로컬 이미지 캐시에 있는지 확인해요. 이미지가 없으면 CLI로 오류가 반환되어 pull을 시작할 수 있어요. 기본값(missing)은 이미지가 데몬의 이미지 캐시에 없을 때만 가져와요. 이 기본값은 로컬에만 존재하는 이미지(예: Dockerfile에서 빌드했지만 레지스트리에 push하지 않은 이미지)를 실행할 수 있게 하고 네트워킹을 줄여요. always 옵션은 컨테이너를 만들기 전에 항상 pull을 시작해요. 이 옵션은 이미지가 최신임을 보장하고 오래된 이미지 사용을 막지만, push하기 전에 로컬 빌드 이미지를 테스트하려는 상황엔 적합하지 않을 수 있어요(pull이 기존 이미지를 이미지 캐시에서 덮어쓰기 때문). never 옵션은 컨테이너를 만들 때 (암시적) 이미지 pull을 비활성화하고 이미지 캐시에 있는 이미지만 사용해요. 지정한 이미지가 없으면 오류가 생성되고 컨테이너는 만들어지지 않아요. 이 옵션은 네트워킹을 쓸 수 없거나 컨테이너를 만들 때 이미지가 암시적으로 pull되는 것을 막으려는 상황에 유용해요. 다음 예제는 --pull=never 옵션을 설정한 docker run을 보여 주는데, 이미지가 이미지 캐시에 없어 오류를 생성해요.
$ docker run --pull=never hello-world
docker: Error response from daemon: No such image: hello-world:latest.
환경 변수 설정 (-e, --env, --env-file)
$ docker run -e MYVAR1 --env MYVAR2=foo --env-file ./env.list ubuntu bash
-e, --env, --env-file 플래그로 실행 중인 컨테이너에 (배열이 아닌) 간단한 환경 변수를 설정하거나, 실행 중인 이미지의 Dockerfile에 정의된 변수를 덮어써요. 컨테이너를 실행할 때 변수와 값을 정의할 수 있어요.
$ docker run --env VAR1=value1 --env VAR2=value2 ubuntu env | grep VAR
VAR1=value1
VAR2=value2
로컬 환경에 export된 변수도 사용할 수 있어요.
export VAR1=value1
export VAR2=value2
$ docker run --env VAR1 --env VAR2 ubuntu env | grep VAR
VAR1=value1
VAR2=value2
명령을 실행할 때 Docker CLI 클라이언트는 변수가 로컬 환경에서 가지는 값을 확인해 컨테이너에 전달해요. =가 제공되지 않고 그 변수가 로컬 환경에 export되지 않았다면, 컨테이너에서 해당 변수는 설정되지 않아요. 파일에서 환경 변수를 로드할 수도 있어요. 이 파일은 <variable>=value(변수를 주어진 값으로 설정) 또는 <variable>(로컬 환경에서 값을 가져옴) 구문을 사용하고, #는 주석용이에요. #로 시작하는 줄은 줄 주석으로 취급되어 무시되는 반면, 줄의 다른 곳에 나타나는 #는 변수 값의 일부로 취급돼요.
$ cat env.list
# This is a comment
VAR1=value1
VAR2=value2
USER
$ docker run --env-file env.list ubuntu env | grep -E 'VAR|USER'
VAR1=value1
VAR2=value2
USER=jonzeolla
컨테이너에 메타데이터 설정 (-l, --label, --label-file)
라벨은 컨테이너에 메타데이터를 적용하는 key=value 쌍이에요. 두 개의 라벨로 컨테이너에 라벨을 지정하려면:
$ docker run -l my-label --label com.example.foo=bar ubuntu bash
my-label 키는 값을 지정하지 않으므로 라벨은 빈 문자열("")로 기본 설정돼요. 여러 라벨을 추가하려면 라벨 플래그(-l 또는 --label)를 반복해요. 라벨 값을 덮어쓰지 않으려면 key=value가 고유해야 해요. 같은 키·다른 값을 가진 라벨을 지정하면 각 후속 값이 이전 값을 덮어써요. Docker는 마지막에 제공한 key=value를 사용해요. --label-file 플래그로 파일에서 여러 라벨을 로드해요. 파일의 각 라벨을 EOL 표시로 구분해요. 아래 예제는 현재 디렉터리의 labels 파일에서 라벨을 로드해요.
$ docker run --label-file ./labels ubuntu bash
label-file 형식은 환경 변수를 로드하는 형식과 비슷해요 (환경 변수와 달리 라벨은 컨테이너 안에서 실행되는 프로세스에 보이지 않아요). 다음 예제는 label-file 형식을 보여 줘요.
com.example.label1="a label"
# this is a comment
com.example.label2=another\ label
com.example.label3
--label-file 플래그를 여러 개 제공하면 여러 label-file을 로드할 수 있어요.
컨테이너를 네트워크에 연결하기 (--network)
컨테이너를 시작하고 네트워크에 연결하려면 --network 옵션을 사용해요. 실행 중인 컨테이너를 네트워크에 추가하려면 docker network connect 하위 명령을 사용해요. 여러 컨테이너를 같은 네트워크에 연결할 수 있어요. 연결되면 컨테이너는 다른 컨테이너의 IP 주소나 이름만으로 통신할 수 있어요. 멀티호스트 연결을 지원하는 오버레이 네트워크나 사용자 지정 플러그인의 경우, 같은 멀티호스트 네트워크에 연결됐지만 다른 엔진에서 시작된 컨테이너도 이렇게 통신할 수 있어요.
참고: 기본 bridge 네트워크는 컨테이너가 내부 IP 주소로만 서로 통신하도록 허용해요. 사용자 생성 bridge 네트워크는 컨테이너 이름으로 컨테이너 간 DNS 해석을 제공해요.
docker network disconnect 명령으로 컨테이너를 네트워크에서 분리할 수 있어요. 다음 명령은 my-net이라는 네트워크를 만들고 busybox 컨테이너를 my-net 네트워크에 추가해요.
$ docker network create my-net
$ docker run -itd --network=my-net busybox
사용자 정의 네트워크에서 컨테이너를 시작할 때 --ip와 --ip6 플래그로 컨테이너의 IP 주소를 선택할 수도 있어요. 컨테이너에 정적 IP를 지정하려면 네트워크에 대해 서브넷 블록을 지정해야 해요.
$ docker network create --subnet 192.0.2.0/24 my-net
$ docker run -itd --network=my-net --ip=192.0.2.69 busybox
컨테이너를 둘 이상의 네트워크에 연결하려면 --network 옵션을 반복해요.
$ docker network create --subnet 192.0.2.0/24 my-net1
$ docker network create --subnet 192.0.3.0/24 my-net2
$ docker run -itd --network=my-net1 --network=my-net2 busybox
둘 이상의 네트워크에 연결할 때 옵션을 지정하려면 --network 플래그의 확장 구문을 사용해요. 확장 --network 구문에서 쉼표로 구분해 지정할 수 있는 옵션은 다음과 같아요.
| 옵션 | 최상위 동등 | 설명 |
|---|---|---|
name |
네트워크 이름 (필수) | |
alias |
--network-alias |
컨테이너에 네트워크 범위 별칭을 추가해요 |
ip |
--ip |
IPv4 주소 (예: 172.30.100.104) |
ip6 |
--ip6 |
IPv6 주소 (예: 2001:db8::33) |
mac-address |
--mac-address |
컨테이너 MAC 주소 (예: 92:d0:c6:0a:29:33) |
link-local-ip |
--link-local-ip |
컨테이너 IPv4/IPv6 링크-로컬 주소 |
driver-opt |
docker network connect --driver-opt |
네트워크 드라이버 옵션 |
gw-priority |
가장 높은 gw-priority가 기본 게이트웨이를 제공해요. 양수·음수 값을 받아요 |
$ docker network create --subnet 192.0.2.0/24 my-net1
$ docker network create --subnet 192.0.3.0/24 my-net2
$ docker run -itd --network=name=my-net1,ip=192.0.2.42 --network=name=my-net2,ip=192.0.3.42 busybox
net.ipv4., net.ipv6. 또는 net.mpls.로 시작하는 sysctl 설정은 com.docker.network.endpoint.sysctls라는 driver-opt 라벨로 인터페이스별로 설정할 수 있어요. 인터페이스 이름은 문자열 IFNAME이어야 해요. 인터페이스에 대해 둘 이상의 sysctl을 설정하려면 전체 driver-opt 필드를 따옴표로 묶되, 필요하면 쉘용 따옴표를 이스케이프하는 걸 기억해요. 예를 들어 my-net에 대한 인터페이스에 eth0이라는 이름이 주어지면 다음 예제는 net.ipv4.conf.eth0.log_martians=1과 net.ipv4.conf.eth0.forwarding=0 sysctl을 설정하고 IPv4 주소 192.0.2.42를 지정해요.
$ docker network create --subnet 192.0.2.0/24 my-net
$ docker run -itd --network=name=my-net,"driver-opt=com.docker.network.endpoint.sysctls=net.ipv4.conf.IFNAME.log_martians=1,net.ipv4.conf.IFNAME.forwarding=0",ip=192.0.2.42 busybox
참고: 네트워크 드라이버는 수정할 수 있는 sysctl 설정을 제한할 수 있고, 네트워크 운영을 보호하기 위해 향후 새 제한이 추가될 수 있어요.
run명령 사용 시 컨테이너를 네트워크에 연결하는 것에 대한 자세한 내용은 Docker network overview를 참고해요.
컨테이너의 볼륨 마운트하기 (--volumes-from)
$ docker run --volumes-from 777f7dc92da7 --volumes-from ba8c0c54f0f2:ro -i -t ubuntu pwd
--volumes-from 플래그는 참조된 컨테이너에서 정의된 모든 볼륨을 마운트해요. --volumes-from 인자를 반복해 둘 이상의 컨테이너를 지정할 수 있어요. 컨테이너 ID에는 선택적으로 :ro 또는 :rw를 접미사로 붙여 볼륨을 각각 읽기 전용 또는 읽기-쓰기 모드로 마운트할 수 있어요. 기본적으로 Docker는 참조 컨테이너와 같은 모드(읽기 쓰기 또는 읽기 전용)로 볼륨을 마운트해요. SELinux 같은 라벨링 시스템은 컨테이너에 마운트된 볼륨 내용에 적절한 라벨을 배치해야 해요. 라벨이 없으면 보안 시스템이 컨테이너 안에서 실행되는 프로세스가 그 내용을 사용하지 못하게 할 수 있어요. 기본적으로 Docker는 OS가 설정한 라벨을 변경하지 않아요. 컨테이너 컨텍스트에서 라벨을 바꾸려면 볼륨 마운트에 :z 또는 :Z라는 두 접미사 중 하나를 추가할 수 있어요. z 옵션은 두 컨테이너가 볼륨 내용을 공유함을 Docker에 알려요. 그 결과 Docker는 내용을 공유 콘텐츠 라벨로 라벨링해요. 공유 볼륨 라벨은 모든 컨테이너가 내용을 읽기/쓰기할 수 있게 해요. Z 옵션은 내용을 private·비공유 라벨로 라벨링하도록 Docker에 지시해요. private 볼륨은 현재 컨테이너만 사용할 수 있어요.
Detached 모드 (-d, --detach)
--detach(또는 -d) 플래그는 터미널 창을 차지하지 않는 백그라운드 프로세스로 컨테이너를 시작해요. 설계상 detached 모드로 시작된 컨테이너는 --rm 옵션도 지정하지 않으면 컨테이너를 실행하는 데 쓰인 root 프로세스가 종료될 때 종료돼요. -d를 --rm과 함께 쓰면 컨테이너는 종료되거나 데몬이 종료될 때(둘 중 먼저 일어나는 것) 제거돼요. detached 컨테이너에 service x start 명령을 전달하지 마세요. 예를 들어 이 명령은 nginx 서비스를 시작하려고 시도해요.
$ docker run -d -p 80:80 my_image service nginx start
이 명령은 컨테이너 안에서 nginx 서비스 시작에는 성공해요. 그런데 root 프로세스(service nginx start)가 반환되어 detached 컨테이너가 설계대로 중지되므로 detached 컨테이너 패러다임에 실패해요. 결과적으로 nginx 서비스는 시작되지만 사용할 수 없어요. 대신 nginx 웹 서버 같은 프로세스를 시작하려면 다음과 같이 해요.
$ docker run -d -p 80:80 my_image nginx -g 'daemon off;'
detached 컨테이너와 입출력을 하려면 네트워크 연결이나 공유 볼륨을 사용해요. 컨테이너가 더 이상 docker run이 실행된 명령줄을 듣지 않으므로 이것들이 필요해요.
분리 시퀀스 덮어쓰기 (--detach-keys)
분리할 때 쓸 Docker 키 시퀀스를 덮어쓰려면 --detach-keys 옵션을 써요. 이는 Docker 기본 시퀀스가 다른 애플리케이션에서 쓰는 키 시퀀스와 충돌할 때 유용해요. 개별 컨테이너에 대한 시퀀스를 덮어쓰려면 docker attach 명령에 --detach-keys="<sequence>" 플래그를 써요. <sequence>의 형식은 문자 [a-Z]이거나 ctrl-과 a-z(단일 소문자), @, [, \\, _, ^ 중 하나를 조합한 것이에요. 모든 컨테이너에 대해 다른 기본 키 시퀀스를 구성하려면 Configuration file 섹션을 참고해요.
컨테이너에 호스트 디바이스 추가 (--device)
$ docker run -it --rm \
--device=/dev/sdc:/dev/xvdc \
--device=/dev/sdd \
--device=/dev/zero:/dev/foobar \
ubuntu ls -l /dev/{xvdc,sdd,foobar}
brw-rw---- 1 root disk 8, 2 Feb 9 16:05 /dev/xvdc
brw-rw---- 1 root disk 8, 3 Feb 9 16:05 /dev/sdd
crw-rw-rw- 1 root root 1, 5 Feb 9 16:05 /dev/foobar
디바이스를 컨테이너에 직접 노출해야 하는 경우가 많아요. --device 옵션이 그걸 가능하게 해요. 예를 들어 특정 블록 스토리지 디바이스·루프 디바이스·오디오 디바이스를 (그렇지 않으면 비권한) 컨테이너에 추가하고 애플리케이션이 직접 접근하게 하는 경우예요. 기본적으로 컨테이너는 이 디바이스들을 읽고·쓰고·mknod할 수 있어요. 이는 각 --device 플래그에 세 번째 :rwm 옵션 집합으로 덮어쓸 수 있어요. 컨테이너가 privileged 모드로 실행되면 Docker는 지정한 권한을 무시해요.
$ docker run --device=/dev/sda:/dev/xvdc --rm -it ubuntu fdisk /dev/xvdc
Command (m for help): q
$ docker run --device=/dev/sda:/dev/xvdc:r --rm -it ubuntu fdisk /dev/xvdc
You will not be able to write the partition table.
Command (m for help): q
$ docker run --device=/dev/sda:/dev/xvdc:rw --rm -it ubuntu fdisk /dev/xvdc
Command (m for help): q
$ docker run --device=/dev/sda:/dev/xvdc:m --rm -it ubuntu fdisk /dev/xvdc
fdisk: unable to open /dev/xvdc: Operation not permitted
참고:
--device옵션은 일시적(ephemeral) 디바이스와는 안전하게 쓸 수 없어요. 제거될 수 있는 블록 디바이스를 신뢰할 수 없는 컨테이너에--device로 추가하면 안 돼요.
Windows에서 --device 옵션에 전달되는 문자열 형식은 --device=<IdType>/<Id> 형식이에요. Windows Server 2019와 Windows 10 October 2018 Update부터 Windows는 IdType class와 Id는 디바이스 인터페이스 클래스 GUID만 지원해요. Windows 컨테이너 문서에 정의된 표를 참고해 컨테이너가 지원하는 디바이스 인터페이스 클래스 GUID 목록을 확인해요. 프로세스 격리된 Windows 컨테이너에 이 옵션을 지정하면 Docker는 요청된 디바이스 인터페이스 클래스 GUID를 구현하는 모든 디바이스를 컨테이너에서 사용할 수 있게 해요. 예를 들어 아래 명령은 호스트의 모든 COM 포트를 컨테이너에 보이게 해요.
PS C:\> docker run --device=class/86E0D1E0-8089-11D0-9CE4-08003E301F73 mcr.microsoft.com/windows/servercore:ltsc2019
참고:
--device옵션은 프로세스 격리된 Windows 컨테이너에서만 지원되며, 컨테이너 격리가 hyperv이면 오류를 생성해요.
CDI 디바이스
Container Device Interface(CDI)는 컨테이너 런타임이 타사 디바이스와 상호작용할 수 있는 컨테이너를 만들 수 있게 하는 표준화된 메커니즘이에요. CDI는 현재 Linux 컨테이너에서만 지원되며 Docker Engine 28.3.0부터 기본적으로 활성화돼요. CDI로 디바이스 구성은 JSON 또는 YAML 파일을 사용해 선언적으로 정의돼요. 디바이스 노드와 컨테이너가 상호작용할 수 있게 하는 것뿐 아니라 환경 변수·호스트 마운트(공유 객체 같은)·실행 가능한 훅 같은 디바이스용 추가 구성을 지정할 수 있게 해요. --device 플래그로 디바이스의 정규화된 이름을 사용해 CDI 디바이스를 참조할 수 있어요.
$ docker run --device=vendor.com/class=device-name --rm -it ubuntu
이 명령은 지정한 CDI 디바이스 vendor.com/class=device-name에 대한 접근으로 ubuntu 컨테이너를 시작해요. 단, 다음을 가정해요.
- 요청한 디바이스에 대한 유효한 CDI 사양(JSON 또는 YAML 파일)이 데몬을 실행하는 시스템의 구성된 CDI 사양 디렉터리 중 하나에 있어야 해요.
- CDI 기능이 데몬에서 활성화돼 있어야 해요.
STDIN/STDOUT/STDERR에 연결 (-a, --attach)
--attach(또는 -a) 플래그는 docker run에게 컨테이너의 STDIN·STDOUT·STDERR에 바인드하라고 지시해요. 이렇게 하면 필요에 따라 출력과 입력을 조작할 수 있어요. 연결하고 싶은 세 가지 표준 스트림(STDIN, STDOUT, STDERR) 중 어떤 것을 지정할 수 있어요.
$ docker run -a stdin -a stdout -i -t ubuntu /bin/bash
다음 예제는 컨테이너의 STDIN에만 연결해 데이터를 파이프하고 컨테이너의 ID를 출력해요.
$ echo "test" | docker run -i -a stdin ubuntu cat -
다음 예제는 출력이 컨테이너의 STDERR에만 연결되어 오류가 없으면 콘솔에 아무것도 출력하지 않아요. 컨테이너의 로그에는 여전히 STDERR와 STDOUT에 쓰인 것이 저장돼요.
$ docker run -a stderr ubuntu echo test
다음 예제는 --attach를 사용해 파일을 컨테이너로 파이프하는 방법을 보여 줘요. 명령은 빌드가 완료된 후 컨테이너의 ID를 출력하고, docker logs로 빌드 로그를 검색할 수 있어요. 파일이나 다른 것을 컨테이너로 파이프하고 컨테이너 실행이 끝나면 컨테이너 ID를 검색해야 할 때 유용해요.
$ cat somefile | docker run -i -a stdin mybuilder dobuild
참고: 컨테이너 안에서 PID 1로 실행되는 프로세스는 Linux에서 특별히 처리돼요. 기본 동작이 있는 신호를 무시하죠. 그래서 그 프로세스는 특별히 그렇게 코딩되지 않는 한 SIGINT나 SIGTERM으로 종료되지 않아요.
docker cp명령도 참고해요.
STDIN 열어 두기 (-i, --interactive)
--interactive(또는 -i) 플래그는 컨테이너의 STDIN을 열어 두고 표준 입력을 통해 컨테이너에 입력을 보낼 수 있게 해요.
$ echo hello | docker run --rm -i busybox cat
hello
-i 플래그는 --tty 플래그와 함께 가장 자주 쓰여, 컨테이너의 I/O 스트림을 pseudo 터미널에 바인드해 컨테이너의 대화형 터미널 세션을 만들어요. pseudo-TTY 할당에 대한 더 많은 예제는 해당 섹션을 참고해요.
$ docker run -it debian
root@10a3e71492b0:/# factor 90
90: 2 3 3 5
root@10a3e71492b0:/# exit
exit
-i 플래그를 단독으로 쓰면 컨테이너에 입력을 파이프하는 같은 합성(composition)이 가능해요.
$ docker run --rm -i busybox echo "foo bar baz" \
| docker run --rm -i busybox awk '{ print $2 }' \
| docker run --rm -i busybox rev
rab
init 프로세스 지정하기
--init 플래그로 컨테이너의 PID 1로 init 프로세스를 사용하도록 지시할 수 있어요. init 프로세스를 지정하면 좀비 프로세스 수거 같은 init 시스템의 일반적인 책임이 생성된 컨테이너 안에서 수행되게 보장해요. 사용되는 기본 init 프로세스는 Docker 데몬 프로세스의 시스템 경로에서 찾은 첫 번째 docker-init 실행 파일이에요. 기본 설치에 포함된 이 docker-init 바이너리는 tini를 기반으로 해요.
pseudo-TTY 할당 (-t, --tty)
--tty(또는 -t) 플래그는 컨테이너에 pseudo-TTY를 연결해서 터미널을 컨테이너의 I/O 스트림에 연결해요. 컨테이너에 pseudo-TTY를 할당하면 TTY 디바이스가 제공하는 입력·출력 기능에 접근할 수 있게 돼요. 예를 들어 다음 명령은 debian 컨테이너에서 passwd 명령을 실행해 root 사용자의 새 암호를 설정해요.
$ docker run -i debian passwd root
New password: karjalanpiirakka9
Retype new password: karjalanpiirakka9
passwd: password updated successfully
-i 플래그만으로 이 명령을 실행하면(컨테이너의 STDIN에 텍스트를 보낼 수 있게 함) passwd 프롬프트는 암호를 평문으로 표시해요. 그런데 -t 플래그도 함께 추가하면 암호가 숨겨져요.
$ docker run -it debian passwd root
New password:
Retype new password:
passwd: password updated successfully
이는 passwd가 echo-off TTY 기능으로 터미널에 문자 출력을 억제할 수 있기 때문이에요. -t 플래그를 -i 플래그 없이 쓸 수 있어요. 이것도 컨테이너에 pseudo-TTY를 할당하지만 STDIN에 쓸 방법은 없어요. 컨테이너의 출력이 TTY 환경을 요구하는 경우에만 유용할 수 있어요.
사용자 지정 cgroup 지정하기
--cgroup-parent 플래그로 컨테이너를 실행할 특정 cgroup을 전달할 수 있어요. 이렇게 하면 cgroup을 직접 만들고 관리할 수 있어요. 그 cgroup들에 사용자 지정 리소스를 정의하고 컨테이너를 공통 부모 그룹 아래에 둘 수 있어요.
동적으로 생성된 디바이스 사용 (--device-cgroup-rule)
Docker는 생성 시점에 컨테이너가 사용할 수 있는 디바이스를 지정해요. 지정된 디바이스는 cgroup.allow 파일에 추가되고 실행될 때 컨테이너에 생성돼요. 이는 실행 중인 컨테이너에 새 디바이스를 추가해야 할 때 문제가 돼요. 한 해결책은 컨테이너에 더 넓은 범위의 디바이스에 접근할 수 있게 하는 더 관대한 규칙을 추가하는 거예요. 예를 들어 컨테이너가 메이저 번호 42와 임의 개수의 마이너 번호(새 디바이스가 나타날 때 추가됨)를 가진 문자 디바이스에 접근해야 한다고 가정하면 다음 규칙을 추가해요.
$ docker run -d --device-cgroup-rule='c 42:* rmw' --name my-container my-image
그다음 사용자는 udev에 새 디바이스가 추가될 때 docker exec my-container mknod newDevX c 42 <minor>를 실행하는 스크립트를 실행하도록 요청할 수 있어요.
참고: 처음에 존재하는 디바이스는 여전히
docker run/docker create명령에 명시적으로 추가해야 해요.
NVIDIA GPU 접근하기
--gpus 플래그로 NVIDIA GPU 리소스에 접근할 수 있어요. 먼저 nvidia-container-runtime을 설치해야 해요. 컨테이너의 리소스 지정 문서를 참고해 자세한 정보를 얻을 수도 있고, --device 플래그로 GPU를 CDI 디바이스로 지정할 수도 있어요.
--gpus를 사용하려면 사용할 GPU(또는 전부)를 지정해요. 값을 제공하지 않으면 Docker는 사용 가능한 모든 GPU를 사용해요. 아래 예제는 사용 가능한 모든 GPU를 노출해요.
$ docker run -it --rm --gpus all ubuntu nvidia-smi
GPU를 지정하려면 device 옵션을 사용해요. 아래 예제는 특정 GPU를 노출해요.
$ docker run -it --rm --gpus device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a ubuntu nvidia-smi
아래 예제는 첫 번째와 세 번째 GPU를 노출해요.
$ docker run -it --rm --gpus '"device=0,2"' ubuntu nvidia-smi
재시작 정책 (--restart)
--restart 플래그로 컨테이너의 재시작 정책을 지정해요. 재시작 정책은 Docker 데몬이 컨테이너 종료 후 컨테이너를 재시작할지 제어해요. Docker는 다음 재시작 정책을 지원해요.
| 플래그 | 설명 |
|---|---|
no |
컨테이너를 자동으로 재시작하지 않아요. (기본) |
on-failure[:max-retries] |
컨테이너가 오류(0이 아닌 종료 코드로 나타남)로 인해 종료되면 재시작해요. 선택적으로 :max-retries 옵션으로 Docker 데몬이 컨테이너를 재시작하려는 횟수를 제한해요. on-failure 정책은 컨테이너가 실패로 종료될 때만 재시작을 유도해요. 데몬이 재시작되면 컨테이너를 재시작하지 않아요 |
always |
컨테이너가 멈추면 항상 재시작해요. 수동으로 중지하면 Docker 데몬이 재시작되거나 컨테이너 자체가 수동으로 재시작될 때만 재시작돼요 |
unless-stopped |
always와 비슷하지만, 컨테이너가 (수동 또는 기타로) 중지되면 Docker 데몬이 재시작된 후에도 재시작되지 않아요 |
$ docker run --restart=always redis
이 명령은 always 재시작 정책으로 redis 컨테이너를 실행해요. 컨테이너가 종료되면 Docker가 재시작해요. 컨테이너에 재시작 정책이 활성화되면 docker ps에서 Up 또는 Restarting으로 표시돼요. docker events로 재시작 정책이 적용되는 것을 보는 것도 유용할 수 있어요. 서버가 넘치는 것을 방지하기 위해 각 재시작 전에 증가하는 지연(100밀리초에서 시작해 이전 지연의 두 배)이 추가돼요. 즉 데몬은 100ms, 200ms, 400, 800, 1600... 순으로 기다리다 on-failure 제한, 최대 지연 1분, 또는 docker stop·docker rm -f을 만날 때까지 기다려요. 컨테이너가 성공적으로 재시작되면(시작되어 최소 10초 동안 실행) 지연은 기본값 100ms로 재설정돼요.
재시작 시도 횟수 지정하기
on-failure 정책을 쓸 때 Docker가 컨테이너를 재시작하려는 최대 횟수를 지정할 수 있어요. 기본적으로 Docker는 컨테이너 재시작 시도를 중단하지 않아요. 다음 예제는 redis 컨테이너를 on-failure 재시작 정책과 최대 재시작 횟수 10으로 실행해요.
$ docker run --restart=on-failure:10 redis
redis 컨테이너가 0이 아닌 종료 상태로 연속해서 10번 이상 종료되면 Docker는 재시작 시도를 중단해요. 최대 재시작 제한 제공은 on-failure 정책에서만 유효해요.
컨테이너 재시작 검사하기
컨테이너의 (시도된) 재시작 횟수는 docker inspect 명령으로 얻을 수 있어요. 예를 들어 my-container 컨테이너의 재시작 횟수를 얻으려면:
$ docker inspect -f "{{ .RestartCount }}" my-container
2
또는 컨테이너가 마지막으로 (재)시작된 시점을 얻으려면:
$ docker inspect -f "{{ .State.StartedAt }}" my-container
2015-03-04T23:47:07.691840179Z
--restart(재시작 정책)와 --rm(정리) 플래그를 결합하면 오류가 발생해요. 컨테이너 재시작 시 연결된 클라이언트는 연결이 끊겨요.
정리 (--rm)
기본적으로 컨테이너의 파일시스템은 컨테이너가 종료된 후에도 유지돼요. 이렇게 하면 디버깅이 훨씬 쉬운데, 컨테이너의 최종 상태를 검사하고 모든 데이터를 보존할 수 있기 때문이에요. 단기 포그라운드 프로세스를 실행한다면 이런 컨테이너 파일시스템이 쌓이기 시작할 수 있어요. Docker가 컨테이너를 자동으로 정리하고 컨테이너가 종료될 때 파일시스템을 제거하길 원하면 --rm 플래그를 사용해요.
--rm: Automatically remove the container and its associated anonymous volumes when it exits
참고:
--rm플래그를 설정하면 Docker는 컨테이너가 제거될 때 컨테이너와 연결된 익명 볼륨도 제거해요. 이는docker rm -v my-container를 실행하는 것과 비슷해요. 이름 없이 지정된 볼륨만 제거돼요. 예를 들어 다음 명령을 실행하면/foo볼륨은 제거되지만/bar는 제거되지 않아요.
$ docker run --rm -v /foo -v awesome:/bar busybox top
--volumes-from으로 상속된 볼륨도 같은 로직으로 제거돼요. 원래 볼륨이 이름으로 지정됐다면 제거되지 않아요.
컨테이너 hosts 파일에 항목 추가 (--add-host)
--add-host 플래그를 하나 이상 사용해 컨테이너의 /etc/hosts 파일에 다른 호스트를 추가할 수 있어요. 이 예제는 my-hostname이라는 호스트에 정적 주소를 추가해요.
$ docker run --add-host=my-hostname=8.8.8.8 --rm -it alpine
/ # ping my-hostname
PING my-hostname (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: seq=0 ttl=37 time=93.052 ms
64 bytes from 8.8.8.8: seq=1 ttl=37 time=92.467 ms
64 bytes from 8.8.8.8: seq=2 ttl=37 time=92.252 ms
^C
--- my-hostname ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 92.209/92.495/93.052 ms
IPv6 주소는 대괄호로 감쌀 수 있어요.
$ docker run --add-host my-hostname=[2001:db8::33] --rm -it alpine
--add-host 플래그는 호스트의 내부 IP 주소로 해석되는 특수 host-gateway 값을 지원해요. 컨테이너가 호스트 머신에서 실행되는 서비스에 연결하길 원할 때 유용해요. 보통 host.docker.internal을 host-gateway를 가리키는 hostname으로 쓰는 게 관례예요. Docker Desktop은 이 hostname을 자동으로 해석해요. 다음 예제는 특수 host-gateway 값이 어떻게 동작하는지 보여 줘요. 예제는 파일을 호스트에서 host.docker.internal hostname(호스트의 내부 IP로 해석됨)을 통해 컨테이너로 서빙하는 HTTP 서버를 실행해요.
$ echo "hello from host!" > ./hello
$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
$ docker run \
--add-host host.docker.internal=host-gateway \
curlimages/curl -s host.docker.internal:8000/hello
hello from host!
--add-host 플래그는 : 구분자도 받아요.
$ docker run --add-host=my-hostname:8.8.8.8 --rm -it alpine
로깅 드라이버 (--log-driver)
컨테이너는 Docker 데몬과 다른 로깅 드라이버를 가질 수 있어요. docker run 명령에 --log-driver=<DRIVER>를 사용해 컨테이너의 로깅 드라이버를 구성해요. 지원되는 로깅 드라이버와 사용법을 배우려면 로깅 드라이버 구성 문서를 참고해요. 컨테이너에 대해 로깅을 비활성화하려면 --log-driver 플래그를 none으로 설정해요.
$ docker run --log-driver=none -d nginx:alpine
5101d3b7fe931c27c2ba0e65fd989654d297393ad65ae238f20b97a020e7295b
$ docker logs 5101d3b
Error response from daemon: configured logging driver does not support reading
컨테이너에서 ulimit 설정 (--ulimit)
컨테이너에서 ulimit 설정은 기본 컨테이너에서 사용할 수 없는 추가 권한이 필요하므로 --ulimit 플래그로 설정할 수 있어요. <type>=<soft limit>[:<hard limit>] 형식으로 소프트·하드 제한과 함께 --ulimit를 지정해요. 예를 들어:
$ docker run --ulimit nofile=1024:1024 --rm debian sh -c "ulimit -n"
1024
참고: 하드 제한 값을 제공하지 않으면 Docker는 두 값 모두에 소프트 제한 값을 사용해요. 값을 제공하지 않으면 데몬에 설정된 기본 ulimit에서 상속돼요.
as옵션은 폐기됐어요. 즉 다음 스크립트는 지원되지 않아요.
$ docker run -it --ulimit as=1024 fedora /bin/bash
--ulimit에 대한 지원 옵션은 다음과 같아요.
| 옵션 | 설명 |
|---|---|
core |
생성된 코어 파일의 최대 크기 (RLIMIT_CORE) |
cpu |
CPU 시간 제한(초) (RLIMIT_CPU) |
data |
최대 데이터 세그먼트 크기 (RLIMIT_DATA) |
fsize |
최대 파일 크기 (RLIMIT_FSIZE) |
locks |
최대 파일 잠금 수 (RLIMIT_LOCKS) |
memlock |
최대 잠긴 메모리 주소 공간 (RLIMIT_MEMLOCK) |
msgqueue |
POSIX 메시지 큐의 최대 바이트 (RLIMIT_MSGQUEUE) |
nice |
최대 nice 우선순위 조정 (RLIMIT_NICE) |
nofile |
최대 열린 파일 디스크립터 수 (RLIMIT_NOFILE) |
nproc |
사용 가능한 최대 프로세스 수 (RLIMIT_NPROC) |
rss |
최대 상주 세트 크기 (RLIMIT_RSS) |
rtprio |
최대 실시간 스케줄링 우선순위 (RLIMIT_RTPRIO) |
rttime |
최대 실시간 실행 시간 (RLIMIT_RTTIME) |
sigpending |
최대 보류 신호 수 (RLIMIT_SIGPENDING) |
stack |
최대 스택 크기 (RLIMIT_STACK) |
Docker는 값을 적절한 OS syscall에 보내고 바이트 변환을 수행하지 않아요. 값을 설정할 때 이 점을 고려해요.
nproc 사용 시 주의
ulimit 플래그로 nproc를 설정할 때 주의해야 해요. Linux는 nproc를 사용자(컨테이너가 아니라)가 사용할 수 있는 최대 프로세스 수를 설정하는 데 사용하기 때문이에요. 예를 들어 daemon 사용자로 네 개의 컨테이너를 시작해요.
$ docker run -d -u daemon --ulimit nproc=3 busybox top
$ docker run -d -u daemon --ulimit nproc=3 busybox top
$ docker run -d -u daemon --ulimit nproc=3 busybox top
$ docker run -d -u daemon --ulimit nproc=3 busybox top
4번째 컨테이너는 [8] System error: resource temporarily unavailable 오류와 함께 실패해요. 호출자가 nproc=3을 설정해서 처음 세 컨테이너가 daemon 사용자에 설정된 3개 프로세스 할당량을 모두 사용해 버렸기 때문이에요.
컨테이너의 umask 설정 (--umask)
--umask 플래그는 컨테이너 프로세스의 umask를 설정하며, 컨테이너 안에서 생성된 파일·디렉터리의 기본 권한을 제어해요. 값은 8진수 표기로 지정해야 해요. 앞의 0은 선택사항이에요. 예를 들어 umask 022는 새 파일을 644 권한(-rw-r--r--), 새 디렉터리를 755 권한(drwxr-xr-x)으로 만들어요. --umask 플래그를 설정하지 않으면 OCI 런타임이 자체 기본 umask를 적용해요. 기본 OCI 런타임(runc)은 기본 umask 0022를 사용하지만 이 값은 구현 정의이며 OCI 런타임마다 다를 수 있어요.
$ docker run --rm busybox sh -c umask
0022
--umask 플래그가 설정되면 그 값은 컨테이너의 entrypoint, docker exec로 시작된 프로세스, healthcheck에 사용되는 OCI 프로세스 구성에 포함돼요. OCI 런타임이 값을 존중하는지는 런타임에 따라 달라지며, runc는 docker exec와 healthcheck에 대해 구성된 umask를 존중해요. 사용자 지정 umask를 설정하려면 --umask 플래그를 8진수 값과 함께 사용해요.
$ docker run --rm --umask 077 busybox sh -c umask
0077
umask는 나중에 docker exec로 시작된 프로세스에도 적용돼요.
$ docker run --rm -d --name umask-test --umask 077 busybox sleep 60
$ docker exec umask-test sh -c umask
0077
umask를 0으로 설정하면 어떤 권한 비트도 마스킹하지 않아서 파일·디렉터리는 전체 권한을 유지해요. 다음 예제는 컨테이너 안에 파일과 디렉터리를 만들어요.
$ docker run --rm --umask 0 busybox sh -c 'touch file && mkdir dir && stat -c "%a %n" file dir'
666 file
777 dir
신호로 컨테이너 중지하기 (--stop-signal)
--stop-signal 플래그는 컨테이너가 종료되도록 시스템 콜 신호를 보내요. 이 신호는 SIG<NAME> 형식의 신호 이름(예: SIGKILL)이거나, 커널의 syscall 테이블 위치에 해당하는 부호 없는 숫자(예: 9)일 수 있어요. 기본값은 이미지의 STOPSIGNAL로 정의되며, 이미지에 STOPSIGNAL이 정의되지 않았으면 SIGTERM이에요.
선택적 보안 옵션 (--security-opt)
| 옵션 | 설명 |
|---|---|
--security-opt="label=user:USER" |
컨테이너의 라벨 사용자를 설정해요 |
--security-opt="label=role:ROLE" |
컨테이너의 라벨 역할을 설정해요 |
--security-opt="label=type:TYPE" |
컨테이너의 라벨 유형을 설정해요 |
--security-opt="label=level:LEVEL" |
컨테이너의 라벨 수준을 설정해요 |
--security-opt="label=disable" |
컨테이너의 라벨 격리(confinement)를 꺼요 |
--security-opt="apparmor=PROFILE" |
컨테이너에 적용할 apparmor 프로필을 설정해요 |
--security-opt="no-new-privileges=true" |
컨테이너 프로세스가 새 권한을 얻는 것을 비활성화해요 |
--security-opt="seccomp=unconfined" |
컨테이너의 seccomp 격리를 꺼요 |
--security-opt="seccomp=builtin" |
컨테이너의 기본(내장) seccomp 프로필을 사용해요. 사용자 지정 기본 프로필이 설정되거나 seccomp가 비활성화된("unconfined") 데몬에서 컨테이너 실행 시 seccomp를 켜는 데 쓸 수 있어요 |
--security-opt="seccomp=profile.json" |
seccomp 필터로 사용할 화이트리스트 syscalls seccomp Json 파일 |
--security-opt="systempaths=unconfined" |
컨테이너의 시스템 경로(마스킹된 경로, 읽기 전용 경로) 격리를 꺼요 |
--security-opt 플래그로 컨테이너의 기본 라벨링 체계를 덮어쓸 수 있어요. 다음 명령에서 수준을 지정하면 컨테이너 간에 같은 내용을 공유할 수 있어요.
$ docker run --security-opt label=level:s0:c100,c200 -it fedora bash
참고: MLS 라벨의 자동 번역은 지원되지 않아요. 컨테이너의 보안 라벨링을 완전히 끄려면
label=disable을 사용할 수 있어요.
$ docker run --security-opt label=disable -it ubuntu bash
컨테이너 안의 프로세스에 더 엄격한 보안 정책을 원한다면 사용자 지정 유형 라벨을 지정할 수 있어요. 다음 예제는 Apache 포트에서만 수신하도록 허용된 컨테이너를 실행해요.
$ docker run --security-opt label=type:svirt_apache_t -it ubuntu bash
참고:
svirt_apache_t유형을 정의하는 정책을 작성해야 해요. 컨테이너 프로세스가 추가 권한을 얻는 것을 방지하려면 다음 명령을 사용할 수 있어요.
$ docker run --security-opt no-new-privileges -it ubuntu bash
이것은 su나 sudo처럼 권한을 높이는 명령이 더 이상 동작하지 않음을 의미해요. 또한 seccomp 필터가 권한이 제거된 후 더 늦게 적용되게 하는데, 이는 더 제한적인 필터 집합을 가질 수 있다는 뜻일 수 있어요. Windows에서는 --security-opt 플래그로 credentialspec 옵션을 지정할 수 있어요. credentialspec은 file://spec.txt 또는 registry://keyname 형식이어야 해요.
제한 시간으로 컨테이너 중지하기 (--stop-timeout)
--stop-timeout 플래그는 사전 정의된(--stop-signal 참고) 시스템 콜 신호를 보낸 뒤 컨테이너가 중지할 때까지 기다릴 시간(초)을 설정해요. 제한 시간이 지나도 컨테이너가 종료되지 않으면 SIGKILL 신호로 강제 종료돼요. --stop-timeout을 -1로 설정하면 제한 시간이 적용되지 않고 데몬이 컨테이너가 종료될 때까지 무기한 기다려요. 데몬이 기본값을 결정하며, Linux 컨테이너는 10초, Windows 컨테이너는 30초예요.
컨테이너 격리 기술 지정 (--isolation)
이 옵션은 Windows에서 Docker 컨테이너를 실행할 때 유용해요. --isolation=<value> 옵션은 컨테이너의 격리 기술을 설정해요. Linux에서 지원되는 것은 Linux 네임스페이스를 쓰는 기본 옵션뿐이에요. 이 두 명령은 Linux에서 동등해요.
$ docker run -d busybox top
$ docker run -d --isolation default busybox top
Windows에서 --isolation은 다음 값 중 하나를 받아요.
| 값 | 설명 |
|---|---|
default |
Docker 데몬의 --exec-opt이 지정하거나 시스템 기본값(아래 참고)을 사용해요 |
process |
공유-커널 namespace 격리예요 |
hyperv |
Hyper-V 하이퍼바이저 파티션 기반 격리예요 |
Windows 서버 운영 체제의 기본 격리는 process이고, Windows 10 같은 Windows 클라이언트 OS에서는 hyperv예요. 프로세스 격리는 성능이 더 좋지만 이미지와 호스트가 같은 커널 버전을 사용해야 해요. Windows 서버에서 기본 구성을 가정하면 다음 명령은 동등하며 process 격리를 결과로 내요.
PS C:\> docker run -d microsoft/nanoserver powershell echo process
PS C:\> docker run -d --isolation default microsoft/nanoserver powershell echo process
PS C:\> docker run -d --isolation process microsoft/nanoserver powershell echo process
Docker 데몬에 --exec-opt isolation=hyperv 옵션을 설정했거나 Windows 클라이언트 기반 데몬에 대해 실행 중이라면 다음 명령은 동등하며 hyperv 격리를 결과로 내요.
PS C:\> docker run -d microsoft/nanoserver powershell echo hyperv
PS C:\> docker run -d --isolation default microsoft/nanoserver powershell echo hyperv
PS C:\> docker run -d --isolation hyperv microsoft/nanoserver powershell echo hyperv
컨테이너에 사용 가능한 메모리의 하드 제한 지정 (-m, --memory)
이 매개변수들은 항상 컨테이너에 사용 가능한 메모리에 상한을 설정해요. Linux는 이를 cgroup에 설정하고 컨테이너의 애플리케이션은 /sys/fs/cgroup/memory/memory.limit_in_bytes에서 조회할 수 있어요. Windows에서 사용하는 격리 유형에 따라 컨테이너에 다르게 영향을 미쳐요. process 격리를 쓰면 Windows는 컨테이너 안에서 실행되는 애플리케이션에 제한이 아니라 호스트 시스템의 전체 메모리를 보고해요.
PS C:\> docker run -it -m 2GB --isolation=process microsoft/nanoserver powershell Get-ComputerInfo *memory*
CsTotalPhysicalMemory : 17064509440
CsPhyicallyInstalledMemory : 16777216
OsTotalVisibleMemorySize : 16664560
OsFreePhysicalMemory : 14646720
OsTotalVirtualMemorySize : 19154928
OsFreeVirtualMemory : 17197440
OsInUseVirtualMemory : 1957488
OsMaxProcessMemorySize : 137438953344
hyperv 격리를 쓰면 Windows는 메모리 제한과 컨테이너를 호스팅하는 데 필요한 최소 OS를 담을 만큼 큰 유틸리티 VM을 만들어요. 그 크기가 "Total Physical Memory"로 보고돼요.
PS C:\> docker run -it -m 2GB --isolation=hyperv microsoft/nanoserver powershell Get-ComputerInfo *memory*
CsTotalPhysicalMemory : 2683355136
CsPhyicallyInstalledMemory :
OsTotalVisibleMemorySize : 2620464
OsFreePhysicalMemory : 2306552
OsTotalVirtualMemorySize : 2620464
OsFreeVirtualMemory : 2356692
OsInUseVirtualMemory : 263772
OsMaxProcessMemorySize : 137438953344
런타임에 namespaced 커널 매개변수(sysctls) 구성 (--sysctl)
--sysctl은 컨테이너에 namespaced 커널 매개변수(sysctls)를 설정해요. 예를 들어 컨테이너의 네트워크 namespace에서 IP 포워딩을 켜려면 이 명령을 실행해요.
$ docker run --sysctl net.ipv4.ip_forward=1 someimage
참고: 모든 sysctl이 namespaced인 것은 아니에요. Docker는 호스트 시스템도 수정하는 sysctl을 컨테이너 안에서 변경하는 것을 지원하지 않아요. 커널이 진화함에 따라 더 많은 sysctl이 namespaced가 될 것으로 기대해요.
현재 지원되는 sysctl:
- IPC Namespace:
kernel.msgmax,kernel.msgmnb,kernel.msgmni,kernel.sem,kernel.shmall,kernel.shmmax,kernel.shmmni,kernel.shm_rmid_forced.fs.mqueue.*로 시작하는 sysctls.--ipc=host옵션을 쓰면 이 sysctls는 허용되지 않아요. - Network Namespace:
net.*로 시작하는 sysctls.--network=host옵션을 쓰면 이 sysctls 사용은 허용되지 않아요.