分类目录归档:技术笔记

如何调试 Vim 脚本

来源: 如何调试 Vim 脚本

使用 -D 参数可以开启 Debug 模式, 在 Debug 模式中可以使用 cont, next, interrupt, step, quit 等调试命令, 以及 breakadd, breakdel 来添加和移除断点。 使用 -u 来禁止加载任何配置文件,使用 :source 命令逐个加载。 使用 :set verbose:set verbosefile配置变量 可以设置日志级别和输出文件, -V 启动参数也可以起到同样的作用。

日志级别

在介绍 Debug 之前有必要先介绍如何查看运行日志,本文只介绍到日志级别的设置方法和日志文件的设置方法。 日志级别是由 verbose 配置变量 控制的。这是一个默认为零的数字,级别越高输出越详细。 verbose 非零时 Vim 就会在当前窗口显示日志信息,例如 :set verbose=20 就可以开启几乎所有日志。 这些级别的描述如下:

>= 1    读写 viminfo 文件时
>= 2    当 ":source" 一个文件时,通常是载入一个配置文件
>= 5    所有搜索到的 tag 文件和 include 文件
>= 8    执行到 autocommands 的文件
>= 9    每次执行到 autocommand 时
>= 12   每次执行到 function 时
>= 13   产生、捕获、结束处理、忽略一个异常时
>= 14   ":finally" 语句中等待的所有命令
>= 15   每一个执行到的 Ex 命令(截断到 200 字符)

更多 verbose 选项变量的信息可以参考 :help vbs。 日志级别也可以通过 -V 启动参数来设置。例如:

vim -V20 main.cpp

日志文件

如果你试过上述日志级别的设置方式会发现日志直接输出在当前屏幕,这会影响在当前 Vim 中的连贯操作。 我们可以通过 :set verbosefile 把日志输出到文件中。例如把它输出到 vim.log 文件:

set verbosefile=vim.log

然后在另一个 Shell 中 tail 这个文件,我们就可以一边使用 Vim 一边查看它的日志了:

tail -f vim.log

更多 verbosefile 选项变量的信息可以参考 :help verbosefile。 日志文件也可以通过 -V 启动参数来设置。例如设置级别为 20,输出文件名为 vim.log

vim -V20vim.log main.cpp

注意级别与文件名之间不能加空格,且文件名不得以数字开头(否则会被识别为级别的一部分)。

零配置启动

有时为了定位问题,我们可以禁止 Vim 自动加载配置文件和插件,手动地逐个加载 Vim 脚本。 需要先零配置启动 Vim,通常设置 -u 即可:

vim -u NONE main.cpp

如果你在使用 GVim 可能需要使用 -U,具体情况可参考 :help -UStackOverflow。 启动后使用 :source {filename} 命令来逐个加载 Vim 配置即可。

Debug 方法

在启动 Vim 时添加 -D 参数即可开启调试模式,Vim 会在第一行配置文件出中断并进入 Debug 模式:

vim -D main.cpp

中断后会进入 Debug 模式,你可以看到 > 提示符。此时你就可以输入 Debug 命令,操作方式和 gdb 非常类似:

  • cont: 继续执行直到下一个断点。
  • quit: 停止当前过程,继续执行到下一个断点。
  • step: 执行当前断点处的指令,执行完后仍然回到 Debug 模式。
  • next: 与 step 一样,但会直接执行完整个方法调用和文件引入(source)
  • interrupt: 停止当前过程,执行下一个命令前回到 Debug 模式。
  • finish: 执行完当前脚本或方法调用,回到 Debug 模式。

掌握 Debug 命令后下一个事情就是打断点,可以在函数和文件的相应行号处添加断点:

breakadd func [lineNumber] functionName
breakadd file [lineNumber] fileName
breakadd here

同样的语法可以用来移除断点,关键字从 breakadd 换为 breakdel

breakdel func [lineNumber] functionName
breakdel file [lineNumber] fileName
breakdel here

还可以按照编号移除 breakdel {number} 和移除全部 breakdel *。 更详细的参数和命令可以参考 :help debug

