Docker는 프로그램을 컨테이너라는 작은 방에 넣어 어느 컴퓨터에서나 똑같이 돌리게 해 주는 도구다. 이 글은 Docker를 쓰면서 자주 치는 명령을 빠르게 찾아보려고 정리한 것이다. 명령마다 표 한 줄과 예제 한 덩어리가 있고, 예제 뒤에는 줄마다 무엇을 하는지 적었다.

공식 문서: https://docs.docker.com/


이 글의 명령은 터미널에 친다

이 글의 명령은 전부 터미널에 글자로 쳐서 실행한다. 그러려면 먼저 컴퓨터에 Docker가 깔려 있어야 한다. Windows와 macOS는 Docker Desktop을 깔고, 리눅스는 Docker Engine을 직접 깐다. Docker Engine은 아래에서 설명할 Docker 데몬과 docker 명령을 합친 Docker의 몸통이고, Docker Desktop은 그 Engine에 Compose 같은 도구를 더 얹은 묶음이다. 운영체제마다 다른 점은 맨 끝 플랫폼별 차이에 모아 두었다.

글에 나오는 표기 몇 가지를 먼저 읽는 법을 적어 둔다.

  • <image> 처럼 꺾쇠로 감싼 자리는 내가 바꿔 넣는 자리다. 꺾쇠까지 치지 않는다. docker run <image> 는 docker run nginx 처럼 친다
  • # 으로 시작하는 줄은 주석이다. 자바의 // 와 같다. 터미널에서 명령을 받아 실행해 주는 프로그램인 셸이 그 줄을 읽지 않고 넘어간다
  • 줄 끝의 \ 는 「다음 줄에 이어진다」는 뜻이다. 긴 명령을 여러 줄로 나눠 적을 때 쓴다. 셸은 여러 줄을 한 줄로 이어 붙여 실행한다
  • $(pwd) 는 「지금 있는 폴더의 경로」로 바뀐다. 셸이 pwd 명령을 먼저 실행하고 그 결과 글자를 그 자리에 끼워 넣는다
  • . 은 지금 폴더, ./logs/ 는 지금 폴더 안의 logs 폴더다. /var/log 처럼 / 로 시작하는 경로는 리눅스에서 맨 꼭대기부터 적은 경로다

Docker 명령은 클라이언트가 데몬에게 부탁하는 모양이다

docker 명령을 칠 때 실제로 일하는 쪽은 따로 있다. 아래 그림이 그 흐름이다.

sequenceDiagram participant Client as Docker Client participant Daemon as Docker Daemon participant Registry as Docker Registry participant Container as Container Note over Client,Container: 이미지 가져오기 Client->>Daemon: docker pull Daemon->>Registry: 이미지 요청 Registry->>Daemon: 이미지 다운로드 Note over Client,Container: 컨테이너 실행 Client->>Daemon: docker run Daemon->>Container: 컨테이너 생성 및 시작 Note over Client,Container: 컨테이너 관리 Client->>Daemon: docker ps/logs/exec Daemon->>Container: 명령 실행 Container->>Daemon: 결과 반환 Daemon->>Client: 출력 표시 Note over Client,Container: 이미지 배포 Client->>Daemon: docker push Daemon->>Registry: 이미지 업로드

그림의 네 칸은 이렇다.

  • Docker 클라이언트 — 내가 터미널에 치는 docker 명령 그 자체다. 스스로 일하지 않고 데몬에게 부탁만 보낸다
  • Docker 데몬 — 뒤에서 늘 돌며 부탁을 받아 실제로 일하는 프로그램(dockerd)이다. 클라이언트와 둘로 나뉘어 있어서, 데몬이 꺼져 있으면 docker 명령은 연결 오류만 낸다
  • 레지스트리 — 이미지를 올려 두고 내려받는 인터넷 저장소다. 누구나 쓰는 공개 레지스트리가 Docker Hub이고, 이미지 이름에 다른 주소를 안 적으면 Docker는 Docker Hub에서 찾는다
  • 컨테이너 — 이미지를 실제로 실행한 것이다

이미지와 컨테이너는 자바의 클래스와 객체 관계와 비슷하다. 이미지는 고칠 수 없는 설계도이고, docker run 은 그 설계도로 컨테이너를 하나 찍어 실행한다. 같은 이미지로 컨테이너를 여러 개 띄울 수 있고, 컨테이너를 지워도 이미지는 남는다.

그림 셋째 칸의 docker ps/logs/exec 는 세 명령을 묶어 적은 것이다. 각각 아래 컨테이너 실행과 컨테이너 접근 절에서 다룬다.


컨테이너는 run으로 띄우고 stop으로 멈추고 rm으로 지운다

컨테이너에는 상태가 셋 있다. 실행 중, 중지됨, 지워짐이다. 멈춘(stop) 컨테이너는 지워진 게 아니라 그대로 남아 있어서 start 로 다시 켤 수 있고, docker ps -a 목록에도 보인다. 완전히 없애려면 rm 으로 따로 지운다.

명령어 설명 예제
docker run <image> 새 컨테이너 실행 docker run nginx
docker run -d <image> 백그라운드로 실행 (detached) docker run -d nginx
docker run -it <image> 대화형 모드로 실행 docker run -it ubuntu bash
docker run --name <name> 컨테이너 이름 지정 docker run --name my-nginx nginx
docker run -p <host>:<container> 포트 매핑 docker run -p 8080:80 nginx
docker run --rm 종료 시 자동 삭제 docker run --rm ubuntu
docker ps 실행 중인 컨테이너 목록 docker ps
docker ps -a 모든 컨테이너 목록 (중지 포함) docker ps -a
docker start <container> 중지된 컨테이너 시작 docker start my-nginx
docker stop <container> 실행 중인 컨테이너 중지 docker stop my-nginx
docker restart <container> 컨테이너 재시작 docker restart my-nginx
docker rm <container> 컨테이너 삭제 docker rm my-nginx
docker rm -f <container> 실행 중인 컨테이너 강제 삭제 docker rm -f my-nginx

