You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

11 KiB

배포 및 버전 관리 운영 절차서

이 문서는 NEW Web Configurator의 버전 관리 정책, 릴리즈 절차, 배포 워크플로우, 검증, 롤백, 디바이스 아키텍처, 트러블슈팅을 다룹니다. 신규 버전을 디바이스에서 실행하는 방법에 대한 단일 참조 문서입니다.


1. 개요

NEW Web Configurator는 여러 현장의 IoT 디바이스에 배포되는 Python stdlib HTTP 서버입니다. 배포는 scripts/deploy.ps1 — PowerShell 스크립트를 사용합니다. 이 스크립트는 커밋된 src/ 트리를 아카이브로 묶어 SSH로 전송하고, 디바이스 상의 기존 트리를 백업 및 교체한 뒤, 배포된 버전을 기록하고 systemd 서비스를 재시작합니다. 모든 프로덕션 배포는 반드시 vMAJOR.MINOR.PATCH 릴리즈 태그와 연결되어야 하므로, 어떤 디바이스에서든 실행 중인 정확한 버전을 언제나 식별할 수 있습니다.


2. 버전 관리 정책

2.1 버전 체계

세 부분으로 구성된 시맨틱 버전 관리: vMAJOR.MINOR.PATCH.

구분 올려야 하는 경우
MAJOR 하위 호환이 깨지는 변경 — 예: 기존 디바이스를 망가뜨리는 board_config 스키마 변경, 또는 API 삭제/이름 변경.
MINOR 새로운 하위 호환 기능 추가 — 예: 새 설정 페이지, 랜딩 대시보드, WiFi 국가 코드 드롭다운.
PATCH 신규 기능 없는 버그 수정 또는 소규모 수정 — 예: db_manager 재시도 수정, 표시 버그 수정.

2.2 단일 진실 소스(Single Source of Truth)

src/static/js/constants.jsAPP_VERSION이 애플리케이션 버전의 단일 진실 소스입니다. 사이드바에 표시되며, 릴리즈 시점에 git 태그와 반드시 일치해야 합니다.


3. 릴리즈 절차

새 릴리즈를 만들려면:

  1. 버전 올림 결정 — 변경 내용이 MAJOR, MINOR, PATCH 중 어느 것에 해당하는지 결정합니다(§2.1 표 참조).
  2. APP_VERSION 업데이트src/static/js/constants.js에서 APP_VERSION을 새 버전 문자열로 설정합니다(예: 'v1.0.1').
  3. CHANGELOG.md 항목 추가CHANGELOG.md 상단에 ## [vX.Y.Z] — YYYY-MM-DD 형식으로 변경 내용을 기술하는 섹션을 추가합니다.
  4. 커밋constants.jsCHANGELOG.md를 함께 커밋합니다.
  5. 태그 — annotated 태그를 생성합니다:
    git tag -a vX.Y.Z -m "Release vX.Y.Z — <one-line summary>"
    

태그는 릴리즈 시점에만 생성합니다. 개발 중간에 태그를 붙이지 마십시오. 태그는 배포되는 커밋을 정확히 표시합니다.


4. 배포

4.1 사전 요구사항

  • 다음이 갖춰진 Windows 개발 호스트:
    • OpenSSH sshscp (Windows 10/11 기본 포함 또는 Git for Windows).
    • PATH에 등록된 git.
    • root@<device>에 인증된 SSH 키.
  • 클린(clean) 상태의 로컬 워킹 트리 (git status에 변경 사항 없음).
  • vX.Y.Z 태그에 위치한 HEAD (플래그 처리된 개발 빌드에는 -AllowUntagged 사용).

4.2 사용법

.\scripts\deploy.ps1 <device-ip>
.\scripts\deploy.ps1 <device-ip> -AllowUntagged
.\scripts\deploy.ps1 <device-ip> -Rollback

예시:

.\scripts\deploy.ps1 192.168.55.56
.\scripts\deploy.ps1 192.168.55.56 -AllowUntagged

