Rclone WebDAV 完整使用挂载教程

Rclone WebDAV 完整使用挂载教程

1. 什么是 Rclone

Rclone 是一个跨平台命令行工具,用于管理云存储、WebDAV 等远程存储。
它可以像操作本地磁盘一样访问远程文件,实现上传、下载、同步和挂载。


2. 安装 Rclone

Windows

  1. 下载 Windows 版本:https://rclone.org/downloads/
  2. 解压到一个目录,例如 C:\Program Files\rclone
  3. 可选:将 rclone.exe 加入系统 PATH,方便命令行直接调用

Linux

1
curl https://rclone.org/install.sh | sudo bash

验证安装:

1
rclone version

3. 配置 WebDAV

执行:

1
rclone config

选择:

  1. n 新建远程
  2. 输入一个名称,例如 oplist(只用于本地标识,不是账号信息)
  3. 选择存储类型:webdav
  4. 输入 WebDAV URL,例如:
1
https://your-webdav-server.com/dav
  1. 认证方式:

    • 用户名和密码
    • 或 Token(如服务端 Macaroon)
  2. 测试连接,确保能正常访问

注意敏感信息

  • 用户名、密码、Token 都不要直接写在命令或公开文件里

  • Rclone 会保存到 rclone.conf 文件,默认在:

    • Linux: ~/.config/rclone/rclone.conf
    • Windows: %USERPROFILE%\.config\rclone\rclone.conf

4. 测试上传下载

上传本地文件到 WebDAV 根目录:

1
rclone copy test.txt oplist:/ --progress

上传到子目录:

1
rclone copy test.txt oplist:/115/ --progress

列出远程文件:

1
rclone ls oplist:/115/

下载远程文件到本地:

1
rclone copy oplist:/115/test.txt ~/Downloads/ --progress

5. Windows 挂载 WebDAV

Windows 挂载依赖 WinFsp,它提供 FUSE 支持。

步骤 1:安装 WinFsp

下载并安装:https://winfsp.dev/rel/


步骤 2:创建挂载目录(可选)

可以直接挂载到 盘符(推荐 X:)或者目录,例如:

1
mkdir C:\oplist

步骤 3:挂载 WebDAV

1
rclone mount oplist: X: --vfs-cache-mode full

或挂载到目录:

1
rclone mount oplist: C:\oplist --vfs-cache-mode full

常用参数说明:

  • --vfs-cache-mode full:完整缓存,保证编辑器可写
  • --vfs-cache-max-size 10G:缓存最大 10GB
  • --buffer-size 64M:传输缓冲区
  • --dir-cache-time 12h:目录缓存 12 小时
  • --network-mode:兼容 Windows 资源管理器

步骤 4:开机自动挂载(可选)

  1. 打开 任务计划程序
  2. 创建新任务:触发器 → “登录时”
  3. 操作 → 启动程序 → 指向 bat 脚本,例如 mount_oplist.bat
  4. 脚本示例:
1
2
3
@echo off
"C:\Program Files\rclone\rclone.exe" mount oplist: X: --vfs-cache-mode full --vfs-cache-max-size 10G --buffer-size 64M --dir-cache-time 12h --network-mode
pause

6. Linux 挂载 WebDAV

Linux 下推荐挂载到目录,例如 ~/oplist

手动挂载

1
2
mkdir -p ~/oplist
rclone mount oplist: ~/oplist --vfs-cache-mode full --daemon
  • --daemon 后台运行
  • 卸载挂载:
1
fusermount -u ~/oplist

开机自动挂载(可选)

使用 systemd 服务(需要网络就绪后挂载):

步骤 1:创建挂载目录

1
2
mkdir -p /home/pc/oplist
chown pc:pc /home/pc/oplist

步骤 2:创建 systemd 服务

1
sudo nano /etc/systemd/system/rclone-oplist.service

内容示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[Unit]
Description=Mount WebDAV oplist
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=pc
Group=pc
ExecStart=/usr/bin/rclone mount oplist: /home/pc/oplist \
--config /home/pc/.config/rclone/rclone.conf \
--vfs-cache-mode full \
--vfs-cache-max-size 10G \
--buffer-size 64M \
--dir-cache-time 12h \
--allow-other \
--log-file /home/pc/.local/share/rclone-oplist.log \
--log-level INFO
ExecStop=/bin/fusermount -u /home/pc/oplist
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

步骤 3:启用并启动服务

1
2
3
4
sudo systemctl daemon-reload
sudo systemctl enable rclone-oplist
sudo systemctl start rclone-oplist
systemctl status rclone-oplist

步骤 4:验证挂载

1
ls /home/pc/oplist/115

7. 常用 Rclone 命令

功能 命令示例
列出文件 rclone ls oplist:/
上传文件 rclone copy file.txt oplist:/115/
下载文件 rclone copy oplist:/115/file.txt ~/Downloads/
同步文件夹 rclone sync ~/localfolder oplist:/remote
检查配置 rclone config show
测试速度 rclone copy --progress bigfile oplist:/115/

8. 注意事项

  1. 敏感信息安全

    • 不要在脚本里明文写用户名/密码
    • 配置文件默认保存在用户目录,并有权限保护
  2. 缓存与性能

    • 大文件编辑必需 --vfs-cache-mode full
    • 调整 --vfs-cache-max-size--buffer-size 提高性能
  3. 网络稳定性

    • Linux systemd 服务可自动重启
    • Windows 可用任务计划程序手动挂载或开机挂载
  4. 卸载挂载

    • Linux: fusermount -u ~/oplist
    • Windows: 直接关闭 rclone 或在资源管理器中弹出 X: 盘

这个教程完整覆盖了:

  • Rclone WebDAV 配置
  • 文件上传下载测试
  • Windows 挂载 + 开机自动挂载
  • Linux 手动挂载 + systemd 开机挂载
  • 缓存优化和编辑器兼容
  • 敏感信息保护

Docker Swarm 命令学习

Docker Swarm 命令学习