小技巧

上述日志和调试命令已经足够定位 Vim 脚本的问题了。 下面介绍几个小技巧可以方便一些场景下的操作:

像 Chrome 控制台一样,可以 Debug 某个命令:

:debug CommandName

也可以直接 debug 某个函数,不需要 Vim 先进入 Debug 模式:

:debug call Foo()

类似地,日志级别也可以在调用函数时进行设置:

:20verbose call Foo()

References

tailscale zerotier wireguard 横向对比

References: Gemini 2.5 Pro

本文对比一下 Tailscale、ZeroTier 和 WireGuard 这三个流行的虚拟网络/VPN 解决方案。

需要明确一点:

  • WireGuard 是一个现代、快速、简单的 VPN 协议。它本身只负责建立安全的点对点隧道,不包含用户管理、密钥分发、NAT 穿透等便捷功能。
  • TailscaleZeroTier 都是基于特定技术(Tailscale 基于 WireGuard,ZeroTier 有自己的协议)构建的完整解决方案或服务。它们旨在简化虚拟网络的创建和管理,内置了用户认证、设备管理、NAT 穿透等功能。

关键特性对比

以下是它们的详细对比:

特性 Tailscale ZeroTier WireGuard (协议本身)
核心技术 基于 WireGuard 协议 自有协议 (类似 L2 虚拟交换机) WireGuard 协议
易用性/设置 非常高:通过 SSO (Google/Microsoft/GitHub 等) 登录,自动配置和密钥管理。 :创建网络,安装客户端,加入网络即可。需要手动授权设备。 :需要手动生成密钥对、交换公钥、配置 IP 地址和路由。
管理方式 中心化控制平面:Tailscale 服务器(官方托管)或自托管 Headscale 管理设备、ACL 等。 中心化控制平面:ZeroTier Central (官方托管) 或自托管 Controller 管理网络、成员、规则等。 去中心化:每个节点独立配置,没有中央管理点。
NAT 穿透 优秀:使用 ICE (STUN/TURN) 和 DERP 中继服务器,穿透成功率高。 优秀:使用 UDP 打洞和中继服务器,穿透能力强。 基本/无:协议本身不处理 NAT 穿透,需要手动端口转发或依赖其他工具。
网络模型 点对点 Mesh 网络 (设备间尽可能直连,失败则通过 DERP 中继)。更像 L3 网络。 虚拟 L2 以太网 (支持广播/多播)。更像一个虚拟交换机。 点对点隧道。
性能 非常高:继承 WireGuard 的高性能,中继时会有损耗。 :性能良好,但协议开销通常略高于 WireGuard。 非常高:协议简洁,内核态实现,开销极小。
安全性 基于 WireGuard 的强加密。通过身份提供商进行认证。ACL 控制访问。 自有加密协议。通过网络 ID 和管理员授权控制成员。规则引擎可定义 L2/L3 策略。 现代、公认的强加密 (ChaCha20, Poly1305)。基于公钥认证。
灵活性/定制 中等:专注于简化连接,配置选项相对较少,但够用。 :提供虚拟 L2 网络,更灵活地构建复杂网络拓扑,规则引擎强大。 非常高:完全控制配置的方方面面,但需要专业知识。
跨平台 广泛支持 (Linux, Windows, macOS, iOS, Android, Synology 等) 广泛支持 (Linux, Windows, macOS, iOS, Android, NAS 等) 广泛支持 (内核集成或用户态实现)
开源情况 客户端开源。官方控制平面闭源,但有开源替代 Headscale。 核心和 Controller 开源,可完全自托管。 完全开源
价格模式 Freemium:免费版功能慷慨 (个人用户足够),提供付费订阅计划。 Freemium:免费版限制网络成员数量 (25 个),提供付费订阅计划。 完全免费 (协议本身)。
主要使用场景 个人设备互联、小型团队远程访问、简单的站点到站点连接、替代传统 VPN。 创建复杂的虚拟局域网、IoT 设备连接、需要 L2 功能 (如广播) 的场景、跨地域组网。 作为 VPN 协议的基础、性能要求极高的点对点连接、需要完全控制和透明度的场景。

