机器准备

本机安装时相关配置

  • CPU:N100

  • RAM:32GB

系统

  • Ubuntu

安装K8s

禁用Swap

sudo swapoff -a
sudo sed -i '/[[:space:]]swap[[:space:]]/d' /etc/fstab
free -h

确认 Swap 行为 0B即可

加载内核模块并配置 Sysctl

cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
overlay
br_netfilter
EOF

sudo modprobe overlay
sudo modprobe br_netfilter

cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF

sudo sysctl --system

安装并配置containerd

sudo apt update && sudo apt install -y containerd

编辑/etc/containerd/config.toml

如果没生成的话执行

sudo containerd config default | sudo tee /etc/containerd/config.toml

找到 SystemdCgroup 并设为 true

sudo nano /etc/containerd/config.toml

找到

SystemdCgroup = false

改为

SystemdCgroup = true

重启

sudo systemctl restart containerd && sudo systemctl enable containerd

看到 active (running) 即可

安装kubeadm/kubelet/kubectl

安装组件

sudo apt install -y apt-transport-https ca-certificates curl gpg

curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | \
  sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] \
  https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /" | \
  sudo tee /etc/apt/sources.list.d/kubernetes.list

# 确保 apt 源配置指向正确的仓库
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

# 修正权限,确保 _apt 用户可读取密钥文件
sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list

# 更新并安装
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

验证安装

kubeadm version
kubelet --version
kubectl version --client

拉取镜像

sudo kubeadm config images pull \
  --image-repository registry.aliyuncs.com/google_containers

初始化集群

sudo kubeadm init \
  --image-repository registry.aliyuncs.com/google_containers \
  --pod-network-cidr=10.244.0.0/16

配置kubectl

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 验证
kubectl get nodes
kubectl get pods -A
# 应显示节点,状态为 NotReady

创建RBAC绑定

sudo kubectl --kubeconfig /etc/kubernetes/super-admin.conf create clusterrolebinding kubeadm:cluster-admins \
  --clusterrole=cluster-admin \
  --group=kubeadm:cluster-admins

sudo kubectl --kubeconfig /etc/kubernetes/super-admin.conf label node <node_name> \
  node-role.kubernetes.io/control-plane="" \
  node.kubernetes.io/exclude-from-external-load-balancers=""

# 验证普通 kubectl 权限
kubectl get nodes

解除控制平面taint(单节点必须,否则业务 Pod 无法调度)

kubectl taint nodes --all node-role.kubernetes.io/control-plane-
# 如果报错 "taint not found",说明已移除过,可忽略

安装CNI插件

kubectl apply -f https://cdn.jsdelivr.net/gh/flannel-io/flannel@master/Documentation/kube-flannel.yml

观察节点状态,STATUS 从 NotReady 变为 Ready 则成功

kubectl get nodes

查看Flannel主容器日志

FLANNEL_POD=$(kubectl get pods -n kube-flannel -l app=flannel -o jsonpath='{.items[0].metadata.name}')
kubectl logs -n kube-flannel $FLANNEL_POD -c kube-flannel --tail=50

创建空间

kubectl create namespace deer-flow

配置密钥

这里使用DeepSeek做演示

kubectl create secret generic deer-flow-deepseek-secret \
  --from-literal=DEEPSEEK_API_KEY='sk-...' \
  -n deer-flow

拉取DeerFlow仓库

cd /opt
git clone https://github.com/bytedance/deer-flow.git
cd deer-flow

正常来说,DeerFlow 2.0的基础能力已经足够强大,但我们依旧会选择把DeerFlow的目录进行挂载出来,为未来的二开做相应准备

sudo mkdir -p /data/deerflow/backend/app
sudo mkdir -p /data/deerflow/frontend
sudo mkdir -p /data/deerflow/skills
sudo mkdir -p /data/deerflow/.deer-flow/deer-flow/data
sudo mkdir -p /data/deerflow/.deer-flow/deer-flow/extensions-config
sudo touch /data/deerflow/config.yaml

# 后端代码
sudo rsync -av /opt/deer-flow/backend/app/ /data/deerflow/backend/app/

# 前端源码
sudo rsync -av /opt/deer-flow/frontend/ /data/deerflow/frontend/

# skills
sudo rsync -av /opt/deer-flow/skills/ /data/deerflow/skills/ 2>/dev/null

