안녕하세요. 드디어 졸업프로젝트 마감 시간이 다가오고 있습니다. 저희 팀은 그동안 열심히 만든 모델을 사용하기 위해서 API를 개발하고, 프론트에 연결하는 작업을 하고 있습니다. 단순한 작업일 거라 생각했는데... 생각보다 어렵더군요. 힘들게 API를 만들었더니 프론트에서 불러올 수가 없는 상황에 놓였었습니다...
그 이유는 저희 프로젝트가 크롬 익스텐션 서비스이기 때문인데, 크롬 익스텐션은 HTTP를 허용하지 않습니다.
이 때문에 HTTPS을 이용해야 했고 이를 위해서 도메인 등록과 SSL 발급이 필요했습니다.
(또한 CORS도 허용해야 했습니다. 만약 CORS 에러 부분만 보고 싶으시다면 맨 밑으로 가주세요!)
그리고 저희 프로젝트 모델이 로컬환경에 있는게 아니라 텐센트 클라우드 인스턴스에 있습니다. 모델 사이즈가 크다 보니 다운로드도 힘들고 로컬에 저장 용량도 없어서, 학교에서 지원해주는 GPU 인스턴스에 모델을 저장해 놓은 상태였습니다.
그래서 저희는 결론적으로 HTTPS 접속이 가능하고 CORS를 허용하는 API 서버를 만들어야 했습니다. 저는 이 과정에서 생각보다 애를 먹었기 때문에... 미래의 어느 한 분 쯤은... 저와 비슷하게 고생을 하고 계실 것 같아 그 과정을 블로그 글로 작성하기로 했습니다.
* 저희의 서버는 Ubuntu 20.04 환경입니다. FastAPI로 만든 main.py가 정상 동작함을 uvicorn으로 확인했다고 가정하고 진행하겠습니다.
무료로 도메인 발급 받기
먼저 인스턴스의 public IP를 도메인으로 등록해줍시다. (만약 고정ip를 사용하실 예정이라면 이 과정을 생략해주세요.)
https://xn--220b31d95hq8o.xn--3e0b707e/
내도메인.한국 - 한글 무료 도메인 등록센터
한글 무료 도메인 내도메인.한국, 웹포워딩, DNS 등 무료 도메인 기능 제공
xn--220b31d95hq8o.xn--3e0b707e
이곳에서 맨 뒤에 .kr이 붙는 도메인을 무료로 등록할 수 있습니다.
회원가입 후 도메인 검색 창에 사용하고 싶은 도메인을 검색해봅시다.

다음과 같은 검색 결과가 뜨면 등록하기 버튼을 누릅니다.
보안코드를 입력 후, 등록하기 버튼을 누르면 다음과 같은 상세 설정 페이지가 뜹니다.

다음과 같이 입력한 후에, 보안코드를 입력하고 수정하기로 적용해줍시다.
이제 우리 서버의 도메인 주소가 발급되었습니다!
Gunicorn(구니콘) 설치하기
우분투 터미널을 종료해도, FastAPI서버가 동작하도록 만들고 싶습니다. uvicorn의 경우 터미널을 종료하면 서버도 종료됩니다. 그러나 Gunicorn을 사용하면 uvicorn 프로세스를 백그라운드로 실행할 수 있습니다.
서버환경에 Gunicorn을 설치해봅시다.
pip install gunicorn
설치 후 Gunicorn이 제대로 동작하는지 확인해야 합니다. 내 api가 저장되어있는 경로로 이동하고, 아래 명령어를 gunicorn명령어를 실행해봅시다. 정상 동작을 확인했다면 <Ctrl+C>를 눌러 Gunicorn을 종료해줍시다.
cd /home/ubuntu/fastAPI
gunicorn --bind 0:8000 main:app --worker-class uvicorn.workers.UvicornWorker
이러면 uvicorn 실행과 똑같은 게 아닌가 싶을 수 있지만, Gunicorn은 서비스로 등록이 가능해서, 시작과 종료를 쉽게 할 수 있을뿐만 아니라 인스턴스 서버를 시작할 때 자동으로 실행하도록 할 수 있습니다. 이제 서비스로 등록해봅시다.
Gunicorn 서비스 등록하기
이번엔 내가 작성한 API를 실행하도록 Gunicorn 서비스 파일을 먼저 작성해야합니다.
/etc/systemd/system/ 디렉토리에서, 아래 명령을 사용하여 관리자 권한으로 파일을 생성합시다.
sudo nano myapi.service #또는 sudo vim myapi.service
1. 가상환경을 이용하는 경우
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/projects/myapi #나의 api파일이 있는 디렉토리
ExecStart=/home/ubuntu/venvs/myapi/bin/gunicorn \ #gunicorn 위치
main:app \
--workers 2 \
--worker-class uvicorn.workers.UvicornWorker \
--bind unix:/tmp/myapi.sock
[Install]
WantedBy=multi-user.target
2. 가상환경을 사용하지 않은 경우 (저는 이 경우에 해당)
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory=/home/ubuntu/fastAPI #작성한 api파일이 있는 디렉토리
ExecStart=/home/ubuntu/.local/bin/gunicorn \ #gunicorn 위치
main:app \
--workers 2 \
--worker-class uvicorn.workers.UvicornWorker \
--bind unix:/tmp/myapi.sock
[Install]
WantedBy=multi-user.target
WorkingDirectory 에는 작성한 API 파일(ex. main.py)이 있는 경로를 작성해 주시면 되고,
ExecStart에는 gunicorn이 위치한 경로를 작성해주시면 됩니다.
이제 서비스를 실행하고 등록해봅시다.
sudo systemctl start myapi.service #서비스 시작
제대로 동작하는지도 확인을 해봐야겠죠? 아래 명령을 통해서 myapi.service가 정상 작동하는지 확인할 수 있습니다.
sudo systemctl status myapi.service #상태 확인
정상 작동 시)