总结和选择建议:

  • 选择 WireGuard (原生)
    • 如果你对网络有深入了解,不介意手动配置。
    • 追求极致的性能和完全的控制权。
    • 只需要建立简单的点对点或 Hub-Spoke VPN。
    • 想将其作为底层协议构建自己的解决方案。
  • 选择 Tailscale
    • 你最看重 简单易用,希望“开箱即用”。
    • 需要将分散在各处的个人设备或小团队设备安全地连接起来。
    • 依赖 Google/Microsoft/GitHub 等账号进行认证。
    • 对 WireGuard 的高性能感兴趣,但不想手动配置。
    • 可以接受官方的控制平面(或者有能力自托管 Headscale)。
  • 选择 ZeroTier
    • 你需要创建一个功能更像物理局域网的虚拟网络 (需要 L2 功能,如设备发现、广播等)。
    • 需要更精细的网络规则控制 (ZeroTier 的规则引擎更强大)。
    • 希望有一个可以完全自托管的、包含控制器的开源解决方案。
    • 对网络成员数量有要求(免费版有限制)。

简单来说:

  • 简单连接设备 -> Tailscale
  • 构建虚拟局域网 (特别是需要 L2 功能) -> ZeroTier
  • 基础协议、高性能、完全控制 -> WireGuard

Tailscale 和 ZeroTier 解决了 WireGuard 原生配置复杂和缺乏管理功能的问题,但引入了中心化的控制平面(尽管可以自托管)。你需要根据自己的具体需求、技术能力和对中心化/易用性的偏好来做出选择。

自托管效能对比

我们来深入对比一下这三者在自托管(Self-Hosted)场景下的情况。

这里需要明确:

  • 自托管 Tailscale:通常指使用 Headscale 作为开源的、自托管的 Tailscale 控制服务器。客户端仍然使用官方 Tailscale 客户端。可能还需要自托管 DERP 中继服务器以优化 NAT 穿透和性能。
  • 自托管 ZeroTier:指使用官方提供的源码或容器部署 ZeroTier Network Controller。客户端使用官方 ZeroTier 客户端。可能还需要自托管 Moons(根服务器/中继)以提升连接稳定性和性能。
  • 自托管 WireGuard:通常指不依赖任何中心化控制平面,手动配置所有 WireGuard 节点,或者使用一些简单的第三方 UI/工具(如 wg-easy, WireGuard UI 等)来辅助管理配置文件和密钥,但这本质上还是点对点配置的延伸,没有像 Headscale 或 ZeroTier Controller 那样的动态协调能力。

自托管场景对比:

特性 Headscale (自托管 Tailscale) 自托管 ZeroTier Controller 手动 WireGuard / 简单管理 UI
易用性 (设置) 中等:部署 Headscale (二进制/Docker),配置数据库,可能需要反向代理。需要 Linux/容器知识。官方文档和社区支持较好。 中等到困难:部署 Controller (二进制/Docker),配置较为复杂,需要理解其网络和身份概念。文档相对 Headscale 可能略少或分散。 非常困难 (纯手动) / 中等 (用 UI):纯手动极繁琐。简单 UI 降低了密钥/配置生成难度,但网络拓扑和路由仍需较多手动干预。
易用性 (客户端添加/管理) 较容易:客户端使用 `tailscale login –login-server
` 指向自建服务器,通过 Headscale CLI 或 Web UI 授权。 中等:客户端加入网络 ID,需要在 Controller UI/API 手动授权每个设备。流程相对 Headscale 稍繁琐。 困难:需要手动在每个相关节点上添加对端公钥、IP、路由等信息。极易出错且耗时。
性能 (数据平面) 优异:基于 WireGuard,P2P 直连性能极佳。若通过自托管 DERP 中继,性能取决于中继服务器的配置和网络。 良好:P2P 直连性能好,但协议开销比 WireGuard 稍大。通过自托管 Moons 中继,性能取决于 Moon 服务器。 最优异:纯 WireGuard 隧道,无额外开销,性能仅受限于硬件和网络路径。
灵活性 中等:专注于 L3 连接和 ACL 访问控制。支持子网路由、出口节点。缺乏 L2 功能。配置相对标准化。 :提供虚拟 L2 网络,支持广播/多播,规则引擎强大,可实现复杂的网络拓扑和策略。 非常高:完全控制所有网络参数、路由、防火墙规则。可以构建任何拓扑,但一切都需要手动实现。
拓展性 (Scalability) 良好:Headscale 设计上可支持大量设备。可通过优化数据库、部署多个 Headscale 实例和自建 DERP 网络来扩展。 良好:Controller 可以管理大量设备和网络。可通过优化主机、自建 Moons 网络来扩展。大规模部署经验社区分享可能略少。 差 (纯手动) / 依赖工具:纯手动管理方式无法扩展。依赖的管理工具决定了其扩展性。
延迟 (Latency) :P2P 直连延迟极低 (WireGuard 本身)。自托管控制平面和 DERP 可以部署在离用户更近的位置,可能优化信令和中继延迟。 较低:P2P 直连延迟好。自托管 Controller 和 Moons 可以就近部署,优化信令和中继延迟。协议本身可能有微小额外延迟。 最低:无任何外部依赖(除网络路径本身),纯 P2P 延迟最低。
维护复杂度 中等:需要维护 Headscale 服务、数据库、可能的反向代理和 DERP 服务器。关注 Headscale 和 Tailscale 客户端的更新。 中等到高:需要维护 Controller 服务、可能的数据库、Moons 服务器。Controller 本身的配置和升级可能比 Headscale 复杂。 低 (稳定后) / 高 (变更时):一旦配置好且不变,几乎无需维护。但任何节点增减、IP 变更都需要手动修改大量配置。
关键优势 平衡易用性与自托管控制,继承 Tailscale 优点,社区活跃。 强大的 L2 网络能力和规则引擎,完全开源的 Controller。 极致性能、简单协议、完全控制、无中心依赖。
关键劣势 缺乏 L2 功能,依赖社区维护 Headscale 及相关生态。 设置和管理相对复杂,性能相比 WireGuard 略逊。 管理噩梦(尤其节点多时),无自动 NAT 穿透,需要大量手动配置。

总结与建议:

  • 选择 Headscale (自托管 Tailscale)

    • 如果你喜欢 Tailscale 的易用模型(特别是设备认证和连接方式),但需要完全的数据主权和控制权。
    • 主要需求是安全的 L3 设备互联、远程访问、子网路由。
    • 能够接受部署和维护 Headscale、数据库以及可能的 DERP 中继。
    • 这是在自托管场景下,易用性、性能和控制权之间的一个极佳平衡点
  • 选择自托管 ZeroTier Controller

    • 你的核心需求是构建复杂的虚拟局域网,特别是需要 L2 功能(如设备发现、广播、网桥)。
    • 你需要非常精细的网络访问控制规则
    • 你希望 Controller 本身也是完全开源的,并且不介意相对更复杂的设置和管理。
    • 你需要自托管 Moons 来保证连接质量。
  • 选择手动 WireGuard / 简单管理 UI

    • 你的网络规模非常小(例如,几个节点之间的固定连接),且不常变动。
    • 你追求极致的性能和最低的延迟,并且对网络有深入理解。
    • 你需要完全、彻底的控制权,不信任任何中心化协调服务(即使是自托管的)。
    • 你愿意承担繁重的手动配置和维护工作,或者找到的简单 UI 能满足你的基本需求且你了解其局限性。

核心考量:自托管 Headscale 和 ZeroTier Controller 都是为了解决纯 WireGuard 在规模化部署和易用性上的短板,它们引入了控制平面来自动化协调工作。你需要付出的代价是部署和维护这个控制平面。选择哪个,主要看你对 L2 功能的需求、对易用性/管理复杂度的偏好以及对社区生态的依赖程度。

总结

三个方案都使用过,tailscale 接入最快,wiregard 最灵活,zerotier 似乎延迟较低。总之得在自己的环境下尝试,最后选择最合适的那一个。

