모든 배포 가이드 배포

Docker와 리버스 프록시에서의 SSL 인증서

Docker 컨테이너는 일반적으로 SSL을 직접 처리하지 않습니다 — 앞단의 리버스 프록시가 TLS를 종료하고 복호화된 트래픽을 컨테이너로 전달합니다. 이 가이드에서는 가장 일반적인 세 가지 방법을 다룹니다.

방법 1: Nginx 리버스 프록시 + 수동 인증서

소규모 배포에 가장 간단한 방법입니다. GetHTTPS에서 인증서를 발급받아 Nginx 컨테이너에 마운트합니다.

docker-compose.yml

services:
  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./certs/fullchain.pem:/etc/ssl/fullchain.pem:ro
      - ./certs/privkey.pem:/etc/ssl/privkey.pem:ro
    depends_on:
      - app

  app:
    image: your-app:latest
    expose:
      - "3000"

nginx.conf

server {
    listen 443 ssl http2;
    server_name yourdomain.com;

    ssl_certificate     /etc/ssl/fullchain.pem;
    ssl_certificate_key /etc/ssl/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://app:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name yourdomain.com;
    return 301 https://$host$request_uri;
}

갱신 방법: ./certs/의 파일을 GetHTTPS에서 받은 새 파일로 교체한 후 docker compose exec nginx nginx -s reload를 실행합니다.

방법 2: Traefik + 자동 Let’s Encrypt

Traefik은 컨테이너 전용으로 설계된 리버스 프록시입니다. Let’s Encrypt 인증서를 자동으로 발급하고 갱신할 수 있습니다.

docker-compose.yml

services:
  traefik:
    image: traefik:v3.0
    command:
      - "--providers.docker=true"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.email=you@example.com"
      - "--certificatesresolvers.letsencrypt.acme.storage=/acme/acme.json"
      - "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - traefik-acme:/acme

  app:
    image: your-app:latest
    labels:
      - "traefik.http.routers.app.rule=Host(`yourdomain.com`)"
      - "traefik.http.routers.app.tls.certresolver=letsencrypt"
      - "traefik.http.services.app.loadbalancer.server.port=3000"

volumes:
  traefik-acme:

Traefik이 인증서 발급, 갱신, HTTPS 종료를 모두 처리합니다. 수동 인증서 관리가 필요 없습니다.

방법 3: Caddy (제로 설정 HTTPS)

Caddy는 설정 없이 Let’s Encrypt 인증서를 자동으로 발급하고 갱신합니다.

docker-compose.yml

services:
  caddy:
    image: caddy:2
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data
    depends_on:
      - app

  app:
    image: your-app:latest
    expose:
      - "3000"

volumes:
  caddy-data:

Caddyfile

yourdomain.com {
    reverse_proxy app:3000
}

설정은 이것이 전부입니다. Caddy가 Let’s Encrypt 인증서를 발급받고 자동으로 갱신합니다.

어떤 방법을 선택해야 하나요?

Nginx + 수동 인증서TraefikCaddy
설정중간 (설정 + 인증서 파일)중간 (라벨)최소
자동 갱신❌ (수동)
유연성높음높음중간
학습 곡선낮음 (익숙함)중간낮음
적합한 용도소규모 배포, 완전한 제어동적 컨테이너, 마이크로서비스간단한 사이트, 최소 설정

Docker에서 인증서 갱신

방법 1 (Nginx + 수동):

# 로컬 ./certs/ 디렉터리의 인증서 파일 교체
cp new-fullchain.pem ./certs/fullchain.pem
cp new-privkey.pem ./certs/privkey.pem

# 컨테이너 내부의 Nginx 리로드 (재시작 불필요)
docker compose exec nginx nginx -s reload

방법 2 (Traefik) 및 방법 3 (Caddy):

자동 처리 — 내부적으로 갱신을 관리합니다. 인증서는 Docker 볼륨(traefik-acme 또는 caddy-data)에 저장되며 컨테이너 재시작 시에도 유지됩니다.

Docker Swarm: 시크릿 사용

Docker Swarm 배포의 경우, 인증서 파일을 바인드 마운트하는 대신 Docker 시크릿을 사용합니다:

# 인증서 파일로 시크릿 생성
docker secret create ssl_cert fullchain.pem
docker secret create ssl_key privkey.pem
# In docker-compose.yml (deploy mode)
services:
  nginx:
    image: nginx:alpine
    secrets:
      - ssl_cert
      - ssl_key
    configs:
      - source: nginx_conf
        target: /etc/nginx/conf.d/default.conf

secrets:
  ssl_cert:
    external: true
  ssl_key:
    external: true