# 配置文件
sudo cp /opt/deer-flow/config.example.yaml /data/deerflow/config.yaml

sudo chown -R 1000:1000 /data/deerflow/backend/app
sudo chown -R 1000:1000 /data/deerflow/frontend
sudo chown -R 1000:1000 /data/deerflow/skills
sudo chown -R 1000:1000 /data/deerflow/.deer-flow
sudo chown 1000:1000 /data/deerflow/config.yaml
sudo chmod 644 /data/deerflow/config.yaml

# 验证
ls -ld /data/deerflow/backend/app /data/deerflow/frontend /data/deerflow/skills /data/deerflow/.deer-flow /data/deerflow/config.yaml

构建Node.js

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs
sudo npm install -g pnpm@10

cd /data/deerflow/frontend
pnpm install
pnpm build

# 验证
ls -la .next/

配置config.yaml

找到models 模块

  - name: deepseek
    display_name: DeepSeek
    use: deerflow.models.patched_deepseek:PatchedChatDeepSeek
    model: deepseek-flash
    api_base: https://api.deepseek.com/v1
    api_key: $DEEPSEEK_API_KEY
    timeout: 600.0
    max_retries: 2
    max_tokens: 8192
    context_window: 1000000
    supports_thinking: true
    supports_vision: true
    when_thinking_enabled:
      extra_body:
        thinking:
          type: enabled
    when_thinking_disabled:
      extra_body:
        thinking:
          type: disabled

找到database 模块

将

database:
  backend: sqlite
  sqlite_dir: .deer-flow/data

改成

database:
  backend: postgres
  postgres_url: $DATABASE_URL

安装Helm

拉取Helm

curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 验证
helm version

编辑values文件

这里用my-values.yaml做演示

image:
  registry: ghcr.io/bytedance
  tag: "latest"
  pullPolicy: IfNotPresent

existingSecret: deer-flow-deepseek-secret

provisioner:
  env:
    - name: SANDBOX_IMAGE
      value: "enterprise-public-cn-beijing.cr.volces.com/vefaas-public/all-in-one-sandbox:latest"
    - name: K8S_NAMESPACE
      value: "deer-flow"
    - name: SANDBOX_SERVICE_TYPE
      value: "ClusterIP"

gateway:
  replicas: 1

scheduler:
  multi_instance: true
run_ownership:
  heartbeat_enabled: true
run_events:
  backend: db

postgresql:
  enabled: true
  auth:
    database: deerflow
    username: deerflow
    password: <pgsql-密码>

nginx:
  service:
    type: NodePort
    port: 2026
    nodePort: <自定义端口> # 必须在 30000-32767

config: |
  config_version: 33
  sandbox:
    use: deerflow.community.aio_sandbox:AioSandboxProvider
    provisioner_url: http://provisioner:8002
  database:
    backend: postgres
    postgres_url: $DATABASE_URL
  run_events:
    backend: db

二开模板改造

创建目录

mkdir -p /opt/deer-flow-deploy
cd /opt/deer-flow-deploy

创建kustomization.yaml

cat > /opt/deer-flow-deploy/kustomization.yaml <<'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - helm-rendered.yaml

patchesJson6902:
  # ==================== gateway ====================
  - target:
      kind: Deployment
      name: deer-flow-deer-flow-gateway
    patch: |-
      # --- volumes ---
      - op: replace
        path: /spec/template/spec/volumes/0
        value:
          name: config
          hostPath:
            path: /data/deerflow/config.yaml
            type: File
      # [1] extensions-seed 保持不变
      - op: replace
        path: /spec/template/spec/volumes/2
        value:
          name: skills
          hostPath:
            path: /data/deerflow/skills
            type: Directory
      - op: replace
        path: /spec/template/spec/volumes/3
        value:
          name: home
          hostPath:
            path: /data/deerflow/.deer-flow
            type: Directory
      - op: add
        path: /spec/template/spec/volumes/-
        value:
          name: code-app
          hostPath:
            path: /data/deerflow/backend/app
            type: Directory

      # --- containers[0].volumeMounts ---
      # config 去掉 subPath(hostPath 指向文件本身)
      - op: replace
        path: /spec/template/spec/containers/0/volumeMounts/0
        value:
          name: config
          mountPath: /app/backend/config.yaml
          readOnly: true
      - op: add
        path: /spec/template/spec/containers/0/volumeMounts/-
        value:
          name: code-app
          mountPath: /app/backend/app

  # ==================== frontend ====================
  - target:
      kind: Deployment
      name: deer-flow-deer-flow-frontend
    patch: |-
      - op: add
        path: /spec/template/spec/volumes/-
        value:
          name: frontend-app
          hostPath:
            path: /data/deerflow/frontend
            type: Directory
      - op: add
        path: /spec/template/spec/containers/0/volumeMounts/-
        value:
          name: frontend-app
          mountPath: /app/frontend
