普通视图

发现新文章,点击刷新页面。
昨天以前WAYJAM's Blog

告别 NPM Token:迁移到 Trusted Publishing 全记录

2026年2月15日 13:26

发生了什么?

2025 年 12 月 9 日,npm 正式永久撤销了所有 Classic Token。与此同时,npm 引入了基于会话的认证(2 小时有效期)和新的 CLI Token 管理方式。这意味着:

  • 所有 Classic Token 立即失效,无法恢复
  • npm login 现在只发放 2 小时的短期会话令牌
  • CI/CD 场景推荐使用 Granular Access TokenOIDC Trusted Publishing

我的 GitHub 项目 CI 工作流自然也挂了 —— 因为 Secrets 里存的还是那个已被吊销的 Classic Token。

Trusted Publishing 是什么?

简单说,Trusted Publishing 利用 OIDC(OpenID Connect) 协议,让 CI/CD 平台(如 GitHub Actions)直接与 npm 交换短期凭证来发布包,完全不需要存储任何 npm token

优势很明显:

  • 没有长期 Token,不存在泄露风险
  • 自动附带 Provenance(来源证明),包的构建来源可追溯
  • 配置一次,后续发版无需维护密钥

目前支持 GitHub Actions(GitHub-hosted runners)和 GitLab CI/CD(shared runners),不支持自托管 runner。

版本要求:npm CLI ≥ 11.5.1,Node.js ≥ 22.14.0。

迁移步骤

1. 在 npmjs.com 配置 Trusted Publisher

