

피로그래밍 최종 프로젝트 중 개인적인 욕심(!)으로 베어메탈 서버를 통한 3-tier 배포를 도맡아 하게 되었는데, 여러가지로 우여곡절이 많았던 부분이라 블로그에 정리하고자 한다.
배포 과정을 설명하기 전에 간단한 사전 지식을 먼저 알고 가자!
가만히 보면, 단계별로 차근차근 따라가는 것임을 알 수 있다!
이제 본격적으로 배포를 해 봅시다!
1티어 배포는 개발 환경 그 자체이므로 2티어 배포부터 설명하겠다.
우선 AWS에 접속해서 DB용 인스턴스를 하나 생성하자!


AWS에 처음 회원가입하면 AWS 프리 티어 요금제를 1년 동안 쓸 수 있게 해주는데, 이 요금제에서 t2.micro 인스턴스 하나만 파면 한 달 750시간 무료라 사실상 공짜로 쓸 수 있다! 물론 인스턴스가 두 개 이상 되면 늘어난 인스턴스들이 750시간을 나눠 쓰게 되므로 과금이 발생할 수도 있다. 필자는 인스턴스 하나로 했다!
(엄밀히 말하면 앱이랑 서버가 한 인스턴스에 들어 있는 셈이니까 완전한 3티어 배포는 아닌 셈이다)
네트워크 설정까지 마치고 launch instance를 누르면 인스턴스가 생성된다.
이제 인스턴스가 생성되었을 건데, 생성된 인스턴스를 체크하고 connect to instance를 누른다.
그 후 터미널을 켜고 프라이빗 키 파일이 있는지 확인한다(프라이빗 키 생성시 자동으로 다운로드 되므로, 경로를 찾아주면 된다.)
(선택)
chmod 400 "프라이빗 키 이름.pem"
을 입력해서 보안적으로 강화할 수 있다. 그런 다음
ssh -i "프라이빗 키 이름.pem" ubuntu@ec2-아이피.리전.compute.amazonaws.com
명령어를 입력하면 서버를 켤 수 있다!
그 후 이 ubuntu 서버 컴퓨터에 사용할 데이터베이스를 받아준다. 필자는 postgresql로 했다!
아 참, 생성한 인스턴스의 Security로 넘어가서 인바운드 규칙을 수정해줘야 한다. 필자는 앱(Django)과 디비를 한 인스턴스에서 열었으므로, 각각 8000 포트와 5432포트를 열어주었다.

요렇게.
이제 ubuntu에 내 소중한 프로젝트 코드를 클론 받는다.
git clone 당신의 레포 주소!!
이때 SSH로 하면 오류가 발생할 수 있으니 반드시 레포 주소를 HTTP로 변경해서 입력하자!
클론을 받았다면 환경 변수 파일이 없을 테니, .env 파일을 만들어서 내 로컬 앱에 있던 환경 변수 내용들을 복붙해준다. 터미널 환경이니 vim을 써야 한다! 가상환경도 없을 테니 venv도 받아주자! venv를 받은 다음에는? 의존성 파일들을 받아 주어야 내 작고 이쁜 프로젝트가 잘 작동할 것이다! 여기까진 역사적으로 흘러가듯 가야 한다~

