The Operations Loop 검증기 — 등록 성공에서 장애 복구까지
AI_Manager
검증의 기준은 화면이 열리는지보다 데이터와 경로가 실제로 이어지는지다. 이 글은 2026-09-20 VirtualBox 실습에서 실행한 작업과 관측 결과를 기록한다. 설계상의 기대, 실제 시험, 아직 수행하지 않은 범위를 구분한다.
이 글의 순서- 1. 검증 환경과 판정 기준
- 2. 실제 정상 온보딩 기록
- 3. 정상 경로까지 발견하고 수정한 문제
- 4. 백업과 격리 복원
- 5. 중앙 노드 장애 시험
- 6. Proxy 단절과 지연 데이터 재전송
- 7. 자산 데이터 보호와 경로 검증
- 8. 인터넷 없는 대상 설치와 패키지 검증
- 9. 아키텍처 화면 검증
- 10. 검증 체크리스트와 경계
- 함께 읽기와 구성 자료
1. 검증 환경과 판정 기준
Rocky Linux 10.2, Zabbix 7.0.30 LTS, NetBox 4.7.1, AWX 24.6.1을 사용했다. 중앙 두 VM과 quorum, Gateway, Provision, 세 환경의 Proxy/Bastion을 구성했다. 모든 VM은 한 물리 PC의 메모리·CPU·스토리지를 공유한다.
정상 온보딩은 PXE 설치 → 승인된 SSH 경로 → 실제 자산 수집 → NetBox 네이티브 IPAM → Zabbix 등록 → NetBox 기반 AWX inventory sync → 실제 최신 값 확인까지 모두 성공해야 통과다. API 등록 응답과 최신 데이터 수신은 다른 체크 항목이다.
2. 실제 정상 온보딩 기록
PROD 대상은 빈 32GiB 디스크와 내부망 NIC 하나로 설치했다. 2026-09-20 04:13 KST에 Bastion을 경유한 SSH로 Rocky 10.2, 10.77.20.101/24, SELinux Enforcing, PXE 설치 완료 표식을 확인했다. 설치 RAM 4GiB는 정상 종료 후 1GiB로 조정했다.
| 증거 | 관측 결과 |
|---|---|
| AWX Workflow | 22, successful |
| 실행 시각 | 04:28:27~04:31:05 KST, 약 158초 |
| Onboard Job | 23, successful |
| NetBox inventory update | 24, successful |
| Verify Job | 26, successful |
| Git 커밋 | 58bb87b9842c159b1986784062916c4d9eb97c00 |
| NetBox 자산 | VM ID 1, fml-prod-app-01 |
| IP 관계 | VM Interface ID 1 → IPAddress ID 1 → primary_ip4 10.77.20.101/24 |
| 수집 상태 | complete, collection_errors 빈 배열 |
| Zabbix | hostid 10683, proxyid 1, templateid 10343 |
| 실제 수집 | system.uptime itemid 50799, state 0, error 없음 |
Verify Job은 lastclock=1789846239, lastvalue=942를 확인했고 검증 시각은 1789846262였다. 검증 시점보다 23초 전에 수집한 실제 uptime 값이다. 이 값은 예시로 만든 수치가 아니라 Job 결과에서 읽은 기록이다.
NetBox에는 vCPU 1, 게스트 사용 가능 메모리 954MiB, 디스크 32768MiB, Rocky Linux 10.2, 가상화 유형 virtualbox, 관리 NIC enp0s3, PROD 환경과 실제 파티션 구조가 저장됐다. 게스트의 메모리 관측값 954MiB와 VirtualBox 할당값 1024MiB는 서로 다른 값이다.
AWX의 NetBox inventory에는 ansible_host=10.77.20.101, ansible_user=labadmin, fml_environment=prod, fml_subnet=20이 들어왔다. 단순히 자산 이름만 동기화한 것이 아니라 관리 IP와 Bastion 경로 계산에 필요한 환경 변수를 함께 확인했다.
DEV도 빈 디스크에서 PXE 설치한 뒤 Workflow 30을 실행했다. 05:00:40~05:03:34 KST에 Onboard 31, inventory update 32, Verify 34가 모두 successful로 끝났다. Zabbix host 10684, proxy 2, uptime item 50884로 PROD와 다른 환경에 연결됐다.
STG는 설치 완료 감지로 자동 실행
STG의 첫 SSH 신원을 VirtualBox serial console의 공개키·fingerprint로 승인한 뒤 설치 RAM을 4GiB에서 1GiB로 줄였다. 설치 완료 컨트롤러가 승인 키, 설치 표식, hostname·machine ID를 확인하고 SCM·bootstrap inventory를 동기화했다. 이후 수동 Workflow 실행 없이 Job 38을 시작했다.
| STG 증거 | 결과 |
|---|---|
| Workflow 38 | 10:11:29~10:14:25 KST, successful |
| Onboard / inventory / Verify | 39 / 40 / 42 모두 successful |
| Git 커밋 | 8877198864680095a1a8b5684d3382b9034237e9 |
| NetBox | VM 3 → Interface 3 → IPAddress 3, 10.77.40.101/24 |
| 수집 상태 | complete, collection_errors 빈 배열 |
| Zabbix | host 10685, proxy 3, uptime item 51005 |
| 검증 데이터 | lastclock 1789866845, lastvalue 292, verified_at 1789866862 |
| 컨트롤러 상태 | 해당 machine ID와 Workflow 38을 successful로 저장 |
검증 시각보다 17초 전의 실제 uptime 값까지 확인했다. AWX의 NetBox 인벤토리에도 PROD·DEV·STG 세 호스트와 각 관리 IP·환경 변수가 조회됐다. 최초 SSH 신원 승인은 의도적으로 남긴 관리 단계이며, 미승인 키를 우회해 자동 연결한 것은 아니다.
3. 정상 경로까지 발견하고 수정한 문제
| 실행 | 실패 지점 | 원인과 수정 |
|---|---|---|
| Workflow 7 / Job 8 | Agent RPM metadata 다운로드 | Minimal ISO에 BaseOS/AppStream 경로가 없어 404. 실제 Minimal 저장소로 변경 |
| Workflow 12 / Job 13 | NetBox API 로컬 작업 실행 전 | 인벤토리의 권한 상승 변수가 delegate_to localhost에도 적용되어 EE에 없는 sudo 호출 |
| Workflow 17 / Job 18 | 같은 로컬 작업 | 기존 inventory 변수 영향까지 고려해 task vars의 ansible_become=false와 EE Python 경로를 명시 |
| Workflow 22 / Jobs 23·26 | 전체 흐름 | 자산·IPAM·관제 등록, 인벤토리 갱신, 실제 수집 검증 성공 |
실패한 작업도 삭제하지 않았다. 수정 커밋과 다음 Job 결과를 비교할 수 있게 남겼다. NetBox/Zabbix 등록 전에 실패한 경우를 실제 자산이 중복 생성된 상황과 혼동하지 않는다.
4. 백업과 격리 복원
2026-09-20 04:40 KST에 NetBox DB를 pg_dump custom format으로 백업하고, 운영 NetBox DB와 다른 netbox_restore_20260919194012에 pg_restore했다. 복원 DB에서 VM ID 1, fml-prod-app-01, primary_ip4_id 1을 조회했다.
백업 SHA-256은 06914eb419d88edb97245ed435177b396309a625b65eb14b72ae9fb627667390이다. 기존 DB를 덮어쓰지 않고 별도의 검증 DB를 사용했다. 이 결과는 NetBox 논리 DB 복원 시험이며, 전체 VM·미디어·AWX·Git을 한 번에 복구한 재해 복구 시험은 아니다.
5. 중앙 노드 장애 시험
첫 시험 직전 PostgreSQL leader는 ops02, ops01은 sync standby이며 복제 lag는 0이었다. Zabbix는 ops01 active, ops02 standby였다. 1호기 전원을 강제로 내려 VM 전원 손실을 재현했다.
VIP 10.77.10.10은 2호기로 이동했다. 그러나 전환 구간에서 NetBox backend가 500을 반환하고 VIP는 503을 반환했다. Zabbix는 이후 2호기에서 active 작업을 시작하고 세 Proxy의 연결을 받았다. 이 시험을 ‘무중단 HA 성공’으로 판정하지 않았다.
관측 도구의 첫 로그 파일은 SELinux 정책과 맞지 않는 경로 때문에 생성되지 않았다. 따라서 그 시험에서 정밀한 RTO를 계산하지 않는다. 로깅 경로를 /var/log 아래로 옮기고 정상 기록을 확인한 후 재시험했다.
NetBox 4.7.1의 실제 설정 코드를 확인하니 caching의 SENTINEL_TIMEOUT은 task queue와 같은 방식으로 매핑되지 않았다. timeout만 추가한 두 번째 시험에서도 캐시 조회 한 번에 55.6초가 걸렸다. 이때 1호기 전원 차단 후 NetBox 첫 성공 응답은 약 104초 뒤였다. 이후 로컬 Sentinel 우선 조회와 명시적 재시도 제한을 함께 적용했다.
수정 후 DB·Redis·Zabbix 활성 2호기 전원 손실
05:08:20.777 KST에 ops02를 강제 종료했다. 직전 ops02가 PostgreSQL primary·Redis master·Zabbix active였고, ops01은 DB sync standby/lag 0이었다. 1호기에서 5초 주기, 요청 timeout 4초로 독립 API들을 조회했다. 아래 값은 장애 명령 시각부터 해당 상태를 처음 관측한 시각까지이며 정밀한 내부 전환 시간이나 보장 SLA가 아니다.
| 항목 | 첫 정상 관측 | 장애 명령 후 |
|---|---|---|
| PostgreSQL ops01 primary | 05:08:57.775 | 약 37초 |
| NetBox 기존 자산 API 조회 | 05:09:02.318 | 약 42초 |
| Zabbix API 응답 | 05:09:02.318 | 약 42초 |
| AWX ping | 05:09:30.637 | 약 70초 |
| 장애 이후 수집 시각을 가진 Zabbix 값 | 05:10:12.302 | 약 112초 |
05:10:11에 2호기가 꺼진 상태로 NetBox 자산의 comments에 시험 표식을 PATCH하고 GET으로 읽은 뒤 원래 내용으로 복원했다. 자산 ID 1과 primary IP는 유지됐다. 조회 화면만 열린 것이 아니라 생존 DB 경로의 쓰기도 확인했다. AWX의 ping 회복은 실행 중이던 Job의 무중단을 의미하지 않는다. 이 시험 중 새 Job을 실행해 성공한 결과로 확대 해석하지 않는다.
2호기는 05:11:19 재가동했다. PostgreSQL은 timeline 3을 따라 복구하고 05:18 점검에서 sync standby/lag 0으로 확인됐다. NetBox는 초기화와 Python worker 기동 때문에 더 늦게 준비됐으며 05:23 점검에서 healthy·HTTP 200을 확인했다. 서비스 생존 시간과 장애 노드가 다시 예비 용량으로 복귀하는 시간은 다르다.
수정 후 VIP·DB·Zabbix 활성 1호기 전원 손실
반대 방향도 시험했다. 05:24:28.045 KST에 ops01을 강제 종료했다. ops02에서 VIP 10.77.10.10을 확인했고, PostgreSQL primary는 약 39초, 오류 이후 Zabbix API 정상 응답은 약 44초, NetBox 자산 조회는 약 69초, 장애 후 시각의 관제 값은 약 111초에 관측됐다. 자산 ID와 관리 IP는 유지됐다.
AWX는 ops01에만 있으므로 노드가 꺼진 동안 접속되지 않았다. 05:26:44에 ops01을 다시 켠 뒤 05:29:24에 AWX ping이 회복됐다. 이 값은 AWX 자체 HA 성공이 아니라 원래 노드 복구에 따른 서비스 재개다. 재가동 후 PostgreSQL은 timeline 4의 sync standby/lag 0으로 돌아왔고 NetBox App도 healthy였다.
두 시험 모두 전환 구간의 timeout·HTTP 500/503을 포함한다. ‘무중단’이라고 쓰지 않는다. 비슷한 시험의 개선 전후 수치는 당시 DB 활성 위치와 부하가 다르므로 단순 비율로 성능 개선 효과를 단정하지 않는다.
6. Proxy 단절과 지연 데이터 재전송
05:12:43 KST에 Gateway에서 PROD Edge 10.77.20.10 → 중앙 대역 TCP 10051만 차단했다. SSH·Agent→Proxy·DEV 경로는 유지했다. 중앙 PROD uptime은 clock 1789848759에서 멈췄지만 DEV는 30초 주기로 계속 갱신됐다.
PROD Proxy의 SQLite를 읽기 전용으로 조회하니 중앙에 아직 없는 clock 1789848879 등의 값이 proxy_history에 존재했다. SQLite의 전체 행 수에는 정리 전 전송 완료 행도 포함될 수 있으므로 행 수 자체를 ‘미전송 수’로 해석하지 않았다.
임시 정책에 timeout=240을 지정했으나 05:18:52 확인 때도 규칙이 남아 있었다. 해당 시험 규칙을 명시적으로 제거했고, 실제 차단 구간은 약 6분 9초로 기록했다. 타이머 옵션만 믿고 4분 후 복구됐다고 쓰지 않았다. 반복 시험에서는 외부 복구 예약과 규칙 부재 확인을 함께 수행한다.
05:19:30 중앙 history.get 결과에는 1789848789, 8819, 8849, 8879부터 9119까지 단절 중 수집한 값이 다시 나타났고, 30초 간격을 유지했다. 최신 값도 1789849149로 갱신됐다. 전송 중단 동안 로컬 수집 → 디스크 보관 → 복구 후 원래 시각의 이력 반영을 실제 데이터로 확인했다. 이 짧은 시험은 24시간 버퍼 보존·디스크 가득 참·Proxy VM 자체 장애까지 검증한 것은 아니다.
7. 자산 데이터 보호와 경로 검증
실제 PROD 수집 결과를 기준으로 같은 NetBox 등록을 다시 실행했다. VM ID 1과 IPAddress ID 1이 유지되고 같은 이름·주소가 하나씩만 존재했다. 수집 시각 갱신과 중복 자산 생성을 구분했다.
| 주입한 조건·실제 경로 | 판정 |
|---|---|
| PROD 자산에 DEV 관리 IP 전달 | API 변경 전 거부, mutation 0 |
| 기존 자산과 다른 machine ID 전달 | API 변경 전 거부, mutation 0 |
| 다른 이름의 자산에 사용 중인 IP 전달 | 새 자산 생성 전 거부, mutation 0 |
| CPU·디스크·네트워크 수집 실패를 명시적으로 주입 | 기존 CPU·파티션·제품 필드 보존, partial 표시 |
| 정상 수집 결과 재적용 | complete 상태 복원 |
| 다른 mount namespace의 제품 프로세스 | 호스트의 버전 실행 파일·JAR 조회를 호출하지 않음 |
| 중앙 → PROD 대상 직접 SSH | 거부 |
| 중앙 → 각 환경 Bastion SSH | 허용 |
| PROD → DEV SSH | 거부 |
| PROD → 외부 1.1.1.1:443 | 거부 |
| PROD → 로컬 Proxy·내부 RPM 저장소 | 허용 |
부분 수집 실패는 시험을 위해 만든 입력이며 실제 디스크 고장 사례로 소개하지 않는다. 거부 조건을 먼저 검사한 뒤 변경하고, 일부 API만 성공한 상황은 성공한 객체를 무조건 삭제하지 않고 재실행으로 이어갈 수 있도록 했다.
8. 인터넷 없는 대상 설치와 패키지 검증
PXE 대상은 Internal Network NIC 하나만 갖고 NAT·브리지 NIC가 없다. OS는 Provision의 Rocky 10.2 Minimal 설치 트리에서 받았다. PROD의 Agent RPM 캐시를 비운 뒤 내부 저장소 두 개만 활성화하여 zabbix-agent2 7.0.30을 재설치했다. 실제 6.4MB 다운로드와 GPG 검증, 서비스 active를 확인했다.
외부 연결이 거부된 같은 대상에서 내부 repo metadata와 CA 검증을 사용하는 NetBox HTTPS가 200을 반환했다. 중앙 전체의 새 폐쇄망 재설치는 별도 범위다. 이번에 확인한 것은 인터넷 없는 대상의 OS 설치·Agent 배포와 내부 API 연동이다.
9. 아키텍처 화면 검증
| 화면·기능 | 결과 |
|---|---|
| 1920×1080 전체 화면 | 중앙 서비스와 세 환경의 12개 노드, 탭·설명·하단 흐름이 화면 안에 표시 |
| 화면 맞춤 배율 | graph-scroll client 1590×740, scroll 1590×740. 기본 배율에서 잘림 없음 |
| 본문 폭 784px | 전체 구조 표시, 설명 패널만 독립 스크롤 |
| 모바일 390×844 | 본문 가로 넘침 없음, 작은 구성도 아래 설명 영역 배치 |
| 전체/자동화/모니터링/장애 | 탭과 NetBox·IPAM 단계, Witness/NFS 장애 설명 확인 |
| 확대·복귀 | 125% 확대, 전체 보기 100%, 전체 화면 진입·종료 확인 |
| 브라우저 오류 | 수집된 console error 없음 |
아키텍처의 장애 상태와 흐름 재생은 설명용이다. 서버에 명령을 보내거나 실제 상태를 표시하는 운영 대시보드는 아니다.
10. 검증 체크리스트와 경계
| 점검 항목 | 결과·근거 |
|---|---|
| Rocky 실제 설치 / SELinux Enforcing | PROD·DEV·STG 실제 게스트 확인 |
| NetBox 상세 필드·IPAM·primary IP | 실제 자산·Interface·IPAddress 관계 확인 |
| NetBox 기반 AWX inventory | Workflow의 source update와 접속 변수 확인 |
| Zabbix API 등록 이후 실제 데이터 | PROD·DEV·STG uptime·lastclock 확인 |
| 설치 완료 감지 → AWX 자동 실행 | 승인 신원 확인 후 STG Workflow 38 자동 실행·성공 |
| 자산 재실행·주소 충돌·식별자 변경 | 중복 없음, 충돌 입력 변경 전 거부 |
| 부분 수집 실패의 기존값 보존 | 주입 시험 통과 후 정상 관측으로 복원 |
| 중앙 활성 노드 전원 손실 | DB 승격·API 재개·새 관제 데이터·쓰기 확인 |
| 장애 노드 복귀 | PostgreSQL sync standby, lag 0, App healthy |
| Proxy 전송 단절 | SQLite 보관과 중앙 이력 backfill 확인 |
| 환경 통신 제한 / Bastion 경로 | 허용·거부 경로별 접속 확인 |
| 내부 RPM 다운로드 / 서명 | 캐시를 비운 실제 재다운로드·재설치 성공 |
| NetBox 논리 DB 백업·복원 | 별도 DB에서 같은 자산·IP 참조 확인 |
| 전체 화면·모바일 아키텍처 | FHD 1920×1080, 본문 784px, 모바일 390px 확인 |
| 물리 서버 UEFI/BMC/RAID | 미실행 |
| 중앙 전체 신규 폐쇄망 재구축 | 절차 작성, 전체 재설치 시험 미실행 |
| NFS·quorum·Gateway·Proxy VM 자체 장애 | 영향 분석, 해당 장애 주입은 미실행 |
| 전체 시스템 재해 복구·부하·장시간 내구 시험 | 미실행 |
실제 물리 서버의 UEFI PXE, BMC·RAID 자동화, 두 물리 호스트 간 장애, 스토리지 전체 장애, 모든 상용 DB/WAS 제품 버전 탐지는 이 VM 실습으로 검증했다고 하지 않는다. 각 기능의 구현과 실제 시험 범위를 분리한다.
온라인 초기 설치와 인터넷 없는 대상의 설치·Agent 배포도 구분한다. 중앙 전체를 새 폐쇄망에서 처음부터 재설치하는 시험은 반입 묶음의 의존성 검증까지 포함해야 한다. 기존 서비스에서 외부 NIC만 끊은 결과로 이를 대신하지 않는다.
이 실습에서 통과한 항목과 다음 검증 과제를 같은 표에 남겼다. 개인 PC 한 대의 결과를 운영 SLA나 모든 장애에서의 데이터 무손실 보장으로 바꾸어 표현하지 않는다.
함께 읽기와 구성 자료
포트폴리오 원문 · 온라인망·폐쇄망 구축 가이드 · 장애 시나리오·검증 기록 · VirtualBox 구성 문제 해결 기록
실습 설정·자동화 소스 ZIP · ZIP SHA-256
공개 ZIP에는 구성 템플릿과 자동화 소스를 담았습니다. OS·RPM·컨테이너 이미지와 자격 증명은 포함하지 않습니다. 자신의 주소·공개 CA·승인 SSH 키·비밀 저장소로 구성한 뒤 적용합니다.