Published on

rsync exit code 23/24 원인과 복구 체크리스트

Authors

서버 간 파일 동기화에서 rsync 는 “대부분 성공했지만 일부 파일에서 문제가 있었다”는 상태를 종료 코드로 알려줍니다. 특히 exit code 23exit code 24 는 CI, 배포, 백업 파이프라인에서 자주 보이며, 로그를 대충 넘기면 조용히 누락된 파일이 생길 수 있습니다.

이 글은 두 코드의 의미를 정확히 구분하고, 현장에서 바로 적용 가능한 원인 분류 + 복구 체크리스트 + 안전한 재시도 전략을 제공합니다.

exit code 23과 24의 의미 (공식 요약)

  • 23: Partial transfer due to error
    전송 중 오류가 발생해 일부 파일이 전송되지 않음. 보통 권한, I/O 오류, 경로 문제, ACL/xattr, 원격 실행 환경 문제 등이 포함됩니다.
  • 24: Partial transfer due to vanished source files
    전송 도중 소스 파일이 사라짐. 대표적으로 로그 로테이션, 임시 파일 정리, 빌드 산출물 정리, 경쟁 상태로 인해 발생합니다.

핵심 차이는 이겁니다.

  • 23 은 “실패 원인이 다양하며, 조치 없이는 계속 실패할 가능성”이 큼
  • 24 는 “경쟁 상태로 인한 일시적 이벤트”가 많아 재시도/스냅샷 전략으로 해결되는 경우가 많음

가장 먼저 확인할 것: 명령행과 로그를 ‘재현 가능’하게 만들기

문제 해결 속도는 로그 품질에 비례합니다. 아래 옵션을 기본으로 붙이면 원인 추적이 훨씬 쉬워집니다.

rsync -aHAX --numeric-ids --delete \
  --info=stats2,progress2,name0 \
  --itemize-changes \
  --human-readable \
  --log-file=/var/log/rsync-sync.log \
  /src/ user@host:/dst/

echo "exit=$?"
  • --itemize-changes 로 어떤 파일이 어떤 이유로 변경/실패했는지 확인
  • --log-file 로 CI 로그 유실 방지
  • --numeric-ids 로 UID/GID 이름 해석 차이로 인한 권한 혼선 감소

운영에서 재시도 설계를 할 때는 에러를 무시하는 대신, 재시도 대상과 무시 대상을 분리하는 게 중요합니다. 이 관점은 네트워크/외부 API 재시도에서도 동일합니다. 예: OpenAI 429 rate_limit_exceeded 재시도 설계

exit code 23: 원인별 진단과 복구 체크리스트

1) 권한 문제 (Permission denied)

가장 흔한 케이스입니다.

증상

  • 로그에 Permission denied 또는 failed to set permissions , chgrp 실패
  • 대상 디렉터리에 파일은 복사되었지만 소유자/권한 설정에서 실패

점검

# 원격 대상 경로 권한 확인
ssh user@host "ls -ld /dst && id"

# SELinux 사용 시 컨텍스트 확인(사용 중인 경우)
ssh user@host "getenforce 2>/dev/null || true"

복구

  • 원격 경로의 소유자/권한 수정
  • 소유자/그룹을 맞출 수 없으면 옵션 조정
# 소유자/그룹 보존이 문제라면 (권한 없는 계정으로 동기화 시)
rsync -a --no-owner --no-group /src/ user@host:/dst/

# 권한 설정 자체가 불필요하면
rsync -a --no-perms /src/ user@host:/dst/

-a 는 편리하지만 “보존하려는 메타데이터”가 많습니다. 운영 정책에 따라 보존 범위를 줄이면 23 을 크게 줄일 수 있습니다.

2) 대상 파일시스템 제약 (읽기 전용, 용량 부족, inode 부족)

증상

  • Read-only file system
  • No space left on device
  • 파일은 조금 가다가 멈추거나, 특정 시점부터 대량 실패

점검

ssh user@host "df -h /dst; df -i /dst; mount | grep ' /dst' || true"

복구

  • 용량 확보 후 재실행
  • 로그/캐시가 쌓이는 경로라면 보관 정책 정비
  • 컨테이너/쿠버네티스 환경이면 PV 용량, 스토리지클래스 제한 확인

