> ## Content Index
> Fetch the complete content index at: https://mlog.me/llms.txt
> Use this file to discover other available public pages before exploring further.

# 서버 보안 점검 6편: Ghost·MySQL 백업 복원 검증과 Docker 실패 포렌식
- URL: https://mlog.me/server-security-check-backup-restore-docker-forensics/
- Published: 2026-09-01T01:37:34.000Z
- Updated: 2026-09-01T01:37:34.000Z
- Description: 체크섬이 일치한 Ghost·MySQL 콜드 백업을 운영 환경과 분리해 복원 검증하고, 실패한 Docker 시작 단계를 읽기 전용 이벤트로 추적한 과정을 정리합니다. 백업 성공과 복원 성공, 실패 안전성과 정확한 원인 규명을 엄격히 구분합니다.
- Author: mLog
- Tags: 블로그 운영, Ghost, Docker, #MySQL, #백업·복구, #보안

> **세 줄 요약**  
> 체크섬과 아카이브 구조가 정상이어도 실제 서비스 복원 성공을 의미하지는 않습니다.  
> 격리 전체 복원 검증기 v2는 `PHYSICAL_DATABASE_RESTORE` 단계에서 실패했고, 범위를 좁힌 v3는 `PHYSICAL_START`에서 실패했습니다.  
> 당시 실행 요약에는 테스트 자원 정리와 운영·입력 경계 보존이 기록됐지만, MySQL 복원 성공과 정확한 Docker 시작 실패 원인은 확인되지 않았습니다.

