이 포스팅에선 이론적인 내용에 보다는 구성 방법과 흐름에 대해서만 조망합니다.
EC2 생성, NGINX 설치, 프록시 설정, 도메인 및 SSL 적용을 해본 적이 없거나
과정에 대해 모호한 부분이 있으시다면 이 포스팅이 도움이 될 겁니다. 😀
전체 과정을 10분 가량으로 녹화해보았습니다.
실제 적용 과정을 보고 싶다면 https://youtu.be/JMPCdfeA-bs 를 참고해주세요
1. 도메인 확보 및 타겟 설정
토이 프로젝트 목적이라면
가비아에서 값싼 도메인을 구해서 진행하셔도 되고,
freenom 을 이용해 무료 도메인을 구해서 진행하셔도 됩니다.
저는 freenom 을 이용해 richplace.ga 라는 도메인을 구했습니다.
가장 윗줄의 설정을 살펴보자면
http://richplace.ga 로 주소창에 입력할 경우 43.200.34.21 으로 보낸다는 설정입니다.
그 아래는 각각 http://www.richplace.ga 와 http://dev.richplace.ga 를 의미합니다.
CNAME은 서브도메인을 설정할 때 사용해야 하는 타입입니다.
타겟에는 도메인명을 입력한 모습입니다.
어찌됐든 세 가지 경우 모두 같은 public ip로 요청을 전달합니다.
http 요청의 기본 포트인 80포트로 요청이 전송되고요,
추후 구성될 웹서버인 nginx 설정을 통해 서브 도메인에 대한 분기 처리를 해줄 겁니다.
가비아에서 진행할 경우, 빈 값은 @로 대체되어야 하고, 서브도메인의 target에는
도메인명 맨 뒤에 .을 추가로 입력해야 한다는 점이 차이점입니다.
(가비아 설정화면에서 IP가 다른 부분은 무시해주세요! 다른 도메인에 대한 설정 화면입니다)
2. 탄력적 IP 확보
EC2 인스턴스를 생성하기 이전에 IP를 할당받아두기 위한 목적 및
추후 EC2 인스턴스를 재기동해도 IP를 유지하기 위한 목적으로 탄력적 IP를 사용했습니다.
추후 EC2 인스턴스 생성 이후 이 IP를 EC2 인스턴스에 연결해주면 되기 때문에,
다소 시간이 걸리는 도메인과 IP를 연결하는 작업을 미리 진행하기 위함이었습니다.
EC2 인스턴스 생성 이후 public ip를 도메인 설정하셔도 무방합니다.
3. 인스턴스 생성 및 인바운드 규칙 설정
EC2 인스턴스를 생성한 뒤엔 80, 443, 22 포트를 허용해줍니다.
생성 단계에서 해당 포트들을 체크박스에 체크함으로써 열어줄 수도 있습니다.
4. NGINX, JDK 11 설치, 도메인 적용 테스트
sudo apt-get install nginx -y
sudo apt install openjdk-11-jdk -y
EC2 인스턴스에 접속해서 위 명령어를 통해 NGINX와 JDK를 설치합니다.
JDK는 API로 사용될 WAS를 기동하기 위해 설치했습니다.
NGINX는 설치와 함께 80포트에 자동으로 기동됩니다.
따라서 도메인 적용이 정상 처리되었다면 아래와 같이 접속이 가능해집니다.
5. 운영환경, 개발환경 WAS 2개 배포
@RequestMapping("/api/hello")
@RestController
public class HelloController {
// 서버 이름을 기동 시에 매개변수로 전달하겠습니다
@Value("${rich.server.name}")
private String serverName;
@GetMapping
public ResponseEntity<String> hello() {
return ResponseEntity.ok("hello from " + serverName);
}
}
// 운영 서버용 API는 8080포트에,
// 개발 서버용 API는 9090포트에 기동하겠습니다
nohup java -Drich.server.name=prod -Dserver.port=8080 -jar testgogo-0.0.1-SNAPSHOT.jar &
nohup java -Drich.server.name=dev -Dserver.port=9090 -jar testgogo-0.0.1-SNAPSHOT.jar &
curl localhost:8080/api/hello // hello from prod
curl localhost:9090/api/hello // hello from dev
NGINX를 통해 운영환경, 개발환경에 별도의 API를 사용할 수 있도록 구성할 예정입니다.
가령 www.richplace.ga에서 www.richplace.ga/api/hello를 호출하면 운영용 WAS를,
dev.richplace.ga 에서 dev.richplace.ga/api/hello 호출하면 개발용 WAS를 사용하도록 말이죠.
운영서버 API는 8080 포트에, 개발서버 API는 9090 포트에 기동하겠습니다.
각각 실행 시점에 매개변수를 전달해 hello from prod, hello from dev를 응답하도록 했습니다.
6. NGINX 설정
# 운영 환경에서 api를 호출할 경우, 8080포트의 WAS를 호출
server {
listen 80;
server_name richplace.ga www.richplace.ga;
location /api {
proxy_pass http://127.0.0.1:8080;
}
}
# 개발 환경에서 api를 호출할 경우, 9090포트의 WAS를 호출
server {
listen 80;
server_name dev.richplace.ga;
location /api {
proxy_pass http://127.0.0.1:9090;
}
}
# 즉 proxy_pass 에 있는 내용을 같은 VPC내 있는
# 다른 EC2의 private IP:port 를 명시하면 리버스 프록시 완료!
sudo service nginx restart
NGINX 기본 설정은 /etc/nginx/sites-available/default 파일에 설정되어 있습니다.
해당 파일에 위와 같은 내용을 추가했습니다.
richplace.ga 또는 www.richplace.ga로부터 요청이 올 경우,
path를 확인해서 /api로 시작한다면, 이를 127.0.0.1:8080로 보낸다는 것입니다.
그러면 운영용 WAS가 해당 요청을 받아 NGINX에게 응답하고,
이를 다시 NGINX가 클라이언트에게 응답하는 식입니다.
반면 dev.richplace.ga 에서 요청이 왔다면,
path를 확인해서 /api로 시작한다면, 이번엔 9090 포트의 개발환경용 API로 보내는 것입니다.
현재 구성은 하나의 EC2 인스턴스에 NGINX와 WAS2개를 모두 띄웠기 때문에 127.0.0.1인데요,
각각 개별 인스턴스에 기동하게 된다면 같은 VPC 내 구성시키고 private ip를 명시하면 될 것 같습니다.
설정을 마치셨다면 NGINX를 재기동해줍니다.
7. 적용 확인
운영환경에서의 요청은 8080 포트에 있는 운영서버 API가,
개발환경에서의 요청은 9090 포트에 있는 개발서버 API가 응답하고 있는 모습입니다.
정상적으로 적용이 완료되었네요.
8. API 주소가 /api로 시작하는 이유
NGINX에서 포트포워딩을 설정할 때,
지금 제가 예시로 사용한 path를 구분하여 분기 처리하는 방법도 있지만,
server_name에 입력한 host를 이용해 분기 처리하는 방법도 있습니다.
가령 api.richplace.ga 와 같은 API주소를 사용하여 분기 처리할 수도 있다는 것이죠
그러나 실제 구현되어있는 사이트들을 둘러보면,
대부분 host를 동일하게 유지하고, path를 이용해 구분하는 모습입니다.
위 사진을 보시면 접속한 페이지의 주소는 map.naver.com/v5 이고,
접속 이후 fetch 요청이 호출된 주소는 map.naver.com/v5/api/~ 입니다.
즉 동일한 host를 유지한 채, path를 이용해 API URL인지를 구분한 모습입니다.
왜 그럴까 고민해봤는데요,
첫 번째로 든 생각은 사용자 입장에서 host가 동일하게 유지되는 것이
더 안전하다고 느끼지 않을까 라는 지점이었습니다.
하지만 이 생각은 개발자 도구를 열어보지 않는 사람이 대다수일 것이기에;
큰 의미가 없는 것 같았는데요
이와 같은 연장선에서 Same Origin Policy와 Cross Origin Resource Sharing가 떠올랐습니다.
그렇다면 Same-Origin을 유지하도록 구성하는 것의 장점을 가져가기 위해
path를 이용한 분리를 선택했다고 유추했습니다.
그러나 이렇게 되면 프론트와 백이 각각의 NGINX를 통해 관리되는 것이 아니라
하나의 공통된 NGINX가 존재하고, 이것이 프,백을 분기해주는 모양새가 되는 거라서
완전히 분리되지 않은 것 같다는 아쉬움이 드는데요, 이 부분은 추가로 탐구해봐야 할 것 같습니다.
8. SSL 적용
마지막으로 SSL을 적용해보겠습니다.
certbot을 이용하면 CLI 몇 번의 입력으로 간단하게 끝납니다.
공식문서를 참고하여 일부 변형하였습니다.
# certbot을 snap 명령어로 설치, 실행하기 때문에 snap을 먼저 설치한다
sudo snap install core
sudo snap refresh core
# 기존에 설치된 certbot을 제거한다
# 공식 가이드에선 certbot명령어를 사용할 때 snap이 사용되게 하기 위함이라고 설명한다
sudo apt-get remove certbot
# certbot을 설치한다
sudo snap install --classic certbot
# certbot 명령어가 실행될 수 있게 세팅한다
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# nginx가 아닌 apache를 웹서버로 사용할 경우, sudo certbot --apache 가 된다
sudo certbot --nginx -d richplace.ga -d www.richplace.ga -d dev.richplace.ga
# 놀랍게도 certbot은 CLI몇 줄로 SSL을 적용해줄 뿐 아니라 자동 리뉴얼까지 해준다;
# 처음 설치할 때부터 이러한 cron job 처리를 위한 내용이 함께 들어온다
# 아래 명령어로 자동 리뉴얼이 적용되고 있는지 확인할 수 있다
sudo certbot renew --dry-run
놀랍게도 certbot은 인증서 자동 갱신까지 처리해줍니다.
systemctl list-timers 명령어를 통해 추가된 타이머를 확인할 수 있습니다.
위 명령어 절차를 따르고 나면 certbot에 의해 인증 파일이 생성되고,
SSL 적용을 위한 NGINX 설정 파일이 수정되어있습니다.
9. 변경된 설정 파일 살펴보기
server {
server_name richplace.ga www.richplace.ga;
location /api {
proxy_pass http://127.0.0.1:8080;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/richplace.ga/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/richplace.ga/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
server_name dev.richplace.ga;
location /api {
proxy_pass http://127.0.0.1:9090;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/richplace.ga/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/richplace.ga/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
if ($host = www.richplace.ga) {
return 301 https://$host$request_uri;
} # managed by Certbot
if ($host = richplace.ga) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name richplace.ga www.richplace.ga;
return 404; # managed by Certbot
}
server {
if ($host = dev.richplace.ga) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name dev.richplace.ga;
return 404; # managed by Certbot
}
상단에는 HTTPS 접속을 위한 443 포트에 대한 listen설정이 처리되어 있습니다.
인증서의 위치도 명시되어 있군요.
하단에는 HTTP 접속이 왔을 때 이를 https로 리디렉팅 해주는 설정입니다.
10. SSL 적용 확인
http로 주소를 입력하고 들어가더라도 https로 리디렉팅 되고 있는 모습입니다.
ssllabs.com 에 방문하여 SSL이 적용된 richplace.ga를 평가해봤습니다.
A를 받았네요~ 초록불은 기분이 좋습니다
학습 출처
4기 백엔드 크루 찬, 칙촉과 함께 학습했습니다.
'Server & Infra' 카테고리의 다른 글
Jacoco with Github Action (0) | 2022.08.08 |
---|---|
CORS 설정이 불필요한 서버 아키텍처 (Same-Origin, @Profile) (0) | 2022.08.05 |
Github Actions 를 이용한 CI 테스트 자동화 (2) | 2022.07.27 |
AWS EC2에 오라클 설치, 접속, Scott&HR 활성화 (0) | 2021.01.24 |
AWS EC2에 설치해둔 Redis에 GUI툴로 접속하기, Java에서 접속하기 (0) | 2021.01.24 |