Docker로 개발환경 통일하기: 컨테이너 기반 개발의 모든 것
🐳 Docker로 개발환경 통일하기
"내 컴퓨터에서는 잘 되는데요?" 이 말, 개발자라면 한 번쯤은 들어봤을 겁니다. 환경 차이로 인한 문제는 개발팀의 영원한 골칫거리죠. 하지만 Docker를 사용하면 이런 문제를 깔끔하게 해결할 수 있습니다!
이 가이드에서는 Docker를 활용해 일관된 개발환경을 구축하는 모든 방법을 알아보겠습니다. 초보자도 따라할 수 있도록 단계별로 설명드릴게요! 🚀
🔍 Docker란 무엇인가?
Docker는 컨테이너 기반 가상화 플랫폼으로, 애플리케이션과 그 실행 환경을 하나의 패키지로 묶어 관리할 수 있게 해줍니다.
기존 개발환경의 문제점
- 🖥️ 환경 차이: 개발자마다 다른 OS, 라이브러리 버전
- ⏰ 설정 시간: 새 팀원 온보딩에 하루 이상 소요
- 🐛 버그 재현 어려움: "내 환경에서는 안 되는데요"
- 📦 의존성 충돌: 프로젝트별 다른 버전 요구사항
Docker의 해결책
- 🎯 환경 일관성: 어디서나 동일한 실행 환경
- ⚡ 빠른 설정: 몇 분 내에 개발환경 구축
- 🔄 쉬운 공유: 환경 설정을 코드로 관리
- 🏠 격리: 프로젝트간 독립적인 환경
📦 Docker 설치하기
Windows/macOS: Docker Desktop
# 1. Docker Desktop 다운로드
https://www.docker.com/products/docker-desktop
# 2. 설치 후 Docker 실행 확인
docker --version
docker run hello-world
Ubuntu/Linux
# Docker 설치
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
# 사용자를 docker 그룹에 추가
sudo usermod -aG docker $USER
# 재로그인 후 확인
docker --version
docker run hello-world
필수 확인사항
# Docker 서비스 상태 확인
docker info
# Docker Compose 설치 확인
docker-compose --version
# 시스템 리소스 확인
docker system df
🏗️ Dockerfile 작성 마스터하기
Dockerfile 기본 구조
# Node.js 애플리케이션 예시
FROM node:16-alpine
# 작업 디렉터리 설정
WORKDIR /app
# 의존성 파일 복사 (캐시 최적화)
COPY package*.json ./
# 의존성 설치
RUN npm ci --only=production
# 애플리케이션 소스 복사
COPY . .
# 애플리케이션 사용자 생성 (보안)
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# 포트 노출
EXPOSE 3000
# 헬스체크 추가
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# 실행 명령
CMD ["npm", "start"]
Python Django 애플리케이션
FROM python:3.9-slim
# 시스템 패키지 업데이트
RUN apt-get update && apt-get install -y \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
# 작업 디렉터리
WORKDIR /app
# Python 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 애플리케이션 복사
COPY . .
# 정적 파일 수집
RUN python manage.py collectstatic --noinput
# 포트 설정
EXPOSE 8000
# 실행 명령
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]
멀티스테이지 빌드 (최적화)
# 빌드 스테이지
FROM node:16-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# 프로덕션 스테이지
FROM node:16-alpine AS production
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app/dist ./dist
EXPOSE 3000
CMD ["npm", "start"]
🎼 Docker Compose로 멀티 컨테이너 관리
기본 docker-compose.yml
version: '3.8'
services:
# 웹 애플리케이션
web:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- DB_HOST=db
- REDIS_HOST=redis
volumes:
- .:/app
- /app/node_modules
depends_on:
- db
- redis
restart: unless-stopped
# 데이터베이스
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: developer
POSTGRES_PASSWORD: secretpassword
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
restart: unless-stopped
# Redis 캐시
redis:
image: redis:6-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
# Nginx 프록시
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
restart: unless-stopped
volumes:
postgres_data:
redis_data:
개발환경별 설정
# docker-compose.dev.yml
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile.dev
environment:
- NODE_ENV=development
- HOT_RELOAD=true
volumes:
- .:/app
- /app/node_modules
command: npm run dev
db:
ports:
- "5432:5432" # 로컬 접근 허용
# docker-compose.prod.yml
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile.prod
environment:
- NODE_ENV=production
restart: always
deploy:
replicas: 2
resources:
limits:
memory: 512M
reservations:
memory: 256M
🛠️ 개발환경별 활용 사례
1. 프론트엔드 개발환경 (React/Vue)
# Dockerfile.dev
FROM node:16-alpine
WORKDIR /app
# 의존성 설치
COPY package*.json ./
RUN npm install
# 포트 노출
EXPOSE 3000
# 개발 서버 실행
CMD ["npm", "run", "dev"]
# docker-compose.yml
version: '3.8'
services:
frontend:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
volumes:
- .:/app
- /app/node_modules
environment:
- CHOKIDAR_USEPOLLING=true # Hot reload 설정
2. 백엔드 API 개발환경
# docker-compose.yml
version: '3.8'
services:
api:
build: .
ports:
- "8000:8000"
environment:
- DEBUG=1
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
volumes:
- .:/app
depends_on:
- db
- redis
db:
image: postgres:13
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:6-alpine
# 개발도구들
adminer:
image: adminer
ports:
- "8080:8080"
volumes:
postgres_data:
3. 마이크로서비스 개발환경
# docker-compose.yml
version: '3.8'
services:
# API Gateway
gateway:
build: ./gateway
ports:
- "80:80"
depends_on:
- user-service
- product-service
# 사용자 서비스
user-service:
build: ./services/user
environment:
- DB_HOST=user-db
depends_on:
- user-db
user-db:
image: postgres:13
environment:
POSTGRES_DB: users
# 상품 서비스
product-service:
build: ./services/product
environment:
- DB_HOST=product-db
depends_on:
- product-db
product-db:
image: mongodb:4.4
# 메시지 큐
rabbitmq:
image: rabbitmq:3-management
ports:
- "15672:15672"
# 모니터링
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
grafana:
image: grafana/grafana
ports:
- "3000:3000"
⚡ 성능 최적화 전략
이미지 크기 최적화
# 1. Alpine 리눅스 사용
FROM node:16-alpine # ubuntu 대신 alpine
# 2. 불필요한 파일 제거
RUN apt-get update && apt-get install -y \
curl \
&& rm -rf /var/lib/apt/lists/*
# 3. 멀티스테이지 빌드
FROM node:16-alpine AS build
# ... 빌드 과정
FROM node:16-alpine AS production
COPY --from=build /app/dist ./dist
# 4. .dockerignore 활용
# .dockerignore 파일
node_modules
npm-debug.log
Dockerfile
.dockerignore
.git
.gitignore
README.md
.env
coverage
.nyc_output
빌드 캐시 최적화
# 의존성 파일을 먼저 복사 (캐시 활용)
COPY package*.json ./
RUN npm ci
# 소스코드는 나중에 복사
COPY . .
# 레이어 합치기
RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/*
실행 시간 최적화
# docker-compose.yml
version: '3.8'
services:
web:
build: .
# 헬스체크로 의존성 관리
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
# 리소스 제한
deploy:
resources:
limits:
memory: 512M
cpus: '0.5'
reservations:
memory: 256M
cpus: '0.25'
🔒 보안 베스트 프랙티스
1. 사용자 권한 관리
# root 사용자 피하기
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# 또는 기존 사용자 활용
USER node
2. 민감 정보 관리
# .env 파일 사용
# .env
DATABASE_PASSWORD=secretpassword
API_KEY=your-secret-key
# docker-compose.yml
services:
web:
env_file:
- .env
# 또는 Docker secrets 사용 (Swarm mode)
secrets:
db_password:
file: ./db_password.txt
services:
web:
secrets:
- db_password
3. 이미지 스캔
# Docker Hub에서 공식 이미지 사용
FROM node:16-alpine # 공식 이미지
# 취약점 스캔
docker scan myapp:latest
# 베이스 이미지 정기 업데이트
FROM node:16-alpine@sha256:abc123... # 특정 digest 사용
🎯 실전 워크플로우
일일 개발 루틴
#!/bin/bash
# start-dev.sh
echo "🚀 개발 환경 시작..."
# 최신 이미지 확인
docker-compose pull
# 개발 환경 실행
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# 로그 확인
docker-compose logs -f web
팀 협업을 위한 Makefile
# Makefile
.PHONY: build up down logs shell test clean
# 개발 환경 시작
up:
docker-compose up -d
# 개발 환경 중지
down:
docker-compose down
# 이미지 빌드
build:
docker-compose build
# 로그 확인
logs:
docker-compose logs -f
# 컨테이너 접속
shell:
docker-compose exec web sh
# 테스트 실행
test:
docker-compose exec web npm test
# 환경 정리
clean:
docker system prune -a
CI/CD 파이프라인 통합
# .github/workflows/docker.yml
name: Docker Build and Test
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build test image
run: docker build -f Dockerfile.test -t myapp:test .
- name: Run tests
run: docker run --rm myapp:test npm test
- name: Build production image
run: docker build -t myapp:latest .
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Push to DockerHub
run: docker push myapp:latest
🐛 문제 해결과 디버깅
일반적인 문제들
# 1. 포트 충돌
Error: Port 3000 is already in use
# 해결: 다른 포트 사용 또는 기존 프로세스 종료
docker-compose down
lsof -ti:3000 | xargs kill
# 2. 볼륨 권한 문제
Permission denied
# 해결: 적절한 사용자 ID 설정
USER 1000:1000
# 3. 메모리 부족
OOMKilled
# 해결: 메모리 한계 증가
deploy:
resources:
limits:
memory: 1G
디버깅 팁
# 컨테이너 내부 확인
docker-compose exec web sh
# 로그 실시간 확인
docker-compose logs -f --tail=100 web
# 리소스 사용량 모니터링
docker stats
# 네트워크 문제 디버깅
docker network ls
docker network inspect myapp_default
# 볼륨 상태 확인
docker volume ls
docker volume inspect myapp_postgres_data
📊 모니터링과 로깅
로깅 설정
# docker-compose.yml
services:
web:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# 중앙집중식 로깅
elasticsearch:
image: elasticsearch:7.14.0
logstash:
image: logstash:7.14.0
kibana:
image: kibana:7.14.0
ports:
- "5601:5601"
헬스체크와 모니터링
# Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# docker-compose.yml
services:
web:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
🎓 마무리
Docker를 활용한 개발환경 통일은 단순히 "내 컴퓨터에서는 되는데"라는 말을 없애는 것 이상의 가치가 있습니다:
- 🚀 개발 속도 향상: 환경 설정 시간 단축
- 🤝 협업 효율성: 일관된 개발 환경
- 🔄 배포 일관성: 개발과 운영 환경의 일치
- 🛡️ 격리와 보안: 안전한 개발 환경
- 📈 확장성: 마이크로서비스 아키텍처 대응
처음에는 복잡해 보일 수 있지만, 한 번 구축해두면 팀 전체의 생산성이 크게 향상됩니다. 특히 새로운 팀원이 합류했을 때 몇 분 만에 개발환경을 구축할 수 있다는 점은 정말 큰 장점이죠.
오늘부터 Docker를 활용해서 더 효율적이고 안정적인 개발 워크플로우를 만들어보세요! 🐳
Featured image by Growtika on Unsplash