Skip to content

fix(docker): 컨테이너만 지우고 볼륨·캐시는 안 지우고 있었다 — 누수 차단 + 정기 정리 - #314

Merged
dldnsgkr merged 1 commit into
developfrom
unhak/docker-garbage-sweeper
Sep 8, 2026
Merged

fix(docker): 컨테이너만 지우고 볼륨·캐시는 안 지우고 있었다 — 누수 차단 + 정기 정리#314
dldnsgkr merged 1 commit into
developfrom
unhak/docker-garbage-sweeper

Conversation

@dldnsgkr

@dldnsgkr dldnsgkr commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

"정리가 코드상으로 되어 있나" 를 확인한 결과. 컨테이너까지만 되어 있었다.

실측 (dev, 2026-09-08)

컨테이너가 0 개인데:

TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Containers        0         0        0B        0B
Local Volumes    10         0       2.184GB   2.184GB (100%)
Build Cache     108         0       6.470GB

디스크 29G 중 19G 사용(67%) 이었고, 손으로 지우니 11G(38%) 가 됐다 — 절반 이상이 찌꺼기였다.

디스크가 차면 앱·MySQL·프리뷰 컨테이너가 한꺼번에 죽는다. 그때는 원인이 "디스크" 라는 것조차 로그를 남길 자리가 없다. 같은 서버에서 pm2 로그가 6.6GB 까지 자라 89% 에 닿은 적도 있다(#302).

원천: 익명 볼륨이 고아로 남았다

removeContainerCmd 세 곳이 전부 withForce(true) 만 쓰고 withRemoveVolumes 를 안 붙였다. 컨테이너는 사라지는데 그것이 만든 익명 볼륨은 남는다 — mysql:8.4 프리뷰 DB 가 /var/lib/mysql 용으로 만드는 볼륨이 그것이라 한 건당 200MB 넘게 쌓인다.

위치
DockerContainerService 2곳
CodingAgentContainerRunner 1곳

명명 볼륨은 쓰지 않고(코드 전체에 없음) bind mount 는 이 옵션의 영향을 받지 않으므로 부작용이 없다.

안전망: DockerGarbageSweeper

원천을 막아도 이전에 쌓인 것과 비정상 종료로 새는 것이 남는다. 6시간마다 고아 볼륨 · dangling 이미지 · 오래된 빌드 캐시를 회수한다.

  • 도커의 prune 은 실행 중이거나 정지 상태로 남아 있는 컨테이너가 참조하는 것을 건드리지 않는다 — 살아 있는 프리뷰 세션은 대상이 아니다
  • 빌드 캐시는 until 이전 것만(기본 48시간). 방금 만든 캐시까지 날리면 다음 빌드가 통째로 느려진다
  • 도커에 못 닿으면 경고만 남기고 넘어간다(이 앱은 도커 없이도 기동하는 것이 전제)
  • 0 바이트 회수는 로그를 남기지 않는다 — 정상 상태가 대부분이라 그때마다 찍으면 정작 "얼마나 쌓였다가 지워졌나" 가 묻힌다
  • 도커를 공유하는 환경을 위해 enabled 로 끌 수 있다

검증

  • DockerGarbageSweeperTest 3개 신규 — 보존 시간 전달 / 끔 / 도커 미도달
  • 전체 1329개 통과

dev 는 이미 손으로 정리해 38% 다. 배포 후 스위퍼가 도는지(회수할 게 없으면 조용한 것이 정상) 확인 필요.

🤖 Generated with Claude Code

https://claude.ai/code/session_013y8USoCXTsRTATAhy88M93

"정리가 코드에 되어 있나" 를 확인해 보니 <b>컨테이너까지만</b> 되어 있었다. 그 뒤에 남는
것들은 아무도 지우지 않는다. dev 실측(2026-09-08): 컨테이너 0 개인데

  Local Volumes  10개  2.184GB  (100% 회수 가능)
  Build Cache   108개  6.470GB

디스크 29G 중 19G 사용(67%)이었고, 손으로 지우니 11G(38%)가 됐다 — 절반 이상이 찌꺼기였다.
디스크가 차면 앱·MySQL·프리뷰 컨테이너가 한꺼번에 죽고, 그때는 원인이 "디스크" 라는 것조차
로그를 남길 자리가 없다.

## 원천: 익명 볼륨이 고아로 남았다

removeContainerCmd 세 곳이 전부 withForce(true) 만 쓰고 withRemoveVolumes 를 안 붙였다.
컨테이너는 사라지는데 그것이 만든 익명 볼륨은 남는다 — mysql:8.4 프리뷰 DB 가 /var/lib/mysql
용으로 만드는 볼륨이 그것이라 한 건당 200MB 넘게 쌓인다.

  DockerContainerService (2곳) · CodingAgentContainerRunner (1곳) → withRemoveVolumes(true)

명명 볼륨은 쓰지 않고 bind mount 는 이 옵션의 영향을 받지 않으므로 부작용이 없다.

## 안전망: DockerGarbageSweeper

원천을 막아도 이전에 쌓인 것과 비정상 종료로 새는 것이 남는다. 6시간마다 고아 볼륨 ·
dangling 이미지 · 오래된 빌드 캐시를 회수한다.

- 도커의 prune 은 <b>실행 중이거나 정지 상태로 남아 있는 컨테이너가 참조하는 것을 건드리지
  않는다</b> — 살아 있는 프리뷰 세션은 대상이 아니다.
- 빌드 캐시는 until 이전 것만(기본 48시간). 방금 만든 캐시까지 날리면 다음 빌드가 통째로 느려진다.
- 도커에 못 닿으면 경고만 남기고 넘어간다(이 앱은 도커 없이도 기동하는 것이 전제다).
- 0 바이트 회수는 로그를 남기지 않는다 — 정상 상태가 대부분이라 그때마다 찍으면 정작 "얼마나
  쌓였다가 지워졌나" 가 묻힌다.
- 도커를 공유하는 환경을 위해 enabled 로 끌 수 있게 했다.

검증: DockerGarbageSweeperTest 3개 신규(보존 시간 전달 / 끔 / 도커 미도달), 전체 1329개 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013y8USoCXTsRTATAhy88M93
@dldnsgkr
dldnsgkr merged commit 16b572c into develop Sep 8, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant