Post

Docker

Docker

목차

Part 4: Docker

  1. Docker 기본 개념
  2. Docker 이미지
  3. Dockerfile 작성
  4. Docker 컨테이너 관리
  5. Docker 네트워킹
  6. Docker 볼륨과 스토리지
  7. Docker Compose
  8. Docker 레지스트리

1. Docker 기본 개념

1.1 Docker란?

Docker의 정의

Docker는 컨테이너 기반 애플리케이션 개발, 배포, 실행 플랫폼이다.

Docker의 주요 가치

  • Build once, run anywhere: 한 번 빌드하면 어디서나 실행
  • Isolation: 애플리케이션 간 격리
  • Portability: 환경 독립적 실행
  • Lightweight: VM보다 가벼운 리소스 사용
  • Fast: 빠른 시작과 배포

Docker의 구성 요소

Docker Client

  • 사용자 인터페이스
  • docker 명령어
  • Docker daemon과 REST API로 통신

Docker Daemon (dockerd)

  • 백그라운드 서비스
  • 이미지, 컨테이너, 네트워크, 볼륨 관리
  • containerd와 통신

Docker Registry

  • 이미지 저장소
  • Docker Hub (공개 레지스트리)
  • 프라이빗 레지스트리

Docker Objects

  • Images: 읽기 전용 템플릿
  • Containers: 이미지의 실행 인스턴스
  • Networks: 컨테이너 간 통신
  • Volumes: 영구 데이터 저장

1.2 Docker 설치

Linux (Ubuntu)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 오래된 버전 제거
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

# Repository 추가
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

# 설치 확인
sudo docker run hello-world

일반 사용자 권한 부여

1
2
3
4
5
6
7
8
9
# docker 그룹에 사용자 추가
sudo usermod -aG docker $USER

# 로그아웃 후 재로그인
# 또는 현재 세션에 적용
newgrp docker

# 권한 확인
docker run hello-world

Docker 버전 확인

1
2
3
4
5
# 클라이언트 및 서버 버전
docker version

# 상세 정보
docker info

1.3 Docker 기본 명령어

이미지 관련

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 이미지 검색
docker search nginx

# 이미지 다운로드
docker pull nginx
docker pull nginx:1.25

# 이미지 목록
docker images
docker image ls

# 이미지 삭제
docker rmi nginx
docker image rm nginx

# 사용하지 않는 이미지 정리
docker image prune

컨테이너 관련

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 컨테이너 실행
docker run nginx
docker run -d nginx  # 백그라운드
docker run -it ubuntu bash  # 대화형

# 실행 중인 컨테이너 목록
docker ps
docker container ls

# 모든 컨테이너 (중지된 것 포함)
docker ps -a
docker container ls -a

# 컨테이너 중지
docker stop <container>

# 컨테이너 시작
docker start <container>

# 컨테이너 재시작
docker restart <container>

# 컨테이너 삭제
docker rm <container>

# 강제 삭제 (실행 중이어도)
docker rm -f <container>

시스템 관리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 디스크 사용량
docker system df

# 전체 정리 (사용하지 않는 모든 리소스)
docker system prune

# 모든 리소스 강제 정리 (볼륨 포함)
docker system prune -a --volumes

# 로그 확인
docker logs <container>
docker logs -f <container>  # 실시간

# 리소스 사용량 모니터링
docker stats
docker stats <container>

2. Docker 이미지

2.1 이미지의 구조

레이어 기반 아키텍처

Docker 이미지는 여러 읽기 전용 레이어로 구성된다:

1
2
3
4
5
6
7
8
9
10
11
Image: nginx:latest
    ↓
Layer 5: nginx 설정 파일
    ↓
Layer 4: nginx 설치
    ↓
Layer 3: 패키지 업데이트
    ↓
Layer 2: 베이스 파일시스템
    ↓
Layer 1: 커널 부트 파일시스템

이미지 레이어 확인

1
2
3
4
5
# 이미지 히스토리
docker history nginx:latest

# 각 레이어의 크기와 생성 명령 확인
docker history --no-trunc nginx:latest

이미지 상세 정보

1
2
3
4
5
6
7
8
9
# 이미지 inspect
docker inspect nginx:latest

# JSON 형식으로 모든 메타데이터 출력
# - 레이어 정보
# - 환경 변수
# - 포트 설정
# - 볼륨 마운트 포인트
# - 엔트리포인트/CMD

2.2 이미지 태그

태그의 개념

태그는 이미지의 버전을 식별하는 레이블이다.

태그 형식

1
2
3
4
5
6
7
[registry/][namespace/]repository[:tag]

예시:
nginx:latest
nginx:1.25
docker.io/library/nginx:1.25-alpine
myregistry.com/myapp:v1.0.0

태그 규칙

1
2
3
4
5
6
7
8
9
10
11
12
# 태그 없이 pull하면 latest
docker pull nginx
# = docker pull nginx:latest

# 특정 버전
docker pull nginx:1.25

# Alpine 기반 (경량)
docker pull nginx:alpine

# 특정 OS/아키텍처
docker pull nginx:1.25-alpine-arm64

이미지 태깅

1
2
3
4
5
6
# 로컬 이미지에 태그 추가
docker tag nginx:latest myregistry.com/nginx:v1.0

# 여러 태그 가능
docker tag nginx:latest nginx:production
docker tag nginx:latest nginx:stable

2.3 이미지 레지스트리

Docker Hub

공식 공개 레지스트리:

1
2
3
4
5
6
7
8
# Docker Hub에서 pull (기본)
docker pull nginx

# 명시적 지정
docker pull docker.io/library/nginx

# 사용자 이미지
docker pull username/myimage

프라이빗 레지스트리

1
2
3
4
5
6
7
8
9
# 로그인
docker login myregistry.com
# Username, Password 입력

# 프라이빗 레지스트리에서 pull
docker pull myregistry.com/myapp:latest

# 로그아웃
docker logout myregistry.com

Harbor, GitLab Registry 등

엔터프라이즈 레지스트리:

  • 접근 제어
  • 이미지 스캐닝
  • 복제 및 백업
  • 취약점 관리

2.4 이미지 빌드

docker build 기본

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 현재 디렉토리의 Dockerfile 사용
docker build .

# 태그 지정
docker build -t myapp:latest .

# 특정 Dockerfile 지정
docker build -f Dockerfile.prod -t myapp:prod .

# 빌드 컨텍스트 제외 (.dockerignore)
# .dockerignore 파일 작성:
# node_modules
# .git
# *.log

빌드 캐시

Docker는 레이어를 캐시하여 빌드 속도를 향상시킨다:

1
2
3
4
5
# 캐시 무시하고 빌드
docker build --no-cache -t myapp:latest .

# 특정 레이어부터 다시 빌드
# (Dockerfile 수정 시 자동)

멀티 스테이지 빌드

빌드 단계를 분리하여 최종 이미지 크기 축소:

1
2
3
4
5
6
7
8
9
10
11
12
# 빌드 스테이지
FROM golang:1.21 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# 실행 스테이지
FROM alpine:latest
COPY --from=builder /app/myapp /usr/local/bin/
CMD ["myapp"]

# 최종 이미지는 alpine만 포함 (작음)

BuildKit

