Load Balancing, Session Clustering
1. 테스트 환경 및 버전
- 모든 테스트는 docker 에서 진행
- centos : CentOS Linux release 8.1.1911(centos 버전 확인은 cat /etc/centos-release)
- haproxy : 2.1.5
- apache : 2.4
- tomcat-conectors(mod_jk) : 1.2.48
- tomcat : 9.0.35
- db container : gaia3d/mago3d-postgresql(postgresql-12.3) docker image
- application : mago3d-CMS(mago3d 브런치)
2. 네트워크 구성도
-
설정 순서
- 사용자 설정
- tomcat 서비스 설정
- session clustering
- apache 설치 및 mod_jk 컴파일 및 설정
- haproxy
3. container 설정
- 테스트에는 db, haproxy, apache-tomcat 2 개 총 4개의 컨테이너가 필요하고 apache-tomcat은 하나의 설정이 완료 된 후 이를 복사하여 사용한다.
3.1. 네트워크 설정
-
테스트 시 모든 사람들이 동일한 ip를 사용하도록 네트워크를 만들어서 사용한다.
cmd> docker network create --subnet=172.18.0.0/16 mynetwork
3.2. database
-
database 는 gaia3d 의 mago3d-postgresql 이미지를 사용하도록 한다. (컨테이너끼리만 접근하므로 별도의 host port는 바인딩하지 않는다.)
cmd> docker container run --privileged --net mynetwork --ip 172.18.0.10 -d --name "container-db" gaia3d/mago3d-postgresql /sbin/init
3.3. haproxy
-
로드 밸런스 테스트를 위한 haproxy container
cmd> docker container run --privileged --net mynetwork --ip 172.18.0.11 -d -p 80:80 -p 9090:9090 --name "haproxy" gaia3d/haproxy /sbin/init
3.4. apache-tomcat
-
mago3d-CMS 저장소의 doc-docker 폴더경로에서 다음을 실행해서 container 를 생성한다.
cmd> docker build apache-tomcat -t apache-tomcat cmd> docker container run --privileged --net mynetwork --ip 172.18.0.12 -d -p 10080:80 -p 18081:8081 -p 19090:9090 --name "apache-tomcat1" apache-tomcat /sbin/init
4. 유저 생성 및 sudo 권한 설정
-
tomcat 과 관련된 다음의 모든 과정은 생성한 apache-tomcat1 컨테이너에 연결해서 수행한다.
cmd> docker exec -it apache-tomcat1 bash
-
root 계정외의 별도의 계정을 생성하고 서비스 실행을 위한 권한만 부여한다.
adduser -m gaia3d
-
유저 생성 후 passwd 명령으로 패스워드를 설정해준다.
passwd gaia3d
-
생성한 계정에 필요한 명령만 권한을 준다. /etc/sudoers 를 직접 수정할 수도 있지만. visudo를 사용 할 경우 파일 수정 시 오타나 문법 오류 등을 검사해 주므로 visudo를 사용한다.
sudo visudo gaia3d ALL=NOPASSWD: /bin/systemctl
5. tomcat 설정
-
container 에는 dockerfile 을 통해 미리 다운 받은 jdk와 tomcat이 있다. 이를 /home/생성한 유저폴더 경로로 옮겨주고 권한은 해당 유저의 권한으로 변경해준다.
mv /setup /home/gaia3d/ chown -R gaia3d:gaia3d /home/gaia3d/setup
-
이후 작업은 생성한 사용자로 진행한다.
su gaia3d
5.1. 압축 해제 / 경로 설정
cd /home/gaia3d/setup
tar -xvzf jdk-11.tgz
tar -xvzf tomcat.tgz
mkdir ../tools && mv apache-tomcat-9.0.36 mago3d-tomcat && mv mago3d-tomcat ../tools && mv jdk-11.0.2 ../tools
rm -rf /home/gaia3d/tools/mago3d-tomcat/webapps/*
5.2. 자바 경로 및 메모리 설정
- linux 의 경우에는 setenv.sh 파일을 만들어 작성하고, windows 의 경우에는 setenv.bat 파일을 만들어 작성한다.
-
windows 의 경우 서비스로 등록시 setenv.bat 파일을 읽지 않기 때문에 service.bat 에 설정하거나 환경 변수로 등록해야 한다.
cd /home/gaia3d/tools/mago3d-tomcat/bin vi setenv.sh
export JAVA_HOME=/home/gaia3d/tools/jdk-11.0.2 export JAVA_OPTS=-D-Xms4096m-Xmx4096m
-
tomcat cache 설정
vi /home/gaia3d/tools/mago3d-tomcat/conf/context.xml
<Resources cachingAllowed="true" cacheMaxSize="100000"/>
5.3. application 경로 설정
-
server.xml 설정 : service 엘리먼트를 다음과 같이 수정한다.
vi /home/gaia3d/tools/mago3d-tomcat/conf/server.xml
- 배치되는 application 의 경로는 docBase로 절대 경로를 잡아준다. webapp 밑에 배치하지 않는 이유는 apllication 에서 로그 파일을 쓸 경우 webapp 밑에 있는 경우 충돌이나 에러가 발생하는 경우가 있어 별도의 폴더에 배치하여 사용한다.
-
application의 의도치 않은 재시작 및 덮어쓰기를 방지하기 위해 unpackWARs, autoDeploy, reloadable 옵션은 false 로 변경한다.
<Service name="Catalina"> <Connector port="9090" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8446" /> <!--<Connector port="8019" protocol="AJP/1.3" redirectPort="8446" address="0.0.0.0" secretRequired="false"/>--> <Engine name="Catalina" defaultHost="localhost" jvmRoute="admin1"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="false" autoDeploy="false"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> <Context docBase="/home/gaia3d/tools/mago3d-tomcat/source/mago3d-admin" path="" reloadable="false" /> </Host> </Engine> </Service> <Service name="Catalina2"> <Connector port="8081" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8447" /> <!--<Connector port="8029" protocol="AJP/1.3" redirectPort="8447" address="0.0.0.0" secretRequired="false"/>--> <Engine name="Catalina2" defaultHost="localhost" jvmRoute="user1"> <Realm className="org.apache.catalina.realm.LockOutRealm"> <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/> </Realm> <Host name="localhost" appBase="webapps" unpackWARs="false" autoDeploy="false"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log2" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> <Context docBase="/home/gaia3d/tools/mago3d-tomcat/source/mago3d-user" path="" reloadable="false" /> </Host> </Engine> </Service>
5.4. application 배치
-
resources/develop/application.properties 수정
- mago3d-CMS jdbc url 은 localhost 로 되어 있으므로 생성한 container-db 의 ip로 변경해준다.
-
프로젝트 빌드
- profile 을 develop 으로 해서 빌드한다.
-
springBoot 2.3.0 버전부터는 gradle-6.2.2 보다 상위 버전의 gradle 로 변경후 빌드해야 한다.
cmd> gradle clean -xtest cmd> gradle build -xtest -Pprofile=develop
-
mago3d-CMS 프로젝트를 빌드 후 war 파일을 컨테이너로 복사한다.
cmd> docker container cp d:\mago3d-user-0.0.1-SNAPSHOT.war apache-tomcat1:/home/gaia3d/setup cmd> docker container cp d:\mago3d-admin-0.0.1-SNAPSHOT.war apache-tomcat1:/home/gaia3d/setup
-
복사한 파일 권한 설정 및 이동
chown gaia3d:gaia3d /home/gaia3d/setup/*.war su gaia3d cd /home/gaia3d/tools/mago3d-tomcat/ && mkdir -p source/mago3d-admin && mkdir -p source/mago3d-user cd /home/gaia3d/setup/ unzip mago3d-admin-0.0.1-SNAPSHOT.war -d ../tools/mago3d-tomcat/source/mago3d-admin/ unzip mago3d-user-0.0.1-SNAPSHOT.war -d ../tools/mago3d-tomcat/source/mago3d-user/
5.5. log 설정
- 톰캣에서 서비스되는 어플리케이션의 비즈니스 로그는 기본적으로 톰캣의 logs/catalina.out 파일에 기록되는데 로그 설정을 따로 하지 않으면 계속 이 파일에 로그가 기록되어 파일 사이즈가 계속 커지게 된다. 따라서 별도의 설정이 필요한데 /etc/rogrotate.conf 에 로그 설정을 하는 방법이 있고 logback이나 log4j를 사용하는 방법이 있는데 여기서는 현재 프로젝트에 사용하고 있는 logback을 사용해서 로그 설정을 한다.
-
먼저 기존에 기록되던 catalina.out 파일에 더이상 로그를 쓰지 않도록 설정한다.
vi /home/gaia3d/tools/mago3d-tomcat/bin/catalina.sh
CATALINA_OUT=/dev/null
-
logback 설정 확인
5.6. service 등록
exit(root 계정으로 변경)
vi /usr/lib/systemd/system/mago3d-tomcat.service
[Unit]
Description=gis
After=syslog.target
[Service]
Type=forking
User=gaia3d
Group=gaia3d
ExecStart=/home/gaia3d/tools/mago3d-tomcat/bin/startup.sh
ExecStop=/home/gaia3d/tools/mago3d-tomcat/bin/shutdown.sh
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
-
서비스 자동 재시작 등록 및 daemon 리로드 후 서비스 시작
systemctl enable mago3d-tomcat && systemctl daemon-reload && systemctl start mago3d-tomcat
6. session clustering 설정
- session 에 담기는 모든 객체는 java.io.Serializable 을 상속 해야 한다. 참고
- tomcat 에서 제공하는 session clustering 은 node 가 4대 이상일 경우 속도가 매우 떨어지므로 4대 이상의 노드를 연결할 경우에는 redis 와 같은 별도의 세션 관리 방법이 필요하다.
-
Manager : 세션을 어떻게 복제할지를 책임지는 객체
- DeltaManager : 모든 노드에 동일한 세션을 복제한다. 정보가 변경될 때 마다 복제하기 때문에 노드 개수가 많을 수록 네트워크 트래픽이 높아지고 메모리 소모가 심해진다.
- BackupManager : Primary Node 와 Backup Node 로 분리 되어 모든 노드에 복제하지 않고 Backup Node 에만 복제한다.
- PersistentManager : DB 나 파일 시스템을 이용하여 세션을 저장하는데, IO 문제가 생기기 때문에 실시간성이 떨어진다.
-
Channel
- Membership : Cluster 안의 노드들을 동적으로 분별하는데 Multicast IP/PORT 를 통해 frequency 에 설정된 간격으로 각 노드들이 UDP packet 을 날려 상태를 확인한다.
- Receiver : Cluster 로부터 메시지를 수신하는 역활을 하며 blocking 방식 org.apache.catalina.tribes.transport.bio.BioReceiver와 non-blocking방식인 org.apache.catalina.tribes.transport.nio.NioReceiver을 지원한다.
- channelSendOptions : 기본값은 8 이며, 8은 비동기 6은 동기 방식이다.
-
앞서 설정한 server.xml 에 각 서비스별로 다음의 내용을 추가한다. 각 서비스별로 Membership 의 address 와 Receiver 의 port는 달라야 한다.
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster" channelSendOptions="8"> <Manager className="org.apache.catalina.ha.session.DeltaManager" expireSessionsOnShutdown="false" notifyListenersOnReplication="true" /> <Channel className="org.apache.catalina.tribes.group.GroupChannel"> <Membership className="org.apache.catalina.tribes.membership.McastService" address="228.0.0.4" port="45564" frequency="500" dropTime="3000" /> <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver" address="auto" port="4000" autoBind="100" selectorTimeout="5000" maxThreads="6" /> <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter"> <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender" /> </Sender> <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector" /> <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor" /> </Channel> <Valve className="org.apache.catalina.ha.tcp.ReplicationValve" filter="" /> <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve" /> <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer" tempDir="/tmp/war-temp/" deployDir="/tmp/war-deploy/" watchDir="/tmp/war-listen/" watchEnabled="false" /> <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener" /> </Cluster>
- web.xml 설정 : <distributable/> 을 web.xml 에 추가한다. 톰캣이 아닌 어플리케이션의 web.xml 에 설정해야 한다. 별도의 web.xml 이 없다면 생성해서 저 내용을 추가해야지 정상동작한다.
-
지금까지 설정한 컨테이너를 이미지로 만들어 동일한 컨테이너를 만든다.
cmd> docker container commit apache-tomcat1 temp cmd> docker container run --privileged --net mynetwork --ip 172.18.0.13 -d -p 20080:80 -p 28081:8081 -p 29090:9090 --name "temp" temp /sbin/init
- apache-tomcat1, temp container 의 톰캣을 재시작 한다.
- apache-tomcat1 에서 mago3d-CMS 시스템에 로그인 하고 temp 의 index 페이지에 접근하여 세션이 공유 되어 로그인이 된 상태인지 확인한다.
-
admin 사이트에서 로그인 한 세션이 user 사이트로 로그인 되지 않는지 확인한다. admin / user 는 각기 다른 세션을 사용해야 한다.
- admin / user 서비스가 같은 클러스터 채널을 사용한다면 Serializable 에러가 발생한다.
7. apache 설정
7.1. apache 및 compile 관련 패키지 설치
-
apache 설정과 관련된 부분은 root 계정으로 진행한다.
dnf install -y gcc gcc-c++ httpd-devel redhat-rpm-config
-
httpd 자동 재시작 설정
systemctl enable httpd && systemctl daemon-reload
7.2. 압축해제 및 컴파일
- apache 와 was 를 연결하는 방법에는 modjk, modproxy, modproxyajp 이렇게 3가지가 있지만 여기에서는 mod_jk를 사용하도록 한다.
-
컴파일이 정상적으로 되면 /etc/httpd/modules 경로에 mod_jk.so 파일이 생성된다.
cd /home/gaia3d/setup tar -xvzf tomcat-connectors.tgz cd tomcat-connectors-1.2.48-src/native ./configure --with-apxs=/usr/bin/apxs make && make install
7.3. mod_jk.conf 파일 생성
-
mod_jk 의 로그 및 worker 파일 위치 및 기타 설정들을 설정한다.
vi /etc/httpd/conf.modules.d/mod_jk.conf
<IfModule mod_jk.c> # 워커 설정파일 위치 JkWorkersFile conf/workers.properties # 공유 메모리파일 위치 반드시 Selinux 보안때문에 run에 위치 필수 JkShmFile run/mod_jk.shm # log 위치 JkLogFile logs/mod_jk.log # 로그레벨 설정 JkLogLevel info # 로그 포맷에 사용할 시간 형식을 지정한다. JkLogStampFormat "[%y %m %d %H:%M:%S]" </IfModule>
7.4. workers.properties 파일 생성
- apache 에서 로드 밸런싱 해줄 톰캣에 대한 설정 정보 파일이다. port는 http port 가 아닌 ajp port 를 사용한다.
- lbfactor : 가중치에 따라 로드 밸런싱 된다.
-
sticky_session : 기존의 세션 아이디 뒤에 jvmroutid 를 붙여 어느 서버로 갈지 결정 한다.
vi /etc/httpd/conf/workers.properties
worker.list=admin,user worker.admin.type=lb worker.admin.balance_workers=admin1,admin2 worker.admin.sticky_session=true worker.admin1.type=ajp13 worker.admin1.host=172.18.0.12 worker.admin1.port=8019 worker.admin1.lbfactor=1 worker.admin2.type=ajp13 worker.admin2.host=172.18.0.13 worker.admin2.port=8019 worker.admin2.lbfactor=1 worker.user.type=lb worker.user.balance_workers=user1,user2 worker.user.sticky_session=true worker.user1.type=ajp13 worker.user1.host=172.18.0.12 worker.user1.port=8029 worker.user1.lbfactor=1 worker.user2.type=ajp13 worker.user2.host=172.18.0.13 worker.user2.port=8029 worker.user2.lbfactor=1
7.5. httpd.conf 설정
vi /etc/httpd/conf/httpd.conf
Listen 9090
LoadModule jk_module modules/mod_jk.so
<VirtualHost *:80>
ServerName mago3d-user
JkMount /* user
JkUnMount /images/* user
JkUnMount /js/* user
JkUnMount /css/* user
jkUnMount /externlib/* user
jkUnMount /sample/* user
DocumentRoot "/var/www/mago3d-user"
</VirtualHost>
<VirtualHost *:9090>
ServerName mago3d-admin
JkMount /* admin
JkUnMount /images/* admin
JkUnMount /js/* admin
JkUnMount /css/* admin
jkUnMount /externlib/* admin
jkUnMount /sample/* admin
DocumentRoot "/var/www/mago3d-admin"
</VirtualHost>
7.6. static resource 복사
-
static 파일들을 apache 에서 처리하도록 httpd.conf 파일에 설정한 폴더를 생성하고 파일들을 복사해준다.
mkdir /var/www/mago3d-user && mkdir /var/www/mago3d-admin cp -R /home/gaia3d/tools/mago3d-tomcat/source/mago3d-admin/WEB-INF/classes/static/* /var/www/mago3d-admin/ cp -R /home/gaia3d/tools/mago3d-tomcat/source/mago3d-user/WEB-INF/classes/static/* /var/www/mago3d-user/
7.7. tomcat ajp 포트 설정
- session clustering 테스르를 위해 http port 로 실행한 톰캣의 port 를 ajp port 로 변경 한다.(기존의 http port 를 주석 하고 ajp port 를 주석 해제 한다.)
-
tomcat 버전에 따라 secretRequired 값을 설정 하지 않으면 톰캣이 시작 되지 않을 수 있다.
su gaia3d vi /home/gaia3d/tools/mago3d-tomcat/conf/server.xml
-
설정 변경 후 톰캣 - apache 순으로 재시작한다.
sudo systemctl restart mago3d-tomcat sudo systemctl restart httpd
7.8. container 생성
-
테스트를 위해 생성한 temp container 를 삭제하고 지금까지 설정한 내용을 기반으로 container 를 생성한다.
cmd> docker container stop temp cmd> docker container rm temp cmd> docker container commit apache-tomcat1 apache-tomcat2 cmd> docker container run --privileged --net mynetwork --ip 172.18.0.13 -d -p 20080:80 -p 28081:8081 -p 29090:9090 --name "apache-tomcat2" apache-tomcat2 /sbin/init
- 생성한 container 에서 tomcat 의 jvmRoute 값을 각각 admin2, user2 로 변경한다.
7.9. 테스트
- apache-tomcat1 의 port 로 사용자 또는 관리자 사이트에 접속해서 개발자 도구의 네트워크 탭에서 세션을 확인 한다.
- apache-tomcat1 container 를 stop 했을 때 기존의 세션값이 그대로 넘어 가고 jvmroutid 가 변경 됐는지 확인 한다.
8. haproxy 설정
-
haproxy container 연결
cmd> docker exec -it haproxy bash
-
haproxy.cfg 파일에서 frontend, backend 부분 수정
vi /etc/haproxy/haproxy.cfg
#--------------------------------------------------------------------- # main frontend which proxys to the backends #--------------------------------------------------------------------- frontend mago3d-user-front bind *:80 stats uri /haproxy?stats default_backend mago3d-user #--------------------------------------------------------------------- # round robin balancing between the various backends #--------------------------------------------------------------------- backend mago3d-user balance roundrobin server user-server-1-host 172.18.0.12:80 check fall 3 rise 2 server user-server-2-host 172.18.0.13:80 check fall 3 rise 2 #--------------------------------------------------------------------- # main frontend which proxys to the backends #--------------------------------------------------------------------- frontend mago3d-admin-front bind *:9090 stats uri /haproxy?stats default_backend mago3d-admin #--------------------------------------------------------------------- # round robin balancing between the various backends #--------------------------------------------------------------------- backend mago3d-admin balance roundrobin server admin-server-1-host 172.18.0.12:9090 check fall 3 rise 2 server admin-server-2-host 172.18.0.13:9090 check fall 3 rise 2
-
파일 유효성 확인 후 재시작
haproxy -f haproxy.cfg -c systemctl restart haproxy
- haproxy container 의 바인딩 포트인 80 / 9090 으로 확인 한다.