本文档整理了 Docker Swarm 部署和调试过程中常用的命令,帮助快速掌握 Swarm 集群管理。

📋 目录

  1. 服务栈管理
  2. 服务管理
  3. 任务管理
  4. 节点管理
  5. 网络管理
  6. 日志查看
  7. 容器操作
  8. Traefik 调试
  9. 常用组合命令

服务栈管理

部署服务栈

1
2
3
4
5
# 从 docker-compose.yml 部署服务栈
docker stack deploy -c docker-compose.yml <stack-name>

# 示例:部署名为 app 的服务栈
docker stack deploy -c docker-compose.yml app

查看服务栈列表

1
2
# 查看所有服务栈
docker stack ls

查看服务栈中的服务

1
2
3
4
5
6
7
8
9
10
11
12
13
# 查看指定服务栈中的所有服务
docker stack services <stack-name>

# 示例:查看 app 服务栈的所有服务
docker stack services app

# 输出格式说明:
# ID: 服务ID
# NAME: 服务名称(格式:stack-name_service-name)
# MODE: 模式(replicated 或 global)
# REPLICAS: 副本状态(运行数/期望数)
# IMAGE: 使用的镜像
# PORTS: 端口映射

删除服务栈

1
2
3
4
5
# 删除整个服务栈(会删除栈中所有服务)
docker stack rm <stack-name>

# 示例:删除 app 服务栈
docker stack rm app

服务管理

查看服务详情

1
2
3
4
5
6
7
8
9
# 查看服务的详细信息(JSON格式)
docker service inspect <service-name>

# 示例:查看 app_web 服务详情
docker service inspect app_web

# 查看特定字段(使用格式化输出)
docker service inspect app_web --format '{{json .Spec.TaskTemplate.ContainerSpec.Labels}}'
docker service inspect app_web --format '{{json .Spec.TaskTemplate.ContainerSpec.Mounts}}'

更新服务

1
2
3
4
5
6
7
8
9
10
11
12
# 更新服务镜像
docker service update --image <new-image> <service-name>

# 示例:更新 app_web 服务镜像
docker service update --image web-app:v2 app_web

# 更新服务环境变量
docker service update --env-add KEY=value <service-name>

# 更新服务约束条件
docker service update --constraint-add 'node.role==manager' <service-name>
docker service update --constraint-rm 'node.role==manager' <service-name>

扩展服务

1
2
3
4
5
6
7
8
# 扩展服务副本数
docker service scale <service-name>=<replicas>

# 示例:将 app_web 扩展到 3 个副本
docker service scale app_web=3

# 示例:扩展到 1 个副本
docker service scale app_web=1

删除服务

1
2
3
4
5
# 删除服务(会停止并删除所有任务)
docker service rm <service-name>

# 示例:删除 app_web 服务
docker service rm app_web

任务管理

查看服务栈中的任务

1
2
3
4
5
6
7
8
9
10
11
# 查看服务栈中所有服务的任务
docker stack ps <stack-name>

# 示例:查看 app 服务栈的所有任务
docker stack ps app

# 显示完整输出(不截断)
docker stack ps app --no-trunc

# 格式化输出
docker stack ps app --format "table {{.Name}}\t{{.Node}}\t{{.CurrentState}}\t{{.Error}}"

查看服务的任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看指定服务的所有任务
docker service ps <service-name>

# 示例:查看 app_web 服务的任务
docker service ps app_web

# 显示完整输出
docker service ps app_web --no-trunc

# 格式化输出
docker service ps app_web --format "table {{.Name}}\t{{.Node}}\t{{.CurrentState}}\t{{.Error}}"

# 只查看运行中的任务
docker service ps app_web --filter "desired-state=running"

任务状态说明

  • Running: 任务正在运行
  • Preparing: 任务准备中
  • Starting: 任务启动中
  • Shutdown: 任务已关闭
  • Rejected: 任务被拒绝(通常有错误信息)
  • Failed: 任务失败

节点管理

查看节点列表

1
2
3
4
5
6
7
8
9
# 查看 Swarm 集群中的所有节点
docker node ls

# 输出说明:
# ID: 节点ID
# HOSTNAME: 节点主机名
# STATUS: 节点状态(Ready/Unavailable)
# AVAILABILITY: 可用性(Active/Drain/Pause)
# MANAGER STATUS: Manager状态(Leader/Reachable/Unreachable)

查看节点详情

1
2
3
4
5
6
7
8
# 查看节点详细信息
docker node inspect <node-id-or-hostname>

# 示例:查看节点详情
docker node inspect ser470818881004

# 查看特定字段
docker node inspect ser470818881004 --format '{{.Status.Addr}}'

节点操作

1
2
3
4
5
6
7
8
# 将节点设置为维护模式(不调度新任务)
docker node update --availability drain <node-id>

# 恢复节点可用性
docker node update --availability active <node-id>

# 移除节点(需要先设置为 drain)
docker node rm <node-id>

网络管理

查看网络列表

1
2
3
4
5
# 查看所有网络
docker network ls

# 过滤 overlay 网络
docker network ls | grep overlay

创建网络

1
2
3
4
5
6
7
8
# 创建 overlay 网络(Swarm 模式)
docker network create --driver overlay <network-name>

# 示例:创建 traefik-net 网络
docker network create --driver overlay traefik-net

# 创建可附加的网络(允许外部容器连接)
docker network create --driver overlay --attachable traefik-net

查看网络详情

1
2
3
4
5
# 查看网络详细信息
docker network inspect <network-name>

# 示例:查看 traefik-net 网络
docker network inspect traefik-net

删除网络

1
2
# 删除网络(需要先删除使用该网络的服务)
docker network rm <network-name>

日志查看

查看服务日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 查看服务的所有日志
docker service logs <service-name>

# 示例:查看 app_web 服务日志
docker service logs app_web

# 查看最后 N 行日志
docker service logs --tail 50 app_web

# 实时跟踪日志(类似 tail -f)
docker service logs -f app_web