进入目标 Package 的 Setting 页面,找到 Trusted Publisher 部分,填写:

  • Owner:GitHub 用户名或组织名
  • Repository:仓库名
  • Workflow filename:工作流文件名(含 .yml 扩展名,如 publish.yml
  • Environment(可选):GitHub 部署环境名

注意:文件名、仓库名区分大小写,必须与 workflow 文件完全一致。

2. 配置 GitHub Actions Workflow

核心改动就两点:声明 OIDC 权限、直接用 npm publish

以下是我最终可用的 workflow(使用 pnpm):

name: publish

on:
  release:
    types: [published, released]
  workflow_dispatch:

jobs:
  publish:
    runs-on: ubuntu-latest

    permissions:
      id-token: write  # OIDC 必需
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 24
          registry-url: 'https://registry.npmjs.org'
          cache: 'pnpm'

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Build
        run: pnpm run build

      - name: Publish
        if: github.repository == 'wayjam/picgo-plugin-s3'
        run: pnpm publish --no-git-checks --ignore-scripts --provenance --access public

不再需要 NPM_TOKEN、不再需要 NODE_AUTH_TOKEN,也不再需要 JS-DevTools/npm-publish 这类第三方 Action。直接原生 npm publish / pnpm publish 就行。

注意事项与故障排除

package.json 必须包含 repository 字段

npm 的 Provenance 机制会校验发布环境与包元数据是否匹配。如果 package.json 中没有 repository 字段(或值为空),会返回 422 错误:

npm error 422 Unprocessable Entity - PUT https://registry.npmjs.org/<package-name>
- Error verifying sigstore provenance bundle: Failed to validate repository information:
  package.json: "repository.url" is "", expected to match
  "https://github.com/<owner>/<repo>" from provenance

确保 package.json 中有正确且与 npm 配置一致的 repository 字段:

"repository": {
  "type": "git",
  "url": "git+https://github.com/<owner>/<repo>.git"
}

pnpm 添加 --no-git-checks(可选)

pnpm publish 默认会执行 git 检查,拒绝从非主分支或脏工作区发布。而 GitHub Actions 的 actions/checkout 默认是 Detached HEAD 状态,会直接触发该检查失败。如果是非 main/master 发布则需加上这个参数:

pnpm publish --no-git-checks --ignore-scripts --provenance --access public

Workflow 必须显式声明权限

没有在 job 级别声明 permissions,OIDC token 交换会直接失败。以下两项缺一不可:

permissions:
  id-token: write  # 用于 OIDC token 交换
  contents: read   # 用于 checkout 代码

报错 “Access token expired or revoked”

看到这个错误不一定是 token 问题。更常见的原因是:npm 网页端填写的 workflow 文件名与实际文件名不一致(包括大小写、.yml/.yaml 扩展名),导致 OIDC 验证时仓库/workflow 信息比对失败。逐项核对 npm 设置中的 owner、repo、workflow filename 即可。

清理

配置成功并验证发布正常后:

  1. 从 GitHub 仓库的 Settings → Secrets 中删除旧的 NPM_TOKEN
  2. 如果之前用的是 JS-DevTools/npm-publish,可以从 workflow 中移除该依赖
  3. 在 npm 包设置中,可以启用 “Require two-factor authentication and disallow tokens” 进一步加固安全

参考

Sing-box:IPv6 优先出站与 Netflix 强制 IPv6 分流

2026年1月16日 21:59

很多 VPS 的 IPv4 出口因为被滥用、共享 NAT 或者 IP 段"脏",导致访问某些服务时体验不佳。相比之下,IPv6 出口往往更干净——毕竟 IPv6 地址段大、用的人少、很多服务商对 IPv6 的风控也没那么严格。

本文记录如何在 sing-box 上实现:

  • 全局 IPv6 优先:所有出站流量优先走 IPv6,IPv6 不可用时自动回退 IPv4
  • Netflix 强制 IPv6:针对 Netflix 这类对 IP 质量敏感的服务,强制只走 IPv6(不回退)

前置检查

确认宿主机 IPv6 可用

在配置 sing-box 之前,先确保系统网络层的 IPv6 是通的:

# 查看 IPv6 地址(需要看到 scope global 的地址)
ip -6 addr show

# 查看 IPv6 默认路由
ip -6 route show

# 测试 IPv6 连通性
ping6 -c 4 2001:4860:4860::8888

# 测试 IPv6 HTTPS
curl -6 -I https://www.google.com

如果上述命令不通,先解决网络层问题(检查 VPS 控制台是否开启 IPv6、路由配置、防火墙规则等),否则后面配置再多也没用。

检测流媒体解锁状态

使用一键脚本检测当前 IP 的流媒体解锁情况:

bash <(curl -L -s check.unlock.media)

这个脚本会分别测试 IPv4 和 IPv6 出口对各大流媒体平台的解锁状态。如果你发现 IPv6 的解锁结果明显优于 IPv4,那就更有理由把流量往 IPv6 上引。

核心概念:domain_strategy

sing-box 的出站(outbound)支持 domain_strategy 参数,用于控制域名解析策略:

策略 行为
prefer_ipv4 优先 A 记录,失败回退 AAAA
prefer_ipv6 优先 AAAA 记录,失败回退 A
ipv4_only 只用 A 记录,无 A 则失败
ipv6_only 只用 AAAA 记录,无 AAAA 则失败
  • 默认出站设置 prefer_ipv6:大部分流量优先走 IPv6,保留 IPv4 作为兜底
  • Netflix 专用出站设置 ipv6_only:强制走 IPv6,不回退

配置:全局 IPv6 优先

最简单的做法是在默认的 direct 出站上加一个 domain_strategy

{
  "outbounds": [
    {
      "type": "direct",
      "tag": "direct",
      "domain_strategy": "prefer_ipv6"
    }
  ],
  "route": {
    "auto_detect_interface": true,
    "final": "direct"
  }
}

这样所有走 direct 出站的流量都会优先尝试 IPv6。如果目标域名没有 AAAA 记录或 IPv6 不通,会自动回退到 IPv4,不影响正常使用。

进阶:Netflix 强制 IPv6

如果想对 Netflix 更激进一点——强制只走 IPv6,不要回退——可以单独定义一个出站,并用规则集把 Netflix 流量指过去。

新增 IPv6 专用出站

{
  "outbounds": [
    {
      "type": "direct",
      "tag": "IPv6-only",
      "domain_strategy": "ipv6_only"
    },
    {
      "type": "direct",
      "tag": "direct",
      "domain_strategy": "prefer_ipv6"
    }
  ]
}

配置 Netflix 规则集

{
  "route": {
    "rules": [
      {
        "rule_set": [
          "geoip-netflix",
          "geosite-netflix"
        ],
        "outbound": "IPv6-only"
      }
    ],
    "rule_set": [
      {
        "tag": "geoip-netflix",
        "type": "remote",
        "format": "binary",
        "url": "https://github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo/geoip/netflix.srs",
        "download_detour": "direct",
        "update_interval": "1d"
      },
      {
        "tag": "geosite-netflix",
        "type": "remote",
        "format": "binary",
        "url": "https://github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo/geosite/netflix.srs",
        "download_detour": "direct",
        "update_interval": "1d"
      }
    ],
    "auto_detect_interface": true,
    "final": "direct"
  }
}

说明

  • 双规则集geoip-netflix 匹配 Netflix 服务器 IP 段,geosite-netflix 匹配 Netflix 相关域名,双保险。
  • 规则集来源:使用 MetaCubeX 维护的规则集,.srs 是 sing-box 专用的二进制格式,体积小、加载快。
  • download_detour:规则集下载走 direct,避免下载本身被绕来绕去。
  • update_interval:每天自动更新一次规则集。

重启 sing-box 并检查状态:

systemctl restart sing-box
systemctl status sing-box

注意事项

Outbound Tag 不能重复

sing-box 启动时会校验所有 outbound 的 tag 必须唯一。如果你用的是一键脚本安装(如 233boy),可能存在"主配置 + 子配置合并"的机制,需要注意:

  • 不要在多个配置文件里定义同名的 tag
  • 典型报错:duplicate outbound/endpoint tag: direct
  • 解决:统一在主配置里定义,子配置里不要重复

ipv6_only 的风险

ipv6_only 意味着"没有 AAAA 记录就直接失败",不会回退 IPv4。如果 Netflix 某些边缘节点或 CDN 在你的网络环境下拿不到 AAAA,访问就会中断。

如果遇到问题,可以把 Netflix 的出站策略从 ipv6_only 改回 prefer_ipv6

客户端配置(Clash/Mihomo)

如果你的客户端使用 Clash 或 Mihomo,需要确保域名是在远端(服务器侧)解析,而不是在本地解析。否则 domain_strategy 就失效了——因为流量到达 sing-box 时已经是 IP 地址,服务器无法再根据域名决定走 IPv4 还是 IPv6。

客户端模式 域名解析位置 客户端是否需要配置
Fake-IP 远端 无需额外配置
Real-IP 本地 需开启 sniffer + override-destination

Fake-IP 模式(推荐)

如果客户端使用 Fake-IP 模式,域名天然就是在远端解析的:

  • 客户端 DNS 返回一个假的 IP(如 198.18.x.x
  • 实际域名随连接一起发送到服务器
  • 服务器侧的 sing-box 拿到的是域名,domain_strategy 正常生效

Fake-IP 模式下无需额外配置,开箱即用。

dns:
  enable: true
  enhanced-mode: fake-ip
  fake-ip-range: 198.18.0.1/16
  # ...

Real-IP 模式(需开启嗅探)

如果客户端使用 Real-IP(redir-host)模式,DNS 查询会在本地完成,流量到达服务器时已经是 IP 地址。这种情况下需要在 Mihomo 客户端开启流量嗅探(sniffer),从流量中还原出域名并覆盖目标地址。

在 Mihomo 配置中添加:

sniffer:
  enable: true
  override-destination: true
  sniff:
    HTTP:
      ports: [80, 8080-8880]
    TLS:
      ports: [443, 8443]
    QUIC:
      ports: [443, 8443]

关键参数

  • enable: true:开启流量嗅探
  • override-destination: true:用嗅探到的域名覆盖原始目标地址,这样服务端的 domain_strategy 才能基于域名工作

注意override-destination 会改变连接的目标地址。如果你的规则依赖 IP 匹配(如 GEOIP),需要确保规则顺序正确,或者同时配置 GEOSITE 规则。

[All In One] HomeLab 虚拟化 iKuai + Mihomo - IPv6 支持

2026年1月15日 23:59

本文是 All In One HomeLab 虚拟化 iKuai + Mihomo 的 IPv6 扩展配置,介绍如何在现有的透明网关(Debian/LXC 作为 Clash/Mihomo 透明网关)架构中启用 IPv6 支持。

本文默认目标:只让透明网关本机具备 IPv6 出口能力,内网终端不强制获取公网 IPv6;如果希望终端也能拿到公网 IPv6,通常还需要额外处理 NDP前缀下发(DHCPv6-PD) 等问题,正文相关步骤会顺带提示。

iKuai IPv6 配置

WAN1 配置 IPv6

确保 iKuai 的主外网口(WAN1)能够获取 IPv6 地址:

  1. 如果是 PPPoE 拨号:在 iKuai 的 WAN1 设置中,勾选 “获取 IPv6” 选项。
  2. 如果是 DHCP:确保光猫支持 IPv6 并已开启 IPv6 功能,iKuai 会自动获取 IPv6 前缀和地址。

LAN 口配置 IPv6(推荐无状态 / 混合)

最容易踩坑的点:如果 iKuai 只开“有状态 DHCPv6”,而透明网关用的是 iface wan0 inet6 auto(SLAAC/无状态),它默认不会去跑 DHCPv6 客户端,最终表现就是“怎么都拿不到 IPv6”。

Debian 是透明网关(必须开 net.ipv6.conf.all.forwarding=1),更推荐让 iKuai 在 LAN 侧提供 RA + SLAAC(无状态),或者 混合模式(SLAAC + DHCPv6)

  1. 进入 网络设置 > LAN1 > IPv6 设置
    • 外网接口:动态接口;绑定 wan1
    • 内网接口:
      • 模式:选择 无状态混合(无状态 + 有状态)
      • RA 发送:开启(默认网关/前缀等信息主要依赖 RA)
      • M(Managed)标识:建议 关闭(M=1 会暗示“用 DHCPv6 拿地址”;Android 不支持有状态 DHCPv6
      • O(Other)标识:建议 开启(用于下发 DNS 等其他信息,具体取决于 iKuai 的实现)

如果希望“终端设备也能拿公网 IPv6”,至少要保证 LAN 侧能 SLAAC(无状态)或混合模式(否则 Android 直接拿不到 IPv6 地址);另外在“透明网关/旁路网关”这种拓扑里,终端要真正拥有公网 IPv6,往往还会遇到 NDP前缀下发(PD) 的问题:

  • NDP:常见做法是在 Debian 上做 NDP 代理(例如 ndppd),让上游认为下游终端“就在同一链路”。
  • PD:更正统的做法是上游支持 DHCPv6-PD,让 Debian 申请一个子前缀(例如 /64)再分配给下游 LAN。

如果“只想让透明网关这一台设备拿 IPv6”,两种做法:

  • 做法 A(更省心):仍然用 无状态/混合,但在 iKuai 上用 IPv6 防火墙策略限制终端的 IPv6 出口(让它们即便拿到 IPv6,也访问不了公网)。
  • 做法 B(坚持有状态 DHCPv6):继续用 DHCPv6 白名单 只给网关发地址,但请注意 Debian 侧要改成 DHCPv6 客户端,见下文“有状态方案”。(参考:特定设备分配 IPv6

ikuai-ipv6

透明网关 IPv6 配置

方案 1:无状态 或者 混合(推荐)

编辑 /etc/network/interfaces,为 wan0(连接到 iKuai LAN)启用 SLAAC,并在“网关/转发模式”下强制接受 RA(透明网关必开 forwarding=1,Linux 默认会停止接收 RA;不收 RA 往往拿不到 IPv6 默认路由):

auto lo
iface lo inet loopback

auto lan0
iface lan0 inet static
        address 192.168.200.1/24
        gateway 192.168.200.2

auto wan0
iface wan0 inet static
        address 192.168.100.2/24

# 无状态(SLAAC)获取 IPv6 地址
iface wan0 inet6 auto
        # 关键:作为网关开启 forwarding 后,Linux 默认不再接受 RA,需要设为 2
        post-up sysctl -w net.ipv6.conf.wan0.accept_ra=2

应用配置:

systemctl restart networking

方案 2:iKuai 纯有状态 DHCPv6(不推荐,但可用)

如果在 iKuai 上做了 DHCPv6 白名单,只给透明网关发 IPv6,那么 Debian 侧就别用 inet6 auto 了,改成 DHCPv6:

auto wan0
iface wan0 inet static
        address 192.168.100.2/24

# 有状态 DHCPv6 获取 IPv6 地址(需要 DHCPv6 客户端)
iface wan0 inet6 dhcp
        post-up sysctl -w net.ipv6.conf.wan0.accept_ra=2

说明:即使用 DHCPv6 拿到了地址,默认路由通常仍依赖 RA,所以 accept_ra=2 依然很关键。

PVE 特殊配置

如果是 PVE LXC 容器记得调整 LXC 配置,否则重启后 network 配置会被重置

pve-lxc-ipv6

开启内核转发(关键)

既然是透明网关,Debian 必须开启 IPv6 转发功能。编辑 /etc/sysctl.conf

# 作为路由/网关:开启 IPv6 转发
net.ipv6.conf.all.forwarding=1
net.ipv6.conf.default.forwarding=1

# 仅在要做 NDP 代理时才需要
# net.ipv6.conf.all.proxy_ndp=1

应用配置:

sysctl -p

验证 IPv6 地址获取

检查透明网关是否成功获取 IPv6 地址和默认路由:

# 查看 wan0 的 IPv6 地址
ip -6 addr show wan0

# 查看 IPv6 路由表(重点看 default)
ip -6 route show

# 测试 IPv6 连通性
curl -6 ifconfig.me

Mihomo IPv6 支持

配置 IPv6 DNS

编辑 /etc/mihomo/config.yaml,添加 IPv6 DNS 服务器:

dns:
  enable: true
  listen: 0.0.0.0:1053
  ipv6: true  # 启用 IPv6 DNS 查询
...

参考资料

[All In One] HomeLab 虚拟化 iKuai + Mihomo

2025年8月23日 23:59

本文思路基本遵循 iKuai与Openwrt的有机结合:传统旁路由方案的完美替代,主要差异点如下:

  • 虚拟化平台: unRaid 替换为 Proxmox VE (PVE)
  • 透明网关: OpenWRT 替换为 Debian + Mihomo
  • DNS 服务: paopaodns 替换为 SmartDNS + AdGuard Home 组合

网络拓扑

为方便描述,下文统一将处理出站流量的虚拟机称为透明网关 (gateway)

架构核心思想

本方案采用“主路由 + 透明网关”的模式。iKuai 作为主路由,以其出色的稳定性、多线负载和流控能力负责整体网络的 DHCP、NAT 和流量调度。透明网关则是一个专职处理特定流量(如国际流量)的“处理器”。iKuai 通过策略路由(PBR)将特定流量“扔”给透明网关处理,处理完毕后再由透明网关“扔”回给 iKuai,由 iKuai 统一出口。这种架构实现了功能解耦,稳定性和灵活性兼得。

  • iKuai (主路由):

    • WAN1: 用于 PPPoE 拨号或从光猫 DHCP 连接到外网。
      • 物理网卡:eth3 (硬件直通)
    • LAN: 地址为 192.168.100.1,桥接所有局域网内的网卡,构成统一的二层网络。
      • eth1, eth2: 硬件直通,连接 AP 或其他有线设备。
      • vmbr0: PVE 虚拟网桥,专用于 PVE 的 Web 管理界面访问,桥接到 LAN 使其可从内网访问。
      • vmbr2: PVE 虚拟网桥,作为通用网桥,接入其他虚拟机或容器。
    • WAN2: 逻辑接口,作为 iKuai 与透明网关之间的数据通道。
      • IP 地址: 192.168.200.2
      • 网关指向: 192.168.200.1 (透明网关的 LAN 地址)
      • 网卡:vmbr1 (PVE 虚拟网桥)
  • gateway (透明网关):

    • LAN: 接收来自 iKuai (WAN2) 的流量。
      • IP 地址: 192.168.200.1
      • 网卡:vmbr1 (连接 iKuai WAN2)
    • WAN: 将处理后的流量发回 iKuai 的 LAN。
      • IP 地址: 192.168.100.2
      • 网关指向: 192.168.100.1 (iKuai 的 LAN 地址)
      • 网卡:vmbr2 (连接 iKuai LAN)

网络拓扑图

PVE 网络设置

pve-net

iKuai 安装与初始化

虚拟机配置

在 PVE 中为 iKuai 创建虚拟机时,以下是一些关键配置建议:

  • 机型: q35。提供更现代的硬件特性,是使用 PCIe Passthrough (硬件直通) 的推荐选项。
  • BIOS: 默认 (SeaBIOS)。对 iKuai 兼容性良好。
  • SCSI 控制器: VirtIO SCSI single。使用半虚拟化驱动 VirtIO 可最大化磁盘 I/O 性能。

安装步骤

  1. 从 iKuai 官网下载 32 位 ISO 镜像并上传到 PVE。

  2. 新建虚拟机,可以暂时不添加网络设备,稍后统一配置。

  3. 硬件直通 (PCI Passthrough): 进入 iKuai 虚拟机的“硬件”菜单,将物理网卡(除 PVE 管理口外)直通给虚拟机。

    什么是硬件直通 (PCI Passthrough)?

    它允许虚拟机绕过 Hypervisor (PVE) 直接控制物理硬件设备。对于网卡而言,这意味着虚拟机可以获得接近物理机的网络性能和延迟,并能直接利用网卡的硬件特性,是追求极致性能的软路由方案首选。

  4. 添加虚拟网卡: 添加 vmbr0, vmbr1, vmbr2 这三个 PVE 虚拟网桥作为虚拟网卡。

  5. 启动虚拟机,iKuai 系统将自动安装。完成后关闭虚拟机,在“选项”中调整引导顺序至硬盘,并移除 ISO 镜像。

  6. 重启虚拟机,通过 PVE 控制台初始化 iKuai。根据 PVE 硬件信息中显示的 MAC 地址,将网卡与 iKuai 的接口(如 eth0, eth1)一一对应绑定,并设置 LAN 口 IP。

系统内设置

使用 192.168.100.1 访问 iKuai Web 管理界面,完成配置。

  • WAN1: 配置为 PPPoE 拨号或 DHCP,作为主外网出口。
  • WAN2: 配置为静态 IP,绑定 vmbr1 对应的虚拟网卡。
    • IP 地址:192.168.200.2
    • 网关:192.168.200.1
    • 关键配置: 将此线路设置为 默认线路。此举的目的是让所有流量在默认情况下都经过透明网关,再通过后续的分流规则将国内流量“豁免”。
    • 开启 掉线切换,并设置线路检测,确保透明网关异常时网络可用性。
  • LAN1:
    • 在“高级设置”中,启用 链路桥接,将所有内网接口(物理和虚拟)桥接在一起,创建一个统一的广播域,确保所有设备都在同一局域网内。

wan2

配置分流

  1. 进入 流控分流 > 多线负载,添加自定义运营商。将 china_ip_list 的 IP 段复制进来。由于 iKuai UI 存在输入长度限制,需要将 IP 列表分批添加。
  2. 添加负载均衡规则,将目标地址为国内 IP 的流量,强制走 WAN1 出口,实现国内流量直连。

分流

添加端口分流规则防止回环

什么是路由回环 (Routing Loop)?

在本架构中,一个数据包的路径可能是:客户端 -> iKuai -> 透明网关 -> iKuai -> 互联网。当数据包从透明网关返回 iKuai 时,如果 iKuai 依然按照默认路由(即 WAN2)处理,就会把这个包再次发给透明网关,形成 iKuai -> 网关 -> iKuai 的死循环,导致网络中断。

为防止回环,需添加一条规则:将源地址为透明网关 IP (192.168.100.2) 的所有流量,强制从 WAN1 出口发出。

防止回环

透明网关

推荐使用 LXC 容器而非完整虚拟机,因为它更轻量,资源开销小,网络性能接近宿主机。我选择 PVE 官方 CT 模板的 debian 12 作为 mihomo 和 dns 服务的运行时。

新建 LXC 容器

LXC 配置文件 (/etc/pve/lxc/111.conf) 示例:

arch: amd64
cores: 2
features: nesting=1
hostname: gateway
memory: 2048
net0: name=wan0,bridge=vmbr2,gw=192.168.100.1,hwaddr=BC:24:11:B8:D5:64,ip=192.168.100.2/24,type=veth
net1: name=lan0,bridge=vmbr1,hwaddr=BC:24:11:AF:9E:03,ip=192.168.200.1/24,type=veth
ostype: debian
rootfs: local:111/vm-111-disk-0.raw,size=8G
swap: 2048
unprivileged: 1
nameserver: 223.5.5.5

网络配置

编辑 LXC 容器内的 /etc/network/interfaces,为 lan0(接收 iKuai 流量)和 wan0(发回 iKuai)配置静态 IP。

auto lo
iface lo inet loopback

auto lan0
iface lan0 inet static
        address 192.168.200.1/24
        gateway 192.168.200.2

auto wan0
iface wan0 inet static
        address 192.168.100.2/24

Mihomo

安装与配置

  1. 从 Github Releases 下载二进制文件并放置到 /usr/local/bin/
  2. 创建配置目录 /etc/mihomo
  3. 编写核心配置文件 /etc/mihomo/config.yaml。与本方案相关的关键配置如下:
# ... (其他配置)
tproxy-port: 7894 # TProxy 监听端口,需与 nftables 脚本一致
routing-mark: 6666 # Mihomo 自身发出流量的标记,用于在 nftables 中豁免,防止代理自身

dns:
  enable: true
  listen: 0.0.0.0:1053 # DNS 服务监听端口
# ... (其他配置)

服务化 (Systemd)

创建 /etc/systemd/system/mihomo.service 文件。

Systemd Capabilities (权能) 说明

CapabilityBoundingSetAmbientCapabilities 是 Linux 的安全特性,用于授予进程精确的权限,而非给予完整的 root 权限。

  • CAP_NET_ADMIN: 允许程序修改网络接口、路由表、防火墙规则等。TProxy 设置需要此权限。
  • CAP_NET_RAW: 允许程序使用 RAW 和 PACKET 套接字。
  • CAP_NET_BIND_SERVICE: 允许程序绑定到 1024 以下的特权端口。

这种配置方式比直接使用 User=root 更安全。

[Unit]
Description=Mihomo Daemon, a Clash API compatible core.
After=network.target

[Service]
Type=simple
LimitNPROC=500
LimitNOFILE=1000000
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE
Restart=always
ExecStartPre=/usr/bin/sleep 1s
ExecStart=/usr/local/bin/mihomo -d /etc/mihomo
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target

TProxy 透明代理

什么是 TProxy?

TProxy (Transparent Proxy) 是 Linux 内核的一项功能,它允许一个应用程序(如 Mihomo)在不修改数据包目的 IP 地址和端口的情况下,接收并处理本应发往其他地址的数据包。相比传统的 REDIRECT (DNAT) 模式,TProxy 能让服务端程序看到真实的客户端源 IP 地址,且对 UDP 的支持更完善,性能也更优。

步骤一:开启 IP 转发

tee /etc/sysctl.conf <<-'EOF'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
EOF

步骤二:配置防火墙与策略路由

启动脚本: /etc/mihomo/ip-route-poststart.sh

#!/bin/sh
# 1. 在策略路由表 100 中,添加一条默认路由,所有流量从 lan0 发出
ip route add local default dev lan0 table 100
# 2. 添加一条规则:为防火墙标记(fwmark)为 1 的数据包,应用策略路由表 100
ip rule add fwmark 1 lookup 100
# 3. 加载 nftables 防火墙规则集
nft -f /etc/mihomo/nftrules.nft

停止脚本: /etc/mihomo/ip-route-stop.sh

#!/bin/sh
# 按相反顺序清理规则
nft delete table ip clash
ip route del local default dev lan0 table 100
ip rule del fwmark 1 lookup 100

nftables 规则集: /etc/mihomo/nftrules.nft

#!/usr/sbin/nft -f

## 只处理指定网卡的流量,要和ip规则中的接口操持一致
define interface = lan0

## clash的透明代理端口
define tproxy_port = 7894

## clash打的标记(routing-mark)
define clash_mark = 6666

## 常规流量标记,ip rule中加的标记,要和ip规则中保持一致,对应 "ip rule add fwmark 1 lookup 100" 中的 "1"
define default_mark = 1

## 本机运行了服务并且需要在公网上访问的tcp端口(本机开放在公网上的端口),仅本地局域网访问的服务端口可不用在此变量中,以半角逗号分隔
define local_tcp_port = {
    22,      # ssh,按需设置
    8000-9999  # http
}

## 要绕过的局域网内tcp流量经由本机访问的目标端口,也就是允许局域网内其他主机主动设置DNS服务器为其他服务器,而非旁路由
define lan_2_dport_tcp = {
    53, 5353, 5354     # dns查询
}

## 要绕过的局域网内udp流量经由本机访问的目标端口,也就是允许局域网内其他主机主动设置DNS服务器为其他服务器,而非旁路由;另外也允许局域网内其他主机访问远程的NTP
服务器
define lan_2_dport_udp = {
    53, 5353, 5354,    # dns查询
    123    # ntp端口
}

## 保留ip地址
define private_address = {
    127.0.0.0/8,
    100.64.0.0/10,
    169.254.0.0/16,
    224.0.0.0/4,
    240.0.0.0/4,
    10.0.0.0/8,
    172.16.0.0/12,
    192.168.0.0/16
}

table ip clash {
    ## 保留ipv4集合
    set private_address_set {
        type ipv4_addr
        flags interval
        elements = $private_address
    }

    ## prerouting链
    chain prerouting {
        type filter hook prerouting priority filter; policy accept;
        ip protocol { tcp, udp } socket transparent 1 meta mark set $default_mark accept # 绕过已经建立的连接
        meta mark $default_mark goto clash_tproxy                                        # 已经打上default_mark标记的属于本机流量转过来的,直接进入透明代理
        fib daddr type { local, broadcast, anycast, multicast } accept                   # 绕过本地、单播、组播、多播地址
        tcp dport $lan_2_dport_tcp accept                                                # 绕过经由本机到目标端口的tcp流量
        udp dport $lan_2_dport_udp accept                                                # 绕过经由本地到目标端口的udp流量
        ip daddr @private_address_set accept                                             # 绕过目标地址为保留ip的地址
        goto clash_tproxy                                                                # 其他流量透明代理到clash
    }

    ## 透明代理
    chain clash_tproxy {
        ip protocol { tcp, udp } tproxy to :$tproxy_port meta mark set $default_mark
    }

    ## output链
    chain output {
        type route hook output priority filter; policy accept;
        oifname != $interface accept                                   # 绕过本机内部通信的流量(接口lo)
        meta mark $clash_mark accept                                   # 绕过本机clash发出的流量
        fib daddr type { local, broadcast, anycast, multicast } accept # 绕过本地、单播、组播、多播地址
        udp dport { 53, 5353, 123 } accept                             # 绕过本机dns查询、NTP流量
        tcp sport $local_tcp_port accept                               # 绕过本地运行了服务的tcp端口,如果并不需要从公网访问这些端口,可以注释掉本行
        ip daddr @private_address_set accept                           # 绕过目标地址为保留ip的地址
        ip protocol { tcp, udp } meta mark set $default_mark           # 其他流量重路由到prerouting
    }
}

步骤三:集成到 systemd 服务

/etc/systemd/system/mihomo.service 加入声明周期脚本,令 systemd 维护 mihomo 的时候自动执行:

ExecStartPost=/etc/mihomo/ip-route-poststart.sh
ExecStop=/etc/mihomo/ip-route-stop.sh

DNS

DNS 查询链

本方案构建了一个“责任链模式”的 DNS 解析流程:客户端 -> AdGuard Home -> SmartDNS -> 最终上游 DNS。每个环节只做自己的专职工作:AdGuard Home 负责过滤广告和黑名单,SmartDNS 负责对国内外域名进行智能、快速的解析。这种分层设计使得结构清晰,易于排错和维护。

  1. 安装 SmartDNS: apt install smartdns。配置好国内外两组上游 DNS 服务器。为获得最佳性能,禁用测速 (speed-check-mode none) 并开启缓存持久化。
  2. 安装 AdGuard Home:
    • 在 AdGuard Home 的上游 DNS 设置中,仅填入 SmartDNS 的监听地址(如 127.0.0.1:5353)。
    • 关闭 AdGuard Home 的查询缓存,避免与 SmartDNS 重复缓存。
    • 去除 客户端 DNS 查询限制
  3. 整合: 在 iKuai 的 DHCP 服务 -> LAN 口设置 中,将主 DNS 服务器地址修改为透明网关的地址:192.168.200.1。这样所有内网设备获取到的 DNS 服务器就是 AdGuard Home。

dns

自动更新配置

为了实现低维护运行,各类配置应尽可能自动化更新。

  • iKuai 大陆 IP 分流: 手动更新即可,IP 列表变动不频繁。
  • Mihomo 订阅配置: 推荐部署私有 sub-store 用于聚合。在透明网关中设置 cron 定时任务,定期 wget 新的配置文件并 systemctl restart mihomo
  • SmartDNS 国内域名列表: 定时任务从 Olixn/china_list_for_smartdns 等项目下载更新列表并重启 SmartDNS。
  • AdGuard Home 过滤规则: 直接在 AdGuard Home 的 Web UI 中订阅优秀的过滤列表即可自动更新。
  • AdGuard Home 过滤规则自定义规则同步: TODO。

参考资料

LiteLLM 多模型 API 中转

2025年2月3日 22:46

前言

随着大语言模型(LLM)应用场景的不断扩展,管理和统一调用多个 AI 模型接口的需求日益突出。目前市面上有多种解决方案,每个都有其独特的优势和适用场景:

  • one-api/new-api: 这类项目提供完整的 Web UI 界面,支持多用户管理、使用量统计等功能,适合团队或小型组织使用。
  • uni-api: 采用 Provider-First 的配置思路,通过简单的配置文件启动,特别适合个人用户快速配置多个模型服务。
  • openrouter: 作为一个集中式的 AI 模型网关,支持多个主流服务商,但对某些区域性服务(如国内的 siliconflow)支持有限,且无法配置同一服务商的多个账号。
  • LiteLLM: 既可作为 SDK 使用,也可作为独立的 LLM Gateway 部署。采用 Model-First 的配置方式,虽然在多模型配置上略显繁琐,但提供了更细粒度的模型控制能力,而且代码质量也比 uni-api 更好。

所以我使用了一段时间的 new-api 和 uni-api 之后,现在切换到 LiteLLM。本文将介绍 LiteLLM 的部署方案和一些实用的配置技巧。

Docker Compose 部署

LiteLLM 支持多种部署方式,使用 Docker Compose 是最简单和可靠的方案之一。以下是一个基础的部署配置:

name: litellm
services:
  litellm:
    image: ghcr.io/berriai/litellm:main-latest # 或者选用 stable
    volumes:
     - ./config.yaml:/app/config.yaml
    command:
     - "--config=/app/config.yaml"
    ports:
      - "4000:4000" # Map the container port to the host, change the host port if necessary
    environment:
        DATABASE_URL: "postgresql://llmproxy:dbpassword9090@db:5432/litellm"
        STORE_MODEL_IN_DB: "True" # allows adding models to proxy via UI
  db:
    image: postgres
    restart: always
    environment:
      POSTGRES_DB: litellm
      POSTGRES_USER: llmproxy
      POSTGRES_PASSWORD: dbpassword9090
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -d litellm -U llmproxy"]
      interval: 1s
      timeout: 5s
      retries: 10

配置技巧

LiteLLM 采用 Model-First 的配置方式,这在配置多个相同提供商的模型时可能会显得重复。虽然官方推荐使用环境变量来复用配置:

model_list:
  - model_name: gpt-3.5-turbo
    litellm_params:
      model: azure/gpt-turbo-small-e
      api_base: https://my-endpoint-europe-berri-992.openai.azure.com/
      api_key: "os.environ/AZURE_API_KEY_EU"

但这种方式会导致配置信息分散在配置文件和环境变量中。对于个人用户来说,我们可以利用 YAML 的锚点(anchor)特性来实现更优雅的配置:

my_providers:
  gemini_1: &gemini_1
    api_key: xxx
  gemini_2: &gemini_2
    api_key: yyy
  provider_1: &provider_1
    api_key: zzz
    api_base: 'http://example.com/v1/chat/completions'

model_list:
  - model_name: gemini-1.5-pro
    litellm_params:
      model: gemini/gemini-1.5-pro
      <<: *gemini_1
  - model_name: gemini-1.5-pro
    litellm_params:
      model: gemini/gemini-1.5-pro
      <<: *gemini_2
  - model_name: gpt-4
    litellm_params:
      model: openai/gpt-4
      <<: *provider_1

这种配置方式有以下优势:

  1. 配置集中管理,易于维护
  2. 减少重复代码,提高配置效率
  3. 方便快速切换或更新 API 密钥
  4. 清晰的配置结构,便于理解和修改

部署完成之后就可以直接通过 MasterKey 和 API 进行使用了,如果需要访问 UI 界面则打开 http://your-endpoint-example.com/ui 即可。

结语

LiteLLM 作为一个灵活的 LLM 网关解决方案,能够很好地满足个人用户和小型团队的需求。通过合理使用 YAML 的高级特性,我们可以构建出更加优雅和易维护的配置。在选择 LLM 网关工具时,建议根据实际使用场景、规模和管理需求来权衡不同方案的优劣。

MacOS Bootstrp with Nix

2025年1月29日 21:57

最近更换了 M4 Pro 的 MacBook Pro,面临着重新配置开发环境的挑战。虽然我之前已经在 dotfiles 项目中维护了 tmux、alacritty 和 neovim 等工具的配置,但实际上还有许多其他组件需要重新设置。起初我考虑编写一个 setup 脚本来自动化这个过程,但在研究过程中接触到了 Nix 这个声明式的包管理器,它的设计理念让我眼前一亮。

Nix 就像一把强大的瑞士军刀,不仅能解决 macOS 的环境配置,还可以统一管理我的 homelab 和 VPS 的环境。回想起之前那些需要手动配置的环境,确实浪费了不少时间。

这次我决定彻底转向 Nix 模式来解决环境配置的问题。不得不说,Nix 的学习曲线确实很陡峭,如果不是现在有 AI 辅助,可能还在苦苦挣扎。经过一番努力,我终于完成了自己的 Nix 配置。

下文简单记录一下我的一个全新 macOS 环境的初始化流程,其他发行版的 Linux 和 NixOS 的配置目前还没写,等需要用的时候逐步完善。

Xcode

xcode-select --install
sudo xcodebuild -license
softwareupdate --install-rosetta --agree-to-license

Brew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

我的设计是 GUI 软件由 Brew 管理,其他所有命令行工具软件都交由 nixpkgs 管理,但是由于现在是全新初始化环境,后面用到了 justfile,此时还是先使用 Brew 安装一下:brew install just

Nix

使用 Determinate Nix Installer 来安装 nix with nix-darwin:

curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | \
  sh -s -- install

部署

git clone https://github.com/wayjam/dotfiles.git
just # see all commands

Ref

即便有 AI 助手辅助,很多问题也是很难解决,对于 Nix 我目前还是有很多不熟悉和不清楚的地方,还得依靠目前网络上开源的各路配置。

Fluency Support Full Rss

2024年10月27日 10:20

最近 Follow 这个内容浏览器十分热门, 我把自己博客添加进去后发现 Hugo 的 RSS 默认并没有全文输出.

所以给当前在用的 Fluency 主题增加了一个可选配置,可以开启全文 RSS 输出.

又水一篇.

This message is used to verify that this feed (feedId:55671961817022580) belongs to me (userId:67409085208279040). Join me in enjoying the next generation information browser https://follow.is.

[All In One] PVE 中 OpenWrt LXC 重启问题

2024年8月18日 20:00

从去年开始,我的 OpenWrt 是安装在 PVE 中的 LXC 容器中,但一直以来都有一个问题,OpenWrt 关机后就无法再次启动了。

我是将 enp6s0(有线网卡)、wlp2s0(无线网卡)硬件直通给 LXC 容器:

lxc.net.0.type: phys
lxc.net.0.link: enp6s0
lxc.net.0.flags: up
lxc.net.1.type: phys
lxc.net.1.link: wlp2s0
lxc.net.1.flags: up

查看 LXC 的启动日志发现网卡重命名失败:

faile to rename network device physNBNrWC to enp6s0
lxc_network_move_created_netdev_priv: 3549 invalid argument - Failed to move network device "wlp2s0" with ifindex 11 to network namespace 539922 and rename to physxAAvs5
lxc_spawn: 1840 failed to create the network

通过 dmesg 也是发现重命名失败的信息,尝试给 LXC 配置中网卡增加明确的命名 lxc.net.0.name: wan 后,有线网卡不再出现重命名失败。

但是无线网卡 PVE 依然无法操作成功。通过 ip link show 发现,LXC 关闭后,原有物理接口 wlp2s0 也消失了,网卡丢失一段时间,然后大概 5-10 分钟之后,接口重新出现,并且正常使用。怀疑是否是设备出现了问题

root@pve:~# lspci -k | grep -i wire -A 2
02:00.0 Network controller: MEDIATEK Corp. MT7922 802.11ax PCI Express Wireless Network Adapter
        Subsystem: Foxconn International, Inc. MT7922 802.11ax PCI Express Wireless Network Adapter
        Kernel driver in use: mt7921e
        Kernel modules: mt7921e

另外通过lsmod |grep 7921(网卡驱动),发现驱动加载正常,即便 modprobe mt7921e 重新加载驱动也没有效果。怀疑是否是电源管理导致设备进入了某种状态。在 PVE 主机和 OpenWrt 内部使用 iw 将无线网卡的电源管理禁用也没有效果。

使用 rfkill 查看是否有被 软/硬 禁用,也正常:

root@pve:~# rfkill list
0: hci0: Bluetooth
        Soft blocked: no
        Hard blocked: no
1: hci0: Wireless LAN
        Soft blocked: no
        Hard blocked: no

最终无奈之下尝试将设备重置,竟然成功了!

echo /sys/bus/pci/devices/02:00.0/remove # 设备号使用 lspci 获取
echo "1" > /sys/bus/pci/rescan

解决方案

LXC 容器的配置增加关机后置脚本:

root@pve:~# vi /usr/local/bin/pcie_hot_reset.sh # (脚本内容)
root@pve:~# chmod +x /usr/local/bin/pcie_hot_reset.sh

root@pve:~# tail /etc/pve/lxc/100.conf
lxc.hook.post-stop: /var/lib/lxc/openwrt_post_stop.sh

root@pve:~# cat /var/lib/lxc/openwrt_post_stop.sh
/usr/local/bin/pcie_hot_reset.sh 02:00.0

脚本参考@alex.forencich 的回答 https://unix.stackexchange.com/questions/73908/how-to-reset-cycle-power-to-a-pcie-device

Tabby 终端配置以及配置全平台同步

2024年3月11日 23:15

自从有了 Windows 的个人电脑之后,经常在不同平台的环境中切换。每个平台都有一个终端软件,SSH 配置成了一个大难题。

一开始用的是全平台的 Termius,基础功能基本满足需求,并且也有 iOS 客户端,后来发现它的多端同步需要 Pro 订阅,价格十分昂贵。我在 macOS 上用的其实是 Alacritty,基础配置也通过了 Github 同步,但是 SSH 配置的同步则无能为力。

后来找到了 Tabby 这个全平台的开源 Terminal 工具(Linux/Windows/macOS,遗憾不支持移动端),它可以自建 Web 同步服务,在不同平台之间同步基础配置以及SSH配置。

Step1: Github 创建应用

tabby-web 支持 Github / Google / Gitlab 等 SSO 登录,此处使用 Github 为例:

Step2:启动 tabby-web

name: tabby
services:
  tabby:
    image: ghcr.io/eugeny/tabby-web:latest
    restart: always
    ports:
      - 8080:80
    environment:
      - TZ=Asia/Shanghai
      - DATABASE_URL=postgres://user:password@postgresql/tabby
      - PORT=80
      - DEBUG=False
      - APP_DIST_STORAGE=file:///data # 支持本地文件系统、S3等
      - SOCIAL_AUTH_GITHUB_KEY=  #上一步获取的client id
      - SOCIAL_AUTH_GITHUB_SECRET= #上一步获取的client secret
    volumes:
      - /data/docker/tabby/data:/data

启动后访问 web 服务(登录需要配置可访问,即 Github 可以回调到上述配置的地址,比如使用 《Cloudflare Tunnel + Nginx Proxy Manager:服务穿透》中提到的方式)。

  • 访问 ${tabby-web地址}/login 登录(或者点击首页左下角登录)
  • 选择 Github 认证登录
  • 登录成功后如下所示,会显示同步 Token

Step3:tabby 配置

打开 tabby,进入_[设置 - 配置同步]_。依次输入同步主机如 https://example.com/xxx 和 同步Token,回车。tabby 会进行校验并设置成功,打开自动同步,完成。

一些问题

  • 保险库密码输入太频繁,编辑每个 Host 的时候都需要输入密码

Cloudflare Tunnel + Nginx Proxy Manager:服务穿透

2024年3月10日 13:43

Nginx Proxy Manager

Nginx Proxy Manager 是一个 Nginx 的可视化代理管理器,自带面板,操作极其简单,非常适合配合 Docker 搭建的应用使用。

version: '3.8'
name: proxy
services:
  nginx:
    image: 'jc21/nginx-proxy-manager:latest'
    restart: unless-stopped
    container_name: nginx
    ports:
      - '80:80'
      - '81:81'
      - '443:443'
    volumes:
      - /data/docker/nginx:/data
      - /data/docker/letsencrypt:/etc/letsencrypt
networks:
  appnet:
    external: true

其他需要被 Nginx 反代的服务理论上不需要配置 ports 暴露端口,只需要都加入同一个 docker 网络,在 Nginx 配置的使用域名指向即可。

另外 Nginx Proxy Manager 可以一键申请 SSL 证书,并且会自动续期。注意,申请证书时,Let’s Encrypt 会访问域名进行校验,所以先通过 Cloudflare 配置可外部访问之后,再进行配置证书。

Cloudflare Tunnel

创建 Tunnel

首先进入Cloudflare 管理面板的 Zero Trust - Network - Tunnels:

  1. 点击 Create a tunnel,创建一个 Cloudflared 类型的 Tunnel,点击下一步
  2. 输入 Tunnel 名称,点击下一步
  3. 页面将会显示各个平台的安装方式,在这里可以找到 Tunnel 的 token

安装 cloudflared

根据指引的安装方式启动连接成功后,页面 Connectors 将会显示连接的实例。

更多安装指南:https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/downloads/

Docker 启动

以下为 Docker 启动案例:

  • 网络模式设置成 host
  • Transport协议设置成 http2:默认 cloudflared 以 quic 协议建立链接,如果当前网络环境 UDP 链接使用有问题则设置成 http2
  • 增加 DNS 配置:如果当前环境的 DNS 无法解析 cloudflare 相关域名(如 clash 的 fake-ip 模式影响),则可以指定 DNS。
version: '3.0'
name: cloudflare
services:
  cloudflared_tunnel:
    restart: always
    container_name: cloudflare_tunnel
    hostname: cloudflare_tunnel
    image: cloudflare/cloudflared:latest
    command: tunnel --no-autoupdate --metrics=127.0.0.1:3333 run --token xxx
    network_mode: host
    user: "0:0"
    environment:
      PUID: 0
      PGID: 0
      TUNNEL_TRANSPORT_PROTOCOL: http2
    dns:
    - 1.2.4.8
networks:
  appnet:
    external: true

配置路由

Step 1 【Nginx Proxy Manager】:新增一个 Proxy Host

  • 配置 Publicly Accessible
  • 不配置 SSL

Step 2【Cloudflare】进入Tunnel 配置页面,找到对应 Tunnel 点击 Configure 进入 Tunnel 配置,这里我们要配置 Public hostnames

  • 分别填写域名(需已经使用 Cloudflare 解析)
  • 转发目标 Service 的 URL(注意,此时必须先选择 HTTP 协议,因为还未申请成功 SSL)

Step 3 【Nginx Proxy Manager】找到对应的 Proxy 配置

  • 切换到 SSL 选择 Request a new SSL Certification
  • 选择 Force HTTPS(根据实际情况)
  • 稍等片刻,证书就申请和配置好了

Step 4 【Cloudflare】进入 Tunnel 配置页面,找到对应 Public hostname,点击 Configure

  • 将 Service 的 Type 切换成 HTTPS(如果不切换且 Nginx 配置了 Force HTTPS,就会无限重定向)
  • 展开 AddAdditional application settings - TLS - 在 Origin Server Name 中填入对应反代的域名,否则 Nginx 不能正确转发。(部分域名敏感的应用还需配置 HTTP Settings 的 HTTP Host Header 才能正确识别,根据实际情况配置)

大功告成,到此内网的服务就可以通过 Cloudflare 和 Nginx Proxy Manager 映射到外网。

内网访问速度优化

在内网访问已经配置好映射的域名的时候,也会被解析至 Cloudflare 然后通过 Tunnel 转发,这影响了访问速度,如果内网是使用类似 Adguard 的解析服务,可以通过重写 DNS,将域名直接指向 Nginx Proxy Manager,减少链路长度,提升访问体验。

其他解析服务如 Clash、dnsmasq,甚至于在路由或者PC上配置 hosts 也能达到同样效果

PVE 7 升级 8 笔记

2024年2月25日 10:37

升级前准备

  1. 磁盘空间检查 df -h
  2. 备份重要文件
  3. 当前 PVE7 是最新的小版本,执行此命令更新 apt update && apt upgrade && apt dist-upgrade
  4. 进行升级检测,执行 pve7to8 命令

升级检测

此步骤需确认执行结果FAILURES: 0,WARNINGS部分也要查看是否存在关键的影响。

resolved node ip not configured or active for 'pve' 错误 可能是修改过安装 PVE 时候的网段,cat /etc/hosts 查看,修改到匹配当前 PVE 的默认IP 地址即可.

最终再次执行完整升级检测 pve7to8 --full

升级

源更新

Debian 源sed -i 's/bullseye/bookworm/g' /etc/apt/sources.list

PVE源sed -i -e 's/bullseye/bookworm/g' /etc/apt/sources.list.d/pve-enterprise.list 如果还添加了其他的仓库比如 no-subscription 也需要更新。

Ceph软件包源echo "deb http://download.proxmox.com/debian/ceph-quincy bookworm no-subscription" > /etc/apt/sources.list.d/ceph.list

  • 执行 grep '' /etc/apt/sources.list* /etc/apt/sources.list.d/* review 是否还有 bullseye 源地址未修改为 bookworm.
  • 最终执行 apt update 刷新源索引(如果出现 401 错误,检查 enterprise 源是否已注释)

开始升级

apt dist-upgrade -y

升级完成之后 reboot 重启

升级过程中可能有的提示

  1. 如果出现 /etc/issue 相关提示,按 N 即可
  2. 如果出现提示 libc 等是否需要重启服务,切换成 YES 并回车
  3. 如果出现 openssh-server 选择 Keep the local version currently installed
  4. 如果出现 /etc/lvm/lvm.conf 提示,如果没有自己更新过此文件则按_Yes_(使用包维护者的版本)
  5. 如果出现 /etc/default/grub 相关提示,建议使用它提供的 diff 界面查看差异(如果配置过硬件直通等修改过此文件的场景),再选择
  6. 如果出现 pve-enterprise.list 修改的提示,按 N 回车

升级后

  1. 清理 apt autoremove
  2. 检查
    • 硬件直通是否正常
    • 如果更新过微码则需检查微码 grep 'stepping\|model\|microcode' /proc/cpuinfo
    • 虚拟机、LXC等功能是否正常

PVE 安装 Android x86 & 安装 Magisk

2023年6月11日 21:05

下载镜像

首先选择镜像,我一开始尝试的是成 CM 的 7.0 镜像但由于是全虚拟的系统,这个版本的系统开启 ADB 后,虽然可以开启网络端口监听,但无法首次就用网络连接(无法 auth)。PrimeOS 也是遇到同样的问题甚至无法直接开启网络 ADB。后来换用 Android x86 9.0 版本,自带 root 权限,默认开启 ADB 且支持首次网络连接。

镜像下载:https://mirrors.tuna.tsinghua.edu.cn/osdn/android-x86/

折腾完发现有打好 Magisk 的镜像,https://androidx86magisk.osdn.io/,如果早点看到本文完结。

提取 boot.img

  1. 从 ISO 镜像中复制出 kernelramdisk.img 这两个文件
mkdir -p /mnt/temp
mount -t iso9660 -o loop android.iso /mnt/temp
cp /mnt/temp/kernel .
cp /mnt/temp/ramdisk.img .
umount /mnt/temp
  1. 安装工具软件 mkbootimgabootimg(在 Linux 开发系统,如以下命令以 Debian 系为例)
apt install android-tools-mkbootimg abootimg
  1. kernelramdisk.img 打包成 boot.img
mkbootimg --kernel kernel --ramdisk ramdisk.img -o boot.img

mkbootimg 是 Android 系统中一个用于创建内核镜像文件的工具,常见用法和参数说明如下:

mkbootimg \
--kernel <kernel-image> \
[--ramdisk <ramdisk-image>] \
[--second <second-image>] \
[--dtb <dtb-image>] \
[--cmdline <kernel-command-line>] \
[--base <address>] \
[--pagesize <size-in-bytes>] \
[--ramdisk_offset <address>] \
[--tags_offset <address>] \
[--ivt_offset <address>] \
[--os_version <version>] \
[--os_patch_level <yyyy-mm-dd>] \
[--header_version <version>] \
-o <image-output-file>

参数说明:

  • --kernel: 指定内核镜像文件路径。
  • --ramdisk: 可选参数,指定 RAM disk 镜像文件路径。如果在 cmdline 中指定将使用另外一个 ramdisk-image。
  • --second: 可选参数,指定第二阶段启动器镜像文件路径。
  • --dtb: 可选参数,指定设备树二进制文件路径。
  • --cmdline: 内核命令行,指定内核启动参数。
  • --base: 可选参数,指定内存基址,为一个 16 进制数。
  • --pagesize: 页面大小,指定内存页面大小,默认为 2048。
  • --ramdisk_offset: RAM disk 镜像文件在内存中的 offset。
  • --tags_offset: Tag 信息在内存中的 offset。
  • --ivt_offset: IVT 信息在内存中的 offset。
  • --os_version: 操作系统版本号。
  • --os_patch_level: 操作系统补丁等级,格式为 yyyy-mm-dd
  • --header_version: Boot image header 版本号,默认值为 1。
  • -o: 输出镜像的文件路径。

除了上述参数外,还有其他较少用到的参数,如 --hash 等。根据实际情况需要设置不同的参数。

开启 ADB

  1. 系统 - 关于 中多次点击版本号,直到开启开发者模式
  2. 开发者选项 中开启ADB
  3. 关于 - 状态 中查看当前 IP
  4. 通过 ADB 连接 adb connect 192.168.100.1

boot.img 补丁

  1. 在 PVE 中创建虚拟机(如下图参数,不添加额外 EFI 盘),用上面步骤下载的 ISO 安装系统(如果有现有的 Android 系统,安装 Magisk 即可用来打补丁)

  1. 引导菜单进入 ISO 启动菜单,选择 Advence Options,然后选择自动安装(此次安装系统只是为了打补丁,并不用于实际运行)。进入系统后,安装 Magisk APK。

  2. 用 ABD 将 boot.img 传输到 Android 系统中

adb push ./boot.img /storage/self/primary/Download/
  1. 在 Magisk 中点击安装,选择并修补一个文件(选择 boot.img

  1. Magisk 补丁后将会在相同目录生成一个 img 文件 magisk_patched-xxx.img,使用 ADB pull 将文件下载到本地。

打包新系统镜像

abootimg -x patched_boot.img

先解压 patched_boot,然后将 zImage 重命名为 kernelinitrd.img 重命名为 ramdisk.img

# 新建临时处理文件夹
mkdir -p /tmp/original_iso
mkdir -p /tmp/new_iso
# 挂载原始镜像 android.iso
mount -t iso9660 -o loop ./android.iso /tmp/original_iso
# 复制 ISO 中所有文件到新的镜像临时目录
cd  /tmp/original_iso && tar cf - . | (cd /tmp/new_iso; tar xfp -)
# 放入新的 patched 文件
cp ./kernel /tmp/new_iso
cp ./ramdisk.img /tmp/new_iso
# 重新打包为 EFI 可引导 ISO
xorriso -as mkisofs -c isolinux/boot.cat -b isolinux/isolinux.bin -no-emul-boot -boot-load-size 4 -boot-info-table -eltorito-alt-boot -e boot/grub/efi.img -no-emul-boot -isohybrid-gpt-basdat -o ./androidx86-patched.iso  /tmp/new_iso

处理完毕后解除挂载 umount /tmp/original_iso

Windows 系统使用 UltraISO 打开原始 Android x86 镜像,将补丁后的 kernelramdisk.img 拖进去,导出新镜像。

安装

用上述提到的 PVE 参数新建虚拟机(或者复用上述现有虚拟机),ISO 镜像选择导出的新镜像。启动虚拟机,选择普通安装模式。

进入 gdisk 分区界面

  • 选择 New 创建 EFI 分区(起始位置默认,大小 250Mb,分区类型 ef00
  • 再次选择 New 创建系统分区(起始位置默认,大小默认全部,分区类型默认 8300
  • 选择 Write 写入

将系统分区格式化为 ext4,第一个分区(EFI)将会默认格式化为 vfat

/system 设置为可读写(下图选择 yes

启动 & 使用

安装系统后,启动 Android,并安装 Magisk 可以看到已经是安装好的状态。

问题

由于 x86 官方镜像里已带有 su,打开 Magisk 检测到多个 su 则会提示:不属于Magisk的su文件异常状态怎么办

开发机内# adb root
开发机内# adb shell
android 内# su root
android 内# rm /system/xin/su

另外此种方式安装的 Magisk 每次启动都会如下图提示,但功能不受影响。

安装 Lsposed

首先安装 riru,从 Github 下载 riru-xxx-release.zip,使用 adb push 上传到 Andorid 中,然后在 Magisk 模块中选择 从本地安装

然后下载 LSPosed-xxx-riru-release.zip,使用 adb push 上传到 Andorid 中,然后在 Magisk 模块中选择 从本地安装。重启系统即可。

相关项目和参考

Fly.io 部署 Artalk 评论系统

2023年6月2日 22:00

最近写了几篇博客,发现博客原来使用的 Valine 评论系统已经年久失修,被 LeanCloud 冻结停止服务了,于是在多个评论系统中选中了 Artalk 重新部署博客评论系统。

Artalk 支持 Docker 形式部署,故需要部署在 CVM 上或者支持 Docker 的 PaaS 上,而我的 CVM 基本是实验环境,经常重建,不太适合评论系统此类需要稳定部署的应用,另外老牌的 Heroku 要下线免费套餐,之前看到网上有推荐 fly.io ,提供有限的免费额度,跑小博客的评论系统完全足够了。

Artalk 是一款简洁的自托管评论系统,你可以在服务器上轻松部署并置入前端页面中。

Fly.io 的核心功能是分布式的应用部署与执行,可以轻松部署和运行多种类型的应用程序(包括 Web 应用、API 应用、容器应用等),支持多种编程语言和框架,例如 Go、Rust、Python、Node.js 等,可以自动水平扩展和负载均衡。Fly.io 利用了全球范围内分布的 20 多个数据中心和数千个云计算节点,提供了高可用的、低延迟的应用程序部署和全球分发服务。

安装 flyctl

fly.io 的 Docker 形式部署目前还不能在页面直接进行,需根据官方文档安装 cli 工具(如 macOS):

brew install flyctl

创建 Fly.io 应用

Fly.io 的 Docker 部署文档:https://fly.io/docs/languages-and-frameworks/dockerfile/,常规的应用简要步骤: 0. 创建 dockerfile

  1. flyctl launch 创建应用(此步骤会新建 fly.toml 配置文件)
  2. 创建 storage
  3. 配置使用 storage
  4. 执行 deploy

但由于 fly.io 无法配置挂载,所以首次部署时 Artalk 会因为没有配置文件无法启动,所以我在模板仓库里面使用了 entrypoint.sh 脚本拷贝配置文件到部署副本内。

我新建了一个部署模板,Git Clone 模板仓库:https://github.com/wayjam/artalk-deploy

(1) 初始化应用

flyctl launch

(2) 创建 Storage

flyctl volumes create data --size 1 --app 上一步初始化的应用名

如果 Storage 名不是 data 则需要修改 fly.toml 的 mounts。

(3) 创建 Artalk 配置

下载配置模板到 Git 仓库根目录:

wget -O artalk.yml https://raw.githubusercontent.com/ArtalkJS/Artalk/master/conf/artalk.example.zh-CN.yml

根据 Artalk 文档 以及自身站点场景修改配置。

不建议修改监听端口,修改后需要更新 fly.toml 且监听端口不会暴露到外部,暴露端口在 fly.toml 中另外声明。

(4) 使用 Postgres 数据库

Artalk 默认使用 sqlite3 作为 DB 存储,Fly.io 同时也支持创建 Postgres 数据库,所以也可以创建一个 Postgres 数据库给 Artalk 使用。

flyctl postgres create

创建完数据库会显示 DB 的连接信息,

连接数据库实例 & 创建数据库

flyctl postgres connect -a wayjam-db
create database artalk;

修改连接信息

将数据库连接信息填入 artalk.yaml

(5) 部署

  • 首次部署 or 需要上传本地配置到部署副本flyctl deploy --build-target with-conf --remote-only
  • 不上传本地配置到部署副本flyctl deploy --build-target base --remote-only

我的模板仓库使用了 Dockerfile 的 multistage 提供的两种部署选项是为了兼容部署场景:是否要上传本地 conf 到远端的场景。用处主要在于首次部署的场景,其他需要更改远端配置的场景可以使用下面提到的 ssh 手工更改或者 sftp 上传。但远端编辑的体验肯定比不上在本地修改再上传。

(6) SSH 连接到副本

flyctl 中内建了很多有用的命令,比如 flyctl ssh console 就可以连接到远程副本。

总结

目前 Fly.io 部署应用基本上没有遇到不可解决的问题,但总体易用性还是比不上 Heroku,很多问题文档没有特别说明的还需到官方论坛去查找解决办法。

本文以 Artalk 的部署为例展示一个完整应用的部署生命周期,希望也可以给到部署其他应用一个参考。

[All In One] HomeLab 2023 搭建总结

2023年5月25日 23:21

上个月由于搬家+宽带升级,所以就想将自己原来的传家宝 N1 旁路由方案升级一下,既然硬件升级了,那就要承载更多的应用。

“All-in-One”(一体机)是指一台设备包含了多个功能模块的电子产品。这些设备通常把所有的功能集成到一个单一的、紧凑的设备中,包括计算机、显示器、摄像头、扬声器、输入设备等等。All-in-One产品可用于各种场合,特别是在Office、教育、医疗和娱乐等领域,由于其一体化的设计和简单易用的操作,深受消费者的欢迎。 Homelab(个人实验室)是一个IT专业人士用来测试、研究、学习、模拟企业环境的地方,通常由个人在家中或小型办公室中自行搭建,其中包括了不同类型的计算机、网络设备、虚拟化软件、存储设备等。Homelab和NAS的概念有些相似,都是为了满足个人或小型组织的需求而搭建的私有化的计算机和存储环境。

搭建一个 HomeLab 应该需要考虑以下要点:

  • 运行的稳定性
  • 功能的可扩展性
  • 网络的安全性
  • 数据的容错性和安全性
  • 运行成本

但每个人都应该根据自己的使用场景,对这几个方面进行权衡。对于我来说,我并不会完全把全部数据存储在本地,关键数据都会做云端备份,所以对于本地的 Homelab 来说,运行成本,合理利用资源是大于数据的容错性和安全性,在这前提之下,运行要保证基本的稳定性,在 all in boom 的场景有一定的 fallback 路径,并且最大化的利用软硬件资源。比如我的存储并没有组 Raid 或者 NAS 存储池令其容错性更高,我的无线也直接使用无线网卡,而没有使用更高速稳定的硬AP,所以我的实践仅供参考。

这几篇文章记录了我初步开始 HomeLab 的过程与结果,HomeLab 没有最终形态,一切都是在使用中演化成最适合自己的模式。

硬件清单

设备 用途 价格 购买途径
R71S N6000 小主机 主服务器 519 拼多多 Intel N6000
金百达 16GB DDR4内存 内存 173 京东
致态 Ti5000 1T 固态 存储 358 京东
西部数据 Element 12T 存储 1295 亚马逊 2020年
MTK MT7922 miniPCIE接口网卡 无线 WiFi 98 淘宝
NGFF 转 miniPCIE 板 转接板 7 淘宝
1.25mm 软路由风扇 散热 14 拼多多
信越 7921 硅脂 4克 散热 12 拼多多
固态散热马甲 散热 12 拼多多

功耗

可能功耗在大部分玩家的场景不是需要关心的,毕竟使用 E5 / i7 等等的大有人在。但我目前的租房电费较贵,功耗则成为我需要考虑的一个要点。

  • 光猫 + AP路由 + N1 + 西部数据Element(单独供电):19w
  • 光猫 + AP路由 + N1:9~10w
  • 光猫 + AP路由: 7w
  • 主机(新配置)PE待机 :9w
  • 光猫 + 主机(新配置)+西部数据Element(单独供电):22w

可以看出来总体的功耗增加了 3w,完全可以接受。N6000 的设计 TDP 为 6W,但待机功耗的统计数据确实不好看,果然网络上其他使用 11 代 CPU(如N5105)的反馈基本属实。另外 N1 不愧历经那么多年依旧能打,毕竟实际功耗可以达到 1~3w。

总体结构图

外部访问

  • tailscale:提供VPN访问
  • cloudflare tunnel + nginx proxy manager:内部服务穿透到外部

系列文章

[All In One] 轻量的文件 & 媒体服务器配置

2023年5月22日 22:12

我的 All In One 里面的 NAS 需求比较轻量,所以不选择安装完备的 NAS 系统(黑群晖、Unraid等),直接按需选择子应用以 Docker 形式部署在 PVE 下的 Debian LXC 容器内。

前置工作 - 用户权限

以下全文环境为 PVE 7.4

LXC 同样是容器技术,基于 Linux 的 namespace 和 cgroups。LXC 非特权容器通过 uidmap 将容器内的用户和宿主机的用户进行映射,提高了 LXC 的安全性。但对于我们文件共享来说,对权限管理造成了麻烦,但还是有方案来处理。

思路是 PVE 所有存储(包括 USB 外置设备)都使用同样的 uid/gid1000:1000,然后容器内统一用 1000:1000 读写文件。

容器 uid(1000)/gid(1000) -> LXC idmap 映射 -> 宿主机 uid(1000)/gid(1000) -> 读写目录
  1. 修改子组 ID(允许映射 1000
root@pve:~# cat /etc/subuid
root:100000:65536
root:1000:1
root@pve:~# cat /etc/subgid
root:100000:65536
root:1000:1
  1. 修改存储目录的权限
chown -R 1000:1000 /mnt/mydisk

扩展阅读

  1. Unprivileged LXC containers - 介绍了 PVE 两种方案处理权限问题
  2. Proxmox: bind mountpoint from host to unprivileged LXC container

Turnkey File Server 文件服务器

搜索 fileserver 下载 CT 模板

创建 LXC 创建容器

修改容器配置

# cat /etc/pve/lxc/101.conf
nameserver: 192.168.100.1
net0: name=eth0,bridge=vmbr0,firewall=1,gw=192.168.100.1,hwaddr=CA:53:82:1B:CE:CC,ip=192.168.100.5/25,type=veth
ostype: debian
rootfs: local:101/vm-101-disk-0.raw,size=2G
swap: 512
unprivileged: 1
# 挂载目录进入容器
mp0: /data,mp=/mnt/data,backup=0
mp1: /dev/sda1,mp=/mnt/disk,backup=0
# 增加 idmap 映射,指定 1000 映射成 1000,否则将按默认设置 从 100000 开始
lxc.idmap: u 0 100000 1000
lxc.idmap: u 1000 1000 1
lxc.idmap: u 1001 101001 64535
lxc.idmap: g 0 100000 1000
lxc.idmap: g 1000 1000 1
lxc.idmap: g 1001 101001 64535

启动容器安装

pct start 101

安装

打开PVE 对应容器的控制台,输入root/容器设定的密码 之后进入安装界面

确认安装

TurnKey File Server 配置

打开 TurnKey File Server 设置页面,如 http://192.168.100.5,如入 _root/容器密码_进入 webmin。

添加用户/组

进入 System - Users and Groups(也可以在命令行操作)

  • 组名可以随意制定
  • GID 指定为 1000

  • 用户名可以随意制定
  • UID 指定为 1000
  • 选择用户组为 GID=1000 的

添加共享文件夹

进入 Servers - Samba Windows File Sharing - Create a new file share

  • 设置用户为上面创建的 UID=1000 的用户
  • 设置组为上面创建的 GID=1000 的组
  • 权限设置为 755

创建之后选择对应的共享文件夹,编辑安全策略,改成可写

转换 Samba 用户

进入 Servers - Samba Windows File Sharing - Samba Users - Convert Users

  •  Only listed users or UID ranges:选择 UID=1000 的用户
  • 点击重启 Samba Server 生效

确认配置生效

通过 Samba 连接到文件服务器,使用创建的用户和密码登录,创建一个文件。然后在 PVE 宿主机内查看权限:预期内是如下的 1000:1000

ls -lahn /mnt/mydisk
drwxr-xr-x   2 1000 1000 4.0K Jan  9  2023 tmp

为什么不使用 NFS? —— https://www.turnkeylinux.org/forum/support/20160225/nfs-unusable-fleserver-tempalte NFS 依赖于内核模块,LXC 容器共用宿主机内核,如果一定要运行 NFS 则需要宿主机安装对应软件和内核模块。

禁用 macOS 残留 dotfiles

macOS 默认会在访问目录生成存储扩展属性的 dotfile,在 macOS 中一般直接存储在文件系统中,但在网络访问(samba)中,macOS 同样也会生成此类文件在远程目录。

可以通过修改 samba 的配置将它们丢弃,进入 Servers - Samba Windows File Sharing - Samba Users - Edit Config File,在 Global 下添加配置

# Delete garbage Apple files
veto files = /._*/
delete veto files = yes

如果还想忽略 .DS_Store(文件夹布局等元信息)

veto files = /.DS_Store/._*/

用于运行 Docker 的 LXC 容器

Docker 可以运行在 PVE 的 VM 或者 LXC中,官方更推荐运行 Docker 在 VM 中,隔离性和安全性更好。甚至可以直接运行在 PVE 宿主机中,但就没有 VM 和 LXC 那么好迁移与备份。

配置 Debian LXC 容器

下载 debian 的 CT 模板,新建容器

修改容器配置文件增加挂载 vim /etc/pve/lxc/102.conf

# 开启嵌套虚拟化和按键
features: keyctl=1,nesting=1

我选择使用_特权容器_来作为运行 Docker 的宿主机。如果使用非特权容器,LXC 和 Docker 都会使用 Linux Namespace 技术,会有两次 user-remap,而且 Docker 的 user-remap 不能禁用,配置起来会比较麻烦。

启动容器

pct start 102

配置 locale 以及时区

dpkg-reconfigure locales
timedatectl set-timezone Asia/Shanghai

在 debian 容器内创建用户

root@docker:/# groupadd -g 1000 mygroup
root@docker:/# useradd -u 1000 -g 1000 myuser

安装 Docker

Install Docker Engine on Debian

Jellyfin 媒体服务器

为方便显卡直通,我使用的方案如下PVE => LXC Debian 容器 => Jellyfin Docker,以下以特权容器内运行为例,非特权容器可参照后文中附的链接。

安装驱动

在 Host 安装驱动

apt install vainfo

增加 i915 内核参数加载 GuC 和 HuC 固件

mkdir -p /etc/modprobe.d  
sh -c "echo 'options i915 enable_guc=2' >> /etc/modprobe.d/i915.conf"
update-initramfs -u && sudo update-grub

重启后检查是否加载 GuC 和 HuC 固件

root@pve:~# dmesg | grep i915
[    3.929105] i915 0000:00:02.0: [drm] VT-d active for gfx access
[    3.929143] i915 0000:00:02.0: vgaarb: deactivate vga console
[    3.929215] i915 0000:00:02.0: [drm] Using Transparent Hugepages
[    3.932495] i915 0000:00:02.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem
[    3.933440] i915 0000:00:02.0: [drm] Finished loading DMC firmware i915/icl_dmc_ver1_09.bin (v1.9)
[    4.602140] i915 0000:00:02.0: [drm] [ENCODER:238:DDI A/PHY A] failed to retrieve link info, disabling eDP
[    4.748421] i915 0000:00:02.0: [drm] GuC firmware i915/ehl_guc_70.1.1.bin version 70.1.1
[    4.748429] i915 0000:00:02.0: [drm] HuC firmware i915/ehl_huc_9.0.0.bin version 9.0.0

确认核显驱动是否支持硬件加速

在 Host执行 如过有 not supported 或没有以下类似输出,则许考虑需要安装驱动或增加内核挂载。

root@pve:/ # dmesg | grep i915
[    3.714527] i915 0000:00:02.0: [drm] VT-d active for gfx access
[    3.714561] i915 0000:00:02.0: vgaarb: deactivate vga console
[    3.714626] i915 0000:00:02.0: [drm] Using Transparent Hugepages
[    3.715865] i915 0000:00:02.0: vgaarb: changed VGA decodes: olddecodes=io+mem,decodes=io+mem:owns=io+mem
[    3.716836] i915 0000:00:02.0: [drm] Finished loading DMC firmware i915/icl_dmc_ver1_09.bin (v1.9)
[    4.385706] i915 0000:00:02.0: [drm] [ENCODER:238:DDI A/PHY A] failed to retrieve link info, disabling eDP
[    4.527630] [drm] Initialized i915 1.6.0 20201103 for 0000:00:02.0 on minor 0
[    4.529784] snd_hda_intel 0000:00:1f.3: bound 0000:00:02.0 (ops i915_audio_component_bind_ops [i915])
[    4.530375] i915 0000:00:02.0: [drm] Cannot find any crtc or sizes
[    4.530875] i915 0000:00:02.0: [drm] Cannot find any crtc or sizes

部分需要安装可以参考 https://zhuanlan.zhihu.com/p/385401945

直通显卡

在 Host 查看显卡 ID

root@pve:/data# ls -l /dev/dri/
total 0
drwxr-xr-x 2 root root         80 May 13 22:41 by-path
crw-rw---- 1 root video  226,   0 May 13 22:41 card0
crw-rw---- 1 root render 226, 128 May 13 22:41 renderD128

编辑 LXC 配置,配置映射显卡

lxc.cgroup2.devices.allow: c 226:0 rwm
lxc.cgroup2.devices.allow: c 226:128 rwm
lxc.mount.entry: /dev/dri/card0 dev/dri/card0 none bind,optional,create=file
lxc.mount.entry: /dev/dri/renderD128 dev/dri/renderD128 none bind,optional,create=file

启动 & 配置 Docker 容器

这里使用 nyanmisaka 大佬封装的 jellyfin docker,更好的支持硬解码。

查看 Host 机中 videorender 组的 GID(LXC 非特权容器还需修改 LXC 配置):

cat /etc/group | grep video
cat /etc/group | grep render

Note:Jellyfin 需要使用显卡设备,需要注意 Jellyfin 容器启动用户是否有使用显卡设备(/dev/dri/xxx)的权限。--group-add参数用于指定在容器内运行的进程所需的其他组,以便容器中的进程可以从操作系统检索附加的用户组。

docker run -d --name=jellyfin \
 --user 1000:1000 \
 -e TZ=Asia/Shanghai \
 -p 8096:8096 \
 -v /root/jellyfin/config:/config \
 -v /mnt/data:/mnt/data \
 -v /mnt/disk:/mnt/disk \
 --device /dev/dri:/dev/dri \
 --group-add="103" \
 --group-add="44" \
 --restart unless-stopped \
 nyanmisaka/jellyfin:latest

启动 Jellyfin 后进入控制台 - 播放,开启硬件加速(11代 Intel CPU 必须开启低电压解码选项)。

验证

在 Jellyfin 容器中执行测试命令确认驱动加载无误

docker exec -it jellyfin /usr/lib/jellyfin-ffmpeg/vainfo
docker exec -it jellyfin /usr/lib/jellyfin-ffmpeg/ffmpeg -v verbose -init_hw_device vaapi=va -init_hw_device opencl@va

最后使用 Jellyfin 播放视频,选择更低质量的分辨率确认是否使用转码播放。也可以安装 intel-gpu-tools 来查看 GPU 负载:

参考

Aria2 下载服务器

在上述 debian LXC 容器的基础上,可参照 https://p3terx.com/archives/docker-aria2-pro.html 使用 1000:1000 的用户启动即可。如

docker run -d \
 --name aria2 \
 --restart unless-stopped \
 --log-opt max-size=1m \
 --network host \
 -e PUID=1000 \
 -e PGID=1000 \
 -e RPC_SECRET=<password> \
 -e RPC_PORT=6800 \
 -e LISTEN_PORT=6888 \
 -v /mnt/data/aria2-config:/config \
 -v /mnt/data/downloads:/downloads\
 p3terx/aria2-pro

NAStool 资源归集整理服务

NAStool 3.x 之后移除了 BT 的支持,并且很多功能都需要 PT 站点认证,这对于轻量玩家比较不友好,我在找到合适的替代之前暂时先用 2.9 版本替代。

成人资源可以使用 Movie_Data_Capture or metatube

Alist

https://alist.nn.ci/zh/guide/install/docker.html

Bitwarden 密码管理

同上,为了避免 all in boom 之后无法输入密码,部署在云服务器上同时每日备份至 Dropbox。

青龙面板

docker run -d \
  --name qinglong \
  --hostname qinglong \
  -v /mnt/data/docker/qinglong:/ql/data \
  -p 5700:5700 \
  --restart unless-stopped \
  docker.io/whyour/qinglong:latest

[All In One] R71s 主机散热改造

2023年5月17日 23:11

购买之前就看网上评论 N5105 的发热很厉害,虽然我这台是 N6000 的 CPU,但毕竟是 24x7 的 All In One 主机,还是预先做好散热改造吧。

更换硅脂

这是主机原有的硅脂,黏度还是不错的。我买了信越的7921硅脂,果然是水泥手感,但是用一次性塑胶手套还是相对好涂抹。要将CPU和导热块的两边都重新涂抹,但导热铜块由于原来的硅脂黏度太好费了好大劲才拆出来更换。

增加固态散热马甲

这款小主机的主板设计NVME M.2 接口在一个 miniPCIE 接口上面,我的 miniPCIE 需要接一个无线网卡,而用的无线网卡是 NGFF 接口所以需要转接板,这导致固态硬盘和无线网卡之间几乎没有什么缝隙,而这两个设备几乎是这台主机的所有热量来源,我的使用实测 CPU 并没有发热很厉害(当然可能和上面图片所示导热到了主机顶板有关)。

我购买了一个导流式的固态 M.2 的散热马甲,体积比市面上另外一种 M.2 固态散热贴片(胶带绑住SSD的)要大,但散热效果很好。但如果大家也想使用这类散热马甲,需考虑主机和固态之间是否有足够的空隙,避免盖板盖不上。我这款实测会增加 9cm 左右的厚度。

增加风扇

由于这款主机默认是被动散热,上面增加固态马甲后,热量可能还会在主机内部带不走,所以考虑一步到位再增加一个风扇。这款主机主板上的风扇接口是 12V 的四线 1.25mm 接口,我购买的是这个规格的软路由小风扇,刚好可以安装在底板内面上(固态马甲和风扇也并没有挡住,高度刚刚够)。如果使用 8010 风扇可能还要安装在主机外,大部分也是要占用 USB 接口。大家在购买的时候一定要注意自己的主板接口。

最终

从上而下是光猫、All In One 主机、西数Element的硬盘(USB方式连接到主机上,没有拆盘使用)。唯一遗憾的是主机的 USB 3.0 接口都在正面,导致硬盘的线要从正面接入。

另外购买了一个三层的路由器收纳盒,让散热更好,也更美观。

小主机的散热改造效果还是很明显的(当然可能他原本的外壳体质不错),原来外壳是有点烫手的,现在基本只有轻微发热。最为明显的是底部,由于风扇就安装在底部,完全没有发热的感觉。现在使用下来,架子上面的光猫和下面硬盘都比主机的温度高。

[All In One] PVE LXC 安装 OpenWrt 软路由以及网络规划

2023年5月13日 11:42

作为 All in One 里面最重要的一环,软路由的安装和配置是最为重要的操作。这里我选择纯软路由,且无硬 AP 的拓扑。如果有多拨需求,可以选择 iKuai 主路由 + OpenWrt 旁路由的方案,我觉得我的场景已经真的 All In Boom 了,就不再改光猫桥接,在真的 boom 的时候还方便一点直接接光猫救急,所以这里我只选用 OpenWrt 作为主路由。路由的部署方式可以使用 VM 或者 LXC,在上一篇也提到了 Intel 11 代 CPU 的潜在虚拟化问题后,我这里部署方式选择为 PVE 下 LXC 容器。

OpenWrt 是一个开源的路由器固件,它基于 Linux 操作系统,并可运行在各种不同品牌的路由器上。OpenWrt 提供了丰富的功能和高度的可定制性,用户可以轻松地对其进行配置和定制,以满足不同的网络需求。OpenWrt 还支持安装各种软件包,例如 VPN、DNS、Web 服务器等,从而进一步扩展其功能。

LXC(Linux Containers)是 Linux 内核中的一个软件容器化工具,它提供了一种轻量级的虚拟化技术。与传统虚拟机相比,LXC 的优点在于更快的启动时间、更少的资源占用以及更高的性能。用户可以使用 LXC 在 Linux 操作系统中创建多个独立的容器,每个容器都提供一个隔离的运行环境,与主机和其他容器完全隔离开来。在容器内,用户可以运行各种不同的应用程序和服务,例如 Web 服务器、数据库、文件共享等。由于 LXC 能够更加高效地利用系统资源,因此它已成为开发人员、测试人员、系统管理员和云计算提供商的首选工具之一。

固件选择

OpenWrt 固件选择上我尝试过官网原版、Lean 预编译版、自编译 Lean 版、其他第三方编译版,包括自编译增加无线驱动,也在不同程度上出现:无法识别无线网卡、无线无法启动等等问题。最终选择以下固件最省心。

https://www.right.com.cn/forum/thread-928319-1-1.html

获取 CT 模板

SquashFS 是一种只读文件系统,不允许对其进行写操作,它可以将多个文件和目录合并到一个文件中,从而提高存储空间的利用率,同时也可以提高对这些文件的访问效率。SquashFS 文件系统具有高效的压缩和解压缩速度,支持很多不同的压缩算法,能够在存储和传输文件时减少网络流量和存储空间的占用,广泛应用于嵌入式系统、上传和下载镜像等场景。

Rootfs 是 Linux 系统启动时候使用的根文件系统,是指在文件系统概念中的根目录。它包含了 Linux 系统启动所必需的一些必要文件和目录,例如/etc、/proc、/bin、/sbin 和/usr 等。rootfs 文件系统通常会以类似于 ext4 或 SquashFS 文件系统的形式储存于嵌入式系统或者云计算平台中,为 Linux 系统提供启动和运行的必要支持。由于 rootfs 文件系统在启动过程中是唯一可用的文件系统,在系统运行时,它也会被其他文件系统所覆盖。

无论是 OpenWrt 官网下载,还是第三方打包的固件,必须是*-rootfs.img*-rootfs.img.gz 或者 *-rootfs.tar.gz 的文件 。*-rootfs.img*-rootfs.img.gz 需要解包后打包 。

首先需要获取 CT 模板,如果是 rootfs 格式打包的上传至 PVE CT 模板目录即可,否则需要先解包重新打包使用。

以下以 *.img.gz 固件包为例:

cd workspace
mkdir openwrt
# 文件名
img_name="openwrt-x86-64-generic-squashfs-combined.img.gz"
# gzip 解压出 img
gzip -d "$img_name"
# 去除变量 gz 后缀
img_name=$(basename "$img_name" .gz)
# 挂载偏移
root_partition=$((`fdisk -l "$img_name" | grep .img2 | awk '{print $2}'` * 512))
# 挂载到 openwrt 目录
mount -o loop,offset=$root_partition "$img_name" ./openwrt
# 打包
cd openwrt && tar zcf /var/lib/vz/template/cache/"$img_name".tar.gz * && cd ..
# 卸载
umount ./openwrt

创建 LXC 容器

pct create 100 \
	local:vztmpl/openwrt.tar.gz \
	--rootfs local:2 \
	--ostype unmanaged \
	--hostname OpenWrt \
	--arch amd64 \
	--cores 2 \
	--memory 1024 \
	--swap 0

此处以 vmid=100 为例,可自定义,创建完先进行配置再启动。

配置 LXC 容器

修改容器配置

root@pve:~ # cat /etc/pve/lxc/100.conf
arch: amd64
cores: 2
hostname: OpenWrt
memory: 1024
ostype: unmanaged
rootfs: local:101/vm-101-disk-0.raw,size=2G
swap: 0
# 设置DNS,根据实际情况填写
nameserver: 192.168.1.1
# 引用 PVE自带 openwrt 配置文件,包含一些基本设置
lxc.include: /usr/share/lxc/config/openwrt.common.conf
# 将主机的网卡 enp6s0/wlp2s0 分配给容器使用,根据自己的实际情况更改
xc.net.0.type: phys
lxc.net.0.link: enp6s0
lxc.net.0.flags: up
lxc.net.0.name: wan
lxc.net.1.type: phys
lxc.net.1.link: wlp2s0
lxc.net.1.flags: up
lxc.net.1.name: wlan
lxc.net.2.type: veth
lxc.net.2.link: vmbr0
lxc.net.2.flags: up
lxc.net.2.name: eth1
lxc.net.3.type: veth
lxc.net.3.link: vmbr1
lxc.net.3.flags: up
lxc.net.3.name: eth2

挂载 PPP/TUN

方法 1: 使用 hook 脚本(需特权容器):

# 创建 hook 脚本
mkdir -p /var/lib/vz/snippets
cp /usr/share/pve-docs/examples/guest-example-hookscript.pl /var/lib/vz/snippets/lxc-hookscript.pl
# vim /var/lib/vz/snippets/lxc-hookscript.pl
...
    # Second phase 'post-start' will be executed after the guest
    # successfully started.

    system("lxc-device add -n $vmid /dev/ppp");
    system("lxc-device add -n $vmid /dev/net/tun");

    print "$vmid started successfully.\n";
...

在容器配置中添加 hook:

hookscript: local:snippets/lxc-hookscript.pl

方法 2: 直接在容器配置中加入挂载配置

挂载 ppp/tun 设备,以及允许容器使用 major=108,minor=0 以及 major=10,minor=200 的设备。

lxc.cgroup2.devices.allow: c 108:0 rwm
lxc.mount.entry: /dev/ppp dev/ppp none bind,create=file
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file

启动 LXC 容器

正常启动信息如下

root@pve:~ # pct start 100
GUEST HOOK: 100 pre-start
100 is starting, doing preparations.
GUEST HOOK: 100 post-start
100 started successfully.

如果无法启动可使用 debug 模式(日志保存在 debug.log 文件)

lxc-start -n 100 --logfile debug.log --logpriority TRACE

如果是挂载了无线网卡可能因为没有安装 iw 而启动失败:

apt install iw

OpenWrt 配置

Attach 到容器内进行操作 lxc-attach 100

设置密码

passwd
# Changing password for root
# New password:
# Retype password:
# passwd: password for root changed by root

配置网络

vim /etc/config/network
...
config interface 'lan'
        option type 'bridge'
        list ifname 'eth1'
        option proto 'static'
        option ip6assign '60'
        option netmask '255.255.255.0'
        option ipaddr '192.168.1.1' # 关键配置 IP 地址
        option gateway '192.168.1.1' # 本机
...

重启网络 /etc/init.d/network restart,完成之后就能使用 SSH 或者 网页打开 OpenWrt 了。

开启无线

  1. ifconfig -a 查看无线网卡是否能被识别
  2. wifi config
  3. LUCI 配置无线
    1. 国家用美国,2.4G 开信道 11,5G 开信道 149(解决 无线未开启或者未关联

OpenWrt 自身无法上网

如果是二级路由,在 DHCP/DNS 中设置 DNS 转发至上层路由或者临时修改 /etc/resolve.conf

openclash 无法启动

refer to https://github.com/vernesong/OpenClash/issues/1915

openclash 启动失败,日志中出现

Unable to set capabilities [--caps=cap_sys_resource,cap_dac_override,cap_net_raw,cap_net_bind_service,cap_net_admin+eip]

是因为引用的 PVE 默认的 openwrt 配置中 /usr/share/lxc/config/openwrt.common.conf 中 drop 掉了权限 lxc.cap.drop = sys_resource

可以在容器配置中加入此行

root@pve:~ # cat /etc/pve/lxc/100.conf
# ...
lxc.cap.drop:

Adguard Home 与 OpenClash

科学上网方案我还是沿用一直使用的 Adguard + OpenClash。

  • OpenClash 接管流量做分流和科学上网
  • OpenClash 使用 meta 内核(基本支持 Premium 的功能)
  • DNS 查询路径:client->adguard->clash(fake-ip),跟以前使用过的方案路径有不一样了,好处是 Adguard Home 中可以看到客户端 IP,也不会因为广告域名走了 clash 的代理模式而不走 Adguard 解析。

另外说一句, OpenClash 的 GUI 配置实在是太难用了(无论是布局、菜单、交互),还好有直接编辑 YAML 的方式配置。

网络配置

以下是我的网络拓扑示意,目前我的所有设备基本上都是以无线方式连接,还有两个网口是闲置状态。

  • 除了无线网卡和与光猫通信的网口是直通之外,其余网口都是以网桥方式接入。
  • enp3s0、enp4s0、enp5s0 这几个网口连接进 OpenWrt 的方式可以有很多种,不一定是我这里使用的这种,比如只建立一个网桥 vmbr0 关联三个网卡,或者建立 bond 绑定三个网口,再基于 bond 建立网桥,都可以正常使用。
  • 下图的 vmbr4 也可以省略,直接使用有物理网口的网桥也可以正常使用。

OpenWrt 容器的网络挂载(页面没有显示直通设备,可以在 /etc/pve/lxc/vmid.conf 中配置)

远程组网

  1. 安装 tailsacle:opkg install tailscale
  2. 登录:tailscale up
  3. 开启子网路由:tailscale up --advertise-routes=192.168.100.0/24 --accept-dns=false
  4. Tailscale 页面编辑对应的设备,禁用 Key 过期
  5. Tailscale 页面编辑对应的设备,开启子网

这时就可以从其他网络的 tailscale 连通设备连接 OpenWrt 的子网。

参考

总结

总体来说,OpenWrt 使用 LXC 方式部署成软路由已经非常成熟,大部分固件都可以运行。只是如果要使用无线网卡的话,必须要解决好驱动问题,就很稳定了。

速度测试

用 iperf3 进行测试(TCP 双向传输) iperf3 -c 192.168.100.6 -d -t 60

发送 接收
无线(WIFI6) 688 Mbits/sec 688 Mbits/sec 近距离
有线(千兆) 936 Mbits/sec 935 Mbits/sec MacBook + 千兆 HUB
内网 20.3 Gbits/sec 20.2 Gbits/sec PVE 两个 Docker 间

有线基本跑满硬件带宽,无线有一些损耗,但我的宽带也就 600M,所以基本达到上限。

无线网卡选择?

本来打算购买 Intel AX210 无线网卡,但购买之前,看恩山论坛的讨论,由于驱动限制 AX210 似乎还不能以 AP 模式在 OpenWrt 下使用,所以购买了 MTK 7922 网卡。

实测 MTK 7922 网卡可以以 AP 模式发出 5G Wi-Fi 信号,但不能同时发出 2.4G 信号。

设备找不到?

在测试固件的过程中,由于部分固件的驱动兼容不好,容易造成无线网卡假死等情况。由于网卡是直通 LXC 容器,有可能会导致 PVE 系统丢失了设备,设备能识别,但是设备加载失败。

所以如果想要隔离性更好还是选择 VM 安装 OpenWrt。

[    0.359071] DMAR: IOMMU feature mts inconsistent
[    3.378372] mt7921e 0000:02:00.0: enabling device (0000 -> 0002)
[    3.378663] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_RAM_CODE_MT7922_1.bin failed with error -2
[    3.398324] mt7921e 0000:02:00.0: ASIC revision: 79220010
[    3.483152] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.566637] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.650631] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.734476] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.818421] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.906385] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    3.994418] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.082389] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.166425] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.250406] mt7921e 0000:02:00.0: Direct firmware load for mediatek/WIFI_MT7922_patch_mcu_1_1_hdr.bin failed with error -2
[    4.336774] mt7921e 0000:02:00.0: hardware init failed

