개발이야기

docker를 활용한 apache-tomcat 이중화 서버 구현

adoreje 2021. 5. 13. 10:20

개요

이 글은 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 파일의 일부를 위와 같이 수정/추가한다.

  1. ServerName은 httpd container 이름과 동일하게 설정하고
  2. 80포트로 들어온 요청을 listen한다.
  3. Dockerfile에서 설치한 mod_jk 모듈을 load한다.
  4. mod_jk와 관련된 설정파일은 conf.d 폴더에 있고 이 폴더를 include한다.
  5. 마지막으로 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 파일을 위와 같이 생성한다.

  1. loadbalancer와 worker에 관한 설정 파일을 명시한다.
  2. 들어오는 모든 요청을 loadbalancer로 전달한다.
  3. 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 파일을 위와 같이 생성한다.

  1. worker.list에 loadbalancer를 추가하고
  2. loadbalancer의 type을 lb로 설정한다.
  3. loadbalancer 내의 workers 의 이름을 worker1, worker2로 설정하고
  4. 동일 접속일 경우 하나의 was에 지속적으로 연결되게 하기 위해 sticky_session을 true로 설정한다.
  5. worker1의 type을 ajp13으로 설정하고
  6. host를 container 이름인 tomcat1으로 설정하고
  7. worker1의 port를 container를 구동할때 개방한 port와 일치시킨다.
  8. worker2에 대해서도 같은 방법으로 작성한다.

4 Network 구동

스크립트 파일 network.sh를 사용하여 docker 네트워크를 구동한다.

./network.sh up

httpd, tomcat 서버를 각각 재시작할때 다음 명령어를 입력한다.

./network.sh restartHttpd
./network.sh restartTomcat

네트워크를 중지할 때 다음 명령어를 입력한다.

./network.sh down