4.3 배포 흐름 (스크립트 동작 상세)

  1. 사전 점검(Pre-flight) — SSH 도달 가능 여부 확인(ssh … echo ok), 로컬 워킹 트리가 클린 상태인지 확인, HEAD가 버전 태그에 위치하는지 확인(버전 태그가 아니면 중단, -AllowUntagged가 전달된 경우 제외).
  2. 클린 아카이브 빌드git archive --output=<temp>.tar HEAD src를 실행하여 src/ 하위의 커밋된 파일만 캡처 — __pycache__, *.bak.*, 미추적 파일은 제외.
  3. 전송scp로 임시 tar 파일을 디바이스로 복사하고, 디바이스에서 스테이징 디렉터리에 압축 해제.
  4. 백업 — 현재 디바이스의 src/backups/src-<timestamp>/로 복사하고, 최근 3개 백업만 유지하도록 정리.
  5. systemd 유닛 및 헬퍼 스크립트 설치 (멱등성 보장 — 해시 비교 후 변경된 경우에만 재복사; src 교체 이전에 수행하므로 실패 시 기존 src가 활성 상태를 유지). 모든 유닛은 영구 경로 /lib/systemd/system/에 설치됩니다(/etc/systemd/system/ 오버레이는 tmpfs라 재부팅 시 초기화됩니다). 설치 항목:
    • web-configurator.service (메인 유닛; 첫 배포 시 systemctl enable도 수행).
    • dpworld-net-recover.service — 네트워크 적용 엔진이 사용하는 modprobe 전용 복구 유닛.
    • dpworld-network-apply.service.d/10-ondemand.conf — 펌웨어 적용 유닛의 부팅 전용 Requires를 온디맨드로 재정의하는 드롭인.
    • 펌웨어 부팅 하드닝(v1.7.1+): 하드닝된 부팅 스크립트 deploy/dpworld-network-apply-hardened.sh/usr/bin/으로 복사, 그리고 두 개의 드롭인(dpworld-network-apply.service.d/20-hardened.confdpworld-network-seed.service.d/20-hardened.conf)이 펌웨어 seed/apply 유닛을 하드닝된 스크립트로 연결(라이브 modprobe -r 없음 — 국가 코드는 재부팅 시 적용됨). 펌웨어 원본 /usr/bin/dpworld-network-apply.sh는 수정하지 않습니다. firmware-boot-hardening.md 참조.
    • Wi-Fi AP 유닛: deploy/dpworld-ap-apply.sh/usr/bin/으로 복사, 그리고 네 개의 유닛(dpworld-ap-seed, dpworld-ap-apply, dpworld-hostapd-ap0, dpworld-udhcpd-ap0). dpworld-ap-seed.service만 enable하며, 나머지는 apply 스크립트에서 온디맨드로 시작됩니다.
    • 엔진/OTA 디렉터리도 사전 생성합니다(/opt/fw_staging, /opt/fw_upload, /opt/config_backups, /home/root/network, /opt/config_backups/network).
  6. 교체(Swap) — 디바이스의 src/를 새로 압축 해제된 트리로 교체(rm -rf src && mv <staging>/src src); 스테이징 tar와 디렉터리를 삭제.
  7. 버전 기록DEPLOYED_VERSION을 기록하고 deploy-history.log에 한 줄을 추가합니다(§5 참조).
  8. 재시작systemctl restart web-configurator를 실행합니다.
  9. 검증 — 최대 약 15초 동안 폴링: systemctl is-active web-configuratoractive인지, :9090으로 HTTP 요청 시 200이 반환되는지 확인합니다. 성공 또는 실패를 보고합니다. 재시작/검증이 실패하면 스크립트가 4단계에서 생성한 backups/src-<timestamp>/로 자동 롤백합니다.

참고: HEAD가 정확히 vX.Y.Z 태그에 있지 않으면 배포가 거부됩니다. 미태그 커밋을 플래그 처리된 개발 빌드로 배포하려면 -AllowUntagged를 전달하십시오(버전은 vX.Y.Z-dev+<short-sha>로 기록됩니다).

이 스크립트는 nginx, Java app-runner, SQLite 데이터베이스, dpworldapp 바이너리를 절대 건드리지 않습니다. 단, 5단계에 나열된 systemd 유닛과 헬퍼 스크립트를 설치/갱신하며(멱등성 보장, 해시가 다를 경우에만), 펌웨어 소유의 /usr/bin/dpworld-network-apply.sh는 수정하지 않습니다 — 하드닝은 원본을 수정하지 않고 드롭인을 통해 적용됩니다.


