한눈에 보기 — 이미 아는 개념으로 Docker 읽기
Docker의 세 개념은 객체지향 프로그래밍의 소스 코드 → 클래스 → 인스턴스 구조와 정확히 대응된다.
[ OOP ] [ Docker ]
소스 코드 (Class 정의) Dockerfile
↓ 컴파일/빌드 ↓ docker build
Class (또는 실행파일) Docker Image
↓ new ↓ docker run
Object (인스턴스) Docker Container
| OOP | Docker | 특징 |
|---|---|---|
| 소스 코드 (Class 정의) | Dockerfile | 텍스트 파일. 무엇을 담을지 정의만 함 |
| 컴파일/빌드 | docker build | 정의를 실행 가능한 형태로 굳힘 |
| Class / 실행파일 | Docker Image | 불변(immutable). 실행 전 정적 산출물 |
| new | docker run | 이미지에서 실제 프로세스를 띄움 |
| Object (인스턴스) | Docker Container | 살아있는 프로세스. 상태를 가짐 |
클래스 하나로 여러 인스턴스를 만들 수 있듯, 이미지 하나로 컨테이너를 여러 개 띄울 수 있다. 이미지 자체는 변하지 않는다.
Docker가 필요한 이유 — “내 컴퓨터에서는 됐는데”
가장 흔한 개발 장애물이다. 로컬에서는 잘 돌아가는데 서버에서는 실패한다. 원인은 대부분 환경 차이다 — Python 버전, 라이브러리 버전, 환경 변수, OS 설정.
Docker는 앱과 그 앱이 실행되기 위한 환경 전체를 하나의 이미지에 묶는다. 이미지를 가져다 실행하면, 어떤 머신에서 돌리든 동일한 환경이 보장된다.
# Docker 없이
개발자 PC: Python 3.11, requests 2.28 → 잘 됨
서버: Python 3.8, requests 2.20 → 오류
# Docker 있으면
개발자 PC: 이미지 실행 → 잘 됨
서버: 동일 이미지 실행 → 동일하게 잘 됨
Dockerfile — 소스 코드
Dockerfile은 이미지를 어떻게 만들지를 서술하는 텍스트 파일이다. 클래스 정의처럼, 실행하기 전까지는 아무것도 하지 않는다.
# Dockerfile 예시 — Go 백엔드 서버
# 1. 베이스 이미지: 어떤 환경 위에서 시작할지
FROM golang:1.22-alpine AS builder
# 2. 작업 디렉토리 설정
WORKDIR /app
# 3. 의존성 먼저 복사 (캐시 최적화)
COPY go.mod go.sum ./
RUN go mod download
# 4. 소스코드 복사 후 빌드
COPY . .
RUN go build -o server ./cmd/server
# 5. 실행 이미지 (빌드 결과물만 가져감 — 경량화)
FROM alpine:3.19
WORKDIR /app
COPY --from=builder /app/server .
# 6. 컨테이너 시작 시 실행할 명령
CMD ["./server"]
각 줄(FROM, RUN, COPY)이 이미지의 레이어가 된다. 변경이 없는 레이어는 캐시되어 재빌드 속도가 빨라진다.
주요 명령어
| 명령어 | 역할 | 예 |
|---|---|---|
FROM | 베이스 이미지 지정 (출발점) | FROM python:3.11-slim |
WORKDIR | 작업 디렉토리 설정 | WORKDIR /app |
COPY | 호스트 파일을 이미지로 복사 | COPY . . |
RUN | 빌드 시점에 명령 실행 (레이어 생성) | RUN pip install -r requirements.txt |
ENV | 환경 변수 설정 | ENV PORT=8080 |
EXPOSE | 컨테이너가 사용할 포트 명시 (문서화 목적) | EXPOSE 8080 |
CMD | 컨테이너 시작 시 실행할 기본 명령 | CMD ["python", "app.py"] |
ENTRYPOINT | CMD와 유사. 덮어쓰기 어렵게 고정할 때 | ENTRYPOINT ["./server"] |
docker build — 컴파일
Dockerfile을 읽어 이미지를 만드는 단계다. 소스코드를 컴파일하는 것과 같다 — 결과물은 실행 가능한 정적 산출물(이미지)이다.
# 기본 빌드
docker build -t my-app:1.0.0 .
# ↑ 이미지 이름:태그 ↑ Dockerfile 위치 (현재 디렉토리)
# 결과 확인
docker images
# REPOSITORY TAG IMAGE ID SIZE
# my-app 1.0.0 a1b2c3d4e5f6 28MB
빌드된 이미지는 불변(immutable)이다. 클래스 파일처럼, 이미지 자체는 실행해도 변하지 않는다. 컨테이너를 아무리 많이 띄우고 수정해도 원본 이미지는 그대로다.
Docker Image — 클래스
이미지는 컨테이너의 설계도다. OS, 런타임, 라이브러리, 앱 코드, 설정이 모두 담긴 레이어 스택이다.
# 이미지 레이어 구조
Layer 4: COPY . . ← 앱 소스코드
Layer 3: RUN go mod download ← 의존성
Layer 2: WORKDIR /app ← 디렉토리 설정
Layer 1: FROM golang:1.22 ← 베이스 OS + Go 런타임
# 변경된 레이어 이후만 재빌드 → 캐시 효율
이미지는 Docker Hub, GCR(Google Container Registry), ECR(AWS) 같은 레지스트리에 올려두고 어디서든 당겨서 쓸 수 있다. npm install로 패키지를 가져오듯, docker pull로 이미지를 가져온다.
docker run — new, 컨테이너 생성
이미지에서 실제 프로세스를 띄우는 단계다. new ClassName()으로 인스턴스를 만드는 것과 같다.
# 기본 실행
docker run my-app:1.0.0
# 포트 바인딩 (호스트:컨테이너)
docker run -p 8080:8080 my-app:1.0.0
# 백그라운드 실행 + 이름 지정
docker run -d --name api-server -p 8080:8080 my-app:1.0.0
# 환경 변수 주입
docker run -d -e DB_HOST=postgres -e DB_PORT=5432 -p 8080:8080 my-app:1.0.0
# 같은 이미지로 컨테이너 여러 개 (클래스 → 여러 인스턴스)
docker run -d -p 8081:8080 --name api-1 my-app:1.0.0
docker run -d -p 8082:8080 --name api-2 my-app:1.0.0
docker run -d -p 8083:8080 --name api-3 my-app:1.0.0
Docker Container — 인스턴스
컨테이너는 이미지에서 실행된 프로세스다. 자체 파일시스템, 네트워크, 프로세스 공간을 가지며, 호스트 OS와 격리된다.
# 실행 중인 컨테이너 목록
docker ps
# CONTAINER ID IMAGE STATUS PORTS NAMES
# a1b2c3d4 my-app:1.0.0 Up 2min 0.0.0.0:8080->8080/tcp api-server
# 컨테이너 내부 접속 (디버깅)
docker exec -it api-server sh
# 로그 확인
docker logs -f api-server
# 중지 / 삭제
docker stop api-server
docker rm api-server
컨테이너는 기본적으로 임시(ephemeral)다. 컨테이너가 삭제되면 내부에서 쓴 파일도 사라진다. 데이터를 유지하려면 볼륨(Volume)을 마운트한다.
# 볼륨 마운트 — 호스트의 ./data가 컨테이너 /app/data와 연결됨
docker run -d -v $(pwd)/data:/app/data my-app:1.0.0
docker-compose — 여러 컨테이너를 한 번에
실제 서비스는 앱 서버 하나만으로 돌아가지 않는다. DB, 캐시, 메시지 큐가 함께 필요하다. docker-compose는 여러 컨테이너를 하나의 파일로 정의하고 한 번에 띄운다.
# docker-compose.yml
version: '3.9'
services:
api:
build: . # 현재 디렉토리 Dockerfile로 빌드
ports:
- "8080:8080"
environment:
- DB_HOST=postgres
- REDIS_HOST=redis
depends_on:
- postgres
- redis
postgres:
image: postgres:16-alpine # 기존 이미지 사용
environment:
POSTGRES_DB: mydb
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
volumes:
- pg_data:/var/lib/postgresql/data # 데이터 영구 보존
redis:
image: redis:7-alpine
volumes:
pg_data:
# 전체 스택 시작
docker compose up -d
# 전체 종료
docker compose down
# 로그 통합 확인
docker compose logs -f
핵심 개념 정리
| 개념 | 한 줄 설명 | OOP 대응 |
|---|---|---|
| Dockerfile | 이미지 빌드 명세서 (텍스트) | 소스 코드 / Class 정의 |
| Image | 불변 실행 가능 패키지 (레이어 스택) | 컴파일된 Class / .class 파일 |
| Container | 이미지에서 실행된 살아있는 프로세스 | Object (인스턴스) |
| Registry | 이미지 저장소 (Docker Hub, GCR 등) | 패키지 저장소 (npm, Maven) |
| Volume | 컨테이너 밖에 데이터를 영구 저장 | 외부 데이터베이스 / 파일시스템 |
| Network | 컨테이너 간 통신 채널 | 객체 간 메서드 호출 경로 |
| docker-compose | 여러 컨테이너를 한 파일로 정의·실행 | 여러 클래스를 묶은 애플리케이션 조립 |
자주 쓰는 명령어 한눈에 보기
# ── 이미지 ──────────────────────────────────────
docker build -t name:tag . # Dockerfile로 이미지 빌드
docker images # 이미지 목록
docker pull nginx:alpine # 레지스트리에서 이미지 가져오기
docker push name:tag # 레지스트리에 이미지 올리기
docker rmi name:tag # 이미지 삭제
# ── 컨테이너 ─────────────────────────────────────
docker run -d -p 8080:8080 name:tag # 이미지로 컨테이너 실행
docker ps # 실행 중인 컨테이너
docker ps -a # 전체 컨테이너 (중지 포함)
docker stop # 컨테이너 중지
docker rm # 컨테이너 삭제
docker logs -f # 로그 스트리밍
docker exec -it sh # 컨테이너 내부 접속
# ── compose ──────────────────────────────────────
docker compose up -d # 전체 스택 백그라운드 시작
docker compose down # 전체 종료 (컨테이너·네트워크 삭제)
docker compose down -v # 볼륨까지 삭제
docker compose logs -f # 전체 로그
docker compose ps # 서비스 상태
답글 남기기