[Docker][Web] Nginx의 Reverse Proxy를 이용한 분산처리(2)

이수진·2022년 2월 12일

저번시간에 이어서 이번에도 Nginx의 Reverse Proxy를 이용한 분산처리입니다.
이번에는 경로(URL)로 구분하여 내부 서버들을 라우팅해보도록 하겠습니다.

먼저 구성한 아키텍처부터 소개하겠습니다.

1. 아키텍처

과장해서 구성해보았는데,
총 4개의 서비스(app1, app2, app3, app4)가 있고 이 서비스들을 각각 독립적인 서버(nginx1, nginx2, apache1, apache2)에서 실행하고 있는 것입니다.

앞단의 웹 서버인 nginx_proxy는 80포트만 개방을 했구요, 기본인 80:80으로 설정하였습니다.

그리고 뒷 단의 서버들을 모두 경로로 구분하였습니다.

앞단의 nginx_proxy서버와 뒷 단의 4개의 서버를 모두 독립적인 컨테이너로 만들어 실행시켰습니다.

2. 파일 구조

파일 구조는 다음과 같습니다.

  • 파일구조
root
│
└───docker-compose.yml
│
└───nginx
    └───nginx.conf

3. docker-compose.yml , nginx.conf 설정

  • docker-compose.yml
version: "3"

services:
    nginx_proxy: # 컨테이너 1
        image: nginx:1.18.0
        ports:
            - "80:80"
        restart: always
        volumes:
            - "./nginx/nginx.conf:/etc/nginx/nginx.conf" # 직접 설정한 nginx.conf로 기존 default nginx.conf를 대체

    nginx1: # 컨테이너 2
        depends_on:
            - nginx_proxy
        image: nginx:1.18.0
        restart: always

    nginx2: # 컨테이너 3
        depends_on:
            - nginx_proxy
        image: nginx:1.18.0
        restart: always

    apache1: # 컨테이너 4
        depends_on:
            - nginx_proxy
        image: httpd:2.4.46
        restart: always
        
    apache2: # 컨테이너 5
        depends_on:
            - nginx_proxy
        image: httpd:2.4.46
        restart: always
  • nginx.conf

제가 추가적으로 설정한 부분은 다음과 같습니다.

