Devin.KR

일관된 사본 만들기

90분 안팎

학습 목표

서비스 쓰기를 잠시 제한하는 실습 절차로 파일과 설정을 압축하고 체크섬 목록을 만듭니다.

개념

읽을 수 있는 사본과 일관된 사본

파일 복사가 성공해도 데이터와 설정이 같은 업무 시점을 나타낸다는 보장은 없습니다. 예를 들어 items.json을 복사한 뒤 설정이 바뀌면 오래된 데이터에 새로운 경로 규칙이 섞일 수 있습니다. 이번 레슨은 쓰기를 마친 작은 앱의 파일 다섯 개를 하나의 묶음으로 만듭니다. 데이터 내용의 체크섬뿐 아니라 대상 목록·권한·계정과 기대 조회값을 함께 기록해 복구 담당자가 무엇을 확인할지 알 수 있게 합니다.

정지 조건은 호출자의 책임입니다

backup 함수의 paused=True는 작성자가 종료했다는 호출자의 확인을 전달하는 값입니다. 이 인자가 프로세스를 멈추거나 디스크를 잠그지는 않습니다. VM 검사기는 기존 서비스의 inactive 상태를 확인하고 다른 작성자가 없는 실습실에서 호출합니다. active 또는 failed면 복사를 시작하지 않고 원인을 조사합니다. 숫자 인자를 바꿔 통과시키는 대신 누가 쓸 수 있는지와 마지막 쓰기가 언제 끝났는지 기록합니다.

자연 종료 뒤에 읽는 이유

앞 모듈의 Store는 한 프로세스 안에서 lock으로 read와 increment를 보호하지만 별도 백업 프로세스는 그 lock을 공유하지 않습니다. 따라서 서비스가 실행 중일 때 파일을 따로 읽으면 JSON 쓰기 중간이나 서로 다른 시점을 볼 수 있습니다. RUN_SECONDS가 끝나고 Restart=no인 것을 확인한 뒤 파일을 읽으면 이번 단일 작성자 실습에서 그 경합을 없앨 수 있습니다. 여러 작성자 환경에서는 애플리케이션 차원의 차단이나 스냅샷 절차가 추가로 필요합니다.

상대 경로와 명시 목록을 사용합니다

묶음의 이름은 data/items.json처럼 원본 기준 상대 경로로 정합니다. 절대 경로로 복원 목적지를 고정하지 않고, 복구 위치는 다음 단계에서 선택합니다. app.py와 service.py, 설정과 유닛을 추가하며 .ssh나 홈 전체를 재귀 탐색하지 않습니다. 일반 파일만 읽고 파일 또는 상위 디렉터리의 심볼릭 링크는 거부합니다. 실습 파일 하나의 최대 크기는 1MiB이므로 더 큰 데이터는 설계 범위부터 다시 검토합니다.

설정 파일은 내용 계약도 검사합니다

service.env에는 DATA_FILE, RUN_SECONDS, BIND_ADDRESS가 각각 한 번 있어야 합니다. DATA_FILE은 절대 경로, RUN_SECONDS는 5부터 30초, 기존 실습망 주소는 192.168.56.10입니다. TOKEN 같은 다른 키가 들어 있으면 secret or unsupported config key 오류를 반환합니다. 키 이름을 바꾸면 통과하는 비밀 탐지 제품은 아니며 정해진 비밀 없는 설정 형식을 요구하는 과제입니다. 검사 실패 시 환경 파일 전체를 로그에 출력하지 않습니다.

내용과 메타데이터를 나누어 기록합니다

SHA-256은 파일 바이트로 계산하므로 권한이나 소유자 변경은 같은 해시로 남을 수 있습니다. manifest의 files에는 파일별 sha256, mode, uid, gid, user, group을 넣습니다. recorded_at은 목록을 기록한 UTC 시각이고 snapshot은 이름 충돌을 피하기 위한 시각 식별자입니다. expected_items에는 백업에서 읽은 count와 items 전체 JSON을 넣어 나중에 health가 아닌 업무 내용으로 검증하게 합니다.

압축 전에 데이터 계약을 확인합니다

items.json이 JSON으로 읽혀도 count가 문자열이거나 음수면 이번 앱의 계약에 맞지 않습니다. count는 음수 아닌 정수, items는 배열이어야 합니다. 기존 손상 데이터를 정상 백업으로 게시하지 않도록 묶기 전에 검사합니다. items 배열의 길이가 count와 같아야 한다는 새 규칙은 만들지 않습니다. 앞 앱의 count는 별도 증가 값이므로 의미를 임의로 바꾸면 올바른 데이터를 잘못 거부할 수 있습니다.

두 단계의 체크섬을 만듭니다

파일별 해시는 내부 내용 확인용이고 .tar.gz.sha256은 압축 묶음 전체의 손상 확인용입니다. 전체 해시가 맞아도 내부 명세가 빠졌거나 잘못된 파일이면 복원 가능한 묶음으로 인정하지 않습니다. 생성 직후 다시 압축을 열어 파일 목록·종류·길이·내부 해시·데이터 계약을 검사한 뒤 전체 해시 파일을 씁니다. 외부 해시와 내부 목록을 함께 검사하는 이유를 테스트의 실패 이름으로 구분합니다.

부분 파일은 정상 이름으로 남기지 않습니다

