운영 장비에서는 에이전트를 감시자(supervisor, 프로세스가 끝나면 다시 띄워 주는 프로그램) 아래에서 돌립니다. 에이전트는 스스로 다시 실행하지 않습니다. 그래서 서버의 재시작 명령(종료 코드 3)이나 충돌, 정전 뒤에는 누군가 다시 띄워 줘야 합니다. systemd 나 Docker 중 하나를 고릅니다.

어느 쪽이든 파일은 둘입니다.

파일내용권한
/etc/geo-mlops/edge.yaml현장 구성(장비 이름, 수집기, 보존 한도)읽기 가능
/etc/geo-mlops/edge.env비밀. GEO_EDGE_*=값 을 한 줄에 하나씩. 최소 GEO_EDGE_CENTRAL__TOKEN600, 소유자만

systemd

  1. 전용 계정과 가상환경을 만들고 설치합니다.

    sudo useradd --system --home /var/lib/geo-mlops-edge --shell /usr/sbin/nologin geo-mlops
    sudo python3 -m venv /opt/geo-mlops/venv
    sudo /opt/geo-mlops/venv/bin/pip install 'geo-mlops-sdk[edge,gpu,modbus]>=0.2,<0.3'
  2. 설정 파일 두 개를 둡니다.

    sudo mkdir -p /etc/geo-mlops
    sudo cp edge.yaml /etc/geo-mlops/edge.yaml
    echo 'GEO_EDGE_CENTRAL__TOKEN=<device-token>' | sudo tee /etc/geo-mlops/edge.env >/dev/null
    sudo chmod 600 /etc/geo-mlops/edge.env
  3. 아래 유닛을 /etc/systemd/system/geo-mlops-edge.service 로 저장하고 켭니다.

    sudo systemctl daemon-reload
    sudo systemctl enable --now geo-mlops-edge
    journalctl -u geo-mlops-edge -f
# /etc/systemd/system/geo-mlops-edge.service
[Unit]
Description=Geo-MLOps edge agent
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=geo-mlops
Group=geo-mlops

# 비밀은 여기서. 토큰을 바꿀 때 고치는 곳도 이 파일이다
EnvironmentFile=-/etc/geo-mlops/edge.env
Environment=GEO_EDGE_CONFIG=/etc/geo-mlops/edge.yaml

ExecStart=/opt/geo-mlops/venv/bin/geo-mlops-edge run
WorkingDirectory=/var/lib/geo-mlops-edge

# 0(정지 요청)·3(재시작 요청)·충돌 모두 다시 띄운다
Restart=always
RestartSec=2

# SIGTERM 이면 보내던 청크를 끝내고 큐를 정리한 뒤 끝난다(에이전트 자체 유예 20초)
KillSignal=SIGTERM
TimeoutStopSec=45

# 에이전트가 쓰는 곳은 한 군데뿐
StateDirectory=geo-mlops-edge
ReadWritePaths=/var/lib/geo-mlops-edge
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
NoNewPrivileges=yes

[Install]
WantedBy=multi-user.target
  • ProtectSystem=strict 이므로 에이전트는 /var/lib/geo-mlops-edge 에만 씁니다. watchdir 수집기가 다른 폴더(예: /data/incoming)의 파일을 옮겨야 한다면 ReadWritePaths= 에 그 폴더를 더합니다.
  • /opt/geo-mlops/venv 가 아닌 곳에 설치했다면 ExecStart 를 바꿉니다.

Docker

이미지는 필요한 엑스트라만 넣어 만듭니다. 모델 엑스트라는 PyTorch 까지 설치해 1 GB 가까이 되므로, 데이터만 보내는 장비는 edge,modbus 로 충분합니다.

# Dockerfile
FROM python:3.12-slim

ARG EXTRAS=edge
ENV PYTHONUNBUFFERED=1 \
    PIP_NO_CACHE_DIR=1 \
    GEO_EDGE_DATA_DIR=/var/lib/geo-mlops-edge

RUN pip install --upgrade pip \
 && pip install "geo-mlops-sdk[${EXTRAS}]>=0.2,<0.3"