.
.
.
    upstream docker-nginx1 {
        server nginx1:80;
    }

    upstream docker-nginx2 {
        server nginx2:80;
    }

    upstream docker-apache1 {
        server apache1:80;
    }

    upstream docker-apache2 {
        server apache2:80;
    }

    server {
        listen 80;

        location /app1/ {
            proxy_pass         http://docker-nginx1;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }

        location /app2/ {
            proxy_pass         http://docker-nginx2;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }

        location /app3/ {
            proxy_pass         http://docker-apache1;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }

        location /app4/ {
            proxy_pass         http://docker-apache2;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
    }

}

4. 도커 컨테이너 생성 및 실행

docker-compose up -d 명령어를 통해 컨테이너를 생성하고,
docker ps 명령어를 통해 현재 실행중인 컨테이너를 확인해보면 다음과 같습니다.

ubuntu@ip-172-31-38-68:~$ docker-compose up -d
Building with native build. Learn about native build in Compose here: https://docs.docker.com/go/compose-native-build/
Creating nginx_proxy_1 ... done
Creating nginx1_1      ... done
Creating apache2_1     ... done
Creating apache1_1     ... done
Creating nginx2_1      ... done
ubuntu@ip-172-31-38-68:~/04_NGINX_PROXY_PATH$ docker ps
CONTAINER ID   IMAGE          COMMAND                  CREATED         STATUS         PORTS                               NAMES
25ee28f263ff   nginx:1.18.0   "/docker-entrypoint.…"   6 seconds ago   Up 2 seconds   80/tcp                              nginx2_1
e3b0a849eb67   httpd:2.4.46   "httpd-foreground"       6 seconds ago   Up 3 seconds   80/tcp                              apache1_1
040166b29057   nginx:1.18.0   "/docker-entrypoint.…"   6 seconds ago   Up 3 seconds   80/tcp                              nginx1_1
2b56cf6141e6   httpd:2.4.46   "httpd-foreground"       6 seconds ago   Up 4 seconds   80/tcp                              apache2_1
814127e654ad   nginx:1.18.0   "/docker-entrypoint.…"   7 seconds ago   Up 3 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp   nginx_proxy_1

❗️중요한 건, reverse proxy에서 경로로 서비스들을 구분하긴 했지만,
경로가 그대로 http request로 따라들어오는것이기 때문에
❗️각각의 내부서버에서도 이 경로로 맞춰주어야❗️ 서비스를 제공할 수 있습니다.

이는 즉 경로를 reverse proxy에서도 바꾸면 각 내부서버의 루트부터해서 파일 경로도 다 바뀌어야한다는 것입니다.

(이 다음번에는 nginx.conf에서 rewrite옵션을 이용하여 이를 보완할 예정입니다)

먼저, nginx와 apache의 default html경로는 다음과 같습니다.

  • nginx: /usr/share/nginx/html
  • apache: /usr/local/apache2/htdocs

귀찮겠지만, 내부 4개의 서버에서 각각의 루트 html경로에
해당하는 경로(ex, /app1)를 추가한 후 index.html을 만들어 이를 확인해보도록 하겠습니다.
index.html 간단하게 각 내부 서버를 확인할 수 있게만 적어놓았습니다.

❗️주의: 해당 컨테이너 접속 후, apt-get update와 vim을 설치한 후에 위의 작업을 수행해야 합니당❗️

5. 각 내부 서버 접속 확인

  • ip주소/app1 -> app1서버(docker-nginx1 컨테이너)에 접속
  • ip주소/app2 -> app2서버(docker-nginx2 컨테이너)에 접속
  • ip주소/app3 -> app3서버(docker-apache1 컨테이너)에 접속
  • ip주소/app4 -> app4서버(docker-apache2 컨테이너)에 접속

이를 확인하면 다음과 같습니다.

  • /app1 -> app1 서버에 연결(docker-nginx1 컨테이너)
  • /app2 -> app2 서버에 연결(docker-nginx2 컨테이너)
  • /app3 -> app3 서버에 연결(docker-apache1 컨테이너)
  • /app4 -> app4 서버에 연결(docker-apache2 컨테이너)

6. rewrite옵션으로 보완해서 -> URL별로 라우팅하기

reverse proxy에서 경로로 서비스들을 구분하긴 했지만,
경로가 그대로 http request로 따라들어오는것이기 때문에
각각의 내부서버에서도 이 경로로 맞춰주어야 서비스를 제공할 수 있었습니다.

이번에는 ❗️내부 서버에 요청하는 경로는 변경되도록❗️ 이를 수정하겠습니다.

예를 들어, 서버ip주소/app1/index.html 과 같이 proxy에 요청했을 때,
내부 서버에서는 서버ip주소/index.html 을 요청한 것처럼 경로를 변경해보도록 하겠습니다.

  • proxy 요청: 서버ip주소/app1/index.html
  • 내부 서버 요청: 서버ip주소/index.html

이렇게 경로 변경을 위해서는, nginx.conf 파일에서 rewrite옵션만 추가해주면 됩니다.

변경한 nginx.conf 파일은 다음과 같습니다.

  • nginx/nginx.conf
.
.
.
    upstream docker-nginx1 {
        server nginx1:80;
    }

    upstream docker-nginx2 {
        server nginx2:80;
    }

    upstream docker-apache1 {
        server apache1:80;
    }

    upstream docker-apache2 {
        server apache2:80;
    }

    server {
        listen 80;

        location /app1/ {
            rewrite            ^/app1(.*)$ $1 break;
            proxy_pass         http://docker-nginx1;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }

        location /app2/ {
            rewrite            ^/app2(.*)$ $1 break;
            proxy_pass         http://docker-nginx2;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }

        location /app3/ {
            rewrite            ^/app3(.*)$ $1 break;
            proxy_pass         http://docker-apache1;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }

        location /app4/ {
            rewrite            ^/app4(.*)$ $1 break;
            proxy_pass         http://docker-apache2;
            proxy_redirect     off;
            proxy_set_header   Host $host;
            proxy_set_header   X-Real-IP $remote_addr;
            proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header   X-Forwarded-Host $server_name;
        }
    }

}

app1으로 들어오는 요청을 예로 들어 설명하면 다음과 같습니다.

location /app1/ {
    rewrite            ^/app1(.*)$ $1 break;
    proxy_pass         http://docker-nginx1;
    proxy_redirect     off;
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Host $server_name;
}

ip주소/app1/index.html 로 요청이 들어온다고 가정해봅시다.
그러면 app1(.*)$ 에서 $1은 index.html이 됩니다.
(자세한 정규표현식에 대한 문법은 따로 설명하지 않겠습니다.)
그래서 아까는 app1의 루트 html 디렉토리 아래에 app1/index.html을 만들었지만,
이제는 내부 서버에서 들어오는 요청이 index.html 이 됩니다.

4개의 서비스 모두 작동 원리는 같습니다.
그러면 아까와 같이 url로 구분하여 요청을 보냈을 때,
nginx서버와 apache서버가 default로 제공하는 html파일이 뜰 것입니다.

이를 확인해보면 다음과 같습니다.

  • /app1 -> app1 서버에 연결(docker-nginx1 컨테이너)

  • /app2 -> app2 서버에 연결(docker-nginx2 컨테이너)

  • /app3 -> app3 서버에 연결(docker-apache1 컨테이너)

  • /app4 -> app4 서버에 연결(docker-apache2 컨테이너)

profile
꾸준히, 열심히, 그리고 잘하자

0개의 댓글