study/server

[Ubuntu] 07. 홈 서버 운영 - Docker 로그 관리와 디스크 최적화

렁치 2026. 7. 27. 03:12

MSSQL 백업과 복구 테스트까지 끝내고 나니 다음으로 신경 쓰인 건 디스크였다.

홈 서버는 클라우드 서버처럼 스토리지를 쉽게 늘릴 수 있는 환경이 아니다. 특히 Docker는 기본 설정으로 오래 운영하면 로그와 빌드 캐시가 생각보다 많이 쌓인다.

처음에는 컨테이너만 잘 떠 있으면 된다고 생각했는데, 운영 쪽으로 넘어가니 “잘 실행되는 것”만큼 “계속 버틸 수 있는 것”도 중요했다.


오늘 목표

오늘 목표는 아래와 같았다.

Docker 로그 사용량 확인
    ↓
컨테이너 로그 로테이션 설정
    ↓
기존 컨테이너에 설정 반영
    ↓
Docker 이미지 / 빌드 캐시 정리 정책 정리

먼저 현재 로그 사용량 확인

Docker 컨테이너 로그는 보통 아래 경로에 쌓인다.

/var/lib/docker/containers

현재 사용량을 확인했다.

sudo du -sh /var/lib/docker/containers

그리고 컨테이너별 JSON 로그 파일을 확인했다.

sudo find /var/lib/docker/containers -name "*-json.log" -exec ls -lh {} \;

초기 상태에서는 큰 문제가 없었다.

40K  ...-json.log
24K  ...-json.log
3.1K ...-json.log

지금은 작지만, 이 상태로 몇 달 동안 운영하면 달라질 수 있다.

ASP.NET Core 로그, EF Core SQL 로그, Nginx 로그, SQL Server 로그가 쌓이면 Docker 로그 파일이 몇 GB까지 커지는 것도 이상한 일이 아니다.


Docker 로그가 커지는 이유

Docker의 기본 로그 드라이버는 보통 json-file이다.

컨테이너가 stdout/stderr로 출력하는 로그를 JSON 파일로 저장한다.

컨테이너 출력
    ↓
Docker logging driver
    ↓
*-json.log

문제는 기본 설정에서는 로그 파일 크기 제한이 없을 수 있다는 점이다.

즉 앱이 계속 로그를 찍으면 파일도 계속 커진다.

홈 서버에서는 이걸 그냥 두기 어렵다.


Docker daemon.json 설정

Docker 데몬 설정 파일을 열었다.

sudo nano /etc/docker/daemon.json

아래 내용을 넣었다.

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "5"
  }
}

의미는 간단하다.

max-size: 로그 파일 하나는 최대 10MB
max-file: 로그 파일은 최대 5개 보관

컨테이너 하나당 최대 약 50MB 정도로 제한되는 구조다.

서버 규모가 크다면 다르게 잡을 수 있겠지만, 지금 홈 서버에는 충분하다고 봤다.


Docker 재시작

설정을 바꾼 뒤 Docker를 재시작했다.

sudo systemctl restart docker

컨테이너들이 다시 올라왔는지 확인했다.

docker ps

정상적으로 컨테이너들이 다시 실행됐다.

survivalgame   Up
mssql-db       Up
postgres-db    Up
portainer      Up

처음 확인했을 때 설정이 안 보였다

바로 확인해봤다.

docker inspect survivalgame --format '{{json .HostConfig.LogConfig}}'

처음에는 이렇게 나왔다.

{"Type":"json-file","Config":{}}

분명 Docker daemon 설정을 바꿨는데 컨테이너에는 반영되지 않은 것처럼 보였다.

이유는 단순했다.

Docker daemon의 기본 설정은 새로 생성되는 컨테이너에 적용된다. 기존 컨테이너는 다시 만들어야 한다.

그래서 Compose로 컨테이너를 재생성했다.

cd ~/docker/compose
docker compose up -d --force-recreate

다시 확인했다.

docker inspect survivalgame --format '{{json .HostConfig.LogConfig}}'

이번에는 정상적으로 적용됐다.

{"Type":"json-file","Config":{"max-file":"5","max-size":"10m"}}

이 상태면 컨테이너 로그가 무한히 커지는 문제는 막을 수 있다.


Docker 이미지 정리

Docker는 이미지를 새로 빌드할 때 이전 이미지나 중간 레이어가 남을 수 있다.

