◾️ 개요
Django 기반 블로그가 AWS Lightsail에서 호스팅 되고 있는 상황이다.
현재 배포되고 있는 구조는 위 사진과 같다.
사용자가 www.waytothem.com
또는 waytothem.com
으로 접속하면 내부 컨테이너인 nginx가 요청을 받게되고 내부 django app으로부터 데이터를 넘겨주게된다.
이 구성은 도커로 서비스 구성시 매우 간단하고 흔하게 볼 수 있는 구성이다.
그러나 최근 Nginx Proxy Manager라는 좋은 오픈소스를 알게되어 해당 이미지를 현재 배포된 Django 프로젝트에 적용해보려고 한다.
💥 Nginx Proxy Manager에 대한 자세한 설명은 아래 게시글로 들어가면 실습과 함께 해당 개념에 대해 이해할 수 있다.
[Docker] Nginx Proxy Manager(NPM) 구축하기
Nginx Proxy Manager를 적용하게 됨으로써 배포 구성도는 아래와 같이 변경될 예정이다.
◾️ 프로젝트 소스코드
https://github.com/JaehyoJJAng/Python_Web_Development_2022
◾️ 사전 준비
1. 도커 네트워크 생성
아래 명령어를 사용하여 도커 네트워크를 생성하자. 도커 네트워크를 생성하면 다른 docker compose에서 앱을 npm 리소스에 붙일 때 networks
옵션만 주면되서 되게 간편해진다.
docker network create npm-net
2. 가비아 도메인
리버스 프록시를 하기 위해서는 도메인이 필요하다.
나는 가비아에서 500원 짜리 .store 도메인을 구입하였다.
추후 dozzle, loki, grafana 등의 앱들도 서브 도메인을 생성하여 리버스 프록시를 적용 할 예정이니 서브 도메인 설정도 해야한다.
관련 방법은 아래 게시글을 참고하자.
가비아 서브 도메인 설정하기 - WTT Devlog
◾️ Docker-Compose
version: '3.2'
services:
npm:
image: 'jc21/nginx-proxy-manager:latest'
depends_on:
- "db"
restart: always
volumes:
- "./data:/data"
- "./letsencrypt:/etc/letsencrypt"
- type: volume # _static 연동
source: "static_volume"
target: "/usr/src/app/_static"
- type: volume # _media 연동
source: "media_volume"
target: "/usr/src/app/_media"
ports:
- "80:80"
- "81:81"
- "443:443"
environment:
- "TZ=Asia/Seoul"
- "DB_MYSQL_HOST=db"
- "DB_MYSQL_PORT=3306"
- "DB_MYSQL_USER=npm"
- "DB_MYSQL_PASSWORD=npm"
- "DB_MYSQL_NAME=npm"
networks:
- "npm-net"
container_name: npm
db:
image: 'jc21/mariadb-aria:latest'
restart: always
environment:
MYSQL_ROOT_PASSWORD: "npm"
MYSQL_DATABASE: "npm"
MYSQL_USER: "npm"
MYSQL_PASSWORD: "npm"
volumes:
- type: volume
source: "my-data"
target: "/var/lib/mysql"
networks:
- "npm-net"
container_name: db
web:
image: yshrim12/wtt-web:latest
restart: always
command: gunicorn do_it_django_prj.wsgi:application --bind 0.0.0.0:8000
volumes:
- type: volume # _static 연동
source: "static_volume"
target: "/usr/src/app/_static"
- type: volume # _media 연동
source: "media_volume"
target: "/usr/src/app/_media"
- type: bind
source: "./WTT"
target: "/usr/src/app"
expose:
- "8000"
env_file:
- ".env/.env.prod"
depends_on:
- "web-db"
networks:
- "npm-net"
container_name: web
web-db:
image: postgres:12.0-alpine
restart: always
volumes:
- type: volume
source: "postgres_data"
target: "/var/lib/postgresql/data"
env_file:
- ".env/.env.prod.db"
networks:
- "npm-net"
container_name: web-db
networks:
npm-net:
name: npm-net
external: true
volumes:
postgres_data: {}
my-data: {}
static_volume: {}
media_volume: {}
80,443은 웹 서비스를 위한 포트이고, 81번의 경우 어드민 페이지임.
1. 컨테이너 실행
작성이 끝났으면 docker-compose up -d --build
명령어로 컨테이너를 생성하도록 하자.
NAME COMMAND SERVICE STATUS PORTS
db "/scripts/run.sh" db running 3306/tcp
npm "/init" npm running 0.0.0.0:80-81->80-81/tcp, 0.0.0.0:443->443/tcp, :::80-81->80-81/tcp, :::443->443/tcp
web "gunicorn do_it_djan…" web running 8000/tcp
web-db "docker-entrypoint.s…" web-db running 5432/tcp
◾️ NPM 접속
컨테이너가 모두 정상적으로 실행됐다면 http://<IP_또는_도메인주소>:81
으로 접속해보자.
초기 실행 시 계정 정보는 다음과 같다. admin@example.com
/ changeme
초기에 로그인을 하게되면 계정명, 이메일 주소, 패스워드를 모두 변경해야 한다.
1. proxy hosts 등록
프록시 호스트를 등록하기 위해 Dashboard - Proxy Hosts로 들어가도록 하자.
'Add Proxy Hosts'를 클릭하고 필수 정보를 입력하도록 하자.
다 작성하고 'save'를 누르면 리버스 프록시 설정은 끝이다!
- Domain Names: 가비아,Route53,CloudFlare 등에서 구입한 도메인 이름
- Foward Hostname / IP: Docker Compose에서 작성했던 컨테이너 이름
- Foward Port: Docker container 내부 Port
이제 도메인 주소를 브라우저에 입력한 후 접속해보자.
서비스에 정상적으로 접속이 되었다 ..
하지만 뭔가 이상하다. 정적 파일들이 적용되지 않은 것 같다!
우선 이 문제는 🔺 트러블슈팅에서 다시 다루는 걸로 하고
SSL 인증서를 적용하러 가보자.
2. SSL 인증서 적용
이전에 nginx 컨테이너를 올려서 배포할 때는 certbot 컨테이너를 생성해야했고, init-letsencrpt.sh 파일도 다운로드 받아서 군데군데 수정해줘야 했고 굉장히 귀찮은 작업의 연속이었는데
Nginx Proxy Manager는 딸칵 한 번이면 될 정도로 SSL 인증서를 정말 쉽게 인증 받을 수 있다!
먼저 이전에 만든 Proxy hosts의 'edit' 창으로 가서 SSL 탭으로 이동해보자.
그럼 다음과 같이 SSL Certificate를 선택할 수 있는데
나는 이전에 SSL 인증서를 한 번 만들어봤기에 인증서 기록이 아직 남아있다.
'Request a new SSL Certificate'를 클릭하면 여러 옵션들이 뜬다.
다음과 같은 옵션들을 선택하고 'save'를 누르면 끝이다.
각 옵션들에 대한 설명은 여기서 하지 않겠다. 궁금하면 GPT를 사용하자.
그럼, 다음과 같이 hosts의 SSL 상태가 Let's Encrypt로 나오게 된다.
정상적으로 발급됐는지 브라우저에 도메인을 입력하여 접속해보자.
정상적으로 발급되었다! ☺️
🔺 트러블슈팅
1. 정적 파일 이슈
현재 배포된 블로그에 정적 파일들이 적용되지 않는다.
nginx proxy manager를 도입하기 전에는 로컬에서 nginx.conf 파일을 직접 수정하여 static 파일 및 media 파일의 서빙 경로를 직접 지정한 후 nginx 컨테이너의 /etc/nginx/conf.d/nginx.conf 파일로 마운트했었다.
그러나, nginx proxy manager를 도입한 지금은 정적 파일 서빙 설정을 어디다가 해주어야 하는걸까? 동일하게 npm 컨테이너의 /etc/nginx/conf.d 경로에 설정 파일을 만들어주고 마운트 시켜줘야하나?
검색 결과 명쾌한 답을 찾을 수 있었다.(https://dimensionquest.net/2021/02/host-static-site-on-npm/)
Proxy hosts를 등록하게 되면 ./data/nginx/proxy_host/ 경로에 등록한 순서대로 1.conf, 2.conf .. 형식으로 파일이 생성되는데, proxy hosts를 등록할 때 기입했던 필수 정보들에 대한 설정들이 모두 .conf 파일에 들어가게 되는 것이었다.
그 설정들 또한 Proxy Hosts의 Advanced 탭에서 추가할 수 있다!
이렇게 설정을 추가한 후 save를 해주자.
설정을 추가해주고 2.conf 파일을 읽어보면 위에서 추가한 설정이 그대로 들어간 것을 확인할 수 있을 것이다.
cat ./data/nginx/proxy_host/2.conf
2. logs 확인
그러나 여전히 정적 파일이 적용이 되지 않고 있다!.
원인이 궁금해 로그 파일을 찾기 시작했다.
find . -name "*.log" -type f 2>/dev/null
./data/logs/fallback_access.log
./data/logs/proxy-host-2_error.log
./data/logs/proxy-host-1_error.log
./data/logs/proxy-host-1_access.log
./data/logs/fallback_error.log
./data/logs/letsencrypt-requests_access.log
./data/logs/letsencrypt-requests_error.log
./data/logs/proxy-host-2_access.log
npm에서 등록된 proxy host들의 로그는 아무래도 ./data/logs
에 저장되나 보다.
proxy_host-2에서 발생한 에러 로그를 읽어보았다.
cat ./data/logs/proxy-host-2_error.log
2023/12/08 23:44:03 [error] 212#212: *57 open() "/usr/src/app/_staticblog/css/footer.css" failed (2: No such file or directory), client: 125.186.88.183, server: waytothem.store, request: "GET /static/blog/css/footer.css HTTP/1.1", host: "waytothem.store", referrer: "http://waytothem.store/"
2023/12/08 23:44:03 [error] 212#212: *57 open() "/usr/src/app/_staticsingle_pages/images/mini_007.png" failed (2: No such file or directory), client: 125.186.88.183, server: waytothem.store, request: "GET /static/single_pages/images/mini_007.png HTTP/1.1", host: "waytothem.store", referrer: "http://waytothem.store/"
"/usr/src/app/_staticsingle_pages/images/..."
이쪽 부분이 조금 이상하다. 아무래도 '/' 를 빼먹은 것 같다.
Proxy hosts 페이지로 이동하여 Advanced를 다시 확인해보자.
정말로 '/'를 빼먹었다!
원래라면 아래처럼 들어갔어야 했다.
location /static/ {
alias /usr/src/app/_static/;
}
location /media/ {
alias /usr/src/app/_media/;
}
수정하고 다시 접속하니 정상적으로 static이 로드된다!