Profile picture

[Docker] Django 블로그 Nginx Proxy Manager로 서빙하기

JaehyoJJAng2023년 05월 16일

◾️ 개요

Django 기반 블로그가 AWS Lightsail에서 호스팅 되고 있는 상황이다. image
현재 배포되고 있는 구조는 위 사진과 같다.

사용자가 www.waytothem.com 또는 waytothem.com으로 접속하면 내부 컨테이너인 nginx가 요청을 받게되고 내부 django app으로부터 데이터를 넘겨주게된다.

이 구성은 도커로 서비스 구성시 매우 간단하고 흔하게 볼 수 있는 구성이다.

그러나 최근 Nginx Proxy Manager라는 좋은 오픈소스를 알게되어 해당 이미지를 현재 배포된 Django 프로젝트에 적용해보려고 한다.

💥 Nginx Proxy Manager에 대한 자세한 설명은 아래 게시글로 들어가면 실습과 함께 해당 개념에 대해 이해할 수 있다.
[Docker] Nginx Proxy Manager(NPM) 구축하기


Nginx Proxy Manager를 적용하게 됨으로써 배포 구성도는 아래와 같이 변경될 예정이다.
image


◾️ 프로젝트 소스코드

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으로 접속해보자.
image
초기 실행 시 계정 정보는 다음과 같다. admin@example.com / changeme

초기에 로그인을 하게되면 계정명, 이메일 주소, 패스워드를 모두 변경해야 한다.


1. proxy hosts 등록

프록시 호스트를 등록하기 위해 Dashboard - Proxy Hosts로 들어가도록 하자.
image


'Add Proxy Hosts'를 클릭하고 필수 정보를 입력하도록 하자.
image
image
다 작성하고 'save'를 누르면 리버스 프록시 설정은 끝이다!

  • Domain Names: 가비아,Route53,CloudFlare 등에서 구입한 도메인 이름
  • Foward Hostname / IP: Docker Compose에서 작성했던 컨테이너 이름
  • Foward Port: Docker container 내부 Port

이제 도메인 주소를 브라우저에 입력한 후 접속해보자.
image
서비스에 정상적으로 접속이 되었다 ..

하지만 뭔가 이상하다. 정적 파일들이 적용되지 않은 것 같다!
우선 이 문제는 🔺 트러블슈팅에서 다시 다루는 걸로 하고

SSL 인증서를 적용하러 가보자.


2. SSL 인증서 적용

이전에 nginx 컨테이너를 올려서 배포할 때는 certbot 컨테이너를 생성해야했고, init-letsencrpt.sh 파일도 다운로드 받아서 군데군데 수정해줘야 했고 굉장히 귀찮은 작업의 연속이었는데

Nginx Proxy Manager는 딸칵 한 번이면 될 정도로 SSL 인증서를 정말 쉽게 인증 받을 수 있다!

먼저 이전에 만든 Proxy hosts의 'edit' 창으로 가서 SSL 탭으로 이동해보자.
image


그럼 다음과 같이 SSL Certificate를 선택할 수 있는데
image
나는 이전에 SSL 인증서를 한 번 만들어봤기에 인증서 기록이 아직 남아있다.


'Request a new SSL Certificate'를 클릭하면 여러 옵션들이 뜬다.
image
다음과 같은 옵션들을 선택하고 'save'를 누르면 끝이다.

각 옵션들에 대한 설명은 여기서 하지 않겠다. 궁금하면 GPT를 사용하자.


그럼, 다음과 같이 hosts의 SSL 상태가 Let's Encrypt로 나오게 된다.
image


정상적으로 발급됐는지 브라우저에 도메인을 입력하여 접속해보자.
image
정상적으로 발급되었다! ☺️


🔺 트러블슈팅

1. 정적 파일 이슈


현재 배포된 블로그에 정적 파일들이 적용되지 않는다.

nginx proxy manager를 도입하기 전에는 로컬에서 nginx.conf 파일을 직접 수정하여 static 파일 및 media 파일의 서빙 경로를 직접 지정한 후 nginx 컨테이너의 /etc/nginx/conf.d/nginx.conf 파일로 마운트했었다.
image


그러나, 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 파일에 들어가게 되는 것이었다.
image


그 설정들 또한 Proxy Hosts의 Advanced 탭에서 추가할 수 있다!
image
이렇게 설정을 추가한 후 save를 해주자.


설정을 추가해주고 2.conf 파일을 읽어보면 위에서 추가한 설정이 그대로 들어간 것을 확인할 수 있을 것이다.

cat ./data/nginx/proxy_host/2.conf

image


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를 다시 확인해보자.
image
정말로 '/'를 빼먹었다!

원래라면 아래처럼 들어갔어야 했다.

location /static/ {
    alias /usr/src/app/_static/;
}

location /media/ {
    alias /usr/src/app/_media/;
}

수정하고 다시 접속하니 정상적으로 static이 로드된다!
image


Loading script...