업그레이드
컨테이너 모드 (2026.05+, 권장)
# 자동 (META-INF/VERSION.JSON 이 가리키는 빌드)
sudo bash /opt/kopens/install/upgrade.sh
# 특정 불변 태그 (YYYY.MM-YYYYMMDD-HHMM). 움직이는 태그는 거부합니다
sudo bash /opt/kopens/install/upgrade.sh --to 2026.11-20261103-1006
# 진행 중인 현재 업그레이드만 롤백 (trial · rollback_failed 상태에서만)
sudo bash /opt/kopens/install/upgrade.sh --rollback
# 사전 점검만 (0 최신, 10 새 빌드 있음)
sudo bash /opt/kopens/install/upgrade.sh --check
--rollback 은 진행 중인 업그레이드를 되돌릴 뿐, 이미 확정한 빌드에서 옛 빌드로 내려가지 않습니다. --to 로 옛 태그를 주어도 거부합니다(종료 코드 3).
레지스트리에 닿지 못하는 박스는 이미지 파일 하나로 업그레이드합니다 — 아래 폐쇄망 업그레이드.
수행 순서 (자동):
- 사전 점검 — 대상이 불변 태그인지, 지금 빌드보다 새로운지, 서명 도구 · 백업 도구 · 디스크 여유가 있는지 봅니다. 하나라도 어긋나면 아무것도 바꾸지 않고 멈춥니다
- 내려받기 —
docker pull <태그> - 검증 — cosign 서명과 이미지의 build_date 를 확인합니다. 서명을 검증하지 못하면 진행하지 않습니다. 검증된 이미지에 든 업그레이드 도구로 자신을 교체해 이어 갑니다
- 스냅샷 —
/data1/pp-backups/pe-backup-pre-upgrade-<ts>.tar.zst백업, 그 옆의 CQL 덤프(<아카이브>.cql/, 아래), Cassandra 스냅샷. 필수이며 건너뛸 수 없습니다. 하나라도 실패하면 아무것도 바꾸지 않고 멈춥니다(snapshot_failed, 종료 코드 4) - 전환 —
/etc/kopens/version.env를 검증된 다이제스트로 바꾸고 재시작 - 시험 기동 —
/api/health?verdict=upgrade&expect_build=<빌드일>이 200 을 줄 때까지 기다립니다. 기본 300초(HEALTH_TIMEOUT), 마이그레이션이 진행 중이면 최대 2400초(MIGRATION_TIMEOUT) - 확정 또는 자동 롤백 — 통과하면
committed. 실패하면 Cassandra 를 스냅샷으로 되돌리고 직전 이미지로 돌아갑니다(rolled_back). 그 복귀도 실패하면rollback_failed
시험 기동 중 전원이 끊기면 부팅 때 kopens-upgrade-resume.service 가 --resume 으로 다시 판정합니다. 확정 없이 부팅이 3회를 넘으면(OTA_MAX_TRIAL_BOOTS) 판정 없이 롤백합니다.
진행 상태: cat /etc/kopens/upgrade.status — JSON. 상태는 idle · downloading · verified · snapshot · switching · trial · committed · rolling_back · rolled_back · rollback_failed 입니다.
종료 코드: 0 확정 또는 이미 최신 · 10 --check 새 빌드 있음 · 3 옛 빌드로 내리는 요청 거부 · 4 아무것도 바꾸지 않음 · 5 내려받기 또는 검증 실패 · 6 롤백 완료 · 7 롤백 실패 · 75 다른 업그레이드가 진행 중.
UI / API 트리거: POST /api/v1/system/upgrade 가 /etc/kopens/upgrade.request 를 쓰고, 호스트의 kopens-upgrade.path 가 감지해 upgrade.sh --from-trigger 를 실행합니다.
자세한 가이드: 컨테이너 모드 운영 의 "OTA 업그레이드" 절.
업그레이드 전 CQL 덤프
사전 백업은 tar 아카이브와 Cassandra 스냅샷 말고 엔진과 무관한 사본을 하나 더 만듭니다 — 스키마는 CQL 텍스트(schema.cql), 행은 테이블마다 CSV. DB 엔진이 바뀌는 업그레이드에서 엔진 파일이 새 엔진에서 읽힌다는 보장이 없기 때문입니다.
- 위치: 아카이브 옆
<아카이브에서 .tar.zst 를 뗀 이름>.cql/. 아카이브와 함께 최신 3개만 남습니다 - 시계열 · 모니터링 표의 행은 담지 않습니다(스키마만 남아 빈 채로 돌아옵니다). 제외 목록은
BACKUP_CQL_EXCLUDE_TABLES - 덤프를 만들지 못하면 백업 실패이고, 업그레이드는 시작하지 않습니다. 덤프 없이 올려야 한다면
BACKUP_CQL=0을 명시합니다 — 경고가 남고, 그 아카이브는 같은 엔진에서만 복원됩니다
복원은 백업 및 복구 의 pe restore --cql.
폐쇄망 업그레이드
레지스트리 · 인터넷 없이 이미지 파일로 같은 업그레이드 절차(사전 점검 → 스냅샷 → 전환 → 시험 기동 → 확정 또는 롤백)를 탑니다.
sudo pe upgrade --bundle /media/usb/plantpulse-edge-2026.12.tar.zst
# 박스에 cosign 3 이 없으면 바이너리를 같이 넘깁니다
sudo pe upgrade --bundle <파일> --cosign /media/usb/cosign-linux-amd64
- 세 파일이 한 자리에 있어야 합니다:
<파일>,<파일>.sha256,<파일>.sha256.sig. 폐쇄망 번들의images/안에 있는 것 또는 배포처의plantpulse-edge-<YYYY.MM>.tar.zst를 씁니다 - 서명을 먼저 검증합니다. 박스에 있는
/etc/kopens/cosign.pub으로.sha256의 서명을 오프라인 검증하고, 파일의 sha256 이 서명된 값과 같을 때만docker load합니다. 서명 파일이 없거나 · 서명이 안 맞거나 · sha256 이 다르면 이미지를 불러오지 않습니다(COSIGN_REQUIRE와 무관) - cosign: 박스에 cosign 3 이 없으면
--cosign <파일>, 또는 파일 옆의cosign-linux-<arch>· 번들의tools/cosign-linux-amd64를 설치합니다. 고정 버전(3.1.3)의 체크섬과 다르면 거부합니다 - 더 새 빌드만 올립니다. 같은 빌드면
up_to_date, 옛 빌드면refused_downgrade --bundle은--to·--check와 함께 쓸 수 없습니다(종료 코드 2). 웹의 «SW 업그레이드» 버튼은 레지스트리 경로만 씁니다
이 기능이 없는 릴리즈에 설치된 upgrade.sh 는 --bundle 을 모릅니다. 처음 한 번은 번들에 든 새 스크립트로 직접 실행합니다.
sudo bash <번들>/repo/bin/upgrade.sh --bundle <번들>/images/<파일>
이미지 파일만 가져왔다면 저장소 tarball(plantpulse-edge-docker.tar.gz 와 .sig)을 함께 가져와 풀고 같은 방법으로 실행합니다. 그 뒤부터는 pe upgrade --bundle 로 됩니다.
출시 전 롤백 실증
새 릴리즈는 현장 박스에 알려지기 전에 스테이징 박스에서 일부러 실패시킨 업그레이드가 실제로 되돌아오는지 확인합니다. 마이그레이션이 실패하는 시험 이미지로 업그레이드해 자동 롤백을 일으키고, 직전 빌드 · 스키마 · 데이터가 그대로인지 검사합니다. 이 검사는 릴리즈 체인의 필수 단계라, 통과해야 VERSION.JSON 이 발행되고 박스가 그 릴리즈를 «최신» 으로 봅니다. 헬스 실패와 부팅 반복 시나리오는 업그레이드 도구가 바뀐 릴리즈에서 함께 돌립니다.
2. 사전 점검
업그레이드를 시작하기 전에:
# 1. 외부 망이 충분히 빠른지 — 회선이 느리면 download 만 수십 분
$PE_HOME/bin/network-speed-test.sh
# 2. 디스크 여유 (산출물 크기의 2–3배는 있어야)
df -h /opt /data1
# 3. 살아있는 백업이 있는지
ls -lt /data1/pp-backup | head -3
3. 업그레이드 결과 검증
$PE_HOME/bin/ps.sh # 모든 컴포넌트 살아있는지
curl -s http://127.0.0.1/api/v1/edge | jq # 버전/엣지 ID 새 값 확인
curl -s http://127.0.0.1/ui/opcua/tree | jq '.data.tree | length' # 사이트/태그 정상
$PE_HOME/bin/log-viewer.sh # 30초 정도 봐서 SEVERE 없는지
# (Ctrl+C)
외부 SCADA / Sparkplug 수신측에서도 정상 도착 여부를 함께 확인.
4. 자주 빠지는 함정
| 증상 | 원인 / 해결 |
|---|---|
download install.sh 에서 멈춤 | 외부 망 / product.kopens.io 점검. curl -v http://product.kopens.io/... 로 직접 확인 |
extracting 후 새 산출물이 깨져있음 | 다운로드 중 중단. 디스크 공간 + 다시 시도 |
| 업그레이드 후 일부 컴포넌트만 살아있음 | start.sh 의 단계별 실패 — log-viewer.sh 로 어디서 막혔는지 확인 |
cosign 3 or later is not installed and could not be installed (종료 코드 4) | 박스가 github.com 에 닿지 못해 cosign 을 받지 못함. 아무것도 바뀌지 않음. 망을 열거나 cosign 3.x 를 미리 설치한 뒤 다시 시도 |
| 업그레이드 후 과거 버전으로 롤백 | 백업으로부터 복구 → 백업 / 복구 |
5. firmware.sh — OS 패키지 업데이트
$PE_HOME/bin/firmware.sh
내부적으로:
dnf update -y
dnf clean all
분기 단위로 보안 패치를 적용할 때 사용. 커널 업데이트가 포함되면 reboot 이 필요 합니다.
$PE_HOME/bin/firmware.sh
needs-restarting -r # 새 커널이 깔렸는지 확인
# 결과가 "Reboot is required" 이면
$PE_HOME/bin/stop.sh
$PE_HOME/bin/reboot.sh
더 알아보기
- 업그레이드 통합 시나리오 (안전한 절차) → 운영 시나리오 — 신버전 배포
- 산출물은 GitLab 릴리즈 체인(
edge:libs→edge:deploy-dev→edge:image→edge:sign→edge:staging→verify:staging-evidence→verify:ota-rollback:image→verify:ota-rollback→edge:version)이 만든다. - 설치 / 배포 (개발자용): 설치 / 배포