기존에 Tomcat에 WAR 파일을 배포하던 방식으로 운영하던 서비스를, 이번 신규 프로젝트는 Docker 이미지로 납품받기로 했습니다. 인프라 엔지니어가 Docker로 서비스를 올리려고 하는데, WAR 배포와 뭐가 다른지, 어떻게 관리해야 하는지 감이 안 옵니다. "컨테이너가 죽으면 어떻게 되나요?", "로그는 어디서 보나요?", "배포는 어떻게 하나요?" — 기존 WAS 운영 경험이 있다면 Docker도 금방 익힐 수 있습니다.
- 1WAR 배포와 Docker 배포의 구조적 차이를 설명할 수 있다
- 2docker-compose.yml로 서비스를 정의하고 배포할 수 있다
- 3컨테이너 상태 확인과 로그를 실시간으로 모니터링할 수 있다
- 4컨테이너 무중단 배포(pull → up -d) 절차를 수행할 수 있다
- 5볼륨 마운트와 환경변수로 설정을 외부화할 수 있다
docker --version && docker-compose --versionsystemctl status dockerdocker psWAR 배포 vs Docker 배포 비교
두 배포 방식의 구조적 차이
Tomcat에 WAR를 올리는 방식과 Docker 컨테이너로 실행하는 방식은 근본적으로 다릅니다. WAR 배포는 서버에 미리 설치된 JDK와 Tomcat 위에서 실행되지만, Docker 이미지는 JDK와 Tomcat까지 포함해서 패키징됩니다.

SI 프로젝트에서 납품된 Docker 이미지를 서버에 올려야 하는데, 기존 Tomcat 배포 방식으로 접근하다 보니 막히는 지점이 생깁니다. "WAR는 어디에 복사하나요?" "catalina.out은 어디서 보나요?" — 질문 자체가 틀렸습니다. Docker 배포는 파일 복사가 아니라 이미지 태그 교체입니다. 로그도 docker logs로 봅니다. 기존 WAS 운영 경험이 있다면 대응 관계를 잡는 것이 빠릅니다. setenv.sh 대신 환경변수, systemctl 대신 docker-compose, webapps/ 대신 이미지 레지스트리 — 개념만 연결되면 Docker 운영은 금방 익숙해집니다.
| 항목 | WAR 배포 (Tomcat) | Docker 배포 |
|---|---|---|
| 실행 단위 | WAR 파일 | 컨테이너 이미지 |
| 환경 의존성 | 서버의 JDK, Tomcat 버전에 의존 | 이미지 내부에 모두 포함 |
| 서버 설정 | server.xml, setenv.sh | docker-compose.yml |
| 로그 위치 | /opt/tomcat/logs/ | docker logs 또는 볼륨 마운트 |
| 배포 방법 | WAR 복사 → Tomcat restart | docker pull → docker-compose up -d |
| 롤백 | 이전 WAR 파일 복원 | 이전 이미지 태그로 전환 |
| 프로세스 관리 | systemctl | docker-compose 또는 docker run |
Docker 배포가 유리한 상황:
- 개발/스테이징/운영 환경을 완전히 동일하게 유지해야 할 때
- 마이크로서비스로 여러 서비스를 한 서버에서 실행할 때
- CI/CD 파이프라인으로 빌드된 이미지를 그대로 배포할 때
WAR 배포가 여전히 유리한 상황:
- 레거시 시스템에서 Docker 인프라가 없을 때
- JEUS, WebLogic 같은 상용 WAS를 사용해야 할 때
- 조직의 보안 정책상 컨테이너 사용이 제한될 때
Docker 기본 명령어
컨테이너 실행과 상태 확인
Docker를 처음 접하는 인프라 엔지니어는 기존 WAS 관리 명령어와 대응해서 익히면 빠릅니다.
# 이미지 다운로드
docker pull myregistry.example.com/myapp:1.2.0
# 컨테이너 실행 (기본)
docker run -d \
--name myapp \
-p 8080:8080 \
-v /opt/logs:/app/logs \
--restart unless-stopped \
myregistry.example.com/myapp:1.2.0
# 실행 중인 컨테이너 목록
docker ps
# CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
# a1b2c3d4e5f6 myapp:1.2.0 "java -jar" Up 3 hours 0.0.0.0:8080->8080/tcp myapp
# 모든 컨테이너 (중지된 것 포함)
docker ps -a
# 실시간 로그 (tail -f와 동일)
docker logs -f myapp
# 최근 100줄 + 실시간
docker logs --tail 100 -f myapp
# 컨테이너 리소스 사용량
docker stats myapp
# 컨테이너 내부 접속 (디버깅용)
docker exec -it myapp /bin/sh
# 컨테이너 중지/시작/재시작
docker stop myapp
docker start myapp
docker restart myapp
주요 옵션 설명:
| 옵션 | 의미 | Tomcat 비교 |
|---|---|---|
-d | 백그라운드 실행 | nohup / systemctl start |
-p 8080:8080 | 호스트:컨테이너 포트 바인딩 | server.xml Connector port |
-v /opt/logs:/app/logs | 볼륨 마운트 | log 파일 경로 설정 |
--name myapp | 컨테이너 이름 | 서비스 이름 |
--restart unless-stopped | 자동 재시작 | systemctl enable |
docker-compose로 서비스 관리
docker-compose.yml 구성과 운영
실제 운영에서는 docker run 명령보다 docker-compose.yml 파일로 서비스를 정의하는 방식을 사용합니다. 설정이 파일로 관리되어 형상관리가 가능하고, 여러 서비스를 한 번에 관리할 수 있습니다.
# /opt/app/docker-compose.yml
version: '3.8'
services:
app:
image: myregistry.example.com/myapp:1.2.0
container_name: myapp
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- DB_HOST=db-server
- DB_PORT=3306
- DB_NAME=mydb
volumes:
- /opt/app/logs:/app/logs
- /opt/app/config:/app/config:ro
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "100m"
max-file: "5"
주요 설정 항목:
environment: 환경변수 주입 (Tomcat의 setenv.sh 역할)volumes: 로그, 설정 파일을 호스트와 공유healthcheck: 서비스 정상 여부 자동 확인logging: 로그 파일 크기/개수 제한 (logrotate 역할)
docker-compose 운영 명령어:
# 서비스 시작 (백그라운드)
docker-compose up -d
# 서비스 중지 (컨테이너 삭제하지 않음)
docker-compose stop
# 서비스 재시작
docker-compose restart
# 이미지 pull 후 재시작 (배포)
docker-compose pull && docker-compose up -d
# 서비스 로그
docker-compose logs -f app
# 서비스 상태 확인
docker-compose ps

