명령

중앙은 공장 NAT 뒤의 장비에 먼저 접속할 수 없으므로, 장비가 commands_poll_s(기본 25초) 동안 기다리는 롱폴로 명령을 가져갑니다. 장비는 처리한 결과를 반드시 보고(ack)하므로, 화면에서 명령이 대기 중 → 전달됨 → 완료/실패 로 바뀌는 것을 볼 수 있습니다. 명령을 보내려면 운영OPERATOR · ADMIN 권한이 필요합니다.

  1. 디바이스 상세의 명령 탭을 엽니다. ① 명령 종류를 고르고 ② 명령 보내기 를 누릅니다.
    명령 탭: ① 명령 종류 ② 명령 보내기 ③ 실패한 명령은 장비가 보고한 오류와 함께 남는다
  2. 장비가 다음 폴링에서 가져가 처리하고, 결과가 결과 열에 남습니다. ③ 실패한 명령은 장비가 보고한 오류가 그대로 보입니다(이 예에서는 아티팩트가 없는 모델 버전을 받으려다 404).
명령화면 이름장비가 하는 일결과 예
ping아무것도 바꾸지 않고 답함{"pong": true, "device_id": "edge-bench-01"}
restart재시작에이전트를 종료 코드 3 으로 끝냄. 감시자가 다시 띄움{"restarting": true}
resync재동기화failed 가 된 큐 항목을 다시 대기 상태로 돌리고 바로 전송{"requeued": 0}
pull_model모델 내려받기인자 {name, version, activate} 의 모델을 받아 활성화{"name": …, "activated": true}
set_policy(정책 저장 시 자동)새 정책을 적용{"applied": true}

명령은 기본 24시간 안에 가져가지 않으면 만료 됩니다. 에이전트가 도는 동안 같은 명령이 다시 전달돼도 한 번만 실행합니다.

재시작은 종료 코드 3

에이전트는 스스로 다시 실행하지 않습니다. 문제가 있다고 본 프로세스가 스스로를 고치게 두지 않으려는 것입니다. restart 를 받으면 하던 전송을 정리하고 종료 코드 3 으로 끝나며, systemd(Restart=always)나 컨테이너 런타임(restart: unless-stopped)이 깨끗한 새 프로세스를 띄웁니다. 디바이스 상세 오른쪽 위 재시작 버튼도 같은 명령을 보냅니다.

실제로 확인한 결과:

WARNING geo_mlops_sdk.edge.runtime: restart requested: command 1a1d2559-…
$ echo $?
3
종료 코드
0요청받아 정상 종료(SIGTERM·Ctrl-C)
3재시작 요청. 감시자가 다시 띄워야 함
4로컬 API 를 열지 못함(포트 사용 중 등). 다시 띄워도 소용없는 설정 문제

Offline 차단

디바이스 상세의 Offline 버튼은 명령이 아니라 서버 쪽 스위치입니다. 켜 두는 동안 그 장비의 토큰 호출은 모두 503(Retry-After: 60)으로 거절됩니다. 장비 입장에서는 중앙이 잠시 사라진 것과 같습니다. 보내지 못한 데이터를 큐에 그대로 두고 기다리다가, 차단을 풀면 쌓인 데이터를 올려 보냅니다. 토큰 폐기와 달리 화면에서 바로 되돌릴 수 있습니다.

정책

정책은 하트비트 주기, 명령 폴링 주기, 보존 한도, 전송 조절(retention·sync)을 중앙에서 장비별로 정하는 것입니다. 바꾸려면 설정ADMIN 권한이 필요합니다.

  1. 디바이스 상세의 정책 탭을 엽니다. ① 현재 리비전이 보입니다. 값을 고치고 ② 정책 저장 을 누릅니다.
    정책 탭: ① 현재 리비전 ② 정책 저장 (저장하면 리비전이 오르고 장비가 다음 하트비트에서 가져간다)
  2. 저장하면 리비전이 1 오르고, 즉시 반영되도록 set_policy 명령도 함께 큐에 들어갑니다. 장비는 하트비트 응답의 policy_revision 이 바뀐 것을 보고도 새 정책을 가져갑니다.
화면 항목설정 키
하트비트 주기 (초)heartbeat_interval_s
보존 기간 (일)retention.max_age_days
보존 용량 (GiB)retention.max_bytes
전송 속도 상한 (B/s)sync.max_bytes_per_s
CPU 일시정지 임계 (%)sync.cpu_pause_percent
전송 허용 시간대sync.windows

하트비트 주기는 서버의 오프라인 판정 기준(기본 180초)보다 충분히 짧아야 합니다. 길면 정상 장비가 비정상 과 정상을 오갑니다.

보존: 끊겨도 버리지 않는다

엣지 장비는 케이블이 뽑히고 전원이 나가고 며칠씩 오프라인인 것이 정상입니다. 에이전트는 이 전제로 움직입니다.

  1. 수집은 멈추지 않습니다. 수집기는 링크 상태와 상관없이 로컬 큐(SQLite + 파일 스풀)에 씁니다.
  2. 한도 안에서 쌓습니다. retention 의 세 한도(max_bytes 50 GiB, max_age_days 30일, free_disk_min_bytes 5 GiB) 중 먼저 걸리는 것을 지키며, 넘치면 우선순위가 낮고 오래된 것부터 버립니다. 버린 건수는 하트비트의 evicted_24h 로 올라가 플릿 목록 대기(적체 건수) 열에 빨간 −N 으로 보입니다. 보내는 속도보다 쌓는 속도가 빠르다는 신호입니다.
  3. 링크가 돌아오면 바로 보냅니다. 연속 두 번 연결 확인(link.online_after_ok)이 되면 등록 → 즉시 하트비트 → 큐 비우기 순으로 움직입니다. 보낼 때는 우선순위가 높은 것부터입니다.
  4. 두 번 저장되지 않습니다. 레코드 id 가 멱등 키(같은 것을 여러 번 보내도 한 번만 저장되게 하는 키)라서, 응답을 못 받고 같은 묶음을 다시 보내도 중앙은 duplicates 로 세고 한 번만 저장합니다. 파일은 청크 단위로 이어 올립니다.

실제로 CPU 가 바쁜 장비에서 cpu_pause_percent(기본 85) 때문에 전송이 멈춰 레코드 7건이 쌓였다가, 한도를 풀자 곧바로 모두 올라간 것을 확인했습니다.

"sync":    { "state": "paused", "last_error": "paused: cpu above threshold" },
"backlog": { "count": 7, "bytes": 1150, "by_kind": { "blob": 1, "http": 3, "robot": 3 } }

전송을 조절하는 값은 설정 레퍼런스sync 표에 있습니다. 실패(failed) 상태가 된 항목은 geo-mlops-edge queue --state failed 로 확인하고, 재동기화 명령이나 POST /api/v1/sync:retry-failed 로 다시 보냅니다.

2026-09-21 기준 플랫폼에 맞춰 작성했습니다.

© Geo-MLOps