EOF

创建post-renderer.sh

cat > /opt/deer-flow-deploy/post-renderer.sh <<'EOF'
#!/bin/bash
set -euo pipefail

cat > /opt/deer-flow-deploy/helm-rendered.yaml

kustomize build /opt/deer-flow-deploy/
EOF

chmod +x /opt/deer-flow-deploy/post-renderer.sh

验证补丁

backend

helm template deer-flow /opt/deer-flow/deploy/helm/deer-flow/ \
  -n deer-flow -f /opt/deer-flow/my-values.yaml \
  --post-renderer /opt/deer-flow-deploy/post-renderer.sh \
  | yq 'select(.kind == "Deployment" and .metadata.name == "deer-flow-deer-flow-gateway") | {
      volumes: [.spec.template.spec.volumes[] | {name: .name, type: (keys | map(select(. != "name")) | .[0])}],
      containerMounts: .spec.template.spec.containers[0].volumeMounts
    }'

结果

volumes:
  - {name: config, type: hostPath}
  - {name: extensions-seed, type: configMap}
  - {name: skills, type: hostPath}
  - {name: home, type: hostPath}
  - {name: code-app, type: hostPath}
containerMounts:
  - {name: config, mountPath: /app/backend/config.yaml, readOnly: true}   # 无 subPath
  - {name: skills, mountPath: /app/skills, readOnly: true}
  - {name: home, mountPath: /app/backend/.deer-flow, subPath: deer-flow}
  - {name: code-app, mountPath: /app/backend/app}

frontend

helm template deer-flow /opt/deer-flow/deploy/helm/deer-flow/ \
  -n deer-flow -f /opt/deer-flow/my-values.yaml \
  --post-renderer /opt/deer-flow-deploy/post-renderer.sh \
  | yq 'select(.kind == "Deployment" and .metadata.name == "deer-flow-deer-flow-frontend") | {
      volumes: [.spec.template.spec.volumes[] | {name: .name, type: (keys | map(select(. != "name")) | .[0])}],
      containerMounts: .spec.template.spec.containers[0].volumeMounts
    }'

结果

volumes:
  - {name: next-cache, type: emptyDir}
  - {name: frontend-app, type: hostPath}
containerMounts:
  - {name: next-cache, mountPath: /app/frontend/.next/cache}
  - {name: frontend-app, mountPath: /app/frontend}

部署Helm

cd /opt/deer-flow

helm dependency update deploy/helm/deer-flow 2>/dev/null || true

helm upgrade --install deer-flow deploy/helm/deer-flow/ \
  -n deer-flow --create-namespace \
  -f my-values.yaml \
  --post-renderer /opt/deer-flow-deploy/post-renderer.sh

验证安装

访问http://localhost:2026 或者http://<ip>:<nodePort>出现相关页面即可

PgSQL映射

获取相关lable

运行

kubectl get svc deer-flow-deer-flow-postgres -n deer-flow -o jsonpath='{.spec.selector}' | python3 -m json.tool
# 得到结果
{
    "app.kubernetes.io/component": "postgres",
    "app.kubernetes.io/instance": "deer-flow",
    "app.kubernetes.io/name": "deer-flow"
}

创建NodePort Service

cat <<'EOF' | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: deer-flow-postgres-external
  namespace: deer-flow
spec:
  type: NodePort
  selector:
    app.kubernetes.io/component: postgres
    app.kubernetes.io/instance: deer-flow
    app.kubernetes.io/name: deer-flow
  ports:
    - name: postgres
      port: 5432
      targetPort: 5432
      nodePort: <自定义端口>
EOF

验证

kubectl get svc deer-flow-postgres-external -n deer-flow

输出结果为

NAME                          TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
deer-flow-postgres-external   NodePort   10.104.153.118   <none>        5432:30432/TCP   5s

