Keyword : healthcheck, Actuator, restart policy, container, docker-compose, log
현재 홈서버는 시스템 재시작 시 컨테이너가 자동으로 복구되지 않는 문제가 있다.
CI/CD와 파이프라인을 구축하기전에
먼저 서버가 기본적으로 운영 가능한 상태인지 점검하고 개선했다.

문제 상황 - 시스템 재시작 시 컨테이너가 자동으로 안뜸
본격적인 홈서버 고도화 및 애플리케이션 운영을 위해
오랜만에 노트북을 켜고 docker ps -a를 실행했다.
CONTAINER ID IMAGE STATUS
5233ec28cd4a nginx:alpine Exited (0) 2 weeks ago
f9e4d2301194 cloud-termproject-frontend Exited (0) 2 weeks ago
dc5557dacfec cloud-termproject-backend3 Exited (143) 2 weeks ago
8e589c249995 cloud-termproject-backend2 Exited (143) 2 weeks ago
1a2c588b4ba5 cloud-termproject-backend1 Exited (143) 2 weeks ago
993b66763044 mysql:8 Exited (0) 2 weeks ago
...
전부 Exited 상태... restart 정책을 확인해봤다.
docker inspect --format='{{.Name}} → {{.HostConfig.RestartPolicy.Name}}' $(docker ps -aq)
/cloud-termproject-nginx-1 → on-failure
/cloud-termproject-frontend-1 → no
/cloud-termproject-backend3-1 → no
/cloud-termproject-backend2-1 → no
/cloud-termproject-backend1-1 → no
/cloud-termproject-mysql-1 → no
...
현재 nginx 하나만 on-failure고 나머지는 전부 no(기본값)이다.
systemctl을 통해 Docker 데몬 자체는 부팅 시 자동시작이 설정돼 있었지만,
컨테이너 restart 정책이 no라서 아무것도 살아나지 않은 것이다.
결국 서버가 재부팅되면? 컨테이너가 죽으면?
아무것도 자동으로 대응이 안되고 있는게 현재 상태다.
Step1 . 컨테이너 restart 정책 변경 → "unless-stopped"
Docker의 restart 정책은 4가지다.
| 정책 | 동작 |
| no | 절대 재시작 안 함 (기본값) |
| on-failure | 에러 코드로 종료됐을 때만 재시작 |
| always | 항상 재시작 (수동 stop 후에도) |
| unless-stopped | 수동으로 stop하지 않는 한 항상 재시작 |
always는 내가 docker stop으로 의도적으로 껐어도 재시작해버리는 문제가 있다.
unless-stopped 정책이 운영에 맞다고 판단했다.
# docker-compose.yml 모든 서비스마다 적용
restart: unless-stopped
Step 2. healthcheck 체인과 depends_on 강화
# 기존 nginx depends_on
depends_on:
backend1:
condition: service_started # 프로세스가 시작됐나만 확인
기존 depends_on인 service_started는 컨테이너 프로세스가 켜졌는지만 본다.
실상은 Spring Boot는 JVM이 뜨고 DB 연결을 맺고 완전히 준비시간을 고려하지 않고있다.
만약 그 사이에 nginx가 먼저 올라와서 아직 준비되지 않은 backend로 트래픽을 보내면 502 에러가 터진다.
service_started 대신 service_healthy를 쓰면
healthcheck가 통과할 때까지 다음 서비스가 기다린다.
MySQL healthcheck :: mysqladmin ping 활용
mysql:
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# mysqladmin ping으로 DB가 실제로 응답하는지 확인한다.
Backend healthcheck : Actuator 활용
backend1:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 10s
timeout: 5s
retries: 5
start_period: 60s
Spring Boot Actuator의 /actuator/health 엔드포인트를 활용한다.
Actuator는 Spring Boot 앱의 내부 상태를 외부에 HTTP로 노출시켜주는 도구다. 여러 엔드포인트를 제공한다.
| 엔드포인트 | 역할 |
| /actuator/health | 앱 생존 여부, DB 연결 상태 확인 |
| /actuator/metrics | CPU, 메모리, 요청 수 등 수치 데이터 |
| /actuator/prometheus | Prometheus가 읽을 수 있는 형식으로 메트릭 제공 |
기존에 Actuator를 설치하여 /actuator/prometheus로
Prometheus가 메트릭을 긁어갈 수 있는 환경을 만들었었다.
이번엔 /actuator/health를 통해
Docker healthcheck 용도로 활용하는 것이다.
depends_on 체인 강화
# backend: mysql이 healthy할 때까지 대기
backend1:
depends_on:
mysql:
condition: service_healthy
# frontend: backend 3개 모두 healthy할 때까지 대기
frontend:
depends_on:
backend1:
condition: service_healthy
backend2:
condition: service_healthy
backend3:
condition: service_healthy
# nginx: frontend까지 healthy할 때까지 대기
nginx:
depends_on:
frontend:
condition: service_healthy
backend1:
condition: service_healthy
backend2:
condition: service_healthy
backend3:
condition: service_healthy
Step 3. 로그 용량 제한
Docker는 기본적으로 /var/lib/docker/containers/ 아래에 컨테이너 로그를 무한정 쌓는다
즉, 서버를 오래 돌리면 디스크가 꽉 차서 서버가 죽는다.
# 모든 서비스에 적용
logging:
driver: json-file
options:
max-size: "10m" # 파일 하나당 최대 10MB
max-file: "3" # 최대 3개 파일 유지
이제 파일 하나가 10MB를 초과하면 자동으로 순환된다.
최대 30MB(10MB × 3개)까지만 유지된다.
결과 확인

확인결과 depends_on 체인이 순서대로 정확하게 동작했다.
mysql Healthy (6.9s)
→ backend1/2/3 Healthy (45.7s)
→ frontend Healthy (50.4s)
→ nginx Started (50.5s)
정리
| 항목 | Before | After |
| restart 정책 | nginx만 on-failure, 나머지 no | 전체 unless-stopped |
| healthcheck | frontend만 존재 | mysql/backend/frontend 전체 |
| depends_on | service_started | service_healthy |
| 로그 관리 | 무한 누적 | 최대 30MB 자동 순환 |
서버 운영의 기본기가 잡혔다.
다음은 Jenkins를 통한 CI/CD 파이프라인 구축을 다뤄보고자 한다.