# 查看特定任务的日志
docker service logs app_web.1

# 过滤日志内容
docker service logs app_web | grep -i error
docker service logs app_web | grep -E "(error|certificate|ACME)"

查看容器日志

1
2
3
4
5
6
7
8
# 查看容器日志(需要先找到容器ID)
docker logs <container-id>

# 实时跟踪
docker logs -f <container-id>

# 查看最后 N 行
docker logs --tail 100 <container-id>

容器操作

查找容器

1
2
3
4
5
6
7
8
9
10
11
# 查找运行中的容器
docker ps

# 按名称过滤
docker ps -f "name=app_web"

# 查找所有容器(包括停止的)
docker ps -a

# 使用格式化输出
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

进入容器执行命令

1
2
3
4
5
6
7
8
9
10
# 进入容器(交互式)
docker exec -it <container-id> bash
docker exec -it <container-id> sh

# 执行单个命令
docker exec <container-id> <command>

# 示例:在容器中执行 Django 命令
docker exec $(docker ps -q -f name=app_web) python manage.py createsuperuser
docker exec $(docker ps -q -f name=app_web) python manage.py migrate

查看容器信息

1
2
3
4
5
# 查看容器详细信息
docker inspect <container-id>

# 查看特定字段
docker inspect <container-id> --format '{{.NetworkSettings.Networks}}'

Traefik 调试

访问 Traefik Dashboard API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 查看所有路由
curl http://localhost:8080/api/http/routers

# 查看所有服务
curl http://localhost:8080/api/http/services

# 查看特定服务
curl http://localhost:8080/api/http/services/web@docker

# 格式化输出(需要 jq)
curl -s http://localhost:8080/api/http/services | jq .

# 过滤特定内容
curl -s http://localhost:8080/api/http/services | grep -i web
curl -s http://localhost:8080/api/http/services/web@docker | grep -o '"url":"[^"]*"'

测试 HTTP 访问

1
2
3
4
5
6
7
8
9
10
11
# 测试 HTTP 请求(带 Host 头)
curl -H "Host: test.bismih520.com" http://localhost

# 查看响应头
curl -I -H "Host: test.bismih520.com" http://localhost

# 测试 HTTPS(忽略证书错误)
curl -k -H "Host: test.bismih520.com" https://localhost

# 跟随重定向
curl -L -H "Host: test.bismih520.com" http://localhost

常用组合命令

检查服务状态

1
2
3
4
5
6
# 一键查看服务栈状态
echo "===服务状态===" && \
docker stack services app && \
echo "" && \
echo "===任务状态===" && \
docker stack ps app

检查服务健康

1
2
3
4
5
6
# 检查服务并查看日志
docker stack services app && \
echo "---Web服务任务---" && \
docker service ps app_web && \
echo "---Web服务日志---" && \
docker service logs app_web --tail 10

重启服务

1
2
3
4
5
6
7
# 更新服务以触发重启
docker service update --force app_web

# 或者删除并重新部署
docker stack rm app && \
sleep 3 && \
docker stack deploy -c docker-compose.yml app

查看端口占用

1
2
3
4
5
# 检查端口是否被占用
ss -tuln | grep -E ':(80|443|8080)'

# 或者使用 netstat
netstat -tuln | grep -E ':(80|443|8080)'

检查 SSL 证书

1
2
3
4
5
6
# 查看证书文件
ls -lh /root/code/letsencrypt/acme.json

# 查看 Traefik 证书相关日志
docker service logs app_traefik | grep -i certificate
docker service logs app_traefik | grep -E "(ACME|certificate|test.bismih520)"

查找并进入容器

1
2
3
4
5
# 查找 web 服务容器并进入
docker exec -it $(docker ps -q -f name=app_web) bash

# 查找 traefik 容器并查看配置
docker exec $(docker ps -q -f name=app_traefik) ls -la /letsencrypt

清理资源

1
2
3
4
5
6
7
8
9
10
11
# 删除服务栈
docker stack rm app

# 删除未使用的网络
docker network prune -f

# 删除未使用的镜像
docker image prune -f

# 查看磁盘使用情况
docker system df

故障排查流程

1. 检查服务栈状态

1
2
3
docker stack ls
docker stack services app
docker stack ps app

2. 检查服务状态

1
2
docker service ps app_web
docker service ps app_traefik

3. 查看日志

1
2
docker service logs app_web --tail 50
docker service logs app_traefik --tail 50

4. 检查网络

1
2
docker network ls
docker network inspect app_traefik-net

5. 检查节点

1
2
docker node ls
docker node inspect <node-id>

6. 测试访问

1
2
3
curl -I http://localhost
curl -I -H "Host: test.bismih520.com" http://localhost
curl http://localhost:8080/api/http/services

实用技巧

1. 使用别名简化命令

1
2
3
4
5
# 添加到 ~/.bashrc
alias dps='docker stack ps'
alias dss='docker stack services'
alias dsl='docker service logs'
alias dsp='docker service ps'

2. 格式化输出

1
2
# 使用 --format 自定义输出格式
docker service ps app_web --format "table {{.Name}}\t{{.Node}}\t{{.CurrentState}}"

3. 过滤输出

1
2
3
4
5
# 只查看运行中的任务
docker service ps app_web --filter "desired-state=running"

# 只查看特定节点的任务
docker stack ps app --filter "node=ser470818881004"

4. 批量操作

1
2
3
4
# 更新所有服务
for service in $(docker stack services -q app); do
docker service update --force $service
done

注意事项

  1. 服务栈命名:服务名称格式为 <stack-name>_<service-name>
  2. 网络命名:网络名称格式为 <stack-name>_<network-name>
  3. 任务命名:任务名称格式为 <service-name>.<replica-number>
  4. 删除顺序:先删除服务,再删除网络
  5. 更新服务:更新服务会触发滚动更新,不会中断服务
  6. 日志限制:默认日志有大小限制,可能需要配置日志驱动

参考资源


最后更新: 2025-11-16

wrangler 使用教程

