개발자를 위한 Docker: 이미지, 컨테이너, Compose
여러분의 컴퓨터에서는 완벽하게 작동하는 웹 앱을 만들었지만, 서버에 배포하거나 동료와 공유하면 작동하지 않습니다. 종속성이 누락되거나, 버전이 충돌하거나, 환경 변수가 잘못 설정됩니다. 이것이 바로 고전적인 "내 컴퓨터에서는 작동해" 문제입니다. Docker는 애플리케이션과 그 종속성을 컨테이너라는 표준화된 단위로 패키징하여 이 문제를 해결합니다. 이 가이드에서는 Docker의 핵심 개념인 이미지, 컨테이너, Compose와 이를 사용하여 개발 워크플로를 간소화하는 방법을 배웁니다.
Docker란 무엇인가?
Docker는 애플리케이션을 컨테이너로 빌드, 실행, 배포하기 위한 플랫폼입니다. 컨테이너는 소프트웨어를 실행하는 데 필요한 모든 것(코드, 런타임, 시스템 도구, 라이브러리, 설정)을 포함하는 경량의 독립 실행형 패키지입니다. 컨테이너는 애플리케이션을 서로 그리고 기본 인프라로부터 격리하여 환경 전반에 걸쳐 일관성을 보장합니다.
이미지 vs. 컨테이너
이미지는 컨테이너를 생성하기 위한 지침이 포함된 읽기 전용 템플릿입니다. 애플리케이션과 그 환경의 스냅샷과 같습니다. 컨테이너는 이미지의 실행 가능한 인스턴스입니다. Docker API 또는 CLI를 사용하여 컨테이너를 생성, 시작, 중지, 이동 또는 삭제할 수 있습니다.
이미지를 클래스로, 컨테이너를 그 클래스의 객체(인스턴스)로 생각하세요. 동일한 이미지에서 여러 컨테이너를 실행할 수 있으며, 각각은 서로 격리됩니다.
Dockerfile로 이미지 빌드하기
Dockerfile은 이미지를 빌드하기 위한 지침이 담긴 텍스트 파일입니다. 다음은 Node.js 앱을 위한 간단한 예입니다:
# Use an official Node.js runtime as a parent image
FROM node:18-alpine
# Set the working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy the rest of the application code
COPY . .
# Expose the port the app runs on
EXPOSE 3000
# Define the command to run the app
CMD ["node", "server.js"]
이미지를 빌드하려면 다음을 실행하세요:
docker build -t my-node-app .
-t 플래그는 이미지에 이름(my-node-app)을 태그합니다. 끝의 .는 빌드 컨텍스트(현재 디렉터리)를 지정합니다.
컨테이너 실행하기
이미지가 있으면 컨테이너를 실행할 수 있습니다:
docker run -p 3000:3000 -d my-node-app
이 명령은 호스트의 포트 3000을 컨테이너의 포트 3000에 매핑하고 분리 모드(-d)로 실행합니다.
일반적인 명령어:
docker ps– 실행 중인 컨테이너 목록docker ps -a– 모든 컨테이너 목록(중지된 것 포함)docker stop <container_id>– 컨테이너 중지docker rm <container_id>– 컨테이너 삭제docker images– 이미지 목록docker rmi <image_id>– 이미지 삭제
볼륨으로 데이터 관리하기
컨테이너는 임시적이므로 제거되면 데이터가 손실됩니다. 데이터를 유지하려면 볼륨을 사용하세요. 볼륨은 컨테이너 파일 시스템 외부에 있는 디렉터리로, Docker가 관리합니다.
docker run -v my_volume:/app/data my-node-app
개발을 위해 호스트 디렉터리를 마운트(바인드 마운트)할 수도 있습니다:
docker run -v $(pwd):/app my-node-app
Docker Compose: 다중 컨테이너 애플리케이션
실제 애플리케이션은 종종 여러 서비스(웹 서버, 데이터베이스, 캐시 등)를 필요로 합니다. Docker Compose를 사용하면 YAML 파일을 사용하여 다중 컨테이너 Docker 애플리케이션을 정의하고 실행할 수 있습니다. 다음은 PostgreSQL 데이터베이스가 있는 Node.js 앱을 위한 docker-compose.yml 예입니다:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:password@db:5432/mydb
depends_on:
- db
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
전체 스택을 실행하려면 다음을 실행하세요:
docker compose up -d
이 명령은 웹 서비스를 빌드하고, Postgres 이미지를 가져오고, 네트워크를 생성하고, 두 컨테이너를 모두 시작합니다. depends_on은 데이터베이스가 웹 서비스보다 먼저 시작되도록 보장합니다.
컨테이너, 네트워크 및 볼륨을 중지하고 제거하려면:
docker compose down -v
개발 시 Docker 모범 사례
- .dockerignore 사용하여
node_modules및.git과 같은 파일을 빌드 컨텍스트에서 제외하세요. - 멀티 스테이지 빌드 활용하여 이미지를 작게 유지하세요. 예를 들어, 한 스테이지에서 앱을 빌드하고 프로덕션 아티팩트만 더 작은 런타임 이미지로 복사합니다.
- 버전 고정을 Dockerfile에 지정하세요(예:
node:latest대신node:18-alpine). 재현성을 위해 필요합니다. - 영구 데이터에는 볼륨을 사용하고 개발 중 실시간 코드 리로딩에는 바인드 마운트를 사용하세요.
- 컨테이너를 무상태로 유지하세요. 상태는 볼륨이나 외부 서비스에 저장하세요.
비교: Docker vs. 가상 머신
| 측면 | Docker 컨테이너 | 가상 머신 |
|---|---|---|
| 시작 시간 | 초 | 분 |
| 리소스 사용 | 경량(OS 커널 공유) | 무거움(VM당 전체 OS) |
| 격리 | 프로세스 수준 | 하드웨어 수준 |
| 이식성 | 높음(Docker가 실행되는 어디든 실행) | 하이퍼바이저 호환성에 의해 제한됨 |
FAQ
이미지와 컨테이너의 차이점은 무엇인가요?
이미지는 애플리케이션과 그 환경을 정의하는 읽기 전용 템플릿입니다. 컨테이너는 이미지의 실행 인스턴스입니다. 동일한 이미지에서 여러 컨테이너를 생성할 수 있습니다.
Docker Compose는 언제 사용해야 하나요?
애플리케이션이 함께 작동해야 하는 여러 서비스(예: 웹 서버, 데이터베이스, 캐시)로 구성된 경우 Docker Compose를 사용하세요. 개발 및 테스트를 위한 오케스트레이션을 단순화합니다.
Docker에서 데이터를 유지하려면 어떻게 해야 하나요?
볼륨을 사용하여 컨테이너 파일 시스템 외부에 데이터를 유지하세요. 볼륨은 Docker가 관리하며 컨테이너 간에 공유할 수 있습니다. 바인드 마운트를 사용하여 호스트 디렉터리를 마운트할 수도 있습니다.
다음 프로젝트를 컨테이너화할 준비가 되셨나요? 간단한 앱을 위한 Dockerfile을 작성하는 것부터 시작하고, Docker Compose로 서비스를 점진적으로 추가하세요. 더 많은 개발자 도구를 원하시면 JSON Formatter를 확인하여 구성 파일을 검증하고 아름답게 만드세요.