3) 심볼릭 링크/특수 파일 처리 문제

증상

  • symlink 관련 오류
  • 디바이스 파일, 소켓, FIFO 처리 실패

점검과 복구

  • 링크를 그대로 복사할지, 링크 대상 파일을 복사할지 결정
# 심볼릭 링크 자체를 보존(기본 -a)
rsync -a /src/ user@host:/dst/

# 링크가 가리키는 실제 파일을 복사
rsync -aL /src/ user@host:/dst/

# 특수 파일이 섞여 있다면 제외
rsync -a --exclude='*.sock' --exclude='/run/***' /src/ user@host:/dst/

4) ACL/xattr 복사 실패 (특히 -A, -X 사용 시)

증상

  • rsync: getxattr , setxattr 실패
  • 파일은 복사되었는데 메타데이터에서 실패하여 23

점검

  • 소스/대상 파일시스템이 xattr, ACL을 지원하는지
  • 컨테이너 환경에서 overlayfs 제약 여부

복구

# ACL/xattr 보존이 필수 아니면 제거
rsync -a --no-acls --no-xattrs /src/ user@host:/dst/

# 반대로 반드시 필요하다면, 대상 FS/마운트 옵션을 지원하도록 변경
# (예: NFS/SMB/overlayfs 설정 점검)

5) 경로/문자 인코딩/파일명 문제

증상

  • 특정 파일명에서만 반복 실패
  • 공백/개행/특수문자 포함 파일에서 로그가 깨짐

점검

# 문제 파일명 추출(로그에서 실패 항목 확인)
# 파일명에 제어문자가 있을 수 있으면 find -print0 기반으로 확인
find /src -print0 | xargs -0 -I{} echo "{}" | head

복구

  • 해당 파일/경로를 정리하거나 제외 규칙 추가
rsync -a --exclude='**/*:Zone.Identifier' /src/ user@host:/dst/

6) 원격 rsync 실행/SSH 환경 문제

증상

  • connection unexpectedly closed 와 함께 23
  • 원격에서 rsync 바이너리 경로 문제

점검

ssh user@host "command -v rsync && rsync --version"

복구

  • 원격에 rsync 설치
  • 강제로 원격 rsync 경로 지정
rsync -a -e "ssh" --rsync-path="/usr/bin/rsync" /src/ user@host:/dst/

exit code 24: 소스 파일이 사라지는 경쟁 상태 다루기

24 는 “전송하려던 파일이 전송 중에 없어졌다”는 의미입니다. 대표 원인은 아래와 같습니다.

  • 로그 로테이션으로 파일이 rename 또는 삭제됨
  • 빌드 디렉터리에서 임시 파일이 생성/삭제되며 동기화가 겹침
  • 애플리케이션이 업로드 디렉터리에서 파일을 이동시키는 중

1) 24 가 ‘정상에 가까운’ 경우와 위험한 경우

  • 정상에 가까움: 수집 대상이 로그/임시 파일이며, 누락되어도 문제 없음
  • 위험함: 배포 아티팩트, 백업, 스냅샷처럼 “정합성”이 중요한 데이터

정합성이 중요하다면 24 를 경고로만 두면 안 되고, 스냅샷/락/원자적 디렉터리 교체 같은 전략이 필요합니다.

2) 소스 디렉터리를 스냅샷처럼 고정하기

가장 효과적인 방법은 “rsync가 읽는 동안 소스가 변하지 않게” 만드는 겁니다.

방법 A: 빌드 산출물 디렉터리를 원자적으로 교체

  • 빌드 결과를 release.tmp 에 만들고
  • 완료 후 release 심볼릭 링크 또는 디렉터리를 교체
  • rsync는 release 만 동기화
# 예시: 원자적 교체
rm -rf /var/www/release.tmp
mkdir -p /var/www/release.tmp
# ... build output to release.tmp ...

mv /var/www/release /var/www/release.prev 2>/dev/null || true
mv /var/www/release.tmp /var/www/release

rsync -a /var/www/release/ user@host:/srv/www/release/

방법 B: LVM/ZFS/Btrfs 스냅샷

파일시스템 스냅샷이 가능하면 rsync 대상은 스냅샷 마운트로 고정하는 것이 베스트입니다.

3) --ignore-missing-args--delete 조합 주의