시크릿은 컨테이너 내부의 /run/secrets/ssl_cert/run/secrets/ssl_key에 마운트되며, 저장 시와 전송 시 모두 암호화됩니다.

흔한 실수

인증서를 Docker 이미지에 포함시키기

# ❌ 절대 이렇게 하지 마세요
COPY fullchain.pem /etc/ssl/fullchain.pem
COPY privkey.pem /etc/ssl/privkey.pem

인증서는 90일마다 만료됩니다. 이미지에 포함시키면 60일마다 이미지를 다시 빌드하고 재배포해야 합니다. 항상 볼륨이나 시크릿으로 인증서를 마운트하세요.

포트 80 노출을 잊는 경우

포트 80은 다음에 필요합니다:

  • HTTP → HTTPS 리다이렉트
  • Let’s Encrypt HTTP-01 챌린지 (Traefik/Caddy 자동 갱신용)
ports:
  - "80:80"    # 이것을 잊지 마세요
  - "443:443"

ACME 데이터를 영속화하지 않는 경우

Traefik과 Caddy는 Let’s Encrypt 인증서와 계정 키를 볼륨에 저장합니다. 영속화 없이는 컨테이너 재시작마다 인증서를 다시 요청하게 되어 레이트 리밋에 걸립니다:

volumes:
  traefik-acme:    # 반드시 네임드 볼륨이어야 합니다. 익명 볼륨은 안 됩니다
  caddy-data:

자주 묻는 질문

앱 컨테이너에서 직접 SSL을 처리할 수 있나요?

가능하지만 권장하지 않습니다. 리버스 프록시 패턴(엣지에서 TLS를 종료하고 내부적으로 평문 HTTP를 전달)이 표준인 이유는: 앱이 인증서를 알 필요가 없고, 갱신 시 앱 재시작이 불필요하며, TLS 설정을 중앙화할 수 있기 때문입니다.

여러 컨테이너에 대한 SSL은 어떻게 처리하나요?

Traefik이나 Caddy를 사용하면 각 서비스에 라벨/설정을 추가하기만 하면 자동으로 별도의 인증서를 받습니다. Nginx를 사용하는 경우 설정에 여러 server 블록을 추가합니다.

GetHTTPS와 Traefik의 내장 ACME 중 어떤 것을 사용해야 하나요?

프로덕션에서는 Traefik/Caddy의 내장 ACME를 사용하세요 — 갱신을 자동으로 처리합니다. GetHTTPS는 Nginx 리버스 프록시 방식(방법 1)이나 ACME 챌린지를 위한 직접 인터넷 접근이 없는 환경에서 인증서가 필요할 때 사용하세요.

Docker에서 인증서를 안전하게 마운트하려면?

읽기 전용으로 마운트하고(:ro), Swarm 모드에서는 개인키에 Docker 시크릿을 사용하며, 리버스 프록시 컨테이너만 인증서 파일에 접근할 수 있도록 합니다. 인증서를 Docker 이미지에 절대 포함시키지 마세요 — 만료되면 이미지를 다시 빌드해야 합니다.

GetHTTPS 인증서를 Kubernetes에서 사용할 수 있나요?

네. GetHTTPS 인증서 파일로 Kubernetes TLS 시크릿을 생성합니다:

kubectl create secret tls my-tls-cert --cert=fullchain.pem --key=privkey.pem

그런 다음 Ingress 리소스에서 참조합니다. Kubernetes에서 자동 갱신을 위해서는 Let’s Encrypt ClusterIssuer가 포함된 cert-manager를 고려하세요.

관련 기사

배포 2026-05-08
Nginx에 SSL 인증서를 설치하는 방법
Nginx에 SSL 인증서를 설치하는 단계별 가이드. 파일 업로드, 전체 서버 블록 설정, TLS 모범 사례, HTTP/2, HSTS, 리다이렉트 설정, 테스트, 6가지 일반 오류 문제 해결을 다룹니다.
시작하기 2026-05-08
무료 SSL 인증서 받는 방법 (단계별 가이드)
5분 만에 Let's Encrypt에서 무료 SSL 인증서를 발급받으세요 - 소프트웨어 설치 불필요, 계정 생성 불필요. 4가지 방법, 두 가지 챌린지 유형, 6개 플랫폼 설치, 문제 해결을 포함하는 완전 가이드입니다.
비교 2026-05-07
브라우저 기반 vs CLI ACME 클라이언트
브라우저 기반 ACME 클라이언트(GetHTTPS)와 CLI 도구(Certbot, acme.sh)를 비교합니다. 개인정보 보호, 자동화, 사용 사례에서의 장단점을 이해하세요.
브라우저에서 무료 SSL 인증서 받기
설치 불필요, 계정 불필요. 개인키는 항상 기기에 남습니다.
인증서 발급