## docker swarm 으로 그누보드5 로드밸런스 흉내내기
흉내는 흉내일뿐.
실제 서비스에서 로드밸런싱(부하분산)의 문제는 웹서버 프로세스의 접속제한이나
DB 서버의 처리능력, 네트워크 대역폭, 디스크의 I/O 성능등
다양한 부분을 감안한 복잡한 작업이지만, 제가 그런건 알리 없고
다만, 웹서버를 2 개이상 복수로 늘려서 웹접속을 늘려서 부하를 조정하는 ..
아주 단순한 차원에서,
도커 스웜에서 제공하는 로드밸런스 기능으로 그누보드5를 설치해 봤습니다.
192.168.219.160 docker-master # 도커 마스터(매니저)
192.168.219.184 docker-node1 # 도커 노동자1
일단, 서버 2 대로 테스트했습니다.
서버는 둘다 우분투리눅스 19.10이고, 기본 도커패키지 docker.io 를 사용하고
hostnamectl 명령어로 각각 호스트이름을 docker-master, docker-node1 으로
지정해 줬습니다.
그누보드5 운영환경은,
웹서버와 mysql DB 서버를 분리하고, DB 서버는 docker-master 에서 1개만
실행하고, 웹서버는 2개이상 실행해서 docker-master 와 docker-node1 에서
나누어 늘려나가게 했습니다.
mysql DB 서버는 docker-master 서버의 /home/mysql_data 라는 디렉토리에
/var/lib/mysql 디렉토리를 볼륨 연결하고,
아파치 웹서버는 /home/gnuboard_data 에 전체 소스를 넣어서 역시 이 디렉토리를
볼륨으로 /var/www/html 에 웹루트로 연결했습니다.
그런데 문제는, 웹서버를 2대 이상 서버에 분산해서 실행하면, 웹루트의 소스를
동일하게 공유해야 하므로,
네트워크 파일시스템인 nfs 로 노드들 간에 마운트해서 동일한 /home/gnuboard_data 를
접근 하게 했습니다.
우선, 도커 스웜 개념 이해에 집중하기 위해서, 보안상 안좋겠지만
까다로운 방화벽이 있다면 미리 꺼두고, 그리고 파일 디렉토리 접근 퍼미션은
대충ㅜㅜ 했습니다.
그리고, 아래 명령들은 마스터 노드와 노동자노드를 각각 왔다가 갔다하면서 실행하는데
구분은, 프롬프트의 master 와 node1 단어를 보고 잘 구분해서 실행하시길.
docker-node2, docker-node3 등으로 계속 새로운 노드를 추가시
아래 node1의 작업을 동일하게 합니다.
1. 사전 작업
우선 docker-master에서 DB 서버와 웹서버가 마운트할 디렉토리를 생성합니다.
root 권한으로 해야해서 sudo 로 합니다.
nonots@docker-master:~$ sudo mkdir /home/mysql_data
nonots@docker-master:~$ sudo mkdir -p /home/gnuboard_data/data
nonots@docker-master:~$ sudo chmod 707 /home/mysql_data/
nonots@docker-master:~$ sudo chmod 707 /home/gnuboard_data/
nonots@docker-master:~$ sudo chmod 707 /home/gnuboard_data/data/
그리고 그누보드5 소스 전체를 여기에 복사해서 gnuboard_data에 넣어 줍니다.
docker-node1 에서는 db서버 볼륨은 필요 없으므로 gnuboard_data 만
생성합니다.
nonots@docker-node1:~/mydocker$ sudo mkdir /home/gnuboard_data
nonots@docker-node1:~/mydocker$ sudo chmod 707 /home/gnuboard_data
그리고 docker-node1 의 /home/gnuboard_data 는 마스터에서 마운트하므로
항상 빈디렉토리이어야 합니다.
그리고 마스터에서 nfs 서버를 설치합니다.
nonots@docker-master:~$ sudo apt-get install nfs-common nfs-kernel-server rpcbind portmap
그리고 /etc/exports 파일 제일 하단에 아래 1 줄 추가합니다.
/home/gnuboard_data 192.168.219.184(rw,sync,no_subtree_check)
192.168.219.184(docker-node1)에서 /home/gnuboard_data 를 마운트 가능하게
하다는 뜻인데, 만약 node2, node3 와 같이 늘어난다면, 그 노드 아이피도 같이
동일하게 추가하면 됩니다.
nonots@docker-master:~$ sudo exportfs -a
nonots@docker-master:~$ sudo systemctl restart nfs-kernel-server
그후 위와 같이 해서 nfs-server 를 실행합니다.
이 nfs 는 111 와 2049 포트를 사용하는데, 추후 방화벽 실행시 이 포트를
열어 줘야 할겁니다.
그리고, 이제 docker-node1 에서는 nfs 클라이언트만 사용하므로
nonots@docker-node1:~$ sudo apt install nfs-common
nonots@docker-node1:~$ sudo mount -t nfs 192.168.219.160:/home/gnuboard_data /home/gnuboard_data
위와 같이 docker-master(192.168.219.160) 에서 gnuboard_data 폴더를
nfs 로 마운트합니다.
만약, 서버 재시작시 자동으로 마운트 되게 하려면 /etc/fstab 에 지정해야 하는데,
그 방법은 알아서 각자.
이렇게 하면
nonots@docker-node1:~$ cd /home/gnuboard_data/
nonots@docker-node1:/home/gnuboard_data$ ls
하면 그누보드 전체소스가 보여야 정상.
그리고 나서, 모든 서버에서 도커데몬이 정상적으로 실행되어 있는지 확인합니다.
2. 사용할 도커 이미지 생성
웹서버는 아파치2.4에 php가 모듈로 지원되고, php-mysqli php-gd 같은 php 모듈도 사용 하기위해,
hub.docker.com 에 있는 php:7.4-apache 라는 이미지를 불러와서 간단하게 다시 빌드를 했습니다.
이렇게 빌드한걸 hub.docker.com 에 nonots/gnudocker:v1 이라는 이름으로 등록을 해두었습니다.
https://hub.docker.com/r/nonots/gnudocker
이제 docker pull nonots/gnudocker:v1 이라는 명령어로 어디서나 원격으로 사용가능하게 했습니다.
만약, 본인이 새로 이미지를 만들겠다면, 아래 Dockerfile 을 적절하게 수정해서
빌드를 해서 로컬에서 사용하면 됩니다.
그런데, 문제는 이렇게 로컬에 빌드하려면 master뿐만 아니라 다른 노드에도 모두
동일 이미지로 빌드를 해야 웹서버를 복제시 정상 작동하게 됩니다.
nonots@nonots-master:~/mydocker$ cat Dockerfile
ROM php:7.4-apache
MAINTAINER nonots
RUN apt update && apt -y install libfreetype6-dev libjpeg62-turbo-dev zlib1g-dev libpng-dev \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) gd mysqli
위와 같이 3줄짜리 Dockerfile 를 간단하게 만듭니다.
그런후 아래와 같이 nonots/gnudocker:v1 이라는 이미지 이름으로 빌드를 합니다.
nonots@nonots-master:~/mydocker$ docker build . -t nonots/gnudocker:v1
빌드가 성공하면
nonots@nonots-master:~/mydocker$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nonots/gnudocker v1 78566041ec11 13 seconds ago 435MB
와 같이 이미지 목록에 보입니다.
3. docker swarm 모드 실행
우선 docker-master 에서 docker swarm init 명령어를 아래와 같이 실행합니다.
nonots@docker-master:~/mydocker$ docker swarm init --advertise-addr 192.168.219.160
Swarm initialized: current node (j0m1pxp35cgd9ylu3aaaa3t2j) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-4m5w9f7sar3xq5xpx0lomsrveqqbm5qda723mi24i2rn8u0ebp-ebuk5n1rcxr21a0ddaln4c7dl 192.168.219.160:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
그러면 위 내용에서 토큰값을 사용하는 docker swarm join 명령줄이 나오는데, 이 명령어를 그대로
복사해서 각 노동자 노드에 실행하면 됩니다.
이 부분은 복사해서 저장해 두는게 좋습니다.
docker-node1 에서 아래와 같이 실행하면, 그 노드는 swarm 패밀리에 포함됩니다.
nonots@docker-node1:~$ docker swarm join --token SWMTKN-1-4m5w9f7sar3xq5xpx0lomsrveqqbm5qda723mi24i2rn8u0ebp-ebuk5n1rcxr21a0ddaln4c7dl 192.168.219.160:2377
This node joined a swarm as a worker.
마스터에서 노드 상태를 보면 아래와 같이 되어 있으면 정상입니다.
이제 본격적으로 로드밸런싱을 사용할수 있습니다.
nonots@docker-master:~/mydocker$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
j0m1pxp35cgd9ylu3aaaa3t2j * docker-master Ready Active Leader 19.03.6
l0taqdgxz2bfcw65u8zqxbme2 docker-node1 Ready Active 19.03.8
3. 각 서비스 생성
우선 mysql DB 서비스(mymysql)를 생성 실행합니다.
이건 항상 제일먼저 생성해서 마스터 노드에서만 컨테이너가 위치하게 해야함.
nonots@docker-master:~$ docker service create --name mymysql --replicas 1 --constraint 'node.role==manager' --mount type=bind,src=/home/mysql_data,dst=/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root2349 -e MYSQL_USER=nonots -e MYSQL_DATABASE=nonots_db -e MYSQL_PASSWORD=kwon2349 -p 3306:3306 -d mysql:latest --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
nonots@docker-master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
1kv5g06zn64z mymysql replicated 1/1 mysql:latest *:3306->3306/tcp
nonots@docker-master:~/mydocker$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
07e9a16b4bab mysql:latest "docker-entrypoint.s…" 8 seconds ago Up 7 seconds 3306/tcp, 33060/tcp mymysql.1.n6cdjbkvl0xeizy32ciwg9pl1
생성한 후 아래 명령어로 컨테이너 ID 값을 이용해서 mysql 접속시 mysql 프롬프트가 뜨면 정상입니다.
nonots@docker-master:~$ docker container exec -it 07e9a16b4bab mysql -unonots -pkwon2349 nonots_db
그 다음에 아파치 웹서버 서비스(mygnu)를 생성 실행하는데 복제 갯수를 (--replicas 3) 3개
만들게 했습니다.
nonots@docker-master:~$ docker service create --name mygnu --replicas 3 --mount type=bind,src=/home/gnuboard_data,dst=/var/www/html -p 8888:80 -d nonots/gnudocker:v1
이렇게 2 개 서비스를 생성하면 아래와 같이 서비스가 보여야 합니다.
nonots@docker-master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
hu2sel7mozt2 mygnu replicated 3/3 nonots/gnudocker:v1 *:8888->80/tcp
tgh1jvrqascz mymysql replicated 1/1 mysql:latest *:3306->3306/tcp
그리고 그중에서 웹서버 mygnu 는 아래와 같이 master 에 1개, node1 에 2 개가 생성되어 있습니다
nonots@docker-master:~$ docker service ps mygnu
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
knmz2tgjwkxo mygnu.1 nonots/gnudocker:v1 docker-master Running Running about a minute ago
ax7unskk8an0 mygnu.2 nonots/gnudocker:v1 docker-node1 Running Running about a minute ago
7m9antdw76yi mygnu.3 nonots/gnudocker:v1 docker-node1 Running Running about a minute ago
docker-master 에서 컨테이너를 보면 mygnu 1개와 mymysql 1개가 보이고
nonots@docker-master:~$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
81e4fb7f162f nonots/gnudocker:v1 "docker-php-entrypoi…" About a minute ago Up About a minute 80/tcp mygnu.1.knmz2tgjwkxorhj2dt5zsyzq6
8d48664abbbb mysql:latest "docker-entrypoint.s…" 2 minutes ago Up 2 minutes 3306/tcp, 33060/tcp mymysql.1.xk40f7u8n0ou83cw95pg5fkhq
docker-node1 서버에서는 mygnu 2개가 보일겁니다.
nonots@docker-node1:~$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6cd8f31238b1 nonots/gnudocker:v1 "docker-php-entrypoi…" 2 minutes ago Up About a minute 80/tcp mygnu.3.7m9antdw76yi2h362jy52rtzq
d2a1e7cf43cd nonots/gnudocker:v1 "docker-php-entrypoi…" 2 minutes ago Up About a minute 80/tcp mygnu.2.ax7unskk8an0mk3fuktr1qkzh
이제 웹브라우저에서
http://192.168.219.160:8888 (http://192.168.219.184:8888로 해도 됨)으로 접속하면
그누보드 설치화면이 뜨게 됩니다.
Mysql DB 정보 입력에서 Host 주소는 기본 localhost 가 아니라
mymysql 혹은 마스터IP 즉 192.168.219.160 둘 다 해도 되더군요.
게시판에 글을 몇개 등록해 봅니다.
이렇게 일단 웹서버가 작동하는건 알겠는데, 과연 로드밸런싱이 되는지 확인하려면
마스터 /home/gnuboard_data 디렉토리에 간단하게 아래와 같이 lb.php 를 만듭니다.
nonots@docker-master:~$ cat /home/gnuboard_data/lb.php
그리고 웹브라우저에서 http://192.168.219.160:8888/lb.php 를 실행합니다.
시간이 지나서 새로고침을 해보고, 다른 브라우저로 위 주소를 열어 봅니다.
터미널에서 curl 로 테스트를 해보면
아래와 같이 실행시 마다 다른 웹서버를 연결함을 알수 있습니다.
즉 URL 은 동일하게 보여도, 도커 스웜에서 라운드로빈 방식으로 로드밸런스를
하고 있어서 특정 접속마다 자동으로 다른 웹서버에 접속이 됩니다.
nonots@nonots-desktop:~$ curl http://192.168.219.184:8888/lb.php
6cd8f31238b1 - 192.168.219.184 - 10.0.0.109
nonots@nonots-desktop:~$ curl http://192.168.219.184:8888/lb.php
d2a1e7cf43cd - 192.168.219.184 - 10.0.0.110
nonots@nonots-desktop:~$ curl http://192.168.219.184:8888/lb.php
81e4fb7f162f - 192.168.219.184 - 10.0.0.108
nonots@nonots-desktop:~$ curl http://192.168.219.184:8888/lb.php
6cd8f31238b1 - 192.168.219.184 - 10.0.0.109
이제 복제 갯수 늘리거나 줄여보겠습니다. 기존 3개에서 5개로 늘리면
nonots@docker-master:~$ docker service scale mygnu=5
mygnu scaled to 5
overall progress: 5 out of 5 tasks
1/5: running [==================================================>]
2/5: running [==================================================>]
3/5: running [==================================================>]
4/5: running [==================================================>]
5/5: running [==================================================>]
verify: Service converged
nonots@docker-master:~$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
hu2sel7mozt2 mygnu replicated 5/5 nonots/gnudocker:v1 *:8888->80/tcp
tgh1jvrqascz mymysql replicated 1/1 mysql:latest *:3306->3306/tcp
그런후에 각 노드에서 어떻게 웹서버가 분산되어 있는지 보면
아래와 같이 마스트에 2개, node1 에 3개로 각각 분산되어 추가됨을
알수 있습니다.
nonots@docker-master:~$ docker node ps docker-master
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
knmz2tgjwkxo mygnu.1 nonots/gnudocker:v1 docker-master Running Running 12 minutes ago
xk40f7u8n0ou mymysql.1 mysql:latest docker-master Running Running 13 minutes ago
xyt8rdnbos5g mygnu.4 nonots/gnudocker:v1 docker-master Running Running about a minute ago
nonots@docker-master:~$ docker node ps docker-node1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ax7unskk8an0 mygnu.2 nonots/gnudocker:v1 docker-node1 Running Running 12 minutes ago
7m9antdw76yi mygnu.3 nonots/gnudocker:v1 docker-node1 Running Running 12 minutes ago
6jozbsc02pz0 mygnu.5 nonots/gnudocker:v1 docker-node1 Running Running about a minute ago
4. yaml 파일을 이용한 서비스 실행
이제 위에서 실행한 mygnu mymysql 서비스를 삭제해보겠습니다.
nonots@docker-master:~$ docker service rm mygnu mymysql
너무 간단해서 허무하게 모든 컨테이너가 삭제되어 사라집니다.
그누보드도 사라졌습니다.
다시 시작하기 위해 위에서 실행한 docker service create 의 기다란 명령어를
실행해되 되지만, 대신
이번에는 yaml 파일을 이용해서 한 큐에 해보겠습니다
nonots@docker-master:~/mydocker$ cat stack.yaml
version: "3"
services:
mymysql:
image: mysql:latest
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: "root2349" # MYSQL root패스워드 설정 옵션
MYSQL_USER: "nonots" # 그누보드 사용자
MYSQL_DATABASE: "nonots_db" # 그누보드 사용 DB
MYSQL_PASSWORD: "kwon2349" #그누보드 비번
command: # 명령어 실행
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
volumes:
- "/home/mysql_data:/var/lib/mysql"
deploy:
replicas: 1
placement:
constraints:
- "node.role==manager"
mygnu: #apache php 웹서버
image: nonots/gnudocker:v1
ports:
- "8888:80"
volumes:
- "/home/gnuboard_data:/var/www/html"
deploy:
replicas: 3
위와 같은 stack.yaml 파일을 만든 후
아래와 같이 docker stack deploy 명령어로 sir 이라는 이름의 스택을 생성합니다.
nonots@docker-master:~/mydocker$ docker stack deploy -c ./stack.yaml sir
Creating network sir_default
Creating service sir_mymysql
Creating service sir_mygnu
그러면, 자동으로 서비스명이 sir_ 가 붙어서 실행됩니다.
(주의:만약 그노보드 초기 설치라면 mysql host 란에 sir_mymysql 을 입력)
nonots@docker-master:~/mydocker$ docker stack ls
NAME SERVICES ORCHESTRATOR
sir 2 Swarm
nonots@docker-master:~/mydocker$ docker stack ps sir
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
i174wfc3e0z9 sir_mygnu.1 nonots/gnudocker:v1 docker-node1 Running Running 7 seconds ago
jqhx57uy58h5 sir_mymysql.1 mysql:latest docker-master Running Running 18 seconds ago
6g30kt5bmt5n sir_mygnu.2 nonots/gnudocker:v1 docker-node1 Running Running 8 seconds ago
w4slrrr5d0cs sir_mygnu.3 nonots/gnudocker:v1 docker-master Running Running 14 seconds ago
nonots@docker-master:~/mydocker$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
6wybc9w12gfw sir_mygnu replicated 3/3 nonots/gnudocker:v1 *:8888->80/tcp
qss522ijv5rp sir_mymysql replicated 1/1 mysql:latest *:3306->3306/tcp
위와 같이 웹서버(sir_mygnu) 3개와 sir_mymysql 1개가 실행되고,
웹브라우저로 접속하면 기존 설치한 그누보드 정보와 DB 정보가 그대로 남아있음을
알게 됩니다.
만약, 서비스 종료시에는
nonots@docker-master:~$ docker stack rm sir
Removing service sir_mygnu
Removing service sir_mymysql
Removing network sir_default
와 같이 역시 한번에 모든 서비스가 종료 됩니다.
클라우드 환경이 만들고 있는 이런 마이크로서비스 세계의 가벼움에서
민첩함과 허망함이 섞여 느껴집니다.
'기술빙자사기' 카테고리의 다른 글
오래만에 도커를 GUI 환경으로 원격데스크탑 사용. (0) | 2023.02.07 |
---|---|
쿠버네티스에서 그누보드5 서비스 설정하기. (1) | 2020.03.27 |
[테스트 노트] docker swarm 모드로 그누보드5 로드밸런스(부하분산) 흉내내기 (0) | 2020.03.24 |
docker 와 podman 으로 그누보드5 명령모드로 설치 (0) | 2020.03.23 |
docker 에서 그누보드5 설치. (0) | 2020.03.23 |