표의 옵션 중 처음 보면 막히는 것만 풀어 둔다.

  • nginx, ubuntu 는 이미지 이름이다. nginx는 웹 페이지를 돌려주는 서버 프로그램이고, ubuntu는 우분투를 담은 이미지다. 리눅스는 한 가지가 아니라, 리눅스 커널에 기본 프로그램을 묶어 내놓은 여러 판(배포판)이 있고 우분투가 그중 하나다. 예제에서는 둘 다 Docker Hub에서 내려받는다
  • -d (detached) — 컨테이너를 뒤에서 돌리고 터미널은 바로 돌려준다. 이 옵션이 없으면 컨테이너가 끝날 때까지 터미널이 그 컨테이너에 붙잡혀 있다
  • -it — -i 와 -t 를 붙여 쓴 것이다. -i 는 내가 치는 글자가 컨테이너로 들어가게 입력을 열어 두고, -t 는 가짜 터미널 화면(pty)을 붙인다. 둘을 같이 써야 컨테이너 안에서 명령을 치고 결과를 보는 「대화」가 된다. ubuntu bash 는 ubuntu 이미지로 컨테이너를 띄우면서 그 안에서 셸인 bash 를 실행하라는 뜻이다
  • -p 8080:80 (포트 매핑) — 콜론 앞이 내 컴퓨터(호스트)의 포트, 뒤가 컨테이너 안의 포트다. nginx는 컨테이너 안에서 80번 포트로 요청을 받는다. 그런데 컨테이너 안의 포트는 바깥에서 바로 안 보인다. -p 8080:80 은 「내 컴퓨터 8080번으로 오는 요청을 컨테이너 80번으로 넘겨라」라는 뜻이고, 그래서 브라우저에서 내 컴퓨터를 가리키는 주소 localhost의 8080번, 곧 localhost:8080 으로 들어가면 nginx가 대답한다
  • --rm — 컨테이너는 끝나도 저절로 지워지지 않고 「중지됨」으로 남는다. --rm 을 붙이면 끝나는 순간 지워진다

웹 서버를 띄웠다가 지우는 예제

# Nginx 웹 서버를 8080 포트로 실행
docker run -d -p 8080:80 --name web nginx

# 실행 중인 컨테이너 확인
docker ps

# 컨테이너 중지
docker stop web

# 컨테이너 삭제
docker rm web

# 한 번에 실행, 작업 후 자동 삭제
docker run --rm -it ubuntu bash
  • docker run -d -p 8080:80 --name web nginx — nginx 이미지로 컨테이너를 하나 띄운다. 뒤에서 돌리고(-d), 내 컴퓨터 8080번을 컨테이너 80번에 잇고(-p), 이름을 web 으로 붙인다(--name). 이름을 붙여 두면 뒤의 명령에서 web 으로 부를 수 있다
  • docker ps — 지금 돌고 있는 컨테이너 목록을 본다. web 이 보이면 잘 뜬 것이다
  • docker stop web — web 을 멈춘다. 아직 지워지지 않아서 docker ps -a 에는 남아 있다
  • docker rm web — 멈춘 web 을 지운다
  • docker run --rm -it ubuntu bash — ubuntu 컨테이너를 띄우고 그 안의 bash에 바로 들어간다. 화면에 컨테이너 안의 셸이 「명령을 치라」고 기다리는 줄(프롬프트)이 뜨고, 거기서 리눅스 명령을 쳐 볼 수 있다. exit 를 치면 bash가 끝난다. 컨테이너는 처음 실행한 그 프로그램(bash)이 끝나면 같이 끝나고, --rm 이라 끝나는 순간 지워진다

실행 중인 컨테이너의 로그를 보고 안에 들어간다

컨테이너가 뒤에서(-d) 돌면 화면에 아무것도 안 보인다. 컨테이너 안의 프로그램이 찍는 로그를 보거나, 안에 들어가 명령을 쳐 봐야 무슨 일이 있는지 안다.

명령어 설명 예제
docker logs <container> 컨테이너 로그 조회 docker logs web
docker logs -f <container> 로그 실시간 추적 (follow) docker logs -f web
docker logs -n <num> <container> 마지막 N줄만 조회 docker logs -n 100 web
docker exec <container> <cmd> 실행 중인 컨테이너에서 명령 실행 docker exec web ls /app
docker exec -it <container> bash 컨테이너에 쉘 접속 docker exec -it web bash
docker attach <container> 컨테이너의 표준 입출력에 연결 docker attach web
docker cp <src> <container>:<dest> 파일 복사 (호스트→컨테이너) docker cp file.txt web:/app/
docker cp <container>:<src> <dest> 파일 복사 (컨테이너→호스트) docker cp web:/app/log.txt .
  • -n 은 --tail 과 같은 옵션이다. 로그 끝에서부터 몇 줄을 볼지 정한다
  • exec 와 attach 는 다르다. exec 는 컨테이너 안에서 새 프로그램을 하나 더 실행한다(ls, bash 등). attach 는 새 프로그램을 띄우지 않고, 컨테이너가 처음부터 돌리던 프로그램의 표준 입출력에 내 터미널을 잇는다. 그래서 안을 둘러보고 싶을 때는 보통 exec -it ... bash 를 쓴다
  • ls 는 폴더 안의 파일 목록을 보는 리눅스 명령이다. ls /app 은 컨테이너 안 /app 폴더를 본다
  • docker cp 는 컨테이너이름:경로 로 컨테이너 쪽을 적는다. web:/app/ 은 「web 컨테이너 안의 /app/ 폴더」다. 콜론이 없는 쪽이 내 컴퓨터의 경로이고, 맨 끝의 . 은 내 컴퓨터의 지금 폴더다

로그를 보고 파일을 주고받는 예제

# 실행 중인 컨테이너 로그 실시간 확인
docker logs -f web

# 컨테이너 안에서 bash 셸 실행
docker exec -it web bash

# 컨테이너 내부 파일 확인
docker exec web ls -la /var/log

# 호스트에서 컨테이너로 파일 복사
docker cp config.json web:/etc/app/

# 컨테이너에서 호스트로 파일 복사
docker cp web:/var/log/app.log ./logs/
  • docker logs -f web — web 의 로그를 찍고, 새 로그가 생기면 계속 이어서 찍는다.
  • docker exec -it web bash — web 안에서 bash를 새로 띄워 들어간다. 나올 때는 exit 를 친다
  • docker exec web ls -la /var/log — 들어가지 않고 명령 하나만 실행해 결과를 받는다. -l 은 크기·날짜까지 길게 보여 주고, -a 는 . 으로 시작하는 숨은 파일까지 보여 준다. /var/log 는 리눅스에서 로그 파일을 두는 폴더다
  • docker cp config.json web:/etc/app/ — 내 지금 폴더의 config.json 을 web 안의 /etc/app/ 폴더로 복사한다. 파일 이름은 예시다
  • docker cp web:/var/log/app.log ./logs/ — 반대로 web 안의 app.log 를 내 지금 폴더의 logs 폴더로 가져온다

