Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
.git
.claude
.idea
__pycache__
*.pyc
.venv
.benchmarks
.pytest_cache
.ruff_cache
.coverage
.env
data/
# K8s 音频冒烟镜像只需要仓库自带的少量种子音频;其他本地数据不能进入构建上下文。
data/*
!data/audio/
!data/audio/**
*.db
*.sqlite
*.sqlite3
31 changes: 31 additions & 0 deletions deploy/k8s/Dockerfile.audio
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 本地 K8s 音频链路的完整 CPU runtime。
# 此镜像与项目 uv.lock 保持一致,包含真实 SenseVoice 所需依赖和 system codec;
# Kubernetes 资源声明不申请 GPU,CUDA 相关依赖即使存在也不会被使用。
FROM python:3.12-slim

COPY --from=ghcr.io/astral-sh/uv:0.9.17 /uv /uvx /bin/

WORKDIR /app
ENV UV_COMPILE_BYTECODE=1 \
UV_PROJECT_ENVIRONMENT=/opt/venv \
# PyPI 上的 Linux ARM64 Torch 依赖包含数百 MiB 的 CUDA wheel;本地网络较慢时,
# 解压阶段可能超过默认超时。保留有限下载并发,并把单个请求/解压的超时提高到 15 分钟。
UV_HTTP_TIMEOUT=900 \
UV_CONCURRENT_DOWNLOADS=2 \
PATH=/opt/venv/bin:$PATH \
CUDA_VISIBLE_DEVICES=""

RUN apt-get update \
&& apt-get install --yes --no-install-recommends \
build-essential ffmpeg libsndfile1 wget curl git \
&& rm -rf /var/lib/apt/lists/*

COPY pyproject.toml uv.lock README.md ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev
COPY . .
RUN --mount=type=cache,target=/root/.cache/uv uv sync --frozen --no-dev
# 项目锁定 Ray 本体;最终 `uv sync` 会移除不在 lock 中的额外包,故必须在其之后
# 安装 KubeRay health check 和 Ray Dashboard 需要的 default extra。
RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install --python /opt/venv/bin/python "ray[default]==2.55.1"
103 changes: 103 additions & 0 deletions deploy/k8s/minio.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# 本地专用 MinIO,不读取宿主机或现有对象存储的任何数据。
apiVersion: v1
kind: Secret
metadata:
name: minio-credentials
namespace: daft-ray
type: Opaque
stringData:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio-data
namespace: daft-ray
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 4Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
namespace: daft-ray
spec:
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
spec:
containers:
- name: minio
image: minio/minio:RELEASE.2025-07-23T15-54-02Z
args: [server, /data, --console-address, ":9001"]
envFrom:
- secretRef:
name: minio-credentials
ports:
- containerPort: 9000
name: api
- containerPort: 9001
name: console
resources:
requests:
cpu: 250m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: minio-data
---
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: daft-ray
spec:
selector:
app: minio
ports:
- name: api
port: 9000
targetPort: api
- name: console
port: 9001
targetPort: console
---
# 创建 benchmark bucket;Job 可安全重复执行,现有 bucket 不会被删除或清空。
apiVersion: batch/v1
kind: Job
metadata:
name: minio-init
namespace: daft-ray
spec:
backoffLimit: 20
template:
spec:
restartPolicy: OnFailure
containers:
- name: mc
image: minio/mc:RELEASE.2025-07-21T05-28-08Z
envFrom:
- secretRef:
name: minio-credentials
command: [/bin/sh, -ec]
args:
- >-
until mc alias set local http://minio:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD";
do sleep 2; done;
mc mb --ignore-existing local/benchmark
44 changes: 44 additions & 0 deletions deploy/k8s/mock-llm.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 使用项目内 OpenAI-compatible Mock LLM,避免本地 E2E 依赖外部模型服务或密钥。
apiVersion: apps/v1
kind: Deployment
metadata:
name: mock-llm
namespace: daft-ray
spec:
replicas: 1
selector:
matchLabels:
app: mock-llm
template:
metadata:
labels:
app: mock-llm
spec:
containers:
- name: mock-llm
image: multimodal-toolkit:audio-local
imagePullPolicy: IfNotPresent
command: [python, -m, benchmark.audio, serve-mock, --host, 0.0.0.0, --port, "8010", --profile, fast]
ports:
- containerPort: 8010
name: http
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
---
apiVersion: v1
kind: Service
metadata:
name: mock-llm
namespace: daft-ray
spec:
selector:
app: mock-llm
ports:
- name: http
port: 8010
targetPort: http
5 changes: 5 additions & 0 deletions deploy/k8s/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# 本地 Daft/Ray 演示环境的所有资源都隔离在此命名空间,便于一键清理。
apiVersion: v1
kind: Namespace
metadata:
name: daft-ray
113 changes: 113 additions & 0 deletions deploy/k8s/prometheus.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# 轻量、单副本 Prometheus:只保存本地 Ray 集群的指标,避免本地开发机负担过重。
apiVersion: v1
kind: ConfigMap
metadata:
name: prometheus-config
namespace: daft-ray
data:
prometheus.yml: |
global:
scrape_interval: 5s
evaluation_interval: 5s
scrape_configs:
- job_name: ray
kubernetes_sd_configs:
# Pod discovery 配合 Ray 节点标签,避免 Endpoints 元数据在不同
# Prometheus/Kubernetes 版本间的端口标签差异。
- role: pod
namespaces:
names: [daft-ray]
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_ray_io_is_ray_node]
action: keep
# KubeRay 将 ray.io/is-ray-node 标签的值设为 "yes"。
regex: "yes"
- source_labels: [__meta_kubernetes_pod_container_port_name]
action: keep
regex: metrics
---
# Prometheus 通过 Kubernetes 服务发现读取 Service、Endpoints 和 Pod;默认账号没有
# 这些读取权限,会导致 UI 中没有任何 active target。
apiVersion: v1
kind: ServiceAccount
metadata:
name: prometheus
namespace: daft-ray
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: prometheus-discovery
namespace: daft-ray
rules:
- apiGroups: [""]
resources: ["services", "endpoints", "pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: prometheus-discovery
namespace: daft-ray
subjects:
- kind: ServiceAccount
name: prometheus
namespace: daft-ray
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: prometheus-discovery
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus
namespace: daft-ray
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
template:
metadata:
labels:
app: prometheus
spec:
serviceAccountName: prometheus
containers:
- name: prometheus
image: prom/prometheus:v3.5.0
args:
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention.time=2d
ports:
- containerPort: 9090
name: web
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 500m
memory: 512Mi
volumeMounts:
- name: config
mountPath: /etc/prometheus
volumes:
- name: config
configMap:
name: prometheus-config
---
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: daft-ray
spec:
selector:
app: prometheus
ports:
- name: web
port: 9090
targetPort: web
15 changes: 15 additions & 0 deletions deploy/k8s/ray-metrics-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Prometheus 集群内抓取所有 Ray Pod 的 OpenMetrics endpoint(head 与 worker)。
apiVersion: v1
kind: Service
metadata:
name: daft-ray-metrics
namespace: daft-ray
labels:
app.kubernetes.io/name: daft-ray
spec:
selector:
ray.io/cluster: daft-ray
ports:
- name: metrics
port: 8080
targetPort: 8080
Loading
Loading