Cloudflare Wrangler 部署教程(wrangler deploy)

📌 前言

wrangler 是 Cloudflare 官方 CLI,用于部署 Workers、Pages Functions、KV、R2 等。

本文只教你最核心的:如何把本地一个 worker 项目部署上线


1️⃣ 安装 Wrangler

Node.js ≥ 16

先确认 Node 版本:

1
2
node -v

如果版本低,请更新 Node。

安装 wrangler

1
2
npm install -g wrangler

检查是否安装成功:

1
2
wrangler -v


2️⃣ 登录 Cloudflare

1
2
wrangler login

浏览器会自动打开 Cloudflare 登录页,登录成功即可。


3️⃣ 创建一个 Worker 项目

Cloudflare 提供自动模板:

1
2
3
wrangler init my-worker
cd my-worker

初始化后生成:

1
2
3
4
5
6
my-worker/
├─ src/
│ └─ index.js
├─ wrangler.json
└─ package.json


4️⃣ 本地调试

1
2
wrangler dev

终端显示:

1
2
http://127.0.0.1:8787

浏览器打开就能测试 Worker。


5️⃣ 配置 wrangler.json(最重要)

wrangler.json 是部署的核心配置。

最基础示例:

1
2
3
4
5
6
{
"name": "my-worker",
"main": "src/index.js",
"compatibility_date": "2024-05-01"
}

如果你需要自定义路由(例如你想绑定你的域名):

1
2
3
4
5
6
7
8
{
"name": "my-worker",
"main": "src/index.js",
"routes": [
"example.com/*"
]
}

如果你只想让系统自动分配 .workers.dev,可以不配置 routes


6️⃣ 部署(就是你要的 wrangler deploy)

只需一行:

1
2
wrangler deploy

部署成功后会显示类似:

1
2
3
4
✨ Success!
Your worker is available at:
https://my-worker.<account>.workers.dev

如果你设置了 route,会显示你绑定的域名。


7️⃣ 更新代码后重新部署

修改代码后,只需要再次:

1
2
wrangler deploy

wrangler 会自动更新线上版本。


8️⃣ 常见报错与解决方案

❌ “Missing required field main

你没有在 wrangler.json 指定入口文件。

解决:

1
2
"main": "src/index.js"


❌ “Precondition Failed 412”

通常是 header/跨域/代理 worker 写法导致。

你可以添加:

1
2
3
4
5
6
return new Response(data, {
headers: {
"Access-Control-Allow-Origin": "*"
}
})


❌ “Route not allowed”

你没有把域名接入 Cloudflare。

去 Cloudflare「DNS」添加 域名 → 代理模式启用(橘色云)。


9️⃣ 推荐的完整文件结构(示例)

1
2
3
4
5
6
7
my-worker/
├─ src/
│ └─ index.js
├─ package.json
├─ wrangler.json
└─ README.md

src/index.js 示例:

1
2
3
4
5
6
export default {
async fetch(request) {
return new Response("Hello Cloudflare Worker!");
}
}


🎉 完成部署!

你现在已经可以:

  • 本地运行 wrangler dev
  • 一键部署 wrangler deploy
  • 配置自己的域名
  • 查看 worker 运行情况

打包deb学习

Debian 包打包学习笔记

一、基础知识

1.1 Debian 包的基本概念

Debian 包(.deb) 是 Debian 及其衍生系统(如 Ubuntu)使用的软件包格式,用于软件的分发、安装和管理。

主要组成部分:

  • 控制信息:包的元数据、依赖关系、描述等
  • 数据文件:实际要安装的文件(二进制、库、配置文件等)
  • 脚本:安装前/后执行的脚本(preinst、postinst、prerm、postrm)

1.2 Debian 打包目录结构

1
2
3
4
5
6
7
8
9
10
项目根目录/
├── debian/ # Debian 打包配置目录
│ ├── control # 包元数据和依赖信息
│ ├── rules # 构建规则(Makefile 格式)
│ ├── changelog # 版本变更日志
│ ├── compat # debhelper 兼容性版本
│ └── ymbot-amp-devel/ # 构建时生成的安装目录
├── src/ # 源代码目录
├── build/ # 构建目录
└── build_deb.sh # 打包脚本

1.3 关键文件说明

debian/control

  • Source: 源码包名称
  • Build-Depends: 构建时需要的依赖
  • Package: 二进制包定义
    • Depends: 运行时依赖
    • Architecture: 支持的架构(any, amd64, arm64 等)
    • Description: 包描述

debian/rules

  • 构建系统的核心文件,使用 Makefile 语法
  • 通过 override_dh_* 覆盖 debhelper 的默认行为
  • 主要阶段:
    • override_dh_auto_configure: 配置阶段
    • override_dh_auto_build: 构建阶段
    • override_dh_auto_install: 安装阶段

debian/changelog

  • 记录包的版本历史和变更
  • 格式:包名 (版本-修订) 发行版; 紧急程度
  • 用于版本管理和生成 .changes 文件

1.4 打包工具

主要工具:

  • dpkg-buildpackage: 主要的打包命令
    • -us -uc: 不签名源码和变更文件
    • -b: 只构建二进制包
  • debhelper: 提供 dh_* 系列命令,简化打包流程
  • devscripts: 提供开发工具

构建流程:

1
2
3
4
5
6
dpkg-buildpackage
├── dpkg-source (生成源码包)
├── debian/rules clean (清理)
├── debian/rules build (构建)
├── debian/rules binary (安装并打包)
└── dpkg-deb (生成 .deb 文件)

二、遇到的问题

2.1 问题描述

在运行 build_deb.sh 进行打包时,构建过程失败,出现链接错误:

错误 1:找不到 onnxruntime 库

1
2
3
/usr/bin/ld: 找不到 -lonnxruntime
collect2: error: ld returned 1 exit status
make[4]: *** [ymbot_amp_devel/CMakeFiles/amp_controller.dir/build.make:111] 错误 1

错误 2:找不到 mujoco 库

