Docker 완전 정복: 컨테이너로 시작하는 현대적 개발 환경|가나투데이
Docker 완전 정복: 컨테이너로 시작하는 현대적 개발 환경|가나투데이
Docker란 무엇인가?
Docker는 애플리케이션을 컨테이너라는 격리된 환경에 패키징하여 실행하는 플랫폼입니다. 쉽게 비유하자면, 화물을 운송할 때 사용하는 표준 컨테이너처럼, 소프트웨어와 그 실행에 필요한 모든 것을 하나의 표준화된 단위로 묶어주는 기술입니다.
전통적인 방식의 문제점
개발자들이 흔히 겪는 상황을 떠올려보세요:
개발자: "제 컴퓨터에서는 잘 되는데요?" 운영팀: "서버에서는 안 돌아가는데요?"
이런 문제가 발생하는 이유는:
- 개발 환경과 운영 환경의 차이 (Windows vs Linux)
- Python, Node.js 등 실행 환경 버전 불일치
- 필요한 라이브러리나 의존성 누락
- 시스템 설정 차이
Docker의 해결책
Docker 컨테이너는 애플리케이션 + 실행 환경 + 의존성을 모두 포함합니다. 마치 이사할 때 짐을 모두 박스에 담듯이, 프로그램 실행에 필요한 모든 것을 하나의 컨테이너에 담아버립니다.
결과: "어디서든 똑같이 실행됩니다"
Docker가 필요한 이유
1. 일관된 개발 환경
팀원 10명이 각자 다른 운영체제를 사용해도, Docker를 사용하면 모두가 동일한 환경에서 작업할 수 있습니다. 신입 개발자가 합류했을 때 "환경 설정에 이틀 걸렸어요"라는 말이 사라집니다.
2. 빠른 배포와 확장
새로운 서버에 애플리케이션을 배포할 때, 복잡한 설치 과정 없이 Docker 컨테이너만 실행하면 됩니다. 서비스 확장이 필요하면 컨테이너를 추가로 띄우기만 하면 됩니다.
3. 리소스 효율성
가상 머신(VM)은 각각 완전한 운영체제를 포함하지만, Docker 컨테이너는 호스트 OS의 커널을 공유하여 훨씬 가볍고 빠릅니다.
- VM 방식: 애플리케이션 1개 = OS 전체 + 수 GB 메모리
- Docker 방식: 애플리케이션 1개 = 수십~수백 MB
4. 마이크로서비스 아키텍처
큰 애플리케이션을 작은 서비스들로 나누어 개발할 때, 각 서비스를 별도의 컨테이너로 관리하면 독립적으로 개발, 배포, 확장할 수 있습니다.
Docker 핵심 개념 이해하기
1. 이미지 (Image)
실행 가능한 패키지의 템플릿입니다. 클래스와 객체의 관계처럼, 이미지는 설계도이고 컨테이너는 실제 실행 중인 인스턴스입니다.
비유: 붕어빵 틀 = 이미지, 만들어진 붕어빵 = 컨테이너
2. 컨테이너 (Container)
이미지를 실행한 인스턴스입니다. 각 컨테이너는 격리된 환경에서 독립적으로 실행됩니다.
3. Dockerfile
이미지를 만드는 레시피입니다. 어떤 베이스 이미지를 사용할지, 무엇을 설치할지, 어떻게 실행할지 등을 정의합니다.
4. Docker Hub
이미지를 공유하는 저장소입니다. GitHub가 코드 저장소라면, Docker Hub는 이미지 저장소입니다.
Docker 설치하기
Windows / Mac 사용자
- Docker Desktop 다운로드
- 설치 파일 실행
- 설치 후 재부팅
- Docker Desktop 실행
Linux (Ubuntu) 사용자
# 이전 버전 제거
sudo apt-get remove docker docker-engine docker.io containerd runc
# 필수 패키지 설치
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
# Docker 공식 GPG 키 추가
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
# Docker 저장소 추가
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Docker 설치
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 현재 사용자를 docker 그룹에 추가 (sudo 없이 사용)
sudo usermod -aG docker $USER
newgrp docker
설치 확인
docker --version
docker run hello-world
"Hello from Docker!"가 보이면 성공입니다.
Docker 기본 사용법: 실전 예제
예제 1: 첫 번째 컨테이너 실행하기
# nginx 웹서버 실행
docker run -d -p 8080:80 --name my-nginx nginx
# 명령어 분해:
# run: 컨테이너 실행
# -d: 백그라운드에서 실행 (detached mode)
# -p 8080:80: 호스트의 8080포트를 컨테이너의 80포트로 연결
# --name my-nginx: 컨테이너 이름 지정
# nginx: 사용할 이미지 이름
웹브라우저에서 http://localhost:8080 접속하면 nginx 환영 페이지를 볼 수 있습니다.
예제 2: 컨테이너 관리하기
# 실행 중인 컨테이너 목록 보기
docker ps
# 모든 컨테이너 보기 (중지된 것 포함)
docker ps -a
# 컨테이너 중지
docker stop my-nginx
# 컨테이너 시작
docker start my-nginx
# 컨테이너 재시작
docker restart my-nginx
# 컨테이너 삭제 (중지 상태여야 함)
docker rm my-nginx
# 실행 중인 컨테이너 강제 삭제
docker rm -f my-nginx
예제 3: 컨테이너 내부 들어가기
# 실행 중인 컨테이너의 쉘 접속
docker exec -it my-nginx bash
# 컨테이너 내부에서 명령 실행
docker exec my-nginx ls /usr/share/nginx/html
# 컨테이너 로그 확인
docker logs my-nginx
# 실시간 로그 모니터링
docker logs -f my-nginx
예제 4: Python 웹 애플리케이션 실행하기
간단한 Flask 앱을 Docker로 실행해봅시다.
1단계: 프로젝트 디렉토리 생성
mkdir my-flask-app
cd my-flask-app
2단계: app.py 작성
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return '<h1>Hello from Docker!</h1><p>Flask 앱이 컨테이너에서 실행 중입니다.</p>'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
3단계: requirements.txt 작성
Flask==3.0.0
4단계: Dockerfile 작성
# Python 3.11 이미지를 베이스로 사용
FROM python:3.11-slim
# 작업 디렉토리 설정
WORKDIR /app
# 의존성 파일 복사
COPY requirements.txt .
# 의존성 설치
RUN pip install --no-cache-dir -r requirements.txt
# 애플리케이션 코드 복사
COPY app.py .
# 컨테이너가 5000 포트를 사용함을 명시
EXPOSE 5000
# 애플리케이션 실행
CMD ["python", "app.py"]
5단계: 이미지 빌드 및 실행
# 이미지 빌드
docker build -t my-flask-app .
# 빌드된 이미지 확인
docker images
# 컨테이너 실행
docker run -d -p 5000:5000 --name flask-container my-flask-app
# 브라우저에서 http://localhost:5000 접속
예제 5: 데이터베이스 실행하기
# PostgreSQL 실행
docker run -d \
--name postgres-db \
-e POSTGRES_PASSWORD=mysecretpassword \
-e POSTGRES_DB=myapp \
-p 5432:5432 \
-v postgres-data:/var/lib/postgresql/data \
postgres:15
# MySQL 실행
docker run -d \
--name mysql-db \
-e MYSQL_ROOT_PASSWORD=rootpassword \
-e MYSQL_DATABASE=myapp \
-p 3306:3306 \
-v mysql-data:/var/lib/mysql \
mysql:8.0
# MongoDB 실행
docker run -d \
--name mongodb \
-e MONGO_INITDB_ROOT_USERNAME=admin \
-e MONGO_INITDB_ROOT_PASSWORD=password \
-p 27017:27017 \
-v mongo-data:/data/db \
mongo:7
환경 변수 (-e) 설명:
- 컨테이너에 설정값을 전달하는 방법
- 비밀번호, 데이터베이스 이름 등을 지정
볼륨 (-v) 설명:
- 컨테이너가 삭제되어도 데이터가 유지되도록 저장
볼륨이름:컨테이너내부경로형식
Docker 볼륨: 데이터 영속성 관리
컨테이너는 기본적으로 상태를 저장하지 않습니다. 컨테이너를 삭제하면 내부 데이터도 사라집니다. 볼륨은 이 문제를 해결합니다.
볼륨 종류
1. Named Volume (권장)
# 볼륨 생성
docker volume create my-data
# 볼륨 사용
docker run -v my-data:/app/data my-image
# 볼륨 목록
docker volume ls
# 볼륨 상세 정보
docker volume inspect my-data
# 볼륨 삭제
docker volume rm my-data
2. Bind Mount
# 호스트의 특정 디렉토리를 컨테이너에 마운트
docker run -v /home/user/myapp:/app my-image
# 현재 디렉토리 마운트 (개발 시 유용)
docker run -v $(pwd):/app my-image
실용 예제: WordPress 블로그 구축
# 1. 네트워크 생성 (컨테이너 간 통신)
docker network create wordpress-network
# 2. MySQL 실행
docker run -d \
--name wordpress-db \
--network wordpress-network \
-e MYSQL_ROOT_PASSWORD=rootpass \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=wppass \
-v wordpress-db:/var/lib/mysql \
mysql:8.0
# 3. WordPress 실행
docker run -d \
--name wordpress \
--network wordpress-network \
-e WORDPRESS_DB_HOST=wordpress-db \
-e WORDPRESS_DB_USER=wpuser \
-e WORDPRESS_DB_PASSWORD=wppass \
-e WORDPRESS_DB_NAME=wordpress \
-p 8080:80 \
-v wordpress-files:/var/www/html \
wordpress:latest
# 브라우저에서 http://localhost:8080 접속
Docker Compose: 여러 컨테이너 한 번에 관리하기
위의 WordPress 예제처럼 여러 컨테이너를 실행할 때, Docker Compose를 사용하면 훨씬 편리합니다.
docker-compose.yml 작성
version: '3.8'
services:
db:
image: mysql:8.0
volumes:
- wordpress-db:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: wordpress
MYSQL_USER: wpuser
MYSQL_PASSWORD: wppass
networks:
- wordpress-network
wordpress:
image: wordpress:latest
depends_on:
- db
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wpuser
WORDPRESS_DB_PASSWORD: wppass
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress-files:/var/www/html
networks:
- wordpress-network
volumes:
wordpress-db:
wordpress-files:
networks:
wordpress-network:
Docker Compose 명령어
# 모든 서비스 시작
docker compose up -d
# 로그 확인
docker compose logs
# 특정 서비스 로그
docker compose logs wordpress
# 서비스 중지
docker compose stop
# 서비스 삭제 (볼륨은 유지)
docker compose down
# 서비스와 볼륨 모두 삭제
docker compose down -v
# 서비스 재시작
docker compose restart
# 특정 서비스만 실행
docker compose up -d wordpress
실전 활용: 개발 환경 구성
Node.js 개발 환경
docker-compose.yml:
version: '3.8'
services:
app:
image: node:18
working_dir: /app
volumes:
- ./:/app
ports:
- "3000:3000"
command: sh -c "npm install && npm run dev"
environment:
- NODE_ENV=development
mongodb:
image: mongo:7
ports:
- "27017:27017"
volumes:
- mongo-data:/data/db
volumes:
mongo-data:
Python Django 개발 환경
Dockerfile:
FROM python:3.11
ENV PYTHONUNBUFFERED=1
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
docker-compose.yml:
version: '3.8'
services:
web:
build: .
command: python manage.py runserver 0.0.0.0:8000
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
- db
environment:
- DATABASE_URL=postgresql://postgres:password@db:5432/django_db
db:
image: postgres:15
environment:
POSTGRES_PASSWORD: password
POSTGRES_DB: django_db
volumes:
- postgres-data:/var/lib/postgresql/data
volumes:
postgres-data:
유용한 Docker 명령어 모음
이미지 관리
# 이미지 검색
docker search nginx
# 이미지 다운로드
docker pull nginx:latest
# 로컬 이미지 목록
docker images
# 이미지 삭제
docker rmi nginx
# 사용하지 않는 이미지 모두 삭제
docker image prune -a
# 이미지에 태그 지정
docker tag my-app:latest my-app:v1.0
컨테이너 관리
# 컨테이너 리소스 사용량 확인
docker stats
# 컨테이너 정보 상세 보기
docker inspect my-container
# 컨테이너에서 파일 복사
docker cp my-container:/app/file.txt ./file.txt
docker cp ./file.txt my-container:/app/
# 컨테이너를 이미지로 저장
docker commit my-container my-new-image
# 사용하지 않는 컨테이너 삭제
docker container prune
시스템 정리
# 모든 중지된 컨테이너, 사용하지 않는 네트워크, 댕글링 이미지 삭제
docker system prune
# 볼륨까지 모두 삭제 (주의!)
docker system prune -a --volumes
# 디스크 사용량 확인
docker system df
Docker 베스트 프랙티스
1. .dockerignore 사용
node_modules
npm-debug.log
.git
.env
*.md
.DS_Store
불필요한 파일을 이미지에 포함시키지 않아 빌드 속도가 빨라지고 이미지 크기가 줄어듭니다.
2. 멀티 스테이지 빌드
# 빌드 스테이지
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 실행 스테이지
FROM node:18-slim
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
CMD ["node", "dist/index.js"]
개발 도구는 빌드 스테이지에만 포함하고, 최종 이미지는 실행에 필요한 것만 포함하여 크기를 최소화합니다.
3. 레이어 캐싱 활용
# 좋은 예: 자주 변경되지 않는 것부터 복사
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# 나쁜 예: 모든 것을 먼저 복사
FROM python:3.11
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
4. 환경별 설정 분리
# 개발 환경
docker compose -f docker-compose.yml -f docker-compose.dev.yml up
# 운영 환경
docker compose -f docker-compose.yml -f docker-compose.prod.yml up
문제 해결 팁
포트가 이미 사용 중일 때
# 특정 포트를 사용하는 프로세스 찾기 (Linux/Mac)
lsof -i :8080
# Windows
netstat -ano | findstr :8080
# 다른 포트 사용
docker run -p 8081:80 nginx
컨테이너가 즉시 종료될 때
# 로그 확인
docker logs my-container
# 대화형 모드로 실행하여 문제 파악
docker run -it my-image /bin/bash
디스크 공간 부족
# 사용량 확인
docker system df
# 정리
docker system prune -a
docker volume prune
결론: Docker로 얻는 것들
Docker는 단순한 도구가 아니라 개발 문화의 변화입니다. Docker를 사용하면:
- 개발자 온보딩 시간이 수일에서 수분으로 단축됩니다
- "내 컴퓨터에서는 되는데" 문제가 사라집니다
- 마이크로서비스 아키텍처 구현이 현실적이 됩니다
- CI/CD 파이프라인 구축이 간단해집니다
- 개발과 운영의 간극이 좁혀집니다
처음에는 새로운 개념에 혼란스러울 수 있지만, 기본 명령어 몇 가지만 익히면 곧 Docker 없는 개발은 상상할 수 없게 됩니다. 위의 예제들을 직접 실행해보면서 컨테이너의 강력함을 체험해보시기 바랍니다.
#가나 투데이 #ganatoday
그린아프로