Tailscale 自建 Derp

TL;DR

必需:将 env DERP_DOMAIN 设置为您的域

docker run -e DERP_DOMAIN=derper.your-domain.com -p 80:80 -p 443:443 -p 3478:3478/udp fredliang/derper

也有其他不使用域名的方法,参考文献自行探索

References

Ceph 检查 rbd io 排名

好的,在 Ceph 中查看哪个 RBD (RADOS Block Device) 镜像的 I/O 读写最高,最常用的方法是使用 rbd perf image iotoprbd perf image iostat 命令。

这两个命令都需要指定 存储池 (pool) 的名称,因为 RBD 镜像是存在于特定的存储池中的。

方法一:使用 rbd perf image iotop (推荐)

这个命令会实时显示指定存储池中各个 RBD 镜像的 I/O 统计信息,并默认按总 I/O 操作数 (IOPS) 或总带宽排序,非常直观。

  1. 首先,确定 RBD 镜像所在的存储池。 如果不确定,可以使用 rbd pool lsceph osd lspools 列出所有存储池。
  2. 执行命令:
    rbd perf image iotop 
    <poolname>

将 `

` 替换为实际的存储池名称。 例如,如果你的 RBD 镜像在名为 `rbd_images` 的存储池中: “`bash rbd perf image iotop rbd_images “` 3. **查看输出:** 该命令会持续刷新输出,通常包含以下列: * `image`: RBD 镜像的名称。 * `rd_ops`, `wr_ops`, `total_ops`: 每秒读、写、总操作次数 (IOPS)。 * `rd_bytes`, `wr_bytes`, `total_bytes`: 每秒读、写、总带宽 (Bytes/s)。 输出通常会按 `total_ops` 或 `total_bytes` 降序排列,排在最前面的就是当前 I/O 最高的 RBD 镜像。按 `Ctrl+C` 退出。 **方法二:使用 `rbd perf image iostat`** 这个命令与 `iotop` 类似,也显示 I/O 统计,但格式可能略有不同,有时显示的是累积值或平均值,具体取决于 Ceph 版本和配置。 1. **确定存储池名称。** 2. **执行命令:** “`bash rbd perf image iostat “` 同样,将 ` ` 替换为实际的存储池名称。 例如: “`bash rbd perf image iostat rbd_images “` 3. **查看输出:** 分析输出中的读写 IOPS 和带宽列,找出数值最高的镜像。这个命令也可能持续刷新,按 `Ctrl+C` 退出。 **重要注意事项:** 1. **指定存储池:** 这两个命令都**必须**指定存储池名称。你需要对每个包含 RBD 镜像的存储池分别运行这些命令,才能找到整个集群中 I/O 最高的镜像。 2. **性能计数器:** 这些命令依赖于 Ceph 的性能计数器 (performance counters)。通常这些计数器是默认启用的,但如果遇到问题,可能需要检查相关配置。 3. **权限:** 执行这些命令需要相应的 Ceph 用户权限(通常是 client.admin 或具有针对特定存储池读写权限的用户)。 4. **Ceph 版本:** 命令的具体行为和输出格式可能随 Ceph 版本略有变化。 5. **Ceph Dashboard:** 如果你部署了 Ceph Dashboard(Web UI),通常在 Dashboard 的 “Block” -> “Images” 部分也能方便地查看各个 RBD 镜像的实时性能指标,包括 IOPS 和带宽,并且可以进行排序。这对于图形化监控来说更加友好。 **总结:** 使用 `rbd perf image iotop ` 是查找指定存储池中当前 I/O 活动最密集的 RBD 镜像的最直接和常用的命令行方法。记得要检查所有相关的存储池。

k8s csi-driver-nfs的一个坑

TL;DR

发现 k8s csi 组的社区项目 csi-driver-nfs v4.10v4.11 至少这两个版本存在删除 pv 时会连带将整个根删除的问题。