即成功,接下来使用相关的数据库连接工具进行访问即可

二开后一键更新脚本

cat > /opt/deer-flow/reload.sh <<'SCRIPT_EOF'
#!/bin/bash
# ============================================================================
# DeerFlow 二开更新脚本
# ============================================================================
# 用法:
#   ./reload.sh              # 更新 gateway + frontend(后端代码/配置)
#   ./reload.sh backend      # 只更新 gateway
#   ./reload.sh frontend     # 只更新 frontend(会先 pnpm build)
#   ./reload.sh config       # 只更新 config.yaml(gateway 强制重建)
#   ./reload.sh all          # 全部(含前端 build)
# ============================================================================

set -uo pipefail
# 注意:不用 set -e,我们手动处理每个失败,保证失败时能打日志

NS="deer-flow"
GW_LABEL="app.kubernetes.io/component=gateway"
FE_LABEL="app.kubernetes.io/component=frontend"
DATA_DIR="/data/deerflow"
SECRET="deer-flow-deepseek-secret"

# 颜色
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
info()  { echo -e "${GREEN}>>>${NC} $*"; }
warn()  { echo -e "${YELLOW}!!!${NC} $*"; }
error() { echo -e "${RED}ERR${NC} $*" >&2; }

# ---------------------------------------------------------------------------
# 前置检查
# ---------------------------------------------------------------------------
preflight() {
    info "前置检查"

    # kubectl 可用
    if ! command -v kubectl >/dev/null 2>&1; then
        error "kubectl 不存在"
        exit 1
    fi

    # 命名空间存在
    if ! kubectl get ns "$NS" >/dev/null 2>&1; then
        error "命名空间 $NS 不存在"
        exit 1
    fi

    # 模型 Secret 存在
    if ! kubectl get secret "$SECRET" -n "$NS" >/dev/null 2>&1; then
        warn "Secret $SECRET 不存在,gateway 可能起不来(模型调用会失败)"
    fi

    # 挂载源目录存在
    local missing=0
    for d in "$DATA_DIR/backend/app" "$DATA_DIR/skills" "$DATA_DIR/.deer-flow" "$DATA_DIR/frontend"; do
        if [ ! -d "$d" ]; then
            error "目录不存在:$d"
            missing=1
        fi
    done
    if [ ! -f "$DATA_DIR/config.yaml" ]; then
        error "文件不存在:$DATA_DIR/config.yaml"
        missing=1
    fi
    [ "$missing" -eq 1 ] && exit 1

    info "前置检查通过"
}

# ---------------------------------------------------------------------------
# 修正属主
# ---------------------------------------------------------------------------
fix_ownership() {
    info "修正属主为 1000:1000"
    sudo chown -R 1000:1000 \
        "$DATA_DIR/backend/app" \
        "$DATA_DIR/skills" \
        "$DATA_DIR/.deer-flow" \
        "$DATA_DIR/frontend" 2>/dev/null
    sudo chown 1000:1000 "$DATA_DIR/config.yaml"
    sudo chmod 644 "$DATA_DIR/config.yaml"

    # 校验
    local uid
    uid=$(stat -c '%u' "$DATA_DIR/backend/app")
    if [ "$uid" != "1000" ]; then
        error "属主修正失败:$DATA_DIR/backend/app 属主为 $uid"
        exit 1
    fi
    info "属主修正完成"
}

# ---------------------------------------------------------------------------
# 等待 Pod Ready(带超时 + 失败 dump)
# ---------------------------------------------------------------------------
wait_deploy() {
    local label="$1" desc="$2" timeout="${3:-180}"
    info "等待 $desc 就绪(${timeout}s)"
    if kubectl rollout status deployment -n "$NS" -l "$label" --timeout="${timeout}s"; then
        info "$desc 已就绪"; return 0
    fi
    error "$desc 未就绪,诊断信息:"
    kubectl get pods -n "$NS" -l "$label"
    for pod in $(kubectl get pods -n "$NS" -l "$label" -o name); do
        echo "--- $pod 日志 ---"
        kubectl logs -n "$NS" "$pod" --tail=50 2>&1 | tail -50
        echo "--- $pod 事件 ---"
        kubectl describe -n "$NS" "$pod" 2>&1 | grep -A 10 "Events:" | tail -15
    done
    return 1
}