차세대 빌드 엔진:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# BuildKit 활성화
export DOCKER_BUILDKIT=1

# 또는 /etc/docker/daemon.json
{
  "features": {
    "buildkit": true
  }
}

# 장점:
# - 병렬 빌드
# - 빌드 캐시 개선
# - 비밀 정보 안전 처리
# - 더 나은 성능

2.5 이미지 공유

Docker Hub에 푸시

1
2
3
4
5
6
7
8
9
10
11
12
# Docker Hub 로그인
docker login

# 이미지 태그 (username 포함)
docker tag myapp:latest username/myapp:latest

# 푸시
docker push username/myapp:latest

# 특정 버전도 푸시
docker tag myapp:latest username/myapp:v1.0
docker push username/myapp:v1.0

프라이빗 레지스트리에 푸시

1
2
3
4
5
6
7
8
# 레지스트리 로그인
docker login myregistry.com

# 이미지 태그
docker tag myapp:latest myregistry.com/myapp:latest

# 푸시
docker push myregistry.com/myapp:latest

이미지 저장/로드

1
2
3
4
5
6
7
8
9
10
11
12
# 이미지를 tar 파일로 저장
docker save -o myapp.tar myapp:latest

# tar 파일에서 이미지 로드
docker load -i myapp.tar

# 여러 이미지 저장
docker save -o images.tar nginx:latest alpine:latest

# 압축
docker save myapp:latest | gzip > myapp.tar.gz
gunzip -c myapp.tar.gz | docker load

이미지 익스포트/임포트 (컨테이너)

1
2
3
4
5
6
7
8
9
10
11
12
# 실행 중인 컨테이너를 이미지로
docker commit <container> myapp:snapshot

# 컨테이너를 tar로 익스포트
docker export <container> -o container.tar

# tar에서 이미지 생성
docker import container.tar myapp:imported

# 차이점:
# - save/load: 이미지 레이어 보존
# - export/import: 단일 레이어로 압축

3. Dockerfile 작성

3.1 Dockerfile 기본 구조

Dockerfile이란?

Dockerfile은 Docker 이미지를 빌드하는 명령어 스크립트이다.

기본 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 베이스 이미지
FROM node:18-alpine

# 작업 디렉토리 설정
WORKDIR /app

# 의존성 파일 복사
COPY package*.json ./

# 의존성 설치
RUN npm install --production

# 애플리케이션 코드 복사
COPY . .

# 포트 노출
EXPOSE 3000

# 실행 명령
CMD ["node", "server.js"]

3.2 주요 Dockerfile 명령어

FROM - 베이스 이미지

1
2
3
4
5
6
7
8
9
10
11
# 공식 이미지
FROM ubuntu:22.04

# 경량 이미지
FROM alpine:3.18

# 멀티 스테이지의 특정 스테이지
FROM node:18 AS builder

# Scratch (빈 이미지)
FROM scratch

WORKDIR - 작업 디렉토리

1
2
3
4
5
6
7
8
9
10
# 작업 디렉토리 설정
WORKDIR /app

# 이후 명령은 모두 /app에서 실행
# 디렉토리가 없으면 자동 생성

# 중첩 가능
WORKDIR /app
WORKDIR src
# 현재 위치: /app/src

COPY vs ADD

1
2
3
4
5
6
7
8
9
10
11
12
13
# COPY: 단순 파일/디렉토리 복사
COPY package.json /app/
COPY src/ /app/src/

# ADD: 복사 + 추가 기능
ADD https://example.com/file.tar.gz /app/
# URL에서 다운로드

ADD archive.tar.gz /app/
# 자동 압축 해제

# 권장: 일반적으로 COPY 사용
# ADD는 특별한 경우에만

RUN - 명령 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Shell 형식
RUN apt-get update && apt-get install -y nginx

# Exec 형식
RUN ["apt-get", "update"]