이미지는 pull로 받고 build로 만들고 push로 올린다

이미지가 어디서 오고 어디로 가는지를 그림으로 먼저 본다. 이 블로그는 Mermaid라는 도구로 글자를 그림으로 바꿔 그리는데, 그림의 [*] 는 Mermaid 상태 그림에서 시작·끝을 나타내는 표시다.

stateDiagram-v2 [*] --> Registry: docker pull Registry --> Local: 이미지 다운로드 Local --> Container: docker run Container --> Local: docker commit Local --> Registry: docker push Local --> [*]: docker rmi note right of Local 로컬 이미지 저장소 - docker images - docker tag end note
  • Registry 는 인터넷의 레지스트리, Local 은 내 컴퓨터에 내려받아 둔 이미지들이다. 「로컬」은 「내 컴퓨터」라는 뜻이다
  • docker commit 은 돌고 있는 컨테이너에서 바뀐 내용을 새 이미지로 굳힌다. 그래서 그림에서 Container → Local 화살표가 있다. 이미지를 만드는 다른 길은 Dockerfile이다. 아래 예제는 Dockerfile 쪽을 쓴다
명령어 설명 예제
docker images 로컬 이미지 목록 조회 docker images
docker pull <image> 이미지 다운로드 docker pull nginx:latest
docker push <image> 이미지 업로드 docker push myuser/myapp:1.0
docker build -t <tag> <path> Dockerfile로 이미지 빌드 docker build -t myapp:1.0 .
docker tag <image> <tag> 이미지에 태그 추가 docker tag myapp:1.0 myapp:latest
docker commit <container> <image> 컨테이너를 새 이미지로 저장 docker commit web myweb:1.0
docker rmi <image> 이미지 삭제 docker rmi nginx
docker rmi -f <image> 이미지 강제 삭제 docker rmi -f nginx
docker image prune 댕글링 이미지 삭제 docker image prune
docker image prune -a 어느 컨테이너도 안 쓰는 이미지 전부 삭제 docker image prune -a
docker history <image> 이미지 레이어 히스토리 조회 docker history nginx
docker inspect <image> 이미지 상세 정보 조회 docker inspect nginx
  • 콜론 뒤는 태그다. nginx:latest 의 latest, myapp:1.0 의 1.0 처럼 버전을 가르는 꼬리표이고, 안 적으면 latest 로 친다. docker tag 는 이미지는 그대로 두고 이름표만 하나 더 붙인다
  • 슬래시 앞은 누구의 이미지인지다. myuser/myapp 의 myuser 는 레지스트리의 계정 이름 자리다. 안 적으면 Docker Hub의 공식 이미지로 친다
  • docker build -t myapp:1.0 . 은 Dockerfile을 읽어 이미지를 만들고(빌드) myapp:1.0 이라는 이름표를 붙인다. 끝의 . 은 지금 폴더를 재료로 쓰라는 뜻이고, 따로 정하지 않으면 그 폴더의 Dockerfile 파일을 읽는다
  • prune 이 지우는 범위는 -a 로 갈린다. 옵션 없이 치면 태그도 없고 쓰는 컨테이너도 없는 댕글링 이미지만 지운다. -a 를 붙이면 태그가 있어도 어느 컨테이너도 안 쓰는 이미지를 전부 지운다
  • 레이어 는 이미지를 이루는 층이다. Dockerfile 한 줄이 층 하나가 되고, docker history 가 그 층들을 보여 준다

이미지를 받고 만들어 올리는 예제

아래 예제의 docker build 를 따라 하려면 지금 폴더에 Dockerfile 이 있어야 한다. 가장 짧은 모양은 이렇다. nginx 공식 이미지 설명에 나오는 예를 그대로 옮긴 것이다.

FROM nginx
COPY static-html-directory /usr/share/nginx/html
  • FROM nginx — nginx 이미지를 출발점(바탕 이미지)으로 삼는다
  • COPY static-html-directory /usr/share/nginx/html — 지금 폴더의 static-html-directory 폴더를 이미지 안의 /usr/share/nginx/html 로 복사한다. nginx가 웹 페이지 파일을 읽는 곳이라, 이 이미지로 컨테이너를 띄우면 내 HTML을 내준다
# 이미지 다운로드
docker pull node:18-alpine

# Dockerfile로 이미지 빌드
docker build -t myapp:1.0 .

# 이미지에 추가 태그 지정
docker tag myapp:1.0 myapp:latest
docker tag myapp:1.0 myuser/myapp:1.0

# Docker Hub에 업로드
docker push myuser/myapp:1.0

# 사용하지 않는 이미지 정리
docker image prune -a
  • docker pull node:18-alpine — Node.js 이미지를 받는다. 태그 18-alpine 은 「Node.js 18 버전을 Alpine 리눅스 위에 얹은 것」이다. Alpine은 이미지 크기가 5MB 남짓인 작은 리눅스다
  • docker build -t myapp:1.0 . — 지금 폴더의 Dockerfile로 myapp:1.0 이미지를 만든다
  • docker tag myapp:1.0 myapp:latest — 같은 이미지에 latest 이름표를 하나 더 붙인다
  • docker tag myapp:1.0 myuser/myapp:1.0 — 이번에는 앞에 계정 이름을 붙인다. Docker Hub에 올리려면 이름이 계정이름/이미지이름 모양이어야 내 계정 아래로 올라간다. myuser 는 내 계정 이름으로 바꿔 넣는 자리다
  • docker push myuser/myapp:1.0 — 그 이미지를 Docker Hub에 올린다. 올리기 전에 docker login 으로 로그인해 두어야 한다
  • docker image prune -a — 어느 컨테이너도 안 쓰는 이미지를 전부 지운다

볼륨에 두어야 컨테이너를 지워도 데이터가 남는다

컨테이너 안에 쓴 파일은 그 컨테이너를 지우면 같이 사라진다. DB처럼 데이터를 지켜야 하는 컨테이너는 그래서 데이터를 컨테이너 바깥에 둔다. 이렇게 컨테이너가 사라져도 데이터가 남는 성질을 영속성이라고 부르고, 그 바깥 자리가 볼륨이다. 바깥에 두는 방법은 셋이다.

