운영체제

rsync 백업 중 메모리 사용률 급증, 원인은 두 가지: dirty page와 page cache

warpmemory 2026. 6. 26. 11:36

리눅스 서버에서 rsync 기반 백업이 도는 시간대에 메모리 사용률이 90%를 넘기며 알람이 뜨는 상황을 겪었다. 원인을 파고들어 보니 서로 다른 두 가지가 얽혀 있었다. 하나는 dirty page 적체, 다른 하나는 rsync가 만들어내는 page cache다. 둘 다 "메모리 사용률 증가"로 보이지만 성격과 대응법이 완전히 다르다. 나중에 또 만날 것 같아 정리해 둔다.

1. dirty page 적체로 인한 메모리 스파이크

증상

대량 파일 쓰기(백업, 미러링 등)가 일어나는 동안 메모리 사용률이 출렁이며 치솟는다. 쓰기가 일정 구간 누적되었다가 한 번에 디스크로 flush될 때 스파이크가 발생한다.

원인

dirty page는 아직 디스크에 반영되지 않은, 메모리에만 존재하는 수정된 페이지다. 커널은 vm.dirty_ratio / vm.dirty_background_ratio 값에 따라 얼마나 쌓였을 때 flush를 시작할지 결정한다.

함정은 일부 튜닝 프로파일에 있었다. 성능 지향 프로파일(예: throughput-performance)을 적용하면 vm.dirty_ratio가 기본 20%가 아니라 40%로 올라간다. 즉 메모리의 40%까지 dirty page가 쌓이도록 허용되고, 그만큼 적체 후 일괄 flush 시 사용률 스파이크가 커진다.

주의할 점은 이 값이 /etc/sysctl.conf 같은 파일에는 드러나 있지 않다는 것이다. 프로파일이 런타임에 덮어쓰기 때문에, sysctl 파일만 봐서는 왜 40%인지 알 수 없다.

현재 값 확인

sysctl vm.dirty_ratio vm.dirty_background_ratio
# 적용 중인 튜닝 프로파일 확인
tuned-adm active

해결: 프로파일 통째로 바꾸지 말고 dirty 값만 오버라이드

성능 프로파일의 다른 이점은 유지하면서 dirty 관련 값만 낮추는 게 핵심이다. 커스텀 프로파일을 만들어 기존 프로파일을 include한 뒤 dirty 값만 덮어쓴다.

mkdir -p /etc/tuned/custom-profile

/etc/tuned/custom-profile/tuned.conf:

[main]
include=throughput-performance

[sysctl]
vm.dirty_ratio=20
vm.dirty_background_ratio=5
tuned-adm profile custom-profile
sysctl vm.dirty_ratio vm.dirty_background_ratio   # 20 / 5 확인

메모리가 매우 큰 장비라면 비율(%) 대신 절대량으로 상한을 거는 방식이 더 예측 가능하다.

[sysctl]
vm.dirty_bytes=1073741824          # 1GB
vm.dirty_background_bytes=268435456 # 256MB

(주의: dirty_bytes를 설정하면 dirty_ratio는 0으로 무시된다. 둘 중 하나만 쓴다.)

이 한 가지 조정만으로 적체 후 일괄 flush로 인한 스파이크는 눈에 띄게 완화된다.

2. rsync가 쌓는 page cache

증상

dirty 값을 조정했는데도 백업이 끝나갈 무렵 메모리 사용률이 높게 유지된다. 그런데 이건 성격이 다르다.

원인

rsync는 읽고 쓰는 파일을 page cache에 올린다. 백업처럼 한 번 읽고 다시 안 쓸 데이터까지 캐시에 차곡차곡 쌓이면서 buff/cache가 부풀어 오른다.

이건 사실 문제가 아닌 정상 동작이다. page cache는 reclaimable이라 다른 프로세스가 메모리를 필요로 하면 커널이 알아서 회수한다. free -m에서 available 값이 충분하면 실제로는 여유가 있는 상태다.

free -m
# total used free shared buff/cache available
# used가 아니라 available을 봐야 한다

문제는 모니터링 알람이 buff/cache까지 포함한 "used 기준 사용률"로 임계치를 잡는 경우다. 실제로는 회수 가능한데 알람만 울린다.

해결 1: nocache로 캐시 적재 자체를 막기

일회성 백업처럼 캐시에 남길 필요가 없는 작업은 nocache로 감싸면 된다. LD_PRELOADopen/close 등을 후킹해서, 파일이 닫힐 때마다 그 파일에 대해 posix_fadvise(POSIX_FADV_DONTNEED)를 호출해 page cache에서 비운다.