역사적으로 흘러가듯 가~
여기까지 하고 터미널에 python(3) manage.py runserver를 입력하면 이전과 똑같이 로컬 환경으로 서버를 열 수 있다. 하지만 python(3) manage.py runserver 0.0.0.0:8000 이렇게 하면? 외부 접속을 8000번 포트로 가능하게 해주는 것으로, 본인 AWS EC2 페이지에 있는 퍼블릭 IPv4 주소:8000 이렇게 검색하면 들어갈 수 있게 된다!
이 단계에서 사실 3-tier 배포 자체는 완료한 것으로 볼 수 있다.
다만 Django의 경우 DisallowedHost 에러가 발생할 텐데, 이는 당연한 것으로 이제 이걸 해결해보러 가보자!!
DisallowedHost 에러는 말 그대로 현재 접근한 호스트가 권한이 허락되지 않았다는 것을 뜻한다! 이걸 해결하기 위해선 Django 앱의 config/settings.py의 ALLOWED_HOSTS를 수정해줘야 한다.
// 자신의 프로젝트 디렉토리 내에서
sudo vi config/settings.py
그럼 settings.py가 뜰 텐데, 기본값은 DEBUG = True에 ALLOWED_HOSTS는 비어 있을 것이다. 여기다가 ALLOWED_HOSTS를 일단 *로 설정해둔다. 의미는 모든 호스트를 허용한다는 뜻으로, 개발 환경에서는 편의를 위해 사용하지만 실제 배포 단계에 가면 본인의 도메인으로 바꿔 설정해줘야 하고, DEBUG도 False로 바꿔줘야 한다.
이렇게 설정해주고 서버를 0.0.0.0:8000으로 다시 열면 접속 가능해질 것이다~!
실제 서버를 배포할 때는 runserver 명령을 사용하는 것이 아닌, WSGI를 사용해야 한다!
위스기가 뭐냐면,
위스기는 Web Server Gateway Interface인데, Nginx, Apache와 같은 서버들은 django, flask로 작성한 web application을 이해하지 못한다. 이 때 WSGI를 이용해 웹 서버와 우리가 작성한 애플리케이션이 서로 통신할 수 있다. (외국인과 나 사이에 번역가가 필요한 것과 비슷하다)
출처: 🤔WSGI는 무엇일까?
우리가 사용할 Gunicorn은 WSGI 기능을 가진 라이브러리이다~
사용하려면, 설치해야겠지?
pip install gunicorn
설치가 된 후에는 wsgi.py 파일이 존재하는 config 폴더가 존재하는 디렉토리로 이동한다.
gunicorn --bind 0:8000 config.wsgi:application
이건 뭐냐면 방금까지 runserver로 실행시켰던 서버를 gunicorn으로 실행시킨 것이다!! 다만 스타일이 깨질 것인데, 이것도 좀 이따 해결할거다!! 우선은
python manage.py collectstatic
를 통해 css 등 정적 파일들을 모은 staticfiles 디렉토리만 생성해놓자.
이제부터 중요하다!
cd /etc/systemd/system
sudo vi 프로젝트명.service
해당 경로에 프로젝트명.service 파일을 생성하고 다음 내용을 작성하자!
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=ubuntu
Group=ubuntu
WorkingDirectory="자신의 ubuntu 내 작업 디렉토리 경로"
EnvironmentFile="환경변수 위치"
ExecStart="gunicorn 설치 위치(보통 venv 내부)" \
--workers 2 \
--bind unix:/tmp/gunicorn.sock \
config.wsgi:application
[Install]
WantedBy=multi-user.target
WorkingDirectory 경로를 잘 적어주는게 중요하다!!!
ExecStart는 시작 명령어이고, Install은 서버 재시작 시 gunicorn도 재시작해주기 위한 명령어이다.
다 작성했으면 저장하고,
sudo systemctl start 프로젝트명.service
아무것도 안뜨면, 시작이 된 것이다! 그 다음
sudo systemctl enable 프로젝트명.service
를 입력하면 symlink가 생성되었다는 메시지가 출력된다. 참고로 enable은 시스템 부팅 시 서비스가 자동으로 start되도록 설정하는 것이다!! 편하겠다~~
잘 실행되는 지 궁금하다면?
sudo systemctl status 프로젝트명.service
여기서 초록 글씨로 active(running)이 뜨면 정상!!
슬슬 끝이 보인다...!
Nginx를 쓰는 이유는 다양한데, 연습인만큼 여기서는 정적 파일 서빙(staticfiles)을 제1의 목적으로 도입해보겠다~
사용하려면, 설치해야겠지?(2트)
sudo apt install nginx
설치한 후에 nginx에 웹 서버 설정파일을 만들어 줄거다!
sudo vi /etc/nginx/sites-available/
이 경로로 가보면 default 파일이 있는데, 여기서 기본적으로 80 포트에 대한 응대를 해주고 있어서, nginx를 설치하기만 해도 아까 그 포트로 접속하면
"Welcome to Nginx!" 라는 문구가 뜨게 된다~
이제 우리 서비스가 서빙되도록 새로운 설정 파일을 만들자!!
sudo vi /etc/nginx/sites-available/프로젝트명
생성을 하고, vi 편집기를 켜서 다음과 같이 입력한다.
server {
listen "서버를 열 포트";
server_name "IP 또는 도메인 이름";
location = /favicon.ico {
access_log off;
log_not_found off;
}
location /static/ {
alias "staticfiles 경로";
}
location / {
include proxy_params;
proxy_pass http://unix:/tmp/gunicorn.sock;
}
}
이렇게 설정을 마치고,
sudo ln -s /etc/nginx/sites-available/프로젝트명 /etc/nginx/sites-enabled/
이 명령어를 통해 symlink를 연결해준다. 프로젝트명과 /etc 사이 공백에 유의하자!!! 이렇게 되면, 시스템 부팅 시에 nginx도 자동으로 시작하게 되는 것이다~~
(enabled에서 눈치챘다면 훌륭하다 아쎄이!!)
이렇게 해준 후
ls /etc/nginx/sites-enabled/
를 입력하면 디렉 내 폴더 구조가
프로젝트명 default
이렇게 아주 이쁘게 자리하고 있을 것이다. 그 후 start nginx, enable nginx, restart nginx 하면 접속이 될 것이다!!!
물론 아직 끝나지 않았다. 들어가보면 여전히 스타일이 깨져 있을 것이고, 콘솔을 열면 style.css 파일에 403 에러가 떠 있을 거다!! 이제 이걸 해결하러 가보쟈.
403 에러는 뭘까? 이것 역시 권한 관련 에러로, nginx가 static 파일들은 찾았지만, 접근을 못하고 있다는 상태를 말한다! 이를 해결하기 위해선 nginx.conf 파일에서 권한을 부여해주면 된다.
sudo vi /etc/nginx/nginx.conf
들어가면, 맨 첫 줄에
user www-data;
이렇게 적혀 있을 건데, 여기에 유저네임을 입력해주자! 우리는 지금 따로 설정해주지 않았기 때문에 ubuntu 서버 기본 유저네임인 ubuntu를 입력하면 된다!
참고로 유저네임은 터미널에
whoami
라고 입력하면 알 수 있다.
그럼 이제 ubuntu를 www-data 옆에 적고 저장해준 뒤 nginx를 재시작해주면 끝!!!

끝인줄 알았는가? 현재 당신의 사이트는 굉장히 취약하다! 왜냐하면 https가 적용되지 않았기 때문이다! HTTPS란 웹 브라우저와 웹 서버 간의 통신을 암호화하는 보안 프로토콜로, 다음과 같은 이점을 제공한다.
참고로 https로 접근하려면 도메인이 필수인데, 있다고 가정하겠다.
내도메인.한국 이곳에 들어가면 무료 도메인을 받을 수 있는데, 실습이나 테스트 때 아주 유용하다!!
sudo snap install core
sudo snap refresh core
sudo apt remove certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo certbot --nginx
이러면 이메일을 입력하라고 뜨는데, 이는 ssl 인증이 3개월 마다 갱신해야 하는데 이걸 이메일로 보내주기 때문이다. 이메일을 오타 없이 잘 입력하도록 하자!! 이러면 nginx에 설정된 도메인을 자동으로 읽어와서 보여주고, 여기다 ssl 인증 할래? 라고 물어본다!!
Successfully 어쩌구가 뜨면 끝!!!
이렇게 하면 모든 과정이 끝났다! 축하합니다! 이제 당신도 배포 장인!!!