flowchart TD Host[호스트 시스템] subgraph "Docker 스토리지" Volume[Volume
Docker 관리 영역] Bind[Bind Mount
호스트 특정 경로] Tmpfs[Tmpfs Mount
메모리 임시 저장] end Container[컨테이너] Volume -.->|docker volume| Container Bind -.->|직접 마운트| Container Tmpfs -.->|메모리| Container Host -->|관리| Volume Host -->|직접 연결| Bind style Volume fill:#2d3748,stroke:#48bb78,stroke-width:2px,color:#e2e8f0 style Bind fill:#2d3748,stroke:#ecc94b,stroke-width:2px,color:#e2e8f0 style Tmpfs fill:#2d3748,stroke:#4299e1,stroke-width:2px,color:#e2e8f0
  • Volume — Docker가 직접 관리하는 저장 공간이다. 리눅스에서는 /var/lib/docker/volumes/ 아래에 실제 파일이 놓인다. 데이터를 오래 지킬 때 쓴다
  • Bind Mount — 내 컴퓨터에 이미 있는 폴더 하나를 컨테이너 안 경로에 그대로 겹쳐 보이게 한다(이걸 「마운트」라고 부른다). 내가 고치는 소스 폴더를 컨테이너에 보여 줄 때 쓴다
  • Tmpfs Mount — 디스크가 아니라 메모리에 둔다. 컨테이너가 멈추면 사라지므로 남기면 안 되는 임시 파일에만 쓴다
명령어 설명 예제
docker volume ls 볼륨 목록 조회 docker volume ls
docker volume create <name> 볼륨 생성 docker volume create mydata
docker volume inspect <name> 볼륨 상세 정보 docker volume inspect mydata
docker volume rm <name> 볼륨 삭제 docker volume rm mydata
docker volume prune 쓰지 않는 이름 없는 볼륨 삭제 (-a 면 이름 있는 것까지) docker volume prune
docker run -v <volume>:<path> 볼륨 마운트 docker run -v mydata:/data nginx
docker run -v <host>:<container> 바인드 마운트 docker run -v /host/path:/container/path nginx
docker run --mount 상세한 마운트 옵션 docker run --mount source=mydata,target=/data nginx
  • -v 하나로 볼륨과 바인드 마운트를 둘 다 쓴다. 콜론 앞에 이름(mydata)을 적으면 볼륨이고, 내 컴퓨터의 경로(/host/path)를 적으면 바인드 마운트다. 콜론 뒤는 둘 다 컨테이너 안의 경로다
  • --mount 는 같은 일을 이름=값 으로 풀어 적는다. 더 길지만 무엇을 어디에 붙이는지가 글자로 드러나고, -v 로 못 적는 옵션까지 다 적을 수 있다
  • 이름 없는 볼륨은 Docker가 알아서 만든 볼륨이다. 예를 들어 postgres 이미지는 /var/lib/postgresql/data 를 볼륨 자리로 정해 두어서, 거기에 아무 볼륨도 안 붙이고 띄우면 Docker가 이름 없는 볼륨을 새로 만들어 붙인다. 이름이 없으니 다음 컨테이너가 다시 찾아 쓰지 못하고 남기만 한다
  • docker volume prune 은 옵션 없이 치면 이름 없는 볼륨만 지운다. docker volume create 로 이름을 붙여 만든 볼륨은 -a 를 붙여야 지워진다. 내 컴퓨터의 Docker 클라이언트 28.1.1에서 docker volume prune --help 로 확인한 동작이다

볼륨과 바인드 마운트를 붙이는 예제

# 볼륨 생성
docker volume create postgres-data

# 볼륨을 사용하는 컨테이너 실행
docker run -d \
  --name db \
  -v postgres-data:/var/lib/postgresql/data \
  postgres:15

# 호스트 디렉토리를 바인드 마운트
docker run -d \
  --name web \
  -v /home/user/app:/usr/share/nginx/html:ro \
  nginx

# 읽기 전용(ro) 마운트
docker run -v $(pwd):/app:ro myapp

# 볼륨 목록 확인
docker volume ls

# 사용하지 않는 볼륨 정리
docker volume prune
  • docker volume create postgres-data — postgres-data 라는 이름의 볼륨을 만든다
  • docker run -d \ ... postgres:15 — 네 줄이 \ 로 이어진 명령 하나다. PostgreSQL DB 15 버전 이미지로 db 컨테이너를 띄우고, 볼륨 postgres-data 를 컨테이너 안 /var/lib/postgresql/data 에 붙인다. 그 경로는 postgres 이미지가 DB 파일을 쓰는 곳이라, 거기에 볼륨을 붙여야 컨테이너를 지워도 DB가 남는다. 실제로 이 이미지는 POSTGRES_PASSWORD 라는 환경변수가 없으면 뜨지 않으므로, 이대로 띄우려면 -e POSTGRES_PASSWORD=... 를 더해야 한다(개발 환경 구성 예제 참고)
  • docker run -d \ ... nginx — 내 컴퓨터의 /home/user/app 폴더를 nginx가 웹 페이지 파일을 읽는 /usr/share/nginx/html 에 겹쳐 붙인다. 내 폴더의 HTML 파일을 고치면 nginx가 바로 그 파일을 내준다. 끝의 :ro 는 읽기 전용(read-only)이라는 뜻이라, 컨테이너 쪽에서는 그 폴더에 쓸 수 없다
  • docker run -v $(pwd):/app:ro myapp — 지금 폴더를 myapp 컨테이너의 /app 에 읽기 전용으로 붙인다. myapp 은 앞 절에서 빌드한 예시 이미지다
  • docker volume ls — 볼륨 목록을 본다
  • docker volume prune — 쓰지 않는 이름 없는 볼륨을 지운다. 위에서 만든 postgres-data 는 이름이 있어서 이 명령으로는 안 지워진다

같은 네트워크에 둔 컨테이너끼리 이름으로 통신한다

웹 서버 컨테이너가 DB 컨테이너에 접속하려면 둘이 이어져 있어야 한다. 그 길이 네트워크다. 네트워크는 종류(드라이버)를 골라 만든다.

