部署
This commit is contained in:
1 parent
8adb4d5cef
commit
e9c56f8635
6 files changed
+277
-10
No files matched your search
@@ -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/
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
Reference in new issue
Block a user