# ---------------------------------------------------------------------------
# 更新 backend / gateway
# ---------------------------------------------------------------------------
reload_backend() {
    info "重启 gateway deployment"
    kubectl rollout restart deployment -n "$NS" -l "$GW_LABEL"
    wait_deploy "$GW_LABEL" "gateway" 180 || return 1
    kubectl logs -n "$NS" deploy/deer-flow-deer-flow-gateway --tail=20 2>&1 || true
}

# ---------------------------------------------------------------------------
# 更新 frontend
# ---------------------------------------------------------------------------
reload_frontend() {
    if [ "${SKIP_FRONTEND_BUILD:-0}" != "1" ]; then
        info "前端构建:pnpm build"
        sudo -u '#1000' bash -c "cd $DATA_DIR/frontend && pnpm build" || {
            error "前端构建失败"; return 1
        }
    else
        warn "跳过前端构建"
    fi

    info "重启 frontend deployment"
    kubectl rollout restart deployment -n "$NS" -l "$FE_LABEL"
    wait_deploy "$FE_LABEL" "frontend" 120 || return 1
    kubectl logs -n "$NS" deploy/deer-flow-deer-flow-frontend --tail=20 2>&1 || true
}

# ---------------------------------------------------------------------------
# 主流程
# ---------------------------------------------------------------------------
main() {
    local mode="${1:-all}"
    local rc=0
    preflight
    case "$mode" in
        backend)  fix_ownership || exit 1; reload_backend || rc=1 ;;
        frontend) fix_ownership || exit 1; reload_frontend || rc=1 ;;
        config)   fix_ownership || exit 1; reload_backend || rc=1 ;;
        all)      fix_ownership || exit 1; reload_backend || rc=1; reload_frontend || rc=1 ;;
        *)        error "未知模式:$mode"; exit 1 ;;
    esac
    info "完成(退出码:$rc)"
    kubectl get pods -n "$NS"
    exit "$rc"
}

main "$@"
SCRIPT_EOF

sudo chmod +x /opt/deer-flow/reload.sh

用法

# 后端代码 / config.yaml 改动
/opt/deer-flow/reload.sh backend

# 只改前端
/opt/deer-flow/reload.sh frontend

# 前后端都改
/opt/deer-flow/reload.sh all

# 跳过后端构建
SKIP_FRONTEND_BUILD=1 /opt/deer-flow/reload.sh frontend

可能遇到问题

Q1:apt update占用apt锁

sudo apt update && sudo apt install -y containerd

Reading package lists... Done

E: Could not get lock /var/lib/apt/lists/lock. It is held by process 6329 (apt-get)

N: Be aware that removing the lock file is not a solution and may break your system.

E: Unable to lock directory /var/lib/apt/lists/

解决方案

# 查看占用 apt 锁的进程
ps -p <pid> -o pid,ppid,cmd,etime
ps -ef | grep -E 'unattended|apt.systemd' | grep -v grep

# 杀掉卡死的 apt 进程
sudo kill -9 <pid>

# 停止并禁用自动更新
sudo systemctl stop unattended-upgrades
sudo systemctl disable --now unattended-upgrades
sudo systemctl mask unattended-upgrades
sudo systemctl disable --now apt-daily.timer apt-daily-upgrade.timer
sudo systemctl mask apt-daily.service apt-daily-upgrade.service

# 清理锁并修复 dpkg
sudo rm -f /var/lib/apt/lists/lock
sudo rm -f /var/lib/dpkg/lock-frontend
sudo rm -f /var/lib/dpkg/lock
sudo rm -f /var/cache/apt/archives/lock
sudo dpkg --configure -a

Q2:curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to pkgs.k8s.io:443

解决方案

# 1. 删除不匹配的旧密钥
sudo rm -f /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# 2. 从 pkgs.k8s.io 官方源下载密钥(注意 URL 中的 v1.33)
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.33/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# 3. 验证密钥是否下载成功
sudo gpg --show-keys /etc/apt/keyrings/kubernetes-apt-keyring.gpg

# 确保 apt 源配置指向正确的仓库
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.33/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list

# 修正权限,确保 _apt 用户可读取密钥文件
sudo chmod 644 /etc/apt/keyrings/kubernetes-apt-keyring.gpg
sudo chmod 644 /etc/apt/sources.list.d/kubernetes.list

# 更新并安装
sudo apt update
sudo apt install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

