G: 안녕하세요, 개발자 여러분! 🧑💻✨
현대 소프트웨어 개발 환경에서 Docker
는 이제 선택이 아닌 필수가 되어가고 있습니다. “내 컴퓨터에서는 잘 되는데, 왜 서버에서는 안 될까요?” 라는 악명 높은 질문을 해결해주는 마법 같은 도구이자, 개발 환경을 일관성 있게 유지하고 배포를 간소화하는 데 혁혁한 공을 세우고 있죠.
하지만 Docker를 단순히 docker run
명령어로 컨테이너를 띄우는 것 이상으로 활용하려면, 몇 가지 핵심 명령어를 숙지하고 능숙하게 사용하는 것이 중요합니다. 이 글에서는 개발 생산성을 폭발적으로 높여줄 수 있는 Docker의 핵심 명령어들을 총정리하고, 실제 개발 시나리오와 예시를 통해 상세하게 설명해 드리겠습니다. 이 글을 통해 여러분의 개발 워크플로우가 더욱 매끄럽고 효율적으로 변모할 것이라고 확신합니다! 🚀
📦 Docker, 왜 개발자에게 필수일까요? (간단 개념)
본격적인 명령어 탐구에 앞서, Docker의 핵심 개념을 짧게 짚고 넘어가겠습니다.
- 이미지 (Image): 컨테이너를 만들기 위한 “설계도” 또는 “템플릿”입니다. 애플리케이션 실행에 필요한 모든 것 (코드, 런타임, 시스템 도구, 라이브러리 등)이 포함된 읽기 전용 템플릿입니다.
Docker Hub
와 같은 레지스트리에 저장됩니다. 🖼️ - 컨테이너 (Container): 이미지의 실행 가능한 “인스턴스”입니다. 이미지를 기반으로 격리된 환경에서 애플리케이션이 실행되는 단위입니다. 가볍고 휴대성이 뛰어나며, 호스트 시스템에 영향을 주지 않습니다. 🐳
- Dockerfile: 이미지를 빌드하기 위한 명령어들을 모아둔 텍스트 파일입니다. 이 파일을 통해 이미지를 생성하는 과정을 자동화하고 재현 가능하게 만듭니다. 🏗️
Docker는 이러한 이미지와 컨테이너를 통해 “환경 불일치” 문제를 해결하고, 개발-테스트-운영 환경을 동일하게 유지하여 개발 생산성을 비약적으로 향상시킵니다.
1. 컨테이너 생명 주기 관리: 시작, 정지, 삭제의 기본! 🏃♀️
가장 기본적이면서도 중요한 컨테이너의 생성, 실행, 중지, 삭제 관련 명령어들입니다.
1.1 docker run
: 컨테이너 실행의 마법사 ✨
새로운 컨테이너를 생성하고 실행하는 가장 핵심적인 명령어입니다. 옵션이 많아 복잡해 보일 수 있지만, 몇 가지만 알면 아주 유용하게 쓸 수 있습니다.
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
자주 사용되는 옵션:
-d
(detached mode): 컨테이너를 백그라운드에서 실행하고 터미널의 제어권을 돌려줍니다. 웹 서버와 같이 지속적으로 실행되어야 하는 서비스에 적합합니다.docker run -d --name my-nginx -p 80:80 nginx
-p
(publish port): 호스트 포트와 컨테이너 포트를 연결합니다.호스트포트:컨테이너포트
형식으로 지정합니다.docker run -p 8080:80 my-web-app
(호스트의 8080번 포트로 접속하면 컨테이너의 80번 포트로 연결)
--name
: 컨테이너에 알아보기 쉬운 이름을 부여합니다. 이름을 지정하지 않으면 Docker가 무작위로 생성합니다.docker run --name my-database postgres:latest
-v
(volume): 호스트 파일 시스템의 디렉토리를 컨테이너 내부의 디렉토리와 마운트하여 데이터를 영속적으로 저장하거나, 개발 시 코드 동기화에 사용합니다.호스트경로:컨테이너경로
형식입니다.docker run -v /Users/dev/my-app:/app my-node-app
(호스트의/Users/dev/my-app
디렉토리를 컨테이너의/app
에 마운트)
--rm
: 컨테이너가 종료될 때 자동으로 삭제되도록 합니다. 임시 컨테이너를 실행할 때 유용합니다.docker run --rm my-temporary-script
-it
(interactive + tty): 컨테이너와 상호작용할 수 있는 터미널을 할당합니다. 디버깅이나 일회성 작업 시 유용합니다. (exec
과 함께 자주 사용됩니다)docker run -it ubuntu bash
(Ubuntu 컨테이너에서 bash 쉘 실행)
예시:
# Nginx 웹 서버를 백그라운드로 실행하고, 호스트의 80번 포트와 연결
docker run -d --name my-nginx -p 80:80 nginx:latest
# 현재 디렉토리의 웹 애플리케이션 코드를 컨테이너에 마운트하여 개발 서버 실행
# (예: Node.js 앱의 경우)
docker run -p 3000:3000 -v $(pwd):/app --name my-dev-app my-node-app npm start
1.2 docker ps
: 실행 중인 컨테이너 확인 📊
현재 실행 중인 컨테이너 목록을 보여줍니다. 컨테이너 ID, 이미지 이름, 명령어, 생성 시간, 상태, 포트 매핑, 이름 등을 확인할 수 있습니다.
docker ps # 실행 중인 컨테이너만 표시
docker ps -a # 모든 컨테이너 (실행 중이거나 종료된 컨테이너) 표시
예시:
docker ps
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# abcd1234efgh nginx:latest "/docker-entrypoint.…" 2 minutes ago Up 2 minutes 0.0.0.0:80->80/tcp my-nginx
1.3 docker start / stop / restart
: 컨테이너 제어 ▶️🛑🔄
이미 생성된 컨테이너를 시작, 중지, 또는 재시작하는 명령어입니다.
docker start [CONTAINER_NAME_OR_ID]
docker stop [CONTAINER_NAME_OR_ID]
docker restart [CONTAINER_NAME_OR_ID]
예시:
docker stop my-nginx # my-nginx 컨테이너 중지
docker start my-nginx # my-nginx 컨테이너 다시 시작
docker restart my-nginx # my-nginx 컨테이너 재시작
1.4 docker rm
: 컨테이너 삭제 🗑️
종료된 컨테이너를 삭제합니다. 실행 중인 컨테이너는 삭제되지 않습니다. (-f
옵션을 사용하면 강제 삭제 가능)
docker rm [CONTAINER_NAME_OR_ID] [CONTAINER_NAME_OR_ID...]
예시:
docker rm my-old-container # my-old-container 삭제
docker rm $(docker ps -aq) # 모든 종료된 컨테이너 삭제 (강력 추천! 🧹)
💡 팁:
docker ps -aq
는 모든 컨테이너 ID를 반환하는 명령어입니다. 이를docker rm
과 함께 사용하면 한 번에 여러 컨테이너를 삭제할 수 있어 매우 편리합니다.
2. 컨테이너 내부 작업 및 디버깅: 문제 해결의 열쇠 🔍
개발 중 컨테이너 내부를 들여다보고 싶거나, 특정 명령어를 실행해야 할 때 유용한 명령어들입니다.
2.1 docker exec
: 컨테이너 내부 명령어 실행 🧑💻
실행 중인 컨테이너 내부에서 새로운 명령어를 실행합니다. 컨테이너 내부에서 쉘 스크립트를 실행하거나, 디버깅을 위해 파일 시스템을 탐색할 때 매우 유용합니다.
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
자주 사용되는 옵션:
-it
(interactive + tty): 컨테이너 내부에서 터미널을 실행하여 상호작용합니다.
예시:
# my-nginx 컨테이너 내부에서 bash 쉘 실행
docker exec -it my-nginx bash
# 컨테이너 내부에서 특정 파일 목록 확인
docker exec my-nginx ls -la /usr/share/nginx/html
# 실행 중인 웹 앱 컨테이너에서 테스트 실행 (예: Jest)
docker exec my-dev-app npm test
2.2 docker logs
: 컨테이너 로그 확인 📜
컨테이너의 표준 출력(stdout)과 표준 에러(stderr)를 확인할 수 있습니다. 애플리케이션의 동작을 모니터링하거나 에러를 디버깅할 때 필수적인 명령어입니다.
docker logs [OPTIONS] CONTAINER
자주 사용되는 옵션:
-f
(follow): 실시간으로 로그를 계속해서 출력합니다. (tail -f
와 유사)--tail N
: 마지막 N줄의 로그만 출력합니다.--since DURATION
: 특정 시간 이후의 로그만 출력합니다.
예시:
docker logs my-nginx # my-nginx 컨테이너의 모든 로그 출력
docker logs -f my-nginx # my-nginx 컨테이너의 로그를 실시간으로 확인
docker logs --tail 100 my-dev-app # my-dev-app 컨테이너의 마지막 100줄 로그 출력
3. 이미지 관리: 나만의 환경 만들기 🏗️
개발 환경을 이미지로 만들고 관리하는 것은 Docker 활용의 핵심입니다.
3.1 docker build
: 이미지 빌드 🛠️
Dockerfile
을 사용하여 새로운 이미지를 빌드합니다. 프로젝트의 루트 디렉토리에서 실행하는 경우가 많습니다.
docker build [OPTIONS] PATH | URL | -
자주 사용되는 옵션:
-t
(tag): 빌드된 이미지에 이름과 태그를 부여합니다.이름:태그
형식으로 지정합니다. 태그를 지정하지 않으면latest
가 기본으로 붙습니다..
(dot): Dockerfile의 경로를 지정합니다. 보통 현재 디렉토리를 의미합니다.
예시:
# 현재 디렉토리의 Dockerfile을 사용하여 'my-app:v1.0' 이미지 빌드
docker build -t my-app:v1.0 .
# 테스트용 이미지 빌드 (캐시 사용 안 함)
docker build --no-cache -t my-app:test .
3.2 docker images
: 이미지 목록 확인 🖼️
로컬에 저장된 이미지 목록을 보여줍니다. 이미지 이름, 태그, ID, 생성 시간, 크기 등을 확인할 수 있습니다.
docker images
예시:
docker images
# REPOSITORY TAG IMAGE ID CREATED SIZE
# my-app v1.0 abcdef123456 2 minutes ago 123MB
# nginx latest ghijk7890123 3 weeks ago 134MB
3.3 docker rmi
: 이미지 삭제 ❌
로컬에 저장된 이미지를 삭제합니다. 이미지를 사용하는 컨테이너가 실행 중이거나 존재하는 경우, 먼저 해당 컨테이너를 삭제해야 이미지를 삭제할 수 있습니다.
docker rmi [IMAGE_NAME_OR_ID] [IMAGE_NAME_OR_ID...]
예시:
docker rmi my-app:v1.0 # 'my-app:v1.0' 이미지 삭제
docker rmi nginx # 'nginx:latest' 이미지 삭제 (태그 생략 시 latest)
docker rmi $(docker images -aq) # 모든 이미지 삭제 (주의! ⚠️)
3.4 docker pull
: 이미지 다운로드 ⬇️
Docker Hub와 같은 이미지 레지스트리에서 이미지를 다운로드합니다.
docker pull [IMAGE_NAME]:[TAG]
예시:
docker pull ubuntu:latest # Ubuntu 최신 이미지 다운로드
docker pull python:3.9-slim # Python 3.9 slim 버전 이미지 다운로드
4. 볼륨 및 데이터 관리: 데이터 영속성의 핵심 💾
컨테이너는 기본적으로 휘발성이므로, 데이터를 영속적으로 저장하려면 볼륨 관리가 필수입니다.
4.1 docker volume
: 볼륨 생성 및 관리
Docker 볼륨은 컨테이너가 삭제되어도 데이터가 유지되도록 해주는 메커니즘입니다.
docker volume create [VOLUME_NAME] # 볼륨 생성
docker volume ls # 생성된 볼륨 목록 확인
docker volume inspect [VOLUME_NAME] # 볼륨 상세 정보 확인
docker volume rm [VOLUME_NAME] # 볼륨 삭제
예시:
docker volume create my-data-volume # 'my-data-volume'이라는 이름의 볼륨 생성
# my-data-volume을 '/var/lib/mysql'에 마운트하여 MySQL 컨테이너 실행
docker run -d --name my-mysql -e MYSQL_ROOT_PASSWORD=secret -v my-data-volume:/var/lib/mysql mysql:latest
docker volume ls
docker run -v
를 통해 이미지를 실행할 때, 이름을 지정하지 않은 볼륨 (-v /host/path:/container/path
)은 바인드 마운트라고 부르며, 호스트의 특정 경로와 컨테이너 내부 경로를 직접 연결합니다.docker volume
명령어로 관리하는 것은 명명된 볼륨(named volume)으로, Docker가 호스트의 특정 디렉토리에 생성하여 관리합니다. 개발 시에는 바인드 마운트를, 운영 환경에서는 명명된 볼륨을 주로 사용합니다.
5. 시스템 자원 최적화 및 정리: 깔끔한 개발 환경 유지 🧹
개발을 하다 보면 수많은 컨테이너와 이미지가 쌓여 디스크 공간을 차지하게 됩니다. 주기적인 정리는 필수입니다!
5.1 docker system prune
: 한방에 싹! ✨
사용하지 않는 모든 Docker 리소스(컨테이너, 이미지, 볼륨, 네트워크)를 삭제하여 디스크 공간을 확보합니다. 개발 환경에서 디스크 공간 부족 문제를 해결하는 데 아주 효과적입니다.
docker system prune [OPTIONS]
자주 사용되는 옵션:
-a
(all): 사용하지 않는 이미지뿐만 아니라, 빌드 캐시 등을 포함한 모든 사용되지 않는 이미지를 삭제합니다.--volumes
: 사용되지 않는 볼륨까지 삭제합니다. (볼륨은 중요한 데이터가 있을 수 있으므로 신중하게 사용해야 합니다!)
예시:
# 사용하지 않는 모든 컨테이너, 이미지 (최신 태그 제외), 네트워크 삭제
docker system prune
# 사용하지 않는 모든 컨테이너, 이미지 (모든 태그), 네트워크, 볼륨까지 삭제
docker system prune -a --volumes
⚠️ 주의:
docker system prune -a --volumes
는 로컬 개발 환경에서 불필요한 모든 Docker 자원을 삭제하여 깨끗한 상태로 되돌리는 데 유용하지만, 필요한 데이터가 있는 볼륨까지 삭제될 수 있으므로 사용 전 반드시 확인하세요!
6. 멀티 서비스 환경 관리: Docker Compose 🏗️🚀
단일 컨테이너로 구성된 애플리케이션은 드뭅니다. 대부분 웹 서버, 데이터베이스, 캐시 서버 등 여러 서비스가 유기적으로 연결되어 동작합니다. Docker Compose
는 이러한 멀티 컨테이너 애플리케이션을 정의하고 실행하는 도구입니다.
Docker Compose 설치: Docker Desktop에는 기본 포함되어 있으며, Linux에서는 별도로 설치해야 할 수 있습니다. (설치 가이드는 Docker 공식 문서를 참조하세요!)
docker-compose.yml
파일 작성
docker-compose.yml
파일 하나로 여러 서비스의 빌드, 네트워크, 볼륨 등을 정의합니다.
예시 docker-compose.yml
:
version: '3.8' # Compose 파일 형식 버전
services:
web: # 웹 서비스 정의
build: . # 현재 디렉토리의 Dockerfile로 이미지 빌드
ports:
- "8000:8000" # 호스트 8000 -> 컨테이너 8000 포트 연결
volumes:
- .:/app # 현재 디렉토리를 컨테이너 /app에 마운트 (코드 동기화)
depends_on: # web 서비스가 db 서비스에 의존함을 명시
- db
environment: # 환경 변수 설정
DATABASE_URL: postgres://user:password@db:5432/mydb
command: python app.py # 컨테이너 시작 시 실행될 명령어
db: # 데이터베이스 서비스 정의
image: postgres:13 # postgres 13 이미지 사용
environment: # 환경 변수 설정 (DB 계정 정보)
POSTGRES_DB: mydb
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- db-data:/var/lib/postgresql/data # 데이터 영속화를 위한 볼륨 마운트
volumes:
db-data: # 명명된 볼륨 정의
Docker Compose 핵심 명령어들
docker-compose.yml
파일이 있는 디렉토리에서 아래 명령어를 실행합니다.
docker-compose up
:docker-compose.yml
파일에 정의된 모든 서비스를 빌드(필요시)하고 실행합니다.docker-compose up -d
(백그라운드 실행)docker-compose up --build
(강제로 이미지 재빌드)
docker-compose down
:docker-compose.yml
파일에 정의된 모든 서비스 컨테이너를 중지하고 삭제합니다.docker-compose down --volumes
(연결된 볼륨까지 삭제)
docker-compose ps
: Compose 프로젝트의 서비스 상태를 확인합니다.docker-compose logs [SERVICE_NAME]
: 특정 서비스의 로그를 확인합니다.docker-compose logs -f web
(web 서비스 로그 실시간 확인)
docker-compose exec [SERVICE_NAME] [COMMAND]
: 특정 서비스 컨테이너 내부에서 명령어를 실행합니다.docker-compose exec web bash
(web 서비스 컨테이너 bash 쉘 실행)
Docker Compose를 사용하는 이유:
- 간소화된 환경 설정: 복잡한
--name
,-p
,-v
옵션들을 한 파일에 정의하여 관리할 수 있습니다. - 원클릭 실행:
docker-compose up
단 한 줄로 전체 애플리케이션 스택을 실행할 수 있습니다. - 재현성: 팀원 모두가 동일한 개발 환경을 몇 초 만에 구축할 수 있습니다.
- 격리: 각 서비스가 독립적인 컨테이너에서 실행되어 서로 영향을 주지 않습니다.
✨ 보너스! 개발 생산성을 위한 Docker 팁
.dockerignore
파일 사용:.gitignore
와 유사하게, Docker 이미지 빌드 시 제외할 파일이나 디렉토리를 지정합니다. 불필요한 파일을 이미지에 포함시키지 않아 빌드 시간을 단축하고 이미지 크기를 줄일 수 있습니다.node_modules/
.git/
.env
tmp/
- Dockerfile 멀티스테이지 빌드 (Multi-stage Builds): 빌드 단계와 최종 이미지 단계를 분리하여 최종 이미지 크기를 극적으로 줄일 수 있습니다. 예를 들어, 빌드 시에만 필요한 컴파일러나 라이브러리를 최종 이미지에는 포함시키지 않는 방식입니다. 💡
- 캐시 활용: Docker는 레이어 기반으로 이미지를 빌드합니다.
Dockerfile
의 변경이 적은 부분을 상단에 배치하고 자주 변경되는 부분을 하단에 배치하면, 빌드 시 캐시를 효율적으로 사용하여 빌드 시간을 단축할 수 있습니다. ⚡
마치며 🙌
지금까지 개발 생산성을 극대화할 수 있는 Docker의 핵심 명령어들을 자세히 살펴보았습니다. run
, ps
, exec
, build
, system prune
과 더불어 Docker Compose
까지 익숙해지신다면, 여러분의 개발 워크플로우는 훨씬 더 효율적이고 즐거워질 것입니다. 🚀
Docker는 끊임없이 발전하고 있으며, 더 많은 기능과 옵션들이 존재합니다. 이 글에서 다룬 명령어들을 직접 실행해보면서 익숙해지는 것이 가장 중요합니다. 꾸준히 연습하고, 여러분의 프로젝트에 Docker를 적극적으로 도입해보세요! 궁금한 점이 있다면 언제든지 공식 문서나 커뮤니티를 활용하여 찾아보는 습관을 들이는 것도 좋습니다.
여러분의 멋진 Docker 활용을 응원합니다! Happy Dockering! 🐳💖