[이전 편: 서버 보안 점검 5편 — Ghost·MySQL 백업 무결성](https://mlog.me/ghost-mysql-backup-integrity-check/)

백업 작업이 오류 없이 끝나고 모든 체크섬이 일치하면 한숨을 돌리게 됩니다. 그러나 장애 복구 관점에서 정말 중요한 질문은 따로 있습니다.

> 이 백업만으로 Ghost와 MySQL을 다시 기동할 수 있는가?

이 질문에 답하려면 백업 생성, 파일 무결성, 복원 입력 적합성, 격리 복원, 서비스 검증을 각각 독립된 관문으로 다뤄야 합니다. 하나의 `backup_success=true`로 모든 상태를 뭉치면 실제 장애가 발생했을 때 가장 중요한 복원 실패를 뒤늦게 발견할 수 있습니다.

이번 글은 5편 이후 진행한 세 단계의 점검을 하나의 흐름으로 정리합니다.

1. 보존된 콜드 백업이 격리 복원에 투입할 수 있는 입력인지 판정합니다.
2. 격리 전체 복원 검증기 v2로 Ghost·MySQL 복구 가능성을 시험합니다.
3. 실패 범위를 좁힌 집중형 물리 복원 검증기 v3와 Docker 이벤트 포렌식으로 원인을 추적합니다.

결과부터 말하면 복원은 성공하지 않았습니다. 대신 어느 단계에서 실패했고, 실패 처리기가 임시 자원 정리와 운영·입력 경계 보존을 어떻게 기록했는지까지 확인했습니다. 이 값들은 후속 독립 재검증이 아니라 당시 실행 요약의 판정입니다. 보안 점검에서는 이처럼 **실패 자체와 실패가 안전하게 통제됐다는 기록을 분리해 해석하는 것**이 중요합니다.

## 핵심 판정 먼저 보기

| 점검 단계            | 실제 보존 결과                         | 이번 글의 판정            |
| ---------------- | -------------------------------- | ------------------- |
| 체크섬              | 검사 대상 모두 일치                      | 파일 무결성 확인           |
| 아카이브와 매니페스트      | 항목과 순서 일치                        | 구조 일치 확인            |
| 중복·위험·비정규 경로     | 검사 범위에서 발견되지 않음                  | 구조 안전성 확인           |
| 허용 경로 정책         | 최종 대조 필요                         | 확인 필요               |
| 격리 전체 복원 검증기 v2  | PHYSICAL\_DATABASE\_RESTORE에서 실패 | 복원 미확인              |
| 집중형 물리 복원 검증기 v3 | PHYSICAL\_START에서 실패             | MySQL 기동·데이터 검증 미수행 |
| 실패 뒤 테스트 컨테이너    | 기록상 정리 완료                        | 실패 안전성 기록 확인        |
| 운영 환경 경계         | 기록상 변경 없음                        | 경계 보존 기록 확인         |
| 입력 백업 경계         | 기록상 변경 없음                        | 경계 보존 기록 확인         |
| Docker 이벤트 포렌식   | 최근 보존 이벤트 메타데이터만 확인              | 정확한 원인 미확정          |

최종 상태를 한 줄로 줄이면 다음과 같습니다.

```text
backup_integrity=verified
isolated_restore=failed
physical_start=failed
cleanup_and_boundaries=recorded_as_preserved
root_cause=undetermined

```

## 자료 범위와 판정 원칙

### 실제 보존 결과와 재현 절차를 분리합니다

이 글에는 두 종류의 내용이 나옵니다.

- **실제 보존 결과**는 당시 점검 실행과 결과 파일에 남은 상태 코드입니다.
- **재현 원칙과 예시 명령**은 다른 환경에서도 같은 기준으로 확인할 수 있도록 일반화한 절차입니다.

예시의 `<BACKUP_DIR>`, `<CHECKSUM_FILE>`, `<ARCHIVE_FILE>`, `<RESTORE_CONTAINER>`, `<START_TIME>`은 모두 자리표시자입니다. 실제 서버명, IP 주소, 도메인, 계정, 비밀번호, 포트, 절대경로, 제품 버전, 해시, 컨테이너 식별자는 공개하지 않습니다.

### 성공 코드가 있다고 실제 성공한 것은 아닙니다

검증 스크립트에는 성공했을 때 출력할 상태와 완료 마커 생성 코드가 들어 있습니다. 하지만 코드에 성공 분기가 구현돼 있다는 사실은 실제 실행이 그 분기에 도달했다는 증거가 아닙니다.

이번 점검에서는 다음 세 가지를 별도로 봅니다.

1. 스크립트가 어떤 성공 경로를 설계했는가
2. 실제 실행이 어느 단계까지 도달했는가
3. 실패 뒤 어떤 안전 경계를 지켰는가

특히 v2와 v3의 실패 정밀도도 구분해야 합니다. v2에서 보존된 실패 경계는 `PHYSICAL_DATABASE_RESTORE` 단계입니다. 이후 범위를 좁힌 v3에서야 `PHYSICAL_START`라는 세부 절차가 확인됐습니다. v3의 세부 코드를 v2의 실제 결과로 소급해서는 안 됩니다.

### 확인하지 않은 항목은 성공도 실패도 아닙니다

컨테이너 시작 전에 중단됐다면 MySQL 인증 검사는 `실패`가 아니라 `미수행`입니다. Ghost 기동까지 가지 않았다면 Ghost 복원도 `실패`가 아니라 `미수행`입니다.

이 구분을 지켜야 후속 작업이 명확해집니다.

- `failed`: 실행했고 성공 조건을 충족하지 못했습니다.
- `not_run`: 선행 단계에서 중단돼 실행하지 않았습니다.
- `pending`: 실행 계획은 있지만 아직 증거가 없습니다.
- `verified`: 정해진 조건과 증거를 모두 충족했습니다.

---

## 1부. 콜드 백업은 복원 가능한 입력인가

### 1\. 백업 성공과 복원 성공은 다른 상태입니다

백업을 다음과 같은 계단식 상태로 나누면 과장을 피할 수 있습니다.

| 단계     | 확인 질문                       | 성공 증거            |
| ------ | --------------------------- | ---------------- |
| 생성     | 예정된 파일과 데이터가 만들어졌는가?        | 백업 작업 결과와 산출물 존재 |
| 무결성    | 파일이 전송·보관 중 바뀌지 않았는가?       | 체크섬 일치           |
| 구조 안전성 | 중복·위험 경로 없이 처리할 수 있는가?      | 아카이브 목록 검사       |
| 입력 완전성 | 복원에 필요한 요소가 빠짐없이 있는가?       | 매니페스트와 정책 목록 대조  |
| 격리 복원  | 운영과 분리된 곳에서 실제로 기동하는가?      | 임시 인스턴스 기동       |
| 데이터 검증 | 인증, 스키마, 테이블, 대표 데이터가 정상인가? | 제한된 읽기 검증        |
| 서비스 검증 | Ghost와 MySQL의 연결이 정상인가?     | 격리 애플리케이션 점검     |

5편에서 확인한 결과는 주로 첫 세 단계에 해당합니다. 격리 복원과 데이터·서비스 검증은 별도의 실행 증거가 필요합니다.

### 2\. 실제로 보존된 백업 점검 결과

읽기 전용 검사에서 다음 결과가 보존됐습니다.

| 항목             | 결과        | 의미                               |
| -------------- | --------- | -------------------------------- |
| 체크섬            | 확인 완료     | 체크섬 생성 이후 파일 내용이 바뀌지 않았습니다.      |
| 아카이브 목록과 매니페스트 | 항목과 순서 일치 | 기록된 목록과 실제 아카이브 구조가 대응했습니다.      |
| 중복 항목          | 발견되지 않음   | 같은 대상 경로를 여러 항목이 덮어쓸 징후가 없었습니다.  |
| 위험 경로          | 발견되지 않음   | 상위 디렉터리 탈출 같은 위험 표현이 확인되지 않았습니다. |
| 비정규 경로         | 발견되지 않음   | 정규화 과정에서 모호해질 경로가 확인되지 않았습니다.    |
| 허용 범위 밖 경로     | 최종 확인 필요  | 조직의 허용 목록과 완전히 대조하는 절차가 남았습니다.   |
| 격리 복원          | 대기        | 당시에는 실제 기동·인증·데이터 검증 증거가 없었습니다.  |

앞의 다섯 항목이 정상이라는 사실은 중요합니다. 격리 복원에 사용할 후보 입력의 기본 품질을 뒷받침하기 때문입니다. 그러나 마지막 두 항목 때문에 이 결과를 `restore_verified`라고 부를 수는 없습니다.

### 3\. 콜드 백업에서도 일관성 증거가 필요합니다

MySQL 공식 문서는 물리 백업을 데이터 디렉터리와 파일의 원시 복사본으로 설명합니다. 서버가 실행 중일 때 파일을 복사하면 쓰기 시점이 섞일 수 있으므로 적절한 잠금이나 전용 백업 방식이 필요합니다. 서버를 정상 종료한 뒤 수행하는 콜드 백업은 애플리케이션의 쓰기 간섭을 줄여 절차를 단순하게 만듭니다.

하지만 `콜드 백업`이라는 이름만으로 일관성이 자동 보장되지는 않습니다. 최소한 다음 기록이 필요합니다.

1. 데이터베이스가 정상 종료된 뒤 파일 복사가 시작됐는지 확인합니다.
2. Ghost 콘텐츠와 MySQL 데이터가 어느 복구 시점을 나타내는지 기록합니다.
3. 백업 시작·종료 시각과 작업 결과 코드를 남깁니다.
4. 체크섬 파일과 매니페스트를 같은 백업 세트에 연결합니다.
5. 복사 이후 원본을 다시 쓰거나 수정하지 않는 검증 경계를 정합니다.
6. 복원에 필요한 구성 요소와 제외한 요소를 명시합니다.

보존 자료의 무결성 검사는 이 전체 과정을 새로 실행한 것이 아닙니다. 당시 파일이 보존된 상태를 읽기 전용으로 확인한 결과와, 앞으로 재현할 때 지켜야 할 원칙을 혼동하면 안 됩니다.

### 4\. 체크섬이 증명하는 범위

재현 환경에서는 체크섬 목록의 각 경로가 **정규화된 상대경로**이고, `..`이나 절대경로를 사용하지 않으며, 실제 해석 결과가 모두 `<BACKUP_DIR>` 안에 머무르는지 먼저 검증해야 합니다. 신뢰하지 않은 체크섬 목록을 바로 실행하면 목록에 적힌 외부 경로까지 읽을 수 있으므로 순서를 바꾸면 안 됩니다.

경로 경계를 확인한 뒤 다음처럼 백업 세트의 체크섬을 검사할 수 있습니다.

```bash
cd "<BACKUP_DIR>"
sha256sum --check "<CHECKSUM_FILE>"

```

이 검사가 성공하면 체크섬을 만든 시점과 현재 파일 내용이 같다는 사실을 뒷받침합니다. 그러나 다음 사실까지 증명하지는 않습니다.

- 체크섬 생성 전부터 백업 파일이 올바른 상태였는지
- 복원에 필요한 모든 파일이 들어 있는지
- 데이터베이스 파일이 하나의 일관된 시점인지
- 대상 환경에서 소유권과 권한을 올바르게 적용할 수 있는지
- Ghost와 MySQL이 실제로 함께 기동하는지

체크섬은 **복원 테스트의 대체재**가 아닙니다. 복원 테스트에 넣을 입력이 운반과 보관 중 손상되지 않았다는 증거입니다.

### 5\. 아카이브는 압축을 풀기 전에 목록을 봅니다

백업 아카이브를 운영 서버에 바로 풀어서는 안 됩니다. 먼저 목록만 읽어 구조를 확인합니다.

```bash
tar -tf "<ARCHIVE_FILE>"

```

목록 검사에서는 다음 질문에 답해야 합니다.

- 매니페스트와 실제 항목 수가 같은가
- 항목 순서까지 정책과 일치하는가
- 모든 경로가 기대한 상대 경로 범위 안에 있는가
- `..`나 의도하지 않은 절대경로가 들어 있지 않은가
- 동일한 대상 경로가 중복되지 않는가
- 심볼릭 링크가 허용 범위 밖을 가리키지 않는가
- 장치 파일이나 특수 파일이 정책상 허용되는가

보존된 검사에서는 목록과 순서가 일치했고 중복·위험·비정규 경로가 발견되지 않았습니다. 다만 이것은 **검사한 규칙의 범위에서** 안전하다는 뜻입니다. 운영 정책의 허용 경로 목록과 완전히 대조하는 단계는 별도로 끝내야 합니다.

### 6\. 복원 준비 상태를 과장 없이 기록합니다

이 시점의 정확한 판정은 다음과 같습니다.

```text
backup_integrity=verified
archive_manifest_match=verified
dangerous_or_duplicate_paths=not_found_in_checked_scope
allowed_scope_validation=verification_required
isolated_restore=pending
service_recovery=not_verified

```

`isolated_restore=pending`을 `restore_verified`로 바꾸려면 운영 환경과 분리된 곳에서 실제 기동, 인증, 스키마, 테이블, 대표 데이터까지 확인해야 합니다.

---

## 2부. 격리 전체 복원 검증기 v2

### 7\. 복원보다 먼저 운영 경계를 보호합니다

운영 서버에서 복원 테스트를 할 때 가장 위험한 실수는 임시 작업이 운영 대상을 참조하는 것입니다. 컨테이너 이름만 다르게 지정한다고 격리가 완성되지는 않습니다.

격리 전체 복원 검증기 v2는 시작 전에 다음 경계를 확인하도록 설계됐습니다.

- 운영 컨테이너와 같은 대상이나 이름을 사용하지 않습니다.
- 운영 볼륨을 테스트에 쓰기 가능 상태로 연결하지 않습니다.
- 백업 입력은 읽기 전용으로 취급합니다.
- 외부 네트워크와 게시 포트를 사용하지 않습니다.
- 테스트 작업 영역의 유형, 소유 관계, 링크, 쓰기 가능 범위를 확인합니다.
- 이전 실패에서 남은 임시 컨테이너가 없는지 확인합니다.
- 성공과 실패 모두에서 임시 자원을 정리합니다.

Docker의 `--network none`은 테스트 컨테이너를 외부 네트워크와 분리할 때 사용할 수 있습니다.

```bash
docker create \
  --name "<RESTORE_CONTAINER>" \
  --network none \
  "<MYSQL_IMAGE>"

```

Docker 공식 문서에 따르면 `none` 네트워크를 사용하는 컨테이너에는 루프백 장치만 남습니다. 그러나 이 옵션이 마운트, Docker 소켓, 호스트 파일 권한까지 격리하는 것은 아닙니다. 네트워크, 마운트, 자격증명, Docker API 접근을 각각 별도로 통제해야 합니다.

### 8\. 네 종류의 잠금은 서로 다른 충돌을 막습니다

검증기는 한 종류의 잠금만 사용하지 않았습니다. 역할이 다른 네 개의 잠금 경계를 두어 동시에 진행될 수 있는 백업·복원·네트워크·계정 정책 작업과 충돌하지 않도록 설계했습니다.

| 잠금 역할        | 막으려는 충돌                  |
| ------------ | ------------------------ |
| 격리 복원 검증     | 동일 검증기의 중복 실행            |
| 전체 콜드 백업     | 검증 도중 백업 세트가 교체되는 상황     |
| 네트워크 격리 정책   | 임시 컨테이너의 네트워크 조건이 바뀌는 상황 |
| 데이터베이스 계정 정책 | 검증 중 인증 조건이 변경되는 상황      |

잠금 파일이 존재한다는 사실만으로 다른 작업이 실행 중이라고 단정해서는 안 됩니다. 잠금 경로의 유형, 소유 관계, 링크 여부, 쓰기 경계를 확인한 뒤 실제로 잠금을 획득할 수 있는지 판단해야 합니다.

모든 종료 경로에서 잠금이 해제되는지도 중요합니다. 성공 분기에만 해제를 두면 한 번의 실패 이후 다음 검증이 계속 막힐 수 있습니다.

### 9\. 작업 영역은 문자열이 아니라 실제 객체를 검사합니다

복원 검증기는 민감한 파일을 다루므로 `<RESTORE_WORKDIR>`라는 경로 문자열만 비교해서는 부족합니다. 심볼릭 링크나 상위 디렉터리 권한 때문에 의도하지 않은 위치를 쓸 수 있기 때문입니다.

검사해야 할 항목은 다음과 같습니다.

1. 작업 영역과 상위 계층이 예상한 파일 유형인지 확인합니다.
2. 경로 중간에 심볼릭 링크가 끼어 있지 않은지 확인합니다.
3. 소유 관계와 권한이 검증 전용 정책에 맞는지 확인합니다.
4. 다른 계정이 작업 경로를 교체할 수 없는지 확인합니다.
5. 이전 성공·실패 마커가 현재 실행의 판정을 오염시키지 않는지 확인합니다.
6. 작업 영역을 입력 백업과 분리합니다.

성공 마커와 실패 마커가 동시에 존재하는 상태는 허용하면 안 됩니다. 어느 실행의 결과인지 모호해지기 때문입니다. 각 실행은 고유한 단위로 결과를 남기고, 모든 관문을 통과한 뒤에만 성공 마커를 만들어야 합니다.

### 10\. 입력 백업은 읽기 전용으로 고정합니다

복원 도구가 원본 백업을 직접 수정하면 재검증이 어려워지고 증거 보존도 깨집니다. 입력과 작업 복사본을 분리해야 합니다.

- `<BACKUP_SET>`은 읽기 전용으로 엽니다.
- 압축 해제와 데이터 파일 준비는 `<RESTORE_WORKDIR>`에서만 수행합니다.
- 실행 전후 입력 파일의 메타데이터와 체크섬을 비교합니다.
- 입력 디렉터리에 결과 파일이나 완료 마커를 쓰지 않습니다.
- 테스트 자격증명은 백업 안의 운영 비밀값과 분리합니다.

실제 결과의 `input_boundary_unchanged=true`는 확인한 범위에서 이 경계가 지켜졌다는 뜻입니다. 백업 내용이 복원 가능한 상태였다는 뜻은 아닙니다.

### 11\. v2에 설계된 성공 경로

격리 전체 복원 검증기의 성공 경로는 대략 다음 순서였습니다.

1. 실행 환경과 입력 백업을 사전 검사합니다.
2. 관련 작업의 잠금을 획득합니다.
3. 이전 임시 자원과 결과 마커 상태를 확인합니다.
4. 격리 작업 영역에 Ghost 콘텐츠와 MySQL 물리 백업을 준비합니다.
5. 외부 네트워크와 게시 포트가 없는 임시 데이터베이스를 만듭니다.
6. 임시 데이터베이스를 시작합니다.
7. 준비 상태, 인증, 예상 스키마와 테이블을 확인합니다.
8. 격리된 Ghost가 임시 데이터베이스를 사용하도록 구성합니다.
9. 제한된 방식으로 애플리케이션 상태를 확인합니다.
10. 임시 자원을 제거하고 성공 완료 마커를 남깁니다.

이 목록은 **설계된 성공 경로**입니다. 실제 실행이 이 순서를 완료했다는 기록이 아닙니다.

### 12\. v2의 실제 결과는 `PHYSICAL_DATABASE_RESTORE` 실패입니다

v2 실행에서 보존된 핵심 결과는 다음 범위입니다.

```text
status=failed
fixed_failure_stage_code=PHYSICAL_DATABASE_RESTORE
test_container_cleanup_complete=true
live_boundary_unchanged=true
input_boundary_unchanged=true
success_complete_marker_present=false

```

여기서 확인된 가장 세부적인 실패 경계는 `PHYSICAL_DATABASE_RESTORE` 단계입니다. v2 결과만으로 이 단계 안의 어느 명령이나 절차에서 실패했는지는 확정하지 않습니다.

| 보존된 값                                    | 올바른 해석                     |
| ---------------------------------------- | -------------------------- |
| status=failed                            | 격리 전체 복원 검증이 완료되지 않았습니다.   |
| PHYSICAL\_DATABASE\_RESTORE              | 물리 데이터베이스 복원 단계에서 중단됐습니다.  |
| test\_container\_cleanup\_complete=true  | 실패 뒤 테스트 컨테이너 정리가 완료됐습니다.  |
| live\_boundary\_unchanged=true           | 확인한 범위에서 운영 환경이 바뀌지 않았습니다. |
| input\_boundary\_unchanged=true          | 확인한 범위에서 입력 백업이 바뀌지 않았습니다. |
| success\_complete\_marker\_present=false | 성공 완료 증거가 생성되지 않았습니다.      |

따라서 v2 실행을 두고 MySQL 복원 성공이나 Ghost 서비스 복구 성공을 주장할 수 없습니다. 또한 v3에서 나중에 확인한 `PHYSICAL_START`를 v2의 실제 세부 결과로 소급해서도 안 됩니다.

### 13\. 실패 안전성과 복원 성공은 별개의 판정입니다

v2가 실패했어도 다음 세 가지는 별도로 확인됐습니다.

- 테스트 컨테이너 정리 완료
- 확인한 범위에서 운영 환경 변경 없음
- 확인한 범위에서 입력 백업 변경 없음

이 값들은 검증기가 실패를 운영 장애나 백업 변조로 확산시키지 않았다는 **실패 안전성 증거**입니다. 복원이 성공했다는 증거는 아닙니다.

마찬가지로 `cleanup_complete`가 민감 정보의 완전 삭제를 자동으로 의미하지는 않습니다. 임시 파일, 원문 로그, 실패 증거는 보존 정책과 접근 권한을 별도로 확인해야 합니다.

---

## 3부. 집중형 물리 복원 검증기 v3

### 14\. 전체 복원에서 물리 시작 단계로 범위를 좁힙니다

v2는 Ghost 콘텐츠와 MySQL 물리 백업을 아우르는 전체 복원을 목표로 했습니다. 실패 경계가 `PHYSICAL_DATABASE_RESTORE`까지밖에 좁혀지지 않았기 때문에 후속 검증에서는 범위를 물리 MySQL 복원으로 제한했습니다.

집중형 물리 복원 검증기 v3의 목표는 다음과 같습니다.

- 콜드 데이터 영역을 임시 작업 공간에 준비합니다.
- 데이터베이스 전용 임시 컨테이너만 만듭니다.
- 외부 네트워크와 게시 포트를 사용하지 않습니다.
- 컨테이너 생성과 시작을 별도 단계로 기록합니다.
- 시작 이후에만 준비 상태와 인증·데이터 검증을 수행합니다.
- 실패 원문 증거는 비공개 영역에 보존합니다.
- 성공과 실패 모두에서 임시 컨테이너를 제거합니다.

반대로 다음 항목은 v3의 실제 검증 범위가 아니었습니다.

- Ghost 애플리케이션 복원
- 논리 SQL 덤프 복원
- 외부 HTTP 요청을 통한 서비스 검증
- 운영 데이터베이스나 운영 볼륨 변경

범위를 좁힌 이유는 더 적은 권한과 더 선명한 단계 코드로 실패를 재현하기 위해서입니다.

### 15\. v3의 성공 경로도 실제 성공 증거가 아닙니다

v3에는 다음 성공 흐름이 구현돼 있었습니다.

1. 물리 백업 입력과 작업 경계를 확인합니다.
2. 외부 네트워크가 없는 임시 컨테이너를 생성합니다.
3. 물리 데이터 영역을 임시 컨테이너에 연결합니다.
4. 컨테이너 시작 요청을 수행합니다.
5. MySQL 준비 상태를 기다립니다.
6. 네트워크 이름공간이 루프백으로 제한됐는지 확인합니다.
7. 테스트 자격증명으로 인증합니다.
8. 예상 데이터베이스와 테이블을 확인합니다.
9. 대표 읽기 또는 테이블 검사를 수행합니다.
10. 제한된 결과를 남기고 임시 컨테이너를 제거합니다.

성공 분기의 출력문과 결과 템플릿이 존재해도 실제 실행이 10단계를 완료한 것은 아닙니다. 보존된 콘솔 결과나 고유 실행 산출물이 없으면 성공으로 판정할 수 없습니다.

### 16\. v3에서 `PHYSICAL_START`가 확인됐습니다

v3와 후속 분류에서 고정된 실제 결과는 다음과 같습니다.

```text
status=failed
fixed_failure_stage_code=PHYSICAL_DATABASE_RESTORE
fixed_failure_reason_code=PHYSICAL_DATABASE_RESTORE_FAILED
fixed_failure_step_code=PHYSICAL_START
mysql_wait_observation_class=NOT_RUN
physical_failure_runtime_state_class=RUNTIME_STATE_ERROR
physical_failure_log_capture_class=DOCKER_LOGS_UNAVAILABLE
physical_failure_private_evidence_retained=true
test_container_cleanup_complete=true
live_boundary_unchanged=true
input_boundary_unchanged=true
success_complete_marker_present=false

```

v2와 비교하면 실패 경계가 한 단계 더 좁아졌습니다.

| 구분               | 실제 확인된 실패 경계                                   |
| ---------------- | ---------------------------------------------- |
| 격리 전체 복원 검증기 v2  | PHYSICAL\_DATABASE\_RESTORE                    |
| 집중형 물리 복원 검증기 v3 | PHYSICAL\_DATABASE\_RESTORE 안의 PHYSICAL\_START |

`PHYSICAL_START`는 임시 데이터베이스 컨테이너의 시작 경계에서 실패했다는 뜻입니다. MySQL 준비 상태 대기에는 도달하지 않았습니다.

여기서 `test_container_cleanup_complete`, `live_boundary_unchanged`, `input_boundary_unchanged`는 당시 실패 처리기가 보존한 실행 요약의 기록값입니다. 후속 분류기가 운영 환경과 입력 자료 전체를 다시 읽어 독립적으로 재검증한 결과는 아닙니다. 따라서 **기록상 정리·경계 보존**으로 표현하고, 복원 성공이나 운영 환경 전체의 무변경 증명으로 확대하지 않습니다.

### 17\. 수행한 항목과 수행하지 않은 항목을 분리합니다

| 항목              | v3 실제 상태        | 해석                                       |
| --------------- | --------------- | ---------------------------------------- |
| 임시 컨테이너 준비      | 시작 시도 전 단계까지 진행 | 시작 성공과는 다릅니다.                            |
| 물리 데이터베이스 시작    | 실패              | PHYSICAL\_START                          |
| MySQL 준비 상태 대기  | 미수행             | mysql\_wait\_observation\_class=NOT\_RUN |
| 인증              | 미수행             | 시작 선행 조건을 통과하지 못했습니다.                    |
| 스키마·테이블 확인      | 미수행             | 데이터 검증에 도달하지 않았습니다.                      |
| Ghost 애플리케이션 복원 | 미수행             | v3 범위 밖입니다.                              |
| 논리 SQL 복원       | 미수행             | v3 범위 밖입니다.                              |
| 실패 증거           | 비공개 보존          | 보존과 원인 확정은 다릅니다.                         |
| 테스트 컨테이너 정리     | 완료              | 실패 안전성 확인                                |
| 운영·입력 경계        | 확인한 범위에서 변경 없음  | 복원 성공과는 별개입니다.                           |

여기서 `mysql_wait_observation_class=NOT_RUN`이 중요합니다. `mysqladmin ping`이 실패한 것이 아니라, 그 판정을 실행할 단계에 도달하지 못했다는 뜻입니다.

MySQL 공식 문서상 `mysqladmin ping`은 서버 실행 여부를 보는 명령입니다. 인증이 거부돼도 서버가 실행 중이면 성공 종료 상태를 반환할 수 있으므로, 향후 이 단계에 도달하더라도 `ping OK`만으로 복원 성공을 선언해서는 안 됩니다. 인증, 스키마, 테이블, 대표 데이터 검증이 별도로 필요합니다.

### 18\. 상태 분류는 원인명이 아닙니다

`physical_failure_runtime_state_class=RUNTIME_STATE_ERROR`는 원인명이 아닙니다. 실행 상태를 넓은 부류로 묶은 관찰값입니다.

`physical_failure_log_capture_class=DOCKER_LOGS_UNAVAILABLE`도 로그 내용이 특정 오류였다는 뜻이 아닙니다. 해당 실행에서 Docker 로그를 원인 증거로 사용할 수 없었다는 뜻입니다.

잘못된 해석과 올바른 해석을 비교하면 다음과 같습니다.

| 잘못된 표현                | 올바른 표현                               |
| --------------------- | ------------------------------------ |
| MySQL 데이터 검증에 실패했습니다. | 컨테이너 시작 단계에서 실패해 데이터 검증을 수행하지 못했습니다. |
| 런타임 오류가 정확한 원인입니다.    | 런타임 상태 오류 부류로 관찰됐지만 정확한 원인은 미확정입니다.  |
| Docker 로그에 원인이 없었습니다. | 해당 실행에서 Docker 로그 증거를 사용할 수 없었습니다.   |
| 증거를 보존했으므로 원인을 찾았습니다. | 비공개 증거를 보존했지만 해석과 원인 확정은 별도입니다.      |

---

## 4부. Docker 이벤트 기반 읽기 전용 포렌식

### 19\. 정확한 실패 실행 하나로 범위를 고정합니다

Docker 데몬의 이벤트 스트림에는 여러 컨테이너, 이미지, 네트워크, 볼륨의 활동이 섞입니다. 시간 범위와 대상을 고정하지 않으면 다른 실행의 이벤트를 잘못 연결할 수 있습니다.

이번 포렌식은 다음 경계로 설계됐습니다.

- 진단 대상은 실패한 고유 실행의 Docker 수명주기 이벤트 메타데이터로 한정합니다.
- 로컬 Docker Unix 소켓만 사용해 원격 데몬으로 전환되지 않게 합니다.
- 검증기 잠금의 유형, 소유 관계, 링크, 쓰기 경계를 확인합니다.
- 동일 작업이 진행 중이지 않은지 보기 위해 자문 잠금을 잠시 획득합니다.
- 실행 시작·종료 시각과 테스트 컨테이너 식별 조건을 함께 사용합니다.
- 결과에는 원문 식별자 대신 고정 분류와 개수만 남깁니다.
- 컨테이너 내부, 애플리케이션 로그, 데이터베이스 내용은 읽지 않습니다.

이 범위는 민감 정보 노출과 운영 변경을 줄이는 대신, 애플리케이션 내부의 상세 원인까지 설명할 수 있는 정보도 제한합니다. 이는 누락이 아니라 의도적으로 선택한 증거 경계입니다.

### 20\. Docker 과거 이벤트는 최근 256개로 제한됩니다

Docker 공식 문서에 따르면 `docker system events`로 과거 이벤트를 조회할 때 마지막 256개만 반환됩니다. `--since`, `--until`, `--filter`를 사용해 관심 대상을 좁힐 수 있지만 보존량 자체가 늘어나는 것은 아닙니다.

일반화한 조회 예시는 다음과 같습니다.

```bash
DOCKER_HOST="unix://<LOCAL_DOCKER_SOCKET>" \
docker system events \
  --since "<START_TIME>" \
  --until "<END_TIME>" \
  --filter "type=container" \
  --filter "container=<RESTORE_CONTAINER>"

```

이 한계를 판정 문구에 반영해야 합니다.

- 올바른 표현: 보존된 최근 256개 이벤트 범위에서 확인했습니다.
- 잘못된 표현: Docker의 전체 과거 이벤트를 확인했습니다.
- 올바른 표현: 현재 보존 범위에서 일치 이벤트가 발견되지 않았습니다.
- 잘못된 표현: 해당 이벤트는 발생하지 않았습니다.

이벤트가 많은 호스트에서는 관심 시간대의 기록이 이미 밀려났을 수 있습니다. 필터는 남아 있는 결과를 골라낼 뿐, 보존 범위를 늘리거나 사라진 이벤트를 복원하지 않습니다.

### 21\. 이벤트는 수명주기 메타데이터이지 원인 로그가 아닙니다

Docker 이벤트는 컨테이너 생성, 시작, 종료, 제거 같은 수명주기 변화를 시간순으로 연결하는 데 유용합니다. 그러나 시작 실패의 상세 원인이 항상 이벤트에 들어 있는 것은 아닙니다.

| 확인하려는 질문                | 주로 필요한 증거              |
| ----------------------- | ---------------------- |
| 컨테이너 생성이 시도됐는가?         | Docker 수명주기 이벤트        |
| 시작 요청 뒤 종료·제거가 이어졌는가?   | 시간순 이벤트 연결             |
| 런타임이 어떤 오류를 반환했는가?      | 제한된 실패 원문 또는 데몬 진단 기록  |
| MySQL이 데이터 영역을 왜 거부했는가? | MySQL 오류 기록과 데이터 상태    |
| LSM 정책이 실행을 막았는가?       | 커널·LSM 감사 기록           |
| 메모리 부족으로 종료됐는가?         | 커널·cgroup·Docker 자원 기록 |

이번 단계는 첫 두 질문에 가까운 이벤트 메타데이터만 읽었습니다. 따라서 파일 권한, 마운트, 런타임, LSM, OOM, 데이터 호환성 중 하나를 정확한 원인으로 고를 수 없습니다.

### 22\. `inspect`, `logs`, `exec`를 의도적으로 제외한 이유

원인 분석이라면 가능한 모든 명령을 바로 실행하는 것이 좋아 보일 수 있습니다. 그러나 운영 서버의 보안 점검에서는 명령마다 정보 노출과 변경 가능성을 검토해야 합니다.

- `docker inspect`는 환경변수, 마운트, 네트워크와 같은 민감한 구성을 출력할 수 있습니다.
- `docker logs`는 자격증명, 내부 경로, 요청 정보가 섞인 애플리케이션 출력을 보여 줄 수 있습니다.
- `docker exec`는 실행 중 컨테이너에 새 프로세스를 만들기 때문에 외부 메타데이터 감사와 성격이 다릅니다.
- HTTP·네트워크 프로브는 서비스에 실제 요청을 만들어 이번 이벤트 전용 범위를 벗어납니다.

따라서 이 단계에서는 다음 행위를 제외했습니다.

```text
container_exec=false
live_container_logs_read=false
application_log_read=false
config_or_secret_payload_read=false
database_payload_read=false
http_or_network_probe=false
explicit_live_mutation=false

```

이 제한으로 운영·비밀정보 경계를 보존했지만, 원인 규명에 필요한 일부 증거도 읽지 않았습니다. 결과가 `root_cause=undetermined`인 중요한 이유입니다.

### 23\. 비공개 실패 증거와 공개 결과를 나눕니다

v3는 실패 시 상세 증거를 공개 출력에 흘리지 않고 접근이 제한된 영역에 보존하도록 설계했습니다. 공개 가능한 결과에는 다음처럼 고정된 상태만 남깁니다.

- 실패 단계와 세부 절차 코드
- 런타임 상태의 넓은 분류
- 로그 증거 사용 가능 여부
- 실패 증거 보존 여부
- 테스트 컨테이너 정리 여부
- 운영·입력 경계 변경 여부
- 성공 마커 존재 여부

이 방식은 블로그나 자동화 출력에 계정, 내부 경로, 컨테이너 식별자, 데이터베이스 정보가 섞이는 일을 줄입니다. 동시에 후속 분석자가 원본 증거의 무결성을 유지한 채 필요한 범위만 확인할 수 있게 합니다.

`physical_failure_private_evidence_retained=true`는 증거가 존재한다는 뜻입니다. 그 증거를 이미 해석해 정확한 원인을 확정했다는 뜻은 아닙니다.

### 24\. 정리와 경계 검증은 실패 뒤에도 계속됩니다

`PHYSICAL_START`에서 중단돼도 검증기는 바로 끝나지 않습니다. 현재 실행이 만든 테스트 컨테이너를 제거하고, 운영·입력 경계가 바뀌지 않았는지 다시 확인해야 합니다.

```text
test_container_cleanup_complete=true
live_boundary_unchanged=true
input_boundary_unchanged=true
success_complete_marker_present=false

```

이 네 값은 다음을 뜻합니다.

- 실패한 임시 컨테이너가 남지 않았습니다.
- 확인한 범위에서 운영 환경이 바뀌지 않았습니다.
- 확인한 범위에서 백업 입력이 바뀌지 않았습니다.
- 실패 실행을 성공으로 오인할 완료 마커가 없습니다.

정리 명령이 성공 종료했다는 사실만 믿지 말고 대상의 부재를 다시 확인해야 합니다. 반대로 이름 패턴만 사용해 컨테이너나 볼륨을 일괄 삭제해서도 안 됩니다. 삭제 대상은 현재 실행이 생성한 임시 자원으로 정확히 고정해야 합니다.

### 25\. 현재 증거로 세울 수 있는 것은 가설까지입니다

`PHYSICAL_START` 실패는 다음 가능성을 남깁니다.

- 임시 데이터 영역의 소유권 또는 권한 문제
- 마운트 대상이나 파일 유형 불일치
- 데이터 파일과 실행 환경의 호환성 문제
- 컨테이너 런타임 시작 오류
- AppArmor 같은 LSM 정책의 거부
- seccomp 또는 다른 실행 제한
- 메모리 부족이나 강제 종료
- 증거 수집 시점과 컨테이너 정리 시점의 경합

이 목록은 **조사 가설**입니다. 현재 보존된 Docker 이벤트 메타데이터와 넓은 상태 분류만으로 어느 하나를 실제 원인이라고 단정할 수 없습니다.

정확한 원인에 접근하려면 같은 고유 실행에 연결되는 보존 시작 증거와, 시간 범위를 제한한 커널·LSM·coredump 기록을 교차 확인해야 합니다. 이 작업도 운영 대상 변경 없이 별도의 읽기 전용 단계로 진행해야 합니다.

---

## 5부. 안전하게 재현하는 전체 절차

### 26\. 1단계: 판정 상태와 변경 금지 경계를 먼저 정합니다

실행 전에 상태 이름과 중단 조건을 문서화합니다.

```text
candidate_input
integrity_verified
restore_in_progress
restore_failed
restore_verified
cleanup_verified
root_cause_undetermined

```

운영 컨테이너, 운영 볼륨, 운영 네트워크, 원본 백업, 운영 자격증명은 변경 금지 대상으로 고정합니다. 어느 하나라도 테스트 대상으로 해석되면 실행을 중단합니다.

### 27\. 2단계: 백업 입력을 읽기 전용으로 검사합니다

1. 체크섬을 검증합니다.
2. 아카이브 목록과 매니페스트를 비교합니다.
3. 중복·위험·비정규 경로를 검사합니다.
4. 허용 경로 정책과 전체 항목을 대조합니다.
5. Ghost 콘텐츠와 MySQL 데이터의 복구 시점 관계를 기록합니다.
6. 검사 전후 입력 상태가 같은지 확인합니다.

입력 검사에 실패하면 복원 컨테이너를 만들지 않습니다.

### 28\. 3단계: 잠금과 작업 영역을 검증합니다

1. 관련 백업·복원·정책 작업과 충돌하지 않는지 확인합니다.
2. 잠금 경로의 유형, 소유 관계, 링크, 쓰기 경계를 검사합니다.
3. 고유 실행 단위를 만듭니다.
4. 이전 임시 컨테이너와 모호한 결과 마커가 없는지 확인합니다.
5. 작업 영역이 운영 경로나 입력 백업과 겹치지 않는지 확인합니다.

사전 조건이 모호하면 자동 보정하지 말고 실패로 종료하는 편이 안전합니다.

### 29\. 4단계: 네트워크·마운트·자격증명을 각각 격리합니다

`--network none`만 적용했다고 격리가 끝난 것은 아닙니다.

- 외부 네트워크를 연결하지 않습니다.
- 게시 포트를 만들지 않습니다.
- 운영 볼륨을 연결하지 않습니다.
- Docker 소켓을 컨테이너 안에 마운트하지 않습니다.
- 입력 백업은 읽기 전용으로 연결합니다.
- 복원 작업은 임시 쓰기 영역에서만 수행합니다.
- 테스트 자격증명은 운영 값과 분리합니다.

각 경계는 다른 경계를 대신하지 않습니다.

### 30\. 5단계: 단계별로 상태를 남깁니다

컨테이너 생성과 시작, MySQL 준비, 인증, 데이터 검증을 한 단계로 묶으면 실패 지점을 잃게 됩니다.

```text
PRECHECK
LOCK_ACQUIRE
WORKDIR_PREPARE
PHYSICAL_DATABASE_RESTORE
PHYSICAL_CREATE
PHYSICAL_START
MYSQL_WAIT
MYSQL_AUTH
SCHEMA_VERIFY
TABLE_VERIFY
CLEANUP
BOUNDARY_VERIFY

```

각 단계의 시작과 종료 시각, 성공 여부, 다음 단계 진입 여부를 고정된 코드로 남깁니다. 원문 오류는 공개 출력과 분리합니다.

### 31\. 6단계: 데이터베이스 기동과 복원 성공을 구분합니다

컨테이너가 시작되더라도 복원이 성공한 것은 아닙니다. 다음 관문이 모두 필요합니다.

1. MySQL 프로세스가 준비 상태가 됩니다.
2. 테스트 자격증명으로 인증에 성공합니다.
3. 예상 데이터베이스가 존재합니다.
4. 예상 테이블이 존재합니다.
5. 대표 읽기 검증 또는 테이블 검사를 통과합니다.
6. 오류 기록에 복원 실패 징후가 없는지 제한된 방식으로 확인합니다.

`mysqladmin ping`은 첫 번째 관문의 보조 증거일 뿐입니다. 인증이나 데이터 무결성을 대신하지 않습니다.

### 32\. 7단계: 실패 증거를 보존한 뒤 정리합니다

실패 즉시 컨테이너를 제거하면 원인 분석에 필요한 메타데이터가 사라질 수 있습니다. 반대로 원문 로그를 공개 출력에 남기면 비밀정보가 유출될 수 있습니다.

안전한 순서는 다음과 같습니다.

1. 실패 단계와 시각을 고정합니다.
2. 허용된 범위의 실패 증거를 비공개 영역에 보존합니다.
3. 공개 결과에는 고정된 상태 코드만 기록합니다.
4. 현재 실행이 만든 임시 자원만 정확히 식별합니다.
5. 테스트 컨테이너와 임시 자원을 정리합니다.
6. 운영·입력 경계를 다시 확인합니다.
7. 성공 마커가 생성되지 않았는지 확인합니다.

### 33\. 8단계: 재현 전에 관찰 지점을 보강합니다

현재 증거로 원인을 확정할 수 없다면 같은 명령을 무작정 반복하기보다 다음 재현의 관찰 지점을 먼저 설계해야 합니다.

1. 실행마다 고유한 상관관계 식별자를 만듭니다.
2. 생성, 시작 요청, 시작 결과, 대기 진입, 정리 시작 시각을 나눠 기록합니다.
3. 원문 오류는 권한이 제한된 비공개 증거 파일에만 남깁니다.
4. Docker 이벤트와 후속 커널 기록을 같은 시간 범위로 연결합니다.
5. 공개 출력에는 단계·이유·증거 보존 여부만 기록합니다.
6. 같은 입력으로 재현해도 입력 백업 자체는 변경하지 않습니다.
7. 운영 식별자와 일치하면 즉시 중단합니다.

---

## 자주 발생하는 잘못된 판정

| 잘못된 판정                     | 문제점                        | 올바른 판정                                |
| -------------------------- | -------------------------- | ------------------------------------- |
| 체크섬이 맞으므로 복원에 성공한다         | 파일 동일성과 서비스 복원을 혼동합니다.     | 무결성 확인, 격리 복원 필요                      |
| 성공 출력 코드가 있으므로 검증 완료다      | 구현된 분기와 실제 실행을 혼동합니다.      | 고유 실행의 실제 결과를 확인                      |
| v2가 PHYSICAL\_START에서 실패했다 | v3에서 확인한 세부 경계를 v2에 소급합니다. | v2는 PHYSICAL\_DATABASE\_RESTORE까지만 확정 |
| ping이 성공하면 데이터도 정상이다       | 서버 응답과 인증·데이터 검증을 혼동합니다.   | 인증·스키마·테이블을 별도 확인                     |
| Docker 이벤트가 없으므로 사건도 없었다   | 최근 256개라는 보존 한계를 무시합니다.    | 보존 범위에서 미발견으로 기록                      |
| 로그를 못 읽었으므로 오류가 없었다        | 증거 부재와 사건 부재를 혼동합니다.       | 로그 증거 사용 불가로 기록                       |
| 컨테이너를 정리했으므로 복원 성공이다       | 실패 안전성과 기능 성공을 혼동합니다.      | 정리 성공과 복원 실패를 함께 기록                   |
| 증거를 보존했으므로 원인을 확정했다        | 보존과 해석을 혼동합니다.             | 원인 미확정 상태 유지                          |
| \--network none이면 완전 격리다   | 마운트·소켓·자격증명 경계가 남습니다.      | 각 권한 경계를 별도 검사                        |

## 통합 재점검 체크리스트

### 백업 입력

- \[ \] 체크섬 결과를 백업 세트와 함께 보존했습니다.
- \[ \] 아카이브 목록과 매니페스트의 항목·순서를 비교했습니다.
- \[ \] 중복, 상위 경로 탈출, 비정규 경로를 검사했습니다.
- \[ \] 조직 정책의 허용 경로 목록과 모든 항목을 대조했습니다.
- \[ \] Ghost 콘텐츠와 MySQL 데이터의 복구 시점 관계를 기록했습니다.
- \[ \] 입력 백업을 읽기 전용으로 취급했습니다.

### 격리 경계

- \[ \] 운영 컨테이너·볼륨·네트워크를 테스트 대상에서 제외했습니다.
- \[ \] 백업, 복원, 네트워크, 계정 정책 잠금의 충돌을 확인했습니다.
- \[ \] 작업 영역의 파일 유형, 상위 계층, 소유 관계, 링크를 검사했습니다.
- \[ \] 외부 네트워크와 게시 포트를 만들지 않았습니다.
- \[ \] Docker 소켓과 운영 자격증명을 임시 컨테이너에 전달하지 않았습니다.
- \[ \] 이전 실행의 임시 자원과 모호한 결과 마커가 없습니다.

### 실행과 판정

- \[ \] 컨테이너 생성과 시작을 다른 단계로 기록했습니다.
- \[ \] MySQL 준비, 인증, 스키마, 테이블 검증을 분리했습니다.
- \[ \] 실행하지 않은 후속 항목을 `미수행`으로 기록했습니다.
- \[ \] 성공 분기 코드와 실제 실행 출력을 분리했습니다.
- \[ \] v2와 v3의 실패 경계를 서로 바꾸어 쓰지 않았습니다.
- \[ \] 모든 관문을 통과한 뒤에만 성공 마커를 만듭니다.

### 포렌식과 정리

- \[ \] 실패 실행의 시간 범위와 대상 식별 조건을 고정했습니다.
- \[ \] Docker 과거 이벤트가 최근 256개로 제한됨을 반영했습니다.
- \[ \] 이벤트 메타데이터와 원인 로그의 역할을 구분했습니다.
- \[ \] 공개 결과에 원문 경로, 환경변수, 비밀값, 식별자를 넣지 않았습니다.
- \[ \] 실패 증거 보존과 원인 확정을 같은 의미로 쓰지 않았습니다.
- \[ \] 현재 실행이 만든 테스트 자원만 정리했습니다.
- \[ \] 정리 뒤 운영·입력 경계를 다시 확인했습니다.
- \[ \] 다음 재현 전에 필요한 관찰 지점을 설계했습니다.

## 최종 판단

5편에서 보존한 콜드 백업은 체크섬, 아카이브와 매니페스트의 항목·순서, 중복·위험·비정규 경로 검사 범위에서 양호했습니다. 격리 복원에 투입할 후보 입력으로서 의미 있는 증거입니다. 다만 허용 경로 정책의 최종 대조와 실제 복원은 별도 관문으로 남았습니다.

격리 전체 복원 검증기 v2는 잠금, 작업 영역, 네트워크, 입력, 성공·실패 마커, 정리 경계를 갖췄지만 실제 실행은 `PHYSICAL_DATABASE_RESTORE` 단계에서 실패했습니다. 복원 성공은 확인되지 않았고, 당시 실행 요약에는 테스트 컨테이너 정리와 운영·입력 경계 보존이 기록됐습니다.

범위를 좁힌 집중형 물리 복원 검증기 v3에서는 실패 지점을 `PHYSICAL_START`까지 세분화했습니다. 하지만 MySQL 준비 상태 대기, 인증, 스키마, 테이블 검증에는 도달하지 못했습니다. Ghost 애플리케이션 복원과 논리 SQL 복원도 수행하지 않았습니다.

Docker 이벤트 기반 읽기 전용 포렌식은 실패 실행의 수명주기 메타데이터를 제한적으로 확인하는 데 유용했습니다. 그러나 과거 이벤트가 최근 256개로 제한되고, 원인 로그를 사용할 수 없었으며, 민감하거나 변경 가능성이 있는 진단을 의도적으로 제외했기 때문에 정확한 원인은 확정되지 않았습니다.

따라서 이번 편의 최종 판정은 다음과 같습니다.

```text
백업 파일 무결성: 확인
격리 전체 복원: 실패
물리 데이터베이스 시작: 실패
MySQL 데이터 검증: 미수행
Ghost 애플리케이션 복원: 미수행
테스트 자원 정리: 실행 요약에 완료로 기록
운영·입력 경계 보존: 실행 요약에 unchanged로 기록
정확한 실패 원인: 미확정

```

다음 편에서는 공개 출력에서 제외한 보존 시작 증거와 시간 범위를 제한한 커널 기록을 교차해, Docker 시작 실패를 어느 수준까지 더 좁힐 수 있는지 살펴봅니다.

[다음 편: 서버 보안 점검 7편 — 보존 증거와 커널·LSM 로그로 Docker 시작 실패 추적하기](https://mlog.me/server-security-check-retained-evidence-kernel-forensics/)

## 공식 문서

- [MySQL 백업 유형](https://dev.mysql.com/doc/refman/8.0/en/backup-types.html?ref=mlog.me)
- [MySQL 원시 데이터 파일 백업](https://dev.mysql.com/doc/refman/8.4/en/replication-solutions-backups-rawdata.html?ref=mlog.me)
- [MySQL mysqladmin 명령](https://dev.mysql.com/doc/refman/8.0/en/mysqladmin.html?ref=mlog.me)
- [MySQL mysqlcheck 명령](https://dev.mysql.com/doc/refman/8.4/en/mysqlcheck.html?ref=mlog.me)
- [Docker none 네트워크 드라이버](https://docs.docker.com/engine/network/drivers/none/?ref=mlog.me)
- [Docker 컨테이너 생성 명령](https://docs.docker.com/reference/cli/docker/container/create/?ref=mlog.me)
- [Docker system events 명령](https://docs.docker.com/reference/cli/docker/system/events/?ref=mlog.me)
- [Docker inspect 명령](https://docs.docker.com/reference/cli/docker/container/inspect/?ref=mlog.me)
- [Docker logs 명령](https://docs.docker.com/reference/cli/docker/container/logs/?ref=mlog.me)
- [Docker exec 명령](https://docs.docker.com/reference/cli/docker/container/exec/?ref=mlog.me)