The Logs from the Day of the Incident Were Already Deleted
한국어 원문으로 표시합니다.
목표
회전된 로그 묶음에서 실제 보관 구간을 재고, 사고 구간이 그 범위 밖임을 숫자로 밝히고, 압축률을 실측해 보존 일수와 용량을 계산해 회전 정책으로 적습니다. 마지막으로 속도 제한에 걸려 애초에 기록되지 않는 줄을 셉니다.
왜 중요한가
조사 첫날 가장 자주 만나는 벽은 어려운 질문이 아니라 빈 디렉터리입니다. 그때 필요한 것은 한탄이 아니라 세 개의 숫자입니다 — 며칠이 모자랐는가, 얼마를 더 남겨야 하는가, 그 값이 디스크 예산에 맞는가. 세 숫자는 전부 지금 있는 파일에서 잴 수 있습니다. 그리고 "로그가 없다" 는 두 가지 뜻입니다. 회전으로 지워졌거나, 속도 제한에 걸려 애초에 기록되지 않았거나. 대책이 완전히 다르므로 둘을 가려야 합니다.
단계
/root/keep/gen_keep.py를 만들어 실행해/root/keep/var/log/아래 보관 상태를 재현하세요./root/keep/order.json에 회전본을 오래된 것부터 세운 목록과 그 규칙을 적으세요./root/keep/coverage.json에 파일마다 줄 수와 첫 줄 · 마지막 줄의 시각을 적으세요./root/keep/window.json에 사고 구간이 보관 범위 안인지, 얼마나 모자랐는지 적으세요./root/keep/budget.json에 압축률 실측값과 보존 일수 · 용량 계산을 적으세요./root/keep/logrotate.conf에 회전 정책을 적으세요.rotate값은 5단계의 계산과 같아야 합니다./root/keep/ratelimit.json에 속도 제한에 걸려 버려질 줄 수를 세어 적으세요./root/keep/keep_report.md에 네 절로 보고서를 남기세요.
참고
- 이 실습이 주는 가정입니다. 사고 구간은
2026-04-05T02:10:00Z부터2026-04-05T02:40:00Z까지입니다. 운영 서버는 이 표본의 1200배를 찍습니다./var/log의 디스크 예산은 4 GiB(4294967296 바이트), 요구 보존 기간은 30일, journald 설정은RateLimitIntervalSec=30·RateLimitBurst=200이고 여유 공간에 따른 배수는 1로 봅니다. - 5단계의 계산 규칙입니다.
sample_raw_bytes_per_day는payments.log.1의 크기,sample_gz_bytes_per_day는payments.log.2.gz의 크기입니다.compression_ratio는 둘의 비를 소수 넷째 자리까지 반올림합니다.delaycompress때문에 최근 두 날은 압축되지 않은 것으로 봅니다.required_bytes_for_30_days는 원본 두 날 + 압축 28일,max_days_in_budget은 예산에서 원본 두 날을 뺀 뒤 압축 하루치로 나눈 몫에 2를 더한 값입니다.rotate_value는 30일치를 남길 때 logrotate 에 적을 숫자입니다. zcat과zgrep으로 압축본을 풀지 않고 읽을 수 있고, 파이썬에서는gzip.open(path, "rt")입니다.- 흔한 실수:
rotate를 보존 일수와 같게 적기, 번호 이름과 날짜 이름을 같은 방향으로 정렬하기, 압축률을 어림으로 넣기, 최근 두 날을 압축본 크기로 계산하기. - 산출물은 전부
/root/keep/아래에 모읍니다. 세션이 끝나면 사라집니다.
고객사의 보관 상태 재현하기
/root/keep/gen_keep.py 를 만들어 실행해 /root/keep/var/log/ 아래 payments.log(720줄) · payments.log.1(2000줄) · payments.log.2.gz · payments.log.3.gz · old/payments.log-20260407.gz · old/payments.log-20260408.gz · burst.ndjson(360줄)을 만드세요.
회전 이름이 두 가지로 섞여 있는 상태를 만듭니다. 번호가 붙은 것과 날짜가 붙은 것, 압축된 것과 안 된 것이 함께 있어야 이 실습이 성립합니다. 압축본은 파이썬 gzip.GzipFile 로 mtime=0 을 주어 쓰면 다시 만들어도 같은 파일이 나옵니다.
회전본을 오래된 것부터 세우기
/root/keep/order.json 에 order(파일 경로를 오래된 것부터 담은 배열, /root/keep/var/log/ 를 뺀 상대 경로)와 rule(그렇게 세운 규칙 한 문장)을 적으세요.
번호가 붙은 이름과 날짜가 붙은 이름은 정렬 방향이 서로 반대입니다. 한쪽은 숫자가 클수록 과거이고 다른 쪽은 이름이 작을수록 과거입니다. 지금 쓰는 파일이 가장 새 것입니다. 파일을 열지 않고 이름만으로 세워 보고, 다음 단계에서 내용으로 확인합니다.
파일마다 실제로 담긴 구간 재기
/root/keep/coverage.json 에 파일마다 file · lines · first_ts · last_ts 를 담은 객체를 오래된 것부터 배열로 적으세요. 시각은 로그 줄의 첫 칸을 그대로 씁니다.
파일 이름은 거짓말을 할 수 있습니다 — 회전이 실패했거나 사람이 손으로 옮긴 파일이 섞이면 이름 순서와 내용 순서가 어긋납니다. 그래서 내용으로 다시 잽니다. 압축본은 zcat 으로 읽거나 파이썬에서 gzip.open(path, 'rt') 로 엽니다.
사고 구간이 범위 밖임을 숫자로 밝히기
/root/keep/window.json 에 oldest_retained · newest_retained · incident_start · incident_end · incident_covered(참/거짓) · short_by_seconds(정수) · extra_rotations_needed(정수)를 적으세요. extra_rotations_needed 는 모자란 초를 하루로 나눈 뒤 올림한 값입니다.
사고 구간은 지시문의 참고 절에 적혀 있습니다. 모자란 초는 보관된 가장 오래된 시각에서 사고 시작 시각을 뺀 값입니다. 하루 단위로 회전하고 있으므로, 며칠치를 더 남겼어야 했는지는 올림으로 구합니다.
압축률을 실측해 보존 일수 계산하기
/root/keep/budget.json 에 sample_raw_bytes_per_day · sample_gz_bytes_per_day · compression_ratio · prod_raw_bytes_per_day · prod_gz_bytes_per_day · required_bytes_for_30_days · fits_in_budget · max_days_in_budget · rotate_value 를 지시문의 계산 규칙대로 적으세요.
압축률은 어림으로 쓰지 말고 이 고객의 파일로 잽니다 — 접근 로그와 스택트레이스는 줄어드는 정도가 다릅니다. delaycompress 때문에 최근 두 날은 원본 크기로 잡아야 하고, logrotate 의 rotate 는 현재 파일을 뺀 개수라는 점이 이 계산의 함정입니다.
계산한 값으로 회전 정책 적기
/root/keep/logrotate.conf 에 /root/keep/var/log/payments.log 블록을 적으세요. daily · rotate <5단계의 rotate_value> · compress · delaycompress · missingok · notifempty · dateext · create 0640 root adm 이 있어야 하고, copytruncate 는 넣지 않습니다.
rotate 값은 5단계에서 계산한 그 숫자입니다 — 보존 일수와 같은 값이 아닙니다. copytruncate 를 빼는 이유는 매뉴얼에 적혀 있습니다. 복사와 비우기 사이의 짧은 틈에 쓰인 줄이 사라지기 때문이고, 그건 조사에서 가장 아쉬운 순간에 사라집니다.
애초에 기록되지 않는 줄 세기
/root/keep/ratelimit.json 에 interval_sec · burst · windows_over_limit · dropped_total · dropped_by_service(서비스 이름을 키로 한 객체) · worst_window(window_start · service · dropped)를 적으세요. 한 구간에서 버려지는 줄은 count - burst 이고 음수면 0입니다.
journald 의 속도 제한은 서비스별로 따로 적용되고, 한 구간에서 한도를 넘으면 그 구간의 나머지가 전부 버려집니다. 보관 기간을 아무리 늘려도 이 손실은 남습니다. worst_window 는 버려진 줄이 가장 많은 구간 하나입니다.
무엇을 바꿀지 글로 남기기
/root/keep/keep_report.md 에 ## 무엇이 없었나 ## 지금 보관 정책은 무엇인가 ## 얼마를 남겨야 하는가 ## 무엇을 바꾸기로 했나 네 절로 적으세요. 모자란 초 · rotate 값 · 예산 안에서 가능한 일수 · 속도 제한으로 버려질 줄 수를 숫자로 포함해야 합니다.
이 보고서를 읽는 사람은 디스크 예산을 쥔 사람입니다. '로그를 더 남겨 주세요' 가 아니라 '하루 N 바이트, 30일에 M 바이트, 예산 안에 들어옵니다' 라고 말해야 결정이 납니다. 속도 제한 손실은 보존과 다른 대책이 필요하다는 점도 함께 적으세요.