개발자를 위한 Docker: 이미지, 컨테이너, Compose
Docker가 개발 워크플로우를 어떻게 간소화할 수 있는지 들어보셨을 겁니다. 하지만 컨테이너가 처음이라면 용어와 명령어가 압도적으로 느껴질 수 있습니다. 이 가이드는 복잡함을 걷어내고 Docker 이미지, 컨테이너, Compose를 실용적으로 이해할 수 있도록 도와드립니다. 오늘부터 효과적으로 사용해 보세요.
Docker란 무엇이며 왜 중요한가?
Docker는 애플리케이션과 그 종속성을 컨테이너라는 표준화된 단위로 패키징하는 플랫폼입니다. 컨테이너는 가볍고 이식성이 뛰어나며 서로 격리되어 있어, 노트북, 동료의 컴퓨터, 프로덕션 서버 어디서든 일관되게 앱을 실행할 수 있습니다.
핵심 이점은 무엇일까요? "내 컴퓨터에서는 되는데"라는 말은 이제 과거의 일이 됩니다. Docker는 코드가 실행되는 환경이 어디서나 동일하도록 보장합니다.
이미지 vs 컨테이너: 구성 요소
시작하기 전에 이미지와 컨테이너의 차이를 이해해야 합니다.
- 이미지: 애플리케이션 코드, 런타임, 라이브러리, 종속성을 포함하는 읽기 전용 템플릿입니다. 스냅샷이나 청사진이라고 생각하세요.
- 컨테이너: 이미지의 실행 가능한 인스턴스입니다. 컨테이너를 시작, 중지, 이동, 삭제할 수 있습니다. 동일한 이미지에서 여러 컨테이너를 실행할 수 있습니다.
요약하자면: 이미지는 레시피이고, 컨테이너는 그것으로 만든 요리입니다.
개발자를 위한 필수 Docker 명령어
가장 자주 사용하게 될 명령어들입니다:
docker pull <image>– Docker Hub와 같은 레지스트리에서 이미지를 다운로드합니다.docker images– 모든 로컬 이미지를 나열합니다.docker run <image>– 이미지에서 컨테이너를 생성하고 시작합니다.docker ps– 실행 중인 컨테이너를 나열합니다.-a를 추가하면 중지된 컨테이너를 포함한 모든 컨테이너를 볼 수 있습니다.docker stop <container>– 실행 중인 컨테이너를 정상적으로 중지합니다.docker rm <container>– 중지된 컨테이너를 제거합니다.docker rmi <image>– 이미지를 제거합니다.docker exec -it <container> bash– 실행 중인 컨테이너 내부에서 대화형 셸을 엽니다.
예를 들어, 간단한 Nginx 웹 서버를 실행하려면:
docker run -d -p 8080:80 nginx
이 명령은 nginx 이미지를 가져오고(없는 경우), 분리 모드(-d)로 컨테이너를 시작하며, 호스트의 포트 8080을 컨테이너의 포트 80에 매핑합니다(-p 8080:80).
Dockerfile로 나만의 이미지 만들기
애플리케이션을 패키징하려면 지침이 담긴 텍스트 파일인 Dockerfile을 만듭니다. 다음은 Node.js 앱을 위한 간단한 예제입니다:
# Use an official Node.js runtime as a parent image
FROM node:18-alpine
# Set the working directory
WORKDIR /usr/src/app
# Copy package.json and install dependencies
COPY package*.json ./
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", "app.js"]
다음 명령으로 이미지를 빌드합니다:
docker build -t my-node-app .
그런 다음 실행합니다:
docker run -p 3000:3000 my-node-app
Docker Compose로 멀티 컨테이너 앱 간소화하기
실제 애플리케이션은 종종 여러 서비스가 필요합니다: 웹 서버, 데이터베이스, 캐시 등. 이를 docker run으로 개별 관리하는 것은 번거로워집니다. Docker Compose를 사용하면 YAML 파일로 멀티 컨테이너 애플리케이션을 정의하고 실행할 수 있습니다.
docker-compose.yml 파일을 만듭니다:
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
depends_on:
- db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: example
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
그런 다음 다음 명령으로 모든 것을 시작합니다:
docker compose up -d
Compose는 네트워킹, 볼륨, 종속성을 대신 처리합니다. 컨테이너, 네트워크, 볼륨을 중지하고 제거하려면 docker compose down을 실행합니다.
개발에서 Docker 모범 사례
- .dockerignore 사용: 빌드 컨텍스트에서 불필요한 파일(예:
node_modules)을 제외하여 빌드 속도를 높입니다. - 멀티 스테이지 빌드 활용: 빌드 시점 종속성과 런타임을 분리하여 최종 이미지를 작게 유지합니다.
- 버전 고정: 재현성을 위해 정확한 이미지 태그(예:
node:18.16.0-alpine)를 지정합니다. - 비루트로 실행: Dockerfile에서 사용자를 생성하고
USER로 전환하여 보안을 강화합니다. - 영구 데이터에 볼륨 사용: 데이터베이스 및 기타 상태 저장 서비스는 데이터를 컨테이너 내부가 아닌 볼륨에 저장해야 합니다.
흔한 실수와 피하는 방법
- 비대한 이미지: 최소한의 기본 이미지(예: Alpine)에서 시작하고 패키지 캐시를 정리합니다.
- 포트 충돌: 호스트 포트가 이미 사용 중이 아닌지 확인합니다.
docker ps로 확인하세요. - 로그 무시:
docker logs <container>를 사용하여 문제를 디버깅합니다. - 로컬 개발에 Compose 미사용: 간단한 앱도 Compose의 선언적 설정의 이점을 누릴 수 있습니다.
FAQ
Docker 이미지와 컨테이너의 차이점은 무엇인가요?
이미지는 애플리케이션과 그 환경을 정의하는 읽기 전용 템플릿입니다. 컨테이너는 해당 이미지의 실행 인스턴스입니다. 하나의 이미지에서 여러 컨테이너를 만들 수 있습니다.
단일 컨테이너 앱에 Docker Compose가 필요한가요?
반드시 그렇지는 않습니다. 간단한 앱에는 docker run으로 충분합니다. 그러나 Compose는 하나의 서비스라도 환경 변수, 볼륨, 네트워킹을 더 쉽게 관리할 수 있게 해주며 나중에 확장을 간소화합니다.
Docker에서 데이터를 어떻게 유지하나요?
볼륨을 사용하세요. 볼륨은 컨테이너의 파일 시스템 외부에 저장되며 컨테이너가 삭제되어도 유지됩니다. Compose에서는 볼륨을 정의하고 컨테이너의 경로에 마운트합니다(예: volumes: - mydata:/var/lib/data).
Docker 워크플로우를 최적화할 준비가 되셨나요? 컨테이너 로그를 분석해야 할 때, Nginx Log Analyzer를 사용하여 로그 데이터를 빠르게 파싱하고 시각화해 보세요. 컨테이너화된 웹 서버 디버깅에 완벽합니다.