mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-05-21 15:36:48 +00:00
Merge branch 'main' into release/2.0-rc
This commit is contained in:
@@ -36,6 +36,11 @@ services:
|
||||
# export DEER_FLOW_ROOT=/absolute/path/to/deer-flow
|
||||
- SKILLS_HOST_PATH=${DEER_FLOW_ROOT}/skills
|
||||
- THREADS_HOST_PATH=${DEER_FLOW_ROOT}/backend/.deer-flow/threads
|
||||
# Production: use PVC instead of hostPath to avoid data loss on node failure.
|
||||
# When set, hostPath vars above are ignored for the corresponding volume.
|
||||
# USERDATA_PVC_NAME uses subPath (threads/{thread_id}/user-data) automatically.
|
||||
# - SKILLS_PVC_NAME=deer-flow-skills-pvc
|
||||
# - USERDATA_PVC_NAME=deer-flow-userdata-pvc
|
||||
- KUBECONFIG_PATH=/root/.kube/config
|
||||
- NODE_HOST=host.docker.internal
|
||||
# Override K8S API server URL since kubeconfig uses 127.0.0.1
|
||||
@@ -69,10 +74,15 @@ services:
|
||||
environment:
|
||||
- LANGGRAPH_UPSTREAM=${LANGGRAPH_UPSTREAM:-langgraph:2024}
|
||||
- LANGGRAPH_REWRITE=${LANGGRAPH_REWRITE:-/}
|
||||
command: >
|
||||
sh -c "envsubst '$$LANGGRAPH_UPSTREAM $$LANGGRAPH_REWRITE'
|
||||
< /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
&& nginx -g 'daemon off;'"
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
envsubst '$$LANGGRAPH_UPSTREAM $$LANGGRAPH_REWRITE' \
|
||||
< /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
test -e /proc/net/if_inet6 || sed -i '/^[[:space:]]*listen[[:space:]]\+\[::\]:2026;/d' /etc/nginx/nginx.conf
|
||||
exec nginx -g 'daemon off;'
|
||||
depends_on:
|
||||
- frontend
|
||||
- gateway
|
||||
@@ -133,8 +143,10 @@ services:
|
||||
- ../extensions_config.json:/app/extensions_config.json
|
||||
- ../skills:/app/skills
|
||||
- ../logs:/app/logs
|
||||
# Mount uv cache for faster dependency installation
|
||||
- ~/.cache/uv:/root/.cache/uv
|
||||
# Use a Docker-managed uv cache volume instead of a host bind mount.
|
||||
# On macOS/Docker Desktop, uv may fail to create symlinks inside shared
|
||||
# host directories, which causes startup-time `uv sync` to crash.
|
||||
- gateway-uv-cache:/root/.cache/uv
|
||||
# DooD: same as gateway — AioSandboxProvider runs inside LangGraph process.
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# CLI auth directories for auto-auth (Claude Code + Codex CLI)
|
||||
@@ -190,8 +202,10 @@ services:
|
||||
- ../extensions_config.json:/app/extensions_config.json
|
||||
- ../skills:/app/skills
|
||||
- ../logs:/app/logs
|
||||
# Mount uv cache for faster dependency installation
|
||||
- ~/.cache/uv:/root/.cache/uv
|
||||
# Use a Docker-managed uv cache volume instead of a host bind mount.
|
||||
# On macOS/Docker Desktop, uv may fail to create symlinks inside shared
|
||||
# host directories, which causes startup-time `uv sync` to crash.
|
||||
- langgraph-uv-cache:/root/.cache/uv
|
||||
# DooD: same as gateway — AioSandboxProvider runs inside LangGraph process.
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
# CLI auth directories for auto-auth (Claude Code + Codex CLI)
|
||||
@@ -228,6 +242,8 @@ volumes:
|
||||
# image build are not shadowed by the host backend/ directory mount.
|
||||
gateway-venv:
|
||||
langgraph-venv:
|
||||
gateway-uv-cache:
|
||||
langgraph-uv-cache:
|
||||
|
||||
networks:
|
||||
deer-flow-dev:
|
||||
|
||||
@@ -137,6 +137,8 @@ The provisioner is configured via environment variables (set in [docker-compose-
|
||||
| `SANDBOX_IMAGE` | `enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest` | Container image for sandbox Pods |
|
||||
| `SKILLS_HOST_PATH` | - | **Host machine** path to skills directory (must be absolute) |
|
||||
| `THREADS_HOST_PATH` | - | **Host machine** path to threads data directory (must be absolute) |
|
||||
| `SKILLS_PVC_NAME` | empty (use hostPath) | PVC name for skills volume; when set, sandbox Pods use PVC instead of hostPath |
|
||||
| `USERDATA_PVC_NAME` | empty (use hostPath) | PVC name for user-data volume; when set, uses PVC with `subPath: threads/{thread_id}/user-data` |
|
||||
| `KUBECONFIG_PATH` | `/root/.kube/config` | Path to kubeconfig **inside** the provisioner container |
|
||||
| `NODE_HOST` | `host.docker.internal` | Hostname that backend containers use to reach host NodePorts |
|
||||
| `K8S_API_SERVER` | (from kubeconfig) | Override K8s API server URL (e.g., `https://host.docker.internal:26443`) |
|
||||
@@ -309,7 +311,7 @@ docker exec deer-flow-gateway curl -s $SANDBOX_URL/v1/sandbox
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **HostPath Volumes**: The provisioner mounts host directories into sandbox Pods. Ensure these paths contain only trusted data.
|
||||
1. **HostPath Volumes**: The provisioner mounts host directories into sandbox Pods by default. Ensure these paths contain only trusted data. For production, prefer PVC-based volumes (set `SKILLS_PVC_NAME` and `USERDATA_PVC_NAME`) to avoid node-specific data loss risks.
|
||||
|
||||
2. **Resource Limits**: Each sandbox Pod has CPU, memory, and storage limits to prevent resource exhaustion.
|
||||
|
||||
@@ -322,7 +324,7 @@ docker exec deer-flow-gateway curl -s $SANDBOX_URL/v1/sandbox
|
||||
## Future Enhancements
|
||||
|
||||
- [ ] Support for custom resource requests/limits per sandbox
|
||||
- [ ] PersistentVolume support for larger data requirements
|
||||
- [x] PersistentVolume support for larger data requirements
|
||||
- [ ] Automatic cleanup of stale sandboxes (timeout-based)
|
||||
- [ ] Metrics and monitoring (Prometheus integration)
|
||||
- [ ] Multi-cluster support (route to different K8s clusters)
|
||||
|
||||
+62
-28
@@ -60,6 +60,8 @@ SANDBOX_IMAGE = os.environ.get(
|
||||
)
|
||||
SKILLS_HOST_PATH = os.environ.get("SKILLS_HOST_PATH", "/skills")
|
||||
THREADS_HOST_PATH = os.environ.get("THREADS_HOST_PATH", "/.deer-flow/threads")
|
||||
SKILLS_PVC_NAME = os.environ.get("SKILLS_PVC_NAME", "")
|
||||
USERDATA_PVC_NAME = os.environ.get("USERDATA_PVC_NAME", "")
|
||||
SAFE_THREAD_ID_PATTERN = r"^[A-Za-z0-9_\-]+$"
|
||||
|
||||
# Path to the kubeconfig *inside* the provisioner container.
|
||||
@@ -243,6 +245,64 @@ def _sandbox_url(node_port: int) -> str:
|
||||
return f"http://{NODE_HOST}:{node_port}"
|
||||
|
||||
|
||||
def _build_volumes(thread_id: str) -> list[k8s_client.V1Volume]:
|
||||
"""Build volume list: PVC when configured, otherwise hostPath."""
|
||||
if SKILLS_PVC_NAME:
|
||||
skills_vol = k8s_client.V1Volume(
|
||||
name="skills",
|
||||
persistent_volume_claim=k8s_client.V1PersistentVolumeClaimVolumeSource(
|
||||
claim_name=SKILLS_PVC_NAME,
|
||||
read_only=True,
|
||||
),
|
||||
)
|
||||
else:
|
||||
skills_vol = k8s_client.V1Volume(
|
||||
name="skills",
|
||||
host_path=k8s_client.V1HostPathVolumeSource(
|
||||
path=SKILLS_HOST_PATH,
|
||||
type="Directory",
|
||||
),
|
||||
)
|
||||
|
||||
if USERDATA_PVC_NAME:
|
||||
userdata_vol = k8s_client.V1Volume(
|
||||
name="user-data",
|
||||
persistent_volume_claim=k8s_client.V1PersistentVolumeClaimVolumeSource(
|
||||
claim_name=USERDATA_PVC_NAME,
|
||||
),
|
||||
)
|
||||
else:
|
||||
userdata_vol = k8s_client.V1Volume(
|
||||
name="user-data",
|
||||
host_path=k8s_client.V1HostPathVolumeSource(
|
||||
path=join_host_path(THREADS_HOST_PATH, thread_id, "user-data"),
|
||||
type="DirectoryOrCreate",
|
||||
),
|
||||
)
|
||||
|
||||
return [skills_vol, userdata_vol]
|
||||
|
||||
|
||||
def _build_volume_mounts(thread_id: str) -> list[k8s_client.V1VolumeMount]:
|
||||
"""Build volume mount list, using subPath for PVC user-data."""
|
||||
userdata_mount = k8s_client.V1VolumeMount(
|
||||
name="user-data",
|
||||
mount_path="/mnt/user-data",
|
||||
read_only=False,
|
||||
)
|
||||
if USERDATA_PVC_NAME:
|
||||
userdata_mount.sub_path = f"threads/{thread_id}/user-data"
|
||||
|
||||
return [
|
||||
k8s_client.V1VolumeMount(
|
||||
name="skills",
|
||||
mount_path="/mnt/skills",
|
||||
read_only=True,
|
||||
),
|
||||
userdata_mount,
|
||||
]
|
||||
|
||||
|
||||
def _build_pod(sandbox_id: str, thread_id: str) -> k8s_client.V1Pod:
|
||||
"""Construct a Pod manifest for a single sandbox."""
|
||||
thread_id = _validate_thread_id(thread_id)
|
||||
@@ -302,40 +362,14 @@ def _build_pod(sandbox_id: str, thread_id: str) -> k8s_client.V1Pod:
|
||||
"ephemeral-storage": "500Mi",
|
||||
},
|
||||
),
|
||||
volume_mounts=[
|
||||
k8s_client.V1VolumeMount(
|
||||
name="skills",
|
||||
mount_path="/mnt/skills",
|
||||
read_only=True,
|
||||
),
|
||||
k8s_client.V1VolumeMount(
|
||||
name="user-data",
|
||||
mount_path="/mnt/user-data",
|
||||
read_only=False,
|
||||
),
|
||||
],
|
||||
volume_mounts=_build_volume_mounts(thread_id),
|
||||
security_context=k8s_client.V1SecurityContext(
|
||||
privileged=False,
|
||||
allow_privilege_escalation=True,
|
||||
),
|
||||
)
|
||||
],
|
||||
volumes=[
|
||||
k8s_client.V1Volume(
|
||||
name="skills",
|
||||
host_path=k8s_client.V1HostPathVolumeSource(
|
||||
path=SKILLS_HOST_PATH,
|
||||
type="Directory",
|
||||
),
|
||||
),
|
||||
k8s_client.V1Volume(
|
||||
name="user-data",
|
||||
host_path=k8s_client.V1HostPathVolumeSource(
|
||||
path=join_host_path(THREADS_HOST_PATH, thread_id, "user-data"),
|
||||
type="DirectoryOrCreate",
|
||||
),
|
||||
),
|
||||
],
|
||||
volumes=_build_volumes(thread_id),
|
||||
restart_policy="Always",
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user