도입 배경
현재 Spring boot 프로젝트에 WAS(내장 톰캣)을 웹 서버로 사용 중이다. WAS(내장 톰캣)가 웹 서버의 역할까지 할 수 있지만, WAS는 비즈니스 로직만을 처리하고, 정적 리소스를 보여주는 부분은 Nginx가 담당하게 하여 WAS의 부하를 줄일 수 있도록 변경하려고 한다.
또한 Spring Boot 서버가 정상 작동하지 않을 경우, Nginx를 통해 사용자에게 에러 페이지를 표시함으로써 사용자 경험을 개선할 수 있을 것이다.
또한, 현재 SSL/TLS 인증서가 Spring boot에 직접 의존하고 있다. Nginx에 SSL 인증서 관리를 맡김으로써 WAS의 책임을 줄이고, 보안을 강화하고자 한다.
1. Docker Nginx 설치
Nginx 이미지를 pull 해오고, 이미지가 잘 받아졌는지 확인한다.
# Nginx 이미지 다운로드
docker pull nginx
# Nginx 이미지 확인
docker images
2. SSL/TLS 인증서 발급
스프링 부트 내장 톰캣에 적용했던 인증서를 Nginx 에서 적용되도록 변경할 것이다. 인증서는 Let's encrypt 에서 certbot을 활용하여 발급받았다. 성공적으로 발급받았다면 /etc/letsencrypt/live/[도메인] 경로에 인증서 파일들이 받아진다.
sudo letsencrypt certonly -a standalone -d coinrun.kr
- cert.pem : 이 파일은 도메인에 대한 SSL 인증서를 포함한다. 즉, 도메인 (coinrun.kr)에 대해 발급된 인증서이다. 클라이언트와의 암호화된 연결을 설정할 때 사용된다.
- chain.pem : 이 파일은 인증서 체인(chain)을 포함한다. 인증서 체인은 SSL 인증서와 이를 검증하는 루트 CA(인증 기관) 사이의 중간 인증서들로 구성된다. 클라이언트가 서버의 인증서를 신뢰할 수 있도록 도와준다. 즉, 클라이언트가 서버의 인증서를 검증할 때 필요한 추가 인증서 정보가 들어 있다.
- fullchain.pem : 이 파일은 cert.pem과 chain.pem을 합친 것이다. 즉, 사용자의 도메인 인증서와 모든 중간 인증서를 포함하고 있다. 이 파일을 사용하면 별도로 chain.pem을 제공하지 않아도 클라이언트가 인증서를 검증할 수 있다.
- privkey.pem : 이 파일은 해당 도메인에 대한 개인 키(private key)를 포함한다. SSL 인증서를 발급받을 때 생성되는 개인 키는 보안상의 이유로 비공개로 유지해야 한다. 이 개인 키는 서버와 클라이언트 간의 암호화된 연결을 설정하는 데 사용된다.
3. Nginx 설정 파일 작성 (nginx.conf) & SSL/TLS 인증서 적용
Nginx 컨테이너를 실행할 때, -v 옵션으로 마운트 해줄 Nginx 설정 파일이다. /etc/nginx 디렉토리에 작성하였다. 발급받았던 SSL 인증서 또한 -v 옵션으로 컨테이너 내부에 마운트 해줄 것이다.
events {
worker_connections 1024; # 최대 동시 연결 수
}
http {
server {
listen 80;
server_name coinrun.kr;
return 301 https://coinrun.kr$request_uri;
}
server {
listen 443 ssl;
server_name coinrun.kr;
ssl_certificate /etc/nginx/ssl/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/privkey.pem;
location / {
proxy_pass http://coinrun.kr:8080;
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;
}
}
}
4. Nginx 컨테이너 실행 (nginx.conf + SSL 인증서 내부로 마운트)
nginx.conf 설정 파일 및 발급받은 SSL 인증서를 컨테이너 내부에서 접근할 수 있도록 마운트 해주도록 한다.
docker run -d --name nginx-container \
-v /etc/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /etc/letsencrypt/live/coinrun.kr/fullchain.pem:/etc/nginx/ssl/fullchain.pem \
-v /etc/letsencrypt/live/coinrun.kr/privkey.pem:/etc/nginx/ssl/privkey.pem \
-p 80:80 -p 443:443 \
nginx
5. 실행 확인
docker ps 로 Nginx 컨테이너가 정상 실행 중 인지 확인한다.
https://coinrun.kr 에 접속하여 적용이 잘 됐는지 확인한다.
❌ 트러블 슈팅
nginx.conf 및 SSL 인증서를 적용하여 nginx 컨테이너를 실행했음에도, 사이트 접근이 안 됐던 문제.
서버 생성 초기에 iptable 포트포워딩 규칙이 443 -> 8080 으로 가도록 설정해뒀었다. 이 때문에 443 접근 시, 8080으로 보내버려서 Nginx를 거치지 않고 포트가 꼬였었다.
필요하지 않은 iptable 포트 포워딩 설정을 제거하여 Nginx를 제대로 탈 수 있도록 하여 해결.
# iptables의 nat 테이블에서 설정된 PREROUTING 규칙을 확인
sudo iptables -t nat -L -v -n --line-numbers
# 해당 포트포워딩 설정 제거
sudo iptables -t nat -D PREROUTING 1
✔️ Nginx 도입 후 아키텍처
Nginx 도입 후, 클라이언트와 Spring Boot 사이에서 리버스 프록시 역할을 하도록 한다. SSL/TLS 인증서를 Spring Boot 에서 Nginx가 담당하도록 변경하여 역할과 책임을 분리하였다.
추후에 정적인 파일은 Nginx 웹 서버가 담당하여 웹 애플리케이션(WAS)의 부하를 줄일 수 있다.
또한 Spring Boot 서버에 문제 발생 시, Nginx에서 오류 페이지를 사용자에게 보여주며 사용자 경험을 향상할 수도 있겠다.