[All In One] 安装 Proxmox VE 7.4

2023年5月8日 23:11

最近将 N1 升级成 R71S 小主机,记录一下安装 PVE 的步骤与问题。

Proxmox Virtual Environment(PVE)是一款开源的服务器虚拟化管理平台,可以让用户轻松管理和监控大量的虚拟化和容器化服务器。PVE支持KVM和LXC虚拟化技术,提供了一个易于使用的Web界面,可以让用户创建、管理、备份和恢复虚拟主机、容器和存储。 PVE采用了分布式架构,可以轻松管理多个远程服务器,并提供了高可用性、容错和负载均衡的功能。此外,PVE还支持各种虚拟存储技术,如网络文件系统(NFS)、本地存储和iSCSI卷,可以便捷地存储虚拟机的镜像、容器和数据。

一、下载镜像

  1. 官方下载:https://www.proxmox.com/en/downloads/category/iso-images-pve
  2. 国内镜像下载(如清华镜像源):https://mirrors.tuna.tsinghua.edu.cn/proxmox/iso

二、制作启动盘

由于小主机和配件刚买回来还需要使用 PE 进行一些检测,我使用了在 UTM 下 Windows 虚拟机使用 ventoy 制作多引导的启动 U 盘。另外,MacOS 下如果没有多引导的需求则可以直接使用 balenaEther(没找到类似 ventoy 的工具)烧录启动盘。

