서버를 배포한고 나면 처음에는 웹 브라우저에서 돌아가는 내 사이트가 신기하기만 하다. 하지만 다음 단계로 https배포, 무중단 배포, 배포 자동화 등등을 이어가다 보면 배포 하는데 어플리케이션 개발보다 더 많은 리소스를 사용하게 되기도 한다. 따라서 각각을 어떻게 해야 하는지를 파악하고, 이를 결합하는 방법을 알 필요가 있다. 본 포스트는 그 중 첫 번째 단계로, NGINX와 certbot을 이용한 https배포의 방법을 소개한다.
만약 NGINX가 뭔지, WS와 WAS의 개념이 뭔지 잘 모른다면, 이를 위해 필요한 개념을 정리해 둔 NGINX에 대해 알아보자 포스트를 우선 읽고 오길 권한다. 또한 배포 서버는 ec2 ubuntu 22.04로 가정하고 있는데, AWS ec2에 대해 잘 모른다면 마찬가지로 관련 포스트를 읽고 오길 권한다. (AWS와 관련된 포스트는 추후에 작성 예정)
아래는 최종적으로 Java Spring 프로젝트가 가동중이고, NGINX를 통해 로드 밸런싱이 이루어지면서 certbot을 이용해 https배포까지 마친 아키텍처 예시이다.
이미 ec2 환경에서 배포를 한 번 해 봤다면 ip주소:8080(x.xx.xx.xxx:8080)으로 요청을 보내본 경험이 있을 것이다. 하지만 일반적으로 사이트에 접속하려면 위와 같이 ip주소를 사용자가 직접 입력하지 않고, naver.com과 같이 영어로 된 주소를 입력하는데, 이를 도메인 주소라 한다. 먼저 우리가 사용하는 ec2 서버의 ip에 도메인 서버를 연동해야 사용자가 ip 주소가 아닌 해당 도메인 주소를 쳐서 접근할 수 있게 된다.
무료 도메인을 받을 수 있는 사이트가 많기는 하지만, 여기서는 가비아를 이용하고 있다. 가비아에서는 .shop으로 끝나는 도메인 네임을 500원에 1년 동안 이용할 수 있도록 판매하고 있다. 다른 무료 사이트는 해외 사이트라 이용이 복잡하거나 보안에 대한 걱정도 있기 때문에 가비아를 이용했다. 가비아에 ec2 ip와 도메인 네임을 연동하는 과정은 가비아 유튜브나 다른 블로그에서도 잘 설명하고 있으므로 우선 적용하고 오자. 여기서 주의할 점은 반드시 해당 도메인에 A레코드를 적용해야 이후 certbot을 이용한 https 적용이 가능하다.
ec2 ip와 도메인 주소를 연동했다면 이제 ec2 서버에 NGINX를 설치할 차례다. 터미널에서 keypare를 이용해 ec2 사용자로 접속해 준다.
ssh -i "본인 키페어.pem" ubuntu@ec2-x-xx-xx-xxx.ap-northeast-2.compute.amazonaws.com
아래 명령어를 입력해 nginx를 설치해 준다.
$ sudo apt update
$ sudo apt install nginx
$ nginx -v # 버전을 확인해 보고 버전이 나오면 잘 설치된 것
nginx를 설치했다면, /etc/nginx의 경로로 들어가 보자
cd /etc/nginx
ls #해당 경로의 모든 파일과 디렉토리 목록을 보여줌
ls 명령어로 해당 위치에 어떤 폴더와 파일이 있는지를 보면, 여러 폴더가 있을 텐데 다른 파일은 모두 필요 없고 가장 중요한 건 설정 파일인 nginx.conf와 설정 폴더인 conf.d이다. nginx.conf파일에 기본적으로 우리가 어떻게 nginx를 쓸 것인가 정의되어 있다. 안에 설정 파일이 어떻게 되어 있는지를 vim 편집기를 통해 한 번 살펴보자
sudo vim nginx.conf
그러면 설정 파일의 내용이 보일 것이다. 스크롤을 조금씩 내리다 보면 아래와 같은 코드 라인이 있을 것이다.
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
아까 nginx.conf와 함께 conf.d도 중요하다고 했다. 그 이유가 여기서 나오는데, nginx.conf파일은 conf.d 폴더 아래에 있는 모든 conf확장자 명을 가진 파일들을 include한다. 따라서, 우리가 만든 프로젝트에 맞게 nginx를 커스텀해 사용하기 위해서는 conf.d 디렉토리로 이동해 conf확장자명을 가진 파일을 생성한 후, 문법에 맞게 설정 코드를 작성해 주면 된다. 그리고 sites-enabled의 경우, 과거의 nginx 설정 방법이기 때문에 더 이상 사용되지 않는다고 한다. 따라서 이 부분은 신경 쓰지 않는다. 이제 conf.d 폴더로 이동해 vim 편집기로 설정 파일을 생성해 보자
( 참고로 vim 편집기에서 빠져 나오려면 ESC를 한 번 누른 후 ':q'를 치고 엔터를 치면 나와진다. 안 된다면 ':wq'를 해 보자. 다만 그러면 본인도 모르게 설정 파일 어딘가를 편집한 것일 수 있으므로 주의 )
cd conf.d
sudo vim default.conf # 일반적으로 default.conf를 많이 쓰는 것이지 example.conf를 써도 된다.
vim 편집기로 default.conf 파일로 들어왔으면 이제 아래의 설정 코드를 작성해 준다.
server {
listen 80;
server_name your.domain.com;
location / {
proxy_pass http://x.xx.xx.xxx:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
}
이 스크립트를 보면, your.domain.com:80을 listen할 경우 location으로 redirect되도록 되어 있다. 그리고 your.domain.com은 가비아에서 우리 ec2 서버의 ip 주소인 x.xx.xx.xxx의 도메인 서버 주소이기 때문에, 결국 x.xx.xx.xxx:80으로 요청이 들어온 것과 같게 된다. 포트가 80인 이유는 http 통신의 기본 포트가 80이기 때문에, 80을 붙이지 않으면 80이 자동으로 붙는다. 예시로 naver.com이라고 치든 naver.com:80이라고 치든 모두 네이버로 이동하는 것을 확인할 수 있다. 이후 location에 해당하는 x.xx.xx.xxx:8080으로 요청이 넘어간다. 여기서는 프로젝트의 포트 예시로 8080을 사용했지만 본인의 프로젝트의 포트 번호에 맞게 넣어주어야 한다.
여기까지 마치고 Esc를 누르고 ':wq'를 입력해 빠져나온 후 ls 명령어를 쳐 보면 default.conf파일이 생성되어 있는 걸 확인할 수 있다. 그리고 이 파일은 nginx.conf에서 include될 것이다. 따라서 현재 요청은 가비아에 의해 your.domain.com:80 -> x.xx.xx.xxx:80로 한 차례 이동되고, nginx에 의해 x.xx.xx.xxx:80 -> x.xx.xx.xxx:8080으로 로드 밸런싱되고 있다. 이제 마지막으로 certbot을 이용해 https를 적용할 차례다.
certbot은 SSL 인증을 돕는 툴이다. 아래 과정을 따라가면 https 적용이 되기야 하겠지만 어떤 원리로 ssl 인증이 되고, https 통신이 되는지 간략하게 짚고 넘어가 보자.
우선 https 프로토콜이란 http프로토콜 + SSL 프로토콜로 이루어져 있다. http통신과 방식은 똑같은데 SSL 인증이라는 과정이 한 번 더 추가되는 것. certbot은 사용자가 지정한 도메인(your.domain.name)에 대한 소유권을 검증하고, 검증되었을 경우 Let's Encrypt라는 곳으로 ssl 인증 요청을 보낸다. 그러면 Let's Encrypt는 받은 정보를 토대로 도메인에 접근이 가능한지 파악한 후 해당 서버에 SSL 인증서를 발급해 준다. 그렇게 우리의 ec2 서버는 SSL 인증서를 갖고 있게 되고, 이 인증서를 이용해 certbot은 nginx.conf에 https 통신의 기본 포트인 443 포트를 통한 요청이 listen될 경우 ssl 인증을 거치도록 스크립트를 추가한다. 이러한 과정을 통해 https 프로토콜을 이용한 통신을 할 수 있게 되는 것이다.
아마 글로 봐도 이해가 잘 가지 않을 수 있다. 아래의 과정을 모두 거친 후 이 위치로 돌아와서 다시 읽어보면 좀 더 쉽게 이해가 갈 것이다.
certbot을 설치하는 과정은 certbot 가이드에서 확인할 수 있다. 들어가 보면 아래 문구가 보일 것이다.
My HTTP website is running (WS 선택) on (OS 선택)
현재 포스트 예시에서는 nginx를 WS로, OS는 ubuntu로 사용하기 때문에 선택해 준다. 그러면 가이드가 나타난다. snap을 설치해야 한다고 하는데, ec2를 ubuntu로 선택하면 이미 다운로드되어 있는 상태인 듯하다. 원래는 아래 명령어를 순서대로 입력해 다운로드 해야 한다.
sudo apt update
sudo apt install snapd
# 설치되었는지 확인
sudo snap install hello-world
hello-world # Hello World!가 출력되면 성공이다.
다음으로 버전이 최신인지 확인하기 위해 아래 명령어를 입력한다.
sudo snap install core; sudo snap refresh core
OS패키지 매니저가 자체적으로 갖고 있는 certbot package를 지워야 한다고 한다. 아래 명령어를 통해 있다면 지워주자.
sudo apt-get remove certbot
이제 certbot을 설치해 주고
sudo snap install --classic certbot
certbot 명령어가 기능하도록 해 준 다음
sudo ln -s /snap/bin/certbot /usr/bin/certbot
WS가 nginx라는 것을 알려줘서 nginx의 설정을 적용하도록 알려주자
완벽히 하기 위해서는 인증서만 발급받고 conf 스크립트를 직접 작성해야겠지만 여기서는 자동 생성되도록 했다.
sudo certbot --nginx
이후 이메일 입력 -> 약관 동의, 도메인 입력을 거치게 되는데 이 과정이 끝나면 Let's Encrypt를 통해 SSL 인증서를 발급해 준다. 이제 default.conf파일로 돌아가 보면 certbot이 추가해 준 스크립트가 추가되어 있을 것이고,
cd etc/nginx/conf.d
sudo vim default.conf
# 아래와 같이 변경되어 있으면 성공
server {
server_name your.domain.name;
location / {
proxy_pass http://x.xx.xx.xxx:8080;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
}
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/도메인 이름/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/도메인 이름/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 = your.domain.name) {
return 301 https://$host$request_uri;
} # managed by Certbot
listen 80;
server_name your.domain.name;
return 404; # managed by Certbot
}
/etc/letsencrypt/live 아래에 인증서가 있을 것이다.
sudo ls -l /etc/letsencrypt/live/myfirstdomain.shop/
# 아래와 같은 출력이 나타나면 성공
total 4
-rw-r--r-- 1 root root 692 May 18 07:09 README
lrwxrwxrwx 1 root root 42 May 18 07:09 cert.pem -> ../../archive/도메인 이름/cert1.pem
lrwxrwxrwx 1 root root 43 May 18 07:09 chain.pem -> ../../archive/도메인 이름/chain1.pem
lrwxrwxrwx 1 root root 47 May 18 07:09 fullchain.pem -> ../../archive/도메인 이름/fullchain1.pem
lrwxrwxrwx 1 root root 45 May 18 07:09 privkey.pem -> ../../archive/도메인 이름/privkey1.pem
여기까지 성공했다면 nginx를 다시 시작해 주면 끝이다.
nginx -s reload
이제 https://your.domain.name으로 접속해서 성공했는지 확인해 보면 된다. 백엔드 프로젝트만 있는 경우 뒤에 api 요청까지 붙여서 response가 오는지 확인해 보면 된다.
Let's Encrypt의 SSL 인증서는 유효 기간이 3개월이기 때문에 갱신을 해 주어야 한다. 옛날 certbot은 이 인증을 갱신하는 cron 명령를 추가해야 했지만, 본 포스트가 작성되는 일자 기준으로는 만료 20일 전에 인증 갱신 명령이 자동으로 추가된다. 아래 명령어를 통해 확인해 보자
systemd.timer
# 아래 사진처럼 최 상단에 snap.certbot.renew.service가 Activities에 있다면 성공이다.