docker를 활용한 apache-tomcat 이중화 서버 구현
개요
이 글은 docker를 활용하여 httpd(apache) - tomcat 이중화 서버를 구현하는 방법을 정리한 글이다.
이 글에서 사용된 소스코드는 github 에 업로드 되어 있으며 프로젝트 구조는 다음과 같다.
디렉토리 구조
├── README.md
├── docker
│ ├── docker-compose-httpd.yml
│ └── docker-compose-tomcat.yml
├── httpd
│ ├── build
│ │ ├── Dockerfile
│ │ └── run-httpd.sh
│ ├── conf
│ │ ├── httpd.conf
│ │ └── magic
│ └── conf.d
│ ├── README
│ ├── autoindex.conf
│ ├── mod_jk.conf
│ ├── uriworkermap.properties
│ ├── userdir.conf
│ ├── welcome.conf
│ └── workers.properties
├── network.sh
├── scripts
│ └── utils.sh
├── target
│ └── ROOT.war
└── tomcat
├── build1
│ ├── Dockerfile
│ ├── setenv.sh
│ └── test.sh
├── build2
│ ├── Dockerfile
│ ├── setenv.sh
│ └── test.sh
├── conf1
│ ├── Catalina
│ │ └── localhost
│ ├── catalina.policy
│ ├── catalina.properties
│ ├── context.xml
│ ├── jaspic-providers.xml
│ ├── jaspic-providers.xsd
│ ├── logging.properties
│ ├── server.xml
│ ├── tomcat-users.xml
│ ├── tomcat-users.xsd
│ └── web.xml
└── conf2
├── Catalina
│ └── localhost
├── catalina.policy
├── catalina.properties
├── context.xml
├── jaspic-providers.xml
├── jaspic-providers.xsd
├── logging.properties
├── server.xml
├── tomcat-users.xml
├── tomcat-users.xsd
└── web.xml
사용기술
- docker
- docker-compose
- mod_jk, ajp/1.3
버전정보(Dockerfile)
- centos:7
- apache 2.4
- tomcat 9.0.45
- tomcat-connectors 1.2.48
- java-1.8.0-openjdk-devel.x86_64
1. Dockerfile
1.1 Httpd(Apache)
FROM centos:7
RUN yum -y update && yum clean all
RUN yum -y install httpd httpd-devel gcc* make && yum clean all
# Install mod_jk
RUN curl -SL https://downloads.apache.org/tomcat/tomcat-connectors/jk/tomcat-connectors-1.2.48-src.tar.gz -o tomcat-connectors-1.2.48-src.tar.gz \
&& mkdir -p /src/tomcat-connectors \
&& tar xzf tomcat-connectors-1.2.48-src.tar.gz -C src/tomcat-connectors --strip-components=1 \
&& cd src/tomcat-connectors/native/ \
&& ./configure --with-apxs=/usr/bin/apxs \
&& make \
&& cp apache-2.0/mod_jk.so /usr/lib64/httpd/modules/ \
&& cd / \
&& rm -rf src/ \
&& rm -f tomcat-connectors-1.2.48-src.tar.gz
EXPOSE 80
# Timezone
RUN mv /etc/localtime /etc/localtime_org \
&& ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# Simple startup script to avoid some issues observed with container restart
ADD run-httpd.sh /run-httpd.sh
RUN chmod -v +x /run-httpd.sh
CMD ["/run-httpd.sh"]
centos 7 기반에서 apache 웹 서버를 설치하고 tomcat과 연동하기 위해 mod_jk를 설치한다.
1.2 Tomcat
FROM centos:7
RUN yum -y update && yum clean all
# Install openjdk 1.8
RUN yum install -y java-1.8.0-openjdk-devel.x86_64
# 다운로드 되는 파일의 버전과 일치
ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el7_9.x86_64
ENV export JAVA_HOME
ENV PATH=$PATH:$JAVA_HOME/bin
ENV export PATH
# Install tomcat
RUN curl -SL https://downloads.apache.org/tomcat/tomcat-9/v9.0.45/bin/apache-tomcat-9.0.45.tar.gz -o apache-tomcat-9.0.45.tar.gz \
&& mkdir -p /src/tomcat/ \
&& tar xzf apache-tomcat-9.0.45.tar.gz -C src/tomcat --strip-components=1 \
&& cd / \
&& mv /src/tomcat /usr/local \
&& rm -rf src/ \
&& rm -f apache-tomcat-9.0.45.tar.gz
ENV CATALINA_HOME=/usr/local/tomcat
ENV CATALINA_BASE=/usr/local/tomcat
ADD ./setenv.sh /usr/local/tomcat/bin
# Timezone
RUN mv /etc/localtime /etc/localtime_org \
&& ln -s /usr/share/zoneinfo/Asia/Seoul /etc/localtime
RUN rm -rf /usr/local/tomcat/webapps/*
ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh", "run"]
EXPOSE 8009
마찬가지로 centos:7 기반에서 openjdk 1.8와 tomcat을 설치한다.
openjdk 1.8을 설치하고 JAVA_HOME환경변수를 설정할 때 해당 경로에 설치된 파일의 버전을 확인하여 일치시켜야 한다.
os시간과 tomcat 시간을 일치시키기 위해 setenv.sh파일을 Dockerfile 같은 경로에 생성한다. setenv.sh의 내용은 다음과 같다.
#! /bin/bash
export CATALINA_OPTS="$CATALINA_OPTS -Dfile.encoding=UTF8 -Duser.timezone=GMT+9"
마지막으로 ajp/1.3 프로토콜에서 사용할 8009포트를 개방한다. tomcat은 ajp/1.3 프로토콜을 사용하여 httpd와 연결된다.
tomcat 이중화를 위해 마지막 개방 포트를 다른 포트로 변경하여 또다른 Dockerfile을 준비한다.
2 Docker-compose
docker-compose를 사용하여 httpd, tomcat을 구동한다.
- docker-compose-httpd.yml
version: '3.7'
services:
httpd:
container_name: httpd
build: ../httpd/build
volumes:
- ../httpd/conf/:/etc/httpd/conf/
- ../httpd/conf.d/:/etc/httpd/conf.d/
ports:
- "80:80"
container의 이름을 httpd로 설정하고 위에서 만든 dockerfile을 사용하여 이미지를 생성한다.
서버의 설정 정보를 담고 있는 conf 폴더를 외부에서 volume mapping하여 사용한다.
- docker-compose-tomcat.yml
version: '3.7'
services:
tomcat1:
container_name: tomcat1
build: ../tomcat/build1
volumes:
- ../target/ROOT.war:/usr/local/tomcat/webapps/ROOT.war
- ../tomcat/conf1/:/usr/local/tomcat/conf/
expose:
- "8009"
ports:
- "8888:8080"
tomcat2:
container_name: tomcat2
build: ../tomcat/build2
volumes:
- ../target/ROOT.war:/usr/local/tomcat/webapps/ROOT.war
- ../tomcat/conf2/:/usr/local/tomcat/conf/
expose:
- "8019"
ports:
- "9999:8080"
두 개의 tomcat container를 tomcat1, tomcat2 이름으로 구동한다. 각 tomcat의 설정 폴더를 외부에서 volume mapping 하여 사용하고 각 Dockerfile에서 개방한 포트와 일치하는 포트를 개방한다. tomcat에서 구동할 서비스는 target폴더 내의 ROOT.war 이름으로 준비한다.
3 Configuration
3.1 Httpd(Apache)
- conf/httpd.conf
ServerName httpd # ----1
Listen 80 # ----2
LoadModule jk_module modules/mod_jk.so # ----3
IncludeOptional conf.d/*.conf # ----4
<VirtualHost *:80>
ServerName localhost
JkMount /* loadbalancer # ----5
<Directory "/message">
Order Allow,Deny
Allow from all
</Directory>
</VirtualHost>
httpd.conf 파일의 일부를 위와 같이 수정/추가한다.
- ServerName은 httpd container 이름과 동일하게 설정하고
- 80포트로 들어온 요청을 listen한다.
- Dockerfile에서 설치한 mod_jk 모듈을 load한다.
- mod_jk와 관련된 설정파일은 conf.d 폴더에 있고 이 폴더를 include한다.
- 마지막으로 80 포트로 들어오는 모든 요청을 loadbalancer로 전달한다.
- conf.d/mod_jk.conf
# workers.properties location
JkWorkersFile /etc/httpd/conf.d/workers.properties # ----1
# url to worker mount
JkMount /* loadbalancer # ----2
# log settings
JkLogFile /var/log/httpd/mod_jk.log # ----3
JkLogLevel warn
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
JkRequestLogFormat "%w %V %T"
# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
conf.d/mod_jk.conf 파일을 위와 같이 생성한다.
- loadbalancer와 worker에 관한 설정 파일을 명시한다.
- 들어오는 모든 요청을 loadbalancer로 전달한다.
- mod_jk 모듈의 로그를 저장할 파일을 설정한다.
- conf.d/workers.properties
worker.list=loadbalancer # ----1
worker.loadbalancer.type=lb # ----2
worker.loadbalancer.balance_workers=worker1,worker2 # ----3
worker.loadbalancer.sticky_session=true # ----4
# server 1
worker.worker1.type=ajp13 # ----5
worker.worker1.host=tomcat1 # ----6
worker.worker1.port=8009 # ----7
worker.worker1.lbfactor=1
worker.worker1.socket_keepalive=1
# server 2 ----8
worker.worker2.type=ajp13
worker.worker2.host=tomcat2
worker.worker2.port=8019
worker.worker2.lbfactor=1
worker.worker2.socket_keepalive=1
conf.d/workers.properties 파일을 위와 같이 생성한다.
- worker.list에 loadbalancer를 추가하고
- loadbalancer의 type을 lb로 설정한다.
- loadbalancer 내의 workers 의 이름을 worker1, worker2로 설정하고
- 동일 접속일 경우 하나의 was에 지속적으로 연결되게 하기 위해 sticky_session을 true로 설정한다.
- worker1의 type을 ajp13으로 설정하고
- host를 container 이름인 tomcat1으로 설정하고
- worker1의 port를 container를 구동할때 개방한 port와 일치시킨다.
- worker2에 대해서도 같은 방법으로 작성한다.
4 Network 구동
스크립트 파일 network.sh를 사용하여 docker 네트워크를 구동한다.
./network.sh up
httpd, tomcat 서버를 각각 재시작할때 다음 명령어를 입력한다.
./network.sh restartHttpd
./network.sh restartTomcat
네트워크를 중지할 때 다음 명령어를 입력한다.
./network.sh down