写入 ventoy 后(将 U 盘会分为两个盘)将 PVE 的 ISO 文件放在 Ventoy 盘下

三、安装

  1. 将制作好的引导 U 盘插入主机,开机启动(最好先进 Bios 修改引导顺序)
  2. 选择 Install Proxmox VE 并回车
    • 点击 I Agree 同意协议
  3. 磁盘配置
    • 选择需要安装 PVE 的硬盘
    • SWAP 分区:如有大内存则完全可以将 swap 分区设置为 0,这里我安装了 16G 的物理内存,由于硬盘是 1TB 的 m.2 NVME 固态还是设置了 16G 的虚拟内存。
    • 点击 Next 完成
  4. 本地化配置
    • 国家选择 China
    • 时区会自动调至 Asia/Shanghai
    • 键盘保持默认的 U.S.English
    • 点击 Next 完成
  5. 管理员配置
    • 输入密码和确认密码
    • 填写邮箱地址
    • 点击 Next 完成
  6. 网络配置
    • 网卡:选择哪个网口作为管理端口,默认第一个网口
    • 域名:自定义即可
    • IP:即 PVE 的管理 IP,例如:192.168.100.10(下面有说明)
    • 网关:即后续安装的软路由的网关,例如:192.168.100.1
    • DNS:填写网关 IP 或者国内 DNS 地址,例如:114.114.114.114
    • 点击 Next 完成
  7. 点击 Install 开始安装然后等待重启
  8. 重启之后将 U 盘移除

