백업 zip은 분명히 있었다
새벽 2시에 SCADA 서버 disk가 죽었다. 백업 zip은 NAS에 있다. 예비 PC에 SCADA software를 깔고 project를 풀었다. Editor는 열린다. 여기까지는 순조롭다.
그리고 runtime이 안 뜬다. License가 죽은 장비의 MAC address에 묶여 있었기 때문이다. 벤더 support는 월요일에 연다.
백업 파일의 존재와 복구 능력은 다른 능력이고, 그 차이는 항상 최악의 시점에 드러난다. 규격들도 이걸 한 줄로 묶지 않는다. IEC 62443-3-3은 백업 요구사항(SR 7.3)과 복구·재구성 요구사항(SR 7.4)을 따로 두고, SR 7.3 RE 1에서 백업 자체의 검증을 또 별도로 요구한다. IEC 62443-2-1은 같은 내용을 기술 기능이 아니라 보안 관리 체계 쪽 요구로 둔다 — 절차가 있고 책임자가 있어야 한다는 얘기다. 여기서 조항 번호를 인용할 거면 판을 같이 적어라. 2010년판과 2024년판은 절 구성이 다르다. 번호만 적힌 사내 문서는 2~3년 지나면 아무도 원문에서 못 찾는다. NIST SP 800-82 Rev 3(2023)은 SP 800-53의 contingency planning 계열을 OT용으로 조정하는데, 거기서도 CP-9(System Backup)와 CP-10(System Recovery and Reconstitution)이 별개 control이고, 백업의 신뢰성·무결성 시험은 CP-9(1)이라는 별도 확장 항목이다. ISO 22301:2019은 RTO와 RPO를 정의된 용어로 쓰고, 계획 문서를 만드는 데서 끝내지 말고 훈련 프로그램으로 검증하라고 요구한다.
세 문서가 전부 "받아두는 것"과 "되살리는 것"을 나눠 썼다. 리허설은 두 번째 칸을 채우는 유일한 방법이다.
백업 세트에서 실제로 빠지는 것들
화면 project는 돌아가는 시스템의 한 조각일 뿐이다. 복구가 막히는 지점은 대부분 project 바깥에 있다.
| 항목 | 빠지면 생기는 일 |
|---|---|
| Runtime deployment package | Editor project와 실행 파일이 다른 제품이 많다. Editor만 열리고 runtime은 못 만든다. |
| Tag database export | 부분 복구와 버전 비교가 불가능해진다. 전부 아니면 전무가 된다. |
| Alarm database와 rationalization 기록 | 우선순위, limit, delay, message가 통째로 사라진다. ISA-18.2가 말하는 master alarm database가 바로 이 기록이고, 재작성은 몇 주짜리 일이다. |
| Driver와 channel 설정 | PLC path, device ID, scan class, timeout. 손으로 복원하면 반드시 하나 틀린다. |
| Historian connector 설정 | Data source 이름, compression 설정, buffering 경로, credential. |
| OPC UA application certificate와 private key | 아래 별도 절에서 다룬다. 여기서 제일 많이 막힌다. |
| License file과 activation 메모 | 위의 새벽 2시가 여기서 나온다. |
| Script, report, SQL job, scheduled task | HMI editor 밖에 있어서 백업 대상에서 조용히 빠진다. |
| Network와 hostname 메모 | Hostname이나 service name에 묶인 제품이 있다. |
항목을 파일 확장자로 적어두면 새벽에 말이 안 엇갈린다. Ignition gateway backup은 .gwbk 하나다. TIA Portal project archive는 .zapNN이고 숫자가 Portal 버전을 따라간다(.zap17, .zap18). 이건 .apNN 파일이 들어 있는 작업 project 폴더와 다른 물건이다. 폴더를 그대로 압축해 두면 다음 Portal 버전이 migration을 거부할 수 있다. AVEVA System Platform(Wonderware) 객체 export는 .aaPKG다. Manifest에는 zip 안에 든 게 이 중 무엇인지까지 적는다. "project는 있다"와 "배포할 수 있는 게 있다"는 대개 다른 파일이고, 현장은 보통 한쪽만 갖고 있다.
VM 환경이면 guest 안의 파일만 담지 말고 VM 설정도 남긴다. vCPU 개수, NIC 순서와 MAC address, disk path, time sync 소스. NIC 순서 하나 바뀌어서 driver가 엉뚱한 subnet으로 나가는 걸 본 적 있다.
크기도 미리 세어 둔다. 내가 다루는 gateway backup은 40~120 MB, 같은 서버의 전체 시스템 image는 60 GB쯤이다. 1 Gb/s NAS 링크면 60 GB를 8분에 당긴다는 계산이 나오는데, 그 계산대로 끝난 적은 한 번도 없다. 이 한 줄이 30분일지 두 시간일지 모른 채로 RTO를 적으면 그 RTO는 처음부터 틀린 값이다.
만든 장비에서 복원하면 아무것도 못 찾는다
이게 리허설의 핵심이고, 대충 하는 현장이 제일 많이 건너뛰는 부분이다. 백업을 만든 그 장비에 되돌리면 당연히 잘 된다. 이미 깔린 driver, registry key, ODBC DSN, 예전 .NET runtime이 전부 남아 있으니까.
대상은 깨끗해야 한다. 예비 산업용 PC, 같은 OS 계열의 test VM, 벤더가 주는 recovery image, 공정망에서 분리된 lab workstation 중 아무거나. 시작은 clean OS에서 한다. SCADA software 설치 → patch 적용 → 백업 복원 순서로 가고, 사람이 손으로 한 단계를 전부 적는다.
복원이 "예전 장비에 우연히 남아 있던 무언가" 덕분에 됐다면 백업 세트는 미완성이다. 그 무언가는 새벽 2시의 예비 PC에는 없다.
인증서와 계정에서 제일 많이 막힌다
요즘 시스템이 복원 후 실패하는 이유는 파일이 아니라 identity다. Project는 멀쩡히 복원됐는데 hostname, certificate, service account SID가 전부 달라져 있다.
OPC UA에서는 이게 status code로 정확하게 나온다. 복원한 server가 새 hostname을 갖게 되면 client는 BadCertificateHostNameInvalid로 튕긴다. 인증서 SubjectAltName의 DNS 이름이 실제 접속한 endpoint host와 다르기 때문이다. ApplicationUri가 안 맞으면 BadCertificateUriInvalid, trust store가 안 따라왔으면 BadCertificateUntrusted가 뜬다. OPC UA Part 4의 인증서 검증 규칙은 이 셋을 각각 다른 실패로 취급한다. 로그에서 구분할 줄 알면 복구 시간이 시간 단위로 줄어든다.
세 개가 왜 다른지는 인증서 하나를 열어보면 바로 보인다. OPC UA application instance certificate의 SubjectAltName에는 항목이 두 종류 들어간다. hostname/IP가 들어가는 DNS·IP 항목, 그리고 ApplicationUri가 그대로 들어가는 URI 항목. 복원 대상의 이름이 바뀌면 앞엣것이 깨지고, 제품이 ApplicationUri를 hostname으로 조립하는 경우에는 뒤엣것까지 같이 깨진다. 그래서 hostname 하나 바꿨는데 status code가 두 개 나온다.
키 길이도 여기서 걸린다. Basic256Sha256 security policy는 RSA key를 2048 비트 이상(최대 4096 비트)으로 요구한다. 옛날 프로젝트에서 딸려온 1024 비트 인증서를 그대로 복원하면 hostname 검사까지 가지도 못하고 policy 단계에서 거절당한다. 복원 대상이 최신 client를 상대한다면 이건 재발급 아니면 답이 없다.
리허설에서 확인할 것:
- Application certificate와 private key가 실제로 어느 경로에 있는지. 파일이 아니라 OS key store 안에 있으면 파일 단위 백업은 개인키를 통째로 놓친다. 이 경우가 제일 조용히 실패한다.
- Trust store를 복사할 것인지 다시 신뢰 등록할 것인지. 양쪽 다 절차를 적어둔다.
- 인증서 SubjectAltName에 복원 대상 hostname/IP가 들어가는지. 안 들어가면 재발급이 필요하고, 재발급은 client 쪽 trust 작업까지 딸려온다.
- Driver, historian, web gateway를 각각 어느 service account가 돌리는지. 로컬 계정이면 OS를 다시 깔 때 SID가 새로 생긴다. 파일 ACL과 DCOM 권한이 옛 SID로 남아 있으면 계정 이름이 같아도 안 붙는다.
- License activation이 hardware, VM UUID, MAC address, dongle 중 무엇에 묶이는지. 주말에 옮길 수 있는 방식인지. 임시 license를 받을 수 있는지, 있다면 유효 기간이 며칠인지는 벤더마다 다르니 계약서에서 직접 확인해야 한다 — 여기에 일반적인 숫자는 없다.
Password를 일반 백업 압축 파일에 plain text로 넣지 않는다. Credential 회수는 현장에서 승인한 secret 관리 경로로 따로 둔다. 계정 쪽 절차는 실시간 데이터 끊지 않고 SCADA 서비스 계정 비밀번호 교체하기에 정리해 뒀고, 인증서 재발급 자체는 OPC UA 인증서 갱신 체크리스트, SCADA client를 안 끊고 MQTT broker 인증서 교체하기와 문제가 겹친다.
Editor가 열리는 건 복구가 아니다
복구 판정 기준은 runtime이 인터페이스를 통과하는 것이다. 그런데 "확인했다"는 판정이 아니다. 경로마다 통과 기준을 숫자로 못 박아 두면 리허설이 끝났는지 아닌지를 놓고 다툴 일이 없다.
| 확인 경로 | 합격 기준 |
|---|---|
| PLC/controller 통신 | 대표 tag 10개가 quality good이고 값이 현장(또는 simulator) 값과 일치 |
| OPC UA endpoint | client가 추가 신뢰 작업 없이 Basic256Sha256로 연결 |
| Alarm | 발생 → acknowledge → 이력 조회 3단계가 다 돌고, 권한 없는 role은 acknowledge가 거부됨 |
| Historian write path | test tag 1개를 10분 기록 후 조회, 샘플 누락 0 |
| Report | 지난달 report 1건이 DB에 붙어 끝까지 생성 |
| 로그인·권한 | 최소 2개 role로 로그인, 각 role의 금지 화면이 실제로 막힘 |
| 이중화 | 강제 절체 1회, 절체 중 tag 갱신 끊김이 미리 정한 한도 이내. 절차는 SCADA 이중화 서버 절체 시험 쪽과 같이 본다 |
| Time sync | 복원 노드와 historian 시계 차이 1초 이내, timezone 동일. 이게 틀리면 historian 데이터가 조용히 어긋난다 |
여기에 세는 단계를 하나 더 붙인다. Manifest에 백업 시점의 tag 개수, alarm 개수, 화면 개수를 숫자로 적어두고 복원 후 runtime에서 같은 숫자를 뽑아 비교한다. 부분 복원은 "editor가 열린다"는 검사에서 절대 안 걸린다. 개수 비교에서 걸린다. 압축 파일도 CRC가 통과했다는 것과 내용이 맞다는 것은 다른 얘기라, 백업 만들 때 SHA-256을 옆에 같이 적어두고 base software 설치를 시작하기 전에 대조한다. 1시간 40분짜리 설치를 끝내고 나서 archive가 잘렸다는 걸 알게 되면 그날 리허설은 거기서 끝이다.
안전은 별개 문제다. 복원한 test node가 live 장비에 command를 쓰게 두면 안 된다. Test network, simulator, read-only connection, 계획된 정비 시간 중 하나를 쓴다. 절차에 명시적으로 허용된 게 아니면 예외 없이 안 된다.
RPO는 서버가 아니라 항목마다 따로 정한다
"매주 일요일 full image" 하나로 끝내는 현장이 많은데, 그러면 이 시스템의 RPO는 7일이다. 문제는 SCADA project가 균일하게 변하지 않는다는 점이다. 시운전 두 주 동안은 alarm limit이 하루에 스무 번 바뀌고, 그 뒤 반년은 아무도 안 건드린다. 7일 RPO는 조용한 반년에는 과분하고 시운전 주에는 재앙이다.
그래서 항목을 나눠서 목표를 다르게 잡는 쪽이 싸게 먹힌다.
| 대상 | 백업 주기 | 보관 개수 | 실질 RPO |
|---|---|---|---|
| Project source, tag/alarm export | 변경할 때마다 (git commit) | 전체 이력 | 마지막 커밋 |
| Runtime deployment package | 배포할 때마다 | 최근 10회 | 마지막 배포 |
| 전체 시스템 image | 주 1회 + patch 직전·직후 | 주간 4 + 월간 12 | 7일 |
| 인증서·license·manifest | 바뀔 때 | 전체 이력 | 마지막 변경 |
보관 개수 칸을 비워 두면 백업은 대개 마지막 한 개만 남는다. 그 한 개에는 어제 잘못 넣은 설정까지 충실히 담겨 있다. 주간 4개면 한 달 전으로, 월간 12개면 1년 전으로 돌아갈 수 있고, 설정 오류를 두 주 뒤에 발견하는 일은 드물지 않다.
세 번째 줄만 있는 백업 정책은 첫 줄이 없다는 뜻이고, 첫 줄이 없으면 alarm 합리화 결과를 손으로 다시 만든다. 그 합리화 기록 자체를 어떻게 남기는지는 알람 합리화 워크숍 체크리스트 쪽이다. RTO 쪽은 아래처럼 재기 전까지는 값이 아니라 희망이다.
시간을 희망이 아니라 초시계로 적는다
리허설의 결과물은 "성공"이 아니라 구간별 실측 시간이다. 실제로 재 본 예시는 대략 이런 모양이 된다.
올바른 백업 찾기 0:25 (파일 5개 중 승인본 확인이 오래 걸림)
Base software + patch 1:40
File/configuration 복원 0:35
인증서, trust 재구성 1:10 ← 가장 크게 튀는 구간
License 재적용 0:50 (벤더 포털 대기 포함)
통신/alarm 검증 1:05
운전 인계 0:20
------------------------------
합계 6:05
이 현장의 승인된 목표는 RTO 8시간, RPO 24시간이었다. lab 실측이 6시간 5분이니 여유가 1시간 55분이다. 새벽 호출, 예비 하드웨어 수배, 벤더 포털 대기 중 하나만 어긋나면 그 여유는 없다. 이 표는 lab 값이고 현장 값의 하한이다. 전화 받으면서 하면 더 걸린다.
목표와 실측이 이만큼 붙어 있으면 둘 중 하나를 고쳐야 한다. 나는 목표를 늘리기 전에 base software 설치 1시간 40분부터 손댄다. patch까지 적용한 golden image를 미리 떠 두면 그 구간이 복원 몇 분으로 줄고, 한 번의 작업으로 가장 크게 떨어지는 구간이 거기다. 조용한 lab에서 6시간 걸렸는데 RTO를 1시간으로 적어 두는 쪽은 문서만 통과시키고 사고 때 그대로 터진다.
이름도 이 시간에 영향을 준다. final_new_2.zip은 위 표의 첫 줄을 25분으로 만드는 이름이다. Site, system, date, version, source가 파일명에서 읽혀야 한다.
WTP_SCADA-SRV1_RuntimeBackup_2026-06-19_app-4.8.2.zip
WTP_SCADA-SRV1_ProjectSource_2026-06-19_git-a1b2c3d.zip
WTP_OPCUA_TrustStore_2026-06-19.zip
백업 옆에는 plain text나 PDF로 짧은 manifest를 둔다. 포함 항목, 만든 사람, software version, license 상태, tag/alarm 개수, archive SHA-256, 마지막 복원 시험 결과와 날짜. SCADA engineering tool 없이 읽혀야 한다는 게 핵심이다. 복구하려는 사람에게는 아직 그 tool이 없다.
실제로 본 실패들
- Project source는 있는데 runtime deployment package가 없다.
- 운영 중 alarm database를 직접 고치고 export를 안 했다.
- OPC UA private key가 OS key store에 있어서 파일 백업에 안 담겼다. 인증서만 복원되고 개인키가 없다.
- Report가 예전 server에만 있던 ODBC DSN에 의존한다.
- Historian buffering 경로가 local disk였는데 새 장비에 안 만들었다.
- License activation을 주말에 옮길 방법이 없다.
- 백업이 죽은 그 disk 안에 같이 있었다.
- 백업 파일이 다섯 개인데 어느 게 승인본인지 아무도 모른다.
리허설 하나 끝냈으면 다음에 확인할 건 정해져 있다. 이번에 손으로 때운 단계가 뭐였는지 찾아서 그걸 백업 세트에 넣는 것. 손으로 때운 단계는 다음 사람이 모르는 단계다.