Docker & Image: "내 컴퓨터에선 되는데 왜 네 컴퓨터에선 안 돼?"
1. 문제점: 환경의 차이
개발자가 프로그램을 만들 때, 내 컴퓨터에는 A, B, C 프로그램이 깔려있어서 잘 돌아갔습니다. 하지만 친구 컴퓨터에는 B가 없고, C 버전이 달라서 에러가 납니다. "이 프로그램 돌리려면 뭐뭐 깔아야 해?" → 설명하기도, 맞추기도 너무 힘듭니다.
2. 해결책: 컨테이너 (Container) - "도시락"
그래서 프로그램을 실행에 필요한 모든 것(코드, 라이브러리, 설정)과 함께 하나의 상자(도시락)에 담아서 포장해버립니다. 이 상자를 컨테이너라고 부릅니다. 이제 친구에게 "이 상자만 받아서 열어봐"라고 하면, 친구 컴퓨터에 뭐가 깔려있든 상관없이 똑같이 돌아갑니다.
가상머신(VM)과 차이점
- 가상머신 (VM): 집(OS)을 통째로 짓는 것. (무겁고 느림)
- 컨테이너 (Docker): 텐트만 치는 것. (가볍고 빠름)
3. 이미지 (Image) - "도시락 레시피 & 레이어"
컨테이너를 찍어내는 원본 틀(설계도)을 이미지라고 합니다. 이미지는 효율성을 위해 레이어(Layer)라는 겹겹이 쌓인 구조로 되어 있습니다.
레이어 구조 (시각화 목표)
마치 투명 필름(OHP 필름)을 여러 장 겹친 것과 같습니다.
- Base Layer: 윈도우/리눅스 같은 기초 바닥.
- Add Python: 그 위에 파이썬을 설치한 투명 필름을 얹음.
- Add Code: 그 위에 내 코드를 쓴 투명 필름을 얹음.
= 최종 결과물: 위에서 내려다보면 하나의 합쳐진 그림처럼 보임!
왜 이렇게 할까? 파이썬까지 똑같고 코드만 다른 프로그램이 있다면, 1, 2번 필름은 재사용하고 3번 필름만 바꾸면 되니까요! (용량 절약)
4. Dockerfile: 레시피 작성법
Dockerfile은 이미지를 만드는 레시피(설계도)입니다. 텍스트 파일로, 어떤 순서로 필름을 쌓을지 적어놓습니다.
기본 명령어
FROM: 기초 바닥 깔기
FROM ubuntu:20.04
- 의미: 우분투 20.04 운영체제를 기초 레이어로 사용
- 비유: 도시락 통 선택 (플라스틱통? 스테인리스?)
RUN: 명령어 실행 (설치, 설정)
RUN apt-get update && apt-get install -y python3
- 의미: 리눅스 명령어 실행 (여기선 파이썬 설치)
- 비유: 밥을 짓고, 반찬을 만드는 과정
COPY: 파일 넣기
COPY . /app
- 의미: 내 컴퓨터의 파일들을 컨테이너의
/app폴더로 복사 - 비유: 조리한 음식을 도시락에 담기
CMD: 실행 명령어 (컨테이너가 시작될 때)
CMD ["python3", "app.py"]
- 의미: 컨테이너가 실행되면
python3 app.py명령어를 실행 - 비유: "도시락 뚜껑 열면 이렇게 먹으세요" 안내문
전체 예시
# 1. 우분투 깔기
FROM ubuntu:20.04
# 2. 파이썬 설치
RUN apt-get update && apt-get install -y python3
# 3. 내 코드 복사
COPY . /app
# 4. 작업 디렉토리 설정
WORKDIR /app
# 5. 실행 명령어
CMD ["python3", "app.py"]
5. Docker Compose: 여러 컨테이너 한번에 관리하기
문제점: 웹 서버 + 데이터베이스 + 캐시 서버... 여러 컨테이너를 하나씩 실행하기 귀찮아요!
Docker Compose는 오케스트라 지휘자처럼 여러 컨테이너를 한 번에 관리합니다.
비유: 오케스트라
- 각 악기 (컨테이너): 바이올린(웹서버), 피아노(DB), 드럼(캐시)
- 지휘자 (Docker Compose): "다 같이 시작!" 한 번에 지휘
docker-compose.yml 예시
version: '3'
services:
web:
image: nginx
ports:
- "80:80"
database:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: secret
cache:
image: redis
실행 방법
# 모든 컨테이너 한 번에 시작
docker-compose up
# 모든 컨테이너 한 번에 종료
docker-compose down
6. 볼륨(Volume): 데이터를 안전하게 보관하기
문제점: 컨테이너를 삭제하면, 안에 있던 데이터도 같이 사라집니다!
볼륨은 데이터를 컨테이너 밖에 저장하는 USB처럼 동작합니다.
비유: USB 메모리
- 컨테이너 없이: 노트북 내장 저장소에 저장 → 노트북 포맷하면 데이터 사라짐
- 볼륨 사용: USB에 저장 → 노트북 포맷해도 USB는 안전함
사용 방법
# 볼륨 생성
docker volume create my_data
# 컨테이너 실행 시 볼륨 연결
docker run -v my_data:/data my_image
활용 사례
- 데이터베이스: MySQL 데이터 파일 보관
- 로그 파일: 애플리케이션 로그 영구 저장
- 설정 파일: 환경 설정 파일 공유
7. Docker 네트워크: 컨테이너끼리 대화하기
기본적으로 각 컨테이너는 격리되어 있습니다. 하지만 웹 서버가 데이터베이스에 접속하려면 연결이 필요합니다.
비유: 아파트 단지
- 각 집 (컨테이너): 독립적인 공간
- 복도/엘리베이터 (네트워크): 집과 집을 연결
네트워크 생성 및 연결
# 네트워크 생성
docker network create my_network
# 컨테이너를 네트워크에 연결
docker run --network my_network --name web nginx
docker run --network my_network --name db mysql
결과: web 컨테이너에서 db라는 이름으로 데이터베이스에 접속 가능!
Docker Compose에서 자동 네트워크
version: '3'
services:
web:
image: nginx
db:
image: mysql
# → 자동으로 같은 네트워크에 연결됨!
8. Nginx: Docker에서 가장 많이 쓰는 컨테이너
위 시뮬레이션의 Compose 탭에서 보듯이, 실무에서는 Nginx 컨테이너가 맨 앞에서 모든 요청을 받아 처리합니다. 왜 굳이 Nginx를 앞에 두는 걸까요?
비유: 건물 안내 데스크
- Nginx: 건물 1층 안내 데스크. 방문자가 오면 목적지를 확인하고 안내합니다.
- 프론트엔드/백엔드: 건물 내부의 각 사무실. 직접 찾아가면 복잡하고 보안에 취약합니다.
- 안내 데스크가 신분증 확인(SSL), 출입 제한(접근 제어), 방문 안내(라우팅)를 모두 처리합니다.
8-1. 리버스 프록시 (Reverse Proxy)
외부 요청을 받아서 내부 컨테이너로 전달하는 역할입니다.
# nginx.conf 예시
server {
listen 80;
# "/" 요청 → 프론트엔드 컨테이너로 전달
location / {
proxy_pass http://frontend:3000;
}
# "/api" 요청 → 백엔드 컨테이너로 전달
location /api {
proxy_pass http://backend:8080;
}
}
요청 흐름:
- 외부 사용자 →
https://mysite.com/api/users요청 - Nginx가
/api경로를 확인 → 백엔드 컨테이너로 전달 - 백엔드가 처리 → Nginx가 응답을 사용자에게 반환
왜 직접 연결하지 않나요? 프론트엔드(3000번 포트)와 백엔드(8080번 포트)를 외부에 직접 노출하면 보안 위험이 커집니다. Nginx 하나만 80/443 포트로 노출하고, 내부 서비스는 Docker 네트워크 안에서만 통신합니다.
8-2. SSL/TLS 종료 (HTTPS 처리)
HTTPS 암호화/복호화를 Nginx가 대신 처리합니다. 내부 컨테이너들은 HTTP로만 통신하면 되어 간단합니다.
server {
listen 443 ssl;
ssl_certificate /etc/ssl/cert.pem;
ssl_certificate_key /etc/ssl/key.pem;
location / {
proxy_pass http://frontend:3000; # 내부는 HTTP
}
}
| 구간 | 프로토콜 | 설명 |
|---|---|---|
| 외부 → Nginx | HTTPS (암호화) | 인터넷 구간, 도청 방지 |
| Nginx → 내부 컨테이너 | HTTP (평문) | Docker 네트워크 내부, 안전한 구간 |
비유: 건물 입구에서 보안 검색(SSL)을 통과하면, 건물 내부에서는 자유롭게 이동할 수 있는 것과 같습니다. 각 사무실마다 보안 검색을 반복할 필요가 없습니다.
8-3. 접근 제어 (Access Control)
Nginx에서 특정 IP만 허용하거나, 요청 속도를 제한할 수 있습니다.
# IP 기반 접근 제어
location /admin {
allow 192.168.1.0/24; # 내부 네트워크만 허용
deny all; # 나머지 차단
proxy_pass http://backend:8080;
}
# 요청 속도 제한 (Rate Limiting)
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api {
limit_req zone=api burst=20; # 초당 10회, 순간 20회까지 허용
proxy_pass http://backend:8080;
}
| 기능 | 설정 | 용도 |
|---|---|---|
| IP 허용/차단 | allow / deny |
관리자 페이지를 내부망에서만 접근 |
| Rate Limiting | limit_req_zone |
API 남용/DDoS 방어 |
| CORS 헤더 | add_header |
프론트엔드 도메인에서만 API 호출 허용 |
| 파일 크기 제한 | client_max_body_size |
업로드 용량 초과 차단 |
8-4. Docker Compose에서 Nginx 설정
실제 프로젝트에서는 이렇게 구성합니다:
version: '3'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./ssl:/etc/ssl
depends_on:
- frontend
- backend
frontend:
build: ./frontend
# 포트를 외부에 노출하지 않음 (Nginx만 접근)
backend:
build: ./backend
# 포트를 외부에 노출하지 않음 (Nginx만 접근)
핵심 포인트:frontend와backend에는ports를 설정하지 않습니다. Docker 네트워크 내부에서 Nginx만 접근하고, 외부에서는 Nginx의 80/443 포트만 열려 있습니다. 이것이 리버스 프록시의 보안 이점입니다.
9. 자주 쓰는 명령어 정리
| 명령어 | 설명 | 비유 |
|---|---|---|
docker build -t my_image . |
Dockerfile로 이미지 만들기 | 레시피대로 도시락 제작 |
docker run my_image |
이미지로 컨테이너 실행 | 도시락 뚜껑 열기 |
docker ps |
실행 중인 컨테이너 목록 | 오픈된 도시락 확인 |
docker stop <ID> |
컨테이너 중지 | 도시락 뚜껑 닫기 |
docker rm <ID> |
컨테이너 삭제 | 도시락 버리기 |
docker images |
이미지 목록 보기 | 레시피북 펼치기 |
docker rmi <이미지> |
이미지 삭제 | 레시피 찢어버리기 |
docker exec -it <ID> bash |
실행 중인 컨테이너 내부 접속 | 도시락 안 들여다보기 |
docker logs <ID> |
컨테이너 로그 확인 | 도시락에서 나는 냄새 맡기 |
docker pull <이미지> |
Docker Hub에서 이미지 다운로드 | 레시피 사이트에서 다운 |
docker-compose up |
여러 컨테이너 한 번에 시작 | 오케스트라 시작 |
docker-compose down |
여러 컨테이너 한 번에 종료 | 오케스트라 종료 |
유용한 옵션
-d: 백그라운드 실행 (화면에 안 띄우기)-p 8080:80: 포트 연결 (내 컴퓨터 8080 → 컨테이너 80)-v /my/folder:/app: 볼륨 연결 (폴더 공유)--name my_container: 컨테이너 이름 지정--rm: 종료 시 자동 삭제 (일회용)
정리
| 개념 | 핵심 | 비유 |
|---|---|---|
| Dockerfile | 이미지 만드는 레시피 | 요리 레시피 |
| Docker Compose | 여러 컨테이너 관리 | 오케스트라 지휘자 |
| Volume | 데이터 영구 보관 | USB 메모리 |
| Network | 컨테이너 간 통신 | 아파트 복도 |
| Nginx | 리버스 프록시, SSL, 접근 제어 | 건물 안내 데스크 |
핵심 워크플로우:
Dockerfile작성 (레시피)docker build(이미지 제작)docker-compose.yml작성 (여러 서비스 설정)docker-compose up(한 번에 실행!)Volume으로 데이터 보호,Network로 연결!