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.
169 lines
18 KiB
169 lines
18 KiB
|
1 month ago
|
<!DOCTYPE html>
|
||
|
|
<html lang="ko">
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
|
|
<title>Web Configurator — deploy/ 통합 가이드 (v1.12.1)</title>
|
||
|
|
<style>
|
||
|
|
:root { --fg:#1c2128; --muted:#57606a; --line:#d0d7de; --bg:#fff; --accent:#0969da;
|
||
|
|
--callout-bg:#f6f8fa; --callout-bd:#0969da; --warn-bg:#fff8e6; --warn-bd:#bf8700; --code-bg:#eff1f3; }
|
||
|
|
* { box-sizing:border-box; }
|
||
|
|
body { margin:0; background:#f4f5f7; color:var(--fg);
|
||
|
|
font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","Malgun Gothic","Apple SD Gothic Neo",sans-serif;
|
||
|
|
line-height:1.6; font-size:15px; }
|
||
|
|
.wrap { max-width:980px; margin:0 auto; padding:40px 28px 80px; background:var(--bg);
|
||
|
|
min-height:100vh; box-shadow:0 0 0 1px var(--line); }
|
||
|
|
h1 { font-size:26px; margin:0 0 6px; }
|
||
|
|
h2 { font-size:20px; margin:34px 0 12px; padding-top:14px; border-top:1px solid var(--line); }
|
||
|
|
h3 { font-size:16px; margin:22px 0 8px; }
|
||
|
|
p { margin:10px 0; }
|
||
|
|
.meta { color:var(--muted); font-size:13.5px; margin:0 0 8px; padding:10px 14px;
|
||
|
|
background:var(--callout-bg); border-radius:6px; }
|
||
|
|
table { border-collapse:collapse; width:100%; margin:12px 0; font-size:14px; }
|
||
|
|
th, td { border:1px solid var(--line); padding:8px 10px; text-align:left; vertical-align:top; }
|
||
|
|
th { background:var(--callout-bg); font-weight:600; }
|
||
|
|
code { background:var(--code-bg); padding:1px 5px; border-radius:4px; font-size:13px;
|
||
|
|
font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace; }
|
||
|
|
blockquote { margin:12px 0; padding:10px 16px; background:var(--callout-bg);
|
||
|
|
border-left:4px solid var(--callout-bd); border-radius:0 6px 6px 0; color:#24292f; }
|
||
|
|
blockquote.warn { background:var(--warn-bg); border-left-color:var(--warn-bd); }
|
||
|
|
ul, ol { margin:10px 0; padding-left:24px; }
|
||
|
|
li { margin:5px 0; }
|
||
|
|
.footer { margin-top:40px; padding-top:16px; border-top:1px solid var(--line);
|
||
|
|
color:var(--muted); font-size:13px; }
|
||
|
|
strong { font-weight:700; }
|
||
|
|
</style>
|
||
|
|
</head>
|
||
|
|
<body>
|
||
|
|
<div class="wrap">
|
||
|
|
|
||
|
|
<h1>Web Configurator — <code>deploy/</code> 통합 가이드 <span style="color:var(--muted);font-size:18px;">(협력사 전달용)</span></h1>
|
||
|
|
<div class="meta">
|
||
|
|
<strong>목적</strong>: <code>deploy/</code> 폴더의 구성요소 중 <strong>꼭 필요한 것</strong>과 그 <strong>기능</strong>을 정리하고, 기존 <code>dpworldapp</code>/펌웨어와의 <strong>"충돌" 오해</strong>를 해소합니다.<br>
|
||
|
|
<strong>대상 버전</strong>: v1.12.1 · <strong>검증</strong>: <code>deploy/</code> 통합 의미론은 v1.11.15 기준 소스 코드 + 실디바이스(192.168.55.54, 펌웨어 원본 상태) 대조 완료. v1.12.0 설치 경로 이전(<code>/usr/lib</code>)과 v1.12.1 <code>nginx.conf</code> 제거를 반영했습니다.
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<h2>0. 한눈에 — 핵심 3가지</h2>
|
||
|
|
<ol>
|
||
|
|
<li><strong>웹 설정기 앱 자체는 네트워크/AP 파일 없이도 단독으로 동작합니다</strong> (<code>:9090</code>). 앱은 <code>src/</code>(파이썬) + <code>web-configurator.service</code> 두 가지만 있으면 기동·설정 저장이 됩니다.</li>
|
||
|
|
<li><strong>"충돌"의 실체는 단 3개 파일</strong>(네트워크 드롭인)이 <em>펌웨어가 소유한 유닛을 덮어쓰는 것</em>뿐입니다. 그 외 모든 파일은 <strong>새로 추가(additive)</strong>되는 것이라 기존 자원을 전혀 건드리지 않습니다 — 크래시·에러를 내지 않습니다.</li>
|
||
|
|
<li>따라서 <strong>원하는 기능만 골라 설치</strong>하면 충돌 없이 통합됩니다. (§3에 3가지 통합 방식)</li>
|
||
|
|
</ol>
|
||
|
|
<blockquote>
|
||
|
|
<strong>경로 표준 (v1.12.0~, 협력사 협의 반영)</strong>: 앱 코드는 <strong><code>/usr/lib/web-configurator</code></strong> 에 설치합니다 — FHS상 <code>/usr/lib</code> 가 read-only program code 자리이며, BSP가 이미지에 굽는 read-only rootfs에 적합합니다. 쓰기 런타임 데이터(로그·펌웨어 staging/upload·설정 백업)는 <code>/usr/lib</code>(read-only)가 아니라 <strong>쓰기 가능한 영속 파티션(<code>/opt</code>, <code>/home/root</code>)</strong> 에 둡니다(§4).<br><br>
|
||
|
|
<strong>신규 텔레메트리 Uplink (v1.12.1)</strong> 는 런타임에 OS 호스트 라우트(/32)만 조작하므로 <strong><code>deploy/</code> 추가 구성요소가 필요 없습니다</strong> — 새 유닛/스크립트 없이 기존 <code>web-configurator.service</code>(root 권한) 만으로 동작합니다.
|
||
|
|
</blockquote>
|
||
|
|
|
||
|
|
<h2>1. 기능 그룹과 필요한 파일</h2>
|
||
|
|
<p><code>deploy/</code>의 구성요소는 <strong>3개 기능 그룹</strong>으로 나뉩니다. 그룹 단위로 켜고 끌 수 있습니다.</p>
|
||
|
|
|
||
|
|
<h3>A. 웹 설정기 (필수 — 항상 설치)</h3>
|
||
|
|
<table>
|
||
|
|
<thead><tr><th>파일</th><th>기능</th><th>설치 위치</th></tr></thead>
|
||
|
|
<tbody>
|
||
|
|
<tr><td>(앱 본체 <code>src/</code>)</td><td>파이썬 stdlib HTTP 서버. 웹 UI + 설정 API. <code>:9090</code> 리슨</td><td><code>/usr/lib/web-configurator/src</code> <em>(표준 — read-only rootfs 가능; 쓰기 데이터는 §4)</em></td></tr>
|
||
|
|
<tr><td><code>web-configurator.service</code></td><td>위 앱을 부팅 자동기동 + 크래시 시 재시작하는 systemd 유닛</td><td><code>${systemd_system_unitdir}</code></td></tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
<p>이 그룹만으로 웹 설정기가 동작합니다(설정은 DB <code>/home/root/db</code>에 저장). <strong><code>dpworldapp</code>과 충돌하지 않습니다.</strong></p>
|
||
|
|
|
||
|
|
<h3>B. 네트워크 즉시(라이브) 적용 — 선택</h3>
|
||
|
|
<blockquote>
|
||
|
|
<strong>왜 필요한가</strong>: <code>dpworldapp</code>은 <strong>재기동 시에만</strong> 네트워크/설정을 OS에 반영합니다. 이 그룹은 웹 UI에서 IP·Wi-Fi(SSID/비밀번호)를 바꾸면 <strong>재부팅·dpworldapp 재기동 없이 즉시</strong> 반영하기 위한 <strong>보완</strong> 구성입니다.<br><br>
|
||
|
|
<strong>동작 메커니즘</strong>: 웹이 렌더 파일을 <code>/home/root/network/</code>에 기록 → 적용 스크립트가 그것을 <code>/run/systemd/network/</code>로 동기화 + <code>networkctl</code>/<code>wpa_cli</code> 재구성. (※ <code>networkctl</code>은 <code>/home/root/network</code>를 직접 읽지 않으므로 이 <strong>동기화 스크립트가 있어야</strong> 라이브 적용이 됩니다.)
|
||
|
|
</blockquote>
|
||
|
|
<table>
|
||
|
|
<thead><tr><th>파일</th><th>기능</th><th>설치 위치</th></tr></thead>
|
||
|
|
<tbody>
|
||
|
|
<tr><td><code>dpworld-network-apply-hardened.sh</code></td><td>네트워크 적용 스크립트. <strong>펌웨어 원본의 안전 버전</strong> — Wi-Fi country 변경 시 <code>modprobe -r wlan</code>(QCA6490 워치독 리부팅 루프 유발)을 제거하고 country를 <strong>재부팅 시 적용(reboot-deferred)</strong>으로 처리</td><td><code>/usr/bin/</code> (0755)</td></tr>
|
||
|
|
<tr><td><code>dpworld-net-recover.service</code> <em>(옵션)</em></td><td>wlan 모듈이 완전히 내려갔을 때 복구(<code>modprobe</code> + <code>wpa</code> 재시작). 웹/워치독이 on-demand로 호출</td><td><code>${systemd_system_unitdir}</code></td></tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
<blockquote>위 스크립트를 <strong>어떻게</strong> 펌웨어 적용 경로에 연결할지(드롭인 vs 전용 유닛 vs 미사용)는 <strong>§3</strong>에서 선택합니다. 드롭인 3종은 §3 옵션 ③에서만 사용합니다.</blockquote>
|
||
|
|
|
||
|
|
<h3>C. Wi-Fi AP 모드 — 선택</h3>
|
||
|
|
<blockquote><strong>기능</strong>: 장치를 Wi-Fi AP로 띄워 작업자가 휴대폰/노트북으로 직접 접속(현장 provisioning). <code>wlan0</code>(STA)은 건드리지 않고 <strong><code>ap0</code> 가상 인터페이스</strong>를 추가해 사용. hostapd + udhcpd + <strong>ap0 전용 방화벽</strong>(INPUT 전체 개방·WPA2 PSK가 접근 게이트 / FORWARD 차단 = AP 클라이언트의 내부망·PLC(eth1) 경유 차단).</blockquote>
|
||
|
|
<table>
|
||
|
|
<thead><tr><th>파일</th><th>기능</th><th>설치 위치</th></tr></thead>
|
||
|
|
<tbody>
|
||
|
|
<tr><td><code>dpworld-ap-apply.sh</code></td><td>AP 기동/해제 스크립트(ap0 생성·IP·방화벽·hostapd/udhcpd 시작)</td><td><code>/usr/bin/</code> (0755)</td></tr>
|
||
|
|
<tr><td><code>dpworld-ap-apply.service</code></td><td>웹이 on-demand로 호출하는 AP 적용 유닛 (<code>[Install]</code> 없음 = 부팅 자동기동 안 함)</td><td><code>${systemd_system_unitdir}</code></td></tr>
|
||
|
|
<tr><td><code>dpworld-hostapd-ap0.service</code></td><td><code>ap0</code>에서 hostapd 실행 (<code>[Install]</code> 없음, ap-apply.sh가 기동)</td><td><code>${systemd_system_unitdir}</code></td></tr>
|
||
|
|
<tr><td><code>dpworld-udhcpd-ap0.service</code></td><td><code>ap0</code>에서 DHCP 서버 실행 (<code>[Install]</code> 없음, ap-apply.sh가 기동)</td><td><code>${systemd_system_unitdir}</code></td></tr>
|
||
|
|
<tr><td><code>dpworld-ap-seed.service</code> <em>(옵션)</em></td><td>재부팅 후 AP를 자동 재기동(복구). <strong>없으면 재부팅 후 AP 수동 재활성 필요</strong></td><td><code>${systemd_system_unitdir}</code></td></tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
<blockquote>AP 그룹은 <strong>전적으로 신규 구성</strong>입니다. <code>dpworldapp</code>/펌웨어 자원을 건드리지 않으며, <code>ap0</code> 전용이라 STA·eth·<code>dpworldapp</code>과 충돌하지 않습니다.</blockquote>
|
||
|
|
|
||
|
|
<h2>2. 제외 / 참고</h2>
|
||
|
|
<table>
|
||
|
|
<thead><tr><th>파일</th><th>사유</th></tr></thead>
|
||
|
|
<tbody>
|
||
|
|
<tr><td><code>deploy/nginx.conf</code></td><td><strong>v1.12.1에서 패키지에서 제거됨</strong>. 앱은 <code>:9090</code>에서 직접 서비스되므로 reverse proxy 없이 동작합니다. 협력사가 자체 nginx로 <code>:80</code> 프런트할 경우, 협력사 설정에서 <code>proxy_pass http://127.0.0.1:9090/</code>만 잡으면 됩니다(앱 포트는 <code>:9090</code> — 레거시 <code>:8080</code> Java app-runner 아님).</td></tr>
|
||
|
|
<tr><td><code>scripts/board_trace*.sh</code></td><td>(<code>deploy/</code> 밖이지만 참고) board_config를 폴링해 <code>/tmp</code>에 기록하는 <strong>개발 진단용</strong> 스크립트 — 운영 배포물에서 제거 권장</td></tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
|
||
|
|
<h2>3. "충돌" 해소 — 네트워크 적용 통합 3가지 방식</h2>
|
||
|
|
<p><strong>"충돌"은 §1-B의 라이브 적용을 <em>펌웨어 유닛을 빌려서</em> 수행하기 때문에 발생합니다.</strong> 웹은 적용을 <code>dpworld-network-apply.service</code>(펌웨어 소유 유닛) 이름으로 호출하므로, 그 유닛이 우리 스크립트를 실행하게 하려면 유닛을 건드려야 합니다. 아래 3가지 중 선택하세요.</p>
|
||
|
|
<table>
|
||
|
|
<thead><tr><th>방식</th><th>설치 파일</th><th>펌웨어 유닛 접촉</th><th>라이브 적용</th><th>비고</th></tr></thead>
|
||
|
|
<tbody>
|
||
|
|
<tr><td><strong>① 저장 전용</strong></td><td>네트워크 파일 <strong>0개</strong></td><td><strong>없음(무접촉)</strong></td><td>✗ (dpworldapp 재기동 시 반영)</td><td>웹은 설정 편집·저장만. 충돌 0. 가장 단순</td></tr>
|
||
|
|
<tr><td><strong>② 전용 유닛 (권장)</strong></td><td><code>dpworld-network-apply-hardened.sh</code> + 신규 <strong>웹 전용 유닛</strong></td><td><strong>없음(무접촉)</strong></td><td>✓</td><td>웹이 펌웨어 유닛 대신 <strong>자체 유닛</strong>으로 적용. <strong>웹 소규모 코드 변경 필요</strong>(적용 유닛명을 <code>NET_APPLY_SERVICE</code> env로 분리 — 현재는 <code>dpworld-network-apply.service</code>로 하드코딩)</td></tr>
|
||
|
|
<tr><td><strong>③ 드롭인 override (현재 패키지)</strong></td><td><code>dpworld-network-apply-hardened.sh</code> + 드롭인 3종</td><td><strong>있음(override)</strong></td><td>✓</td><td>펌웨어 유닛의 ExecStart/의존성을 우리 것으로 교체. 이것이 협력사가 본 "충돌"</td></tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
<p><strong>드롭인 3종(③에서만 사용):</strong></p>
|
||
|
|
<ul>
|
||
|
|
<li><code>dpworld-network-apply.service.d/20-hardened.conf</code> — 펌웨어 <code>dpworld-network-apply.service</code>의 <code>ExecStart</code>를 hardened.sh로 교체</li>
|
||
|
|
<li><code>dpworld-network-seed.service.d/20-hardened.conf</code> — 펌웨어 <code>dpworld-network-seed.service</code>(부팅 seed)를 <code>hardened.sh --boot</code>로 교체</li>
|
||
|
|
<li><code>dpworld-network-apply-ondemand.conf</code> — 위 유닛의 <code>Requires=</code>(boot-only seed 의존)를 비워 on-demand 기동 허용</li>
|
||
|
|
</ul>
|
||
|
|
<blockquote><strong>권장</strong>: 펌웨어 베이킹(BSP)에서는 하드닝본을 <code>/usr/bin/dpworld-network-apply.sh</code> <strong>원본 이름으로 직접 교체</strong>하는 방식이 1차입니다(드롭인 미설치 — <code>BSP-INTEGRATION.md</code> §7 ①). 펌웨어 유닛을 못 건드리는 <strong>라이브 디바이스</strong>에서만 드롭인 override(③)를 fallback으로 씁니다. 라이브 적용이 불필요하면 <strong>① 저장 전용</strong>. (③/직접교체 시 §1-B 동작 차이 — Wi-Fi country는 재부팅 시 적용 — 만 합의하면 됨).</blockquote>
|
||
|
|
|
||
|
|
<h2>4. 설치 시 주의 (부분배포 사고 방지)</h2>
|
||
|
|
<ul>
|
||
|
|
<li><strong>드롭인(③)을 설치하면 <code>dpworld-network-apply-hardened.sh</code>도 반드시 함께</strong> 설치하세요. 드롭인만 있고 스크립트가 없으면 ExecStart가 없는 파일을 가리켜 <strong>펌웨어 네트워크 유닛이 EXEC 실패</strong>합니다(멀쩡하던 적용이 깨짐).</li>
|
||
|
|
<li><strong><code>web-configurator.service</code>를 설치하면 앱 본체 <code>src/</code>도 함께</strong> 설치하세요. 앱이 없으면 서비스가 재시작 루프에 빠집니다.</li>
|
||
|
|
<li><strong>쓰기 가능 경로</strong>: 앱 코드는 read-only rootfs(<code>/usr/lib/web-configurator</code>)에 두지만, 아래 런타임 경로는 <strong>쓰기 가능한 영속 파티션</strong>(<code>/opt</code>, <code>/home/root</code>)에 있어야 합니다.
|
||
|
|
<ul>
|
||
|
|
<li>DB <code>/home/root/db</code> · 네트워크 렌더 <code>/home/root/network</code> <em>(dpworldapp과 공유 — 그대로)</em></li>
|
||
|
|
<li>로그 <code>/opt/log/dpworldapp</code>(유닛 <code>LOG_DIR</code>) · 펌웨어 staging/upload <code>/opt/fw_staging</code>·<code>/opt/fw_upload</code> · 설정 백업 <code>/opt/config_backups</code> <em>(유닛 <code>ReadWritePaths</code> 참조)</em></li>
|
||
|
|
<li>hardened.sh 상태/로그 <code>STATE_DIR=/opt/dpworld-network</code> <em>(펌웨어 원본엔 없던 신규 경로 — <code>/opt</code>가 늦게 마운트되면 로그/reboot 마커만 유실, 적용 자체는 진행). <code>/opt</code> 마운트 보장(<code>RequiresMountsFor=/opt</code>)을 권장</em></li>
|
||
|
|
</ul>
|
||
|
|
</li>
|
||
|
|
</ul>
|
||
|
|
|
||
|
|
<h2>5. 전체 파일 분류표 (요약)</h2>
|
||
|
|
<table>
|
||
|
|
<thead><tr><th><code>deploy/</code> 파일</th><th>그룹</th><th>필요성</th><th>펌웨어 유닛 접촉</th></tr></thead>
|
||
|
|
<tbody>
|
||
|
|
<tr><td><code>web-configurator.service</code></td><td>A 코어</td><td><strong>필수</strong></td><td>무접촉(신규 유닛)</td></tr>
|
||
|
|
<tr><td><code>dpworld-network-apply-hardened.sh</code></td><td>B 네트워크</td><td>라이브 적용 시 필요</td><td>무접촉(파일)</td></tr>
|
||
|
|
<tr><td><code>dpworld-network-apply.service.d/20-hardened.conf</code></td><td>B 네트워크</td><td><strong>③에서만</strong></td><td>★ override</td></tr>
|
||
|
|
<tr><td><code>dpworld-network-seed.service.d/20-hardened.conf</code></td><td>B 네트워크</td><td><strong>③에서만</strong></td><td>★ override</td></tr>
|
||
|
|
<tr><td><code>dpworld-network-apply-ondemand.conf</code></td><td>B 네트워크</td><td><strong>③에서만</strong></td><td>★ override</td></tr>
|
||
|
|
<tr><td><code>dpworld-net-recover.service</code></td><td>B 네트워크</td><td>옵션(복구 안전망)</td><td>무접촉(신규 유닛)</td></tr>
|
||
|
|
<tr><td><code>dpworld-ap-apply.sh</code></td><td>C AP</td><td>AP 시 필수</td><td>무접촉(파일)</td></tr>
|
||
|
|
<tr><td><code>dpworld-ap-apply.service</code></td><td>C AP</td><td>AP 시 필수</td><td>무접촉(신규 유닛)</td></tr>
|
||
|
|
<tr><td><code>dpworld-hostapd-ap0.service</code></td><td>C AP</td><td>AP 시 필수</td><td>무접촉(신규 유닛)</td></tr>
|
||
|
|
<tr><td><code>dpworld-udhcpd-ap0.service</code></td><td>C AP</td><td>AP 시 필수</td><td>무접촉(신규 유닛)</td></tr>
|
||
|
|
<tr><td><code>dpworld-ap-seed.service</code></td><td>C AP</td><td>옵션(재부팅 후 AP 자동복구)</td><td>무접촉(신규 유닛)</td></tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
<blockquote>★ 표시(드롭인 3종)만이 펌웨어 소유 유닛을 건드립니다 = "충돌"의 전부. 이 3개를 빼면 펌웨어/<code>dpworldapp</code> 자원은 <strong>0개</strong> 건드리지 않습니다.<br><em><code>nginx.conf</code>는 v1.12.1에서 패키지에서 제거됨(§2) — deploy/ 구성요소 아님.</em></blockquote>
|
||
|
|
|
||
|
|
<h2>부록: dpworldapp과의 공유·격리 요약</h2>
|
||
|
|
<ul>
|
||
|
|
<li><strong>공유(의도된 연동)</strong>: DB <code>/home/root/db</code>, 네트워크 렌더 <code>/home/root/network</code> — 웹과 <code>dpworldapp</code>이 동일 파일 계약(byte 호환)을 공유. 웹이 편집·저장하고 <code>dpworldapp</code>이 소비하는 구조(충돌 아님).</li>
|
||
|
|
<li><strong>격리(신규, 무접촉)</strong>: 웹 앱(<code>:9090</code>), AP(<code>ap0</code> 전용 + 방화벽), 복구 유닛, 텔레메트리 Uplink(OS 호스트 라우트만) — 전부 신규 자원.</li>
|
||
|
|
<li><strong>유일한 접점</strong>: <code>dpworld-network-apply.service</code>/<code>-seed.service</code>(펌웨어 소유) — 라이브 적용을 위해 ③ 방식에서만 override. ① 또는 ②를 택하면 이 접점도 사라집니다.</li>
|
||
|
|
</ul>
|
||
|
|
|
||
|
|
<div class="footer">Web Configurator v1.12.1 · <code>deploy/</code> 통합 가이드 · 앱 코드 <code>/usr/lib/web-configurator</code> 표준 · 소스 + 실디바이스(.54) 검증본(통합 의미론)</div>
|
||
|
|
|
||
|
|
</div>
|
||
|
|
</body>
|
||
|
|
</html>
|