가상 호스트와 웹 애플리케이션 배포
Tomcat은 Host 설정을 통해 여러 가상 호스트를 운영하고, WAR 파일 또는 디렉터리를 자동·수동으로 배포할 수 있습니다. 이 챕터에서는 실무에서 자주 쓰는 배포 패턴과 안전한 운영 방법을 다룹니다.
웹 애플리케이션 배포 방식
Tomcat에는 세 가지 배포 방식이 있습니다.
| 방식 | 방법 | 특징 |
|---|---|---|
| 자동 배포 | WAR 파일을 webapps/에 복사 | 간단, 프로덕션 비권장 |
| 수동 배포 | Context XML 파일 생성 | 명시적, 권장 |
| Manager 앱 | HTTP PUT 또는 웹 UI | CI/CD 통합 가능 |
WAR 파일 자동 배포
autoDeploy="true"로 설정된 Host에서는 webapps/ 디렉터리에 WAR 파일을 복사하면 자동으로 배포됩니다.
# WAR 파일을 배포 디렉터리에 복사
sudo cp myapp-1.2.0.war /opt/tomcat/latest/webapps/myapp.war
# Tomcat이 자동으로 압축 해제
# /opt/tomcat/latest/webapps/myapp/ ← 생성됨
# 접근 URL: http://host:8080/myapp/
자동 배포 설정 (server.xml)
<Host name="localhost" appBase="webapps"
unpackWARs="true"
autoDeploy="true"
deployOnStartup="true">
| 속성 | 설명 | 프로덕션 권장 |
|---|---|---|
unpackWARs | WAR 자동 압축 해제 | true |
autoDeploy | 런타임 WAR 자동 감지·배포 | false |
deployOnStartup | 시작 시 appBase 스캔 | true |
프로덕션 주의:
autoDeploy="true"+autoDeploy="true"는 Tomcat이 실행 중에 파일 시스템을 주기적으로 검사합니다. 불완전하게 복사 중인 WAR가 배포될 수 있으므로, 프로덕션에서는autoDeploy="false"를 권장합니다.
Context XML 파일로 수동 배포 (권장)
conf/Catalina/localhost/ 디렉터리에 Context XML 파일을 생성하는 방식으로, 배포 경로를 명시적으로 제어할 수 있습니다.
$CATALINA_HOME/conf/
└── Catalina/
└── localhost/
├── ROOT.xml → / (루트 컨텍스트)
├── myapp.xml → /myapp
└── api.xml → /api
기본 Context XML
<!-- conf/Catalina/localhost/myapp.xml -->
<Context docBase="/var/www/myapp"
path="/myapp"
reloadable="false"
crossContext="false"
privileged="false">
</Context>
JNDI 데이터소스 포함
<!-- conf/Catalina/localhost/myapp.xml -->
<Context docBase="/var/www/myapp"
path="/myapp"
reloadable="false">
<Resource name="jdbc/myDB"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.mysql.cj.jdbc.Driver"
url="jdbc:mysql://db-host:3306/mydb?useSSL=false&serverTimezone=Asia/Seoul"
username="appuser"
password="AppP@ssw0rd"
maxTotal="50"
maxIdle="10"
minIdle="5"
maxWaitMillis="30000"
validationQuery="SELECT 1"
testOnBorrow="true"
removeAbandoned="true"
removeAbandonedTimeout="60"
logAbandoned="true"/>
</Context>
ROOT 컨텍스트 교체 (루트 경로에 앱 배포)
<!-- conf/Catalina/localhost/ROOT.xml -->
<Context docBase="/var/www/myapp/current"
reloadable="false">
</Context>
이 설정으로 /var/www/myapp/current 디렉터리의 앱이 http://host:8080/에서 서빙됩니다.
다중 가상 호스트 설정
server.xml 설정
<Engine name="Catalina" defaultHost="localhost">
<!-- 기본 호스트 (개발용) -->
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="false">
</Host>
<!-- 프로덕션 호스트 1 -->
<Host name="app1.example.com" appBase="/var/www/app1"
unpackWARs="true" autoDeploy="false">
<Alias>www.app1.example.com</Alias>
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="app1_access_log"
suffix=".txt"
pattern="%h %l %u %t "%r" %s %b %D"/>
</Host>
<!-- 프로덕션 호스트 2 -->
<Host name="app2.example.com" appBase="/var/www/app2"
unpackWARs="true" autoDeploy="false">
</Host>
</Engine>
각 가상 호스트 Context 파일 구조
conf/
└── Catalina/
├── localhost/
│ └── ROOT.xml
├── app1.example.com/
│ └── ROOT.xml → app1의 루트 컨텍스트
└── app2.example.com/
└── ROOT.xml → app2의 루트 컨텍스트
Tomcat Manager를 통한 배포
Manager 앱 계정 설정
<!-- conf/tomcat-users.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<tomcat-users>
<!-- 관리자 계정 (강력한 패스워드 사용!) -->
<role rolename="manager-gui"/>
<role rolename="manager-script"/>
<role rolename="admin-gui"/>
<user username="admin"
password="MyStr0ngP@ssw0rd!"
roles="manager-gui,manager-script,admin-gui"/>
</tomcat-users>
Manager 앱 접근 IP 제한
기본적으로 Manager 앱은 localhost에서만 접근 가능합니다. 원격 접근이 필요하다면:
<!-- webapps/manager/META-INF/context.xml -->
<Context antiResourceLocking="false" privileged="true">
<CookieProcessor className="org.apache.tomcat.util.http.Rfc6265CookieProcessor"
sameSiteCookies="strict"/>
<Valve className="org.apache.catalina.valves.RemoteAddrValve"
allow="127\.0\.0\.1|192\.168\.1\.\d+"/>
<!-- localhost + 내부망만 허용 -->
</Context>
curl로 원격 배포 (CI/CD 통합)
# WAR 파일 배포
curl -u admin:MyStr0ngP@ssw0rd! \
"http://localhost:8080/manager/text/deploy?path=/myapp&update=true" \
--upload-file /path/to/myapp.war
# 애플리케이션 목록 확인
curl -u admin:MyStr0ngP@ssw0rd! \
"http://localhost:8080/manager/text/list"
# 애플리케이션 언배포
curl -u admin:MyStr0ngP@ssw0rd! \
"http://localhost:8080/manager/text/undeploy?path=/myapp"
# 재시작
curl -u admin:MyStr0ngP@ssw0rd! \
"http://localhost:8080/manager/text/restart?path=/myapp"
무중단 배포 패턴
패턴 1: WAR 파일 교체 + 즉시 재시작
#!/bin/bash
set -e
CATALINA_HOME=/opt/tomcat/latest
APP_NAME=myapp
WAR_FILE=/tmp/myapp-1.2.0.war
# 1. 이전 WAR 백업
if [ -f "$CATALINA_HOME/webapps/${APP_NAME}.war" ]; then
cp "$CATALINA_HOME/webapps/${APP_NAME}.war" \
"/var/backup/${APP_NAME}-$(date +%Y%m%d-%H%M%S).war"
fi
# 2. 기존 배포 디렉터리 제거 (stale class 방지)
rm -rf "$CATALINA_HOME/webapps/${APP_NAME}"
rm -f "$CATALINA_HOME/webapps/${APP_NAME}.war"
# 3. 작업 디렉터리 초기화
rm -rf "$CATALINA_HOME/work/Catalina/localhost/${APP_NAME}"
# 4. 새 WAR 복사
cp "$WAR_FILE" "$CATALINA_HOME/webapps/${APP_NAME}.war"
# 5. Tomcat 재시작
sudo systemctl restart tomcat
echo "배포 완료: ${APP_NAME}"
패턴 2: 심볼릭 링크 + 무중단 (Nginx 앞단)
#!/bin/bash
# Blue-Green 배포 (Nginx + 2개 Tomcat 인스턴스)
BLUE_PORT=8080
GREEN_PORT=8081
NEW_WAR=/tmp/myapp-1.2.0.war
# 현재 Active 인스턴스 확인
ACTIVE=$(cat /var/run/active-tomcat)
if [ "$ACTIVE" = "blue" ]; then
TARGET_PORT=$GREEN_PORT
NEW_ACTIVE="green"
else
TARGET_PORT=$BLUE_PORT
NEW_ACTIVE="blue"
fi
# 비활성 인스턴스에 배포
cp "$NEW_WAR" "/opt/tomcat-${NEW_ACTIVE}/webapps/ROOT.war"
systemctl restart "tomcat-${NEW_ACTIVE}"
# 헬스체크
sleep 10
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
http://localhost:${TARGET_PORT}/actuator/health)
if [ "$HTTP_STATUS" = "200" ]; then
# Nginx 설정 전환
sed -i "s/proxy_pass http:\/\/localhost:[0-9]*/proxy_pass http:\/\/localhost:${TARGET_PORT}/" \
/etc/nginx/sites-available/myapp.conf
nginx -s reload
echo "$NEW_ACTIVE" > /var/run/active-tomcat
echo "✅ 배포 성공: ${NEW_ACTIVE} (포트 ${TARGET_PORT})"
else
echo "❌ 헬스체크 실패 — 롤백"
exit 1
fi
배포 후 확인
# Tomcat 로그로 배포 상태 확인
tail -100 /opt/tomcat/latest/logs/catalina.out | grep -E "(deploy|error|warn)" -i
# 컨텍스트 로드 성공 메시지
# INFO [main] org.apache.catalina.startup.HostConfig.deployWAR
# Deployment of web application archive [.../myapp.war] has finished in X ms
# 접근 테스트
curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/myapp/
# 200
# Manager API로 배포된 앱 목록 확인
curl -u admin:password http://localhost:8080/manager/text/list
Summary
| 항목 | 내용 |
|---|---|
| 기본 배포 경로 | $CATALINA_HOME/webapps/ |
| 권장 배포 방식 | Context XML 파일 + autoDeploy="false" |
| ROOT 컨텍스트 | conf/Catalina/localhost/ROOT.xml |
| Manager 보안 | 강력한 패스워드 + IP 제한 |
| CI/CD 통합 | Manager REST API (curl 사용) |
| 무중단 배포 | Blue-Green 패턴 + Nginx 포트 전환 |