본문으로 건너뛰기

양산 라인 설치 (factory.sh)

생산 라인에서 박스 100 ~ 1000 대를 처리할 때 사용. 운영자가 한 화면 prompt 만 채우면 EDGE_ID / hostname / 자격증명 / Docker / cosign verify / 컨테이너 기동까지 자동. 박스당 30 초.

이 페이지는 양산 라인 SOP

단일 박스 / 현장 설치 (SI 직원) 라면 빠른 설치 (원라인) 페이지를 먼저 보세요. 양산 라인 vs 현장 설치 의 차이는 진입점 만 다르고, 결과는 동일합니다.


0. 한눈에

sudo -i
mkdir -p /home/kopens
curl -fsSL https://product.kopens.io/plantpulse-edge/plantpulse-edge-docker.tar.gz \
| tar xz -C /home/kopens
cd /home/kopens/plantpulse-edge-docker/bin
sudo bash factory.sh

factory.sh 가 자동으로:

  1. LAN1 NIC MAC 자동 감지 — MAC 마지막 6 자로 EDGE_<HEX6> 생성 (예: EDGE_3F8A21).
  2. 호스트명 동기hostnameedge-<hex6> (RFC 1123, 소문자+하이픈) 로 변경.
  3. 5 필드 prompt — SITE_ID / SERVER_HOST / SERVER_API_KEY / LAN1 NIC / LAN2 NIC.
  4. install.sh 위임 — Docker / cosign / random secret / app.properties / 컨테이너 기동.
  5. factory-label.txt 출력 — 박스 부착용. 인쇄 후 봉인.
  6. factory-inventory.csv append — 라인 끝에서 회수 → 사내 inventory DB.

1. ID / hostname 자동 생성

EDGE_ID — EDGE_<HEX6>

LAN1 NIC eth1 MAC = 00:1a:2b:3f:8a:21
─────
마지막 6 자 (HEX6) = 3F8A21
EDGE_ID = EDGE_3F8A21
hostname = edge-3f8a21

Why MAC?

  • hostname 은 OS install 마다 바뀔 수 있고 (cloud-init / hostnamectl), 충돌 가능
  • MAC 은 hardware 에 박혀있어 박스마다 unique 보장
  • 24-bit 마지막 6 자 → 약 1670 만 조합. 1000 대 양산에서 충돌 확률 ~0.003% (Birthday paradox 기준 안전)

도메인 규칙(제품 저장소의 CLAUDE.md) 강제:

식별자정규식예시
EDGE_ID^EDGE_[A-Z0-9_]{1,60}$EDGE_3F8A21
SITE_ID^SITE_[A-Z0-9_]{1,60}$SITE_FAB1_042
hostname (RFC 1123)소문자 + 하이픈edge-3f8a21

factory.sh 가 위 regex 검증 후 진행 — 통과 못하면 즉시 abort.

MAC 충돌 / NIC 없는 박스

  • 충돌 가능성/data1/factory-inventory.csv 에 이미 같은 EDGE_ID 있으면 즉시 abort (silent 중복 방지). 동일 박스를 두 번 provisioning 하거나 MAC 충돌 케이스 자동 검출.
  • MAC 없는 박스 (가상 NIC 등)derive_edge_id_from_mac 가 fail → install.sh 가 hostname 기반 fallback. 라인에서는 거의 안 일어남.

2. Prompt 5 필드

운영자가 라인에서 입력:

══════════════════════════════════════════════════════════════════════
PlantPulse Edge — Factory Provisioning
══════════════════════════════════════════════════════════════════════
Detected NIC eth1 MAC : 00:1a:2b:3f:8a:21
Auto EDGE_ID : EDGE_3F8A21
Auto hostname : edge-3f8a21
══════════════════════════════════════════════════════════════════════
Site ID (^SITE_[A-Z0-9_]+) [SITE_00001]: SITE_FAB1_042
Platform server host [192.168.0.41]: 10.10.0.5
Platform API key []: kFa7bGz8pQrN3xMv9wT2sLcEjY1uV6oH
LAN1 NIC (PLC) [eth1]: ← Enter = default
LAN2 NIC (WAN) [eth2]: ← Enter = default
══════════════════════════════════════════════════════════════════════
Summary
EDGE_ID : EDGE_3F8A21
hostname : edge-3f8a21
SITE_ID : SITE_FAB1_042
SERVER_HOST : 10.10.0.5
SERVER_API_KEY : kFa7…(32 chars)
LAN1 NIC (PLC) : eth1
LAN2 NIC (WAN) : eth2
══════════════════════════════════════════════════════════════════════
Proceed with install.sh? [y/N]

y 누르면 install.sh 가 5 ~ 10 분 (첫 이미지 pull 3 GB 포함) 동작 후 라벨 출력.


3. 자동 생성된 자격증명 (G2)

install.sh 가 박스마다 다른 random 비번 생성 (자세한 건 내려받은 plantpulse-edge-docker/README.md비밀번호 / API 키 (Secrets) 절):