网络配置相关说明

这里的安装配置需考虑整体网络拓扑,如果 PVE 上层还有路由,则 IP 和网关填写上层路由网段信息即可,如果打算 PVE 内虚拟主路由,则自行选择一个网段。

如果没有主路由,则需要用网线将主机和电脑连接,且配置电脑的网络配置:

IP 地址:填写和 PVE 管理 IP 同一网段的地址 子掩网码:255.255.255.0

否则只需要将电脑和主机连接到上层路由即可。

至此,安装完毕,可以通过 Web 页面打开或者通过 SSH 连接到 PVE。

四、配置

4.1 软件源配置

然后执行更新

# debian 源配置更新后
apt update
apt upgrade -y
# 更新完 CT 模板配置
systemctl restart pvedaemon.service
pveam update

4.2 更新内核以及微码

我的小主机CPU是 N6000,由于以 N5105 为代表的 11 代 Intel CPU 在虚拟化场景,大面积出现重启崩溃的现象,PVE 需更新内核和微码。详情可参考以下帖子:

内核

使用 apt-cache search pve-kernel 查看当前 PVE 最新内核(如今天 2023-05-08 的最新内核为 6.2)

apt install pve-kernel-6.2
apt -y dist-upgrade
reboot

重启后查看内核版本 uname -r

