Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bea8b368e | ||
|
|
ee3e3852ee | ||
|
|
bf746e0cf3 | ||
|
|
7a120bb068 |
@@ -0,0 +1,108 @@
|
||||
schema: gitea-agent/release/v1
|
||||
|
||||
version:
|
||||
sources:
|
||||
- path: pyproject.toml
|
||||
format: toml
|
||||
selector: project.version
|
||||
- path: jarca_ledger_mcp/__init__.py
|
||||
format: python-assignment
|
||||
selector: __version__
|
||||
lockfiles:
|
||||
- path: uv.lock
|
||||
package: jarca-ledger-mcp
|
||||
tag_template: "v{version}"
|
||||
|
||||
prepare:
|
||||
runtime:
|
||||
python: "3.12"
|
||||
commands:
|
||||
- name: sync
|
||||
argv: [uv, sync, --python, "3.12", --frozen, --extra, dev, --extra, dashboard]
|
||||
- name: lint
|
||||
argv: [uv, run, ruff, check, .]
|
||||
- name: test
|
||||
argv: [uv, run, pytest]
|
||||
- name: dashboard-http-smoke
|
||||
argv: [uv, run, python, scripts/ci_dashboard_http_smoke.py, --host, 127.0.0.1, --port, "18501", --timeout, "45"]
|
||||
- name: db-safety-smoke
|
||||
argv: [uv, run, python, scripts/ci_db_safety_smoke.py, --timeout, "45"]
|
||||
|
||||
artifacts:
|
||||
- type: container
|
||||
name: jarca-ledger-mcp
|
||||
platform: linux/arm64
|
||||
|
||||
build:
|
||||
adapter: docker-buildx
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
pull: true
|
||||
local_tag_template: "jarca-ledger-mcp:release-{commit}"
|
||||
labels:
|
||||
org.opencontainers.image.source: "https://gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp"
|
||||
org.opencontainers.image.revision: "{commit}"
|
||||
org.opencontainers.image.version: "{version}"
|
||||
|
||||
smoke:
|
||||
- type: image-platform
|
||||
expected_os: linux
|
||||
expected_architecture: arm64
|
||||
- type: container-command
|
||||
environment:
|
||||
JARCA_LEDGER_DB_PATH: /data/ledger.sqlite3
|
||||
argv:
|
||||
- python
|
||||
- -c
|
||||
- >-
|
||||
import os, pathlib, platform; from jarca_ledger_mcp import service;
|
||||
assert os.getuid() == 1000, os.getuid();
|
||||
assert os.getgid() == 1000, os.getgid();
|
||||
probe = pathlib.Path("/data/.write-probe");
|
||||
probe.write_text("ok"); probe.unlink();
|
||||
result = service.health_check();
|
||||
assert platform.machine() == "aarch64";
|
||||
assert pathlib.Path("/data/ledger.sqlite3").is_file();
|
||||
assert result["success"], result
|
||||
- type: container-health
|
||||
name_template: "jarca-mcp-{run_id}"
|
||||
command: default
|
||||
expected: healthy
|
||||
timeout_seconds: 90
|
||||
- type: container-http
|
||||
name_template: "jarca-dashboard-{run_id}"
|
||||
command:
|
||||
- python
|
||||
- -m
|
||||
- streamlit
|
||||
- run
|
||||
- dashboard/app.py
|
||||
- --server.address
|
||||
- 0.0.0.0
|
||||
- --server.port
|
||||
- "8501"
|
||||
- --server.headless
|
||||
- "true"
|
||||
- --browser.gatherUsageStats
|
||||
- "false"
|
||||
container_port: 8501
|
||||
path: /_stcore/health
|
||||
expected_status: 200
|
||||
expected_body: ok
|
||||
timeout_seconds: 90
|
||||
|
||||
publish:
|
||||
adapter: gitea-container-registry
|
||||
registry: gitea.jsfamily.quest
|
||||
repository: jsfamily/jarca-ledger-mcp
|
||||
credentials:
|
||||
username: authenticated-gitea-login
|
||||
token_env: GITEA_TOKEN
|
||||
tags:
|
||||
- "{tag}"
|
||||
- "sha-{commit}"
|
||||
overwrite: false
|
||||
verify:
|
||||
- digest
|
||||
- platform
|
||||
- org.opencontainers.image.revision
|
||||
@@ -1,134 +0,0 @@
|
||||
name: Verify and publish ARM64 image
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- Dockerfile
|
||||
- .dockerignore
|
||||
- pyproject.toml
|
||||
- uv.lock
|
||||
- jarca_ledger_mcp/**
|
||||
- dashboard/**
|
||||
- .github/workflows/publish-image.yml
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: image-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
verify-arm64:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build ARM64 verification image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64
|
||||
load: true
|
||||
push: false
|
||||
tags: jarca-ledger-mcp:arm64-verify
|
||||
cache-from: type=gha,scope=jarca-ledger-arm64
|
||||
cache-to: type=gha,mode=max,scope=jarca-ledger-arm64
|
||||
|
||||
- name: Verify image platform, imports, and SQLite
|
||||
run: |
|
||||
set -euo pipefail
|
||||
test "$(docker image inspect jarca-ledger-mcp:arm64-verify --format '{{.Architecture}}')" = arm64
|
||||
docker run --rm --platform linux/arm64 \
|
||||
-e JARCA_LEDGER_DB_PATH=/tmp/ledger.sqlite3 \
|
||||
jarca-ledger-mcp:arm64-verify \
|
||||
python -c 'import platform, cryptography, numpy, pandas, pyarrow, pymupdf; from jarca_ledger_mcp import service; result=service.health_check(); assert platform.machine() == "aarch64"; assert result["success"], result; print(result["schema_version"])'
|
||||
|
||||
- name: Verify MCP and Dashboard startup
|
||||
run: |
|
||||
set -euo pipefail
|
||||
trap 'docker rm -f jarca-mcp-smoke jarca-dashboard-smoke >/dev/null 2>&1 || true' EXIT
|
||||
|
||||
docker run -d --name jarca-mcp-smoke --platform linux/arm64 \
|
||||
jarca-ledger-mcp:arm64-verify
|
||||
for _ in $(seq 1 60); do
|
||||
status=$(docker inspect --format '{{.State.Health.Status}}' jarca-mcp-smoke)
|
||||
test "$status" = healthy && break
|
||||
test "$status" = unhealthy && { docker logs jarca-mcp-smoke; exit 1; }
|
||||
sleep 1
|
||||
done
|
||||
test "$(docker inspect --format '{{.State.Health.Status}}' jarca-mcp-smoke)" = healthy
|
||||
|
||||
docker run -d --name jarca-dashboard-smoke --platform linux/arm64 \
|
||||
-p 127.0.0.1::8501 jarca-ledger-mcp:arm64-verify \
|
||||
python -m streamlit run dashboard/app.py \
|
||||
--server.address 0.0.0.0 --server.port 8501 \
|
||||
--server.headless true --browser.gatherUsageStats false
|
||||
port=$(docker port jarca-dashboard-smoke 8501/tcp | sed 's/.*://')
|
||||
code=000
|
||||
for _ in $(seq 1 90); do
|
||||
code=$(curl -sS -o /tmp/dashboard-health -w '%{http_code}' \
|
||||
"http://127.0.0.1:${port}/_stcore/health" || true)
|
||||
test "$code" = 200 && break
|
||||
sleep 1
|
||||
done
|
||||
test "$code" = 200
|
||||
test "$(tr -d '\n' < /tmp/dashboard-health)" = ok
|
||||
|
||||
publish:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: verify-arm64
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
outputs:
|
||||
digest: ${{ steps.build.outputs.digest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: arm64
|
||||
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GHCR
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and publish immutable ARM64 image
|
||||
id: build
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/arm64
|
||||
push: true
|
||||
tags: |
|
||||
ghcr.io/genichin/jarca-ledger-mcp:${{ github.ref_name }}
|
||||
ghcr.io/genichin/jarca-ledger-mcp:sha-${{ github.sha }}
|
||||
labels: |
|
||||
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
|
||||
org.opencontainers.image.revision=${{ github.sha }}
|
||||
org.opencontainers.image.version=${{ github.ref_name }}
|
||||
cache-from: type=gha,scope=jarca-ledger-arm64
|
||||
cache-to: type=gha,mode=max,scope=jarca-ledger-arm64
|
||||
provenance: mode=max
|
||||
sbom: true
|
||||
|
||||
- name: Report published digest
|
||||
run: |
|
||||
echo "ghcr.io/genichin/jarca-ledger-mcp:${GITHUB_REF_NAME}@${{ steps.build.outputs.digest }}"
|
||||
+13
-3
@@ -11,6 +11,8 @@ ENV PYTHONUNBUFFERED=1 \
|
||||
JARCA_MCP_HOST=0.0.0.0 \
|
||||
JARCA_MCP_PORT=8000 \
|
||||
JARCA_MCP_STREAMABLE_HTTP_PATH=/mcp \
|
||||
HOME=/home/jarca \
|
||||
TMPDIR=/tmp \
|
||||
PATH=/app/.venv/bin:$PATH
|
||||
|
||||
COPY --from=uv /uv /uvx /usr/local/bin/
|
||||
@@ -20,9 +22,15 @@ WORKDIR /app
|
||||
COPY pyproject.toml uv.lock README.md ./
|
||||
RUN uv sync --frozen --no-dev --extra dashboard --no-install-project
|
||||
|
||||
COPY jarca_ledger_mcp ./jarca_ledger_mcp
|
||||
COPY dashboard ./dashboard
|
||||
RUN uv sync --frozen --no-dev --extra dashboard
|
||||
RUN groupadd --gid 1000 jarca \
|
||||
&& useradd --uid 1000 --gid 1000 --create-home --home-dir /home/jarca --shell /usr/sbin/nologin jarca \
|
||||
&& chown -R 1000:1000 /home/jarca \
|
||||
&& install -d -o 1000 -g 1000 -m 0770 /data
|
||||
|
||||
COPY --chown=1000:1000 jarca_ledger_mcp ./jarca_ledger_mcp
|
||||
COPY --chown=1000:1000 dashboard ./dashboard
|
||||
RUN uv sync --frozen --no-dev --extra dashboard \
|
||||
&& chmod -R a+rX /app
|
||||
|
||||
VOLUME ["/data"]
|
||||
EXPOSE 8000 8501
|
||||
@@ -30,4 +38,6 @@ EXPOSE 8000 8501
|
||||
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()"
|
||||
|
||||
USER 1000:1000
|
||||
|
||||
CMD ["jarca-ledger-mcp"]
|
||||
|
||||
@@ -85,16 +85,19 @@ Deployment: jarca-ledger (replicas: 1)
|
||||
|
||||
- 하나의 Pod 안에서 MCP와 Streamlit을 **별도 컨테이너**로 실행합니다. 한 컨테이너에서 두 프로세스를 supervisor나 background command로 함께 실행하지 않습니다.
|
||||
- 두 컨테이너는 동일한 PVC를 `/data`에 마운트하고 `JARCA_LEDGER_DB_PATH=/data/ledger.sqlite3`를 사용합니다.
|
||||
- 이미지와 Pod는 UID/GID `1000:1000`으로 실행하며, PVC 쓰기 권한은 `fsGroup: 1000`으로 설정합니다.
|
||||
- frontend 컨테이너에만 `JARCA_DASHBOARD_ADMIN_ID`와 `JARCA_DASHBOARD_ADMIN_PASSWORD`를 Kubernetes Secret 또는 동등한 secret store에서 주입합니다. 실제 값은 manifest나 repository에 기록하지 않습니다.
|
||||
- SQLite 파일을 공유하므로 Deployment의 replica는 `1`로 유지합니다.
|
||||
- MCP와 frontend는 목적별 Kubernetes Service로 분리합니다. MCP는 `8000`, frontend는 `8501`을 대상으로 합니다.
|
||||
- 외부 공개가 필요하면 일반적으로 frontend Service만 Ingress 또는 NodePort에 연결하고, MCP Service는 필요한 범위에서만 노출합니다.
|
||||
- SQLite 파일을 공유하므로 Deployment의 replica는 `1`, update 전략은 `Recreate`로 유지합니다.
|
||||
- Base는 Dashboard용 `ClusterIP` Service(`8501`)만 생성합니다. 인증 계층이 없는 MCP port `8000`에는 Service를 만들지 않고 `NetworkPolicy`로 ingress를 차단합니다. MCP 원격 접근이 필요하면 environment overlay에서 인증된 gateway와 제한된 ingress 정책을 함께 정의합니다.
|
||||
- 외부 공개가 필요하면 Dashboard Service만 environment overlay의 Ingress 또는 NodePort에 연결합니다.
|
||||
- ARM64 worker에서만 실행하도록 Pod의 `nodeSelector` 또는 node affinity를 설정합니다.
|
||||
- 배포 이미지는 가변 태그 대신 검증된 GHCR digest로 고정합니다.
|
||||
- 배포 이미지는 가변 태그 대신 검증된 `gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp` Gitea Container Registry digest로 고정합니다.
|
||||
|
||||
검증된 k3s Kustomize base는 [`deploy/k8s/base`](deploy/k8s/base)에 있으며, Secret 준비·digest pinning·배포 절차는 [`deploy/k8s/README.md`](deploy/k8s/README.md)를 따릅니다. Base에는 실제 Secret, Ingress, TLS와 cluster별 설정을 포함하지 않습니다.
|
||||
|
||||
Streamlit이 SQLite에 직접 접근하지 않고 MCP/API만 호출하도록 변경하거나 네트워크 DB로 전환한 뒤에는 MCP와 frontend를 각각 독립 Deployment로 분리할 수 있습니다.
|
||||
|
||||
로컬 `docker-compose.yml`도 동일한 애플리케이션 이미지를 두 컨테이너로 실행하는 구조입니다. 현재 Compose 설정은 로컬에서 빌드한 `jarca-ledger:latest`를 사용하며, MCP는 호스트 `8000` 포트로, Streamlit은 호스트 `40059`에서 컨테이너 `8501` 포트로 연결됩니다. Compose 실행 전 host environment 또는 안전한 `.env` 전달 경계에 두 Dashboard credential 변수를 설정해야 하며 누락 시 Compose가 시작을 거부합니다. GHCR 릴리즈를 배포할 때는 두 서비스의 `image`를 검증된 동일 digest로 지정해야 합니다.
|
||||
로컬 `docker-compose.yml`도 동일한 애플리케이션 이미지를 두 컨테이너로 실행하는 구조입니다. 현재 Compose 설정은 로컬에서 빌드한 `jarca-ledger:latest`를 사용하며, MCP는 호스트 `8000` 포트로, Streamlit은 호스트 `40059`에서 컨테이너 `8501` 포트로 연결됩니다. Compose 실행 전 host environment 또는 안전한 `.env` 전달 경계에 두 Dashboard credential 변수를 설정해야 하며 누락 시 Compose가 시작을 거부합니다. Bind mount 대상 `/volume1/docker/jarca-ledger/data`는 UID/GID `1000:1000`이 쓸 수 있어야 합니다. Gitea Container Registry 릴리즈를 배포할 때는 두 서비스의 `image`를 검증된 동일 `gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp` digest로 지정해야 합니다.
|
||||
|
||||
## 주요 도구
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# k3s 배포
|
||||
|
||||
`base/`는 `jarca-ledger` namespace에 다음 리소스를 배포하는 Kustomize base다.
|
||||
|
||||
- 단일 `Deployment`와 `Recreate` 전략
|
||||
- 동일 ARM64 이미지의 MCP·Dashboard 컨테이너
|
||||
- SQLite 초기화를 담당하는 init container
|
||||
- 공유 `ReadWriteOnce` PVC
|
||||
- Dashboard용 내부 `ClusterIP` Service
|
||||
- Dashboard port `8501` ingress만 허용하는 `NetworkPolicy`
|
||||
- UID/GID `1000:1000`, `fsGroup: 1000`, read-only root filesystem
|
||||
|
||||
## 사전 준비
|
||||
|
||||
Base는 실제 Secret 값을 포함하지 않는다. 먼저 namespace를 생성한다.
|
||||
|
||||
```bash
|
||||
kubectl apply -f deploy/k8s/base/namespace.yaml
|
||||
```
|
||||
|
||||
그 다음 승인된 secret manager나 Git에 기록되지 않는 운영 절차로 다음 Secret을 `jarca-ledger` namespace에 생성한다.
|
||||
|
||||
- `jarca-ledger-registry`
|
||||
- type: `kubernetes.io/dockerconfigjson`
|
||||
- `gitea.jsfamily.quest`에서 image를 pull할 수 있는 credential
|
||||
- `jarca-ledger-dashboard-auth`
|
||||
- key `admin-id`
|
||||
- key `admin-password`
|
||||
|
||||
Registry credential을 application container에 환경변수나 volume으로 전달하지 않는다. `imagePullSecrets`로만 사용한다.
|
||||
|
||||
## Immutable image 지정
|
||||
|
||||
`base/deployment.yaml`의 release version tag는 템플릿 기본값일 뿐이며 운영 배포에 안전한 immutable 참조가 아니다. Release commit 자체에는 최종 image digest를 넣을 수 없다. Image의 revision label이 release commit을 가리키므로, 그 commit에 digest를 기록하면 commit과 image digest가 다시 바뀌는 자기참조가 발생한다.
|
||||
|
||||
Release가 완료된 뒤 검증된 digest를 사용하는 환경/인프라 저장소의 overlay를 생성한다. 예를 들어 `environments/production/jarca-ledger/kustomization.yaml`은 다음과 같다.
|
||||
|
||||
```yaml
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- ../../../../jarca-ledger-mcp/deploy/k8s/base
|
||||
images:
|
||||
- name: gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp
|
||||
newName: gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp
|
||||
digest: sha256:<verified-release-digest>
|
||||
```
|
||||
|
||||
두 application container와 init container는 반드시 같은 digest를 사용해야 한다.
|
||||
|
||||
## 배포
|
||||
|
||||
Namespace와 Secret 준비가 끝난 뒤, base가 아니라 위의 외부 digest-pinned overlay를 적용한다.
|
||||
|
||||
```bash
|
||||
kubectl apply -k environments/production/jarca-ledger
|
||||
kubectl -n jarca-ledger rollout status deployment/jarca-ledger
|
||||
kubectl -n jarca-ledger get pods,svc,pvc
|
||||
```
|
||||
|
||||
Dashboard를 외부에 공개하는 Ingress나 NodePort는 environment overlay에서 추가한다. Base에는 MCP Service가 없고, `NetworkPolicy`는 workload ingress를 dashboard port `8501`로 제한하여 인증 계층이 없는 MCP port `8000` ingress를 차단한다. Cluster의 network plugin이 `NetworkPolicy`를 enforce하는지 운영 환경에서 확인한다.
|
||||
|
||||
## SQLite와 PVC
|
||||
|
||||
- PVC 전체를 `/data`에 mount한다. SQLite journal, WAL, SHM 파일이 DB 옆에 생성되므로 DB 파일 하나만 `subPath`로 mount하지 않는다.
|
||||
- `replicas`는 `1`, update 전략은 `Recreate`로 유지한다. RollingUpdate는 일시적으로 두 Pod가 같은 SQLite DB를 열 수 있다.
|
||||
- k3s `local-path` volume에서는 `fsGroup: 1000` 적용 여부를 기존 PVC에서도 확인한다. 권한이 맞지 않으면 rollout 전에 운영 절차로 소유권을 수정하고, 상시 privileged `chown` init container는 두지 않는다.
|
||||
- 같은 PVC 안의 backup은 node 또는 disk 손실에 대한 재해 복구가 아니다. 별도 저장소로 backup을 내보낸다.
|
||||
@@ -0,0 +1,184 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: jarca-ledger
|
||||
labels:
|
||||
app.kubernetes.io/name: jarca-ledger
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: jarca-ledger
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: jarca-ledger
|
||||
spec:
|
||||
automountServiceAccountToken: false
|
||||
enableServiceLinks: false
|
||||
terminationGracePeriodSeconds: 30
|
||||
imagePullSecrets:
|
||||
- name: jarca-ledger-registry
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
kubernetes.io/arch: arm64
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
runAsGroup: 1000
|
||||
fsGroup: 1000
|
||||
fsGroupChangePolicy: OnRootMismatch
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
- name: init-db
|
||||
image: gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp:v0.1.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- python
|
||||
- -c
|
||||
- >-
|
||||
from jarca_ledger_mcp.service import health_check;
|
||||
result = health_check();
|
||||
assert result["success"], result
|
||||
env:
|
||||
- name: JARCA_LEDGER_DB_PATH
|
||||
value: /data/ledger.sqlite3
|
||||
- name: HOME
|
||||
value: /tmp
|
||||
- name: TMPDIR
|
||||
value: /tmp
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
- name: init-tmp
|
||||
mountPath: /tmp
|
||||
containers:
|
||||
- name: mcp
|
||||
image: gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp:v0.1.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: JARCA_LEDGER_DB_PATH
|
||||
value: /data/ledger.sqlite3
|
||||
- name: HOME
|
||||
value: /tmp
|
||||
- name: TMPDIR
|
||||
value: /tmp
|
||||
ports:
|
||||
- name: mcp
|
||||
containerPort: 8000
|
||||
protocol: TCP
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
startupProbe:
|
||||
tcpSocket:
|
||||
port: mcp
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 24
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: mcp
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: mcp
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
- name: mcp-tmp
|
||||
mountPath: /tmp
|
||||
- name: dashboard
|
||||
image: gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp:v0.1.2
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- python
|
||||
- -m
|
||||
- streamlit
|
||||
- run
|
||||
- dashboard/app.py
|
||||
- --server.address
|
||||
- 0.0.0.0
|
||||
- --server.port
|
||||
- "8501"
|
||||
- --server.headless
|
||||
- "true"
|
||||
- --browser.gatherUsageStats
|
||||
- "false"
|
||||
env:
|
||||
- name: JARCA_LEDGER_DB_PATH
|
||||
value: /data/ledger.sqlite3
|
||||
- name: HOME
|
||||
value: /tmp
|
||||
- name: TMPDIR
|
||||
value: /tmp
|
||||
- name: JARCA_DASHBOARD_ADMIN_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: jarca-ledger-dashboard-auth
|
||||
key: admin-id
|
||||
- name: JARCA_DASHBOARD_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: jarca-ledger-dashboard-auth
|
||||
key: admin-password
|
||||
ports:
|
||||
- name: dashboard
|
||||
containerPort: 8501
|
||||
protocol: TCP
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities:
|
||||
drop: ["ALL"]
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /_stcore/health
|
||||
port: dashboard
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 24
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /_stcore/health
|
||||
port: dashboard
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /_stcore/health
|
||||
port: dashboard
|
||||
periodSeconds: 20
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 3
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
- name: dashboard-tmp
|
||||
mountPath: /tmp
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: jarca-ledger-data
|
||||
- name: init-tmp
|
||||
emptyDir: {}
|
||||
- name: mcp-tmp
|
||||
emptyDir: {}
|
||||
- name: dashboard-tmp
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: jarca-ledger
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- pvc.yaml
|
||||
- deployment.yaml
|
||||
- services.yaml
|
||||
- networkpolicy.yaml
|
||||
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: jarca-ledger
|
||||
@@ -0,0 +1,14 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: jarca-ledger-ingress
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: jarca-ledger
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- ports:
|
||||
- protocol: TCP
|
||||
port: 8501
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: jarca-ledger-data
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: jarca-ledger-dashboard
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: jarca-ledger
|
||||
ports:
|
||||
- name: http
|
||||
port: 8501
|
||||
targetPort: dashboard
|
||||
appProtocol: http
|
||||
@@ -1,3 +1,3 @@
|
||||
"""Jarca Ledger MCP server."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
__version__ = "0.1.2"
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "jarca-ledger-mcp"
|
||||
version = "0.1.0"
|
||||
version = "0.1.2"
|
||||
description = "Jarca household ledger MCP server for transactions, accounts, budgets, cashflow, and investable cashflow analysis"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.11"
|
||||
|
||||
Reference in New Issue
Block a user