톰캣을 구동하려면 Java 가 필요하다. 톰캣 자체를 구동하려면 JRE 만 있어도 충분하지만 직접 Java 어플리케이션을 빌드하려면 JDK 가 필요하므로 JRE 대신 JDK 를 설치하도록 하자.

RHEL 에는 OpenJDK 라는 JDK 구현물이 포함되어 있지만 안정적으로 사용하려면 오라클의 JDK 를 사용하는 것이 좋다.  

JDK 설치하기

  1. oracle java 사이트(java.oracle.com) 에서 사용할 JDK를 다운로드 받는다. 주력 제품인 7.x대 중에 가장 마지막 버전중 rpm 패키징을 받으면 된다. 사용하는 리눅스의 아키텍처에 맞는 패키지를 받는 걸 잊지 말자. 거의 대다수가 64비트이지만 잘 모르겠는 독자는 uname -m 명령어를 실행하여 64 항목이 보이면 64비트이다.
     
  2. yum 으로 다운 로드한 패키지를 설치한다. 

    yum localinstall jdk-7u*.rpm -y

  3. 패키지 파일은 /usr/java/jdk-version 명 디렉터리에 설치된다. jdk1.7.0_60 일 경우 /usr/java//jdk1.7.0_60/ 디렉터리에 설치된다.
  4. JDK 는 한 시스템에 여러 버전이 설치될 수 있다. RHEL 은 이렇게 여러 버전이 설치되는 명령어중 특정 버전을 기본 명령어로 간단하게 설정할 수 있는 기능을 제공한다.
    alternative 가 그 명령어로 1.7 버전의 자바를 사용하다가 1.8을 사용하고 싶을 경우 기존 java 명령어를 덮어 쓰지 않고 간단하게 심볼릭 링크 수정으로 교체가 가능하다.
    먼저 alternatives 명령어에 새로 설치한 java를 등록해 보자.

    ## 기존 심볼릭 링크를 삭제한다.
    rm -f /usr/java/jdk1.7
    # 새로운 심볼릭 링크를 생성한다. 설치한 JDK 의 버전이 다르면 JDK 디렉터리명을 수정한다.
    ln -s /usr/java/jdk1.7.0_60 /usr/java/jdk1.7

    alternatives --install /usr/bin/java java /usr/java/jdk1.7/bin/java 20000
    alternatives --install /usr/bin/javac javac /usr/java/jdk1.7/bin/javac 20000
    alternatives --install /usr/bin/javaws javaws /usr/java/jdk1.7/jre/bin/javaws 20000
    alternatives --install /usr/bin/jar jar /usr/java/jdk1.7/bin/jar 20000
    alternatives --install /usr/bin/keytool keytool /usr/java/jdk1.7/bin/keytool 20000


  5. 이제 시스템의 기본 java 명령어를 교체한다. --set 옵션으로 기본 명령어를 설정하거나 --config 명령어로 대화형으로 진행할 수 있다. 우리는 --set 명령어를 사용하자.

    alternatives --set java /usr/java/jdk1.7/bin/java
    alternatives --set javac /usr/java/jdk1.7/bin/javac
    alternatives --set javaws /usr/java/jdk1.7/jre/bin/javaws
    alternatives --set jar /usr/java/jdk1.7/bin/jar
    alternatives --set keytool /usr/java/jdk1.7/bin/keytool


  6. java 에 -version 옵션을 주고 실행해서 버전을 확인하여 제대로 설정되었는지 확인한다.

    javac -version
    javac 1.7.0_60

    # java -version

    java version "1.7.0_60"
    Java(TM) SE Runtime Environment (build 1.7.0_60-b19)
    Java HotSpot(TM) 64-Bit Server VM (build 24.60-b09, mixed mode) 

성공적으로 JDK 설치가 끝났으면 톰캣을 설치할 차례이다. 

톰캣 설치하기