작성 중에는 snapshot 시각 뒤에 .tar.gz.part를 붙입니다. 모든 검사가 끝나야 정식 .tar.gz 이름으로 바꿉니다. 같은 백업 폴더 안에서 이름을 바꾸며 체크섬 파일과 정식 묶음이 모두 있어야 소비자가 사용할 수 있습니다. 두 파일을 하나의 트랜잭션처럼 원자적으로 게시하는 구현은 아니므로 중간에 하나만 남으면 복원 검사에서 거부합니다. 파일 생성과 전원 장애 뒤 지속성 보장은 별도 문제입니다.

보관 정리는 생성 성공 뒤에 합니다

유효 묶음을 이름순으로 정렬해 최근 세 개를 남깁니다. 이 규칙은 14자리 연월일시분초 형식을 고정했을 때만 시간순 의미를 갖습니다. 같은 식별자가 이미 있으면 snapshot collision 오류로 끝내고 덮어쓰지 않습니다. 다른 notes.txt는 삭제 대상이 아니고 .part도 정상 개수에 넣지 않습니다. 기존 묶음이 손상되었으면 정리를 중단해 담당자가 먼저 보존 여부를 결정하게 합니다.

동시 실행은 숨기지 않습니다

백업 폴더의 .backup-lock 디렉터리를 배타적으로 생성합니다. 이미 있으면 두 번째 실행은 실패하며 먼저 시작한 작업 상태를 확인합니다. 실패를 무시하고 lock을 지우면 두 작업이 같은 보관 파일을 정리할 수 있습니다. 비정상 종료 뒤 남은 lock은 활성 작업이 없다는 증거를 확인한 뒤 별도 절차로 처리합니다. 이 락은 백업 작업끼리의 동시 실행을 막는 장치이며 앱의 쓰기 차단을 대신하지 않습니다.

오류 메시지를 생성 단계와 연결합니다

writer must be quiescent는 쓰기 종료 확인이 빠졌다는 뜻입니다. missing, non-file or oversized member는 허용 파일 누락·파일 종류·크기 제한을 확인할 단서입니다. symlink refused는 파일이나 상위 경로가 링크인지 조사합니다. macOS에서 /tmp와 /var가 별칭이면 실제 해석한 경로를 사용합니다. archive checksum mismatch는 이전 묶음 또는 읽은 묶음이 기준과 다르다는 뜻이며 해시를 새로 써서 정상 처리하지 않습니다.

실습 테스트를 기능별로 읽습니다

BackupTest의 여섯 검사는 목록과 제외, 쓰기 종료·경로 중첩, 설정 오류·누락, 이름 충돌·보관 개수, 링크·잘못된 keep, 기존 손상 보존을 다룹니다. starter는 paused를 검사하지 않아 일부 검사가 실패합니다. 함수의 인자만 보지 말고 파일을 읽기 전에 거부하는 순서를 완성합니다. 테스트가 임시 경로를 쓰므로 실제 서비스 파일을 고치거나 키 파일을 만들 필요가 없습니다.

실행 결과를 백업 증거로 묶습니다

demo.py copy는 닫힌 fixture에서 파일 다섯 개, count 2, 비밀 이름 제외를 재현합니다. 이는 실제 VM 백업 시각이나 운영 데이터가 아닙니다. 본인 실습에서는 유닛 inactive 원문·백업 식별자·기록 시각·허용 파일 목록·검사 종료 코드를 추가로 보관합니다. tar의 옵션 사용법은 더 읽기로 보내고 여기서는 정식 묶음으로 인정하는 조건을 코드에서 설명할 수 있어야 합니다.

따라하기

서비스 자연 종료 확인

Linux VM에서 기존 유닛이 inactive이고 다른 작성자가 없는지 확인합니다. active 또는 failed면 원인을 조사합니다. 20초 자연 종료를 기다리며 출력 원문과 시각을 기록합니다.

systemctl is-active bootcamp-systems.service
systemctl show bootcamp-systems.service -p User -p Group -p Restart

닫힌 fixture의 사본 재현

solution ZIP에서 실행합니다. 임시 파일만 사용하며 실제 VM 백업이 아닙니다.

python3 -B demo.py copy

실행 결과

FILES 5
COUNT 2
SECRETS False

해시는 권한과 별도임을 확인

같은 바이트와 바뀐 바이트를 비교합니다. 권한 검사는 manifest의 별도 항목입니다.

import hashlib
a = b'{"count":2,"items":[]}\n'
b = b'{"count":3,"items":[]}\n'
print("SAME", hashlib.sha256(a).hexdigest() == hashlib.sha256(a).hexdigest())
print("CHANGED", hashlib.sha256(a).hexdigest() == hashlib.sha256(b).hexdigest())

실행 결과

SAME True
CHANGED False

종료 조건을 구현하고 여섯 검사 실행

starter의 paused 거부 조건을 완성한 뒤 bash check.sh를 실행합니다. solution은 stderr에 Ran 6 tests와 OK를 표시합니다. 실패하면 검사 이름과 이유를 읽습니다.

bash check.sh

확인 문제

실습

recovery.py의 backup에서 TODO를 완성합니다. paused가 참이 아니면 파일을 읽기 전에 거부합니다. 허용 파일·설정 계약·해시·모드·계정 기록·부분 파일 게시·최근 세 개 보관을 읽고 검사 이유를 설명합니다. 테스트는 종료 조건 외에도 목록·비밀 제외·충돌·손상·링크·원본 경로 중첩을 검사합니다.

시작 코드·테스트 내려받기

실행 명령

bash check.sh

기대 결과

6 tests, OK, 종료 코드 0

모범 답안모범 답안 내려받기

더 읽기

면접 질문

  • 백업 결과를 신뢰하기 위한 확인 방법을 설명합니다.