flowchart TB subgraph "Docker 네트워크 타입" Bridge[Bridge
기본 네트워크] Host[Host
호스트 네트워크 공유] None[None
네트워크 없음] Custom[Custom Bridge
사용자 정의] end C1[컨테이너 1] C2[컨테이너 2] C3[컨테이너 3] Bridge --> C1 Bridge --> C2 Custom --> C3 style Bridge fill:#2d3748,stroke:#4299e1,stroke-width:2px,color:#e2e8f0 style Custom fill:#2d3748,stroke:#48bb78,stroke-width:2px,color:#e2e8f0 style Host fill:#2d3748,stroke:#ecc94b,stroke-width:2px,color:#e2e8f0 style None fill:#2d3748,stroke:#f56565,stroke-width:2px,color:#e2e8f0
  • Bridge — 네트워크를 따로 안 고르면 컨테이너가 들어가는 기본 네트워크다. 그림의 컨테이너 1, 2가 여기 있다
  • Custom Bridge — 내가 docker network create 로 만든 bridge 네트워크다. 그림의 컨테이너 3이 여기 있다. 기본 Bridge와 달리 여기서는 컨테이너가 서로를 이름으로 찾는다. 그래서 컨테이너끼리 통신시킬 때는 네트워크를 직접 만든다
  • Host — 컨테이너가 내 컴퓨터의 네트워크를 그대로 같이 쓴다. Bridge에 있는 컨테이너는 내 컴퓨터와 네트워크가 분리돼 있어서 -p 로 포트를 이어야 바깥에서 닿는데, Host는 그 분리가 없다. 그래서 Host에서는 -p 가 아예 무시된다
  • None — 네트워크를 아예 끊는다

그림에서 Host와 None에 이어진 컨테이너가 없는 것은 이 두 종류를 쓰는 예제가 이 글에 없어서다.

명령어 설명 예제
docker network ls 네트워크 목록 조회 docker network ls
docker network create <name> 네트워크 생성 docker network create mynet
docker network inspect <name> 네트워크 상세 정보 docker network inspect mynet
docker network rm <name> 네트워크 삭제 docker network rm mynet
docker network connect <net> <container> 컨테이너를 네트워크에 연결 docker network connect mynet web
docker network disconnect <net> <container> 네트워크에서 컨테이너 분리 docker network disconnect mynet web
docker run --network <name> 특정 네트워크로 실행 docker run --network mynet nginx

네트워크를 만들고 컨테이너를 잇는 예제

# 사용자 정의 브리지 네트워크 생성
docker network create --driver bridge myapp-net

# 네트워크에 연결된 컨테이너 실행
docker run -d --name db --network myapp-net -e POSTGRES_PASSWORD=secret postgres
docker run -d --name web --network myapp-net nginx

# 같은 네트워크 내에서 컨테이너 이름으로 통신 가능
docker exec web ping db

# 네트워크 없이 띄운 컨테이너 하나 (기본 Bridge에 들어간다)
docker run -d --name redis redis

# 기존 컨테이너를 네트워크에 연결
docker network connect myapp-net redis

# 네트워크 정보 확인
docker network inspect myapp-net

# 네트워크에서 컨테이너 분리
docker network disconnect myapp-net redis
  • docker network create --driver bridge myapp-net — bridge 종류로 myapp-net 네트워크를 만든다. --driver 는 종류를 고르는 옵션이고, 안 적어도 bridge가 기본이다
  • docker run -d --name db --network myapp-net -e POSTGRES_PASSWORD=secret postgres — db 컨테이너를 myapp-net 에 넣어 띄운다. -e 는 컨테이너에 환경변수를 넣는 옵션이고, postgres 이미지는 비밀번호 POSTGRES_PASSWORD 가 없으면 뜨지 않아서 넣었다
  • docker run -d --name web --network myapp-net nginx — web 도 같은 네트워크에 넣는다
  • docker exec web ping db — web 안에서 ping으로 db 라는 이름에 신호를 보낸다. 대답이 오면 web 이 db 를 이름만으로 찾아 닿았다는 뜻이다. 둘이 내가 만든 네트워크에 같이 있어서 이름이 통한다. 다만 nginx 이미지 안에 ping 명령이 들어 있는지는 확인하지 않았다. 없으면 「명령을 찾을 수 없다」는 오류가 나고, 그때는 아래 docker network inspect myapp-net 으로 두 컨테이너가 같은 네트워크에 붙어 있는지까지만 확인할 수 있다
  • docker run -d --name redis redis — 네트워크를 안 고르고 redis 이미지로 컨테이너를 하나 띄운다. 기본 Bridge에 들어간다. 다음 줄에서 「이미 떠 있는 컨테이너를 나중에 네트워크에 잇는」 모습을 보이려고 띄운 것이고, redis가 무슨 프로그램인지는 이 예제에서 중요하지 않다
  • docker network connect myapp-net redis — 이미 떠 있는 redis 를 myapp-net 에도 잇는다
  • docker network inspect myapp-net — 네트워크에 어떤 컨테이너가 붙어 있는지 등 상세 정보를 본다
  • docker network disconnect myapp-net redis — redis 를 myapp-net 에서 뗀다

Docker Compose는 컨테이너 여러 개를 파일 하나로 띄운다

앞 절까지는 컨테이너를 하나씩 명령으로 띄웠다. 웹 페이지를 내주는 웹 서버, 데이터를 주고받는 창구(API)를 맡는 API 서버, DB를 매번 따로 띄우고 네트워크와 볼륨까지 손으로 만들면 명령이 길어지고 빠뜨리기 쉽다. Docker Compose는 이것들을 docker-compose.yml 이라는 YAML 파일 하나에 적어 두고 docker compose up 한 번으로 띄운다. Compose에서는 컨테이너 하나하나를 서비스라고 부른다. 서비스는 「이 이미지로, 이 설정으로 컨테이너를 띄운다」는 정의이고, 띄운 결과가 컨테이너다.

sequenceDiagram participant User as 개발자 participant File as docker-compose.yml participant Compose as Docker Compose participant Docker as Docker Engine participant Services as 서비스들 User->>File: 서비스 정의 작성 User->>Compose: docker compose up Compose->>Docker: 이미지 빌드/풀 Compose->>Docker: 네트워크 생성 Compose->>Docker: 볼륨 생성 Docker->>Services: 컨테이너들 시작 Services->>User: 애플리케이션 실행 User->>Compose: docker compose down Compose->>Docker: 컨테이너 중지 및 삭제 Compose->>Docker: 네트워크 삭제

그림의 Docker Engine은 처음 절에서 말한 그 Engine이다. 데몬과 docker 명령을 합친 이름이지만 실제 일은 그 안의 데몬이 한다. Compose도 결국 데몬에게 부탁해서 일을 시킨다.

