Docker는 애플리케이션을 컨테이너라는 가상 환경에 담아서 실행하는 기술입니다.
"내 컴퓨터에서 작동하는 프로그램을 다른 어떤 컴퓨터에서도 똑같이 작동하게 해주는 마법의 상자"
개발자 A: "내 컴퓨터에서는 잘 되는데..."
개발자 B: "내 컴퓨터에서는 안 돼요..."
운영팀: "서버에서는 또 다른 오류가..."
"Docker 컨테이너에서는 모든 컴퓨터에서 똑같이 작동해요!"
장점 | 설명 | 예시 |
---|---|---|
일관성 | 어디서나 동일하게 실행 | 개발-테스트-운영 환경 통일 |
격리 | 다른 프로그램과 충돌 없음 | Node.js 16과 18 동시 사용 |
이식성 | 쉽게 다른 서버로 이동 | AWS에서 Google Cloud로 간단 이전 |
확장성 | 필요에 따라 여러 개 실행 | 트래픽 증가 시 컨테이너 추가 |
실제 애플리케이션이 실행되는 가상 환경
= 프로그램 + 운영체제 + 라이브러리가 모두 포함된 박스
컨테이너를 만들기 위한 설계도/템플릿
= 요리 레시피 같은 것
이미지를 만들기 위한 명령어들이 적힌 파일
= 상세한 요리 레시피
Dockerfile → (빌드) → Image → (실행) → Container
Windows/Mac:
1. Docker Desktop 다운로드
2. 설치 후 고래 아이콘 확인
Ubuntu:
# Docker 설치
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh
# 사용자를 docker 그룹에 추가
sudo usermod -aG docker $USER
docker --version
# Docker version 24.0.x 출력되면 성공!
# "Hello World" 컨테이너 실행
docker run hello-world
이 명령어가 하는 일:
1. hello-world
이미지 찾기
2. 없으면 Docker Hub에서 다운로드
3. 컨테이너 생성하고 실행
4. 메시지 출력 후 종료
# 이미지 목록 보기
docker images
# 이미지 다운로드
docker pull nginx
docker pull node:18
# 이미지 삭제
docker rmi nginx
# 이미지 상세 정보
docker inspect nginx
# 컨테이너 실행
docker run nginx
# 백그라운드로 실행 (-d: detached)
docker run -d nginx
# 포트 매핑해서 실행
docker run -d -p 8080:80 nginx
# 이름 지정해서 실행
docker run -d -p 8080:80 --name my-web nginx
# 실행 중인 컨테이너 보기
docker ps
# 모든 컨테이너 보기 (중지된 것 포함)
docker ps -a
# 컨테이너 중지
docker stop my-web
# 컨테이너 재시작
docker restart my-web
# 컨테이너 삭제
docker rm my-web
# 실행 중인 컨테이너 강제 삭제
docker rm -f my-web
# Nginx 웹서버 실행
docker run -d -p 3000:80 --name my-website nginx
# 브라우저에서 http://localhost:3000 접속
# Nginx 기본 페이지 확인!
# 로그 확인
docker logs my-website
# 컨테이너 내부 접속
docker exec -it my-website bash
# 1. 베이스 이미지 선택
FROM node:18
# 2. 작업 디렉토리 설정
WORKDIR /app
# 3. 파일 복사
COPY package*.json ./
# 4. 명령어 실행
RUN npm install
# 5. 소스 코드 복사
COPY . .
# 6. 포트 노출
EXPOSE 3000
# 7. 컨테이너 시작 명령어
CMD ["npm", "start"]
명령어 | 설명 | 예시 |
---|---|---|
FROM | 베이스 이미지 지정 | FROM node:18-alpine |
WORKDIR | 작업 디렉토리 설정 | WORKDIR /app |
COPY | 파일 복사 | COPY . . |
RUN | 빌드 시 명령어 실행 | RUN npm install |
EXPOSE | 포트 노출 | EXPOSE 3000 |
CMD | 컨테이너 시작 명령어 | CMD ["npm", "start"] |
ENV | 환경변수 설정 | ENV NODE_ENV=production |
# Dockerfile로 이미지 빌드
docker build -t my-app .
# 태그와 함께 빌드
docker build -t my-app:v1.0 .
# 빌드한 이미지로 컨테이너 실행
docker run -d -p 3000:3000 my-app
my-react-app/
├── src/
├── public/
├── package.json
├── Dockerfile
└── .dockerignore
FROM node:18-alpine
# 작업 디렉토리 설정
WORKDIR /app
# package.json 복사 및 의존성 설치
COPY package*.json ./
RUN npm install
# 소스 코드 복사
COPY . .
# 개발 서버 포트 노출
EXPOSE 3000
# 개발 서버 시작
CMD ["npm", "start"]
# 빌드 스테이지
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
# 프로덕션 스테이지
FROM nginx:alpine
# 빌드된 파일을 nginx로 복사
COPY --from=builder /app/build /usr/share/nginx/html
# nginx 설정 파일 복사 (선택사항)
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
coverage
.nyc_output
# 개발용 이미지 빌드
docker build -f Dockerfile.dev -t my-react-app:dev .
# 프로덕션용 이미지 빌드
docker build -t my-react-app:prod .
# 개발 서버 실행
docker run -d -p 3000:3000 -v $(pwd)/src:/app/src my-react-app:dev
# 프로덕션 서버 실행
docker run -d -p 8080:80 my-react-app:prod
여러 개의 Docker 컨테이너를 하나의 애플리케이션으로 정의하고 관리하는 도구
version: '3.8'
services:
# 프론트엔드 (React)
frontend:
build: ./frontend
ports:
- "3000:3000"
volumes:
- ./frontend/src:/app/src
environment:
- REACT_APP_API_URL=http://localhost:5000
depends_on:
- backend
# 백엔드 (Node.js)
backend:
build: ./backend
ports:
- "5000:5000"
environment:
- NODE_ENV=development
- DB_HOST=database
- DB_PORT=5432
- DB_NAME=myapp
- DB_USER=postgres
- DB_PASSWORD=password
- REDIS_URL=redis://redis:6379
depends_on:
- database
- redis
volumes:
- ./backend:/app
- /app/node_modules
# 데이터베이스 (PostgreSQL)
database:
image: postgres:15
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
# 캐시 (Redis)
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
# 웹서버 (Nginx)
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- frontend
- backend
volumes:
postgres_data:
redis_data:
# 모든 서비스 시작 (백그라운드)
docker-compose up -d
# 특정 서비스만 시작
docker-compose up frontend backend
# 서비스 재빌드 후 시작
docker-compose up --build
# 로그 확인
docker-compose logs
# 특정 서비스 로그 확인
docker-compose logs backend
# 실시간 로그 보기
docker-compose logs -f
# 실행 중인 서비스 확인
docker-compose ps
# 서비스 스케일링
docker-compose up --scale backend=3
# 모든 서비스 중지
docker-compose stop
# 모든 서비스 중지 및 삭제
docker-compose down
# 볼륨까지 삭제
docker-compose down -v
# 개발용 compose 파일
docker-compose -f docker-compose.dev.yml up -d
# 핫 리로드 가능한 개발 서버
docker-compose exec backend npm run dev
# 테스트 환경 구성
docker-compose -f docker-compose.test.yml up -d
# 단위 테스트 실행
docker-compose exec backend npm test
# 통합 테스트 실행
docker-compose exec backend npm run test:integration
# .github/workflows/docker.yml
name: Docker Build and Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: |
docker build -t myapp:$GITHUB_SHA .
docker tag myapp:$GITHUB_SHA myapp:latest
- name: Run tests
run: |
docker run --rm myapp:latest npm test
- name: Push to registry
run: |
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push myapp:$GITHUB_SHA
docker push myapp:latest
# 프로덕션 서버에서
docker pull myapp:latest
docker-compose -f docker-compose.prod.yml up -d
# 무중단 배포 (Blue-Green)
docker-compose -f docker-compose.prod.yml up -d --scale app=2
# 컨테이너 내부 접근
docker exec -it <container-name> bash
docker exec -it <container-name> sh # alpine 기반
# 컨테이너 리소스 사용량 확인
docker stats
# 컨테이너 상세 정보
docker inspect <container-name>
# 실시간 로그 보기
docker logs -f <container-name>
# 컨테이너에서 프로세스 확인
docker exec <container-name> ps aux
# 중지된 컨테이너 삭제
docker container prune
# 사용하지 않는 이미지 삭제
docker image prune
# 사용하지 않는 볼륨 삭제
docker volume prune
# 사용하지 않는 네트워크 삭제
docker network prune
# 모든 것 정리 (주의!)
docker system prune -a
# 특정 기간 이전 이미지 삭제
docker image prune -a --filter "until=24h"
# 좋은 예: 캐시 활용
FROM node:18-alpine
WORKDIR /app
# package.json을 먼저 복사 (의존성 변경이 없으면 캐시 사용)
COPY package*.json ./
RUN npm ci --only=production
# 소스 코드는 나중에 복사
COPY . .
RUN npm run build
# 나쁜 예: 매번 전체 재빌드
FROM node:18-alpine
WORKDIR /app
COPY . . # 소스 변경 시마다 전체 재빌드
RUN npm install && npm run build
# 빌드 스테이지 (큰 이미지)
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 런타임 스테이지 (작은 이미지)
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
# 1. 루트 사용자 피하기
FROM node:18-alpine
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
USER nextjs
# 2. 최소한의 권한으로 실행
COPY --chown=nextjs:nodejs . .
# 3. .dockerignore 활용
# .dockerignore 파일에서 민감한 정보 제외
.env
.env.local
.git
node_modules
*.log
특성 | Docker 컨테이너 | 가상머신 |
---|---|---|
크기 | 가볍다 (수십 MB) | 무겁다 (수 GB) |
시작 속도 | 빠르다 (초 단위) | 느리다 (분 단위) |
자원 사용 | 효율적 | 많은 오버헤드 |
격리 수준 | 프로세스 격리 | 완전한 OS 격리 |
이식성 | 매우 높음 | 상대적으로 낮음 |
사용 사례 | 마이크로서비스, CI/CD | 레거시 앱, 완전 격리 필요시 |