deploy: add Streamlit dashboard service
This commit is contained in:
+1
-1
@@ -18,7 +18,7 @@ RUN python -m pip install --upgrade pip \
|
|||||||
&& python -m pip install '.[dashboard]'
|
&& python -m pip install '.[dashboard]'
|
||||||
|
|
||||||
VOLUME ["/data"]
|
VOLUME ["/data"]
|
||||||
EXPOSE 8000
|
EXPOSE 8000 8501
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
|
||||||
CMD python -c "import os, socket; s=socket.create_connection(('127.0.0.1', int(os.getenv('JARCA_MCP_PORT', '8000'))), timeout=3); s.close()"
|
CMD python -c "import os, socket; s=socket.create_connection(('127.0.0.1', int(os.getenv('JARCA_MCP_PORT', '8000'))), timeout=3); s.close()"
|
||||||
|
|||||||
@@ -17,3 +17,43 @@ services:
|
|||||||
PYTHONUNBUFFERED: "1"
|
PYTHONUNBUFFERED: "1"
|
||||||
volumes:
|
volumes:
|
||||||
- /volume1/docker/jarca-ledger/data:/data
|
- /volume1/docker/jarca-ledger/data:/data
|
||||||
|
|
||||||
|
jarca-ledger-dashboard:
|
||||||
|
image: jarca-ledger:latest
|
||||||
|
container_name: jarca-ledger-dashboard
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
jarca-ledger:
|
||||||
|
condition: service_healthy
|
||||||
|
ports:
|
||||||
|
- "8501:8501"
|
||||||
|
environment:
|
||||||
|
JARCA_LEDGER_DB_PATH: /data/ledger.sqlite3
|
||||||
|
STREAMLIT_BROWSER_GATHER_USAGE_STATS: "false"
|
||||||
|
PYTHONUNBUFFERED: "1"
|
||||||
|
volumes:
|
||||||
|
- /volume1/docker/jarca-ledger/data:/data
|
||||||
|
command:
|
||||||
|
- python
|
||||||
|
- -m
|
||||||
|
- streamlit
|
||||||
|
- run
|
||||||
|
- dashboard/app.py
|
||||||
|
- --server.address
|
||||||
|
- 0.0.0.0
|
||||||
|
- --server.port
|
||||||
|
- "8501"
|
||||||
|
- --server.headless
|
||||||
|
- "true"
|
||||||
|
- --browser.gatherUsageStats
|
||||||
|
- "false"
|
||||||
|
healthcheck:
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
start_period: 30s
|
||||||
|
retries: 3
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- python
|
||||||
|
- -c
|
||||||
|
- "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8501/_stcore/health', timeout=3).read()"
|
||||||
|
|||||||
@@ -2,13 +2,14 @@
|
|||||||
|
|
||||||
## 목적
|
## 목적
|
||||||
|
|
||||||
`jarca-ledger`를 `genichin-agent` 생명주기와 분리된 독립 Docker 서비스로 배포하고, stdio `docker exec`가 아니라 HTTP 기반 Streamable MCP endpoint를 직접 제공한다.
|
`jarca-ledger`를 `genichin-agent` 생명주기와 분리된 독립 Docker 서비스로 배포하고, stdio `docker exec`가 아니라 HTTP 기반 Streamable MCP endpoint를 직접 제공한다. 같은 이미지에서 별도 Streamlit 컨테이너(`jarca-ledger-dashboard`)를 실행해 운영 프론트엔드도 함께 서비스한다.
|
||||||
|
|
||||||
## 운영 고정값
|
## 운영 고정값
|
||||||
|
|
||||||
| 항목 | 값 |
|
| 항목 | 값 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 컨테이너 이름 | `jarca-ledger` |
|
| 컨테이너 이름 | `jarca-ledger` |
|
||||||
|
| Streamlit 컨테이너 이름 | `jarca-ledger-dashboard` |
|
||||||
| 이미지 | `jarca-ledger:latest` |
|
| 이미지 | `jarca-ledger:latest` |
|
||||||
| Host data path | `/volume1/docker/jarca-ledger/data` |
|
| Host data path | `/volume1/docker/jarca-ledger/data` |
|
||||||
| Container data path | `/data` |
|
| Container data path | `/data` |
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
| MCP transport | `streamable-http` |
|
| MCP transport | `streamable-http` |
|
||||||
| MCP listen | `0.0.0.0:8000` |
|
| MCP listen | `0.0.0.0:8000` |
|
||||||
| MCP path | `/mcp` |
|
| MCP path | `/mcp` |
|
||||||
|
| Streamlit listen | `0.0.0.0:8501` |
|
||||||
|
|
||||||
## 배포
|
## 배포
|
||||||
|
|
||||||
@@ -29,8 +31,10 @@ docker compose up -d --build
|
|||||||
```bash
|
```bash
|
||||||
docker ps --filter name=jarca-ledger
|
docker ps --filter name=jarca-ledger
|
||||||
docker inspect jarca-ledger --format='MOUNTS: see HostConfig.Binds and Mounts in docker inspect output'
|
docker inspect jarca-ledger --format='MOUNTS: see HostConfig.Binds and Mounts in docker inspect output'
|
||||||
|
docker inspect jarca-ledger-dashboard --format='{{.State.Status}} {{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}'
|
||||||
docker inspect jarca-ledger | python3 -c "import json,sys; data=json.load(sys.stdin)[0]; [print(f'{m.get(\"Source\")} -> {m.get(\"Destination\")} ({m.get(\"Type\")})') for m in data.get('Mounts', [])]"
|
docker inspect jarca-ledger | python3 -c "import json,sys; data=json.load(sys.stdin)[0]; [print(f'{m.get(\"Source\")} -> {m.get(\"Destination\")} ({m.get(\"Type\")})') for m in data.get('Mounts', [])]"
|
||||||
docker logs --tail 80 jarca-ledger
|
docker logs --tail 80 jarca-ledger
|
||||||
|
docker logs --tail 80 jarca-ledger-dashboard
|
||||||
```
|
```
|
||||||
|
|
||||||
Python MCP client smoke:
|
Python MCP client smoke:
|
||||||
@@ -54,9 +58,25 @@ anyio.run(main)
|
|||||||
PY
|
PY
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Streamlit HTTP smoke:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python - <<'PY'
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
for url in [
|
||||||
|
'http://127.0.0.1:8501/_stcore/health',
|
||||||
|
'http://127.0.0.1:8501/?page=%EC%9A%94%EC%95%BD',
|
||||||
|
]:
|
||||||
|
with urllib.request.urlopen(url, timeout=10) as response:
|
||||||
|
print(url, response.status)
|
||||||
|
PY
|
||||||
|
```
|
||||||
|
|
||||||
## 운영 원칙
|
## 운영 원칙
|
||||||
|
|
||||||
- `jarca-ledger` 컨테이너는 Docker daemon의 `restart: unless-stopped` 정책으로 독립 실행된다.
|
- `jarca-ledger` 컨테이너는 Docker daemon의 `restart: unless-stopped` 정책으로 독립 실행된다.
|
||||||
|
- `jarca-ledger-dashboard` 컨테이너는 같은 image와 `/data` volume을 사용하며, Streamlit UI를 `http://<synology-host>:8501`로 제공한다.
|
||||||
- `genichin-agent`는 이 서비스를 배포하거나 실행시키는 필수 구성요소가 아니다.
|
- `genichin-agent`는 이 서비스를 배포하거나 실행시키는 필수 구성요소가 아니다.
|
||||||
- 재무관리 에이전트는 `http://<synology-host>:8000/mcp` 또는 같은 Docker network에서 `http://jarca-ledger:8000/mcp`로 접속한다.
|
- 재무관리 에이전트는 `http://<synology-host>:8000/mcp` 또는 같은 Docker network에서 `http://jarca-ledger:8000/mcp`로 접속한다.
|
||||||
- 운영 DB와 import 원본/민감자료는 이미지에 포함하지 않고 `/volume1/docker/jarca-ledger/data`에만 둔다.
|
- 운영 DB와 import 원본/민감자료는 이미지에 포함하지 않고 `/volume1/docker/jarca-ledger/data`에만 둔다.
|
||||||
|
|||||||
Reference in New Issue
Block a user