声明 StorageClass 时虽然支持 subDir ,类似这样:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: nfs-aliyun-gz
provisioner: nfs.csi.k8s.io
parameters:
  share: "/csi"
  server: "28364f4a1fa-eok75.cn-guangzhou.nas.aliyuncs.com"
  #server: "172.26.12.20"
  #subDir: "${pvc.metadata.namespace}/${pvc.metadata.name}"
reclaimPolicy: Delete
#volumeBindingMode: WaitForFirstConsumer
volumeBindingMode: Immediate
allowVolumeExpansion: true
mountOptions:
#  - nolock,tcp,noresvport
  - vers=3,nolock,proto=tcp,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport

但如果类似这样使用 subDir 声明路径,同命名空间下的其他 pvc 删除,会导致整个 subDir 根目录都被删除。目前官方 pr 已经修复,但实测还是有问题,有空再研究一下代码,不知道是不是刻意为之。

回溯 issuer 历史发现是有人提了 bug 发现目录下出现很多空目录,认为需要删除,修复者修复这一问题时错误的将整个根删除。为了规避这一问题,暂时回退到更早的 4.9 版本 csi

helm upgrade --install csi-driver-nfs csi-driver-nfs/csi-driver-nfs --namespace kube-system --version v4.  
9.0 -f values.yaml

升级版本要谨慎,新装版本要充分测试,特别是这种涉及数据安全的!

最后发现 sig 组还有一个 nfs-subdir-external-provisioner 可以看一下。

References

k3s 容器 mirror 配置方法

TL; DR

root@tencent-sh1:~# cat /etc/rancher/k3s/registries.yaml 
mirrors:
  "docker.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-dockerhub/$1"
  "registry.k8s.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-registry-k8s-io/$1"
  "ghcr.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-registry-ghcr-io/$1"
  "quay.io":
    endpoint:
      - "https://harbor.xxx.me"
    rewrite:
      "^(.*)": "mirror-registry-quay-io/$1"

以上是我的配置,在 harbor 中镜像以上镜像源,之后这样 配置即可。

如果没有路径,比如使用 registry 镜像,忽略 rewrite 部分即可。

References

wordpress 使用 k8s 部署并使用 nginx ingress 代理无限 302 到 ssl 问题解决

发现容器化之后,wp 网站打开一直尝试 302 到 https 的页面,即使我当前已经是 https 了,经过排查是由于代理提供了 ssl 但 wordpress 不知道,默认会再重定向一次,出现无限 302 。

TL; DR

解决方法很简单,只需在 wp 配置文件 /wp-config.php 中增加这几行即可解决:

define( 'FORCE_SSL_ADMIN', true );
// in some setups HTTP_X_FORWARDED_PROTO might contain 
// a comma-separated list e.g. http,https
// so check for https existence
if( strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false )
    $_SERVER['HTTPS'] = 'on';

方法来源于官网.

References

ArchLinux pacman 一键找到最快的镜像源清单

curl -s "https://archlinux.org/mirrorlist/?country=CN&protocol=https&use_mirror_status=on" | sed -e 's/^#Server/Server/' -e '/^#/d' | rankmirrors -n 5 -

运行这个命令,即可自动从 archlinux 官方 mirror 清单获取中国 (CN) 的镜像清单,并调用 rankmirrors 测速得到速度最快的前5个。

配置到 /etc/pacman.d/mirrorlist 目录中即可使用。

References

LLM 聚合 API 价格对比

List

  • gpt-4
  • gpt-4o
  • claude-3-7-sonnet-20250219
  • `claude-3-7-sonn

单位:Inout/Output /M

Model gpt-4o gpt-4o-mini deepseek-r1 deepseek-v3 claude-3-7-sonnet claude-3-5-sonnet
UniAPI $0.2871/$1.1484 $2.376/$11.88 $2.376/$11.88
GPTAPI ¥0.07/¥0.14 ¥5.25/¥26.25 ¥5.25/¥26.25
OpenRouter $5/$7 $3/$15 $3/$15
AiHubMix $0.62/$2.48 $3.3/$16.5
V3 API $1.8/$7.2 $7.4/$37

Refereneces