rsyncで同期する
한국어 원문으로 표시합니다.
목표
rsync 의 핵심 옵션을 손으로 확인합니다. 특히 후행 슬래시 규칙, --delete 의 위험과 가드, --link-dest 세대 백업 세 가지를 직접 만듭니다.
왜 중요한가
rsync 는 가장 많이 쓰고 가장 많이 틀리는 도구입니다. -a 가 ACL·확장 속성·하드 링크를 포함하지 않는다는 사실, 원본 경로 끝의 슬래시 하나가 디렉터리 깊이를 바꾼다는 사실, --delete 가 원본이 비어 있을 때 대상을 전멸시킨다는 사실 — 이 셋은 전부 실제 사고로 이어진 적이 있습니다. --link-dest 는 반대로 rsync 의 가장 아름다운 기능입니다. 각 세대가 전체 스냅샷처럼 보이면서 디스크는 변경분만 차지합니다.
단계
/root/sync/src아래에 다음을 만드세요.app/main.py,app/util.py,conf/app.yaml,logs/app.log,logs/error.log. 각 파일에는 아무 내용이나 한 줄 이상 넣으세요./root/sync/src/를/root/sync/dst1/로 동기화하는 dry-run 을 변경 내역 표시와 함께 실행해/root/sync/dryrun.txt로 저장하세요.- 같은 동기화를 SSH 를 통해 실제로 수행하세요. 대상은
root@127.0.0.1:/root/sync/dst1/이고 포트 2222, 키는/root/.ssh/id_labhub입니다. 완료 후/root/sync/dst1/app/main.py가 존재해야 합니다. - 후행 슬래시 차이를 실증하세요.
/root/sync/src(슬래시 없음)를/root/sync/dst2/로 보내면/root/sync/dst2/src/app/main.py가 생겨야 합니다. /root/sync/backup.sh스크립트를 작성하세요.set -euo pipefail로 시작하고, 원본이 비어 있으면 종료 코드 1로 중단하는 검사를 포함하고,--delete를 써서/root/sync/src/를/root/sync/dst3/로 동기화해야 합니다. 원본 경로는 첫 번째 인자로 받고, 인자가 없으면/root/sync/src/를 씁니다 — 채점기가 빈 디렉터리를 첫 인자로 넘겨 가드가 실제로 도는지 확인합니다. 스크립트를 실행해/root/sync/dst3를 만드세요.logs/아래를 제외하고/root/sync/src/를/root/sync/dst4/로 동기화하세요./root/sync/dst4/logs는 존재하면 안 됩니다.- 세대 백업을 만드세요. 먼저
/root/sync/gen/daily.1/로 전체 동기화한 뒤,--link-dest로/root/sync/gen/daily.0/를 만드세요. 바뀌지 않은 파일은 두 세대가 같은 inode 를 공유해야 합니다. /root/sync/final.txt를 다음 3줄로 만드세요.PENDING=<3번과 같은 동기화를 다시 dry-run 했을 때 전송 예정 파일 수>/DST4_HAS_LOGS=no/SHARED_INODE=yesPENDING은 0 이어야 합니다.
참고
rsync -ain <원본> <대상>이 dry-run + itemize 조합입니다.- SSH 지정은
-e 'ssh -p 2222 -i /root/.ssh/id_labhub'입니다. - inode 공유 확인은
stat -c '%i %h' <파일>로 두 세대의 파일을 비교하면 됩니다. - 흔한 실수 1: 3번에서 원본 끝 슬래시를 빼면
dst1/src/가 만들어져 검사에 실패합니다. - 흔한 실수 2: 7번에서
--link-dest를 상대 경로로 주면 대상 디렉터리 기준으로 해석돼 링크가 안 걸립니다. 절대 경로를 쓰세요.
원본 트리 만들기
/root/sync/src 아래에 다음을 만드세요. app/main.py, app/util.py, conf/app.yaml, logs/app.log, logs/error.log. 각 파일에는 아무 내용이나 한 줄 이상 넣으세요.
하위 디렉터리와 로그 파일을 섞어 두면 뒤 단계의 제외 규칙이 의미를 갖습니다.
먼저 시뮬레이션
/root/sync/src/ 를 /root/sync/dst1/ 로 동기화하는 dry-run 을 변경 내역 표시와 함께 실행해 /root/sync/dryrun.txt 로 저장하세요.
실제 변경 없이 무엇이 바뀔지만 보여 주는 옵션과, 변경 내용을 한 줄씩 표시하는 옵션을 함께 씁니다.
SSH 위로 실제 동기화
같은 동기화를 SSH 를 통해 실제로 수행하세요. 대상은 root@127.0.0.1:/root/sync/dst1/ 이고 포트 2222, 키는 /root/.ssh/id_labhub 입니다. 완료 후 /root/sync/dst1/app/main.py 가 존재해야 합니다.
rsync 에는 원격 셸 명령을 통째로 지정하는 옵션이 있습니다. 포트와 키를 그 안에 넣으세요.
후행 슬래시 차이 실증
후행 슬래시 차이를 실증하세요. /root/sync/src (슬래시 없음)를 /root/sync/dst2/ 로 보내면 /root/sync/dst2/src/app/main.py 가 생겨야 합니다.
같은 원본을 슬래시 유무만 바꿔 두 대상에 각각 보내고 결과 구조를 비교하세요.
가드가 있는 --delete
/root/sync/backup.sh 스크립트를 작성하세요. set -euo pipefail 로 시작하고, 원본이 비어 있으면 종료 코드 1로 중단하는 검사를 포함하고, --delete 를 써서 /root/sync/src/ 를 /root/sync/dst3/ 로 동기화해야 합니다. 원본 경로는 첫 번째 인자로 받고, 인자가 없으면 /root/sync/src/ 를 씁니다 — 채점기가 빈 디렉터리를 첫 인자로 넘겨 가드가 실제로 도는지 확인합니다. 스크립트를 실행해 /root/sync/dst3 를 만드세요.
원본이 비어 있으면 즉시 중단하는 검사를 스크립트 앞에 넣으세요. set -euo pipefail 도 함께.
제외 규칙 적용
logs/ 아래를 제외하고 /root/sync/src/ 를 /root/sync/dst4/ 로 동기화하세요. /root/sync/dst4/logs 는 존재하면 안 됩니다.
제외 패턴은 여러 번 지정할 수도 있고 파일에서 읽을 수도 있습니다. 로그 파일이 대상에 없어야 합니다.
하드 링크 세대 백업
세대 백업을 만드세요. 먼저 /root/sync/gen/daily.1/ 로 전체 동기화한 뒤, --link-dest 로 /root/sync/gen/daily.0/ 를 만드세요. 바뀌지 않은 파일은 두 세대가 같은 inode 를 공유해야 합니다.
--link-dest 는 상대 경로로 주면 대상 기준으로 해석됩니다. 절대 경로가 안전합니다. 확인은 stat 의 링크 수로 합니다.
동기화 완료 증명
/root/sync/final.txt 를 다음 3줄로 만드세요.
PENDING=<3번과 같은 동기화를 다시 dry-run 했을 때 전송 예정 파일 수> / DST4_HAS_LOGS=no / SHARED_INODE=yes
PENDING 은 0 이어야 합니다.
제대로 동기화됐다면 같은 명령을 다시 dry-run 으로 돌렸을 때 전송할 것이 없어야 합니다.