변수길이위치
ADMIN_PASS (UI 로그인)random 14자+!/etc/kopens/credentials.txt
API_KEY (REST)random 16자위 파일 + env.sh.generated
MQTT_PASS (HiveMQ)random 14자+!위 파일
OPCUA_PASSrandom 14자+!위 파일
CERT_PASS (SSL keystore)random 14자+!위 파일

/etc/kopens/env.sh.generated (chmod 0600 root) 가 모든 후속 스크립트 (upgrade.sh / restart.sh) 가 source 하는 사이드카 — 재부팅 후에도 동일 자격증명 유지.


4. 이미지 서명 verification (G3)

install.sh 가 cosign 자동 설치 + cosign.pub 배치 + docker pull 직후 자동 verify.

[install] Installing cosign ...
[install] OK — cosign installed (cosign version 2.4.1)
[install] cosign.pub installed — image verification enabled
[install] docker pull — docker.kopens.io/pe/plantpulse-edge:2026-20260523
[install] OK — Pull complete
[cosign] OK — signature verified for docker.kopens.io/pe/plantpulse-edge:2026-20260523

cosign.pub 이 아직 placeholder (운영팀이 keypair 미생성) 면 WARN 만, 설치는 계속 진행. 내려받은 plantpulse-edge-docker/README.md이미지 서명 (Cosign) 절 참고.


5. 라벨 + 인벤토리

/etc/kopens/factory-label.txt — 박스 부착용

설치 끝나면 화면에 출력 + chmod 0600 파일 저장. 라인 직원이 라벨 프린터로 인쇄 후 박스 봉인:

══════════════════════════════════════════════════════════════════════
PlantPulse Edge — Factory Label
══════════════════════════════════════════════════════════════════════
EDGE_ID : EDGE_3F8A21
hostname : edge-3f8a21
SITE_ID : SITE_FAB1_042
build_date : 2026-05-19 14:25:33
──────────────────────────────────────────────────────────────────────
LAN1 (PLC) : eth1 MAC=00:1a:2b:3f:8a:21
LAN2 (WAN) : eth2 MAC=00:1a:2b:3f:8a:22
──────────────────────────────────────────────────────────────────────
UI admin : admin / aBcD1234EfGh!
REST API : kFa7bGz8pQrN3xMv9wT2sLcEjY1uV6oH
(full credentials → /etc/kopens/credentials.txt — root only)
══════════════════════════════════════════════════════════════════════

/data1/factory-inventory.csv — 라인 회수용

박스마다 한 줄 append. 라인 끝에서 운영자가 회수 → 사내 inventory DB 로 업로드:

edge_id,hostname,site_id,server_host,lan1_iface,lan1_mac,lan2_iface,lan2_mac,build_date,iso8601
EDGE_3F8A21,edge-3f8a21,SITE_FAB1_042,10.10.0.5,eth1,00:1a:2b:3f:8a:21,eth2,00:1a:2b:3f:8a:22,2026-05-19 14:25:33,2026-05-19T05:25:33Z
EDGE_5B7C03,edge-5b7c03,SITE_FAB1_043,10.10.0.5,eth1,00:1a:2b:5b:7c:03,eth2,00:1a:2b:5b:7c:04,2026-05-19 14:28:12,2026-05-19T05:28:12Z

6. 라인 끝 일괄 회수

# 라인 마지막 박스 처리 후 회수 워크스테이션에서
scp root@edge-3f8a21:/data1/factory-inventory.csv ./inventory-line1-$(date +%F).csv

# 회수한 CSV 들을 사내 inventory DB 로 업로드 (예: scripts/import-inventory.py)
python3 import-inventory.py inventory-line1-2026-05-19.csv

또는 박스가 켜진 시점에 platform 측으로 자동 push 하도록 hook 추가 가능 (Phase 1 — 사내 provisioning REST API 도입 시).


7. 자주 빠지는 함정

증상원인대응
EDGE_ID '...' fails ^EDGE_[A-Z0-9_]{1,60}$LAN1 NIC 의 MAC 가 ASCII 가 아닌 가상 NICLAN1_IFACE=enp1s0 bash factory.sh 처럼 명시
already_in_inventory: EDGE_3F8A21 already provisioned같은 박스 재실행 또는 MAC 충돌vi /data1/factory-inventory.csv 로 해당 줄 제거 후 재실행. MAC 충돌이면 라인 stop + 보고
cosign verify FAILED (COSIGN_REQUIRE=1)image 가 미서명 OR 잘못된 cosign.pub빌드 서버 sign-image.sh 호출 누락 확인, cosign.pub 갱신
hostnamectl set-hostname failedcontainer 안에서 실행 / 권한 없음factory.sh 는 root 강제 — sudo bash factory.sh
prompt 가 안 보임 (라벨 인쇄 안 됨)install.sh 가 mid-way 실패journalctl -u plantpulse-edge.service -n 50 확인 후 트러블슈팅

8. 관련 문서