프론트에서 사용하는 정적 웹사이트 호스팅 서비스인 Vercel은 HTTPS를 통해서 배포된다.
하지만 백엔드에서는 HTTP로 동작하기 때문에 CORS 에러가 발생한다.
웹서버의 TLS 인증서를 사용해 웹사이트를 HTTPS로 열 수 있게끔 Nginx 프록시 서버에 TLS 인증서를 적용하도록 구현해보자
유저가 어떤 웹사이트에 보내는 정보를 다른 누군가 훔쳐보지 못하도록 하기 위함
EX)
HTTP로 만들어진 네이버에 로그인 시 아이디와 비밀번호를 네이버의 서버로 보낸다고 했을 때
입력한 텍스트 그대로 누구든 알아볼 수 있는 형식으로 보내진다.
만약 누군가가 이 정보를 중간에 들여다 본다면 유저는 아이디와 비밀번호를 알게 되는 것이다.
그래서 나온 것이 HTTPS이다. HTTPS로 만들어진 네이버에 로그인 시에는
유저의 아이디와 비밀번호를 네이버 서버의 인증서와 약속한 텍스트로 인코딩해서 보낸다.
다른 누군가가 로그인 정보를 중간에 들여다 보더라도 알아볼 수가 없다.
HTTP(HyperText Transfer Protocol)
는 클라이언트와 서버 사이에서 데이터를 주고 받기 위한 프로토콜이다.HTTPS(HyperText Transfer Protocol Secure)
는 SSL/TLS 인증서를 통해 기존 HTTP에 보안 계층을 추가한 프로토콜이다.HTTPS는 공개키 암호화 방식과 대칭키 암호화 방식의 장점을 활용해 하이브리드를 사용한다.
데이터를 대칭키 방식으로 암복호화하고, 공개키 방식으로 대칭키를 전달한다.
서버(WAS)에 HTTPS를 적용할 때는 보통 리버스 프록시 서버를 두어 TLS 인증에 대한 엔드포인트 역할을 수행하도록 한다. 이렇게 하면, TLS 인증과 같은 부가 기능 처리와 비즈니스 로직을 분리하여 한쪽으로 치우치는 부하를 분산 시킬 수 있다.
프록시(Proxy)
란, 클라이언트와 서버 사이에 위치한 중계 서버로써 대리자 역할을 하는 것을 말한다.
클라이언트와 서버 사이에 프록시 서버를 두면, 보안이 강화되고 통신 성능이 높아진다는 장점이 있다.
크게 포워드 프록시(Forward Proxy)
와 리버스 프록시(Reverse Proxy)
로 나눌 수 있다
보안
용도로도 사용한다.로드 밸런서
의 역할로 사용하여, 집중적으로 발생하는 부하를 여러 서버로 나눠 보낼 수 있다.무중단 배포
시 배포 중인 서버에 요청을 보내지 않도록 할 수 있다.보안
을 강화시킬 수 있다.리버스 프록시 서버는 별도의 웹 서버(Apache, Nginx)를 클라이언트와 WAS 사이에 두는 방식으로 구현된다.
웹 서버(Web Server)
란, HTTP(HTTPS)를 통해 클라이언트에서 요청하는 문서나 이미지 파일 등의 정적 리소스를 전송해주는 서버를 말한다. 대표적으로 Apache HTTP Server
와 Nginx
가 있다.
Apache는 프로세스 / 스레드 기반 구조로 동작하는 웹 서버 이다.
클라이언트로부터 요청이 들어오면 새 커넥션을 생성하기 위해 새로운 프로세스(혹은 스레드)를 할당한다.
두 가지 방식으로 프로세스(혹은 스레드)를 분배한다.
Apache가 등장하고 시간이 지나면서 PC의 보급률이 높아져 점점 트래픽이 많아졌다.
클라이언트 요청 하나당 하나의 프로세스(혹은 스레드)를 생성하는 구조는 C10K
의 문제가 발생하게 된다.
C10K
문제란, Connection 10,000개 문제
라는 의미로, 요청에 의해 생겨나는 커넥션들이 10,000개를 넘어가지 못하는 문제를 말한다. 당시 CPU는 충분히 요청을 처리할 수 있었으나 Apache 구조 상, 한 개 커넥션 당 하나의 프로세스(스레드) 할당으로 인해 메모리가 버티지 못했다.C10K
문제를 극복하고자 Nginx
가 등장한다.
Nginx는 Event-Driven 기반 구조
로 작동하는 웹 서버이다.
Apache와 달리 하나의 고정된 프로세스만 생성하고, 새로 들어오는 클라이언트의 요청은 이벤트 핸들러를 이용해 비동기 방식으로 처리하는 방식
요청이 늘어날 때 추가적으로 프로세스(혹은 스레드)를 할당하지 않기 때문에 메모리를 적게 사용한다.
Master-Worker 방식
Nginx는 설정 파일을 읽고 Worker 프로세스를 생성하는 Master 프로세스
와, 실제로 클라이언트의 요청을 처리하는 Worker 프로세스
로 이루어진다.
Nginx를 구동하면, Master 프로세스는 정해진 수 만큼의 Worker 프로세스를 생성한다.
Worker 프로세스는 Working Queue에 담긴 이벤트(커넥션 연결, 요청, 커넥션 종료 등)들을 순차적으로 처리한다. 새로운 프로세스(혹은 스레드)를 생성하지 않기 때문에 이 Worker 프로세스는 끊임없이 작업을 수행한다.
따라서 커넥션마다 프로세스(혹은 스레드)를 할당하던 Apache와 달리 메모리를 적게 점유하여 효율적으로 요청을 처리한다.
향상된 Nginx(Feat. Thread pool)
Working Queue에 시간이 오래 걸리는 작업이 들어오면 Worker 프로세스가 해당하는 이벤트를 처리하는 동안 Queue에 쌓여있는 다른 이벤트들은 계속 대기하게 된다. ⇒ 성능 저하
그래서 Nginx에서는 스레드 풀(Thread Pool)을 추가하여, 오래 걸리는 작업을 처리하는 스레드 묶음을 따로 만들어 이를 해결한다. ⇒ Nginx 1.7.11 버전부터 도입
Nginx를 통해서 Apache에 비해 처리 가능한 동시 커넥션 양이 1000배 정도 증가하게 되었다.
또한 동일한 커넥션 일 때 Apache에 비해 약 2배 정도 속도가 향상되었다.
지금부터 Nginx를 통해서 리버스 프록시용 웹서버를 구현해보겠다.
가비아 홈페이지에서 원하는 도메인을 검색한 후 신청하기로 구매한다.
필자는 [review-ranger.store](http://review-ranger.store)
를 구매하였다.
My가비아 → DNS 관리 → 레코드 수정을 통해 DNS에 EC2 인스턴스의 public IP를 등록한다.
필자는 [api.review-ranger.store](http://api.review-ranger.store)
로 백엔드 API 도메인을 지정해주었다.
필자의 EC2 환경은 Ubuntu 22.04.3 LTS를 사용하고 있다.
2.1 설치 가능한 패키지 목록 최신화
$ sudo apt update
2.2 Nginx 설치
$ sudo apt install nginx
2.3 Nginx 설치 확인
$ nginx -v
=> nginx version: nginx/1.18.0 (Ubuntu)
2.4 Nginx 실행 확인
$ sudo systemctl status nginx
=> running이 나오면 실행되고 있는 것이다.
2.5 Nginx Routing 코드 작성
$ cd /etc/nginx/sites-available/
# api.review-ranger.store 파일 생성 및 수정
$ sudo vi api.review-ranger.store
# api.review-ranger.store 파일 내용
server {
server_name api.review-ranger.store;
location / {
proxy_pass http://localhost: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;
}
}
=> **server** : 해당 도메인으로 요청이 들어오면 어떻게 처리할 것인지를 적는다.
=> **server_name** : 어떤 도메인의 요청을 처리할 것인지 명시한다. 여러 개도 지정 가능
=> **location** : 어떤 endpoint 요청을 처리할 것인지 작성한다.
=> **proxy_pass** : 해당 요청을 지정한 경로로 포워딩한다.(위에서는 스프링 서버와 매칭)
=> **proxy_set_header** : 포워딩 시의 헤더 값을 정의한다.
# 서버와 설정 파일 연결
$ sudo ln -s /etc/nginx/sites-available/api.review-ranger.store /etc/nginx/sites-enabled/api.review-ranger.store
# 파일이 잘 동작하는지 확인
$ sudo nginx -t
=> nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
=> nginx: configuration file /etc/nginx/nginx.conf test is successful
# Nginx 재시작
$ sudo service nginx restart
# snap 설치
$ sudo apt install snapd
$ sudo snap install core && sudo snap refresh core
# CertBot 설치
$ sudo snap install -–classic certbot
# 심볼릭 링크 생성
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot
# 버전 확인
$ certbot --version
# 인증서 설치
$ sudo certbot --nginx -d api.review-ranger.store
=> 이메일 입력 beombu13@gmail.com
=> 약관 동의 Y
=> successfully received certificate.
=> ...
=> (공개 키 저장 위치) Certificate is saved at: /etc/letsencrypt/live/api.review-ranger.store/fullchain.pem
=> (비밀 키 저장 위치) Key is saved at: /etc/letsencrypt/live/api.review-ranger.store/privkey.pem
=> (인증서 유효기간) This certificate expires on 2024-05-05.
# nignx 재시작
$ sudo service nignx restart
TLS 인증서를 받았으니 브라우저에서 접속해서 확인하자.
인증서도 아래 명령어를 통해 확인할 수 있다.
# 인증서 확인
$ sudo certbot certificates
=>
Saving debug log to /var/log/letsencrypt/letsencrypt.log
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Found the following certs:
Certificate Name: api.review-ranger.store
Serial Number: 40d84bac8226ac3d8e6eb8b834a337ed046
Key Type: ECDSA
Domains: api.review-ranger.store
Expiry Date: 2024-05-05 04:13:34+00:00 (VALID: 89 days)
Certificate Path: /etc/letsencrypt/live/api.review-ranger.store/fullchain.pem
Private Key Path: /etc/letsencrypt/live/api.review-ranger.store/privkey.pem
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Let’s Encrypt로 만들어진 TLS 인증서는 90일마다 갱신이 필요하다.
Certbot 패키지는 인증서가 만료되기 전에 자동으로 갱신하는 cron 작업 또는 시스템 타이머와 함께 제공된다.
/etc/cron.d
에 자동으로 갱신 시켜주는 커멘드가 추가되어 있다. 구성을 변경하지 않는 한 Certbot을 다시 실행 할 필요가 없다.
# 인증서 자동 갱신 테스트
$ sudo certbot renew --dry-run
$ cd etc/
# 타이머 조회
$ systemctl list-timers
=> **snap.certbot.renew.service이 실행되고 있는지 확인**
NEXT LEFT LAST PASSED UNIT ACTIVATES
Tue 2024-02-13 16:09:00 KST 13min left n/a n/a snap.certbot.renew.timer snap.certbot.renew.service
...