하나의 서버에서 여러개의 도메인에 대한 요청을 처리하기 위해서는 nginx나 apache등의 도메인별 설정을 이용해서 구현이 가능하다. 최근 보안 이슈로 HTTP만으로 웹서비스를 제공하는 경우는 거의 없기에 HTTPS를 위한 인증서를 같이 제공해야하는데, 이는 Let's encrypt 등의 인증기관을 이용하면 무료로 쉽고 빠르게 설정할 수 있다.
다만, 도메인별 설정과 인증서 설정을 웹UI를 통해 쉽게 설정할 수 있는 패키지가 있으니, Nginx Proxy Manager(이하 npm)가 그것이다.
Npm은 Nginx의 Reverse Proxy 기능을 이용해서 뒷단(upstream)에서 제공할 다양한 웹 기반의 서비스들의 첫 출입구를 담당해준다. 특징을 몇가지 적어보면:
- 여러 도메인들에 대한 설정을 UI를 기반으로 쉽게 설정할 수 있다.
- SSL termination을 제공하여 뒷단 서버와의 소통을 HTTP만 하게 함으로 뒷단 서버의 설정을 간단하게 할 수 있다.
- HTTP/2, HSTS를 지원하지 않는 웹서버라도, proxy 단에서 지원한다. 또한, websocket를 지원하는 뒷단 서버에게 websocket을 연결한다.
- nginx의 server, location 등에 추가해야하는 설정들(proxy_pass, proxy_set_header 등)을 추가로 설정할 수 있다.
- 설정을 저장할 때마다 nginx reload를 실행하고 online/offline 등 상태를 보여주어 설정을 즉시 적용하고 상태를 확인할 수 있다.
- 다만, 이것은 단점이 되기도 하는데, 잘못된 설정이 저장되었을 때 그 설정이 바로 적용되어 서비스에 문제가 될 수 있다.
- Docker로 쉽게 사용할 수 있도록 Dockerfile 및 docker-compose 예제파일을 제공하며, dockerhub에서도 이미지를 제공(2024/04 기준 167M docker pulls)하고 있다.
- Docker 이미지는 64비트 아키텍쳐(arm64) 뿐 아니라 M1, M2, M3등 Apple silicon(arm64)을 위한 아키텍쳐도 지원한다.
- 2024년에 들어서는 버젼 release가 빠른 편은 아니지만, 코드는 자주 업데이트 되는 편이다.
여기서는 집에서 소규모로 작게 운영하는 웹사이트들을 가정하고 설명하기로 한다. 실제 production 환경에서 사용하기 위해서는 더 나은 솔루션(AWS의 경우, WAF + ALB SSL termination 등)이 있기 때문에, 높은 트래픽과 높은 가용성이 필요로 하는 환경보다는 낮은 트래픽에 조금 더 쉽게 사용할 수 있는 운영환경에 초점을 맞추고 있다. 또한, 개인적으로 사용하는 homelab에는 서비스들을 docker container로 운영하고 있지 때문에, 여기서도 docker-compose를 이용하여 docker container로 운영하는 방식을 기준으로 설명한다. Docker container들끼리는 docker network로 통신하면 되므로, 개별 서비스에 직접 접근해야하지 않는 이상 각 서비스의 포트를 호스트에 직접 노출시키지 않아도 된다.
네트워크 설정
가정집에서 사용 중인 인터넷 환경을 고려해보면, 대개 다음과 같다.
공유기에서 서버로 TCP 80/443 포트가 모두 서버로 향하게 설정하는 것이 필요하다. 또한, 서버에서도 npm이 직접 80과 443번 포트를 점유할 수 있도록 설정해야한다. (사용하는 OS환경에 따라 80/443 포트를 이용하기 위해서는 root 권한이 필요할 수 있다.)
서버에서는 HTTP로 통신하는 여러 서비스들이 docker container 위에서 운영 중인 것으로 가정한다.
- npm과 별개로 웹서비스를 제공하기 위한 nginx 혹은 apache (example.com 80 포트)
- S3와 호환되는 고성능 스토리지 서비스인 MinIO (static.example.com, 9000 포트)
- jupyter notebook (nb.example.com, 8080 포트)
Docker compose
# docker-compose.yml
version: '3.8'
services:
npm:
container_name: npm
image: jc21/nginx-proxy-manager:2.11.1
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
# MySQL/Maria DB가 준비되어 있지 않다면, 아래 environment항목을 삭제 혹은 comment out
environment:
DB_MYSQL_HOST: ${NPM_DB_MYSQL_HOST}
DB_MYSQL_PORT: ${NPM_DB_MYSQL_PORT}
DB_MYSQL_USER: ${NPM_DB_MYSQL_USER}
DB_MYSQL_PASSWORD: ${NPM_DB_MYSQL_PASSWORD}
DB_MYSQL_NAME: ${NPM_DB_MYSQL_NAME}
volumes:
- ./nginx-proxy-manager/data:/data
- ./nginx-proxy-manager/letsencrypt:/etc/letsencrypt
healthcheck:
test: ["CMD", "/bin/check-health"]
interval: 10s
timeout: 3s
networks:
- default
# 마찬가지로, MySQL 혹은 Maria DB가 준비되어 있지 않다면, 아래 db-network 부분을 삭제
- db-network
networks:
default:
name: ${NETWORK_NAME}
# 마찬가지로, MySQL/Maria DB가 준비되어 있지 않다면, 아래 db-network 부분을 삭제
# MySQL/Maria DB를 사용하고, 해당 서비스가 docker container에서 특성 network를 사용 중인 것을 가정
db-network:
name: ${NETWORK_NAME_STORAGE}
external: true
# .env 파일 - docker-compose.yml에서 사용될 변수들을 정의한다.
NPM_DB_MYSQL_HOST=
NPM_DB_MYSQL_PORT=
NPM_DB_MYSQL_USER=
NPM_DB_MYSQL_PASSWORD=
NPM_DB_MYSQL_NAME=
NETWORK_NAME=
NETWORK_NAME_STORAGE=
- jc21/nginx-proxy-manager 이미지를 사용한다. (2024년 4월 기준으로 2.11.1이 최신)
- 80/443 포트는 reverse proxy 자체를 위해서, 81번 포트는 reverse proxy 설정을 위한 관리페이지의 접근을 위해서 열어둔다.
- 80번 포트를 열기 위해서 root 권한이 필요할 수 있다.
- volumes 항목에서 nginx-proxy-manager가 사용할 data 디렉토리와 인증서 디렉토리(letsencrypt)를 호스트의 특정 디렉토리를 사용하도록 하고 있다.
- environment 항목에서는 npm이 여러 도메인에 대한 설정을 MySQL에 저장하도록 하고 있다. 만약 MySQL 등이 준비되어 있지 않다면 아래 항목들을 삭제한다. MySQL를 따로 준비하지 않았다면, 이 docker compose 안에 추가해도 되지만, 없어도 sqlite를 사용해서 잘 동작한다
- environment 항목 전체를 삭제
- services > npm > networks > db-network 항목을 삭제
- networks > db-network 항목 전체를 삭제
준비가 되면, docker compose 명령을 이용해서 서비스를 실행시킨다.
docker compose up -d
# docker 버젼에 따라서는
# docker-compose up -d
관리도구 설정
도메인별 proxy 설정을 추가하기 위해서는 81번 관리포트로 접근한다. 80/443, 그리고 81번 포트를 열어두었기 때문에 웹브라우져로 http://<서버 주소>:81 를 통해 접속한다. Mac이나 리눅스 개발 환경에서 직접 설정했을 경우, http://localhost:81 혹은 http://127.0.0.1:81 로 접속한다.
처음 로그인 시에는 이메일주소 admin@example.com / 비밀번호 changeme 로 접속한다. 첫 접속 후, 관리자 계정의 이름과 이메일 주소, 비밀번호를 설정한다.
다음 글에서는 실제로 proxy 설정하는 방법에 대해서 다루어본다.