SSH 와 파일 전송 · scp, rsync, sftp 로 파일 옮기기 · 실습
rsync 로 동기화하기
목표
rsync 의 핵심 옵션을 손으로 확인합니다. 특히 후행 슬래시 규칙, --delete 의 위험과 가드, --link-dest 세대 백업 세 가지를 직접 만듭니다.
왜 중요한가
rsync 는 가장 많이 쓰고 가장 많이 틀리는 도구입니다. -a 가 ACL·확장 속성·하드 링크를 포함하지 않는다는 사실, 원본 경로 끝의 슬래시 하나가 디렉터리 깊이를 바꾼다는 사실, --delete 가 원본이 비어 있을 때 대상을 전멸시킨다는 사실 — 이 셋은 전부 실제 사고로 이어진 적이 있습니다. --link-dest 는 반대로 rsync 의 가장 아름다운 기능입니다. 각 세대가 전체 스냅샷처럼 보이면서 디스크는 변경분만 차지합니다.
단계
1. /root/sync/src 아래에 다음을 만드세요. app/main.py, app/util.py, conf/app.yaml, logs/app.log, logs/error.log. 각 파일에는 아무 내용이나 한 줄 이상 넣으세요.
2. /root/sync/src/ 를 /root/sync/dst1/ 로 동기화하는 dry-run 을 변경 내역 표시와 함께 실행해 /root/sync/dryrun.txt 로 저장하세요.
3. 같은 동기화를 SSH 를 통해 실제로 수행하세요. 대상은 root@127.0.0.1:/root/sync/dst1/ 이고 포트 2222, 키는 /root/.ssh/id_labhub 입니다. 완료 후 /root/sync/dst1/app/main.py 가 존재해야 합니다.
4. 후행 슬래시 차이를 실증하세요. /root/sync/src (슬래시 없음)를 /root/sync/dst2/ 로 보내면 /root/sync/dst2/src/app/main.py 가 생겨야 합니다.
5. /root/sync/backup.sh 스크립트를 작성하세요. set -euo pipefail 로 시작하고, 원본이 비어 있으면 종료 코드 1로 중단하는 검사를 포함하고, --delete 를 써서 /root/sync/src/ 를 /root/sync/dst3/ 로 동기화해야 합니다. 원본 경로는 첫 번째 인자로 받고, 인자가 없으면 /root/sync/src/ 를 씁니다 — 채점기가 빈 디렉터리를 첫 인자로 넘겨 가드가 실제로 도는지 확인합니다. 스크립트를 실행해 /root/sync/dst3 를 만드세요.
6. logs/ 아래를 제외하고 /root/sync/src/ 를 /root/sync/dst4/ 로 동기화하세요. /root/sync/dst4/logs 는 존재하면 안 됩니다.
7. 세대 백업을 만드세요. 먼저 /root/sync/gen/daily.1/ 로 전체 동기화한 뒤, --link-dest 로 /root/sync/gen/daily.0/ 를 만드세요. 바뀌지 않은 파일은 두 세대가 같은 inode 를 공유해야 합니다.
8. /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를 상대 경로로 주면 대상 디렉터리 기준으로 해석돼 링크가 안 걸립니다. 절대 경로를 쓰세요.
단계 8개
- 원본 트리 만들기
- 먼저 시뮬레이션
- SSH 위로 실제 동기화
- 후행 슬래시 차이 실증
- 가드가 있는 --delete
- 제외 규칙 적용
- 하드 링크 세대 백업
- 동기화 완료 증명