현재 사용량은 아래 명령으로 확인할 수 있다.

docker system df

사용하지 않는 이미지는 정리할 수 있다.

docker image prune -f

배포 workflow에도 이 명령을 넣었다.

docker image prune -f

이 명령은 사용하지 않는 dangling image를 정리한다. 실행 중인 컨테이너가 사용하는 이미지는 지우지 않는다.


Docker build cache 정리

Docker build cache도 계속 커질 수 있다.

완전히 지워버리면 다음 빌드가 많이 느려질 수 있다. 그래서 어느 정도는 남기고 제한하는 방식을 사용했다.

docker builder prune -f --keep-storage=10gb

의미는 대략 이렇다.

빌드 캐시를 정리하되,
최대 10GB 정도는 유지한다.

서버 용량이 넉넉하지 않기 때문에 무제한으로 두는 것보다 안전하다.

배포가 자주 일어나면 빌드 캐시가 쌓일 수 있으니, deploy workflow 마지막에 넣어두었다.

echo "Cleaning up old images and excess build cache"
docker image prune -f
docker builder prune -f --keep-storage=10gb

Git에 올라가는 설정과 아닌 설정

여기서 한 번 정리할 필요가 있었다.

/etc/docker/daemon.json은 서버 운영 설정이다. GitHub 프로젝트 소스에 올라가는 파일이 아니다.

구분하면 이렇게 된다.

GitHub repository에 올라가는 것
- 애플리케이션 소스
- Dockerfile
- GitHub Actions workflow
- 테스트 코드

서버에만 있는 것
- /etc/docker/daemon.json
- /etc/nginx 설정
- ~/docker/compose/.env
- SSL 인증서
- DB 데이터
- 백업 파일
- cron 설정

처음에는 “이런 설정도 Git에 올려야 하나?”라는 생각이 들었는데, 지금 구조에서는 서버 전용 운영 설정으로 두는 게 맞다.

다만 나중에 잊어버리지 않게 PROJECT_CONTEXT.md 같은 인계 문서에는 기록해두는 것이 좋다.


현재 디스크 관리 구조

현재 디스크 관리 흐름은 이렇게 정리된다.

Docker container logs
    ↓
json-file driver
    ↓
max-size 10m
max-file 5

Docker images
    ↓
docker image prune

Docker build cache
    ↓
docker builder prune --keep-storage=10gb

MSSQL backups
    ↓
주 1회 백업
56일 지난 파일 삭제

이 정도면 당장은 디스크가 갑자기 차는 상황을 어느 정도 막을 수 있다.


운영 중 가끔 볼 명령어

앞으로는 아래 명령어를 가끔 확인하면 된다.

df -h

전체 디스크 사용량 확인.

docker system df

Docker 이미지, 컨테이너, 볼륨, 빌드 캐시 사용량 확인.

sudo du -sh /var/lib/docker/containers

컨테이너 로그 사용량 확인.

ls -lh ~/docker/backups/mssql

DB 백업 파일 확인.


오늘 배운 점

Docker는 컨테이너를 쉽게 띄우게 해주지만, 오래 운영할 때 생기는 파일들까지 자동으로 관리해주지는 않는다.

처음에는 컨테이너가 Up 상태인지, HTTP 200이 나오는지만 봤다. 그런데 운영 쪽으로 넘어오면 로그, 백업, 빌드 캐시 같은 파일들이 계속 쌓인다.

이번에 느낀 건 서버 운영에서 디스크는 조용히 망가지는 부분이라는 점이다. 앱이 잘 돌아가고 있어도, 로그가 계속 쌓이다가 어느 날 디스크가 꽉 차면 DB나 컨테이너가 정상적으로 동작하지 않을 수 있다.

그래서 지금처럼 초기에 제한을 걸어두는 편이 마음이 편하다.


다음 작업

이제 서버 운영의 기본적인 안전장치는 어느 정도 갖춰졌다.

다음에는 테스트 구조를 정리한다.

처음에는 모든 테스트가 로컬 SQL Server나 LocalDB에 의존할 수 있었는데, Codex나 GitHub Actions 같은 환경에서는 LocalDB가 항상 있는 것이 아니다.

그래서 Unit Test와 Integration Test를 나누고, 실제 DB가 필요한 테스트는 GitHub Actions의 MSSQL 컨테이너에서 실행하도록 정리할 예정이다.