오류가 난 경우에는 /var/log/syslog 에서 오류 원인을 확인하고 수정해야합니다.
Nginx 설치 및 설정하기
이제 정상동작을 확인하였으니, Nginx를 사용해서 api에 접속할 수 있도록 해보겠습니다.
Nginx를 관리자 권한으로 설치해줍시다.
sudo apt install nginx
nginx를 사용하기 위해 /etc/nginx/sites-available/ 로 이동하여 관리자 권한으로 Nginx 설정 파일을 하나 만들어 줍시다.
cd /etc/nginx/sites-available/
sudo nano myapi
그 후 myapi에 다음과 같은 내용을 작성해줍니다.
server {
listen 80;
server_name geuldobi.kro.kr; #내 도메인 주소 입력하기
#도메인 주소 없이 ip주소만 사용한다면
#server_name ip주소;
location = /favicon.ico { access_log off; log_not_found off; }
location / {
include proxy_params;
proxy_pass http://unix:/tmp/myapi.sock;
}
}
이제 Nginx가 이 myapi 설정 파일을 읽을 수 있도록 하기 위해 /etc/nginx/sites-enabled/ 로 이동하고, default를 삭제한 후, myapi를 링크해줍시다.
cd /etc/nginx/sites-enabled/
sudo rm default
sudo ln -s /etc/nginx/sites-available/myapi
ls 명령으로 myapi 링크만 남아있다면 성공입니다.
이제 Nginx 설정을 적용하기 위해 Nginx를 재시작해줍시다.
sudo systemctl restart nginx
그 후 정상 동작하는지 확인해 줍시다.
sudo nginx -t
정상 동작하지 않는 경우
1. Nginx 파일이 잘못되었거나
2. 재시작 되지 않거나
입니다.
1의 경우에는 설정파일을 다시 작성한 후, 아래 명령어를 입력하여 재시작해줍시다.
sudo systemctl stop nginx # 종료
sudo systemctl start nginx # 시작
위 과정을 거쳐도 동작하지 않는다면, 다른 문제일 수 있습니다.
그럴 경우 다음과 같이 강제 종료할 수 있습니다.
ps -ef | grep nginx (으로 포트 동작 확인)
sudo kill -9 port번호 #port번호에 nginx 포트번호 입력
이후 sudo systemctl start nginx 명령을 입력하여 다시 시작해주세요 .
정상적으로 동작한다면, 이제 포트번호 없이 도메인으로 접속이 가능합니다!
SSL 인증서 발급하기
고정 IP 대신 도메인을 입력하여 서버에 접속할수 있지만, 주소창을 보면 "주의 요함" 이라는 경고 메시지가 표시됩니다. 크롬 확장 앱은 인증기관으로부터 인증된 SSL 만을 허용기 때문에 HTTP 프로토콜을 사용하는 API는 사용할 수 없습니다. 따라서 SSL 인증서를 발급해주어야합니다. 다행히도, 무료로 SSL 인증서를 발급해 주는 Let's Encrypt 서비스가 있습니다. 이를 사용해봅시다.
다음과 같이 certbot을 설치해줍니다.
sudo apt install certbot
sudo apt install python3-certbot-nginx
그 후 다음 명령어를 입력하여 인증서 발급 절차를 밟습니다.
sudo certbot certonly --nginx
이후 나오는 안내대로 이메일 입력 -> 동의(a) -> 이메일(개인정보) 공유 허용(y) -> 도메인명(ex. geuldobi.kro.kr)을 입력합니다.
입력을 완료하면 인증서가 생성됩니다. 관리자 계정으로 전환한 후에 생성된 위치에 접근할 수 있습니다.
#생략하셔도 됩니다.
sudo -i #root 계정으로 로그인
cd /etc/letsencrypt/live/geuldobi.kro.kr
ls
#cert.pem chain.pem fullchain.pem privkey.pem README 파일들을 확인할 수 있습니다.
#exit 명령으로 관리자 모드 해제 가능
생성을 확인했다면,
이제 Nginx 설정을 확인해봅시다.
cat /etc/nginx/sites-available/myapi #내용 확인

이런식으로 적혀있다면 정상입니다!
(myapi에 적혀있지 않다면, default 파일을 한번 확인해봅시다. default에 적용되는 경우가 있는 것 같습니다. 이후 시간이 된다면 이 부분도 블로그로 정리해보도록 하겠습니다.)
설정이 바뀐것을 확인했다면 Nginx를 재시작 해줍시다.
sudo systemctl restart nginx.service
* 참고로 인스턴스의 방화벽 설정을 해주어야합니다. 클라우드 서비스에서 443포트를 규칙에 추가해서 허용했는지 확인해주세요!
이제 HTTPS로 API 요청을 할 수 있습니다! 아래와 같이 "주의 요함"이 뜨지 않고, 자물쇠 모양이 뜬다면 성공적으로 인증이 완료된 것입니다.

CORS 에러가 뜰 경우
* 여기서 만약 CORS(cross-origin resource sharing) 에러가 뜬다면...
/etc/nginx/sites-available/myapi파일(혹은 default)을 다시 열고, add_header Access-Control-Allow-Origin *; 을 다음과 같이 추가해주면 됩니다.
server {
#...내용 생략
location / {
add_header Access-Control-Allow-Origin *;
#... 생략
}
}
그러나, 보안상에 문제가 있을 수 있으니 테스트 용으로만 사용해주세요!