# 큐와 모델 캐시는 컨테이너보다 오래 살아야 한다
VOLUME ["/var/lib/geo-mlops-edge"]
RUN useradd --system --create-home --home-dir /var/lib/geo-mlops-edge geo-mlops \
 && chown -R geo-mlops:geo-mlops /var/lib/geo-mlops-edge
USER geo-mlops

EXPOSE 8600
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
    CMD python -c "import httpx,sys; sys.exit(0 if httpx.get('http://127.0.0.1:8600/health', timeout=3).status_code==200 else 1)"

ENTRYPOINT ["geo-mlops-edge"]
CMD ["run"]
docker build --build-arg EXTRAS='edge,modbus' -t geo-mlops-edge .
docker build --build-arg EXTRAS='edge,gpu,yolo' -t geo-mlops-edge:yolo .

docker compose

# docker-compose.yml
name: geo-mlops-edge

services:
  edge:
    build:
      context: .
      args:
        EXTRAS: ${EDGE_EXTRAS:-edge,modbus}
    image: geo-mlops-edge:latest
    container_name: geo-mlops-edge
    restart: unless-stopped
    env_file:
      - path: /etc/geo-mlops/edge.env
        required: false
    volumes:
      - edge-state:/var/lib/geo-mlops-edge          # 큐·모델 캐시
      - /etc/geo-mlops:/etc/geo-mlops:ro            # edge.yaml 을 기본 경로에서 읽는다
      - ${EDGE_INCOMING:-/data/incoming}:/data/incoming   # watchdir 수집기용
    ports:
      - "${EDGE_API_PORT:-8600}:8600"

volumes:
  edge-state:
    name: geo-mlops-edge-state
docker compose up -d --build     # 빌드 + (재)시작
docker compose logs -f edge
docker compose down              # 정지 (큐와 모델 캐시는 남는다)
docker compose down -v           # 정지 + 상태 볼륨까지 삭제. 보내지 못한 데이터도 사라진다
  • 컨테이너 안에서는 /etc/geo-mlops/edge.yaml 이 기본 탐색 경로이므로 따로 넘길 것이 없습니다.
  • edge.yamlapi.host0.0.0.0 이어야 포트 매핑으로 접근됩니다.
  • edge.env 에 있는 GEO_EDGE_* 키를 compose 의 environment:다시 적지 마세요. compose 는 environment:env_file 보다 우선하므로, 토큰을 교체해도 옛 값이 조용히 쓰입니다.
  • 두 파일 모두 없어도 컨테이너는 기본값으로 뜹니다. 그때는 status 가 보낼 곳이 없다는 것을 알려 줍니다.

디바이스 토큰 교체

토큰은 1년 동안 유효하고, 교체는 현장에서 해야 하는 작업입니다. 장비에 새 토큰을 원격으로 넣을 방법은 없습니다.

  1. 중앙에서 디바이스 상세 토큰 관리 카드의 ① 회전 을 누릅니다. 새 토큰이 한 번만 표시되니 복사합니다. (② 회수 는 모든 토큰을 무효로 만들 뿐 새로 발급하지 않습니다.)

    토큰 관리 카드: ① 회전(새 토큰 발급) ② 회수(모든 토큰 무효화)
  2. 그 순간부터 장비의 다음 호출은 401 로 거절되고, 에이전트는 링크를 auth_failed 로 두고 스스로 멈춥니다. 어차피 거절될 요청을 계속 보내 서버에 부담을 주지 않으려는 것입니다. 명령 폴링도 함께 멈추므로 재시작 명령도 닿지 않습니다.

  3. 장비에서 /etc/geo-mlops/edge.envGEO_EDGE_CENTRAL__TOKEN 을 새 값으로 바꾸고 서비스를 다시 시작합니다.

    sudo systemctl restart geo-mlops-edge        # 또는: docker compose up -d

그동안에도 수집은 계속됩니다. 데이터는 로컬 큐에 쌓였다가, 새 토큰으로 연결되면 올라갑니다.

플릿 API 는 토큰 남은 날수(token_expires_in_days)를 알려 줍니다. 갑자기 만료되지 않도록, 만료 전 현장 방문 일정에 토큰 교체를 넣어 두세요.

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

© Geo-MLOps