imhamburger 님의 블로그

도커(Docker) - 도커 컴포즈로 Nginx 로드밸런서 실행하기 본문

도커(Docker)

도커(Docker) - 도커 컴포즈로 Nginx 로드밸런서 실행하기

imhamburger 2024. 10. 31. 14:30

같은 어플리케이션을 도커를 이용해 여러 개의 컨테이너로 생성하여 실행하고 로드밸런서를 이용해 분산시키는 것을 해보려고 한다.

 

나는 간단하게 웹사이트를 만들었고 도커를 이용해 3개의 컨테이너를 실행한다고 가정해보자.

웹사이트는 Apache httpd 웹 서버 소프트웨어를 이용해 서빙할 것이다. apache httpd 는 도커 이미지를 사용하였다. (httpd 도커)

 

도커파일은 다음과 같이 설정해주었다.

 

Dockerfile

ROM httpd:2.4

ARG REPO_URL=https://github.com/hamsunwoo/hamsunwoo.github.io.git
COPY ./my-httpd.conf /usr/local/apache2/conf/httpd.conf

RUN ["apt-get", "update"]
RUN ["apt-get", "install", "-y", "vim"]
RUN ["apt-get", "install", "-y", "git"]
RUN git clone ${REPO_URL} /usr/local/apache2/blog
  • 만들어놓은 웹사이트 소스코드는 나의 깃허브에서 가져올 것이다.

 

이제 위의 Dockerfile을 이용하여 빌드하여 실행시켜보자.

$ docker build -t my-apache2 .
$ docker run -dit --name my-running-app -p 8080:80 my-apache2

 

httpd.conf 에서 DocumentRoot?

httpd.conf 파일에서 DocumentRoot는 Apache HTTP Server가 웹 콘텐츠를 제공하는 기본 디렉토리를 지정하는 지시어.
DocumentRoot에 지정된 디렉토리는 웹 서버가 요청받은 URL과 일치하는 파일을 찾는 기본 위치이다.
예를 들어, DocumentRoot /var/www/html로 설정되어 있다면, 웹 서버는 /var/www/html 디렉토리에서 요청된 파일을 찾는다.

 

 

만약, 실행되고 있는 나의 웹 어플리케이션의 html 파일을 컨테이너 안이 아닌 내 컴퓨터로 복사해오고싶다면 다음과 같은 명령어를 사용한다.

$ docker cp my-running-app:/usr/local/apache2/htdocs/index.html index.html

 

이 명령어를 사용하면 도커 컨테이너 내부의 index.html 파일을 내 컴퓨터로 복사해올 수 있다.

이렇게 함으로써, 내 컴퓨터에서 파일을 수정하거나 백업하는 등의 작업을 할 수 있다.

 

응용해보기

$ docker run --rm httpd:2.4 cat /usr/local/apache2/conf/httpd.conf > my-httpd.conf

 

위 명령어는,

도커허브에 있는 httpd:2.4 이미지에서 httpd.conf 복사파일의 내용이 my-httpd.conf라는 이름의 파일로 호스트의 현재 작업 디렉토리에 저장된다. (--rm: 컨테이너가 종료되면 자동으로 삭제되도록 하는 옵션)

 

이 방법은 Apache의 기본 설정을 쉽게 가져오고 수정할 수 있는 유용한 방법이다!!

 

 

다시돌아와서,

 

웹 어플리케이션을 실행시키기 전에 나는 내 컴퓨터에서 작업을 계속 하고싶은데, 도커를 다시 빌드하고 실행시키려니 너무 번거로울 것 같다.

 

따라서, 다음과 같은 명령어로 내 로컬의 작업파일이 바로 도커 컨테이너에도 반영되게끔 하였다.

$ docker run -dit --name my-apache-app -p 8080:80\
-v "$PWD":/usr/local/apache2/blog/ my-apache2

#읽기모드
docker run -dit --name my-apache-app -p 8080:80\
-v "$PWD":/usr/local/apache2/blog/:ro --rm  my-apache2

 

나는 컨테이너 안에 httpd.conf 파일에서 DocumentRoot를 변경하였었는데 이것을 일치하게 명령어를 줘야 에러가 나지 않는다.

 

:ro 옵션은 이 마운트를 읽기 전용 모드로 설정.
즉, 컨테이너 내에서 호스트의 파일을 수정할 수 없고, 오직 읽기만 가능하다.

 

 

웹 어플리케이션 도커 컨테이너는 실행시켜놓았고 이제 nginx 로드밸런서를 이용해 분산시켜보자.

나는  컨테이너들을 blog-1 / blog-2 / blog-3 / blog-4 으로 총 3개를 만들었고 포트번호는 주지않았다.

 

 

그리고 로드밸런서 설정을 해주자.

 

default.conf

upstream blog_servers {
	server blog-1:80;
	server blog-2:80;
	server blog-3:80;
    	server blog-4:80;
}

server {
	listen 80;

	location / {
		proxy_pass http://blog_servers;
	}
}

 

nginx 를 위한 Dockerfile

FROM nginx:1.25.1

COPY ["default.conf", "/etc/nginx/conf.d/"]

 

 

로드밸런서 실행하기

