nginx, docker compose를 이용한 리버스 프록시 구성

chickenfondue·2022년 1월 20일
10

개요

진행중인 프로젝트의 초기 PoC 버전 배포를 위해 AWS EC2 인스턴스 1대에 nginx(web server), web(frontend), api(backend) 총 3개의 컨테이너를 띄우고 nginx 웹서버를 구성해 서버사이드에 리버스 프록시 형태로 web, api 접근을 라우팅 하도록 구성하려고 한다.

구성

전체 구성은 다음과 같다.

구성도

http 포트(80) 에 nginx 를 오픈하고 3000번 포트에 프론트엔드, 8080포트에 백엔드를 구성하였고 / 경로로 오는 호출은 프론트엔드로, /api 경로로 오는 호출은 백엔드로 포워딩 해줄 수 있도록 하는 구성이다.

프론트엔드와 백엔드간의 통신은 public domain을 사용해 이루어지게 되는데 이는 백엔드 API가 외부에 오픈되야하는 API는 아니지만 개발 단계에서는 비용 절감을 위해 한개의 호스트에서 돌아가고 향후에는 별도의 호스트에서 동작하게 될 것이기 때문에 프론트엔드와 백엔드 간의 통신은 외부 호스트와 통신을 하는 것 처럼 동작하게 된다. 만약 한개의 호스트에 올라가 동작할 컨테이너들인 경우 내부 통신을 활용해 구성하여도 무방하다.

개발 환경(host 1대 구성)
개발 환경(host 1대 구성)

개발 환경(host 여러대 구성)
운영 환경(각각 host 구성)

nginx 설정

nginx가 리버스 프록시 역할을 하도록 다음과 같이 nginx.conf 파일을 설정해 준다. upstream 설정은 docker-compose에서 설정한 서비스명을 사용하여 <서비스명>: 로 지정한다. 도커에서 같은 네트워크 상의 컨테이너와 통신하는 방법이다.

nginx.conf

user  nginx;
worker_processes  auto;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}
http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

	# 백엔드 upstream 설정
    upstream myweb-api {
        server api:8080;
    }

	# 프론트엔드 upstream 설정
    upstream myweb-web {
        server web:3000;
    }

    server {
        listen 80;

		# /api 경로로 오는 요청을 백엔드 upstream 의 /api 경로로 포워딩
        location /api {
            proxy_pass         http://myweb-api/api;
        }

		# / 경로로 오는 요청을 프론트엔드 upstream 의 / 경로로 포워딩
        location / {
            proxy_pass         http://myweb-web/;
        }
    }
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  65;
    include /etc/nginx/conf.d/*.conf;
}

docker-compose 설정

docker-compose.yaml 은 다음과 같이 구성해준다. proxy 폴더에 있는 nginx.conf를 nginx 컨테이너에 포함시킨다.

폴더 구조

/
docker-compose.yaml
proxy/
ㄴ nginx.conf

docker-compose.yaml

version: '3'
services:
  nginx:
    image: nginx:1.21.5-alpine
    ports:
      - 80:80
    volumes:
      - ./proxy/nginx.conf:/etc/nginx/nginx.conf
    container_name: myweb-proxy
    depends_on:
      - web
      - api
  web:
    image: frontend-web:latest
    ports:
      - 3000:3000
    container_name: myweb-web
  api:
    image: backend-api:latest
    ports:
      - 8080:8080
    container_name: myweb-api

여기서 주의해야 할 점은 프론트엔드와 백엔드 컨테이너의 포트 설정 또한 외부로 열어 주어야 한다는 점이다. docker-compose의 포트 설정은 expose와 ports 두가지가 있는데 expose로 설정할 경우 같은 도커 네트워크 인터페이스 안에 있는 컨테이너 에서만 접근을 할 수 있고 ports로 포트 맵핑을 해줘야 외부에서 해당 포트를 통해 접근 할 수 있게 된다. 처음에는 외부와의 접점은 nginx 이고 나머지는 직접적으로 외부에 노출되지 않으니 expose로 구성을 하였으나 프론트엔드가 public domain을 통해 백엔드에 request를 날리는 과정에서 response가 오지 않는 문제가 발생하였다. 프론트엔드 컨테이너의 포트가 오픈되어 있지 않기 때문에 외부에서 직접 접근이 불가능 하여 발생한 문제로 보인다.

컨테이너 내부에서 직접 외부와 통신을 할 일이 전혀 없다면 상관 없지만 그렇지 않다면 리버스 프록시 뒤에 있는 컨테이너라 할지라도 반드시 ports 를 이용해 구성을 하기를 권장한다.

결과

위와 같이 구성 후 docker-compose up 커맨드로 모든 컨테이너를 띄우고 root 도메인 (/) 으로 접속이 정상적으로 이루어지고 프론트엔드에서 백엔드의 API를 통해 데이터를 제대로 정상적으로 이루어지는 것을 확인 할 수 있다.

1개의 댓글

comment-user-thumbnail
2023년 1월 30일

좋은 글 감사합니다. 위 처럼 3개의 컨테이너로 구성된 상황이라면 ports를 통해서 host와 docker 의 port 를 매핑해주어야 하지만 nginx와 reverse proxy 로 묶인 docker의 container 가 같은 물리적 기기에 있는 상태라면 expose 만으로도 동작이 가능해서 혹시라도 단일 하드웨어로 구성하실 분들은 그냥 expose 만 해주시면 되며, 본문 처럼 nginx 와 frontend , backend 서버가 따로 구성되어있어서 외부로 노출 시켜야하는경우 -p 3000:3000 보다는 nginx 의 서버 공인 ip가 111.222.111.2 인 경우 -p 111.222.111.2:3000:3000 으로 구성을 해주시면 nginx 서버쪽에서만 frontend와 backend 서버로 다이렉트 접근이 가능하기 때문에 좀 더 보안에 신경쓴 설정이 가능합니다.

답글 달기