들어가며
Docker 컨테이너 환경에서 애플리케이션을 개발하다 보면, 백엔드와 프론트엔드 서버를 분리해서 구성하게 된다. 이때 클라이언트가 API를 호출할 때마다 CORS 문제에 직면하게 되는데, 이를 해결하기 위해 프록시 서버를 도입한다.
오늘은 Docker 환경에서 가장 많이 사용되는 두 가지 프록시 솔루션인 Nginx와 Traefik을 비교해보고, 각각의 구성 방법을 살펴보고자 한다.
Nginx vs Traefik: 어떤 것을 선택할까?
Nginx의 특징
- 전통적이고 검증된 웹 서버로 시작했지만, 리버스 프록시로도 널리 사용된다
- 정적 파일 서빙 능력이 뛰어나다
- 설정 파일 기반으로 세밀한 제어가 가능하다
- 대용량 트래픽 처리에 강점이 있다
Traefik의 특징
- Docker와 Kubernetes 같은 컨테이너 오케스트레이션 환경을 위해 설계되었다
- 자동 서비스 디스커버리 기능이 탁월하다
- 설정을 레이블로 관리할 수 있어 동적으로 구성 변경이 가능하다
- Let's Encrypt 자동 SSL 인증서 발급 기능이 내장되어 있다
Nginx를 활용한 프록시 구성
먼저 전통적인 방식인 Nginx로 프록시를 구성해보기로 했다. Nginx는 고성능 웹 서버이자 리버스 프록시로, 정적 파일 서빙에 뛰어난 성능을 보이며 다양한 설정 옵션을 제공한다. 특히 upstream 설정을 통해 로드 밸런싱도 쉽게 구현할 수 있어서 실무에서 자주 사용된다.
Docker Compose 설정
우선 컨테이너들을 연결할 네트워크를 정의하고, 각 서비스들이 같은 네트워크에 속하도록 설정한다. Nginx 컨테이너는 외부에 포트를 노출하고, 설정 파일은 볼륨으로 마운트하여 관리하기로 했다.
version: '3.8'
services:
frontend:
build: ./frontend
container_name: my-app-frontend
networks:
- app-network
backend:
build: ./backend
container_name: my-app-backend
networks:
- app-network
nginx:
image: nginx:alpine
container_name: nginx-proxy
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/ssl/private
depends_on:
- frontend
- backend
networks:
- app-network
networks:
app-network:
driver: bridge
Nginx 설정 파일
실제 Nginx 설정에서는 upstream으로 백엔드/프론트엔드 서버를 정의하고, location 블록으로 요청 경로별 라우팅을 처리한다. CORS 문제를 해결하기 위해 필요한 헤더들도 추가하고, OPTIONS 요청도 따로 처리해야 한다. WebSocket 지원을 위한 헤더 설정도 빼먹지 말자.
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
upstream frontend {
server my-app-frontend:3000;
}
upstream backend {
server my-app-backend:8080;
}
server {
listen 80;
server_name localhost;
# 프론트엔드로 프록시
location / {
proxy_pass http://frontend;
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;
# WebSocket 지원
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 백엔드 API로 프록시
location /api {
proxy_pass http://backend;
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;
# CORS 헤더 추가
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
}
# OPTIONS 요청 처리
location /api {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204;
}
}
}
# HTTPS 설정 (선택사항)
server {
listen 443 ssl;
server_name localhost;
ssl_certificate /etc/ssl/private/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
location / {
proxy_pass http://frontend;
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;
}
location /api {
proxy_pass http://backend;
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;
}
}
}
Traefik을 활용한 프록시 구성
이번에는 좀 더 현대적인 접근 방식인 Traefik으로 동일한 구성을 해보기로 했다. Traefik의 가장 큰 장점은 자동 서비스 디스커버리인데, Docker 레이블을 통해 자동으로 라우팅 규칙을 생성하고 적용한다. 설정 파일을 수정하고 재시작할 필요 없이 새로운 컨테이너가 추가되면 자동으로 인식한다는 점이 매력적이다.
Docker Compose 설정
Traefik의 설정은 대부분 레이블로 처리된다. 각 서비스마다 라우팅 규칙, 포트, 미들웨어 등을 레이블로 지정하면 Traefik이 자동으로 인식하고 적용한다. priority 설정으로 라우팅 우선순위도 조정할 수 있어서 /api와 / 같은 겹치는 경로를 쉽게 처리할 수 있다.
version: '3.8'
services:
traefik:
image: traefik:v2.10
container_name: traefik
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
ports:
- "80:80"
- "443:443"
- "8080:8080" # Traefik 대시보드
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- app-network
frontend:
build: ./frontend
container_name: my-app-frontend
labels:
- "traefik.enable=true"
- "traefik.http.routers.frontend.rule=PathPrefix(`/`)"
- "traefik.http.routers.frontend.entrypoints=web"
- "traefik.http.routers.frontend.priority=1"
- "traefik.http.services.frontend.loadbalancer.server.port=3000"
networks:
- app-network
backend:
build: ./backend
container_name: my-app-backend
labels:
- "traefik.enable=true"
- "traefik.http.routers.backend.rule=PathPrefix(`/api`)"
- "traefik.http.routers.backend.entrypoints=web"
- "traefik.http.routers.backend.priority=2"
- "traefik.http.services.backend.loadbalancer.server.port=8080"
# 미들웨어로 CORS 처리
- "traefik.http.middlewares.cors.headers.accesscontrolalloworigin=*"
- "traefik.http.middlewares.cors.headers.accesscontrolallowheaders=*"
- "traefik.http.middlewares.cors.headers.accesscontrolallowmethods=*"
- "traefik.http.routers.backend.middlewares=cors"
networks:
- app-network
networks:
app-network:
driver: bridge
고급 Traefik 설정
Traefik의 진정한 힘은 정적 설정 파일을 통한 고급 기능들이다. HTTP에서 HTTPS로의 자동 리다이렉트, Let's Encrypt 자동 SSL 인증서 발급 등 운영 환경에서 필요한 기능들을 간단한 설정으로 구현할 수 있다.
# traefik.yml (볼륨으로 마운트)
global:
checkNewVersion: true
sendAnonymousUsage: false
api:
dashboard: true
insecure: true
entryPoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
websecure:
address: ":443"
providers:
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
watch: true
# Let's Encrypt 자동 SSL
certificatesResolvers:
letsencrypt:
acme:
email: your-email@example.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: web
두 방식의 비교
Nginx의 장단점
장점:
- 정적 파일 서빙 성능이 뛰어나다
- 복잡한 설정이 가능하다
- 커뮤니티 지원이 풍부하다
- 메모리 사용량이 적다
단점:
- 동적 설정 변경이 어렵다
- 컨테이너 재시작 시 설정을 수동으로 관리해야 한다
- 직접 SSL 인증서를 관리해야 한다
Traefik의 장단점
장점:
- 자동 서비스 디스커버리가 편리하다
- Let's Encrypt 자동 SSL이 내장되어 있다
- 컨테이너 환경에 최적화되어 있다
- 대시보드가 직관적이다
단점:
- Nginx에 비해 정적 파일 서빙 성능이 떨어진다
- 메모리 사용량이 상대적으로 많다
- 복잡한 설정에서는 제약이 있을 수 있다
실무 활용 팁
실제 프로젝트에서 프록시를 사용할 때 고려해야 할 중요한 요소들을 살펴보자. 환경별 설정 분리, 로깅, 성능 최적화는 안정적인 서비스 운영을 위해 필수적이다.
개발 환경과 운영 환경 분리
개발과 운영 환경에서는 다른 설정이 필요하다. 개발에서는 HTTPS가 필요 없지만, 운영에서는 SSL 인증서가 필수다. Docker Compose의 오버라이드 기능을 활용하면 환경별로 다른 설정을 쉽게 관리할 수 있다.
# docker-compose.dev.yml
version: '3.8'
services:
nginx:
volumes:
- ./nginx/dev.conf:/etc/nginx/nginx.conf
ports:
- "80:80"
# docker-compose.prod.yml
version: '3.8'
services:
nginx:
volumes:
- ./nginx/prod.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/ssl/private
ports:
- "80:80"
- "443:443"
로깅과 모니터링
프록시 서버의 로그는 트래픽 분석, 성능 모니터링, 문제 진단에 매우 중요하다. 접근 로그에 클라이언트 정보, 응답 시간, 상태 코드 등의 정보를 남기면 운영에 큰 도움이 된다.
# Nginx 로그 설정
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;
# Traefik 로그 설정
services:
traefik:
command:
- "--log.level=INFO"
- "--accesslog=true"
volumes:
- ./logs:/var/log/traefik
성능 최적화
트래픽이 늘어나면 프록시 서버의 성능 튜닝이 중요해진다. worker 프로세스 수, 연결 수 제한, 버퍼 크기 조정, 그리고 gzip 압축 설정 등으로 성능을 크게 개선할 수 있다.
# Nginx 성능 최적화
worker_processes auto;
worker_connections 1024;
# 버퍼 크기 조정
client_body_buffer_size 10K;
client_header_buffer_size 1k;
client_max_body_size 8m;
large_client_header_buffers 2 1k;
# 압축 설정
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
application/json
application/javascript
text/xml
application/xml
application/xml+rss
text/javascript;
결론
Nginx와 Traefik 모두 Docker 환경에서 훌륭한 프록시 솔루션이다. 전통적이고 안정적인 솔루션을 원한다면 Nginx를, 현대적이고 동적인 환경에서 편리함을 원한다면 Traefik을 선택하기로 하면 좋을 것 같다.
- 정적 콘텐츠 서빙이 많은 경우: Nginx
- 마이크로서비스 아키텍처: Traefik
- 자동 SSL 관리가 필요한 경우: Traefik
- 세밀한 설정이 필요한 경우: Nginx
어떤 선택을 하든, 중요한 것은 자신의 프로젝트 요구사항에 맞는 도구를 선택하고, 적절히 설정하여 안정적이고 효율적인 서비스를 제공하는 것이다.
'Study > Server' 카테고리의 다른 글
[AWS] aws ec2에서 Swap memory 활용하기 (1) | 2025.02.23 |
---|