infra
Platform

모듈 맵

[Infra Ops] 컨테이너 배포 vs WAS 배포 비교와 실무 전환

0 / 52 완료

펼치기
0 / 52 완료0%

Infra-ops · 38 / 52

[Infra Ops] 컨테이너 배포 vs WAS 배포 비교와 실무 전환

Docker 컨테이너 배포 흐름, 기존 WAS 배포와 차이점, docker-compose 운영, 마이그레이션 고려사항까지 — 온프레미스 WAS 환경을 컨테이너로 전환하는 실무

🚨INCIDENT ALERT
HIGH

기존에 Tomcat에 WAR 파일을 배포하던 방식으로 운영하던 서비스를, 이번 신규 프로젝트는 Docker 이미지로 납품받기로 했습니다. 인프라 엔지니어가 Docker로 서비스를 올리려고 하는데, WAR 배포와 뭐가 다른지, 어떻게 관리해야 하는지 감이 안 옵니다. "컨테이너가 죽으면 어떻게 되나요?", "로그는 어디서 보나요?", "배포는 어떻게 하나요?" — 기존 WAS 운영 경험이 있다면 Docker도 금방 익힐 수 있습니다.

이번 챕터에서 배울 것
  • 1WAR 배포와 Docker 배포의 구조적 차이를 설명할 수 있다
  • 2docker-compose.yml로 서비스를 정의하고 배포할 수 있다
  • 3컨테이너 상태 확인과 로그를 실시간으로 모니터링할 수 있다
  • 4컨테이너 무중단 배포(pull → up -d) 절차를 수행할 수 있다
  • 5볼륨 마운트와 환경변수로 설정을 외부화할 수 있다
실습 환경 준비
Docker 설치 확인
docker --version && docker-compose --version
Docker 서비스 상태 확인
systemctl status docker
현재 실행 중인 컨테이너 확인
docker ps

WAR 배포 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.shdocker-compose.yml
로그 위치/opt/tomcat/logs/docker logs 또는 볼륨 마운트
배포 방법WAR 복사 → Tomcat restartdocker pull → docker-compose up -d
롤백이전 WAR 파일 복원이전 이미지 태그로 전환
프로세스 관리systemctldocker-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 파일로 서비스를 정의하는 방식을 사용합니다. 설정이 파일로 관리되어 형상관리가 가능하고, 여러 서비스를 한 번에 관리할 수 있습니다.

YAML
# /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 배포는 이미지 태그 교체 방식으로 롤백이 간단합니다. 배포 전 이전 이미지 태그를 기록해두는 것이 핵심입니다.

로컬 터미널
# 현재 실행 중인 이미지 버전 확인
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 배포 실습

1docker-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/메모리 정상 범위 확인
2컨테이너 리소스와 로그 모니터링

컨테이너 리소스 사용량을 확인합니다.

로컬 터미널
# 리소스 사용량 스냅샷
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 운영 방식과 달라지는 핵심 사항:

  1. 설정 파일 위치 — server.xml, context.xml 대신 환경변수와 볼륨 마운트로 외부화
  2. 로그 경로/opt/tomcat/logs/ 대신 볼륨 마운트 경로로 통일
  3. 프로세스 관리systemctl restart tomcat 대신 docker-compose restart
  4. 헬스체크 — URL 헬스체크를 docker-compose healthcheck로 설정
  5. 네트워크 — 포트 바인딩(-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 서비스 등록과 자동 재시작 설정 — 컨테이너 외 환경에서의 서비스 관리를 다룹니다.

지식 확인

퀴즈 — 4문제

Q1

Docker 컨테이너 배포가 WAR 배포보다 환경 일관성이 높은 이유는?

Q2

`docker-compose up -d`와 `docker-compose up`의 차이는?

Q3

실행 중인 컨테이너의 로그를 실시간으로 보는 명령어는?

Q4

컨테이너 재시작 정책 `restart: unless-stopped`의 의미는?

0 / 4 답변

🧪 실습으로 확인하기

Nginx 설치 및 기동

초급

Linux 서버에 Nginx를 설치하고 systemd 서비스로 등록하여 80포트에서 응답하는 상태까지 만든다.

30📋 3단계💻 직접 환경
실습 시작하기 →

이것도 배워보세요

infra-ops중급 · 60
[Infra Ops] 작업계획서 작성과 변경관리 체크리스트
인프라 서비스 운영 트랙 계속
linux입문 · 30
[Linux] 개발자가 왜 리눅스 서버와 커맨드라인을 반드시 배워야 하는가
Linux 트랙 시작점