docker run -d --name lb -p 8080:80 \
--link blog-1 --link blog-2 --link blog-3 --link blog-4 --rm lb

 

결과

 

 

이렇게 실행하면 nginx conf도 일일이 수정해줘야하고 명령어를 줄 때도 실행시켜놓은 모든 어플리케이션을 link 해줘야 한다.

 

그렇지만!!

Docker Compose 를 사용하면 간단하게 모든 것을 실행시킬 수 있다!!

 

 

Docker compose

 

실행시켜놓은 모든 어플리케이션을 모두 중단 및 삭제를 해준다.

 

그리고 compose.yml 을 만든다. (공식문서 참고)

 

docker-compose.yml

services:
  blog_1:
    build: ./docker/httpd
    container_name: blog-1
    ports:
      - 8051:80

  blog_2:
    build: ./docker/httpd
    container_name: blog-2
    ports:
      - 8052:80

  blog_3:
    build: ./docker/httpd
    container_name: blog-3
    ports:
      - 8053:80

  load_balancer:
    build: ./docker/nginx
    container_name: blog-lb
    ports:
      - 8949:80
    depends_on:
      - blog_1
      - blog_2
      - blog_3
    links:
      - blog_1
      - blog_2
      - blog_3

 

 

(참고로 외부 클라이언트가 각 블로그에 직접 접근하려면 포트가 지정해야한다.)

 

default.conf

upstream blog_servers {
	server blog-1:80;
	server blog-2:80;
	server blog-3:80;
}

server {
	listen 80;

	location / {
		proxy_pass http://blog_servers;
	}
}

 

 

Docker compose.yml 실행시키기

$ docker compose up -d #실행
$ docker compose -f <yml 파일명> down #중단

 

결과

 

아까와 동일하게 잘 나온것을 확인할 수 있다.

 

하지만, 어플리케이션을 더 늘리고 싶을 때는?

위와 같은 방법을 사용할 시 yml파일에 blog_4를 추가하고 name 지정하고... 번거로운 작업을 해줘야 한다.

 

그리하여....

 

--scale 옵션을 활용하여 container 개수를 늘려보자!

 

 

Docker-compose.yml

name: ham
services:
  blog:
    build: ./docker/httpd
    ports:
      - 80
    deploy:
      mode: replicated
      replicas: 1
    environment:
      - VIRTUAL_HOST=localhost
      - VIRTUAL_PORT=80

  load_balancer:
    image: nginxproxy/nginx-proxy
    container_name: blog-lb
    ports:
      - 8949:80
    depends_on:
      - blog
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

 

  • nginx 도 바로 도커허브에서 nginxproxy를 가져다 쓰기때문에 별도로 default.conf파일이 필요없다.
  • mode: replicated: 복제된 서비스로 설정하여 스케일링이 가능하다.
  • replicas: 1: 블로그 컨테이너의 복제본을 1개로 제한. (추후 확장가능)
  • VIRTUAL_HOST=localhost: Nginx 프록시가 localhost라는 호스트 이름을 기반으로 이 컨테이너에 트래픽을 전달하도록 설정
  • VIRTUAL_PORT=80: 트래픽이 blog 컨테이너의 포트 80으로 라우팅

 

실행하기

docker compose up -d --scale blog=<개수입력> --build

 

어플리케이션 컨테이너를 원하는 개수만큼 입력해주면 된다!

이렇게 설정하면 훨씬 간편하다.

 

 

부록: Develop 옵션

 

 

 

Use Compose Watch

Use File watch to automatically update running services as you work

docs.docker.com

 

Docker-compose.yml

services:
  blog_1:
    build: ./docker/httpd
    container_name: blog-1
    ports:
      - 8051:80

  blog_2:
    build: ./docker/httpd
    container_name: blog-2
    ports:
      - 8052:80

  blog_3:
    build: ./docker/httpd
    container_name: blog-3
    ports:
      - 8053:80

  load_balancer:
    build: ./docker/nginx
    container_name: blog-lb
    ports:
      - 8949:80
    depends_on:
      - blog_1
      - blog_2
      - blog_3
    links:
      - blog_1
      - blog_2
      - blog_3

    develop:
      watch:
        - action: sync+restart
          path: ./docker/nginx/default.conf
          target: /etc/nginx/conf.d/default.conf
  • watch: 이 키는 특정 파일이나 디렉토리를 감시하는 설정을 포함. 파일의 변경 사항이 감지되면 지정된 동작이 수행.
  • sync는 감시 중인 파일의 변경 내용을 특정 위치로 동기화하는 작업
  • restart는 Nginx 서버를 재시작하는 작업 (변경된 설정이 즉시 적용)
  • path: ./docker/nginx/default.conf는 감시할 파일의 경로를 지정
  • target: /etc/nginx/conf.d/default.conf는 동기화된 파일이 Nginx 컨테이너 내에서 위치할 경로 (Nginx가 설정 파일을 읽는 위치)

즉, 이 설정은 ./docker/nginx/default.conf 파일이 수정될 때마다 Nginx 설정 파일이 자동으로 업데이트되고 Nginx 서버가 재시작되어 변경 사항이 적용되도록 하는 것이다.

 

이를 통해 개발자는 설정을 빠르게 변경하고 결과를 즉시 확인할 수 있다.