톰캣은 1024 이후의 포트를 사용하므로 일반 사용자로 구동하는 게 바람직하다. JDK 는 루트로 작업했지만 톰캣 설치는 구동할 사용자 계정으로 su 를 하여 일반 사용자로 작업하자.

  1. 톰캣 공식 사이트(http://tomcat.apache.org/download-70.cgi)에서 7.x 버전의 마지막 버전을 다운로드한다. 현재 버전은 7.0.54 이다.

    $ wget http://mirror.apache-kr.org/tomcat/tomcat-7/v7.0.54/bin/apache-tomcat-7.0.54.tar.gz

  2. 다운로드 받은 톰캣의 압축을 해제한다.

    $ tar zxvf apache-tomcat-7.0.54.tar.gz

  3. 압축이 해제된 디렉터리로 이동한다.

    $ cd apache-tomcat-7.0.54

디렉터리 구성

톰캣은 다음과 같은 파일과 디렉터리 구성을 갖고 있다. 각 디렉터리별 용도를 살펴 보자.

  • bin: 톰캣의 바이너리와 실행 스크립트가 있는 디렉터리이다. 이중에서 중요한 용도의 파일들을 정리해 보자.

    파일명용도
    startup.sh톰캣 구동 스크립트
    shutdown.sh톰캣 종료 스크립트 
    version.sh 톰캣의 버전과 OS, JDK 정보를 출력하는 스크립트 
    configtest.sh 설정의 이상 유무를 테스트할 수 있는 스크립트.
    setenv.sh톰캣 실행시 JVM 에 넘겨줄 옵션을 설정하는 파일. 포함되어 있지 않으므로 별도로 생성해야 한다
    톰캣 스크립트별 용도
  • conf: 톰캣의 설정 파일이 위치한다. global configuration applicable to all the webapps. The default installation provides:

    파일명용도
    server.xml가장 중요한 설정 파일로 톰캣의 서비스, 엔진, 호스트등의 중요 설정을 한다. 톰캣의 아키텍처와 동일한 구조를 가지므로 아키텍처를 이해했다면 설정 파일도 쉽게 이해할 수 있을 것이다.
    tomcat-users.xml역할에 기반한 사용자 권한과 인증 정보를 설정한다. 보안때문에 기본적으로는 주석으로 막혀 있다.
    web.xml모든 웹 어플리케이션을 배포할 때 사용할 기본 설정을 지정한다.
    context.xml세션 쿠키 저장 경로등 톰캣 의존적인 설정을 하는 파일이다.
    톰캣 설정 파일 용도
  • lib: 모든 webapps 에서 사용 가능한 JAR 파일들이 위치한다. 기본적으로 자바 서블릿을 구현한 servlet-api.jar와 JSP 를 구현한 jasper.jar 파일이 있다. JDBC 등 전역적으로 사용할 jar 파일들도 여기에 넣어 주면 된다.
  • logs: 기본 로그 파일이 저장되는 디렉터리이다. 카탈리나 엔진은 catalina.{yyyy-mm-dd}.log 파일에 로그를 남기고 server.xml 에 호스트의 로그는 localhost.{yyyy-mm-dd}.log  형식으로 남게 된다.
  • webapps: 웹 어플리케이션이 있는 디렉터리이다. 톰캣을 설치하면 기본적으로 docs, examples, host-manager , manager 네 개의 어플리케이션이 포함되어 있다. 
  • work: 서블릿이나 JSP 파일을 컴파일 한 소스와 클래스 파일이 위치한다. 엔진 이름(Catalina) 디렉터리 밑에 호스트 이름(localhost)  밑에 웹 어플리케이션명으로 계층적으로 디렉터리가 구성된다. myapp 라는 웹 어플리케이션이 있을 경우 work/Catalina/localhost/myapp/ 디렉터리가 생성된다.
  • temp: 임시 파일들을 저장하는 디렉터리이다.

톰캣은 bin 디렉터리에 여러 가지 쉘 스크립트를 포함하고 있다. 이중에서 중요한 용도의 파일들을 정리해 보자.

JVM 옵션 설정

기본 톰캣 설정은 메모리가 작게 설정되어 있으니 커스텀 setenv.sh 을 만들고 JVM 설정을 최적화하자. 에디터로 bin/setenv.sh 를 열어서 다음 내용을 추가한다.

#!/bin/sh
 
MIN_MEMORY="128m"
MAX_MEMORY="512m"
MAX_PERM_SIZE="256m"
SERVICE_NAME="myWebApp"
JAVA_OPTS="-Dcom.example.servicename=${SERVICE_NAME} -Xms${MIN_MEMORY} -Xmx${MAX_MEMORY} -XX:MaxPermSize=${MAX_PERM_SIZE} ${JAVA_OPTS}"
CODE

자바의 가상 머신에 전달할 메모리 관련 옵션에 파라미터로 전달할 설정으로 각각의 의미는 다음과 같다.

  • MIN_MEMORY : 자바의 가상 머신의 초기 할당되는 메모리 풀의 크기. -Xms 옵션의 파라미터로 전달된다.
  • MAX_MEMORY: 가상 머신에 할당되는 메모리의  최대 크기. 큰 어플리케이션을 구동하거나 시스템에 메모리 여유가 있다면 늘리는 것을 권장한다.
  • MAX_PERM_SIZE 가상 머신의 PermGen(Permanent Generation) 영역의 크기를 지정한다. 클래스나 메소드, 기타 객체가 저장되는 영역으로 부족할 경우 OutOfMemoryError 를 만날 수 있다. 특히 톰캣의 manager 를 이용하여 핫 디플로이(Hot deploy) 를 한다면 크기를 더 늘려야 한다.
  • SERVICE_NAME : ps 명령어로 현재 구동된 톰캣 어플리케이션 이름을 가져올 경우 여러 개의 JVM 이 구동되어 있으면 grep java 로 찾을 경우 원하지 않는 JVM 프로세스가 출력될 수 있다. 이를 방지하고 현재 톰캣의 인스턴스를  명확히 식별하기 위해 구동시에 서비스명을 지정하면 grep 을 이용하여 손쉽게 프로세스 정보를 가져올 수 있다. 

시작과 종료

setenv.sh 는  catalina.sh 에서 참고하므로 chmod 로 실행 속성을 주지 않아도 된다.. 이제 JVM 의 옵션도 설정했으니 톰캣을 구동해 보자.

구동은 startup.sh 를 실행하면 구동이 된다.

$ ./bin/startup.sh


Using CATALINA_BASE: /home/lesstif/apache-tomcat-7.0.54
Using CATALINA_HOME: /home/lesstif/apache-tomcat-7.0.54
Using CATALINA_TMPDIR: /home/lesstif/apache-tomcat-7.0.54/temp
Using JRE_HOME: /usr
Using CLASSPATH: /home/lesstif/apache-tomcat-7.0.54/bin/bootstrap.jar:/home/lesstif/apache-tomcat-7.0.54/bin/tomcat-juli.jar
Tomcat started.

톰캣이 정상적으로 구동되면 위와 같은 메시지가 표시된다. 더 자세한 메시지는 logs/catalina.out 에서 볼 수 있다. 이제 브라우저를 통해 톰캣에 연결해 보자.

톰캣은 기본적으로 HTTP 포트로 8080 을 사용한다. 브라우저의 주소창에 톰캣이 설치된 서버의 IP와 톰캣이 사용하는 포트인 8080 을 넣으면 연결되지만 사전에 먼저 해야할 작업이 있다.

보통 톰캣 앞에 웹서버를 위치시키고 외부에서는  80이나 443 포트를 이용해서 연결하게 된다. 

하지만 아직 우리는 아파치 웹서버 설정을 하지 않았으므로 URL 에 톰캣의 서비스 포트인 8080으로 바로 연결해야 하지만 iptables 방화벽 정책때문에 8080 포트는 바로 연결할 수 없다.

톰캣의 정상 구동 테스트를 위해 잠시 8080 포트를 열도록 하자. /etc/sysconfig/iptables 파일을 에디터로 수정하고 iptables 서비스를 재구동해도 되지만 중요 명령어와 유틸리티 장에서 익힌 lokkit 툴을 사용해 보자.

다음 명령어로 8080 포트를 열수 있다. lokkit 명령어는 iptables 서비스를 재구동하므로 바로 설정이 반영된다. 

lokkit -p 8080:tcp

이제 브라우저로 연결하면 다음과 같은 톰캣의 기본 웹 어플리케이션이 표시될 것이다.

톰캣 초기 화면

톰캣의 종료는 bin/shutdown.bat 를 실행하면 된다.

$ ./bin/shutdown.sh

Using CATALINA_BASE: /home/lesstif/apache-tomcat-7.0.54
Using CATALINA_HOME: /home/lesstif/apache-tomcat-7.0.54
Using CATALINA_TMPDIR: /home/lesstif/apache-tomcat-7.0.54/temp
Using JRE_HOME: /usr
Using CLASSPATH: /home/lesstif/apache-tomcat-7.0.54/bin/bootstrap.jar:/home/lesstif/apache-tomcat-7.0.54/bin/tomcat-juli.jar

포트가 사용중이라 톰캣이 시작되지 않을때 처리

톰캣은 내부적으로 관리용으로 TCP 포트를 하나 사용한다. 가끔 톰캣이 제대로 종료되지 않아 관리용 포트는 닫히고 서비스 포트는 열린 상태로 Java VM이 떠 있는 경우가 있다.

shutdown.sh 는 관리용 포트에 연결하여 서비스 종료를 시도하므로 위와 같은 상황에서는 shutdown.sh 로 종료를 할 수도 없고 startup.sh 를 해도 이미 기존 프로세스가 서비스 포트를 사용하므로 시작도 할 수 없다.

프로세스 및 파일을 보는 명령어인 lsof 를 사용하면 포트를 사용중인 프로세스의 정보를 가져올 수 있으므로 PID 를 알아 내어 kill 명령어로 종료할 수 있다.

$ lsof -i TCP:8005,8080,8009

위 화면을 보면 tomcat 을 구동한 JVM 의 프로세스 id 가 7011 이므로 kill 7011 로 종료할 수 있다.

프로세스를 종료할 때 kill -9 로 종료하는 건 좋지 않다. -9 는 KILL 시그널로 STOP 시그널과 함께 프로세스가 잡거나 무시할 수 없는 시그널이므로 프로세스가 즉시 종료되므로 내부에서 사용중인 자원을 제대로 정리하고 종료하지 못할 수 있다.

kill 명령어에 시그널을 생략하면 TERM(inate) 시그널이 전송되며 이 시그널은 핸들러를 등록할 수 있으므로 안전하게 종료될 수 있다.

우측의 NAME 부분에서 mxi, webcache로 표시되는 건 /etc/service 파일에 포트가 해당 서비스명으로 등록되어 있기 때문이다. 보기 불편하면 /etc/service 의 서비스명을 수정하면 되며 사용에는 문제가 없다.

톰캣 프로세스 우아하게 종료시키기

톰캣이 제대로 종료되지 않았을 경우 위에서 설명한대로 lsof 를 사용하여 PID 를 얻어 온 후에 kill 로 종료 시킬 수 있지만 lsof 의 출력물을 가공해야 하므로 자동화가 어려운 단점이 있다.

"중요 명령어와 유틸리티" 에서 다룬 스크립트를 활용하여 톰캣의 PID 를 얻어서 종료시키는 스크립트를 작성해 보자.

#!/bin/sh
 
## 프로세스 이름과 사용자명으로 pid 를 찾아서 종료시키는 함수
killproc() {
    local prog=$1
    local user=$2
    local signal="TERM"
 
    if [ "$#" = 0 ] ; then
        echo $"Usage: killproc {program} {user} {signal}"
        return 1
    fi
 
    if [ "$#" = 3 ]; then
        signal=$3
    fi
    PID=`ps -eaf|grep ${prog}|grep -v grep|grep ${user}|awk '{print $2}'`
    ## process still running..
    if [ ! -z ${PID} ] && [ ${PID} -gt 0 ];then
        echo "kill -${signal} ${PID}"
        kill -${signal} ${PID};
        return 1;
    else
         return 0;
    fi
}
 
## 톰캣 인스턴스 이름. grep 으로 프로세스 찾을 때 java 나 tomcat 으로 찾으면 여러 개의 프로세스가 나올 수 있으므로 이 이름으로 찾는게 좋다. 
## setenv.sh 에 SERVICE_NAME 프로퍼티에 지정한 이름을 적어주자.

SERVICE_NAME="servicename=myWebApp"
 
## 톰캣 홈. 설치 위치에 맞게 수정하자. 디렉터리가 없다면 스크립트를 호출한 위치를 톰캣 홈으로 사용한다.
TC_HOME=/var/tomcat/tomcat-7.0.55
if [ ! -d ${TC_HOME} ];then
        TC_HOME=`pwd`
fi
 
## 사용자 별로 톰캣이 떠 있을수 있으므로 프로세스 소유자 명으로도 필터링한다.
USER=`whoami`
 
cd ${TC_HOME}
./bin/shutdown.sh >& /dev/null
sleep 1 # 톰캣 정상 종료를 위해 1초 대기
 
## 루프를 2번 실행
for i in 1 2;do
    killproc ${SERVICE_NAME} ${USER}
    RET=$?
    if [ $RET = 0 ];then
        break;
    fi;
    sleep $i;
done
 
## 아직도 종료되지 않았다면 KILL 시그널을 전송
killproc "${SERVICE_NAME}" "${USER}" "KILL"
CODE
톰캣 종료 스크립트

위 스크립트를 적당한 이름으로 저장한 후에 (예: shutdown_graceful.sh) chmod +x 로 실행 속성을 부여하자.

그리고 이 스크립트를 실행하면 톰캣이 제대로 종료되지 않아 서비스 쓰레드가 살아 있는 경우에도 종료시킬 수 있으며 톰캣에 어플리케이션을 디플로이할 경우 유용하게 사용할 수 있다.

tomcat 과 vfablic 의 차이점

Architecture