更新 Intel Microcode

apt install intel-microcode

更新系统引导

update-initramfs -u -k all
reboot

重启后查看 microcode 版本(如 0x24000024):grep 'stepping\|model\|microcode' /proc/cpuinfo

4.3 虚拟内存策略的调整

即便是使用了 NVME 的虚拟内存,我也倾向于使用更多的物理内存。PVE 可以自行调整 Linux 的内核参数:

首先查看当前虚拟内存策略:cat /proc/sys/vm/swappiness,默认为 60。

vm.swappiness:取值为 0-100,值越大则表示越倾向于使用 swap。 比如物理内存为 16GB,设置 vm.swappiness=30,则系统会在使用 16GB * (100 - 30)% = 11.2GB 后使用虚拟内存。

# vi /etc/sysctl.conf
# 增加
vm.swappiness=30
# 应用配置
sysctl -p

4.4 开启硬件直通

  1. 编辑 grub 配置:vi /etc/default/grub

找到这一行:

GRUB_CMDLINE_LINUX_DEFAULT="quiet"

注释并在下面添加或直接修改成:

GRUB_CMDLINE_LINUX_DEFAULT="quiet intel_iommu=on"
  1. 更新 grub:update-grub
  2. 调整所需模块 vi /etc/modules
vfio
vfio_iommu_type1
vfio_pci
vfio_virqfd
  1. 更新系统引导
