운영 장비에서는 에이전트를 감시자(supervisor, 프로세스가 종료되면 재시작하는 프로그램) 아래에서 실행. 에이전트는 스스로 재실행하지 않음. 서버의 재시작 명령(종료 코드 3), 충돌, 정전 뒤에는 감시자의 재시작 필요. systemd 나 Docker 중 하나 선택.

어느 방식이든 두 가지 설정 파일 사용.

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

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=MLOps Central Platform 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 을 받으면 전송을 중단하고, 처리 중인 큐 항목을 대기 상태로 되돌리고, DB 를 닫은 뒤 종료한다(에이전트 자체 유예 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.yaml 의 api.host 는 0.0.0.0 이어야 포트 매핑으로 접근 가능.
  • edge.env 에 있는 GEO_EDGE_* 키를 compose 의 environment: 에 중복 작성 금지. compose 는 environment: 를 env_file 보다 우선하므로 토큰을 교체해도 이전 값이 경고 없이 사용됨.
  • 두 파일 모두 없어도 컨테이너는 기본값으로 기동. 이때 status 의 link.state 는 계속 offline, GET /api/v1/settings 의 central.configured 는 false.

디바이스 토큰 교체

토큰 유효 기간은 1년, 교체는 현장에서 수행해야 하는 작업. 장비에 새 토큰을 원격으로 전달하는 방법은 없음.

  1. 중앙에서 디바이스 상세 토큰 관리 카드의 ① 회전 클릭. 새 토큰이 한 번만 표시되므로 복사. (② 회수 는 모든 토큰을 무효화할 뿐 새로 발급하지 않음.)

    토큰 관리 카드: ① 회전(새 토큰 발급) ② 회수(모든 토큰 무효화)
  2. 그 순간부터 장비의 다음 호출은 401 로 거부, 에이전트는 링크를 auth_failed 로 전환하고 중앙 호출을 스스로 중단. 거부될 요청으로 서버에 부담을 주지 않기 위한 동작. 명령 폴링도 함께 중단되므로 재시작 명령도 전달되지 않음.

  3. 장비에서 /etc/geo-mlops/edge.env 의 GEO_EDGE_CENTRAL__TOKEN 을 새 값으로 변경하고 서비스 재시작.

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

그동안에도 수집은 계속. 데이터는 로컬 큐에 누적되었다가 새 토큰으로 연결되면 업로드.

토큰 만료까지 남은 일수는 플릿 API(GET /api/v1/edge/fleet, GET /api/v1/edge/devices/{id})의 token_expires_in_days 로 확인. 화면에는 표시되지 않음. 만료 전 현장 방문 일정에 토큰 교체 포함 필요.

2026-10-02 기준 플랫폼으로 작성

© MLOps Central Platform