Q3:rpc error: code = DeadlineExceeded desc = failed to start sandbox

解决方法

# 1. 先看当前实际版本
grep "sandbox" /etc/containerd/config.toml

# 2. 用通配替换(避免版本号写死匹配不到)
sudo sed -i "s|sandbox = 'registry.k8s.io/pause:[^']*'|sandbox = 'registry.aliyuncs.com/google_containers/pause:3.10'|" /etc/containerd/config.toml

# 3. 验证
grep -A2 'pinned_images' /etc/containerd/config.toml

# 4. 重启 containerd
sudo systemctl restart containerd

# 5. 重置 kubeadm
sudo kubeadm reset -f
sudo systemctl restart containerd kubelet
sudo crictl images | grep pause

# 6. 重新初始化集群(注意 --image-repository)
sudo kubeadm init \
  --image-repository registry.aliyuncs.com/google_containers \
  --pod-network-cidr=10.244.0.0/16

# 7. 重置后必须重走以下步骤
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

sudo kubectl --kubeconfig /etc/kubernetes/super-admin.conf create clusterrolebinding kubeadm:cluster-admins \
  --clusterrole=cluster-admin --group=kubeadm:cluster-admins

sudo kubectl --kubeconfig /etc/kubernetes/super-admin.conf label node <node_name> \
  node-role.kubernetes.io/control-plane="" \
  node.kubernetes.io/exclude-from-external-load-balancers=""

kubectl taint nodes --all node-role.kubernetes.io/control-plane-

kubectl apply -f https://cdn.jsdelivr.net/gh/flannel-io/flannel@master/Documentation/kube-flannel.yml

# 8. 确认节点 Ready
kubectl get nodes

Q4:缺少ClusterRoleBinding、kube-proxy、CoreDNS

解决方法

# 缺少ClusterRoleBinding
sudo kubectl --kubeconfig /etc/kubernetes/super-admin.conf create clusterrolebinding kubeadm:kubelet-api-admin \
  --clusterrole=system:kubelet-api-admin \
  --user=kube-apiserver-kubelet-client

# 缺少kube-proxy
sudo kubeadm init phase addon kube-proxy \
  --image-repository registry.aliyuncs.com/google_containers \
  --pod-network-cidr 10.244.0.0/16

# 缺少CoreDNS
sudo kubeadm init phase addon coredns \
  --image-repository registry.aliyuncs.com/google_containers \
  --service-cidr 10.96.0.0/12 \
  --pod-network-cidr 10.244.0.0/16 \
  --service-dns-domain cluster.local

Q5:Failed to check br_netfilter: stat /proc/sys/net/bridge/bridge-nf-call-iptables: no such file or directory

解决方法

# 加载模块
sudo modprobe br_netfilter

# 设置 sysctl 参数
sudo sysctl net.bridge.bridge-nf-call-iptables=1

# 验证模块已加载
lsmod | grep br_netfilter
# 应该能看到 br_netfilter 模块

# 确保系统重启后自动加载
cat /etc/modules-load.d/k8s.conf 2>/dev/null
echo -e "overlay\nbr_netfilter" | sudo tee /etc/modules-load.d/k8s.conf

# 持久化 sysctl 参数
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system

# 重启 Flannel
kubectl delete pod -n kube-flannel -l app=flannel

Q6:PgSQL在value.yaml设置密码和容器内密码不一致

chart首次初始化postgres时,若 postgresql.auth.password未被正确识别,会生成随机密码,导致数据库密码与 Secret、values 不一致

解决方法

# 1. 看实际密码
kubectl exec -n deer-flow deer-flow-deer-flow-postgres-0 -- env | grep POSTGRES_PASSWORD

# 2. 数据库里改成 values 中的密码
kubectl exec -n deer-flow deer-flow-deer-flow-postgres-0 -- \
  env PGPASSWORD='<当前实际密码>' \
  psql -U deerflow -d postgres \
  -c "ALTER USER deerflow WITH PASSWORD 'ChangeMe-Strong-Password-2026';"

# 3. 确认 my-values.yaml 里 postgresql.auth.password 就是这个值

# 4. helm upgrade(而不是 patch secret)
cd /opt/deer-flow
helm upgrade --install deer-flow deploy/helm/deer-flow/ \
  -n deer-flow -f my-values.yaml \
  --post-renderer /opt/deer-flow-deploy/post-renderer.sh