nocache rsync -avuz /src/ /dst/

핵심은 끝에 한 번만 비우는 게 아니라 파일 단위로 비운다는 점이다. rsync가 파일 하나를 다 전송하고 close() 하는 순간 그 파일이 차지하던 캐시가 즉시 해제된다. 그래서 수많은 파일을 도는 장시간 동기화에서도 캐시가 끝까지 누적되지 않고 일정 수준으로 유지된다. (쓰기 파일은 DONTNEED가 dirty page를 못 비우므로 nocache가 close 시 fdatasync로 먼저 flush한 뒤 비운다.)

nocache라는 이름 때문에 "캐시를 아예 못 쓰게 해서 느려지는 것 아니냐"는 오해가 있는데, 그렇지 않다. 처리가 끝난 파일의 페이지를 캐시에서 비워줄 뿐, 다른 프로세스나 시스템 전체의 캐시 사용을 막는 게 아니다. 또 열 때 이미 캐시에 있던 페이지는 건드리지 않고 자기가 새로 올린 페이지만 골라 비운다. 단발성 백업/복사처럼 어차피 재사용 안 할 데이터엔 안성맞춤이다.

다만 약점이 하나 있다. 하나의 거대한 파일을 오래 전송하는 경우, 그 파일은 close될 때까지 열려 있으므로 그동안 캐시가 쌓이고 닫힐 때 한꺼번에 해제된다. 작은~중간 파일이 많은 일반적인 백업에는 잘 맞지만, "수십 GB짜리 파일 하나"가 메인이면 효과가 제한적일 수 있다. 이럴 땐 아래 cgroup 방식이 더 확실하다.

설치 (EPEL 또는 소스 빌드)

nocache는 RHEL/Rocky/CentOS 계열 기본 저장소에는 없다. EPEL에 있거나, 없으면 소스로 빌드한다.

EPEL이 가능하면:

yum install -y epel-release
yum install -y nocache

EPEL을 못 쓰는 환경(폐쇄망 등)이면 소스 빌드가 깔끔하다. 의존성이 거의 없고 빌드도 가볍다.

# 빌드 도구
yum install -y git make gcc

# 소스 받아 빌드
git clone https://github.com/Feh/nocache.git
cd nocache
make
make install        # 기본 PREFIX=/usr/local → /usr/local/bin/nocache

# 확인
which nocache
nocache --help

폐쇄망이면 인터넷 되는 곳에서 tarball(또는 git clone)만 받아 반입한 뒤 make && make install 하면 된다.

다만 같은 데이터를 반복적으로 읽어 캐시 효과가 중요한 쪽(서비스가 읽는 원본 경로 등)에는 적용하지 않는 게 좋다. 백업의 목적지(dest) 쪽이나 일회성 작업에 거는 게 안전하다.

해결 2: cgroup으로 메모리 상한 걸기

특정 작업이 쓸 수 있는 메모리(캐시 포함) 자체를 제한하고 싶다면 cgroup으로 스코프를 묶는다.

systemd-run --scope -p MemoryHigh=3G -p MemoryMax=4G rsync -avuz /src/ /dst/

MemoryHigh를 넘으면 커널이 해당 스코프의 캐시를 적극적으로 회수하며 압박하고, MemoryMax는 하드 리밋이다. 시스템 전체 메모리 사용률이 백업 때문에 튀는 걸 근본적으로 묶어둘 수 있다.

정리

현상 원인 핵심 대응
쓰기 중 사용률 스파이크 dirty page 적체 후 일괄 flush dirty_ratio 낮춤 (커스텀 tuned 프로파일)
백업 후 사용률 높게 유지 rsync page cache 누적 nocache 또는 cgroup 메모리 상한

교훈 세 가지.

  1. "메모리 사용률 높음"을 한 덩어리로 보면 안 된다. dirty page(쓰기 적체)와 page cache(읽기 캐시)는 원인도 대응도 다르다.
  2. 튜닝 프로파일이 sysctl 값을 몰래 바꾼다. 설정 파일만 보지 말고 tuned-adm active로 적용 프로파일을 확인하자. 그리고 프로파일을 통째로 갈아엎기보다 include + 필요한 값만 오버라이드하는 게 깔끔하다.
  3. page cache 증가는 대부분 정상이다. available을 보고 판단하고, 알람 임계치가 buff/cache를 포함하는지 점검하자.