이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- Container lifecycle은 Spring 애플리케이션의 시작, 종료, 재시작 판단에 어떤 영향을 주는가?
created,running,exited,restarting,unhealthy상태를 어떻게 해석해야 하는가?- container 재시작 전에 어떤 로그와 설정을 보존해야 하는가?
개요
Container는 image를 실행한 인스턴스다.
같은 image라도 환경 변수, mount, network, command, resource limit이 다르면 다른 container로 동작한다.
운영에서 중요한 것은 container를 언제든 교체할 수 있게 만들되, 교체하면 사라지는 것과 보존해야 하는 것을 구분하는 것이다.
Spring 애플리케이션의 lifecycle은 Docker 상태, JVM shutdown, health check, log 보존과 연결된다.
Container 상태
상태는 docker ps -a로 본다.
docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"대표 상태는 다음이다.
Created: container는 만들어졌지만 시작되지 않았다.Up: 실행 중이다.Exited: 프로세스가 종료됐다.Restarting: restart policy에 따라 반복 재시작 중이다.Up ... (unhealthy): 프로세스는 살아 있지만 health check가 실패한다.
Up이라고 해서 Spring API가 정상이라는 뜻은 아니다.
health, logs, port, dependency를 함께 봐야 한다.
실행 옵션이 만드는 차이
container lifecycle은 image만으로 결정되지 않는다.
docker run -d \
--name order-api \
--restart unless-stopped \
-p 8080:8080 \
--env-file /etc/order-api/order-api.env \
--memory 512m \
registry.example.com/order-api:3f21a9c여기에는 운영에 중요한 정보가 들어 있다.
- restart policy
- port mapping
- environment file
- memory limit
- container name
- image tag
장애 중에는 image보다 먼저 실제 실행 옵션을 확인해야 할 때가 많다.
docker inspect order-api시작 실패
container가 바로 종료되면 logs를 먼저 본다.
docker logs --tail 200 order-api
docker inspect order-api --format '{{.State.ExitCode}} {{.State.Error}}'Spring에서는 다음 원인이 흔하다.
- jar 경로 오류
- active profile 오류
- DB 연결 실패
- port bind 실패
- 환경 변수 누락
- permission denied
- JVM memory option 문제
docker run을 반복하기 전에 exit code와 마지막 로그를 보존해야 한다.
종료와 Signal
Docker stop은 기본적으로 SIGTERM을 보내고 일정 시간 후 SIGKILL을 보낸다.
Spring Boot는 graceful shutdown을 설정하면 진행 중인 요청을 정리할 시간을 가질 수 있다.
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30sdocker stop --time 30 order-apidocker kill은 즉시 SIGKILL을 보내므로 마지막 수단으로 본다.
DB transaction, file upload, message consumer가 있는 서비스에서는 강제 종료가 데이터 처리 중단을 만들 수 있다.
Restart Policy
restart policy는 장애를 자동으로 덮는 장치가 아니다.
always나 unless-stopped는 서버 재부팅 후 자동 시작에 유용하지만, 설정 오류로 즉시 죽는 container를 무한 재시작시킬 수 있다.
재시작 폭주가 있으면 logs가 밀리고 외부 dependency에 연결 폭주를 만들 수 있다.
docker events --since 10m
docker inspect order-api --format '{{.RestartCount}}'restart count가 갑자기 증가하면 원인을 먼저 봐야 한다.
운영 체크리스트
- container name과 image tag가 release note와 일치하는가?
docker inspect로 env, mount, network, restart policy를 확인할 수 있는가?- stop timeout과 Spring graceful shutdown 설정이 맞는가?
- restart policy가 장애를 숨기지 않는가?
- health check 실패와 process 종료를 구분하는가?
- 재시작 전 logs와 inspect 결과를 보존하는가?
- container 내부 상태에 중요한 데이터를 저장하지 않는가?
장애 신호
- container가
Restarting상태로 반복된다. ExitCode가 1 또는 137로 남는다.Up이지만 health check는unhealthy다.- restart count가 배포 직후 급증한다.
docker logs에 같은 Spring startup failure가 반복된다.- stop 후에도 port가 계속 사용 중이다.
- container 재생성 후 내부 파일이 사라졌다.
안전한 완화 조치
재시작 반복이 보이면 신규 배포를 멈추고 container 상태를 고정한다.
무작정 docker rm을 하기 전에 docker logs, docker inspect, docker events를 저장한다.
이전 image와 동일한 env/mount/network로 새 container를 띄울 수 있는지 확인한다.
health check 실패만 있는 경우에는 process 재시작보다 dependency 상태와 health endpoint 기준을 먼저 본다.
강제 종료는 in-flight request와 데이터 처리 영향을 확인한 뒤 실행한다.
인프라 담당자와 공유할 자료
서비스: order-api
container: order-api
image: order-api:3f21a9c
상태: Restarting, RestartCount=12
ExitCode: 1
로그: DB_URL missing
최근 변경: env file 경로 변경
요청: 배포 스크립트의 --env-file 경로와 secret mount 확인container lifecycle 문제는 상태, exit code, restart count, 실행 옵션을 함께 줘야 한다.
실전 팁
docker ps는 시작점이고docker inspect가 실제 계약을 보여준다.- container 삭제 전 logs와 inspect 결과를 남긴다.
docker stoptimeout은 Spring graceful shutdown과 맞춘다.- restart policy는 편의가 아니라 운영 정책으로 정한다.
- health failure와 process exit을 구분해서 대응한다.
위험 신호!
- restart count를 보지 않고 정상 실행으로 판단한다.
docker rm -f로 증거를 먼저 지운다.docker kill을 일반 종료처럼 사용한다.- container 내부 임시 파일에 중요한 데이터를 둔다.
- image는 같지만 실행 env가 다르다는 점을 놓친다.
확인 질문
확인 질문
- container가
Up상태인데도 장애일 수 있는 이유는 무엇인가?
- 프로세스는 살아 있어도 health check, DB 연결, 외부 API, port mapping, application readiness가 실패할 수 있기 때문이다.
docker rm전에 보존해야 할 증거는 무엇인가?
- logs, inspect 결과, exit code, restart count, events, env/mount/network 정보를 보존해야 한다.
- restart policy를 조심해야 하는 이유는 무엇인가?
- 설정 오류나 dependency 장애를 계속 재시작해 로그를 밀고 외부 시스템 연결 폭주를 만들 수 있기 때문이다.