- Update edge README with local dev instructions - Update edge main.go and tests - Update deploy-dev documentation - Archive completed task files
580 lines
22 KiB
Markdown
580 lines
22 KiB
Markdown
# Dev Field Deployment
|
|
|
|
이 문서는 dev 필드 테스트 기준의 배포 단위를 정리한다. Control Plane과 Web은 compose 서비스로 묶고, Edge와 Node는 호스트에서 직접 실행하는 단일 바이너리로 배포한다.
|
|
|
|
## 배포 단위
|
|
|
|
```text
|
|
control host
|
|
docker compose
|
|
- postgres
|
|
- redis
|
|
- control-plane
|
|
- web
|
|
|
|
edge host
|
|
iop-edge binary
|
|
edge.yaml
|
|
|
|
node host
|
|
iop-node binary
|
|
bootstrap-managed runtime state
|
|
|
|
oto build host
|
|
oto binary
|
|
agent.yaml
|
|
```
|
|
|
|
Edge와 Node는 Docker 이미지로 만들지 않는다. Jenkins는 repo의 shell entrypoint를 호출해 Edge/Node 바이너리 산출물을 만든다.
|
|
OTO는 별도 프로젝트의 단일 바이너리 산출물로 두고, IOP Edge는 장기적으로 OTO agent를 설치/등록시키는 bootstrap provider 역할을 가진다.
|
|
Agent 설치가 어렵거나 일회성 유지보수 대상인 host/device는 별도 배포 단위로 보지 않고, 대상에 접근 가능한 Node의 terminal profile을 통해 Edge가 remote terminal bridge session을 중계하는 방향으로 둔다.
|
|
|
|
## Edge/Node 바이너리 빌드
|
|
|
|
기본 빌드는 현재 호스트의 `GOOS/GOARCH` 대상으로 `edge`, `node`를 모두 만든다.
|
|
|
|
```bash
|
|
bin/build/field-binaries.sh
|
|
```
|
|
|
|
Jenkins에서 Linux amd64/arm64를 함께 만들 때는 다음처럼 호출한다.
|
|
|
|
```bash
|
|
TARGETS="linux/amd64 linux/arm64" \
|
|
VERSION="${BUILD_TAG}" \
|
|
COMMIT="${GIT_COMMIT}" \
|
|
bin/build/field-binaries.sh
|
|
```
|
|
|
|
산출물은 `dist/field/<version>/<goos>-<goarch>/` 아래에 생성된다.
|
|
|
|
```text
|
|
iop-edge
|
|
iop-edge.sha256
|
|
iop-node
|
|
iop-node.sha256
|
|
SHA256SUMS
|
|
manifest.env
|
|
```
|
|
|
|
## Control Plane/Web Compose
|
|
|
|
루트의 `.env.example`을 참고해 필요한 값만 `.env`로 복사해 조정한다.
|
|
|
|
```bash
|
|
cp .env.example .env
|
|
docker compose up --build -d
|
|
```
|
|
|
|
서비스 포트 기본값은 다음과 같다.
|
|
|
|
| Service | Port | Purpose |
|
|
|---|---:|---|
|
|
| web | 3000 | Web Portal |
|
|
| control-plane | 9080 | HTTP health/bootstrap |
|
|
| control-plane wire | 19080 | IOP wire endpoint |
|
|
| postgres | 5432 | Control Plane DB |
|
|
| redis | 6379 | Control Plane cache/queue 후보 |
|
|
|
|
현재 Control Plane 구현은 DB/Redis를 아직 사용하지 않지만, dev compose에는 먼저 포함해 이후 schema, queue, audit, event 처리 작업을 같은 배포 단위 안에서 진행할 수 있게 둔다.
|
|
로컬에서 Control Plane 바이너리를 직접 실행하는 테스트는 외부 `code-server-postgres`의 `iop-control-plane-local` DB와 외부 `code-server-redis`의 Redis DB 1, `iop:control-plane:local:` key prefix를 사용한다. dev compose 환경은 Postgres `iop-control-plane-dev` DB와 Redis DB 2, `iop:control-plane:dev:` key prefix를 사용한다. compose의 `control-plane` 서비스는 `IOP_DATABASE_URL`, `IOP_REDIS_URL`, `IOP_REDIS_KEY_PREFIX` 환경변수로 dev 값을 지정해 `configs/control-plane.yaml`의 로컬 기본값을 덮어쓴다.
|
|
|
|
헬스체크는 다음 명령으로 확인한다.
|
|
|
|
```bash
|
|
docker compose ps
|
|
curl -fsS http://localhost:${IOP_CONTROL_PLANE_HTTP_PORT:-9080}/healthz
|
|
curl -fsS http://localhost:${IOP_CONTROL_PLANE_HTTP_PORT:-9080}/readyz
|
|
```
|
|
|
|
## Edge 실행
|
|
|
|
Jenkins 산출물의 `iop-edge`를 edge host에 배포한 뒤, 초기에는 dev 환경의 `edge.yaml`을 지정해 직접 실행한다.
|
|
|
|
```bash
|
|
./iop-edge serve --config /etc/iop/edge.yaml
|
|
```
|
|
|
|
콘솔 기반 현장 smoke가 필요하면 다음처럼 실행한다.
|
|
|
|
```bash
|
|
./iop-edge console --config /etc/iop/edge.yaml
|
|
```
|
|
|
|
dev 초기 단계에서는 `iop-edge config init` 명령을 실행해 생성한 bundle-local `edge.yaml`을 기준으로 포트와 node token을 맞추며, 이 bundle-local `edge.yaml`이 개발/테스트의 primary source of truth다. repo의 `configs/edge.yaml`은 직접 편집하거나 참조하는 공식 기본 경로가 아니며 단지 로컬 개발 환경용 fallback 또는 예시 템플릿일 뿐이다. 이후 Control Plane enrollment/config sync가 붙으면 edge 설정 파일은 bootstrap 정보만 남기는 방향으로 줄인다.
|
|
|
|
## Node 실행
|
|
|
|
사용자/field 기본 경로에서 Node는 직접 설정 파일을 만들거나 `iop-node serve --config ...`로 실행하지 않는다.
|
|
Edge가 제시한 bootstrap 명령을 대상 host에서 실행하면 다운로드, 검증, 연결, 실행까지 이어져야 한다.
|
|
|
|
## Field 테스트 환경 라우팅
|
|
|
|
field 테스트의 1차 기준 환경은 `ssh toki@toki-labs.com`의 code-server 컨테이너다.
|
|
컨테이너 compose 원본은 원격 `~/docker/services/code-server/compose/docker-compose.yml`이며, 재기동 타이밍은 운영자가 정한다.
|
|
이 저장소의 작업자는 compose를 수정할 수 있지만, 사용자가 명시하지 않으면 code-server 컨테이너를 재시작하지 않는다.
|
|
|
|
| Host port | Container port | Purpose |
|
|
|---:|---:|---|
|
|
| `13000-13099` | `3000-3099` | web/dev app preview range |
|
|
| `18080` | `18080` | IOP field artifact/bootstrap HTTP |
|
|
| `18081` | `8080` | Edge OpenAI-compatible HTTP |
|
|
| `19080` | `19080` | Portal/Control Plane wire test endpoint |
|
|
| `19090` | `9090` | Edge-Node TCP transport |
|
|
| `19092` | `9092` | Edge metrics |
|
|
| `19190` | `9190` | OTO/specialized agent field transport reserve |
|
|
|
|
field bootstrap UX는 Jenkins agent 연결처럼 Edge가 command를 제시하고 대상 host가 실행하는 형태를 목표로 한다.
|
|
현재 field 기준 artifact/bootstrap 외부 주소는 `http://toki-labs.com:18080`이며, Edge-Node 런타임 주소는 `toki-labs.com:19090`이다.
|
|
초기 field 단계의 Node bootstrap은 다음 요구를 만족해야 한다.
|
|
|
|
- Edge 또는 field artifact server가 `dist/field/<version>/<goos>-<goarch>/iop-node`를 정해진 URL로 노출한다.
|
|
- Apple Silicon Mac은 `darwin/arm64` 산출물을 사용한다. `linux/arm64`는 Mac 실행 대상이 아니다.
|
|
- 사용자가 실행하는 명령은 한 줄 수준으로 유지하고, 내부에서 다운로드, 실행 권한 부여, 필요한 내부 상태 준비, `iop-node` 실행까지 이어진다.
|
|
- 사용자가 `node.yaml`을 만들거나 확인하거나 편집하는 흐름은 기본 경로에 두지 않는다.
|
|
- 직접 포트가 아직 적용되지 않았거나 외부망에서 막히면 SSH tunnel 또는 reverse tunnel은 fallback으로만 사용한다.
|
|
|
|
Apple Silicon Node용 field artifact는 다음처럼 만든다. Node token은 artifact에 굽지 않고 사용자 명령의 첫 번째 인자로 전달한다.
|
|
|
|
```bash
|
|
VERSION=<field-version> \
|
|
APPS=node \
|
|
TARGETS="darwin/arm64" \
|
|
ARTIFACT_BASE_URL="http://toki-labs.com:18080" \
|
|
BOOTSTRAP_EDGE_ADDR="toki-labs.com:19090" \
|
|
DIST_DIR=dist/field \
|
|
./bin/build/field-binaries.sh
|
|
```
|
|
|
|
산출물 layout과 artifact URL 규칙은 다음을 기준으로 한다.
|
|
|
|
```text
|
|
dist/field/<field-version>/darwin-arm64/iop-node
|
|
dist/field/<field-version>/darwin-arm64/SHA256SUMS
|
|
dist/field/<field-version>/bootstrap/node-darwin-arm64.sh
|
|
|
|
http://toki-labs.com:18080/darwin-arm64/iop-node
|
|
http://toki-labs.com:18080/darwin-arm64/SHA256SUMS
|
|
http://toki-labs.com:18080/bootstrap/node-darwin-arm64.sh
|
|
```
|
|
|
|
정적 artifact/bootstrap HTTP는 version 디렉터리를 document root로 열어 둔다.
|
|
|
|
```bash
|
|
python3 -m http.server 18080 --bind 0.0.0.0 -d dist/field/<field-version>
|
|
```
|
|
|
|
작업자는 artifact URL을 완성한 뒤 사용자가 token 값 하나만 바꾸는 한 줄 명령을 전달한다.
|
|
사용자에게 전달하는 명령에는 `<artifact-base-url>` placeholder를 남기지 않는다.
|
|
|
|
```bash
|
|
curl -fsSL http://toki-labs.com:18080/bootstrap/node-darwin-arm64.sh | bash -s CHANGE_ME_FIELD_TOKEN
|
|
```
|
|
|
|
bootstrap 스크립트는 `~/iop-field`를 만들고, `darwin-arm64/iop-node`와 `SHA256SUMS`를 받아 checksum을 확인한 뒤 필요한 내부 상태를 준비하고 `iop-node`를 실행한다.
|
|
내부 구현이 임시 설정 파일을 쓰더라도 사용자에게 그 파일을 만들거나 관리하게 하지 않는다.
|
|
첫 번째 인자 `CHANGE_ME_FIELD_TOKEN`은 Edge config의 `nodes[].token`과 같은 값이어야 한다.
|
|
|
|
## Field Bootstrap 작업자 테스트 Runbook
|
|
|
|
이 절차는 LLM/작업자가 사용자 직접 테스트를 준비하고 실패 지점을 분리할 때 기준으로 삼는다.
|
|
사용자에게 전달할 짧은 가이드는 `docs/field-bootstrap-user-test.md`를 사용한다.
|
|
다음 작업 재개와 포트 의미 정리는 `docs/field-bootstrap-work-guide.md`를 먼저 확인한다.
|
|
토큰과 version 값은 테스트 시점에 맞춰 바꾸고, 기본 `configs/*.yaml`은 수정하지 않는다.
|
|
|
|
### 1. code-server 포트 확인
|
|
|
|
원격 host에서 code-server 컨테이너의 외부 포트가 실제로 열렸는지 먼저 확인한다.
|
|
|
|
```bash
|
|
ssh toki@toki-labs.com '/usr/local/bin/docker ps --format "{{.Names}} {{.Ports}}" | grep "^code-server "'
|
|
```
|
|
|
|
기대 포트:
|
|
|
|
```text
|
|
18080 artifact/bootstrap HTTP
|
|
18081 Edge OpenAI-compatible HTTP
|
|
19090 Edge-Node TCP transport
|
|
19092 Edge metrics
|
|
19190 OTO/specialized agent reserve
|
|
```
|
|
|
|
`docker ps`에 포트가 보이지 않으면 compose 파일은 갱신됐지만 컨테이너 recreate가 아직 안 된 상태일 수 있다.
|
|
컨테이너 recreate 타이밍은 운영자가 정한다.
|
|
|
|
### 2. Edge field config 작성
|
|
|
|
edge host에서 임시 설정 파일을 만든다.
|
|
Apple Silicon Mac에서 Ollama를 실행하고 Node도 같은 Mac에서 실행할 경우, Edge가 Node에 내려줄 Ollama base URL은 Node 관점의 `http://127.0.0.1:11434`다.
|
|
|
|
```bash
|
|
cat > /tmp/iop-edge-field-bootstrap.yaml <<'YAML'
|
|
edge:
|
|
id: "edge-field-bootstrap"
|
|
name: "Field Bootstrap Edge"
|
|
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
|
|
openai:
|
|
enabled: true
|
|
listen: "0.0.0.0:8080"
|
|
node: "node-silicon-ollama"
|
|
adapter: "ollama"
|
|
target: ""
|
|
models:
|
|
- "gemma4:26b"
|
|
session_id: "openai-field"
|
|
timeout_sec: 300
|
|
strict_output: true
|
|
strict_stream_buffer: false
|
|
|
|
metrics:
|
|
port: 9092
|
|
|
|
nodes:
|
|
- id: "node-silicon-ollama"
|
|
alias: "silicon-ollama"
|
|
token: "CHANGE_ME_FIELD_TOKEN"
|
|
adapters:
|
|
ollama:
|
|
enabled: true
|
|
base_url: "http://127.0.0.1:11434"
|
|
context_size: 262144
|
|
YAML
|
|
```
|
|
|
|
config가 로드되는지 확인한다.
|
|
|
|
```bash
|
|
go run ./apps/edge/cmd/edge config check --config /tmp/iop-edge-field-bootstrap.yaml
|
|
```
|
|
|
|
### 3. Apple Silicon artifact 생성
|
|
|
|
repo root에서 Apple Silicon Node artifact와 bootstrap script를 만든다.
|
|
|
|
```bash
|
|
VERSION=field-bootstrap-manual \
|
|
APPS=node \
|
|
TARGETS="darwin/arm64" \
|
|
ARTIFACT_BASE_URL="http://toki-labs.com:18080" \
|
|
BOOTSTRAP_EDGE_ADDR="toki-labs.com:19090" \
|
|
DIST_DIR=dist/field \
|
|
./bin/build/field-binaries.sh
|
|
```
|
|
|
|
기대 파일:
|
|
|
|
```bash
|
|
test -x dist/field/field-bootstrap-manual/darwin-arm64/iop-node
|
|
test -f dist/field/field-bootstrap-manual/darwin-arm64/SHA256SUMS
|
|
test -x dist/field/field-bootstrap-manual/bootstrap/node-darwin-arm64.sh
|
|
```
|
|
|
|
### 4. artifact/bootstrap HTTP 시작
|
|
|
|
version 디렉터리를 document root로 열어 둔다.
|
|
|
|
```bash
|
|
python3 -m http.server 18080 --bind 0.0.0.0 -d dist/field/field-bootstrap-manual
|
|
```
|
|
|
|
다른 터미널에서 URL이 보이는지 확인한다.
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:18080/darwin-arm64/SHA256SUMS
|
|
curl -fsS http://127.0.0.1:18080/bootstrap/node-darwin-arm64.sh | sed -n '1,40p'
|
|
```
|
|
|
|
기준 field 환경에서는 Apple Silicon Mac에서도 같은 URL이 보여야 한다.
|
|
`18080`을 사용할 수 없는 별도 환경에서만 내부망, front/control-plane, CI artifact, 임시 로컬 HTTP 같은 artifact 채널을 사용한다.
|
|
|
|
```bash
|
|
curl -fsS http://toki-labs.com:18080/darwin-arm64/SHA256SUMS
|
|
```
|
|
|
|
### 5. Edge serve 시작
|
|
|
|
Edge process를 field config로 실행한다.
|
|
|
|
```bash
|
|
go run ./apps/edge/cmd/edge serve --config /tmp/iop-edge-field-bootstrap.yaml
|
|
```
|
|
|
|
다른 터미널에서 listener를 확인한다.
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:8080/healthz
|
|
curl -fsS http://127.0.0.1:8080/v1/models
|
|
```
|
|
|
|
`/v1/models`는 Node가 아직 연결되지 않아도 configured model 목록을 반환해야 한다.
|
|
|
|
### 6. Apple Silicon Mac에서 one-line bootstrap 실행
|
|
|
|
Mac에서 Ollama가 떠 있고 `gemma4:26b`를 사용할 수 있는지 먼저 확인한다.
|
|
|
|
```bash
|
|
ollama list | grep 'gemma4:26b'
|
|
```
|
|
|
|
그 다음 one-line bootstrap을 실행한다.
|
|
사용자에게 전달할 때는 완성된 URL을 포함하고, token 값만 마지막 positional value로 바꾸게 한다.
|
|
|
|
```bash
|
|
curl -fsSL http://toki-labs.com:18080/bootstrap/node-darwin-arm64.sh | bash -s CHANGE_ME_FIELD_TOKEN
|
|
```
|
|
|
|
성공하면 Mac에 다음 파일이 생기고, Node process가 foreground로 실행된다.
|
|
|
|
```text
|
|
~/iop-field/iop-node
|
|
```
|
|
|
|
Edge 로그에는 `node registered`와 `node-silicon-ollama`가 보여야 한다.
|
|
|
|
### 7. Edge에서 모델 호출 확인
|
|
|
|
Edge host에서 Node 등록 후 OpenAI-compatible API를 호출한다.
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:8080/v1/models
|
|
```
|
|
|
|
non-streaming:
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:8080/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"model":"gemma4:26b","messages":[{"role":"user","content":"Reply with IOP_FIELD_BOOTSTRAP_OK only."}]}'
|
|
```
|
|
|
|
streaming:
|
|
|
|
```bash
|
|
curl -fsS -N http://127.0.0.1:8080/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"model":"gemma4:26b","stream":true,"messages":[{"role":"user","content":"Reply with IOP_FIELD_STREAM_OK only."}]}'
|
|
```
|
|
|
|
성공 기준:
|
|
|
|
- Mac의 bootstrap 명령이 foreground Node process 실행까지 도달한다.
|
|
- Edge 로그에 `node registered`가 나온다.
|
|
- `/v1/models`가 `gemma4:26b`를 포함한다.
|
|
- non-streaming 응답에서 content가 비어 있지 않다.
|
|
- streaming 응답에서 SSE chunk와 `data: [DONE]`이 보인다.
|
|
|
|
### 8. 실패 시 확인 순서
|
|
|
|
다운로드 실패:
|
|
|
|
```bash
|
|
curl -v http://toki-labs.com:18080/bootstrap/node-darwin-arm64.sh
|
|
curl -v http://toki-labs.com:18080/darwin-arm64/iop-node
|
|
```
|
|
|
|
Node가 Edge에 연결하지 못함:
|
|
|
|
```bash
|
|
nc -vz toki-labs.com 19090
|
|
```
|
|
|
|
token 불일치:
|
|
|
|
- Edge config의 `nodes[].token` 값과 bootstrap 명령 마지막의 token 인자 값을 맞춘다.
|
|
|
|
Ollama 호출 실패:
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:11434/api/tags
|
|
```
|
|
|
|
Apple Silicon Mac에서 Node와 Ollama를 함께 실행하는 경우 Edge config의 `nodes[].adapters.ollama.base_url`은 `http://127.0.0.1:11434`여야 한다.
|
|
|
|
### 9. 정리
|
|
|
|
Mac의 foreground Node process는 `Ctrl-C`로 종료한다.
|
|
재실행은 같은 one-line bootstrap command를 다시 실행한다.
|
|
|
|
관련 완료 Milestone 기록은 `agent-roadmap/archive/phase/serving-routing-optimization/milestones/field-bootstrap-test-port-readiness.md`에 보존되어 있다.
|
|
이후 field bootstrap 관련 구현, 문서, 테스트 포트 변경은 새 활성 Milestone 또는 후속 Phase 범위로 판단한다.
|
|
|
|
## 공통 Ollama 필드 테스트 환경
|
|
|
|
OpenAI-compatible Ollama E2E 서빙 검증은 아래 필드 환경을 공통 기준으로 사용한다.
|
|
이 환경은 특정 마일스톤에만 두지 않고, 이후 모델 서빙, routing, fallback, client 통합 검증에서도 재사용한다.
|
|
`scripts/e2e-openai-ollama.sh` 같은 fake Ollama smoke는 보조 확인이다.
|
|
완료 판정은 `iop-edge` 실행, Edge가 제시한 Node bootstrap 명령, 실제 Ollama endpoint를 연결한 full-cycle 흐름으로 한다.
|
|
`gemma4:26b`는 thinking을 먼저 stream할 수 있으므로, 기본 검증은 `think:false`로 우회하지 않는다.
|
|
Edge 응답에서 thinking/reasoning stream과 최종 content stream이 분리되어 보이는지 확인한다.
|
|
|
|
| 항목 | 값 |
|
|
|---|---|
|
|
| Node host | `ssh toki@toki-labs.com` |
|
|
| Ollama base URL | `http://192.168.0.97:11434` |
|
|
| 기준 model | `gemma4:26b` |
|
|
| 권장 node id | `node-toki-labs-ollama` |
|
|
| 권장 node alias | `toki-labs-ollama` |
|
|
|
|
기본 `configs/*.yaml`은 필드 테스트 값으로 덮어쓰지 않는다.
|
|
edge와 node는 임시 config 또는 `/etc/iop/*.yaml` 필드 설정을 사용한다.
|
|
Node가 사용할 Ollama endpoint는 node config가 아니라 edge config의 `nodes[].adapters.ollama.base_url`에 둔다.
|
|
Node는 registration 이후 Edge가 내려주는 adapter config로 Ollama를 호출한다.
|
|
|
|
edge host에서 임시 config를 만들 때의 기준은 다음과 같다.
|
|
`CHANGE_ME_FIELD_TOKEN`과 `<edge-host>`는 테스트 시점의 값으로 바꾼다.
|
|
`openai.target`은 비워 두면 요청의 `model`을 내부 target으로 사용하고, `gemma4:26b`로 고정하면 외부 요청 model과 무관하게 해당 model로만 호출한다.
|
|
Node는 Edge로 outbound 연결하므로 `ssh toki@toki-labs.com` 환경에서 `<edge-host>:9090`에 접근할 수 있어야 한다.
|
|
로컬 개발 머신이 원격 host에서 직접 보이지 않으면 Edge를 접근 가능한 host에서 실행하거나 터널을 구성한다.
|
|
|
|
```yaml
|
|
edge:
|
|
id: "edge-toki-labs-field"
|
|
name: "Toki Labs Field Edge"
|
|
|
|
server:
|
|
listen: "0.0.0.0:9090"
|
|
|
|
openai:
|
|
enabled: true
|
|
listen: "0.0.0.0:8080"
|
|
node: "node-toki-labs-ollama"
|
|
adapter: "ollama"
|
|
target: ""
|
|
models:
|
|
- "gemma4:26b"
|
|
session_id: "openai-field"
|
|
timeout_sec: 300
|
|
strict_output: true
|
|
strict_stream_buffer: false
|
|
|
|
nodes:
|
|
- id: "node-toki-labs-ollama"
|
|
alias: "toki-labs-ollama"
|
|
token: "CHANGE_ME_FIELD_TOKEN"
|
|
adapters:
|
|
ollama:
|
|
enabled: true
|
|
base_url: "http://192.168.0.97:11434"
|
|
context_size: 262144
|
|
```
|
|
|
|
edge 실행:
|
|
|
|
```bash
|
|
iop-edge serve --config /tmp/iop-edge-toki-labs.yaml
|
|
```
|
|
|
|
node host에서는 별도 Node config를 만들지 않는다. Edge가 제시한 bootstrap 명령만 실행한다.
|
|
|
|
```bash
|
|
ssh toki@toki-labs.com
|
|
```
|
|
|
|
```bash
|
|
curl -fsSL http://toki-labs.com:18080/bootstrap/node-darwin-arm64.sh | bash -s CHANGE_ME_FIELD_TOKEN
|
|
```
|
|
|
|
edge host에서 OpenAI-compatible API를 호출해 검증한다.
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:8080/v1/models
|
|
```
|
|
|
|
```bash
|
|
curl -fsS http://127.0.0.1:8080/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"model":"gemma4:26b","messages":[{"role":"user","content":"Reply with IOP_OLLAMA_E2E_OK only."}]}'
|
|
```
|
|
|
|
streaming 검증:
|
|
|
|
```bash
|
|
curl -fsS -N http://127.0.0.1:8080/v1/chat/completions \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"model":"gemma4:26b","stream":true,"messages":[{"role":"user","content":"Reply with IOP_OLLAMA_STREAM_OK only."}]}'
|
|
```
|
|
|
|
완료 기준은 사용자가 재현하는 실제 절차에서 node registration, `/v1/models` 응답, non-streaming chat completion, streaming SSE chunk와 `data: [DONE]` 확인이다.
|
|
`gemma4:26b`가 thinking을 내보내는 경우 thinking/reasoning chunk가 content와 섞이지 않고, content가 시작된 뒤 답변 stream이 이어지는지도 확인한다.
|
|
Edge/Node 뒤의 소켓 relay가 사용자 UX에 드러나지 않아야 하며, 실패 시 edge와 node 양쪽 로그를 함께 확인한다.
|
|
|
|
## Agent Bootstrap / Bridge References
|
|
|
|
OTO 같은 specialized domain agent의 bootstrap/enrollment 상세 계획은 `agent-roadmap/milestones/agent-bootstrap-oto-enrollment.md`에서 관리한다.
|
|
Agent 설치가 어렵거나 임시 제어만 필요한 대상의 remote terminal bridge 계획은 `agent-roadmap/milestones/remote-terminal-bridge-poc.md`에서 관리한다.
|
|
|
|
이 배포 문서에서는 현재 dev field 기준의 배포 단위와 smoke 절차만 유지한다.
|
|
|
|
## Field Smoke
|
|
|
|
최소 확인 순서는 다음을 기준으로 한다.
|
|
|
|
1. `docker compose ps`에서 `postgres`, `redis`, `control-plane`, `web`이 healthy/running인지 확인한다.
|
|
2. edge host에서 `iop-edge console --config /etc/iop/edge.yaml`을 실행한다.
|
|
3. node host에서 Edge가 제시한 bootstrap 명령을 실행한다.
|
|
4. edge console에서 `/nodes`로 node 등록을 확인한다.
|
|
5. 메시지 2회를 보내고 각 요청의 start, message, complete event가 edge console에 표시되는지 확인한다.
|
|
6. `/capabilities`, `/transport`, `/sessions`를 실행해 command 응답을 확인한다.
|
|
|
|
보조 자동 smoke는 repo root에서 다음 명령으로 실행한다.
|
|
|
|
```bash
|
|
make test-e2e
|
|
```
|
|
|
|
이 명령은 보조 확인이며, 필드 배포 완료 기준은 실제 edge/node 바이너리 실행 흐름 확인이다.
|
|
|
|
## Edge setup CLI
|
|
|
|
Edge 바이너리는 호스트 환경 준비를 `setup` 명령으로 일원화한다. 공식 운영 경로는 하나다.
|
|
|
|
```bash
|
|
sudo iop-edge setup --enable --start
|
|
```
|
|
|
|
배포 전에 결과를 검토하려면 `--dry-run`을 쓴다.
|
|
|
|
```bash
|
|
sudo iop-edge setup --dry-run --binary /usr/local/bin/iop-edge
|
|
```
|
|
|
|
`setup`은 다음을 담당한다.
|
|
|
|
- 실행 user/group(`iop`) 준비
|
|
- `/etc/iop`와 `/var/lib/iop/edge` 디렉터리 준비
|
|
- 설정 파일이 없을 때만 기본 템플릿 생성 (`--overwrite-config`로 강제 갱신)
|
|
- systemd unit 생성 또는 갱신 (`/etc/systemd/system/iop-edge.service`)
|
|
- `systemctl daemon-reload`
|
|
- 옵션에 따른 `--enable`, `--start`, `--restart`
|
|
|
|
`setup`의 `--config` 기본값은 `/etc/iop/edge.yaml`이다. dev 단계 명령(`serve`, `console`, `config init/print/check`, `env`, `node register`)의 root persistent `--config` 기본값은 bundle-local `edge.yaml` (없을 시 executable-adjacent `edge.yaml`, 그 다음 repo dev fallback `configs/edge.yaml`)로 확인된다.
|
|
|
|
별도 `render`, `service install`, `service status` 명령은 초기 범위에 넣지 않는다. 검토와 CI 확인은 `setup --dry-run`으로 흡수하고, 상태/로그/재시작은 `systemctl`과 `journalctl`을 기준 운영 도구로 둔다.
|
|
|
|
현재 구현된 edge CLI 표면은 다음과 같다.
|
|
|
|
```text
|
|
iop-edge serve
|
|
iop-edge console
|
|
iop-edge setup
|
|
iop-edge env
|
|
iop-edge config init
|
|
iop-edge config print
|
|
iop-edge config check
|
|
iop-edge node register
|
|
iop-edge nodes list
|
|
iop-edge smoke openai
|
|
iop-edge version
|
|
```
|
|
|
|
`setup`은 여러 번 실행해도 같은 결과로 수렴한다. 기존 설정 파일은 기본적으로 덮어쓰지 않고, 강제 갱신이 필요하면 `--overwrite-config`를 지정한다.
|
|
|
|
## Node host setup note
|
|
|
|
Node setup/config CLI는 현재 구현에 남아 있는 저수준 보조 표면이다.
|
|
field 사용자 기본 경로와 다음 개발 방향에서는 Node setup/config를 안내하지 않는다.
|
|
Control Plane 없는 개발 단계에서도 Node별 실행 설정은 Edge config의 `nodes[]`에서 관리하고, Node host에서는 Edge가 제시한 bootstrap 명령만 실행한다.
|