This commit is contained in:
董宏凯 committed 2026-06-01 17:33:40 +08:00
1 parent 8adb4d5cef
commit e9c56f8635
6 files changed
+277 -10

No files matched your search

+34
View File
@@ -0,0 +1,34 @@
# Docker build context exclusions (context = offline-backend-20260512/)
.env
.env.*
!.env.example
# Runtime / local data — must not enter the image
data/
backend/.deer-flow/
backend/.venv/
backend/venv/
# Python caches
backend/**/__pycache__/
backend/**/*.py[cod]
backend/.pytest_cache/
backend/.ruff_cache/
backend/.coverage
backend/htmlcov/
# IDE / OS
.git/
.idea/
.vscode/
.DS_Store
Thumbs.db
# Docs and tests (not needed in runtime image)
backend/tests/
backend/docs/
*.md
!backend/README.md
# Large optional trees (uncomment if build context is too slow)
# backend/knowledge/
+3
View File
@@ -0,0 +1,3 @@
# Shell scripts must use LF so Linux/bash can run them directly.
*.sh text eol=lf
scripts/*.sh text eol=lf
@@ -184,17 +184,36 @@ curl -s http://127.0.0.1:8001/openapi.json | head
HOST_PORT=9001 bash deploy.sh up
```
### 5.2 离线镜像模式(无法构建时)
### 5.2 离线镜像模式(内网/无法访问 docker.io 时必用)
若已提供预构建镜像 tar:
**典型失败日志:**
```text
failed to fetch anonymous token: Get "https://auth.docker.io/token...": i/o timeout
```
说明服务器访问不了 Docker Hub,**不能在线 build**,必须用离线 tar。
**步骤 1:在有公网的机器上导出镜像**
```bash
bash scripts/export-offline-image.sh
# 产出: deerflow-backend-offline-image-20260505.tar
```
**步骤 2:上传到内网服务器部署目录**
**步骤 3:内网服务器加载并启动**
```bash
DEPLOY_MODE=offline \
IMAGE_TAR=./deerflow-backend-offline-image-20260505.tar \
IMAGE_TAG=deerflow-backend-offline:20260505 \
DEPLOY_MODE=offline \
bash deploy.sh up
```
若 tar 已在目录中,`deploy.sh` 会**自动检测**并切换为离线模式。
或分步:
```bash
@@ -202,6 +221,25 @@ bash deploy.sh load
IMAGE_TAG=deerflow-backend-offline:20260505 bash deploy.sh up
```
### 5.3 配置 Docker 镜像加速(可选,有内网镜像源时)
编辑 `/etc/docker/daemon.json`:
```json
{
"registry-mirrors": [
"https://你的内网或国内镜像地址"
],
"dns": ["114.114.114.114", "223.5.5.5"]
}
```
```bash
sudo systemctl daemon-reload
sudo systemctl restart docker
bash deploy.sh build
```
---
## 6. 前端对接
@@ -268,6 +306,73 @@ bash deploy.sh restart
## 10. 故障排查
### 10.0 Docker 未运行或无权限
报错示例:
```text
[FAIL] Docker 未运行或无权限...
```
**第一步:看具体原因**
```bash
docker info
```
**情况 A:permission denied(最常见)**
说明当前用户不在 `docker` 组:
```bash
# 把当前用户加入 docker 组
sudo usermod -aG docker "$USER"
# 让组权限立即生效(或重新 SSH 登录)
newgrp docker
# 验证
docker info
docker ps
# 再部署
bash deploy.sh
```
若仍不行,确认 socket 权限:
```bash
ls -l /var/run/docker.sock
# 期望类似: srw-rw---- root docker ...
```
**情况 B:Cannot connect to the Docker daemon**
说明 Docker 服务没起来:
```bash
sudo systemctl status docker
sudo systemctl start docker
sudo systemctl enable docker
docker info
bash deploy.sh
```
**情况 C:未安装 Docker**
```bash
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker "$USER"
newgrp docker
bash deploy.sh
```
**临时绕过(仅测试用,不推荐生产)**
```bash
sudo bash deploy.sh
```
### 10.1 容器启动后立即退出
```bash
@@ -281,6 +386,37 @@ bash deploy.sh logs
- MySQL 连接失败(检查 `MYSQL_DATABASE_URL` 是否用了容器可达的 IP)
- 缺少 `asyncmy`(MySQL 模式):确认 `backend/wheelhouse` 中有对应 wheel,或配置 Python 私服
### 10.1.1 脚本报 `$'\r': command not found` 或 `pipefail: invalid option`
这是 **Windows 换行符(CRLF)** 导致的。在 Linux 上执行:
```bash
sed -i 's/\r$//' deploy.sh start_backend.sh stop_backend.sh scripts/container-entrypoint.sh switch_database_to_mysql.sh
chmod +x deploy.sh start_backend.sh stop_backend.sh scripts/container-entrypoint.sh
bash deploy.sh
```
或在 Windows 上传前,用 VS Code 右下角把文件编码改为 **LF**。
### 10.1.2 构建时报 `.venv cannot be used because it is not a valid Python environment`
原因:开发机(尤其 Windows)上的 `backend/.venv` 被打包进 Docker 构建上下文,Linux 容器内无法使用。
解决:仓库已添加 `.dockerignore` 排除 `.venv`。更新代码后重新构建:
```bash
bash deploy.sh build
# 或
bash deploy.sh up
```
若仍报错,手动删除后再构建:
```bash
rm -rf backend/.venv
bash deploy.sh build
```
### 10.2 健康检查失败
```bash
+2 -1
View File
@@ -47,8 +47,9 @@ COPY backend ./backend
# Install dependencies with cache mount
# When UV_EXTRAS is set (e.g. "postgres"), installs optional dependencies.
# Remove host .venv if present — Windows/macOS venvs break Linux container builds.
RUN --mount=type=cache,target=/root/.cache/uv \
sh -c "cd backend && UV_INDEX_URL=${UV_INDEX_URL:-https://pypi.org/simple} uv sync ${UV_EXTRAS:+--extra $UV_EXTRAS}"
sh -c "cd backend && rm -rf .venv && UV_INDEX_URL=${UV_INDEX_URL:-https://pypi.org/simple} uv sync ${UV_EXTRAS:+--extra $UV_EXTRAS}"
# ── Stage 2: Dev ──────────────────────────────────────────────────────────────
# Retains compiler toolchain from builder so startup-time `uv sync` can build
+74 -6
View File
@@ -37,6 +37,32 @@ IMAGE_TAR="${IMAGE_TAR:-${SCRIPT_DIR}/deerflow-backend-offline-image.tar}"
DEPLOY_MODE="${DEPLOY_MODE:-build}"
SKIP_BUILD="${SKIP_BUILD:-0}"
resolve_offline_tar() {
if [ -f "${IMAGE_TAR}" ]; then
return 0
fi
local candidate
for candidate in \
"${SCRIPT_DIR}/deerflow-backend-offline-image-20260505.tar" \
"${SCRIPT_DIR}/deerflow-backend-offline-image.tar"; do
if [ -f "${candidate}" ]; then
IMAGE_TAR="${candidate}"
return 0
fi
done
return 1
}
# 内网服务器通常无法访问 docker.io,检测到离线 tar 时自动切换模式
resolve_offline_tar || true
if [ -f "${IMAGE_TAR}" ] && [ "${DEPLOY_MODE}" = "build" ]; then
DEPLOY_MODE="offline"
if [ "${IMAGE_TAG}" = "deerflow-backend:latest" ]; then
IMAGE_TAG="deerflow-backend-offline:20260505"
fi
warn "检测到离线镜像 ${IMAGE_TAR},已自动切换为 DEPLOY_MODE=offline"
fi
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
@@ -92,7 +118,28 @@ ensure_env() {
check_docker() {
require_cmd docker
docker info >/dev/null 2>&1 || fail "Docker 未运行或无权限,请确认 docker 服务已启动且当前用户在 docker 组"
local docker_err
docker_err="$(docker info 2>&1)" || {
echo "${docker_err}" >&2
echo >&2
if echo "${docker_err}" | grep -qiE 'permission denied|connect: permission'; then
fail "当前用户无权访问 Docker。请执行:
sudo usermod -aG docker \$USER
newgrp docker
然后重新运行: bash deploy.sh
临时方案(不推荐长期使用): sudo bash deploy.sh"
fi
if echo "${docker_err}" | grep -qiE 'cannot connect|Is the docker daemon running|connection refused'; then
fail "Docker 服务未运行。请先启动:
sudo systemctl start docker
sudo systemctl enable docker
然后重新运行: bash deploy.sh"
fi
fail "Docker 不可用。请先在服务器上执行 docker info 查看详细错误"
}
docker compose version >/dev/null 2>&1 || fail "未找到 docker compose,请安装 Docker Compose V2"
}
@@ -122,16 +169,33 @@ load_offline_image() {
ok "镜像已存在: ${IMAGE_TAG}"
return 0
fi
[ -f "${IMAGE_TAR}" ] || fail "离线镜像不存在: ${IMAGE_TAR},请设置 IMAGE_TAR 或先执行 bash deploy.sh build"
[ -f "${IMAGE_TAR}" ] || fail "离线镜像不存在: ${IMAGE_TAR}
内网服务器无法访问 docker.io 时,请:
1. 在有公网的机器上执行 bash scripts/export-offline-image.sh 导出 tar
2. 上传到本目录后执行: DEPLOY_MODE=offline bash deploy.sh up"
info "加载离线镜像: ${IMAGE_TAR}"
docker load -i "${IMAGE_TAR}"
local load_out loaded_tag
load_out="$(docker load -i "${IMAGE_TAR}")"
echo "${load_out}"
loaded_tag="$(echo "${load_out}" | sed -n 's/^Loaded image: //p' | tail -n1)"
if [ -n "${loaded_tag}" ] && ! docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1; then
info "将 ${loaded_tag} 标记为 ${IMAGE_TAG}"
docker tag "${loaded_tag}" "${IMAGE_TAG}"
fi
docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1 || fail "镜像加载后未找到 ${IMAGE_TAG},请检查 IMAGE_TAG 是否与 tar 内标签一致"
ok "镜像加载完成: ${IMAGE_TAG}"
}
build_image() {
info "构建 Docker 镜像: ${IMAGE_TAG}"
info "构建 Docker 镜像: ${IMAGE_TAG}(需要能访问 docker.io / ghcr.io)"
export IMAGE_TAG
"${COMPOSE[@]}" build gateway
if ! "${COMPOSE[@]}" build gateway; then
fail "在线构建失败。内网服务器请改用离线镜像:
DEPLOY_MODE=offline IMAGE_TAR=./deerflow-backend-offline-image-20260505.tar bash deploy.sh up
或在 /etc/docker/daemon.json 配置 registry-mirrors 后重试。"
fi
ok "镜像构建完成"
}
@@ -184,7 +248,11 @@ cmd_up() {
fi
info "启动容器..."
"${COMPOSE[@]}" up -d --remove-orphans
if [ "${DEPLOY_MODE}" = "offline" ] || docker image inspect "${IMAGE_TAG}" >/dev/null 2>&1; then
"${COMPOSE[@]}" up -d --no-build --remove-orphans
else
"${COMPOSE[@]}" up -d --remove-orphans
fi
ok "容器已启动: ${CONTAINER_NAME}"
echo
echo " API 地址: http://$(hostname -I 2>/dev/null | awk '{print $1}' || echo 127.0.0.1):${HOST_PORT}"
@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# 在有公网的环境构建并导出离线镜像 tar,供内网服务器 docker load 使用。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${SCRIPT_DIR}"
IMAGE_TAG="${IMAGE_TAG:-deerflow-backend-offline:20260505}"
OUTPUT="${OUTPUT:-${SCRIPT_DIR}/deerflow-backend-offline-image-20260505.tar}"
echo "构建镜像: ${IMAGE_TAG}"
IMAGE_TAG="${IMAGE_TAG}" docker compose -p deerflow-backend -f docker-compose.yaml build gateway
loaded="$(docker images --format '{{.Repository}}:{{.Tag}}' | grep -F "${IMAGE_TAG}" | head -n1 || true)"
if [ -z "${loaded}" ]; then
# compose build 可能产出 deerflow-backend:latest,再打离线标签
docker tag deerflow-backend:latest "${IMAGE_TAG}" 2>/dev/null || true
fi
echo "导出到: ${OUTPUT}"
docker save -o "${OUTPUT}" "${IMAGE_TAG}"
ls -lh "${OUTPUT}"
echo
echo "上传到内网服务器后执行:"
echo " DEPLOY_MODE=offline IMAGE_TAR=${OUTPUT##*/} IMAGE_TAG=${IMAGE_TAG} bash deploy.sh up"