# 5. 重启 gateway
kubectl rollout restart deployment -n deer-flow -l app.kubernetes.io/component=gateway

Q7:拉取image超时

解决方案

使用国内镜像源拉取

DeerFlow 三个业务镜像

原仓库

  • ghcr.io/bytedance/deer-flow-backend:latest

  • ghcr.io/bytedance/deer-flow-frontend:latest

  • ghcr.io/bytedance/deer-flow-provisioner:latest

# Backend
sudo ctr -n k8s.io images pull ghcr.1ms.run/bytedance/deer-flow-backend:latest
sudo ctr -n k8s.io images tag ghcr.1ms.run/bytedance/deer-flow-backend:latest ghcr.io/bytedance/deer-flow-backend:latest

# Frontend
sudo ctr -n k8s.io images pull ghcr.1ms.run/bytedance/deer-flow-frontend:latest
sudo ctr -n k8s.io images tag ghcr.1ms.run/bytedance/deer-flow-frontend:latest ghcr.io/bytedance/deer-flow-frontend:latest

# Provisioner
sudo ctr -n k8s.io images pull ghcr.1ms.run/bytedance/deer-flow-provisioner:latest
sudo ctr -n k8s.io images tag ghcr.1ms.run/bytedance/deer-flow-provisioner:latest ghcr.io/bytedance/deer-flow-provisioner:latest
nginx

原仓库

  • docker.io/library/nginx:alpine

# DaoCloud 源
sudo ctr -n k8s.io images pull docker.m.daocloud.io/library/nginx:alpine
sudo ctr -n k8s.io images tag docker.m.daocloud.io/library/nginx:alpine docker.io/library/nginx:alpine

# 或轩辕源
sudo ctr -n k8s.io images pull docker.xuanyuan.me/library/nginx:alpine
sudo ctr -n k8s.io images tag docker.xuanyuan.me/library/nginx:alpine docker.io/library/nginx:alpine

# 或毫秒源
sudo ctr -n k8s.io images pull docker.1ms.run/library/nginx:alpine
sudo ctr -n k8s.io images tag docker.1ms.run/library/nginx:alpine docker.io/library/nginx:alpine
busybox

原仓库

  • docker.io/library/busybox:latest

  • docker.io/library/busybox:1.36

sudo ctr -n k8s.io images pull docker.m.daocloud.io/library/busybox:latest
sudo ctr -n k8s.io images tag docker.m.daocloud.io/library/busybox:latest docker.io/library/busybox:latest

# 补 1.36 tag(initContainer 用)
sudo ctr -n k8s.io images tag docker.io/library/busybox:latest docker.io/library/busybox:1.36
sudo ctr -n k8s.io images tag docker.io/library/busybox:latest busybox:1.36
local-path-provisioner

原仓库

  • docker.io/rancher/local-path-provisioner:v0.0.37

# 华为云 SWR 源
sudo ctr -n k8s.io images pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/rancher/local-path-provisioner:v0.0.37
sudo ctr -n k8s.io images tag swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/rancher/local-path-provisioner:v0.0.37 docker.io/rancher/local-path-provisioner:v0.0.37
postgres

原仓库

  • docker.io/library/postgres:16

sudo ctr -n k8s.io images pull docker.m.daocloud.io/library/postgres:16
sudo ctr -n k8s.io images tag docker.m.daocloud.io/library/postgres:16 docker.io/library/postgres:16
redis

原仓库

  • docker.io/library/redis:7-alpine

sudo ctr -n k8s.io images pull docker.m.daocloud.io/library/redis:7-alpine
sudo ctr -n k8s.io images tag docker.m.daocloud.io/library/redis:7-alpine docker.io/library/redis:7-alpine

Q8:如何重建Helm容器

解决方法

cd /opt/deer-flow
helm upgrade --install deer-flow deploy/helm/deer-flow/ \
  -n deer-flow \
  -f my-values.yaml \
  --post-renderer /opt/deer-flow-deploy/post-renderer.sh

kubectl rollout restart deployment -n deer-flow -l app.kubernetes.io/component=gateway
kubectl rollout restart deployment -n deer-flow -l app.kubernetes.io/component=frontend
kubectl rollout status deployment -n deer-flow -l app.kubernetes.io/component=gateway
kubectl rollout status deployment -n deer-flow -l app.kubernetes.io/component=frontend