명령어 설명 예제
docker compose up 서비스 시작 docker compose up
docker compose up -d 백그라운드로 서비스 시작 docker compose up -d
docker compose down 서비스 중지 및 삭제 docker compose down
docker compose ps 서비스 상태 확인 docker compose ps
docker compose logs 서비스 로그 조회 docker compose logs
docker compose logs -f 로그 실시간 추적 docker compose logs -f
docker compose exec <service> 서비스에서 명령 실행 docker compose exec web bash
docker compose build 이미지 빌드 docker compose build
docker compose pull 이미지 다운로드 docker compose pull
docker compose restart 서비스 재시작 docker compose restart
docker compose stop 서비스 중지 (컨테이너 유지) docker compose stop
docker compose start 중지된 서비스 시작 docker compose start

down 과 stop 은 남기는 것이 다르다. stop 은 컨테이너를 멈추기만 하고 지우지 않아서 start 로 다시 켤 수 있다. down 은 컨테이너와 Compose가 만든 네트워크를 지운다. 볼륨은 down 만으로는 안 지워지고, down -v 를 쳐야 파일의 volumes 에 적힌 이름 있는 볼륨까지 지워진다.

웹 서버 · API · DB를 한 파일에 적는 예제

# docker-compose.yml 예제
version: '3.8'

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html
    networks:
      - frontend
    depends_on:
      - api

  api:
    build: ./api
    environment:
      - DATABASE_URL=postgresql://db:5432/mydb
    networks:
      - frontend
      - backend
    depends_on:
      - db

  db:
    image: postgres:15
    environment:
      - POSTGRES_PASSWORD=secret
      - POSTGRES_DB=mydb
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - backend

volumes:
  postgres-data:

networks:
  frontend:
  backend:

YAML은 들여쓰기로 「무엇 안에 무엇이 있는지」를 나타낸다. 한 칸 더 들어간 줄은 바로 위 줄에 속한다. - 로 시작하는 줄은 목록의 한 칸이다.

  • version: '3.8' — 파일 형식의 버전을 적던 자리다. 지금 Compose는 이 값을 쓰지 않고 「더 이상 쓰지 않는 항목」이라는 경고만 낸다. 빼도 된다
  • services: — 아래에 띄울 서비스를 적는다. web, api, db 는 내가 정한 이름이다. 이 이름이 곧 같은 네트워크 안에서 서로를 찾는 이름이 된다
  • web 의 image: nginx:alpine — Alpine 리눅스 위에 얹은 nginx 이미지로 띄운다
  • ports: - "8080:80" — docker run -p 8080:80 과 같다. 내 컴퓨터 8080번을 컨테이너 80번에 잇는다
  • volumes: - ./html:/usr/share/nginx/html — 콜론 앞이 경로(./html, 이 파일 옆의 html 폴더)이므로 바인드 마운트다. 내 html 폴더를 nginx가 읽는 폴더에 겹친다
  • networks: - frontend — web 을 frontend 네트워크에 넣는다
  • depends_on: - api — api 를 먼저 띄우고 나서 web 을 띄운다. 띄우는 순서를 정하는 항목이다
  • api 의 build: ./api — 이미 있는 이미지를 받는 image 대신, ./api 폴더 안의 Dockerfile로 이미지를 직접 빌드해 띄운다. 그 폴더에 Dockerfile이 없으면 오류가 난다
  • environment: - DATABASE_URL=postgresql://db:5432/mydb — api 컨테이너에 환경변수를 넣는다. 값은 「postgresql 방식으로, db 라는 이름의 컴퓨터의 5432번 포트에 있는 mydb DB」라는 뜻의 주소다. 5432는 PostgreSQL이 기본으로 요청을 받는 포트이고, db 는 아래 db 서비스의 이름이다. 이 값을 읽는 쪽은 api 안의 프로그램이다
  • networks: - frontend - backend — api 는 두 네트워크에 다 들어간다. 그래서 web 과도, db 와도 닿는다
  • db 의 POSTGRES_PASSWORD=secret — postgres 이미지가 읽는 값으로, DB 관리자 비밀번호가 된다. 이 값이 없으면 postgres 컨테이너가 뜨지 않는다
  • db 의 POSTGRES_DB=mydb — 처음 뜰 때 mydb 라는 DB를 만든다. 위 api 의 주소가 가리키는 mydb 가 이것이다. 이 줄이 없으면 postgres 이미지는 사용자 이름(기본 postgres)과 같은 이름으로 DB를 만들어서, mydb 는 생기지 않는다
  • volumes: - postgres-data:/var/lib/postgresql/data — 콜론 앞이 이름이므로 볼륨이다. DB 파일을 볼륨에 둔다
  • networks: - backend — db 는 backend 에만 있다. web 은 backend 에 없어서 db 에 바로 닿지 않는다. 웹 서버가 DB를 직접 건드리지 못하게 가른 것이다
  • 맨 바깥 volumes: postgres-data: — 이름 있는 볼륨 postgres-data 를 쓰겠다고 선언하는 자리다. 값이 비어 있으면 기본 설정으로 만든다
  • 맨 바깥 networks: frontend: backend: — 네트워크 둘을 쓰겠다고 선언한다. 값이 비어 있으면 기본 설정으로 만든다
# 모든 서비스 시작
docker compose up -d

# 특정 서비스만 시작
docker compose up -d web

# 로그 확인
docker compose logs -f api

# 서비스 상태 확인
docker compose ps

# 모든 서비스 중지 및 삭제
docker compose down

# 볼륨까지 함께 삭제
docker compose down -v
  • docker compose up -d — 지금 폴더의 docker-compose.yml 을 읽어 세 서비스를 전부 뒤에서 띄운다
  • docker compose up -d web — web 만 띄우라고 했지만, web 이 depends_on 으로 기대는 api 와, api 가 기대는 db 도 아직 안 떠 있으면 같이 띄운다
  • docker compose logs -f api — api 서비스의 로그를 계속 이어서 본다
  • docker compose ps — 서비스마다 컨테이너가 떠 있는지 본다
  • docker compose down — 컨테이너와 네트워크를 지운다. 볼륨 postgres-data 는 남는다
  • docker compose down -v — 볼륨까지 지운다. DB 데이터가 사라진다

디스크를 차지한 찌꺼기는 system prune으로 한 번에 지운다

