Docker
고친 사람 github-actions[bot]
Docker는 프로그램과 그 프로그램이 돌아가는 데 필요한 파일을 한 덩이로 묶어 줍니다. 그 덩이를 컨테이너라고 부릅니다. 컨테이너를 받은 기계는 미리 무엇을 깔아 두지 않아도 같은 방식으로 프로그램을 띄웁니다. 띄운 컨테이너 하나하나는 같은 기계에서 도는 다른 프로그램과 갈라져 돕니다.
쉽고 빠른 이해
- 무슨 일을 하나 — 애플리케이션을 컨테이너에 담아 띄웁니다. 파이썬으로 짠 웹 서버를 파이썬 설치본까지 함께 묶어 두면, 파이썬이 없는 기계에서도 그 컨테이너를 받아 띄울 수 있습니다.
- 왜 이렇게 하나 — 프로그램 하나를 남과 갈라 놓자고 운영체제를 한 벌 통째로 띄우는 것은 무겁습니다. 컨테이너는 기계에서 이미 도는 운영체제를 함께 쓰므로 같은 장비에서 더 많이 띄울 수 있습니다.
- 어떻게 도나
- 무엇을 담고 무엇을 실행할지 적은 파일을 두고 컨테이너의 틀을 짓습니다.
- 그 틀을 공용 보관소에 올리고, 띄울 기계에서 내려받습니다.
- 내려받은 틀 위에 쓰고 지울 수 있는 칸을 하나 얹어 컨테이너로 띄웁니다.
- 대가 — 컨테이너를 지우면 그 안에서 쓴 파일이 함께 사라집니다. 남기려면 따로 저장할 곳을 붙여 줘야 합니다.
- 언제 안 쓰나 — 쓰기가 많은 프로그램은 값을 치릅니다. 데이터베이스처럼 디스크에 계속 쓰는 것은 컨테이너 안에 그냥 두지 않습니다.
상세
Docker는 애플리케이션을 개발하고 나르고 실행하는 데 쓰는 열린 플랫폼입니다. 애플리케이션을 인프라에서 떼어 놓아 소프트웨어를 빨리 내보낼 수 있게 하는 것이 공식 문서가 밝힌 목적입니다. 떼어 놓는 수단이 컨테이너입니다.
컨테이너는 느슨하게 격리된 환경이고, 그 안에 애플리케이션을 담아 실행합니다. 격리되어 있으니 한 기계에서 여러 개를 동시에 띄울 수 있습니다. 컨테이너 안에 애플리케이션 실행에 필요한 것이 다 들어 있으니 호스트에 무엇이 깔려 있는지에 기대지 않습니다.
「Docker」라는 한 이름이 가리키는 부분은 셋입니다. 셋을 갈라 두면 뒤의 절이 읽힙니다.
flowchart TD
CLI["docker 명령"] -->|Docker API| D["dockerd 데몬"]
D -->|짓고 지운다| IMG["이미지"]
D -->|만들고 실행한다| CON["컨테이너"]
D -->|push · pull| REG["레지스트리"]
첫째는 데몬입니다. dockerd라는 이름으로 뒤에서 돌면서 Docker API 요청을 듣고, 이미지·컨테이너·네트워크·볼륨 같은 객체를 관리합니다. 이미지를 짓고 컨테이너를 실행하고 배포하는 일은 전부 이쪽이 합니다. 여기서 API는 Application Programming Interface, 프로그램끼리 주고받기로 정해 둔 호출 규약을 말합니다.
둘째는 클라이언트입니다. docker라는 명령입니다. 많은 사용자가 Docker를 만지는 주된 통로입니다. docker run 같은 명령을 치면 클라이언트가 그 명령을 dockerd에 보내고 데몬이 수행합니다.
둘은 REST API로 말을 주고받습니다. REST는 REpresentational State Transfer의 줄임말로, 자원을 주소로 가리키고 정해진 몇 가지 동작으로 다루는 방식입니다. 둘은 한 기계에 같이 있어도 되고, 클라이언트가 멀리 있는 데몬에 붙어도 됩니다. 클라이언트 하나가 데몬 여럿과 통할 수도 있습니다.
셋째는 레지스트리입니다. 컨테이너 이미지를 두는 저장소입니다. Docker Hub는 누구나 쓸 수 있는 공개 레지스트리입니다. Docker는 기본으로 거기서 이미지를 찾습니다. 직접 세운 사설 레지스트리를 써도 됩니다. docker pull이나 docker run을 치면 설정된 레지스트리에서 필요한 이미지를 받아 오고, docker push를 치면 그쪽으로 올립니다.
이미지와 컨테이너는 다른 물건입니다. 이미지는 컨테이너를 만드는 방법이 적힌 읽기 전용 틀입니다. 대개 다른 이미지를 바탕에 깔고 거기에 손을 더해 만듭니다.
이미지는 레이어가 쌓인 더미입니다. 레이어 하나는 그 아래 레이어들 위에 내용을 더한 한 켜입니다. Dockerfile의 명령 하나가 레이어 하나가 됩니다. 그 파일을 고쳐 다시 지으면 바뀐 레이어만 다시 지어집니다.
컨테이너는 그 이미지를 실행한 하나입니다. 만들고 시작하고 멈추고 옮기고 지울 수 있습니다. 컨테이너가 무엇인지는 바탕 이미지와, 만들거나 시작할 때 준 설정이 함께 정합니다.
가상 머신과 갈리는 대목이 하나 있습니다. 가상 머신은 자기 커널과 하드웨어 드라이버와 프로그램을 가진 운영체제 한 벌입니다. 커널은 운영체제의 한가운데에서 프로세스와 메모리와 장치를 직접 다루는 프로그램입니다. 애플리케이션 하나를 격리하자고 운영체제 한 벌을 띄우는 것은 짐이 큽니다.
컨테이너는 실행에 필요한 파일을 전부 가진 격리된 프로세스일 뿐입니다. 여러 개를 띄우면 모두 같은 커널을 나눠 씁니다. 그래서 같은 인프라에서 더 많은 애플리케이션을 돌릴 수 있습니다.
flowchart TD
subgraph VM["가상 머신 · 앱마다 운영체제 한 벌"]
VA["앱 A"] --> VG1["손님 운영체제 · 커널"]
VB["앱 B"] --> VG2["손님 운영체제 · 커널"]
end
subgraph CT["컨테이너 · 커널 하나를 나눠 쓴다"]
CA["앱 A"] --> K["호스트 커널"]
CB["앱 B"] --> K
CC["앱 C"] --> K
end
격리를 만드는 것은 리눅스 커널의 네임스페이스입니다. 네임스페이스는 프로세스에게 자기 몫의 작업 공간만 보여 주는 커널 기능입니다. 컨테이너를 띄울 때 Docker가 그 컨테이너 몫의 네임스페이스 한 벌을 만듭니다. 컨테이너의 각 부분은 저마다의 네임스페이스 안에서 돕니다. 그래서 그 바깥에 닿지 못합니다.
Docker 자신은 Go 언어로 짜여 있습니다.
포기한 것
Docker가 무엇을 안 하기로 했는지부터 봅니다. 셋이고, 셋 다 얻은 것과 짝입니다.
손님 운영체제를 따로 두지 않는다
컨테이너는 자기 커널을 갖지 않습니다. 같은 기계의 컨테이너들이 호스트의 커널 하나를 나눠 씁니다. 얻은 것은 무게입니다. 가상 머신처럼 운영체제 한 벌을 띄울 필요가 없으니 더 적은 인프라에서 더 많은 애플리케이션을 돌립니다.
치르는 값은 리눅스가 아닌 호스트에서 드러납니다. Docker Desktop은 컨테이너를 돌릴 리눅스 가상 머신을 하나 띄우고, 그 안에서 컨테이너를 굴립니다. 그 가상 머신을 굴리는 하이퍼바이저를 Docker 문서는 VMM(Virtual Machine Manager, 가상 머신 관리자)이라고 부릅니다. 하이퍼바이저는 한 기계 위에 가상 머신을 만들어 돌리는 프로그램입니다.
Docker Desktop은 VMM을 여러 개 지원합니다. 무엇을 고를 수 있는지는 플랫폼이 정합니다. 그중 Docker VMM은 컨테이너 작업에 맞춰 만든 하이퍼바이저입니다. 컨테이너가 놀고 있을 때 쓰지 않는 메모리를 호스트에 돌려주고, 컨테이너와 호스트 사이의 파일 입출력 지연을 줄입니다. 대신 리눅스 가상 머신에 최소 4GB의 메모리를 떼어 줘야 돕니다.
컨테이너가 쓴 것을 남기지 않는다
컨테이너 안에서 만든 파일은 기본으로 쓰기 가능한 컨테이너 레이어에 쌓입니다. 이 레이어는 읽기 전용인 이미지 레이어들 위에 얹힙니다. 컨테이너를 없애면 이 레이어도 같이 지워집니다. 거기 쓴 데이터는 다른 프로세스가 필요로 해도 꺼내기 어렵습니다. 쓰기 레이어는 컨테이너마다 따로이고, 호스트나 다른 컨테이너로 쉽게 빼낼 수 없습니다.
flowchart TD
W1["컨테이너 A의 쓰기 레이어"] -->|얹힌다| IMG
W2["컨테이너 B의 쓰기 레이어"] -->|얹힌다| IMG
subgraph IMG["이미지 레이어 · 읽기 전용 · 둘이 나눠 쓴다"]
L3["레이어 3"]
L2["레이어 2"]
L1["레이어 1"]
end
얻은 것은 공유입니다. 변경분이 각자의 쓰기 레이어에만 쌓이니 여러 컨테이너가 같은 이미지를 나눠 쓰면서도 저마다의 데이터 상태를 가집니다. 쓸 때 복사, 곧 copy-on-write 전략이 이것을 해 줍니다. 아래 레이어에 있는 파일을 읽기만 할 때는 그 파일을 그냥 씁니다. 처음으로 고칠 때만 그 파일을 자기 레이어로 복사해 고칩니다. 그래서 입출력과 레이어 크기가 줄어듭니다.
값은 두 가지로 치릅니다. 하나는 데이터를 남기려면 볼륨을 붙여야 한다는 것입니다. 볼륨은 데몬이 관리하는 저장 수단입니다. 그것을 쓰던 컨테이너가 없어져도 데이터가 남습니다.
다른 하나는 쓰기 속도입니다. 스토리지 드라이버는 이미지 레이어와 컨테이너 쓰기 레이어의 데이터를 디스크에 두는 부품입니다. 이 드라이버는 공간 효율에 맞춰 최적화되어 있습니다. 그래서 드라이버에 따라서는 쓰기 속도가 호스트 파일시스템보다 낮습니다. copy-on-write 파일시스템을 쓰는 드라이버에서 특히 그렇습니다. 데이터베이스 저장처럼 쓰기가 많은 애플리케이션이 이 값을 치릅니다. 읽기 전용 레이어에 이미 데이터가 있을 때 더 그렇습니다.
데몬이 루트 권한을 요구한다
Docker로 컨테이너를 돌린다는 것은 Docker 데몬을 돌린다는 뜻입니다. 이 데몬은 루트 권한을 요구합니다. rootless 모드를 택하지 않는 한 그렇습니다. 얻은 것은 데몬이 할 수 있는 일의 폭입니다. 예컨대 Docker는 호스트와 컨테이너가 디렉토리를 나눠 쓰게 해 주고, 그때 컨테이너의 접근 권한을 제한하지 않습니다. 호스트의 /를 컨테이너의 /host로 내주는 컨테이너를 띄울 수 있고, 그 컨테이너는 호스트 파일시스템을 아무 제약 없이 고칠 수 있습니다.
그래서 공식 문서는 신뢰된 사용자만 데몬을 제어하게 하라고 못 박습니다. 이 때문에 클라이언트가 데몬과 통하는 통로도 Docker 0.5.2에서 유닉스 소켓으로 바뀌었습니다. 소켓이면 평범한 유닉스 권한 검사로 접근을 막을 수 있기 때문입니다.
REST API를 HTTP(HyperText Transfer Protocol, 웹에서 문서와 데이터를 주고받는 규약)로 열 수도 있지만, 열기로 했다면 위의 뜻을 알고 열어야 합니다. 다른 호스트에서 오는 접근을 방화벽으로 막아도 컨테이너 쪽에서는 여전히 닿습니다. 그 길로 권한이 올라갈 수 있습니다.
rootless 모드는 데몬과 컨테이너를 루트가 아닌 사용자로, 사용자 네임스페이스 안에서 돌립니다. 대신 제약을 집니다. 공식 문서가 적은 알려진 제약은 아래와 같습니다.
| 제약 | 내용 |
|---|---|
| 스토리지 드라이버 | overlay2 · fuse-overlayfs · btrfs · vfs 넷만 지원합니다. 앞의 셋은 커널 판에 조건이 붙습니다 |
| cgroup | cgroup(control group, 프로세스 묶음이 쓸 자원을 제한하는 커널 기능)은 cgroup v2와 systemd가 있을 때만 됩니다 |
| 지원하지 않는 기능 | AppArmor · 체크포인트 · 오버레이 네트워크 · SCTP(Stream Control Transmission Protocol, 여러 흐름을 한 연결에 실어 나르는 전송 규약) 포트 열기 |
| 특권 포트 | 1024 미만의 TCP·UDP 포트를 열려면 따로 손을 봐야 합니다 |
| 원본 주소 전달 | docker run -p로 포트를 이어 붙여도 기본으로는 원본 IP 주소가 전달되지 않습니다 |
| 데이터 저장 경로 | data-root를 NFS 마운트에 두는 것은 지원하지 않습니다 |
여기서 IP는 Internet Protocol, 인터넷에서 기계를 가리키는 주소 규약입니다.
예시
성격이 다른 두 벌을 봅니다. 하나는 이미지를 짓는 쪽이고 하나는 띄우는 쪽입니다.
파이썬 애플리케이션을 담는 Dockerfile
Dockerfile은 컨테이너 이미지를 만들 때 쓰는 텍스트 문서입니다. 무슨 명령을 돌리고 어떤 파일을 복사하고 무엇으로 시작할지를 이미지 빌더에게 알려 줍니다. 공식 문서가 바로 돌릴 수 있는 파이썬 애플리케이션의 예로 든 것이 아래입니다.
FROM python:3.13
WORKDIR /usr/local/app
# Install the application dependencies
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# Copy in the source code
COPY src ./src
EXPOSE 8080
# Setup an app user so the container doesn't run as the root user
RUN useradd app
USER app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"]
각 줄이 무엇을 정하는지는 이렇습니다. FROM은 새 빌드 단계를 시작하고 그 아래 명령들이 딛고 설 베이스 이미지를 정합니다. Dockerfile은 반드시 FROM으로 시작해야 합니다. WORKDIR은 파일이 복사되고 명령이 실행될 이미지 안의 경로를 정합니다. COPY는 새 파일이나 디렉토리를 <src>에서 가져다 이미지 파일시스템의 <dest>에 더합니다.
RUN은 명령을 실행해 지금 이미지 위에 새 레이어를 만들고, 그 레이어가 다음 단계에 쓰입니다. EXPOSE는 이 이미지가 열고 싶어 하는 포트를 이미지에 적어 둡니다. USER는 그 아래 명령들이 쓸 기본 사용자를 정합니다. CMD는 이 이미지로 컨테이너를 띄울 때 실행할 명령을 정합니다.
RUN과 CMD는 헷갈리기 쉽습니다. RUN은 빌드할 때 명령을 정말로 실행하고 그 결과를 굳힙니다. CMD는 빌드할 때 아무것도 실행하지 않고, 실행할 명령이 무엇인지만 적습니다. 한 Dockerfile에 CMD는 하나만 있을 수 있고, 여럿 적으면 마지막 것만 듣습니다.
이 파일이 있는 곳에서 이미지를 짓고 이름을 붙입니다.
docker build -t my-username/my-image .
끝의 .은 빌드 컨텍스트의 경로입니다. 빌더는 거기서 Dockerfile과 참조된 파일들을 찾습니다. -t는 만들어진 이미지에 이름을 붙이는 옵션입니다.
이름은 [HOST[:PORT_NUMBER]/]PATH[:TAG] 꼴입니다. 호스트를 안 적으면 docker.io의 공개 레지스트리를 씁니다. 태그를 안 적으면 latest입니다. 짓고 나면 docker push my-username/my-image로 레지스트리에 올립니다.
빌드가 돌 때 화면에 찍히는 것은 아래와 같습니다. 공식 문서가 그대로 실은 출력입니다.
$ docker build .
[+] Building 3.5s (11/11) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> [1/6] FROM docker.io/library/python:3.12 0.0s
=> [2/6] WORKDIR /usr/local/app 0.0s
=> [3/6] RUN useradd app 0.1s
=> [4/6] COPY ./requirements.txt ./requirements.txt 0.0s
=> [5/6] RUN pip install --no-cache-dir --upgrade -r requirements.txt 3.2s
=> [6/6] COPY ./app ./app 0.0s
=> exporting to image 0.1s
=> => writing image sha256:9924dfd9350407b3df01d1a0e1033b1e543523ce7d5d5e2c83a724480ebe8f00 0.0s
Dockerfile에 적은 명령들이 한 줄씩 지나가고, 마지막에 writing image sha256: 뒤로 만들어진 이미지가 찍힙니다.
nginx 컨테이너를 띄우는 docker run 한 줄
이미 있는 이미지를 받아 띄우는 쪽은 한 줄로 끝납니다. 공식 레퍼런스가 든 예입니다.
$ docker run -p 127.0.0.1:80:8080/tcp nginx:alpine
-p는 컨테이너의 포트를 호스트 쪽으로 이어 붙입니다. 위 줄은 컨테이너의 8080 포트를 호스트 127.0.0.1의 TCP 80 포트에 묶습니다. TCP는 Transmission Control Protocol, 끊기지 않는 흐름으로 바이트를 나르는 규약입니다.
주소를 안 적고 -p 80:80처럼만 쓰면 Docker는 모든 인터페이스, 곧 0.0.0.0에 포트를 엽니다. 그 포트는 바깥에서 닿을 수 있게 됩니다. 그 포트를 막으라고 방화벽을 잡아 두었더라도 그렇습니다. Docker가 자기 방화벽 규칙을 따로 관리하기 때문입니다.
자주 같이 쓰는 옵션이 둘 더 있습니다. -d는 컨테이너를 배경 프로세스로 띄워 터미널을 붙잡지 않게 하고 컨테이너 ID를 찍습니다. 이렇게 띄운 컨테이너는 컨테이너를 돌리는 최상위 프로세스가 끝나면 함께 끝납니다. --rm을 같이 주지 않았다면 그렇습니다. -d와 --rm을 같이 주면 컨테이너가 끝날 때나 데몬이 끝날 때 중 먼저 오는 쪽에서 컨테이너가 지워집니다.
-v는 볼륨을 걸어 줍니다. 같은 일을 하는 --mount가 하나 더 있습니다. --volume을 없앨 계획은 없지만 공식 문서는 --mount 쪽을 권합니다.
동작
한 벌의 코드가 남의 기계에서 도는 컨테이너가 되기까지 네 걸음을 지납니다. 짓고, 올리고, 내려받고, 띄웁니다.
flowchart TD
D["Dockerfile"] -->|docker build| I["이미지 · 레이어 묶음"]
I -->|docker push| R["레지스트리"]
R -->|docker pull| L["호스트에 받아 둔 이미지"]
L -->|docker run| C["컨테이너 · 쓰기 레이어가 얹힌다"]
짓기. Dockerfile의 명령 하나가 최종 이미지의 레이어 하나로 번역됩니다. 각 레이어가 앞선 레이어들 위에 내용을 더합니다.
올리기와 내려받기. 지은 이미지는 docker push로 레지스트리에 올립니다. 밀어 올릴 때는 데몬이 기본으로 한 번에 다섯 레이어씩 올립니다. docker pull로 받을 때는 기본이 세 레이어입니다. 대역폭이 좁으면 시간 초과가 날 수 있어 --max-concurrent-downloads 데몬 옵션으로 낮출 수 있습니다.
띄우기. docker run은 새 컨테이너에서 명령을 돌립니다. 필요하면 이미지를 받아 오고 컨테이너를 시작합니다. 공식 문서가 docker run -i -t ubuntu /bin/bash를 예로 들어 적은 순서는 이렇습니다.
flowchart TD
A["docker run 을 부른다"] --> B{"이미지가 호스트에 있나"}
B -->|없다| C["레지스트리에서 받아 온다"]
B -->|있다| D["컨테이너를 만든다"]
C --> D
D --> E["마지막 레이어로 읽고 쓸 파일시스템을 떼어 준다"]
E --> F["네트워크 인터페이스를 만들고 IP 주소를 준다"]
F --> G["명령을 실행한다"]
이미지를 받아 오는 걸음은 docker pull ubuntu를 손으로 친 것과 같습니다. 세 번째 걸음에서 떼어 주는 파일시스템이 컨테이너의 쓰기 레이어이고, 컨테이너가 자기 파일시스템에 파일을 만들고 고칠 수 있는 것이 이 레이어 덕입니다. exit로 명령을 끝내면 컨테이너는 멈추지만 지워지지는 않습니다. 다시 시작하거나 지울 수 있고, 멈춘 컨테이너를 docker start로 다시 켜면 그 안의 변경분은 그대로 살아 있습니다.
빌드 캐시가 가르는 곳. 짓기 단계에는 갈림이 하나 더 있습니다. 빌더는 먼저 베이스 이미지가 이미 캐시에 있는지 봅니다. 그다음 명령들을 캐시된 레이어와 하나씩 견줍니다. 명령과 정확히 맞는 캐시 레이어가 없으면 그 레이어의 캐시가 깨집니다.
ADD와 COPY, 그리고 바인드 마운트를 쓰는 RUN은 판정이 다릅니다. 바인드 마운트는 호스트의 파일이나 디렉토리를 컨테이너 안 경로에 그대로 이어 붙이는 것입니다. 이 셋은 파일 메타데이터로 캐시 체크섬을 계산해 판정합니다. 파일의 수정 시각은 이 체크섬에 넣지 않습니다.
그 밖의 명령은 컨테이너 안의 파일을 들여다보지 않고 명령 문자열만으로 맞는지 찾습니다. RUN apt-get -y update가 그런 경우입니다.
한 레이어가 깨지면 그 뒤의 레이어가 모두 다시 돕니다. 뒤 레이어들이 다른 결과를 낼 일이 없더라도 다시 돕니다.
flowchart TD
subgraph OK["캐시가 맞는 구간"]
L1["FROM · 레이어 1"] --> L2["WORKDIR · 레이어 2"]
end
subgraph BROKEN["깨진 레이어와 그 뒤"]
L3["COPY · 레이어 3 · 여기서 깨진다"] --> L4["RUN · 레이어 4 · 다시 돈다"]
L4 --> L5["CMD · 레이어 5 · 다시 돈다"]
end
OK --> BROKEN
그래서 RUN 명령의 캐시는 빌드 사이에 저절로 깨지지 않습니다. 일주일 뒤에 다시 지어도 같은 패키지를 받게 됩니다. 다시 돌리고 싶으면 앞 레이어를 바꾸거나, docker builder prune으로 빌드 캐시를 비우거나, --no-cache 옵션을 씁니다.
운영
굴릴 때 걸리는 곳은 대개 세 군데입니다. 로그가 디스크를 먹는 것, 안 쓰는 것이 쌓이는 것, 그리고 빌드가 매번 처음부터 도는 것입니다.
로그 파일은 기본으로 안 잘린다
Docker는 기본으로 모든 컨테이너의 표준 출력과 표준 오류를 붙잡아 JSON 형식으로 파일에 씁니다. JSON은 JavaScript Object Notation, 값을 중괄호와 따옴표로 적는 텍스트 형식입니다. 줄마다 어디서 나왔는지와 시각이 같이 붙습니다.
{
"log": "Log line is here\n",
"stream": "stdout",
"time": "2019-01-01T11:11:11.111111111Z"
}
이 json-file 드라이버의 max-size 기본값은 -1, 곧 무제한입니다. 잘라 주지 않으면 로그 파일이 계속 자랍니다. max-size를 정하면 그 크기에서 파일을 넘깁니다.
max-file은 남겨 둘 로그 파일 수입니다. 기본값은 1입니다. max-size를 같이 정했을 때만 듣습니다. 넘기다 파일이 넘치면 가장 오래된 것을 지웁니다. 넘긴 파일을 압축할지 정하는 compress는 기본이 false입니다.
데몬 설정에 이렇게 적어 둡니다.
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
고친 뒤에는 Docker를 다시 시작해야 새로 만드는 컨테이너에 적용됩니다. 이미 도는 컨테이너는 새 로그 설정을 저절로 쓰지 않습니다. 컨테이너 하나에만 걸고 싶으면 docker run -it --log-opt max-size=10m --log-opt max-file=3 꼴로 줍니다.
쌓인 것을 재고 지우는 명령
무엇이 얼마나 쌓였는지는 docker system df가 보여 줍니다. 종류별로 전체 수와 쓰이고 있는 수, 크기, 되찾을 수 있는 크기를 찍습니다.
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 5 2 16.43 MB 11.63 MB (70%)
Containers 2 0 212 B 212 B (100%)
Local Volumes 2 1 36 B 0 B (0%)
지우는 것은 docker system prune입니다. 지우기 전에 무엇을 지울지 찍고 물어봅니다. 무엇이 지워지고 무엇이 남는지는 아래와 같습니다.
| 대상 | 기본 동작 |
|---|---|
| 멈춘 컨테이너 | 전부 지웁니다 |
| 컨테이너가 하나도 안 쓰는 네트워크 | 전부 지웁니다 |
| 이름표 없는 이미지 | 지웁니다. -a를 주면 안 쓰는 이미지를 전부 지웁니다 |
| 안 쓰는 빌드 캐시 | 언제나 지웁니다 |
| 볼륨 | 안 지웁니다. 익명 볼륨까지 지우려면 --volumes를 줍니다 |
볼륨을 빼 둔 이유가 있습니다. 지금 아무 컨테이너도 안 쓰는 볼륨에 중요한 데이터가 있을 수 있기 때문입니다. 빌드 캐시는 이 명령에서 언제나 삭제 대상입니다. 컨테이너와 네트워크와 이미지는 두고 빌드 캐시만 되찾고 싶으면 docker builder prune을 씁니다.
Dockerfile의 줄 순서
빌드가 매번 처음부터 도는 것은 대개 순서 때문입니다. 캐시가 한 번 깨지면 그 뒤의 Dockerfile 명령은 전부 새 이미지를 만들고 캐시를 쓰지 않습니다. 레이어가 여럿인 빌드에서 캐시를 다시 쓰고 싶으면 덜 자주 바뀌는 명령을 위에, 자주 바뀌는 명령을 아래에 두라고 공식 문서가 적습니다. 위 예시의 Dockerfile이 requirements.txt만 먼저 복사해 설치하고 소스 코드를 나중에 복사하는 것이 그 순서입니다. 소스를 고쳐도 의존성 설치 레이어는 캐시에 맞아 건너뜁니다.
관련 항목
Docker가 짓고 띄우는 산출물
컨테이너 이미지 · 컨테이너 · 레이어 · Dockerfile · 볼륨 · 바인드 마운트
Docker가 격리를 만들 때 기대는 커널 기능
네임스페이스 · cgroup · 커널 · Linux · 유니온 파일시스템 · copy-on-write · 운영체제
이미지를 주고받는 저장소와 규격
컨테이너 레지스트리 · Docker Hub · 이미지 태그 · 다이제스트 · OCI 이미지 명세
컨테이너를 여러 대에 걸쳐 굴리는 도구
Kubernetes · 오케스트레이션 · Docker Compose · kubelet · 파드 · 디플로이먼트
Docker를 대신할 수 있는 다른 실행 수단
가상 머신 · Podman · containerd · LXC · chroot · 컨테이너 런타임
데몬 권한과 맞물리는 보안 개념
최소 권한 · 권한 상승 · 접근 제어 · 사용자 네임스페이스 · 인증과 인가
컨테이너를 굴리며 보는 기록과 지표
로그 · 모니터링 · 관측성 · 메트릭 · 디스크 사용량
Docker가 놓이는 개발과 배포 단계
다른 이름: 도커 · docker