update-initramfs -u -k all
reboot
  1. 验证
dmesg | grep iommu
# 出现如下例子。则代表成功
[    0.847285] pci 0000:00:02.0: Adding to iommu group 0
[    0.848441] pci 0000:00:00.0: Adding to iommu group 1
[    0.848467] pci 0000:00:04.0: Adding to iommu group 2
[    0.848508] pci 0000:00:14.0: Adding to iommu group 3
[    0.848527] pci 0000:00:14.2: Adding to iommu group 3
find /sys/kernel/iommu_groups/ -type l
# 出现很多直通组,表示成功开启
/sys/kernel/iommu_groups/17/devices/0000:05:00.0
/sys/kernel/iommu_groups/7/devices/0000:00:1c.3
/sys/kernel/iommu_groups/15/devices/0000:03:00.0
/sys/kernel/iommu_groups/5/devices/0000:00:17.0
/sys/kernel/iommu_groups/13/devices/0000:01:00.0
/sys/kernel/iommu_groups/3/devices/0000:00:14.2

4.5 local-lvm 优化

PVE 默认安装会创建 local 和 local-lvm 两个存储。其中 local 大约磁盘容量的 10%,存储类别为目录。 local-lvm 的存储类别为 lvm-thin。实际使用 lvm-thin 效率比目录低,建议不适用 thin 模式。