명령어 설명 예제
docker system df 디스크 사용량 조회 docker system df
docker system prune 사용하지 않는 리소스 정리 docker system prune
docker system prune -a 모든 사용하지 않는 리소스 정리 docker system prune -a
docker info Docker 시스템 정보 docker info
docker version Docker 버전 정보 docker version
docker stats 컨테이너 리소스 사용량 실시간 확인 docker stats
docker top <container> 컨테이너 프로세스 확인 docker top web

docker system prune 이 지우는 「사용하지 않는 리소스」는 넷이다. 멈춘 컨테이너 전부, 어느 컨테이너도 안 쓰는 네트워크, 댕글링 이미지, 빌드 캐시다. Docker는 이미지를 빌드할 때 층마다 결과를 남겨 두고, 다음 빌드에서 Dockerfile 중 바뀌지 않은 층은 다시 만들지 않는다. 그 남겨 둔 결과가 빌드 캐시라, 빌드를 거듭할수록 쌓인다. -a 를 붙이면 댕글링이 아니어도 어느 컨테이너도 안 쓰는 이미지까지 지운다. 볼륨은 기본으로 안 지운다. --volumes 를 붙여야 이름 없는 볼륨까지 지운다.

docker stats 의 리소스는 CPU와 메모리 같은 컴퓨터 자원이고, docker top 의 프로세스는 컨테이너 안에서 돌고 있는 프로그램 하나하나다.

디스크와 자원을 살피는 예제

# 디스크 사용량 확인
docker system df

# 중지된 컨테이너, 사용하지 않는 네트워크 등 정리
docker system prune

# 사용하지 않는 이미지도 함께 정리
docker system prune -a

# 모든 컨테이너의 리소스 사용량 실시간 모니터링
docker stats

# Docker 정보 확인
docker info
  • docker system df — 이미지·컨테이너·볼륨이 디스크를 얼마나 쓰는지 본다
  • docker system prune — 위에 적은 넷(멈춘 컨테이너, 안 쓰는 네트워크, 댕글링 이미지, 빌드 캐시)을 지운다
  • docker system prune -a — 거기에 어느 컨테이너도 안 쓰는 이미지까지 지운다
  • docker stats — 돌고 있는 컨테이너마다 CPU·메모리를 얼마나 쓰는지 계속 갱신해 보여 준다.
  • docker info — 데몬 쪽 정보(컨테이너 수, 저장 방식 등)를 본다

개발 DB · 로그 · 정리처럼 자주 치는 명령 조합

1. 개발 환경 구성

# Node.js 개발 환경
docker run -it --rm \
  -v $(pwd):/app \
  -w /app \
  -p 3000:3000 \
  node:18-alpine \
  sh

# 데이터베이스 개발용 실행
docker run -d \
  --name dev-db \
  -e POSTGRES_PASSWORD=dev \
  -e POSTGRES_DB=myapp \
  -p 5432:5432 \
  -v postgres-dev:/var/lib/postgresql/data \
  postgres:15

첫 명령은 내 컴퓨터에 Node.js를 깔지 않고 컨테이너 안에서 Node.js 코드를 돌려 보는 모양이다.

  • docker run -it --rm \ — 대화형으로 띄우고, 끝나면 지운다
  • -v $(pwd):/app \ — 지금 폴더(내 소스)를 컨테이너의 /app 에 바인드 마운트한다
  • -w /app \ — 컨테이너 안에서 일을 시작할 폴더(작업 폴더)를 /app 으로 정한다. 들어가자마자 내 소스 폴더에 서 있게 된다
  • -p 3000:3000 \ — 내 컴퓨터 3000번을 컨테이너 3000번에 잇는다. 컨테이너 안에서 띄울 Node.js 앱이 3000번을 쓴다고 가정한 예시 번호다
  • node:18-alpine \ — 쓸 이미지다
  • sh — 컨테이너 안에서 실행할 셸이다. 앞 예제의 bash 대신 sh 라는 셸을 띄운다. Alpine 이미지에 bash 가 들어 있는지는 확인하지 않았다

둘째 명령은 개발용 PostgreSQL을 하나 띄운다.

  • --name dev-db — 이름을 dev-db 로 붙인다
  • -e POSTGRES_PASSWORD=dev — postgres 이미지에 꼭 필요한 관리자 비밀번호를 환경변수로 넣는다
  • -e POSTGRES_DB=myapp — 처음 뜰 때 만들 DB 이름을 myapp 으로 정한다
  • -p 5432:5432 — PostgreSQL이 기본으로 요청을 받는 5432번을 내 컴퓨터 5432번에 이어, 내 컴퓨터의 프로그램이 localhost:5432 로 DB에 접속하게 한다
  • -v postgres-dev:/var/lib/postgresql/data — DB 파일을 볼륨 postgres-dev 에 둔다. 볼륨이 없으면 Docker가 이 이름으로 만든다
  • postgres:15 — PostgreSQL 15 이미지다

2. 로그 및 디버깅

# 최근 100줄의 로그 확인
docker logs --tail 100 my-container

# 타임스탬프와 함께 로그 실시간 추적
docker logs -f --timestamps my-container

# 컨테이너 리소스 사용량 확인
docker stats my-container

# 컨테이너 내부 프로세스 확인
docker top my-container
  • docker logs --tail 100 my-container — 마지막 100줄만 본다. 앞 절의 -n 100 과 같다. my-container 는 컨테이너 이름을 바꿔 넣는 자리다
  • docker logs -f --timestamps my-container — 줄마다 그 로그가 찍힌 시각(타임스탬프)을 앞에 붙여 계속 이어서 본다
  • docker stats my-container — 그 컨테이너 하나의 CPU·메모리 사용량만 본다
  • docker top my-container — 그 컨테이너 안에서 돌고 있는 프로세스 목록을 본다

3. 정리 및 최적화

# 중지된 모든 컨테이너 삭제
docker container prune

# 사용하지 않는 모든 이미지 삭제
docker image prune -a

# 사용하지 않는 볼륨 삭제
docker volume prune

# 한 번에 모두 정리
docker system prune -a --volumes
  • docker container prune — 멈춘 컨테이너를 전부 지운다. 앞의 표에는 없던 명령이다
  • docker image prune -a — 어느 컨테이너도 안 쓰는 이미지를 전부 지운다
  • docker volume prune — 쓰지 않는 이름 없는 볼륨을 지운다. 이름 있는 볼륨까지 지우려면 -a 를 붙인다
  • docker system prune -a --volumes — 위 셋을 한 번에 한다. 단 --volumes 도 이름 없는 볼륨만 지운다