# 여러 명령을 하나로 (레이어 최소화)
RUN apt-get update && \
    apt-get install -y \
      nginx \
      curl && \
    rm -rf /var/lib/apt/lists/*

# 각 RUN은 새 레이어 생성
# 최적화: 관련 명령을 하나의 RUN으로

ENV - 환경 변수

1
2
3
4
5
6
7
8
9
10
# 환경 변수 설정
ENV NODE_ENV=production
ENV PORT=3000

# 여러 변수 한번에
ENV NODE_ENV=production \
    PORT=3000 \
    LOG_LEVEL=info

# 빌드 시 및 런타임에 모두 사용

ARG - 빌드 인수

1
2
3
4
5
6
7
8
9
10
# 빌드 시에만 사용하는 변수
ARG VERSION=latest
ARG BUILD_DATE

# 사용
FROM node:${VERSION}
LABEL build-date=${BUILD_DATE}

# 빌드 시 전달
# docker build --build-arg VERSION=18 .

EXPOSE - 포트 노출

1
2
3
4
5
6
7
# 포트 노출 (문서화 목적)
EXPOSE 80
EXPOSE 443/tcp
EXPOSE 53/udp

# 실제 포트 매핑은 실행 시:
# docker run -p 8080:80 myapp

CMD vs ENTRYPOINT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# CMD: 기본 명령 (덮어쓰기 가능)
CMD ["nginx", "-g", "daemon off;"]

# 실행 시 덮어쓰기:
# docker run myapp echo "hello"

# ENTRYPOINT: 항상 실행되는 명령
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

# 실행 시:
# docker run myapp -c /custom/nginx.conf
# = nginx -c /custom/nginx.conf

# 조합:
# ENTRYPOINT: 실행 파일
# CMD: 기본 인수

VOLUME - 볼륨 마운트 포인트

1
2
3
4
5
# 볼륨 마운트 포인트 선언
VOLUME /data
VOLUME ["/var/log", "/var/db"]

# 컨테이너 실행 시 익명 볼륨 자동 생성

USER - 실행 사용자

1
2
3
4
5
6
7
8
9
# 사용자 생성
RUN addgroup -S appgroup && \
    adduser -S appuser -G appgroup

# 사용자 전환
USER appuser

# 이후 명령은 appuser로 실행
# 보안 모범 사례: root 사용 지양

LABEL - 메타데이터

1
2
3
4
5
6
7
8
9
# 메타데이터 추가
LABEL version="1.0"
LABEL description="My Application"
LABEL maintainer="devops@example.com"

# 여러 레이블
LABEL version="1.0" \
      description="My Application" \
      maintainer="devops@example.com"

3.3 Dockerfile 최적화

레이어 최소화

1
2
3
4
5
6
7
8
9
10
11
12
13
# 나쁜 예: 레이어가 많음
RUN apt-get update
RUN apt-get install -y nginx
RUN apt-get install -y curl
RUN apt-get clean

# 좋은 예: 하나의 레이어
RUN apt-get update && \
    apt-get install -y \
      nginx \
      curl && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

캐시 활용

1
2
3
4
5
6
7
8
9
10
# 나쁜 예: 코드 변경 시 의존성도 재설치
COPY . /app
RUN npm install

# 좋은 예: 의존성 파일만 먼저 복사
COPY package*.json /app/
RUN npm install
COPY . /app

# package.json 변경 시에만 npm install 재실행

멀티 스테이지 빌드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 빌드 스테이지
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build

# 프로덕션 스테이지
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
EXPOSE 3000
CMD ["node", "dist/server.js"]

# 최종 이미지: 빌드 도구 제외, 크기 감소

불필요한 파일 제외 (.dockerignore)

1
2
3
4
5
6
7
8
9
10
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
.DS_Store
*.md
tests/
docs/

베이스 이미지 선택

1
2
3
4
5
6
7
8
9
10
11
12
# 크기 비교:
# node:18 (약 1GB)
FROM node:18

# node:18-slim (약 200MB)
FROM node:18-slim

# node:18-alpine (약 170MB)
FROM node:18-alpine

# Distroless (최소, 보안 강화)
FROM gcr.io/distroless/nodejs18

3.4 보안 모범 사례

최소 권한 원칙

1
2
3
4
5
6
7
8
# 나쁜 예: root로 실행
FROM nginx

# 좋은 예: 일반 사용자
FROM nginx
RUN addgroup -S appgroup && \
    adduser -S appuser -G appgroup
USER appuser

민감 정보 제외

1
2
3
4
5
6
7
# 나쁜 예: 비밀키 포함
ENV API_KEY=secret123

# 좋은 예: 런타임에 주입
# docker run -e API_KEY=secret123 myapp

# 또는 Docker Secrets 사용

최신 패치

1
2
3
4
5
6
7
# 베이스 이미지 업데이트
FROM alpine:3.18

# 시스템 패키지 업데이트
RUN apk update && \
    apk upgrade && \
    rm -rf /var/cache/apk/*

취약점 스캔

1
2
3
4
5
# Trivy로 이미지 스캔
trivy image myapp:latest

# Docker Scout (Docker Desktop)
docker scout cves myapp:latest

4. Docker 컨테이너 관리

4.1 컨테이너 생명주기

컨테이너 상태

  • Created: 생성됨, 아직 시작 안 함
  • Running: 실행 중
  • Paused: 일시 정지
  • Stopped: 중지됨
  • Deleted: 삭제됨

생명주기 명령

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 생성 (시작 안 함)
docker create nginx

# 생성 + 시작
docker run nginx

# 시작
docker start <container>

# 중지 (SIGTERM, 10초 후 SIGKILL)
docker stop <container>

# 강제 중지 (SIGKILL)
docker kill <container>

# 재시작
docker restart <container>

# 일시 정지
docker pause <container>

# 재개
docker unpause <container>

# 삭제
docker rm <container>

# 강제 삭제
docker rm -f <container>

4.2 docker run 옵션

기본 실행

1
2
3
4
5
6
7
8
9
10
11
# 포그라운드 실행
docker run nginx

# 백그라운드 실행 (-d, --detach)
docker run -d nginx

# 컨테이너 이름 지정
docker run --name mynginx nginx

# 실행 후 자동 삭제 (--rm)
docker run --rm nginx

대화형 모드

1
2
3
4
5
6
7
8
9
10
11
# 대화형 모드 (-it)
docker run -it ubuntu bash

# -i: STDIN 유지
# -t: TTY 할당

# 실행 중인 컨테이너에 접속
docker exec -it mynginx bash

# 특정 사용자로 실행
docker exec -it -u root mynginx bash

포트 매핑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 단일 포트
docker run -p 8080:80 nginx

# 여러 포트
docker run -p 8080:80 -p 4443:443 nginx

# 호스트 IP 지정
docker run -p 127.0.0.1:8080:80 nginx

# UDP 포트
docker run -p 53:53/udp dns-server

# 호스트 포트 자동 할당
docker run -p 80 nginx

환경 변수

1
2
3
4
5
6
7
8
9
10
11
12
13
# 단일 환경 변수
docker run -e NODE_ENV=production myapp

# 여러 환경 변수
docker run -e NODE_ENV=production -e PORT=3000 myapp

# 파일에서 읽기
docker run --env-file .env myapp

# .env 파일 형식:
# NODE_ENV=production
# PORT=3000
# DB_HOST=db.example.com

볼륨 마운트

1
2
3
4
5
6
7
8
9
10
11
# Named 볼륨
docker run -v myvolume:/data nginx

# Bind mount
docker run -v /host/path:/container/path nginx

# 읽기 전용
docker run -v myvolume:/data:ro nginx

# tmpfs (메모리)
docker run --tmpfs /tmp nginx

네트워크

1
2
3
4
5
6
7
8
9
10
11
# 특정 네트워크
docker run --network mynetwork nginx

# 호스트 네트워크
docker run --network host nginx

# 네트워크 없음
docker run --network none nginx

# 네트워크 별칭
docker run --network mynetwork --network-alias db postgres

리소스 제한

1
2
3
4
5
6
7
8
9
10
11
12
# 메모리 제한
docker run -m 512m nginx
docker run --memory=512m nginx

# CPU 제한
docker run --cpus=1.5 nginx

# CPU 공유
docker run --cpu-shares=512 nginx

# 블록 I/O
docker run --device-write-bps=/dev/sda:10mb nginx

재시작 정책

1
2
3
4
5
6
7
8
9
10
11
# 항상 재시작
docker run --restart=always nginx

# 실패 시에만 재시작
docker run --restart=on-failure nginx

# 최대 재시작 횟수
docker run --restart=on-failure:3 nginx

# 명시적으로 중지하지 않으면 재시작
docker run --restart=unless-stopped nginx

4.3 컨테이너 모니터링

로그 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 로그 출력
docker logs <container>

# 실시간 로그 (tail -f)
docker logs -f <container>

# 마지막 N줄
docker logs --tail 100 <container>

# 타임스탬프 포함
docker logs -t <container>

# 특정 시간 이후
docker logs --since 2023-01-01T00:00:00 <container>
docker logs --since 1h <container>

리소스 사용량

1
2
3
4
5
6
7
8
9
10
11
# 실시간 통계
docker stats

# 특정 컨테이너
docker stats <container>

# 스트리밍 없이 한 번만
docker stats --no-stream

# 출력:
# CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT     MEM %     NET I/O

프로세스 확인

1
2
3
4
5
6
7
8
# 컨테이너 내 프로세스
docker top <container>

# ps aux 형식
docker top <container> aux

# 실행 중인 프로세스 확인
docker exec <container> ps aux

파일 시스템 변경사항

1
2
3
4
5
6
7
# 이미지 대비 변경된 파일
docker diff <container>

# 출력:
# A: 추가된 파일
# C: 변경된 파일
# D: 삭제된 파일

이벤트 모니터링

1
2
3
4
5
6
7
8
9
# 실시간 Docker 이벤트
docker events

# 특정 컨테이너
docker events --filter container=<container>

# 특정 이벤트 타입
docker events --filter event=start
docker events --filter event=stop

4.4 컨테이너 디버깅

실행 중인 컨테이너 접속

1
2
3
4
5
6
7
8
9
# Bash 실행
docker exec -it <container> bash

# sh 실행 (Alpine 등)
docker exec -it <container> sh

# 특정 명령 실행
docker exec <container> cat /etc/hosts
docker exec <container> env

컨테이너 inspect

1
2
3
4
5
6
7
# 전체 정보 (JSON)
docker inspect <container>

# 특정 필드 추출
docker inspect --format='' <container>
docker inspect --format='' <container>
docker inspect --format='' <container> | jq

파일 복사

1
2
3
4
5
6
7
8
# 컨테이너 → 호스트
docker cp <container>:/path/to/file ./local/path

# 호스트 → 컨테이너
docker cp ./local/file <container>:/path/to/

# 디렉토리 복사
docker cp <container>:/app ./app-backup

네트워크 디버깅

1
2
3
4
5
6
7
8
9
10
11
# 네트워크 정보 확인
docker exec <container> ip addr
docker exec <container> ip route

# 연결 테스트
docker exec <container> ping google.com
docker exec <container> curl http://api.example.com

# DNS 확인
docker exec <container> nslookup google.com
docker exec <container> cat /etc/resolv.conf

헬스체크

1
2
3
# Dockerfile에서 헬스체크 정의
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
  CMD curl -f http://localhost/ || exit 1
1
2
3
4
5
6
7
8
9
# 실행 시 헬스체크 추가
docker run --health-cmd='curl -f http://localhost/ || exit 1' \
           --health-interval=30s \
           --health-timeout=3s \
           --health-retries=3 \
           nginx

# 헬스 상태 확인
docker inspect --format='' <container>

4.5 컨테이너 정리

중지된 컨테이너 삭제

1
2
3
4
5
6
7
8
# 모든 중지된 컨테이너 삭제
docker container prune

# 확인 없이 삭제
docker container prune -f

# 특정 레이블 필터
docker container prune --filter "label=env=test"

자동 삭제 (–rm)

1
2
3
4
5
# 종료 시 자동 삭제
docker run --rm nginx

# 임시 작업에 유용
docker run --rm -it ubuntu bash

대량 정리

1
2
3
4
5
# 실행 중이지 않은 모든 컨테이너 삭제
docker ps -aq -f status=exited | xargs docker rm

# 특정 이미지의 모든 컨테이너 삭제
docker ps -a --filter ancestor=nginx -q | xargs docker rm -f

5. Docker 네트워킹

5.1 Docker 네트워크 드라이버 상세

Bridge 네트워크 (기본)

Docker의 기본 네트워크 모드이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 기본 브리지 네트워크
docker network inspect bridge

# 특징:
# - 네트워크: 172.17.0.0/16
# - 게이트웨이: 172.17.0.1 (docker0 브리지)
# - 컨테이너 간 IP로만 통신 (이름 해석 안 됨)

# 사용자 정의 브리지 생성
docker network create --driver bridge mybridge

# 서브넷 지정
docker network create --driver bridge \
  --subnet=192.168.100.0/24 \
  --gateway=192.168.100.1 \
  mybridge

# 장점:
# - 자동 DNS 해석 (컨테이너 이름으로 통신)
# - 네트워크 격리
# - 동적 연결/해제 가능

사용자 정의 브리지 vs 기본 브리지

1
2
3
4
5
6
7
8
9
10
# 기본 브리지
docker run -d --name web1 nginx
docker run -d --name web2 nginx
docker exec web1 ping web2  # 실패! (IP로만 가능)

# 사용자 정의 브리지
docker network create mynet
docker run -d --name web1 --network mynet nginx
docker run -d --name web2 --network mynet nginx
docker exec web1 ping web2  # 성공! (DNS 해석됨)

Host 네트워크

컨테이너가 호스트의 네트워크 스택을 직접 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
# Host 네트워크로 실행
docker run --network host nginx

# 특징:
# - 최고 성능 (네트워크 오버헤드 없음)
# - 포트 매핑 불필요 (컨테이너 포트 = 호스트 포트)
# - 네트워크 격리 없음
# - 호스트의 모든 네트워크 인터페이스 접근

# 주의:
# - 여러 컨테이너가 같은 포트 사용 불가
# - 보안 위험 (호스트 네트워크 노출)
# - Linux에서만 완전 지원 (Mac/Windows는 제한적)

None 네트워크

네트워크를 완전히 비활성화한다.

1
2
3
4
5
6
7
8
9
10
11
12
# None 네트워크로 실행
docker run --network none alpine

# 특징:
# - lo (루프백)만 존재
# - 외부 통신 완전 차단
# - 최대 격리

# 용도:
# - 네트워크가 필요 없는 배치 작업
# - 보안이 매우 중요한 작업
# - 커스텀 네트워크 설정

Overlay 네트워크

여러 Docker 호스트에 걸친 네트워크를 구성한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Docker Swarm 초기화 필요
docker swarm init

# Overlay 네트워크 생성
docker network create \
  --driver overlay \
  --subnet 10.0.9.0/24 \
  myoverlay

# 서비스 배포
docker service create \
  --name web \
  --network myoverlay \
  --replicas 3 \
  nginx

# 특징:
# - 멀티 호스트 통신
# - 자동 서비스 디스커버리
# - 내장 로드 밸런싱
# - 암호화 가능 (--opt encrypted)

# Kubernetes에서는 CNI 플러그인 사용

Macvlan 네트워크

컨테이너에 고유한 MAC 주소를 할당한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Macvlan 네트워크 생성
docker network create -d macvlan \
  --subnet=192.168.1.0/24 \
  --gateway=192.168.1.1 \
  -o parent=eth0 \
  mymacvlan

# 컨테이너 실행
docker run --network mymacvlan \
  --ip=192.168.1.100 \
  nginx

# 특징:
# - 컨테이너가 물리 네트워크에 직접 연결된 것처럼 보임
# - 각 컨테이너가 고유 MAC 주소 가짐
# - 라우터/스위치가 컨테이너를 별도 호스트로 인식

# 용도:
# - 레거시 애플리케이션 (MAC 주소 기반 라이센싱)
# - 네트워크 모니터링 도구
# - DHCP 서버

# 주의:
# - 프로미스큐어스 모드 필요할 수 있음
# - 클라우드 환경에서는 지원 안 될 수 있음

5.2 네트워크 관리

네트워크 목록 및 정보

1
2
3
4
5
6
7
8
9
10
11
# 네트워크 목록
docker network ls

# 네트워크 상세 정보
docker network inspect bridge

# 특정 컨테이너의 네트워크 정보
docker inspect <container> --format '' | jq

# 네트워크에 연결된 컨테이너 목록
docker network inspect mynet --format ' '

네트워크 생성 옵션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 기본 생성
docker network create mynet

# 드라이버 지정
docker network create --driver bridge mynet

# 서브넷 및 게이트웨이
docker network create \
  --subnet 172.20.0.0/16 \
  --gateway 172.20.0.1 \
  mynet

# IP 범위 지정
docker network create \
  --subnet 172.20.0.0/16 \
  --ip-range 172.20.240.0/20 \
  mynet

# IPv6 활성화
docker network create \
  --ipv6 \
  --subnet 2001:db8::/64 \
  mynet

# Internal 네트워크 (외부 통신 차단)
docker network create --internal mynet

# 레이블
docker network create \
  --label environment=production \
  mynet

컨테이너 네트워크 연결

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 생성 시 네트워크 지정
docker run --network mynet nginx

# 실행 중인 컨테이너에 네트워크 추가
docker network connect mynet <container>

# IP 주소 지정
docker network connect --ip 172.20.0.10 mynet <container>

# 네트워크 별칭
docker network connect --alias db mynet postgres

# 네트워크 연결 해제
docker network disconnect mynet <container>

# 강제 연결 해제
docker network disconnect -f mynet <container>

네트워크 삭제

1
2
3
4
5
6
7
8
# 네트워크 삭제
docker network rm mynet

# 사용하지 않는 네트워크 정리
docker network prune

# 확인 없이 정리
docker network prune -f

5.3 포트 매핑 상세

포트 매핑 방식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 기본 매핑 (모든 인터페이스)
docker run -p 8080:80 nginx
# 0.0.0.0:8080 → 컨테이너:80

# 특정 IP에만 바인딩
docker run -p 127.0.0.1:8080:80 nginx
# localhost에서만 접근 가능

# 특정 IP 범위
docker run -p 192.168.1.10:8080:80 nginx

# UDP 포트
docker run -p 53:53/udp dns-server

# TCP와 UDP 모두
docker run -p 8080:80/tcp -p 53:53/udp myapp

# 포트 범위
docker run -p 8000-8010:8000-8010 myapp

# 호스트 포트 자동 할당
docker run -P nginx
# Dockerfile의 EXPOSE 포트를 랜덤 포트로 매핑

포트 확인

1
2
3
4
5
6
7
8
# 컨테이너의 포트 매핑 확인
docker port <container>

# 특정 포트 확인
docker port <container> 80

# 모든 매핑 정보
docker inspect <container> --format '' | jq

포트 매핑과 iptables

1
2
3
4
5
6
7
8
# Docker가 생성한 iptables 규칙 확인
sudo iptables -t nat -L -n

# DOCKER 체인 확인
sudo iptables -t nat -L DOCKER -n

# 예시 규칙:
# DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:172.17.0.2:80

5.4 컨테이너 간 통신

같은 네트워크 내 통신

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 네트워크 생성
docker network create myapp-net

# 데이터베이스 컨테이너
docker run -d \
  --name db \
  --network myapp-net \
  -e POSTGRES_PASSWORD=secret \
  postgres

# 애플리케이션 컨테이너
docker run -d \
  --name web \
  --network myapp-net \
  -e DATABASE_URL=postgresql://postgres:secret@db:5432/mydb \
  myapp

# web에서 db로 접근
docker exec web ping db  # 성공!
docker exec web psql -h db -U postgres  # 성공!

다중 네트워크

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 프론트엔드 네트워크
docker network create frontend

# 백엔드 네트워크
docker network create backend

# 웹 서버 (프론트엔드만)
docker run -d --name web --network frontend nginx

# API 서버 (양쪽 모두)
docker run -d --name api --network frontend myapi
docker network connect backend api

# 데이터베이스 (백엔드만)
docker run -d --name db --network backend postgres

# 결과:
# web → api (가능)
# api → db (가능)
# web → db (불가능, 격리됨)

네트워크 별칭

1
2
3
4
5
6
7
8
9
10
11
12
# 여러 별칭으로 참조
docker run -d \
  --network mynet \
  --network-alias db \
  --network-alias database \
  --network-alias postgres \
  postgres

# 모든 별칭으로 접근 가능
docker exec web ping db
docker exec web ping database
docker exec web ping postgres

서비스 디스커버리

1
2
3
4
5
6
7
8
# 같은 서비스의 여러 인스턴스
docker run -d --name web1 --network mynet --network-alias web nginx
docker run -d --name web2 --network mynet --network-alias web nginx
docker run -d --name web3 --network mynet --network-alias web nginx

# 라운드 로빈 DNS
# "web"을 조회하면 세 인스턴스의 IP가 번갈아 반환됨
docker exec client nslookup web

5.5 Docker DNS

내장 DNS 서버

Docker는 127.0.0.11에 내장 DNS 서버를 제공한다.

1
2
3
4
5
6
7
8
9
10
# 컨테이너 내 DNS 설정 확인
docker exec web cat /etc/resolv.conf
# nameserver 127.0.0.11
# options ndots:0

# Docker DNS 서버가 다음을 해석:
# 1. 컨테이너 이름 → IP
# 2. 네트워크 별칭 → IP
# 3. 서비스 이름 → 여러 IP (라운드 로빈)
# 4. 외부 도메인 → 호스트 DNS로 전달

커스텀 DNS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 컨테이너별 DNS 서버 지정
docker run --dns 8.8.8.8 nginx

# 여러 DNS 서버
docker run --dns 8.8.8.8 --dns 8.8.4.4 nginx

# DNS 검색 도메인
docker run --dns-search example.com nginx
# ping server → server.example.com 시도

# DNS 옵션
docker run --dns-option ndots:2 nginx

# /etc/hosts 추가
docker run --add-host myhost:192.168.1.100 nginx

전역 DNS 설정

1
2
3
4
5
6
7
8
9
# /etc/docker/daemon.json
{
  "dns": ["8.8.8.8", "8.8.4.4"],
  "dns-search": ["example.com"],
  "dns-opts": ["ndots:2"]
}

# Docker 재시작
sudo systemctl restart docker

5.6 네트워크 트러블슈팅

연결 문제 진단

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 컨테이너 IP 확인
docker inspect <container> --format ''

# 네트워크 인터페이스 확인
docker exec <container> ip addr

# 라우팅 테이블
docker exec <container> ip route

# 연결 테스트
docker exec <container> ping google.com
docker exec <container> ping 8.8.8.8

# 포트 열림 확인
docker exec <container> nc -zv db 5432

# DNS 확인
docker exec <container> nslookup google.com
docker exec <container> dig google.com

네트워크 디버깅 컨테이너

1
2
3
4
5
6
7
8
9
10
# nicolaka/netshoot 사용 (추천)
docker run -it --rm --network container:<container> nicolaka/netshoot

# 사용 가능한 도구:
# - tcpdump: 패킷 캡처
# - curl, wget: HTTP 테스트
# - nmap: 포트 스캔
# - iperf: 대역폭 테스트
# - traceroute: 경로 추적
# - mtr: 네트워크 진단

패킷 캡처

1
2
3
4
5
6
7
8
9
10
11
12
13
# 컨테이너 내부에서
docker exec <container> tcpdump -i eth0 -w /tmp/capture.pcap

# 호스트의 veth에서
# veth 인터페이스 찾기
docker exec <container> cat /sys/class/net/eth0/iflink
ip link | grep <번호>

# 캡처
sudo tcpdump -i <veth> -w capture.pcap

# Wireshark로 분석
wireshark capture.pcap

일반적인 문제와 해결

문제: 컨테이너가 외부와 통신 안 됨

1
2
3
4
5
6
7
8
9
10
11
12
# 1. IP 포워딩 확인
cat /proc/sys/net/ipv4/ip_forward
# 1이어야 함

# 2. NAT 규칙 확인
sudo iptables -t nat -L POSTROUTING -n

# 3. 방화벽 확인
sudo iptables -L -n

# 4. DNS 확인
docker exec <container> cat /etc/resolv.conf

문제: 컨테이너 간 통신 안 됨

1
2
3
4
5
6
7
8
9
# 1. 같은 네트워크인지 확인
docker network inspect <network>

# 2. ICC(Inter-Container Communication) 활성화 확인
docker network inspect <network> | grep "com.docker.network.bridge.enable_icc"
# true여야 함

# 3. 방화벽 규칙 확인
sudo iptables -L DOCKER-ISOLATION -n

문제: DNS 해석 실패

1
2
3
4
5
6
7
8
9
10
11
12
# 1. Docker DNS 작동 확인
docker exec <container> nslookup google.com 127.0.0.11

# 2. 호스트 DNS 확인
cat /etc/resolv.conf

# 3. 컨테이너 DNS 설정 확인
docker exec <container> cat /etc/resolv.conf

# 4. 네트워크 재생성
docker network rm <network>
docker network create <network>

6. Docker 볼륨과 스토리지

6.1 스토리지 옵션 비교

Docker는 데이터 영속성을 위해 세 가지 마운트 타입을 제공한다.

볼륨 (Volumes) - 권장

Docker가 관리하는 영구 저장소:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 볼륨 생성
docker volume create myvolume

# 볼륨 위치
ls /var/lib/docker/volumes/myvolume/_data

# 장점:
# - Docker가 완전히 관리
# - 백업/복원 용이
# - 플랫폼 독립적
# - 여러 컨테이너가 안전하게 공유 가능
# - 볼륨 드라이버로 원격 스토리지 지원

# 단점:
# - 호스트에서 직접 접근하기 어려움

Bind Mount

호스트 디렉토리를 직접 마운트:

1
2
3
4
5
6
7
8
9
10
11
12
13
# Bind mount
docker run -v /host/path:/container/path nginx

# 장점:
# - 호스트 파일시스템에 직접 접근
# - 개발 시 편리 (코드 변경 즉시 반영)
# - 호스트의 모든 경로 사용 가능

# 단점:
# - 호스트 경로 의존성
# - 플랫폼 종속적
# - 보안 위험 (호스트 파일시스템 노출)
# - Docker가 관리하지 않음

tmpfs Mount

메모리에만 존재하는 임시 저장소:

1
2
3
4
5
6
7
8
9
10
11
12
# tmpfs mount
docker run --tmpfs /app/cache nginx

# 장점:
# - 매우 빠름 (메모리 속도)
# - 민감한 정보 임시 저장 (컨테이너 종료 시 자동 삭제)
# - 디스크 I/O 없음

# 단점:
# - 메모리 사용
# - 영구적이지 않음
# - Linux에서만 사용 가능

비교표

특성VolumeBind Mounttmpfs
Docker 관리OXO
백업 용이성OXX
성능좋음좋음매우 좋음
플랫폼 독립OXX
영구성OOX
개발 편의성중간높음낮음

6.2 볼륨 관리

볼륨 생성 및 조회

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 볼륨 생성
docker volume create myvolume

# 드라이버 지정
docker volume create --driver local myvolume

# 옵션 지정
docker volume create \
  --opt type=nfs \
  --opt o=addr=192.168.1.100,rw \
  --opt device=:/path/to/dir \
  nfs-volume

# 레이블
docker volume create --label env=prod myvolume

# 볼륨 목록
docker volume ls

# 볼륨 상세 정보
docker volume inspect myvolume

# 볼륨 사용 중인 컨테이너 확인
docker ps --filter volume=myvolume

볼륨 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 명명된 볼륨 마운트
docker run -v myvolume:/data nginx

# 익명 볼륨 (자동 생성)
docker run -v /data nginx

# 읽기 전용
docker run -v myvolume:/data:ro nginx

# --mount 사용 (더 명시적)
docker run \
  --mount type=volume,source=myvolume,target=/data \
  nginx

# 읽기 전용 (--mount)
docker run \
  --mount type=volume,source=myvolume,target=/data,readonly \
  nginx

# 볼륨 드라이버 옵션
docker run \
  --mount type=volume,source=myvolume,target=/data,volume-driver=local \
  nginx

볼륨 삭제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 볼륨 삭제
docker volume rm myvolume

# 여러 볼륨 삭제
docker volume rm vol1 vol2 vol3

# 사용하지 않는 볼륨 정리
docker volume prune

# 확인 없이 정리
docker volume prune -f

# 특정 레이블 필터
docker volume prune --filter "label=env=test"

6.3 Bind Mount 상세

Bind Mount 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 기본 bind mount
docker run -v /host/path:/container/path nginx

# 절대 경로 필요
docker run -v $(pwd)/app:/app nginx

# 읽기 전용
docker run -v /host/path:/container/path:ro nginx

# --mount 사용 (권장)
docker run \
  --mount type=bind,source=/host/path,target=/container/path \
  nginx

# 읽기 전용 (--mount)
docker run \
  --mount type=bind,source=/host/path,target=/container/path,readonly \
  nginx

개발 환경에서 활용

1
2
3
4
5
6
7
8
9
10
# 소스 코드 동기화
docker run -d \
  -v $(pwd)/src:/app/src \
  -v $(pwd)/package.json:/app/package.json \
  -p 3000:3000 \
  node:18 \
  npm run dev

# 코드 변경 시 자동 반영 (nodemon, webpack-dev-server 등)
# 컨테이너 재시작 불필요

권한 문제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 문제: 컨테이너 내부 사용자와 호스트 사용자 UID 불일치
# 호스트: UID 1000
# 컨테이너: UID 33 (www-data)

# 해결 1: 컨테이너 사용자를 호스트 UID와 맞춤
docker run --user $(id -u):$(id -g) myapp

# 해결 2: Dockerfile에서 사용자 생성
RUN addgroup -g 1000 appgroup && \
    adduser -u 1000 -G appgroup -D appuser
USER appuser

# 해결 3: chown으로 소유권 변경 (엔트리포인트 스크립트)
chown -R appuser:appgroup /data

보안 주의사항

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 위험: 호스트 루트 마운트
docker run -v /:/host nginx  # 매우 위험!

# 위험: 민감한 디렉토리 마운트
docker run -v /etc:/host-etc nginx  # 위험!

# 위험: Docker 소켓 마운트
docker run -v /var/run/docker.sock:/var/run/docker.sock myapp
# Docker API 완전 접근 = 호스트 제어 가능

# 안전한 사용:
# - 필요한 최소 경로만 마운트
# - 가능하면 읽기 전용
# - 민감한 디렉토리 피하기

6.4 tmpfs Mount

tmpfs 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# tmpfs mount
docker run --tmpfs /tmp nginx

# 크기 제한
docker run --tmpfs /tmp:size=100M nginx

# --mount 사용
docker run \
  --mount type=tmpfs,target=/tmp,tmpfs-size=100M \
  nginx

# 여러 tmpfs
docker run \
  --tmpfs /tmp \
  --tmpfs /var/cache \
  nginx

사용 사례

1
2
3
4
5
6
7
8
9
10
11
# 1. 캐시 디렉토리
docker run --tmpfs /app/cache myapp

# 2. 임시 파일 처리
docker run --tmpfs /tmp myapp

# 3. 민감한 정보 (비밀번호, 토큰 등)
docker run --tmpfs /secrets myapp

# 4. 세션 데이터
docker run --tmpfs /var/lib/sessions myapp

6.5 볼륨 드라이버

로컬 드라이버 옵션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# NFS 볼륨
docker volume create \
  --driver local \
  --opt type=nfs \
  --opt o=addr=192.168.1.100,rw \
  --opt device=:/path/to/dir \
  nfs-volume

# CIFS/SMB 볼륨
docker volume create \
  --driver local \
  --opt type=cifs \
  --opt o=username=user,password=pass,addr=192.168.1.100 \
  --opt device=//192.168.1.100/share \
  cifs-volume

# tmpfs 볼륨
docker volume create \
  --driver local \
  --opt type=tmpfs \
  --opt device=tmpfs \
  --opt o=size=100m \
  tmpfs-volume

서드파티 볼륨 드라이버

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# REX-Ray (클라우드 스토리지)
docker plugin install rexray/ebs

docker volume create \
  --driver rexray/ebs \
  --opt size=10 \
  ebs-volume

# Convoy (스냅샷 지원)
docker volume create \
  --driver convoy \
  convoy-volume

# Flocker (컨테이너 이동 시 데이터 함께 이동)
docker volume create \
  --driver flocker \
  flocker-volume

6.6 데이터 백업 및 복원

볼륨 백업

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 방법 1: tar로 압축
docker run --rm \
  -v myvolume:/data \
  -v $(pwd):/backup \
  alpine \
  tar czf /backup/myvolume-backup.tar.gz /data

# 방법 2: 컨테이너를 통한 백업
docker run --rm \
  -v myvolume:/source:ro \
  -v $(pwd):/backup \
  alpine \
  sh -c "cd /source && tar czf /backup/backup.tar.gz ."

# 방법 3: 직접 복사
sudo cp -r /var/lib/docker/volumes/myvolume/_data ./backup

볼륨 복원

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# tar에서 복원
docker run --rm \
  -v myvolume:/data \
  -v $(pwd):/backup \
  alpine \
  tar xzf /backup/myvolume-backup.tar.gz -C /

# 새 볼륨에 복원
docker volume create myvolume-restored

docker run --rm \
  -v myvolume-restored:/data \
  -v $(pwd):/backup \
  alpine \
  tar xzf /backup/backup.tar.gz -C /data

볼륨 복사

1
2
3
4
5
6
7
8
9
10
11
12
13
# 한 볼륨에서 다른 볼륨으로 복사
docker run --rm \
  -v source-volume:/source:ro \
  -v target-volume:/target \
  alpine \
  sh -c "cp -av /source/. /target/"

# 또는 tar 파이프라인
docker run --rm \
  -v source-volume:/source:ro \
  -v target-volume:/target \
  alpine \
  sh -c "cd /source && tar c . | tar x -C /target"

데이터베이스 백업

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# PostgreSQL 백업
docker exec postgres pg_dump -U postgres mydb > backup.sql

# 볼륨을 통한 백업
docker run --rm \
  -v postgres-data:/var/lib/postgresql/data \
  -v $(pwd):/backup \
  postgres \
  pg_dump -U postgres -f /backup/mydb.sql mydb

# MySQL 백업
docker exec mysql mysqldump -u root -p mydb > backup.sql

# MongoDB 백업
docker exec mongo mongodump --out /backup

7. Docker Compose

7.1 Docker Compose란?

Docker Compose의 목적

Docker Compose는 여러 컨테이너 애플리케이션을 정의하고 실행하는 도구이다.

주요 기능

  • 멀티 컨테이너 애플리케이션 정의 (YAML 파일)
  • 한 번의 명령으로 전체 스택 시작/중지
  • 환경별 설정 관리 (개발, 테스트, 프로덕션)
  • 서비스 스케일링
  • 의존성 관리

설치

1
2
3
4
5
6
7
8
9
# Docker Desktop은 Compose 포함

# Linux에서 별도 설치
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" \
  -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# 버전 확인
docker-compose --version

7.2 docker-compose.yml 기본

기본 구조

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
version: '3.8'

services:
  web:
    image: nginx:latest
    ports:
      - "8080:80"
    volumes:
      - ./html:/usr/share/nginx/html
    networks:
      - frontend

  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: secret
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend

networks:
  frontend:
  backend:

volumes:
  db-data:

버전

1
2
3
4
5
6
7
# Compose 파일 버전 (3.8 권장)
version: '3.8'

# 버전별 주요 차이:
# - 3.x: Docker Swarm 지원
# - 2.x: 로컬 개발 중심
# - 1.x: 레거시 (사용 안 함)

7.3 서비스 정의

이미지 지정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
services:
  # 이미지 사용
  web:
    image: nginx:latest

  # Dockerfile 빌드
  app:
    build: .

  # Dockerfile 경로 지정
  app:
    build:
      context: ./app
      dockerfile: Dockerfile.dev

  # 빌드 인수
  app:
    build:
      context: .
      args:
        - VERSION=1.0
        - BUILD_DATE=2024-01-01

포트 매핑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
services:
  web:
    ports:
      # 호스트:컨테이너
      - "8080:80"
      
      # IP 지정
      - "127.0.0.1:8080:80"
      
      # 여러 포트
      - "8080:80"
      - "4443:443"
      
      # UDP
      - "53:53/udp"
      
      # 포트 범위
      - "8000-8010:8000-8010"

환경 변수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
services:
  web:
    environment:
      # 직접 지정
      NODE_ENV: production
      PORT: 3000
      
      # 배열 형식
      - NODE_ENV=production
      - PORT=3000
      
    # 파일에서 로드
    env_file:
      - .env
      - .env.prod

  db:
    environment:
      # 호스트 환경 변수 참조
      DB_PASSWORD: ${DB_PASSWORD}

볼륨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
services:
  web:
    volumes:
      # Named volume
      - data:/app/data
      
      # Bind mount
      - ./app:/app
      - ./config:/etc/nginx
      
      # 읽기 전용
      - ./config:/etc/nginx:ro
      
      # tmpfs
      - type: tmpfs
        target: /tmp

volumes:
  data:
    driver: local

네트워크

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
services:
  web:
    networks:
      - frontend
      - backend
      
  db:
    networks:
      backend:
        # IP 주소 지정
        ipv4_address: 172.20.0.10

networks:
  frontend:
    driver: bridge
  backend:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

의존성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services:
  web:
    depends_on:
      - db
      - redis
    
  db:
    image: postgres

  redis:
    image: redis

# depends_on은 시작 순서만 제어
# 서비스가 준비될 때까지 기다리지 않음
# 준비 여부는 healthcheck나 wait-for-it 스크립트 사용

헬스체크

1
2
3
4
5
6
7
8
services:
  web:
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

재시작 정책

1
2
3
4
5
6
7
services:
  web:
    restart: always
    # no: 재시작 안 함 (기본값)
    # always: 항상 재시작
    # on-failure: 실패 시에만
    # unless-stopped: 명시적 중지 전까지

7.4 Docker Compose 명령어

기본 명령

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 서비스 시작 (빌드 포함)
docker-compose up

# 백그라운드 실행
docker-compose up -d

# 빌드만
docker-compose build

# 강제 재빌드
docker-compose up --build

# 특정 서비스만
docker-compose up web

# 스케일링
docker-compose up -d --scale web=3

서비스 관리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 서비스 중지
docker-compose stop

# 특정 서비스 중지
docker-compose stop web

# 서비스 시작 (이미 생성된 컨테이너)
docker-compose start

# 서비스 재시작
docker-compose restart

# 서비스 일시 정지
docker-compose pause

# 재개
docker-compose unpause

정리

1
2
3
4
5
6
7
8
9
10
11
# 컨테이너 중지 및 삭제
docker-compose down

# 볼륨도 삭제
docker-compose down -v

# 이미지도 삭제
docker-compose down --rmi all

# 고아 컨테이너 제거
docker-compose down --remove-orphans

로그 및 모니터링

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 로그 확인
docker-compose logs

# 실시간 로그
docker-compose logs -f

# 특정 서비스
docker-compose logs -f web

# 마지막 N줄
docker-compose logs --tail=100

# 타임스탬프 포함
docker-compose logs -t

# 실행 중인 서비스 확인
docker-compose ps

# 프로세스 확인
docker-compose top

명령 실행

1
2
3
4
5
6
7
8
# 서비스에서 명령 실행
docker-compose exec web bash

# 새 컨테이너에서 일회성 명령
docker-compose run web python manage.py migrate

# 컨테이너 생성 없이 실행
docker-compose run --rm web python script.py

설정 검증

1
2
3
4
5
6
7
8
# 설정 파일 검증
docker-compose config

# 환경 변수 치환 후 출력
docker-compose config

# 특정 파일 지정
docker-compose -f docker-compose.prod.yml config

7.5 실전 예시

웹 애플리케이션 스택

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
version: '3.8'

services:
  # Nginx 웹 서버
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/ssl:/etc/nginx/ssl:ro
      - static-files:/static
    depends_on:
      - web
    networks:
      - frontend

  # Django 웹 애플리케이션
  web:
    build: ./app
    command: gunicorn myapp.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - ./app:/app
      - static-files:/app/static
    environment:
      - DEBUG=False
      - DATABASE_URL=postgresql://postgres:secret@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis
    networks:
      - frontend
      - backend

  # PostgreSQL 데이터베이스
  db:
    image: postgres:15
    volumes:
      - postgres-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=mydb
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=secret
    networks:
      - backend

  # Redis 캐시
  redis:
    image: redis:alpine
    volumes:
      - redis-data:/data
    networks:
      - backend

  # Celery Worker
  celery:
    build: ./app
    command: celery -A myapp worker -l info
    volumes:
      - ./app:/app
    environment:
      - DATABASE_URL=postgresql://postgres:secret@db:5432/mydb
      - REDIS_URL=redis://redis:6379/0
    depends_on:
      - db
      - redis
    networks:
      - backend

networks:
  frontend:
  backend:

volumes:
  postgres-data:
  redis-data:
  static-files:

개발 환경 vs 프로덕션

1
2
3
4
5
6
7
8
9
10
11
# docker-compose.yml (베이스)
version: '3.8'

services:
  web:
    build: .
    environment:
      - DATABASE_URL=postgresql://db/mydb

  db:
    image: postgres:15
1
2
3
4
5
6
7
8
9
10
11
# docker-compose.override.yml (개발 - 자동 로드)
version: '3.8'

services:
  web:
    volumes:
      - ./app:/app
    environment:
      - DEBUG=True
    ports:
      - "8000:8000"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# docker-compose.prod.yml (프로덕션)
version: '3.8'

services:
  web:
    image: myregistry.com/myapp:latest
    environment:
      - DEBUG=False
    restart: always
    
  db:
    volumes:
      - db-data:/var/lib/postgresql/data
    restart: always

volumes:
  db-data:
1
2
3
4
5
# 개발
docker-compose up  # .yml + .override.yml 자동 병합

# 프로덕션
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d

8. Docker 레지스트리

8.1 Docker Hub

Docker Hub 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 로그인
docker login
# Username: 
# Password:

# 이미지 태그
docker tag myapp:latest username/myapp:latest
docker tag myapp:latest username/myapp:v1.0

# 푸시
docker push username/myapp:latest
docker push username/myapp:v1.0

# 풀
docker pull username/myapp:latest

# 로그아웃
docker logout

자동 빌드 (Automated Builds)

Docker Hub는 GitHub/Bitbucket과 연동하여 자동 빌드를 지원한다:

  1. Docker Hub에서 저장소 생성
  2. GitHub 저장소 연결
  3. 빌드 규칙 설정
  4. Git 푸시 시 자동 빌드

프라이빗 저장소

1
2
3
4
5
# 프라이빗 저장소 풀 (로그인 필요)
docker pull username/private-repo:latest

# 무료 플랜: 1개 프라이빗 저장소
# 유료 플랜: 무제한

8.2 프라이빗 레지스트리 구축

Registry 컨테이너 실행

1
2
3
4
5
6
7
8
9
10
11
12
13
# 기본 레지스트리
docker run -d \
  -p 5000:5000 \
  --name registry \
  -v registry-data:/var/lib/registry \
  registry:2

# 이미지 푸시
docker tag myapp:latest localhost:5000/myapp:latest
docker push localhost:5000/myapp:latest

# 이미지 풀
docker pull localhost:5000/myapp:latest

TLS/SSL 설정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 인증서 준비
mkdir -p certs
# certs/domain.crt, certs/domain.key

# TLS로 레지스트리 실행
docker run -d \
  -p 443:443 \
  --name registry \
  -v registry-data:/var/lib/registry \
  -v $(pwd)/certs:/certs \
  -e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
  registry:2

Basic 인증

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# htpasswd 파일 생성
mkdir auth
docker run --rm --entrypoint htpasswd \
  httpd:2 -Bbn username password > auth/htpasswd

# 인증 활성화
docker run -d \
  -p 5000:5000 \
  --name registry \
  -v registry-data:/var/lib/registry \
  -v $(pwd)/auth:/auth \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  registry:2

# 로그인
docker login localhost:5000

8.3 Harbor

Harbor란?

Harbor는 엔터프라이즈급 컨테이너 레지스트리이다.

주요 기능

  • 웹 UI
  • RBAC (역할 기반 접근 제어)
  • 이미지 스캐닝 (Trivy, Clair)
  • 이미지 서명
  • 복제 (다중 레지스트리)
  • Helm 차트 저장소
  • 감사 로그

설치 (Docker Compose)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Harbor 다운로드
wget https://github.com/goharbor/harbor/releases/download/v2.9.0/harbor-offline-installer-v2.9.0.tgz
tar xzvf harbor-offline-installer-v2.9.0.tgz
cd harbor

# 설정
cp harbor.yml.tmpl harbor.yml
vi harbor.yml
# hostname 수정
# 인증서 경로 설정 (선택)

# 설치
sudo ./install.sh

# 접속
# https://your-harbor-domain
# admin / Harbor12345 (기본)

Harbor 사용

1
2
3
4
5
6
7
8
9
10
11
12
# 로그인
docker login harbor.example.com

# 프로젝트 생성 (UI에서)
# library, myapp 등

# 이미지 푸시
docker tag myapp:latest harbor.example.com/library/myapp:latest
docker push harbor.example.com/library/myapp:latest

# 이미지 풀
docker pull harbor.example.com/library/myapp:latest

This post is licensed under CC BY 4.0 by the author.