5. 배포 검증

배포 성공 후, 디바이스에서 실행 중인 내용을 확인합니다:

ssh root@<ip> cat /usr/lib/web-configurator/DEPLOYED_VERSION

이 파일은 매 배포 시 덮어쓰이며, 다음 내용을 포함합니다:

version=v1.0.0
tag=v1.0.0
commit=<full-sha>
deployed_at=<ISO-8601 timestamp>
deployed_by=<user>@<host>

배포 이력을 확인하려면:

ssh root@<ip> cat /usr/lib/web-configurator/deploy-history.log

각 줄의 형식: <timestamp> <version> <commit-short> <deployed_by>.


6. 롤백

디바이스의 가장 최근 백업으로 복원하려면:

.\scripts\deploy.ps1 <ip> -Rollback

이 명령은 가장 최근의 backups/src-* 스냅샷을 src/에 복원하고, 서비스를 재시작하여 정상 여부를 검증한 뒤, 롤백이 발생했음을 기록하도록 DEPLOYED_VERSION을 다시 기록합니다(복원된 백업 타임스탬프 포함). 백업 스냅샷은 롤백 후에도 삭제되지 않습니다.


7. 디바이스 아키텍처

항목
앱 디렉터리 /usr/lib/web-configurator/
systemd 서비스 web-configurator.servicesrc/server.py 실행
Python 설정 서버 포트 9090
Nginx (리버스 프록시) 포트 80
Java app-runner (레거시·별개 프로그램) 포트 8080 — 이 앱(설정기)과 무관
디바이스 — 프로덕션(메인) 192.168.55.56 (유선 eth1)
디바이스 — 개발/검증(보조) 192.168.55.54
Python 런타임 Python 3.10
SSH 사용자 root

디바이스 주요 경로:

경로 용도
/usr/lib/web-configurator/src/ 실행 중인 애플리케이션 소스
/usr/lib/web-configurator/backups/ 디바이스 내 백업 스냅샷(최근 3개)
/usr/lib/web-configurator/DEPLOYED_VERSION 현재 버전 기록 파일
/usr/lib/web-configurator/deploy-history.log 배포별 감사 로그
/home/root/db/dynamic_data.db SQLite 설정 데이터베이스(board_config 테이블)
/opt/log/dpworldapp/ 애플리케이션 로그 디렉터리

8. 트러블슈팅

SSH 접속 불가

증상: Cannot reach <ip> over SSH.

조치:

  • 디바이스 전원이 켜져 있고 네트워크에 연결되어 있는지 확인합니다: ping <ip>.
  • SSH 키가 root@<ip>:~/.ssh/authorized_keys에 등록되어 있는지 확인합니다.
  • 수동으로 테스트합니다: ssh -o StrictHostKeyChecking=no root@<ip> echo ok.

배포 거부 — HEAD에 태그 없음

증상: HEAD is not at a version tag. Tag a release, or pass -AllowUntagged for a dev build.

조치: 커밋에 태그를 붙이거나(git tag -a vX.Y.Z -m "...") 다시 실행하거나, -AllowUntagged를 전달하여 플래그 처리된 개발 빌드를 배포합니다. 프로덕션 배포에 -AllowUntagged를 절대 사용하지 마십시오.

배포 거부 — 더티(dirty) 워킹 트리

증상: Local working tree is not clean.

조치: 배포 전에 모든 로컬 변경 사항을 커밋하거나 stash합니다. git status로 미처리 항목을 확인하십시오.

배포 후 서비스 비정상

증상: 스크립트가 Verification failed: web-configurator not healthy on <ip> after restart를 보고합니다.

조치:

ssh root@<ip> journalctl -u web-configurator -n 50 --no-pager

Python import 오류, 파일 누락, 포트 충돌 등을 확인합니다. 새 버전에 문제가 있으면 즉시 롤백합니다:

.\scripts\deploy.ps1 <ip> -Rollback

롤백 방법

§6을 참조합니다. 롤백 자체가 실패한 경우(백업 없음), 알려진 정상 상태의 .tar 파일에서 수동 복원하거나 이전 릴리즈 태그를 다시 배포합니다.