1
make[4]: *** 没有规则可制作目标"/home/pc/ymbot_c_21dofwalk/src/ymbot_amp_devel/lib/mujoco-3.2.7/lib/libmujoco.so"

2.2 问题分析

根本原因:

  1. 符号链接缺失:库文件只有带版本号的 .so 文件(如 libonnxruntime.so.1.21.0),但链接器需要不带版本号的符号链接(libonnxruntime.so
  2. Linux 动态库命名规范
    • libname.so → 符号链接,指向当前版本
    • libname.so.major → 符号链接,指向主版本
    • libname.so.major.minor.patch → 实际的库文件

为什么需要符号链接?

  • CMake 的 target_link_libraries(target onnxruntime) 会查找 libonnxruntime.so
  • 链接器通过 -lonnxruntime 参数查找库时,会在库路径中搜索 libonnxruntime.so
  • 如果没有符号链接,链接器无法找到库文件

检查方法:

1
2
3
4
5
6
# 检查库文件
ls -la src/ymbot_amp_devel/lib/onnxruntime-linux-x64-1.21.0/lib/
# 输出:只有 libonnxruntime.so.1.21.0,缺少 libonnxruntime.so

ls -la src/ymbot_amp_devel/lib/mujoco-3.2.7/lib/
# 输出:只有 libmujoco.so.3.2.7,缺少 libmujoco.so

三、解决方法

3.1 临时解决(手动创建符号链接)

1
2
3
4
5
6
7
# 创建 onnxruntime 符号链接
cd src/ymbot_amp_devel/lib/onnxruntime-linux-x64-1.21.0/lib
ln -sf libonnxruntime.so.1.21.0 libonnxruntime.so

# 创建 mujoco 符号链接
cd src/ymbot_amp_devel/lib/mujoco-3.2.7/lib
ln -sf libmujoco.so.3.2.7 libmujoco.so

3.2 永久解决(修改构建脚本)

build_deb.sh 中添加自动创建符号链接的步骤:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建必要的符号链接
echo "3. 创建必要的库符号链接..."
ONNXRUNTIME_LIB_DIR="src/ymbot_amp_devel/lib/onnxruntime-linux-x64-1.21.0/lib"
MUJOCO_LIB_DIR="src/ymbot_amp_devel/lib/mujoco-3.2.7/lib"

if [ -f "$ONNXRUNTIME_LIB_DIR/libonnxruntime.so.1.21.0" ] && [ ! -f "$ONNXRUNTIME_LIB_DIR/libonnxruntime.so" ]; then
ln -sf libonnxruntime.so.1.21.0 "$ONNXRUNTIME_LIB_DIR/libonnxruntime.so"
echo " 创建了 libonnxruntime.so 符号链接"
fi

if [ -f "$MUJOCO_LIB_DIR/libmujoco.so.3.2.7" ] && [ ! -f "$MUJOCO_LIB_DIR/libmujoco.so" ]; then
ln -sf libmujoco.so.3.2.7 "$MUJOCO_LIB_DIR/libmujoco.so"
echo " 创建了 libmujoco.so 符号链接"
fi

关键点:

  • 使用条件判断,避免重复创建
  • 检查源文件存在且符号链接不存在时才创建
  • 使用相对路径创建符号链接(ln -sf 目标 链接名

四、知识点总结

4.1 动态库的版本管理

Linux 动态库版本命名规范:

1
2
3
4
libname.so              # 开发链接(符号链接)
libname.so.major # 主版本(符号链接)
libname.so.major.minor # 次版本(符号链接)
libname.so.major.minor.patch # 完整版本(实际文件)

示例:

1
libonnxruntime.so → libonnxruntime.so.1 → libonnxruntime.so.1.21 → libonnxruntime.so.1.21.0

4.2 CMake 链接库的方式

方式 1:使用库名(推荐)

1
2
target_link_libraries(target onnxruntime)
# 链接器会查找 libonnxruntime.so

方式 2:使用完整路径

1
2
3
target_link_libraries(target 
${CMAKE_CURRENT_SOURCE_DIR}/lib/onnxruntime-linux-x64-1.21.0/lib/libonnxruntime.so.1.21.0
)

方式 3:使用 find_library

1
2
3
4
5
6
find_library(ONNXRUNTIME_LIB 
NAMES onnxruntime
PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib/onnxruntime-linux-x64-1.21.0/lib
NO_DEFAULT_PATH
)
target_link_libraries(target ${ONNXRUNTIME_LIB})

4.3 debhelper 的覆盖机制

debian/rules 中的覆盖:

1
2
3
4
5
6
7
8
9
10
11
override_dh_auto_configure:
# 自定义配置步骤
@echo "Configuring..."

override_dh_auto_build:
# 自定义构建步骤
@echo "Building..."

override_dh_auto_install:
# 自定义安装步骤
@echo "Installing..."

为什么要覆盖?

  • ROS/catkin 项目需要特殊的构建流程
  • 需要 source ROS 环境变量
  • 需要自定义安装路径

4.4 构建环境隔离

问题: deb 打包需要在干净的环境中构建

解决:

  • debian/rules 中每次构建前 source ROS 环境
  • 使用 $(CURDIR) 确保路径正确
  • 清理旧的构建产物

五、反思与最佳实践

5.1 问题预防

应该在项目初始化时:

  1. ✅ 检查所有第三方库是否包含正确的符号链接
  2. ✅ 在 README 中说明库的版本和符号链接要求
  3. ✅ 在 CI/CD 中自动检查符号链接

建议的检查脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# check_lib_links.sh
check_lib() {
local lib_dir=$1
local lib_name=$2
local lib_version=$3

if [ -f "$lib_dir/$lib_name.$lib_version" ] && [ ! -f "$lib_dir/$lib_name" ]; then
echo "警告: $lib_dir 缺少 $lib_name 符号链接"
return 1
fi
return 0
}

check_lib "src/ymbot_amp_devel/lib/onnxruntime-linux-x64-1.21.0/lib" \
"libonnxruntime.so" "1.21.0"

5.2 构建脚本设计原则

好的构建脚本应该:

  1. 幂等性:多次运行结果一致
  2. 错误检查:检查依赖和前置条件
  3. 清理机制:清理旧的构建产物
  4. 环境准备:自动修复常见问题(如符号链接)
  5. 清晰的输出:显示每个步骤的进度

示例结构:

1
2
3
4
5
6
7
8
#!/bin/bash
set -e # 遇到错误立即退出

# 1. 检查依赖
# 2. 清理旧文件
# 3. 准备环境(创建符号链接等)
# 4. 执行构建
# 5. 验证结果

5.3 版本管理建议

对于第三方库:

  • 将符号链接纳入版本控制(如果库文件也在版本控制中)
  • 或者在构建脚本中自动创建
  • 在文档中说明符号链接的要求

对于项目库:

  • 使用 CMake 的 set_target_properties 设置正确的版本:
    1
    2
    3
    4
    set_target_properties(mylib PROPERTIES
    VERSION 1.2.3
    SOVERSION 1
    )

5.4 调试技巧

查看链接器搜索路径:

1
2
3
4
5
# 查看链接命令
make VERBOSE=1

# 查看库搜索路径
ld --verbose | grep SEARCH_DIR

检查库依赖:

1
2
3
4
5
# 查看可执行文件的库依赖
ldd /path/to/binary

# 查看库的符号
nm -D /path/to/library.so

调试 deb 包:

1
2
3
4
5
6
7
8
# 查看包内容
dpkg -c package.deb

# 查看包信息
dpkg -I package.deb

# 解压包查看内容
dpkg-deb -x package.deb /tmp/extract

5.5 常见问题排查清单

  1. 链接错误

    • 检查库文件是否存在
    • 检查符号链接是否正确
    • 检查库搜索路径(link_directories
    • 检查库名称拼写
  2. 构建失败

    • 检查构建依赖是否安装
    • 检查环境变量(ROS 环境)
    • 检查构建日志的详细错误
  3. 安装问题

    • 检查安装路径权限
    • 检查依赖关系
    • 检查 postinst 脚本

六、参考资料

七、经验总结

  1. 符号链接很重要:第三方库必须包含正确的符号链接,否则链接器无法找到库
  2. 构建脚本要健壮:自动检测和修复常见问题,减少手动干预
  3. 环境隔离:每次构建前清理旧文件,确保构建环境干净
  4. 错误信息要仔细看:链接错误通常有明确的提示,要仔细分析
  5. 版本管理要规范:库的版本号和符号链接要符合 Linux 规范

最后更新: 2024-11-20
问题解决日期: 2024-11-20
相关文件: build_deb.sh, debian/rules, src/ymbot_amp_devel/CMakeLists.txt

通过 saas 回源优选加速 Cloudflare 中国大陆访问

CF优选,简单来说,就是用一个在 Cloudflare 上配置好的域名(B),来给另一个我们自己的主域名(A)借道加速,尤其是显著提升中国大陆的访问速度

角色介绍

为了方便理解,我们先给今天的主角们分配好角色:

  1. **主域名 (A)**:4399.com
    • 这是访客真正访问的域名,我们希望它能享受到 Cloudflare 的服务。
    • 重要:它的 DNS 解析在别的平台管理(比如阿里云、腾讯云、GoDaddy 等),不是在 Cloudflare。
  2. **回源域名 (B)**:123456.xyz
    • 这是我们的“工具域名”,它在 Cloudflare 上进行全部配置,充当一个中间桥梁。
    • 它本身不重要,访客也看不到它。
  3. 你的服务器 IP1.2.3.4 (这里用一个假的 IP 地址举例)
    • 这是你网站内容真正存放的地方。

我们的目标:当用户访问 4399.com 时,实际流量会通过 123456.xyz 在 Cloudflare 上的配置,最终安全地访问到你的服务器。


操作开始:一共六个步骤

:rocket:

第一步:配置“回源域名” (123456.xyz)

首先,我们要把我们的“工具域名”123456.xyz 配置好。

  1. 登录你的 Cloudflare 账号。
  2. 进入域名 123456.xyz 的管理后台。
  3. 点击左侧的 “DNS” → “Records”。
  4. 添加一条 A 记录,指向你的真实服务器 IP。
    • 类型 (Type)A
    • 名称 (Name)origin (你可以自定义,但用 origin 比较清晰)
    • IPv4 地址 (Content)1.2.3.4 (换成你自己的服务器 IP)
    • 代理状态 (Proxy status)务必点亮那朵橙色的小黄云! 这代表流量会经过 Cloudflare。

现在,访问 origin.123456.xyz 就应该能看到你服务器上的网站内容了,并且是经过 Cloudflare 加速的。

第二步:设置“回退源” (Fallback Origin)

这一步是为了让 Cloudflare 知道,当收到访问 123456.xyz 的请求时,应该去哪里找内容。

  1. 在 123456.xyz 的管理后台,点击左侧的 “SSL/TLS” → “自定义主机名”。
  2. 向下滑动,找到 “回退源 (Fallback Origin)” 部分。
  3. 点击 “添加回退源 (Add Fallback Origin)”。
  4. 在输入框里填入我们上一步创建的子域名:origin.123456.xyz
  5. 点击保存。

保存成功后,状态应该是**有效 (Active)**。

第三步:添加“自定义主机名” (Custom Hostname)

这是最关键的一步!我们要告诉 Cloudflare:“我授权你用 123456.xyz 的配置来服务 4399.com 这个域名。”

  1. 在 123456.xyz 的管理后台,继续停留在 “SSL/TLS” → “自定义主机名 (Custom Hostnames)” 标签页。
  2. 点击 “添加自定义主机名 (Add Custom Hostname)”。
  3. 在弹出的窗口中,输入我们的主域名4399.com
  4. 其他设置保持默认,点击右下角的 “添加自定义主机名” 按钮。

完成后,Cloudflare 会开始处理,但此时状态会是“待验证 (Pending Validation)”。它需要你证明 4399.com 确实是你的。

第四步:验证“主域名”的所有权

Cloudflare 会给你一条 TXT 记录,你需要把它添加到 4399.com 的 DNS 解析记录里。

  1. 在刚才的“自定义主机名”页面,你会看到 4399.com 旁边有“查看验证 TXT”之类的链接,点开它。
  2. 你会得到两条 TXT 记录,一条用于域名所有权验证,一条用于证书验证。记下它们的**主机名 (Name)和值 (Value)**。
  3. 登录 4399.com 的 DNS 管理平台(比如阿里云、腾讯云等)。
  4. 添加那两条 TXT 记录。
    • 主机记录: 复制 Cloudflare 提供的 Name。
    • 记录类型TXT
    • 记录值: 复制 Cloudflare 提供的 Value。
  5. 添加完成后,回到 Cloudflare 页面,耐心等待几分钟到几小时(DNS 生效需要时间)。

当你看到

1
4399.com

主机名状态

证书状态

都显示为

:white_check_mark:

有效 (Active)

时,就说明验证成功了!

第五步:将“主域名”指向 Cloudflare

万事俱备,只欠东风!现在我们要让 4399.com 指向我们刚刚在 Cloudflare 搭建好的“桥梁”。

  1. 再次回到 4399.com 的 DNS 管理平台。
  2. 找到主域名的解析记录(通常是 @ 或者 4399.com 本身)。
  3. 修改或添加一条 CNAME 记录。
    • 主机记录@ (或者 www,取决于你想让哪个生效)
    • 记录类型CNAME
    • 记录值origin.123456.xyz

:light_bulb:

优选技巧

为了获得国内更快的速度,你可以不直接 CNAME 到 origin.123456.xyz,而是使用 优选 CNAME 地址 或者 A 记录。这些地址可以帮助你连接到速度更快的 Cloudflare 节点。

你可以在一些第三方优选工具网站上找到这些地址。找到一个适合你网络的 CNAME 地址(例如

1
cloudflare.182682.xyz

1
linux.do

也行

:nerd_face:

:shushing_face:

:shushing_face:

),然后把上面的记录值换成这个优选的 CNAME 地址。

第六步:大功告成,最终检查!

现在,所有的配置都完成了!

等待几分钟让 DNS 解析在全球生效后,在浏览器里输入 https://4399.com

你会发现,打开的网站和你直接访问 origin.123456.xyz 或者你的服务器 IP 所看到的网站一模一样,但此时 4399.com 已经成功套上了 Cloudflare 的 CDN 加速和安全防护!

  • 背后发生了什么?

    当你完成所有设置后,一个访客(比如小明)打开网站的奇妙旅程是这样的:

    第 1 步:用户访问主域名

    小明在浏览器里兴冲冲地输入 https://4399.com,然后按下回车。

    第 2 步:DNS 指路

    小明的电脑向 DNS 系统发出询问:“4399.com 的服务器在哪?”

    4399.com 的 DNS 解析服务(在阿里云/腾讯云等平台)回应说:“我这没直接地址,但你去找 origin.123456.xyz(优选地址) 就行了,它知道在哪。” (这就是我们设置的 CNAME 记录在起作用)

    第 3 步:找到 Cloudflare 的加速节点

    小明的电脑继续问:“那 origin.123456.xyz 在哪?”

    这次,Cloudflare 的 DNS 系统接管了。它会智能地分析小明的位置,并告诉他一个离他最近、访问速度最快的 Cloudflare 加速节点服务器的 IP 地址。

    (如果你用了优选 CNAME,这里会分配到更适合国内网络的节点!)

    第 4 步:连接到“接待员”

    小明的浏览器连接到这个近在咫尺的 Cloudflare 加速节点,并递上“名片”说:“你好,我是来访问 4399.com 网站的。”

    第 5 步:Cloudflare 核对身份并服务

    Cloudflare 加速节点(我们的“接待员”)收到请求后,立刻查看自己的“贵宾名单”(我们设置的“自定义主机名”)。

    它发现 4399.com 确实是授权过的贵宾,于是开始为它提供服务。

    第 6 步:智能中转与缓存 (配置与123456.xyz保持一致)

    • 如果节点有缓存:接待员发现自己手上正好有 4399.com 的网页内容备份,就直接把内容发给小明。(速度飞快,网站秒开!)
    • 如果节点没缓存:接待员会通过 Cloudflare 的内部高速专线,去联系我们远在海外的真实服务器(IP: 1.2.3.4),抓取最新的网页内容。这个过程比普通访问快得多也稳定得多。

    第 7 步:内容安全送达

    Cloudflare 节点把从服务器取来的新鲜内容,或者它自己的缓存,安全地交给小明的浏览器。

    小明输入 4399.com → 浏览器问DNS → DNS通过解析 优选地址 (一个优选的高速CNAME或IP),最终为小明挑选出Cloudflare节点IP → 浏览器连接这个高速节点IP并说:“我要 4399.com” → Cloudflare节点核对“贵宾名单”,确认 4399.com 已被授权(自定义主机名) → 节点于是套用 123456.xyz 的全套服务(缓存、安全规则等)来处理请求 → 节点根据这些规则,检查缓存或去真实服务器取回内容 → 内容安全返回给小明 → 小明成功看到网站

    1
    4399.com

    :face_savoring_food:

  • 为什么能加速?

    看到这里,你可能心里在犯嘀咕,绕了这么大一圈,凭什么就能给国内访问加速了?而且既然加速了,为啥速度还是比不上真正的国内服务器?

    哈哈哈哈哈把一次完整的访问拆成两段路来看就明白了:

    第一段路:从你的电脑 → 到 Cloudflare 的节点

    这通常是国内用户访问海外网站时 最堵、最慢 的一段路。你可以把它想象成下班高峰期出城的路,所有车都挤在运营商的国际出口那几个收费站,延迟高得吓人(动不动就 200ms+),还老丢包。

    而“优选”干的事,就是给你找了条 VIP 通道。

    你用的那个“优选 CNAME”或“优选 IP”,它背后走的不是普通拥挤的公用出口,而是像 CN2 GIA、CMI 这种高质量的专线。它直接绕开了那个大堵点,让你的访问请求“嗖”地一下就抵达了 Cloudflare 在海外的服务器节点。

    说白了,加速的核心就是优化了这“第一段路”,让用户的请求成功“出海”的过程变得丝般顺滑。

    那速度瓶颈又在哪?

    既然第一段路这么快,为啥整体体验还是超不过国内的服务器呢?

    答案在第二段路:从 Cloudflare 节点 → 回到你的源站服务器(回源)

    你想想,就算你的请求光速到达了 CF 的节点,但网站内容还在你那台 1.2.3.4 的服务器上放着呢。CF 节点得亲自跑一趟,跨越重洋去你的服务器上把数据取回来,然后再交给你。

    这个“回源”的过程,物理距离是硬伤,跨国光缆的延迟是实打实存在的。虽然 Cloudflare 的内部网络已经很牛了,但它也变不出消除这段路程的耗时。

    核心知识点:优选IP只管“去”,不管“回”

    用“优选IP”当跳板,让国内访客不走运营商拥堵的国际出口,直接抄近道连上 Cloudflare;再通过“自定义主机名”功能,让你网站套用上另一域名预设好的加速配置。

    加速效果完全取决于“优选IP”的质量。这个IP会变慢、会失效,需要定期更换。

    Cloudflare 回源到你服务器的延迟是硬伤,所以速度再快也快不过优秀的国内服务器,因为当Cloudflare节点把数据(下行)发回给用户时,它走的是Cloudflare自己的BGP路由网络。这条路不受我们选择的优选IP影响。

    优选 IP 是有“时效性”的。今天测速飞快的 IP,明天可能因为用的人太多、或者被运营商盯上而变慢。所以,把它当成一个动态优化的过程,而不是一劳永逸的配置。手里多准备几个备用节点,定期检查速度,才能确保你的网站时刻保持在最佳状态

[details=“如何处理多个子域名服务?”]

如果 4399.com 旗下还有 blog.4399.com 和 shop.4399.com 等多个子域名,它们该如何一同享受加速服务?

第一步:为所有子域名添加“自定义主机名”

首先,需要在 123456.xyz 的 “自定义主机名” 列表里,将每一个需要加速的子域名都添加进去并完成所有权验证。

例如,列表中应包含:

  • 4399.com
  • blog.4399.com
  • shop.4399.com

第二步:理解“回退源”的唯一性限制

这里的关键在于!Cloudflare 允许添加多个“自定义主机名”,但 “回退源 (Fallback Origin)” 只能设置一个

这意味着,无论访客的请求是发往 blog.4399.com 还是 shop.4399.com,Cloudflare 最终都会将所有流量转发到同一个“总出口”——即教程中设置的 origin.123456.xyz,也就是那台作为回源的主服务器(IP: 1.2.3.4)。

第三步:解决方案,引入 Nginx 作为中央反向代理

既然所有子域名的流量都汇集到了同一台主服务器,就需要在这台服务器上部署一个“智能交通指挥官”来根据域名分发流量。这个角色最适合由 Nginx 担任。

Nginx 会检查每个请求的目标域名,然后将其转发到正确的后端服务器。连接后端服务器主要有两种方式:


实战来咯,后端流量转发的两种方法

方法 A:标准反向代理 (Direct Proxy_Pass)

这是最直接、最常见的方法,适用于主服务器和后端服务器之间网络通畅(例如在同一内网或公网可直接访问)的情况。

操作:

在主服务器 (1.2.3.4) 的 Nginx 配置文件中,通过 proxy_pass 直接指向后端服务器的 IP 和端口。

Nginx 配置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 处理博客子域名的流量
server {
listen 80;
server_name blog.4399.com;

location / {
# 直接转发到博客服务器的 8080 端口
proxy_pass http://10.0.0.2:8080;
proxy_set_header Host $host;
# ... 其他 header 设置 ...
}
}

方法 B:SSH 反向隧道代理 (Advanced & Secure)

这种方法适用于后端服务器无法被主服务器直接访问(例如后端在内网 NAT 之后)或需要极高安全性的场景。它通过建立一个加密的 SSH 隧道来传输数据。

工作原理: 由后端服务器主动连接主服务器,并在主服务器上“开一个口”,Nginx 只需将流量送到这个本地的“口”即可。

操作步骤:

  1. 在后端服务器上建立隧道:

    登录后端博客服务器 (10.0.0.2),执行以下命令,与主服务器 (1.2.3.4) 建立连接。

    1
    2
    3
    4

    # 命令格式:ssh -fN -R [主服务器端口]:localhost:[后端服务端口] [主服务器用户名]@[主服务器IP]
    ssh -fN -R 9001:localhost:8080 user@1.2.3.4

    • fN: 让 SSH 在后台运行。
    • R 9001:localhost:8080: 意思是将主服务器 (1.2.3.4) 的 9001 端口收到的所有流量,通过这条加密隧道,转发到当前后端服务器 (10.0.0.2) 的 8080 端口。
    • 提示: 为保证隧道断开后能自动重连,建议使用 autossh 工具。
  2. 在主服务器上配置 Nginx:

    现在,主服务器上的 Nginx 只需将 blog.4399.com 的流量转发给自己的 9001 端口即可。

    Nginx 配置示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    # 处理博客子域名的流量 (通过 SSH 隧道)
    server {
    listen 80;
    server_name blog.4399.com;

    location / {
    # 转发到本地 SSH 隧道的入口端口
    proxy_pass http://localhost:9001;
    proxy_set_header Host $host;
    # ... 其他 header 设置 ...
    }
    }

PS

无论使用哪种方法,所有子域名的流量都会经过 Nginx 主服务器。因此,务必确保这台服务器拥有足够大的带宽,以防成为性能瓶颈。