- Published on
rsync exit code 23/24 원인과 복구 체크리스트
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버 간 파일 동기화에서 rsync 는 “대부분 성공했지만 일부 파일에서 문제가 있었다”는 상태를 종료 코드로 알려줍니다. 특히 exit code 23 과 exit 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 systemNo 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 동기화 후 원자적 스위치
배포/정적 파일 배치처럼 “완전한 세트”가 중요하면 아래 패턴이 안정적입니다.
- 대상에
staging으로 rsync - 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 는 충분히 통제 가능한 범주로 내려옵니다.