요약
develop/main push 후 CD 파이프라인이 서버 디스크 부족으로 실패했습니다.
원인은 배포마다 쌓인 미사용 컨테이너 이미지이고, 즉시 조치는 수동 prune, 재발 방지는 deploy.sh에 자동 정리 추가로 처리했습니다.
증상
GitHub Actions CD의 SSH 배포 단계에서 exit code 125로 종료되었습니다.
==> pull ghcr.io/***/vibepulse-worker:b2bcf4b2...
...
no space left on device
web, api 이미지 pull은 성공
worker 이미지 unpack 중 실패
blue/green 배포에서 현재 슬롯(blue)은 그대로 — pull 실패는 health gate 이전이라 트래픽 전환 없음
원인
서버 root 볼륨이 100% 사용 상태였습니다.
항목 수치
디스크
89G 중 89G 사용 (~484M 여유)
Podman 이미지
618개 중 8개만 사용 중
회수 가능 용량
~48GB (96%)
매 push마다 web / api / worker 3개 이미지가 git SHA 태그로 pull되지만, deploy.sh에 이전 이미지 정리 로직이 없어 태그만 쌓였습니다.
추가로 api/worker 이미지는 아직 전체 monorepo node_modules를 포함하는 walking skeleton 구조라 태그당 ~2.3GB 수준입니다. (이미지 슬리밍은 TASK-018 예정)
배포 구조 (참고)
Blue/Green: blue 311x, green 321x 포트
새 슬롯 pull → health check → Caddy 전환 → 구 슬롯 종료
런타임: rootless Podman
스토리지: ~/.local/share/containers/storage/
실패 시점에는 green 슬롯 worker pull 중이었고, blue 슬롯이 계속 서비스 중이었습니다.
즉시 조치 (서버)
실행 중인 컨테이너가 참조하는 이미지는 유지됩니다.
실행 중 컨테이너 확인
podman ps --format 'table {{.Names}}\t{{.Image}}'
미사용 이미지·레이어 정리
podman image prune -a -f
podman system prune -f
공간 확인
df -h /
podman system df
약 45~48GB 회수 후 CD 워크플로를 재실행합니다.
재발 방지 (코드 변경)
scripts/deploy.sh에 prune_unused_images()를 추가했습니다.
시점 동작
pull 전
미사용 이미지 정리 → 새 이미지 pull 전 디스크 확보
구 슬롯 compose down 후
이전 SHA 태그 이미지 정리
prune_unused_images() {
echo "==> prune unused $CONTAINER_RUNTIME images"
"$CONTAINER_RUNTIME" image prune -a -f
"$CONTAINER_RUNTIME" system prune -f
}
실행 중 컨테이너(현재 active 슬롯 + Postgres/Redis 등)가 쓰는 이미지는 삭제되지 않음
docker/podman 모두 동일 명령 사용
교훈 / 체크리스트
컨테이너 이미지도 디스크를 먹는다 — 로그·DB만 볼 게 아니라 podman system df도 주기적으로 확인
SHA 태그만 쓰는 CD는 prune이 필수 — 배포 스크립트에 정리 단계를 넣거나 cron으로 보완
blue/green은 일시적으로 이미지가 2세트 — 디스크 여유는 최소 태그 2~3세트 분량 권장
실패해도 서비스는 유지될 수 있음 — pull/health gate 실패 시 새 슬롯만 정리되고 기존 슬롯 유지 (의도된 동작)
후속 (선택)
항목 설명
TASK-018
api/worker 이미지 슬리밍 (prod-only node_modules)
디스크 용량
89G root에서 빈번한 CD 시 여유 확보 검토
모니터링
디스크 사용률 알람 (예: 85% 이상)
한 줄 요약
CD가 이미지를 계속 쌓아 root 디스크를 채웠고, podman image prune -a로 복구한 뒤 deploy.sh에 배포 전·후 자동 prune을 넣어 재발을 막았다.