如果要将 local-lvm 转换成目录模式可参照此文:https://www.cnblogs.com/doracloud/p/16874171.html

如果要将 local-lvm 合并至 local 可参照此文:https://foxi.buduanwang.vip/virtualization/pve/1434.html/

这里记录合并至 local 的方法。

  1. 打开 Web 页面 - 数据中心 - 存储 - 编辑 local - 修改内容选中全部

  2. 删除 lvm-local:Shell 执行 lvremove pve/data

  3. 扩容 local:Shell 执行 lvextend -rl +100%FREE /dev/pve/root

  4. 打开 Web 页面 - 数据中心 - 存储 - 删除 local-lvm

五、其他

查看硬盘信息
apt install smartmontools
smartctl -a /dev/nvme0n1
查看当前 CPU 工作模式
apt install linux-cpupower
cpupower frequency-info
移除无用内核
# 查看当前安装的内核
dpkg --get-selections |grep kernel
移除内核
dpkg --purge --force-remove-essential pve-kernel-***-pve
# 更新引导菜单  
update-grub  
# 重启PVE  
reboot
获取传感器温度
apt install lm-sensors

从零开始配置Rime - 2023

2023年4月29日 08:00

第一步:安装鼠须管

在MacOS上,可以使用Homebrew安装“鼠须管”的输入法引擎,只需要在终端中运行以下命令:

brew install --cask squirrel

第二步:下载東風破

在输入法引擎安装完毕后,下一步就是安装输入法方案。Rime使用基于Lua脚本的方案,您可以使用Rime中文输入法维基中提供的方案,也可以自己编写方案。但是,我们建议通过東風破来快速安装您喜欢的输入法方案。

打开终端并运行以下命令,将東風破克隆到本地:

git clone https://github.com/rime/plum.git

第三步:安装输入法方案

以安装雾凇拼音为例,运行以下命令:

bash plum/rime-install iDvel/rime-ice:others/recipes/full

第四步:自定义配置文件

在第三步完成后,您已经可以使用该输入法方案了。但是,如果您想更改默认设置来获得更好的使用体验,该怎么办?

您需要使用一个名为“自定义配置文件”的工具来编辑默认设置。自定义文件位于以下文件夹中:

~/Library/Rime/

只需要在该文件夹中新建两个文件即可。

  • default.custom.yaml:用于修改输入法方案和键盘行为。
  • squirrel.custom.yaml:用于修改皮肤和其他外观设置。

第五步:同步

编辑 installation.yaml 添加 sync_dir 配置到目标目录(如 icloud 目录)

# ~/Library/Rime/installation.yaml
sync_dir: /Users/wayjamsu/Library/Mobile Documents/com~apple~CloudDocs/Rime

参考

❌
❌