팁 — 별칭 · 자동완성 · 자주 쓰는 run 옵션

별칭(Alias) 설정으로 생산성 향상

# ~/.bashrc 또는 ~/.zshrc에 추가
alias dps='docker ps'
alias dpsa='docker ps -a'
alias di='docker images'
alias dex='docker exec -it'
alias dlog='docker logs -f'
alias dcp='docker compose'

별칭은 긴 명령에 짧은 이름을 붙이는 셸 기능이다. alias 짧은이름='원래 명령' 모양으로 적는다. ~ 는 내 홈 폴더이고, ~/.bashrc 와 ~/.zshrc 는 각각 bash와 zsh 셸이 시작할 때 읽는 설정 파일이다. 여기에 적어 두어야 터미널을 새로 열어도 별칭이 남는다.

  • alias dps='docker ps' — 이제 dps 만 치면 docker ps 가 실행된다
  • alias dex='docker exec -it' — dex web bash 라고 치면 docker exec -it web bash 가 된다. 뒤에 적은 말은 원래 명령 뒤에 그대로 붙는다
  • 나머지 넷도 같은 모양이다

이 문법은 bash · zsh 셸의 것이라 Windows의 PowerShell에서는 그대로 안 된다.

컨테이너 이름으로 자동완성

# Bash completion 설치
sudo apt-get install bash-completion

# Docker completion 활성화
sudo curl -L https://raw.githubusercontent.com/docker/cli/master/contrib/completion/bash/docker -o /etc/bash_completion.d/docker

자동완성은 명령이나 컨테이너 이름을 앞 몇 글자만 치고 Tab 키를 누르면 나머지를 채워 주는 기능이다.

  • sudo apt-get install bash-completion — 우분투에서 apt-get으로 bash 자동완성 기능을 설치한다. sudo는 이 명령을 관리자(root) 권한으로 실행한다
  • sudo curl -L <주소> -o /etc/bash_completion.d/docker — curl로 Docker용 자동완성 스크립트를 내려받는다. -L 은 주소가 다른 곳으로 넘겨 주면 따라가라는 옵션이고, -o 는 받은 내용을 화면 대신 뒤에 적은 파일에 저장하라는 옵션이다. bash 자동완성이 읽는 폴더(/etc/bash_completion.d/)에 넣는다

자주 사용하는 docker run 옵션 조합

# 개발용: 현재 디렉토리 마운트, 자동 삭제, 대화형
docker run -it --rm -v $(pwd):/app -w /app node:18-alpine sh

# 서비스용: 백그라운드, 재시작 정책, 포트 매핑
docker run -d --restart unless-stopped -p 8080:80 nginx
  • 첫 줄 — 개발 환경 구성의 Node.js 명령을 한 줄로 적은 것이다
  • 둘째 줄 — --restart 는 컨테이너가 끝났을 때 다시 켤지 정하는 재시작 정책이다. 기본값은 no(다시 안 켬)다. unless-stopped 는 늘 다시 켜되, 내가 직접 멈춘 컨테이너는 Docker 데몬이 다시 시작해도 켜지 않는다

주의 — 데이터를 잃는 명령과 자주 나는 실패

  • docker rm -f: 실행 중인 컨테이너를 강제 삭제하면 데이터 손실 가능. 컨테이너를 지우면 볼륨에 두지 않은 컨테이너 안의 파일은 사라진다(볼륨 참고)
  • docker system prune -a: 사용하지 않는 모든 이미지가 삭제되므로, 그 이미지가 다시 필요하면 다시 내려받거나 다시 빌드하느라 시간이 든다
  • 볼륨 삭제: docker compose down -v는 데이터를 영구적으로 삭제하므로 주의
  • 포트 충돌: -p 8080:80 의 8080처럼 내 컴퓨터 쪽 포트를 다른 프로그램이 이미 쓰고 있으면 컨테이너가 뜨지 못한다
  • 메모리 제한: 실제 사용자가 쓰는 운영 환경에서는 --memory 옵션으로 메모리 제한 설정 권장. docker run --memory 512m nginx 처럼 숫자 뒤에 단위(b, k, m, g)를 붙여 적고, 6MB보다 작게는 못 정한다

Windows · Linux · macOS는 설치하는 것이 다르다

Windows

  • Docker Desktop 사용 필요
  • WSL 2 백엔드 권장. WSL 2는 Windows 안에서 리눅스를 돌리는 기능이고, 「WSL 2 백엔드」는 Docker Desktop이 Docker 데몬을 그 WSL 2 위에서 돌리는 방식이다
  • 볼륨 경로: /c/Users/... 형식 사용. Git Bash에서 Windows 폴더 C:\Users 를 가리키는 모양이다(내 PC의 Git Bash에서 cd /c/Users 가 C:/Users 로 가는 것을 확인했다). Docker가 바인드 마운트 경로로 이 모양을 받는지는 데몬이 꺼져 있어 실행해 보지 못했다

Linux

  • Docker Engine 직접 설치. Engine은 데몬과 docker 명령을 합친 것이라, 둘을 리눅스에 바로 깐다
  • sudo 없이 사용하려면 docker 그룹에 사용자 추가: sudo usermod -aG docker $USER. 리눅스에서 docker 명령은 데몬이 열어 둔 연결 통로(Unix 소켓)로 부탁을 보내는데, 이 통로의 주인이 root라서 다른 사용자는 원래 sudo를 붙여야 닿는다. 그룹은 여러 사용자를 한 이름으로 묶은 것이고(uid · gid의 gid가 그룹 번호다), 데몬은 그 통로를 docker 그룹에 든 사용자도 쓸 수 있게 열어 둔다. 그래서 내 계정을 docker 그룹에 넣으면 sudo 없이 닿는다. usermod 는 사용자 계정 설정을 바꾸는 명령이고, -aG docker 는 기존 그룹은 그대로 두고 docker 그룹을 더하라는 뜻이다. $USER 는 지금 로그인한 사용자 이름이 든 환경변수다

macOS

  • Docker Desktop 사용
  • 파일 공유 성능: Bind mount보다 볼륨 사용 권장. 왜 macOS에서 성능 차이가 나는지는 이 글에서 다루지 않았다

관련 문서


자신만의 철학을 만들어가는 중입니다.
최상단으로 이동했습니다!
확대 이미지

댓글남기기