컨테이너 배포 절차
이미지 기반 무중단 배포 흐름
Docker 배포는 이미지 태그 교체 방식으로 롤백이 간단합니다. 배포 전 이전 이미지 태그를 기록해두는 것이 핵심입니다.
# 현재 실행 중인 이미지 버전 확인
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
# 1. 새 이미지 pull (서비스 중단 없이)
docker-compose pull
# 2. 컨테이너 재생성 (1~2초 중단)
docker-compose up -d
# 3. 배포 확인
docker ps
docker logs --tail 20 myapp
curl -s http://localhost:8080/actuator/health
# 롤백 (이전 태그로 변경)
# docker-compose.yml에서 image: myapp:1.1.0 으로 변경 후
docker-compose up -d
Private Registry에서 이미지 받기:
# Registry 로그인
docker login myregistry.example.com -u user -p password
# 이미지 확인
docker images | grep myapp
# 불필요한 이전 이미지 정리
docker image prune -f
실습: docker-compose 배포 실습
현재 서비스 상태를 확인하고 새 이미지를 pull합니다.
# 서비스 상태
docker-compose ps
# 새 이미지 pull
docker-compose pull
# 컨테이너 재시작
docker-compose up -d
# 배포 후 상태 확인
docker-compose ps
docker-compose logs --tail 30 app
docker-compose ps && docker-compose pull- docker-compose ps에서 State가 Up인지 확인
- docker-compose logs에서 'Started' 또는 'Server startup' 메시지 확인
- curl http://localhost:8080/health 에서 200 응답 확인
- docker stats로 CPU/메모리 정상 범위 확인
컨테이너 리소스 사용량을 확인합니다.
# 리소스 사용량 스냅샷
docker stats --no-stream
# 실시간 모니터링
docker stats
# 최근 에러 로그 확인
docker-compose logs app | grep -E "ERROR|Exception|WARN" | tail -20
# 컨테이너 내부 프로세스 확인
docker exec myapp ps aux
docker stats --no-stream- MEM USAGE가 컨테이너에 설정된 limit 이하인지 확인
- CPU 사용률이 비정상적으로 높지 않은지 확인
- 에러 로그가 없거나 허용 범위 내인지 확인
트러블슈팅
원인: 애플리케이션 시작 실패. 주로 필수 환경변수 미설정, DB 연결 실패, 포트 충돌이 원인입니다.
# 종료된 컨테이너 로그 확인 (가장 중요)
docker logs myapp
# 컨테이너 상세 정보 확인
docker inspect myapp | grep -A 10 '"State"'
# 환경변수 확인
docker exec myapp env | grep -E "DB|SPRING|APP"
# 포트 충돌 확인
ss -tlnp | grep 8080
해결: docker logs에서 에러 메시지 확인 → 환경변수 추가 또는 수정 → docker-compose up -d --force-recreate로 재시작
원인: 호스트의 디렉터리 소유자와 컨테이너 내 프로세스 실행 UID가 다른 경우 발생합니다.
# 호스트 디렉터리 권한 확인
ls -la /opt/app/logs
# 컨테이너 내 실행 사용자 확인
docker exec myapp id
docker exec myapp whoami
# 컨테이너 내 마운트 경로 권한 확인
docker exec myapp ls -la /app/logs
해결:
# 방법 1: 호스트 디렉터리 소유자를 컨테이너 UID에 맞춤
sudo chown -R 1000:1000 /opt/app/logs
# 방법 2: 권한을 777로 임시 허용 (보안 주의)
sudo chmod 777 /opt/app/logs
기존 Tomcat 서비스를 Docker로 전환할 때 체크포인트:
SI 프로젝트에서 "이번 버전부터 Docker로 납품한다"는 요청이 늘고 있습니다. 기존 WAS 운영 방식과 달라지는 핵심 사항:
- 설정 파일 위치 — server.xml, context.xml 대신 환경변수와 볼륨 마운트로 외부화
- 로그 경로 —
/opt/tomcat/logs/대신 볼륨 마운트 경로로 통일 - 프로세스 관리 —
systemctl restart tomcat대신docker-compose restart - 헬스체크 — URL 헬스체크를 docker-compose healthcheck로 설정
- 네트워크 — 포트 바인딩(-p)으로 외부 노출, 내부 서비스끼리는 docker network로 통신
# 운영 서버에서 자주 쓰는 Docker 명령어 모음
alias dc="docker-compose"
alias dps="docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'"
alias dlogs="docker logs --tail 100 -f"
다음 모듈에서는 systemd 서비스 등록과 자동 재시작 설정 — 컨테이너 외 환경에서의 서비스 관리를 다룹니다.