24 상황에서 무심코 옵션을 섞으면 “사라진 파일”이 대상에서도 삭제되는 등 의도치 않은 결과가 날 수 있습니다.

# 소스에서 빠진 파일을 대상에서도 지우는 --delete는 신중히
rsync -a --delete /src/ user@host:/dst/
  • 배포 목적이라면 --delete-delay 나 staging 디렉터리 동기화 후 원자적 스위치가 안전합니다.

실전 복구 체크리스트 (운영/CI 공통)

아래 순서대로 보면 대부분의 23/24 를 빠르게 정리할 수 있습니다.

1) 실패 파일 목록을 먼저 확보

# itemize 로그 기반으로 실패 파일을 추적하기 쉽게 실행
rsync -a --itemize-changes --log-file=./rsync.log /src/ user@host:/dst/ || true

# 실패 원인 키워드 스캔
grep -E "Permission denied|No space|Read-only|vanished|xattr|ACL|closed" ./rsync.log || true

2) 종료 코드별 1차 분기

  • exit=24 이면: 소스 변경 프로세스(로그 로테이션, 빌드, 크론, 정리 작업)부터 찾기
  • exit=23 이면: 권한/용량/메타데이터/원격 환경을 우선 점검

3) 용량과 inode 확인

ssh user@host "df -h /dst; df -i /dst"

4) 권한/소유권/UID 매핑 확인

ssh user@host "id; ls -ld /dst"

필요 시 --no-owner --no-group --no-perms 로 보존 범위를 줄여 재시도합니다.

5) xattr/ACL 사용 여부 재검토

  • 꼭 필요할 때만 -A, -X 를 켭니다.
  • 컨테이너나 NFS 환경에서는 지원 여부가 흔한 함정입니다.

6) 경로/패턴 exclude로 불안정 파일 제거

로그, 임시 파일, 소켓 등은 동기화 대상에서 제외하는 것이 일반적으로 맞습니다.

rsync -a \
  --exclude='/tmp/***' \
  --exclude='/run/***' \
  --exclude='*.log' \
  /src/ user@host:/dst/

7) 재시도는 “같은 조건”에서 하지 말고 조건을 바꿔라

  • 24: 소스 고정(스냅샷/원자적 교체) 후 재시도
  • 23: 권한/메타데이터 옵션 조정 후 재시도

CI에서 재시도를 넣을 때도 “캐시/환경”이 꼬이면 같은 실패를 반복합니다. 빌드 환경 초기화가 필요할 때는 GitHub Actions 캐시 충돌 시 빌드 완전 초기화 전략 같은 접근이 도움이 됩니다.

안전한 운영 패턴: staging 동기화 후 원자적 스위치

배포/정적 파일 배치처럼 “완전한 세트”가 중요하면 아래 패턴이 안정적입니다.

  1. 대상에 staging 으로 rsync
  2. rsync 성공 시에만 current 를 교체
set -euo pipefail

SRC=/src/
HOST=user@host
BASE=/srv/app
STAGING="$BASE/staging"
CURRENT="$BASE/current"

# 1) 스테이징 동기화
rsync -a --delete "$SRC" "$HOST:$STAGING/"

# 2) 원자적 스위치(원격에서 mv)
ssh "$HOST" "set -e; rm -rf $BASE/prev || true; if [ -d $CURRENT ]; then mv $CURRENT $BASE/prev; fi; mv $STAGING $CURRENT; mkdir -p $STAGING"

이 방식은 24 로 일부 누락된 상태가 current 로 노출되는 위험을 크게 줄입니다.

마무리: 23은 ‘원인 제거’, 24는 ‘정합성 전략’

  • exit code 23 은 대개 구성/권한/파일시스템 제약 문제라서 원인을 제거하지 않으면 계속 재발합니다.
  • exit code 24소스가 변하는 환경에서 흔하며, 스냅샷/원자적 교체/제외 규칙으로 “정합성”을 설계해야 합니다.

운영에서는 “rsync가 성공했는가”보다 “동기화 결과가 기대한 정합성을 만족하는가”가 더 중요합니다. 위 체크리스트를 기준으로 로그를 구조화하고, 실패 코드를 분기 처리하면 23/24 는 충분히 통제 가능한 범주로 내려옵니다.