解决 Linux Wayland 环境下 Joplin 使用 fcitx5 中文输入漏字问题

在 Linux + Wayland 环境下使用 Joplin 时,如果配合 fcitx5 输入中文,可能会遇到输入漏字的问题。

下面这份配置可作为参考:

➜  Downloads cat /usr/share/applications/joplin-desktop.desktop
[Desktop Entry]
Name=Joplin
Comment=Joplin for Desktop
-Exec=/usr/bin/joplin-desktop --gtk-version=3 --ozone-platform=x11
+Exec=/usr/bin/joplin-desktop --gtk-version=3 --ozone-platform-hint=auto --enable-wayland-ime
Terminal=false
Icon=joplin-desktop
StartupWMClass=@joplin/app-desktop
Type=Application
Categories=Office;
MimeType=x-scheme-handler/joplin;
SingleMainWindow=true

使用 --ozone-platform=x11 可以解决 Joplin 全局菜单不显示的问题,但中文输入时可能会出现漏字。

改用 --ozone-platform-hint=auto --enable-wayland-ime 后,可以解决 fcitx5 中文输入漏字的问题;不过相应地,Joplin 全局菜单可能又无法正常显示。

实测二者不可两全,暂无找到很好的方法。建议先采用第一行配置,显示出来菜单做好配置后,改用第二行配置,确保后期中文输入体现。

输入漏字真的很恼火,如有两全解决方案欢迎讨论。

Refs

修复 Joplin on KDE 菜单栏显示问题

在 KDE 桌面下默认使用全局菜单显示应用程序的菜单栏,但是唯独 Joplin 无法显示。

最后在这里找到了解决方案,下面简单记录:

sudo vim /usr/share/applications/joplin-desktop.desktop 
--Exec=/usr/bin/joplin-desktop
++Exec=/usr/bin/joplin-desktop --gtk-version=3 --ozone-platform=x11

启动程序增加这两个参数即可解决。

Refs

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=21yjpwt8mhhc0

极简但强大的个人财务管理软件 —— 溪流记账(Rivulet)

从开始工作,就一直想开发一款面向个人的财务管理软件,用来直观的分析自己的财务状况,对个人财务情况提供直观的反馈,帮助自己更好的存钱、消费和投资。获取正向反馈,激励自己更好的存钱。

此前,基于 Notion 制作过一套个人财务管理模版,取名 BJ-PFD , 是一长串英文的缩写:Bullet Journal – Personal Finance Dashboard,意为子弹笔记个人财务仪表盘。之前写过一系列的 文章 介绍这套模版。这套模版我个人也一直在用,从2020年到2026年,已经使用了6年了,积累了大量的财务数据,帮助我更好的分析和管理个人财务状况。数据条数也增加到好几千条。此时我发现 Notion 的一些弊端暴露出来了。比如,数据库数量大了以后,加载会比较慢,而且一些数据关联会有极小概率出现问题,导致最终的数据统计出现细微的差错。更重要的一个问题,随着我数据的变多,基于 Notion API 开发的看板工具,启动速度越来越慢了,每次统计数据都要花费数分钟来加载数据。

最近,结合自己之前的工作流和数据模型,使用 AI 辅助编程开发了这款软件。经过一段时间的调试和打磨,这款软件已经能够完美承载我个人目前的财务管理需求,我也将 Notion 中的数千条数据,顺利导入这款新的软件中,数据统计速度从1分钟以上,提升到了1秒以内。现在我已经完全迁移到这款软件上了,使用体验非常好。现在,我希望将这款软件发布出来,分享给更多有需要的人使用。

下面是关于这款软件的介绍:

Rivulet 简介

溪流记账(Rivulet)是一款极简但强大的个人财务管理软件,支持快速记录收支、多账户、多账本与共享账本;提供预算管理、投资记录与财务分析。

Rivulet 使用 GO 和 Svelte 开发,使用 Docker 封装并公开发布,支持 SQLite 和 PostgreSQL 数据库。

更多信息,可以进入 Rivulet 的 官网GitHub Docs仓库 查看,如果有问题可以直接在 留言板 或是 GitHub Docs 仓库的 Issues/Discussions 提出。

Rivulet 功能介绍

  • 支持灵活的流水管理功能,区分支出、收入和转账,并支持灵活的分类,可以方便的进行流水统计;
  • 支持财务规划功能,在财务规划界面可以方便的进行每月收入、支出的预算规划,并查看实时的预算执行情况;
  • 支持投资管理,提供方便的投资买入、卖出、分红记录,投资记录自动生成流水记录,并提供投资盈亏分析;
  • 支持账户管理,方便关联自己实际的各个账户;
  • 支持多账本,账本间的流水互相隔离,适合不同的使用场景,比如个人账本、家庭账本、公司账本等;支持账本共享。
  • 更多功能陆续开发中。

Rivulet 截图

Rivulet dashboard 2026 05 07 22 29 38 Rivulet transactions 2026 05 07 22 29 51 Rivulet budgets 2026 05 07 22 35 12 Rivulet investments 2026 05 07 22 35 26 Rivulet ledgers 2026 05 07 22 35 37 Rivulet accounts 2026 05 07 22 35 45 Rivulet settings 2026 05 07 22 36 01

最后,欢迎大家试用这款软件,如果有任何疑问随时与我交流,也欢迎大家提出宝贵的意见和建议,帮助我一起把这款软件做得更好。

Refs

Copy Fail:Linux 内核 2017 年至今的高危漏洞(附临时缓解方案) | CVE-2026-31431

最近爆出一个 Linux 内核存在近10年的漏洞,随便找了一台最近在用的机器试了一下,直接成功提权:

~$ whoami
songtianlun
~$ python3 test.py 
# whoami
root

临时解决方案如下:

由于 SSH/OpenSSL 等安全基建库几乎都在使用自行维护的用户态加密库, 所以 AF_ALG 可以直接禁用, 为临时缓解措施 (仅供参考):

rmmod algif_aead 2>/dev/null || true
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf

再尝试就不行了:

~$ python3 test.py Traceback (most recent call last):
  File "/home/songtianlun/test.py", line 9, in 
<module>
    while i<len(e):c(f,i,e[i:i+4]);i+=4
  File "/home/songtianlun/test.py", line 5, in c
    a=s.socket(38,5,0);a.bind(("aead","authencesn(hmac(sha256),cbc(aes))"));h=279;v=a.setsockopt;v(h,1,d('0800010000000010'+'0'*64));v(h,5,None,4);u,_=a.accept();o=t+4;i=d('00');u.sendmsg([b"A"*4+c],[(h,3,i*4),(h,2,b'\x10'+i*19),(h,4,b'\x08'+i*3),],32768);r,w=g.pipe();n=g.splice;n(f,w,o,offset_src=0);n(r,u.fileno(),o)
FileNotFoundError: [Errno 2] No such file or directory

影响面甚广,赶快检查一下自己的服务器。

Refs

Hermes Agent — 在 K3s / K8s 中运行指南

本文基于官方 Docker 文档,将 Hermes Agent 迁移到 Kubernetes / K3s 环境,使用 StatefulSet 管理持久化工作负载。

1. 前置准备

  • K3s 或 K8s 集群已就绪(本文以 K3s 为例)
  • 节点上已有 containerd(K3s 默认内置)
  • 推荐安装 nerdctl 作为容器管理工具(参考:在 K3s 节点上安装并使用 nerdctl
  • 镜像:nousresearch/hermes-agent:latest

2. 初始化配置(持久化数据目录)

在首次运行前,需要先执行一次 Setup Wizard,将 API Keys 等配置写入宿主机目录,再挂载进容器使用。

这里建议使用 nerdctl 运行,其他的方法需自行探索。

# 在目标节点上创建数据目录
mkdir -p /var/lib/hermes-data

# 使用 nerdctl 运行一次性 setup 容器(交互模式)
sudo nerdctl run -it --rm \
  -v /var/lib/hermes-data:/opt/data \
  nousresearch/hermes-agent:latest setup

配置完成后的数据目录结构

/var/lib/hermes-data/
├── .env            # API Keys 与密钥
├── config.yaml     # 主配置文件
├── SOUL.md         # Agent 人格 / 身份设定
├── sessions/       # 会话历史
├── memories/       # 持久记忆
├── skills/         # 已安装的技能
├── cron/           # 定时任务定义
├── hooks/          # 事件钩子
├── logs/           # 运行日志
└── skins/          # 自定义 CLI 皮肤

3. 部署 Gateway 后台服务(StatefulSet)

这里直接给出参考 yaml,按需调整:

---
apiVersion: v1
kind: Service
metadata:
  name: gateway
  namespace: hermes
spec:
  selector:
    app: gateway
  ports:
    - name: api
      port: 8642
      targetPort: 8642
  type: ClusterIP
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: data
  namespace: hermes
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 50Gi
  storageClassName: nfs-hhus3
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: gateway
  namespace: hermes
spec:
  serviceName: gateway
  replicas: 1
  selector:
    matchLabels:
      app: gateway
  template:
    metadata:
      labels:
        app: gateway
    spec:
      nodeSelector:
        hosthatch/zone: lax
      containers:
        - name: gateway
          image: nousresearch/hermes-agent:latest
          args: ["gateway", "run"]
          ports:
            - containerPort: 8642
          env:
            - name: TZ
              value: "Asia/Shanghai"
          volumeMounts:
            - name: hermes-data
              mountPath: /opt/data
          resources:
            requests:
              memory: "1Gi"
              cpu: "500m"
            limits:
              memory: "4Gi"
              cpu: "2"
      volumes:
        - name: hermes-data
          persistentVolumeClaim:
            claimName: data

4. 部署 Dashboard 仪表盘(StatefulSet)

直接给出参考 yaml,按需调整:

apiVersion: v1
kind: Service
metadata:
  name: dashboard
  namespace: hermes
spec:
  selector:
    app: dashboard
  ports:
    - name: web
      port: 9119
      targetPort: 9119
  type: ClusterIP   # 按需改为 NodePort / LoadBalancer
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: dashboard
  namespace: hermes
spec:
  serviceName: dashboard
  replicas: 1
  selector:
    matchLabels:
      app: dashboard
  template:
    metadata:
      labels:
        app: dashboard
    spec:
      nodeSelector:
        hosthatch/zone: lax
      containers:
        - name: dashboard
          image: nousresearch/hermes-agent:latest
          args: ["dashboard"]
          #args: ["dashboard", "--host", "0.0.0.0", "--insecure"]
          ports:
            - containerPort: 9119
          env:
            # 指向 Gateway Service 的 ClusterIP DNS 名称
            - name: GATEWAY_HEALTH_URL
              value: "http://gateway.hermes.svc.cluster.local:8642"
            - name: GATEWAY_HEALTH_TIMEOUT
              value: "3"
          volumeMounts:
            - name: hermes-data
              mountPath: /opt/data
              readOnly: true    # Dashboard 只读数据目录
          resources:
            requests:
              memory: "256Mi"
              cpu: "100m"
            limits:
              memory: "512Mi"
              cpu: "500m"

      volumes:
        - name: hermes-data
          persistentVolumeClaim:
            claimName: data

可以使用 port-forward 安全的访问仪表盘,不建议对外暴露:

kubectl port-forward -n hermes svc/hermes-dashboard 9119:9119
# 浏览器访问 http://localhost:9119

5. 运行交互式 CLI 聊天

在已部署并配置好的数据目录基础上,可随时进行交互式聊天,使用 kubectl exec 进入 Gateway 容器

kubectl exec -it -n hermes gateway-0 -- /opt/hermes/.venv/bin/hermes

Refs

在 K3s 节点上安装并使用 nerdctl

适用场景:K3s 默认不附带 nerdctl,但其内置的 containerd 与 nerdctl 完全兼容。本教程讲解如何在 K3s 节点上以最小代价安装 nerdctl,并正确指向 K3s 的 containerd socket,无需重复安装 containerd 或 CNI。

一、背景与原理

工具 说明
ctr containerd 内置调试工具,与 Docker CLI 不兼容,功能有限
crictl CRI 调试工具,K3s 自带,面向 Kubernetes 运维
nerdctl Docker 兼容 CLI,支持 run/build/compose推荐日常使用

K3s 的 containerd socket 路径为 /run/k3s/containerd/containerd.sock,而非标准路径 /run/containerd/containerd.sock。只需在配置中指向该路径,nerdctl 即可接管 K3s 容器管理。

K3s 已自带 CNI 插件(flannel/calico 等),查看 K3s 节点已有的 Pod 和镜像无需额外 CNI。若需要 nerdctl run 启动独立容器并连接网络,则需要补充安装 CNI 插件(见第四节)。

二、安装 nerdctl(仅二进制)

K3s 节点已有 containerd,只需下载 nerdctl 的精简包(不含 containerd/CNI,体积小)。

2.1 下载二进制

# 查询最新版本(或手动前往 https://github.com/containerd/nerdctl/releases 查看)
NERDCTL_VERSION=$(curl -s https://api.github.com/repos/containerd/nerdctl/releases/latest \
  | grep tag_name | cut -d '"' -f4 | tr -d 'v')

echo "最新版本: ${NERDCTL_VERSION}"

# 下载精简包(仅 nerdctl 二进制)
curl -LO "https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz"

ARM64 节点(如树莓派、ARM 服务器)将 amd64 替换为 arm64

curl -LO "https://github.com/containerd/nerdctl/releases/download/v${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION}-linux-arm64.tar.gz"

2.2 解压并安装

# 解压到 /usr/local/bin
sudo tar Cxzvf /usr/local/bin nerdctl-${NERDCTL_VERSION}-linux-amd64.tar.gz nerdctl

# 验证安装
nerdctl --version

三、配置 nerdctl 指向 K3s containerd

nerdctl 默认连接 /run/containerd/containerd.sock,在 K3s 节点上需要修改为 K3s 专用路径。

3.1 创建配置文件

sudo mkdir -p /etc/nerdctl

sudo tee /etc/nerdctl/nerdctl.toml > /dev/null <<EOF
# nerdctl 全局配置,适配 K3s 节点
address        = "/run/k3s/containerd/containerd.sock"
namespace      = "k8s.io"
EOF

说明

  • address:K3s containerd 的 socket 路径
  • namespace:K3s 所有容器和镜像均存储在 k8s.io 命名空间下

3.2 验证连接

# 列出 K3s 命名空间下的所有容器(等同于 kubectl get pods 的容器视角)
sudo nerdctl ps -a

# 列出镜像
sudo nerdctl images

如果能看到 K3s 系统 Pod(如 coredns、traefik 等),说明配置成功。

四、安装 CNI 插件(按需,用于 nerdctl run)

如果只需要查看 K3s 已有容器和镜像,可跳过此节。

只有当你需要用 nerdctl run 启动独立容器(即非 Kubernetes 管理的容器)时,才需要 CNI 插件。K3s 自带的 CNI 仅供 Kubernetes 使用,nerdctl 的独立容器网络需要单独配置。

4.1 下载官方 CNI 插件

CNI_VERSION=$(curl -s https://api.github.com/repos/containernetworking/plugins/releases/latest \
  | grep tag_name | cut -d '"' -f4)

curl -LO "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-amd64-${CNI_VERSION}.tgz"

# 安装到标准路径
sudo mkdir -p /opt/cni/bin
sudo tar Cxzvf /opt/cni/bin cni-plugins-linux-amd64-${CNI_VERSION}.tgz

4.2 创建默认网络配置

sudo mkdir -p /etc/cni/net.d

sudo tee /etc/cni/net.d/10-nerdctl-bridge.conflist > /dev/null <<EOF
{
  "cniVersion": "1.0.0",
  "name": "nerdctl-bridge",
  "plugins": [
    {
      "type": "bridge",
      "bridge": "nerdctl0",
      "isGateway": true,
      "ipMasq": true,
      "ipam": {
        "type": "host-local",
        "ranges": [
          [{"subnet": "10.88.0.0/16"}]
        ],
        "routes": [{"dst": "0.0.0.0/0"}]
      }
    },
    {
      "type": "portmap",
      "capabilities": {"portMappings": true}
    },
    {
      "type": "firewall"
    }
  ]
}
EOF

注意:此桥接网络(10.88.0.0/16)仅供 nerdctl 管理的独立容器使用,不会影响 K3s 自身网络。

4.3 验证独立容器运行

# 注意:启动独立容器时需使用默认命名空间(不加 --namespace k8s.io)
# 或在 nerdctl.toml 中临时切换,推荐直接在命令行覆盖:
sudo nerdctl --namespace default run -d --name test-nginx -p 8080:80 nginx:alpine

# 确认运行
sudo nerdctl --namespace default ps
curl http://localhost:8080

五、常用命令速查

所有命令均需 sudo(或将当前用户加入 containerd 相关权限组)。

查看 K3s 容器和镜像

# 列出所有容器(K3s 管理)
sudo nerdctl ps -a

# 列出镜像
sudo nerdctl images

# 查看容器日志
sudo nerdctl logs <容器ID或名称>

# 进入容器终端
sudo nerdctl exec -it <容器ID或名称> sh

镜像管理

# 拉取镜像(拉取后可直接被 K3s Pod 使用)
sudo nerdctl pull nginx:alpine

# 查看镜像详情
sudo nerdctl inspect <镜像ID>

# 删除镜像
sudo nerdctl rmi <镜像ID>

# 从 tar 包导入镜像(常用于离线环境)
sudo nerdctl load < image.tar

# 导出镜像为 tar 包
sudo nerdctl save nginx:alpine -o nginx.tar

构建镜像(需安装 BuildKit,见第六节)

sudo nerdctl build -t myapp:v1 /path/to/dockerfile-dir

配合 kubectl 使用本地镜像

# 构建并打 tag 到 k8s.io 命名空间
sudo nerdctl --namespace k8s.io build -t myapp:local .

# 然后在 Pod spec 中指定 imagePullPolicy: Never 即可使用本地镜像
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: myapp
spec:
  containers:
  - name: myapp
    image: myapp:local
    imagePullPolicy: Never
EOF

六、可选:安装 BuildKit(支持 nerdctl build)

nerdctl 构建镜像需要 BuildKit daemon。

BUILDKIT_VERSION=$(curl -s https://api.github.com/repos/moby/buildkit/releases/latest \
  | grep tag_name | cut -d '"' -f4)

curl -LO "https://github.com/moby/buildkit/releases/download/${BUILDKIT_VERSION}/buildkit-${BUILDKIT_VERSION}.linux-amd64.tar.gz"

sudo tar Cxzvf /usr/local buildkit-${BUILDKIT_VERSION}.linux-amd64.tar.gz

# 创建 systemd 服务
sudo tee /etc/systemd/system/buildkit.service > /dev/null <<EOF
[Unit]
Description=BuildKit
After=network.target containerd.service

[Service]
ExecStart=/usr/local/bin/buildkitd \
  --addr unix:///run/buildkit/buildkitd.sock \
  --containerd-worker-addr /run/k3s/containerd/containerd.sock
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now buildkit

Refs

Mouser:轻量开源的罗技鼠标驱动替代方案

以下内容整理自:https://meta.appinn.net/t/topic/83933

项目地址:https://github.com/TomBadash/Mouser


之前曾分享过一个可以按需精简安装 Logitech Options+ 功能的脚本工具 tjsky/logi-options-plus-mini,算是治标之策。然而实际使用下来,即便精简到了极致,Options+ 依然有个”顽疾”:它每周自动下载小几百 MB 的更新文件,却从不删除旧副本,硬盘俨然成了它的私人垃圾场。实测三个月后,相关文件体积已经膨胀到惊人的 2 GB

于是,找到了一个真正的替代方案——Mouser

简介

Mouser 是一个极其轻量、开源、完全本地运行的 Logitech Options+ 替代品,专用于对罗技 HID++ 鼠标进行按键和手势的重映射。目前对 MX Master 系列的支持最为完善,同时也对其他罗技型号提供了早期识别和通用 UI。

划重点:无需云端、无需罗技账号、纯本地运行!

安装包仅约 50 MB——而罗技官方完整离线驱动安装包高达 1.1 GB,半条命 2 整个游戏也才 1 GB……

功能特性

🖱️ 按键重映射

  • 可重映射中键、手势键、前进/后退、模式切换(滚轮按压)、水平滚轮等所有可编程按键
  • 按应用切换配置:在 Chrome 和 VS Code 之间切换时自动切换映射
  • 自定义快捷键:可将任意组合键(如 Ctrl+Shift+P)设为按键动作
  • 内置 30+ 常用动作,覆盖导航、浏览器、编辑、媒体、桌面等场景

⚙️ 设备控制

  • DPI / 指针速度:200–8000 DPI 滑块与快捷预设,实时同步
  • Smart Shift 开关:控制罗技”棘轮 ↔ 自由滚动”自动切换
  • 滚动方向反转:垂直/水平滚动可分别独立反转
  • 手势键 + 方向滑动:轻点触发一个动作,上/下/左/右滑动各自绑定不同动作

🖥️ 跨平台支持

  • 支持 Windows / macOS / Linux,各平台使用原生 Hook
  • 可开机自启,并支持”启动后最小化到托盘”
  • 系统托盘常驻,后台安静运行

🛡️ 隐私优先

  • 配置以本地 JSON 文件保存
  • 零遥测 / 零云端 / 零账号

当前支持的设备

鼠标型号 自动 HID++ 检测 UI 界面
MX Master 4 / 3S / 3 / 2S / MX Master 支持 MX Master 系列专用交互界面
MX Anywhere 3S / 3 / 2S 支持 通用界面(实验性专用界面,需手动切换)
MX Vertical 支持 通用界面(实验性专用界面,需手动切换)
其他罗技 HID++ 鼠标 部分支持(基于 PID/名称) 通用界面

下载与安装

无需安装,下载解压双击运行即可。

  1. 前往 最新 Release 页面 下载
  2. 根据系统选择:Mouser-Windows.zip / Mouser-macOS.zip / Mouser-Linux.zip
  3. 解压到任意目录
  4. ⚠️ 关键步骤:彻底关闭后台的 Logitech Options+!(两者同时抢占 HID++ 访问会一起崩溃)
  5. 运行:双击 Mouser.exe(Windows)/ Mouser.app(macOS)/ ./Mouser(Linux)

启动后,托盘区会出现图标,按键重映射立即生效。关闭窗口不会退出程序,如需完全退出请右键托盘图标选择 Quit Mouser

macOS 用户首次运行需授予两个隐私权限(辅助功能 + 输入监控);Linux 用户需要 /dev/input/event* 读取权限和 /dev/uinput 写入权限。

使用注意事项

  • Windows SmartScreen 拦截:点击”更多信息” → “仍要运行”即可
  • 配置文件位置%APPDATA%\Mouser(Windows)、~/Library/Application Support/Mouser(macOS)、~/.config/Mouser(Linux)
  • 平时无需管理员权限;若游戏或系统级弹窗下侧键映射失效,可尝试以管理员权限运行
  • MX Anywhere / MX Vertical 系列可能需要在右上角手动切换交互式配置图,目前属实验性支持

平台替代方案参考

  • macOS 用户可以考虑付费但体验更好的 SteerMouseBetterMouse,平滑滚动更接近苹果原生体验
  • Linux 用户推荐 LogiOps,无 GUI 但效果更好

小结

如果你是 MX Master 或 MX Anywhere 系列用户,只需要按键映射和 DPI 调节等基础功能,Mouser 是一个值得尝试的轻量替代方案,可以彻底告别臃肿的 Logitech Options+。

Claude Opus 4.7:优缺点与评测信息汇总

以下内容转载自:https://linux.do/t/topic/1984117

基本资料

官方文:https://www.anthropic.com/news/claude-opus-4-7 官方文档:https://platform.claude.com/docs/en/about-claude/models/whats-new-claude-4-7 官方模型卡:https://cdn.sanity.io/files/4zrzovbb/website/037f06850df7fbe871e206dad004c3db5fd50340.pdf

价格:输入$5/输出$25与4.6一样 上下文:1m,与4.6一样 最大输出:128k,与4.6一样 输出速度:50tps左右,比4.6快30% 4.7 vs mythos:性能提升但明显不如mythos 切换回4.6的方法:/model claude-opus-4-6[1m]


优点:自主编程能力显著提升

能搞定更难、更长链的任务,还会主动验证输出再汇报。

优点:视觉处理能力显著提升

支持长边最高2576像素,即2.5k,是4.6三倍

新功能:/ultrareview

在线review,额外收费,Pro和Max用户可以免费试用三次

新功能:auto模式,自主决策、连续执行

首次向Max用户开放

新功能:在high和max之间增加xhigh

个人感觉实用性没多大,更多是一种营销策略,用来对用户认知作战,证明max比xhigh(映射codex)大 eb3ef932161795bdd77360f683519808|690x388


缺点:费token

因为分词器调整,4.7比4.6多费Token 来源:https://linux.do/t/topic/1983983 image|472x500


中文多费6%,英文多费59%,python多费21%,中国用户也别偷笑,你的代码还是英文的 image|332x500


官方说max会员额度可能有增加,对应分词器问题 来源:https://linux.do/t/topic/1984150 image|587x275


缺点:长上下文注意力差

MRCR长上下文测试,1m上下文性能比4.6有了大幅下降 来源:https://www.reddit.com/r/ClaudeAI/comments/1sn6eyd/ https://linux.do/t/topic/1983983/ https://linux.do/t/topic/1983616/


缺点:说话风格像GPT

https://linux.do/t/topic/1984103/


评测

官方自评


LMArena评分 截止2026-04-17 01:54尚未上线

来源:https://arena.ai/leaderboard

LiveBench评分

来源:https://livebench.ai 暂无opus-4.7-max


ArtificialAnalysis评分 截止2026-04-17 01:54尚未上线

来源:https://artificialanalysis.ai/

ARC-AGI评分

来源:https://arcprize.org/leaderboard


Humanity’s Last Exam (HLE)


门萨智商测试,未上线

来源:https://www.trackingai.org/home


vals.ai评分


Linux 裸机安全部署 Hermes Agent

第一步:准备服务器环境

出于安全考虑,我们不应直接使用 root 用户运行应用程序。第一步是创建一个专用的非特权用户,并给予其 sudo 权限。

  1. root 用户 SSH 登录你的服务器
ssh root@your_server_ip
  1. 创建一个新用户(我们称之为 hermes):
adduser hermes

系统会提示你为新用户设置密码和其他信息。

  1. 将新用户添加到 sudo,以便执行需要管理员权限的操作:
usermod -aG sudo hermes
  1. 切换到新用户
su - hermes
  1. 更新系统软件包
sudo apt update && sudo apt upgrade -y

现在,我们所有的操作都将在 hermes 用户下进行。

第二步:安装 hermes agent

curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash

第三步:配置 hermes agent

hermes setup

Refs

openFuyao NPU-Operator故障排查

故障 pod describe

[root@master1 ~]# kubectl -n kube-system describe pod ascend-device-plugin-ll46f 
Name:                 ascend-device-plugin-ll46f
Namespace:            kube-system
Priority:             2000001000
Priority Class Name:  system-node-critical
Service Account:      ascend-device-plugin-sa
Node:                 master1/10.17.30.131
Start Time:           Mon, 30 Mar 2026 11:08:32 +0800
Labels:               app.kubernetes.io/managed-by=npu-operator
                      controller-revision-hash=7df5dcb887
                      helm.sh/chart=npu-operator-0.15.0
                      name=ascend-device-plugin-ds
                      pod-template-generation=1
Annotations:          cni.projectcalico.org/containerID: c1f2adcaeaaf2bdcf0a6e09730f68231a293074e31d58f61997f714dfb520878
                      cni.projectcalico.org/podIP: 192.168.137.118/32
                      cni.projectcalico.org/podIPs: 192.168.137.118/32
                      scheduler.alpha.kubernetes.io/critical-pod: 
                      seccomp.security.alpha.kubernetes.io/pod: runtime/default
Status:               Running
IP:                   192.168.137.118
IPs:
  IP:           192.168.137.118
Controlled By:  DaemonSet/ascend-device-plugin
Init Containers:
  init-permission:
    Container ID:  containerd://4406968a522bea48dfefebae81ec53644312762af4781c25de689952ed6c2d27
    Image:         cr.openfuyao.cn/openfuyao/busybox:1.36.1
    Image ID:      cr.openfuyao.cn/openfuyao/busybox@sha256:4b8407fadd8100c61b097d63efe992b2c033e7d371c9117f7a9462fe87e31176
    Port:          
<none>
    Host Port:     
<none>
    Command:
      sh
      -c
      chown 9000:9000 /var/log/mindx-dl /var/log/mindx-dl/devicePlugin
      chmod 750 /var/log/mindx-dl/devicePlugin

    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Mon, 30 Mar 2026 15:28:32 +0800
      Finished:     Mon, 30 Mar 2026 15:28:32 +0800
    Ready:          True
    Restart Count:  1
    Environment:    
<none>
    Mounts:
      /var/log/mindx-dl/devicePlugin from log-path (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-gfldg (ro)
Containers:
  device-plugin-01:
    Container ID:  containerd://fcc0c4742285847e2621a9a9217502307fc7e28644fbf86b32f9c11d67a2c0ab
    Image:         cr.openfuyao.cn/openfuyao/ascend-image/ascend-k8sdeviceplugin:v6.0.0
    Image ID:      cr.openfuyao.cn/openfuyao/ascend-image/ascend-k8sdeviceplugin@sha256:a5b9612b21bcd35384f9f19a05b2d7915b865e7b2be6a30bfd7806a9b8a86f58
    Port:          
<none>
    Host Port:     
<none>
    Command:
      /bin/bash
      -c
      --
    Args:
      device-plugin  -useAscendDocker=true -volcanoType=false -logFile=/var/log/mindx-dl/devicePlugin/devicePlugin.log -logLevel=0
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Tue, 31 Mar 2026 10:28:58 +0800
      Finished:     Tue, 31 Mar 2026 10:28:58 +0800
    Ready:          False
    Restart Count:  274
    Limits:
      cpu:     500m
      memory:  500Mi
    Requests:
      cpu:     500m
      memory:  500Mi
    Environment:
      NODE_NAME:   (v1:spec.nodeName)
    Mounts:
      /tmp from tmp (rw)
      /usr/local/Ascend/driver from hiai-driver (ro)
      /var/lib/kubelet/device-plugins from device-plugin (rw)
      /var/lib/kubelet/pod-resources from pod-resource (rw)
      /var/log/mindx-dl/devicePlugin from log-path (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-gfldg (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True 
  Initialized                 True 
  Ready                       False 
  ContainersReady             False 
  PodScheduled                True 
Volumes:
  device-plugin:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/kubelet/device-plugins
    HostPathType:  
  pod-resource:
    Type:          HostPath (bare host directory volume)
    Path:          /var/lib/kubelet/pod-resources
    HostPathType:  
  hiai-driver:
    Type:          HostPath (bare host directory volume)
    Path:          /usr/local/Ascend/driver
    HostPathType:  
  log-path:
    Type:          HostPath (bare host directory volume)
    Path:          /var/log/mindx-dl/devicePlugin
    HostPathType:  DirectoryOrCreate
  tmp:
    Type:          HostPath (bare host directory volume)
    Path:          /tmp
    HostPathType:  
  kube-api-access-gfldg:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    Optional:                false
    DownwardAPI:             true
QoS Class:                   Burstable
Node-Selectors:              openfuyao.com/npu.present=
Tolerations:                 CriticalAddonsOnly op=Exists
                             device-plugin=v2:NoSchedule
                             huawei.com/Ascend910:NoSchedule op=Exists
                             node-role.kubernetes.io/control-plane:NoSchedule
                             node-role.kubernetes.io/master:NoSchedule
                             node.kubernetes.io/disk-pressure:NoSchedule op=Exists
                             node.kubernetes.io/memory-pressure:NoSchedule op=Exists
                             node.kubernetes.io/not-ready:NoExecute op=Exists
                             node.kubernetes.io/pid-pressure:NoSchedule op=Exists
                             node.kubernetes.io/unreachable:NoExecute op=Exists
                             node.kubernetes.io/unschedulable:NoSchedule op=Exists
Events:
  Type     Reason   Age                     From     Message
  ----     ------   ----                    ----     -------
  Normal   Pulled   16m (x205 over 18h)     kubelet  (combined from similar events): Successfully pulled image "cr.openfuyao.cn/openfuyao/ascend-image/ascend-k8sdeviceplugin:v6.0.0" in 403ms (403ms including waiting). Image size: 48017174 bytes.
  Warning  BackOff  2m47s (x5216 over 18h)  kubelet  Back-off restarting failed container device-plugin-01 in pod ascend-device-plugin-ll46f_kube-system(8edcd384-ab2d-4998-8077-5ac58801c79e)
  Normal   Pulling  66s (x227 over 19h)     kubelet  Pulling image "cr.openfuyao.cn/openfuyao/ascend-image/ascend-k8sdeviceplugin:v6.0.0"

故障 pod /dev 检查

[root@master1 fuyao-26.3-rc3]# kubectl  -n kube-system exec -it daemonsets/ascend-device-plugin -- ls /dev
Defaulted container "device-plugin-01" out of: device-plugin-01, init-permission (init)
autofs           null               tty10  tty34  tty58    vcs5
bsg              ppp                tty11  tty35  tty59    vcs6
btrfs-control    ptmx               tty12  tty36  tty6     vcsa
bus              pts                tty13  tty37  tty60    vcsa1
core             random             tty14  tty38  tty61    vcsa2
cpu_dma_latency  raw                tty15  tty39  tty62    vcsa3
cuse             relationship_ctrl  tty16  tty4   tty63    vcsa4
davinci0         rfkill             tty17  tty40  tty7     vcsa5
davinci_manager  rtc0               tty18  tty41  tty8     vcsa6
devmm_svm        sda                tty19  tty42  tty9     vcsu
dri              sda1               tty2   tty43  ttyAMA0  vcsu1
fb0              sda2               tty20  tty44  ttyS0    vcsu2
fd               sg0                tty21  tty45  ttyS1    vcsu3
full             sg1                tty22  tty46  ttyS2    vcsu4
fuse             sg2                tty23  tty47  ttyS3    vcsu5
hidraw0          shm                tty24  tty48  uhid     vcsu6
hidraw1          snapshot           tty25  tty49  uinput   vfio
hisi_hdc         sr0                tty26  tty5   urandom  vga_arbiter
hwrng            sr1                tty27  tty50  usbmon0  vhost-net
input            stderr             tty28  tty51  usbmon1  vhost-vsock
kmsg             stdin              tty29  tty52  usbmon2  vport2p1
loop-control     stdout             tty3   tty53  vcs      zero
mapper           termination-log    tty30  tty54  vcs1
mem              tty                tty31  tty55  vcs2
mqueue           tty0               tty32  tty56  vcs3
net              tty1               tty33  tty57  vcs4

故障 pod 驱动检查

[root@master1 fuyao-26.3-rc3]# kubectl  -n kube-system exec -it daemonsets/ascend-device-plugin -- ls -lha /usr/local/Ascend/driver
Defaulted container "device-plugin-01" out of: device-plugin-01, init-permission (init)
total 44K
drwxr-xr-x  8 root root 4.0K Mar 27 08:03 .
drwxr-xr-x  3 root root 4.0K Mar 31 02:34 ..
drwxr-xr-x  2 root root 4.0K Mar 27 08:01 bin
-r--r--r--  1 root root   20 Mar 27 08:01 build.info
dr-xr-x---  2 root root 4.0K Mar 27 08:01 device
dr-x------ 41 root root 4.0K Mar 27 08:01 kernel
drwxr-xr-x  6 root root 4.0K Mar 27 08:01 lib64
-r--r-----  1 root root   56 Mar 27 08:01 scene.info
dr-xr-x---  2 root root 4.0K Mar 27 08:01 script
drwxr-xr-x  2 root root 4.0K Mar 27 08:01 tools
-r--r--r--  1 root root  352 Mar 27 08:03 version.info

故障 pod 日志

[root@master1 ~]# kubectl -n kube-system logs daemonsets/ascend-device-plugin --previous
Defaulted container "device-plugin-01" out of: device-plugin-01, init-permission (init)
[INFO]     2026/03/31 06:46:54.593254 1       hwlog/api.go:108    devicePlugin.log's logger init success
[INFO]     2026/03/31 06:46:54.593449 1       main.go:187    ascend device plugin starting and the version is v6.0.0_linux-aarch64
[INFO]     2026/03/31 06:46:54.593494 1       main.go:188    ascend device plugin starting scene is center
[INFO]     2026/03/31 06:46:54.787930 1       devmanager/devmanager.go:104    the dcmi version is 24.1.rc3
[ERROR]    2026/03/31 06:46:54.788019 1       devmanager/devmanager.go:211    get error card quantity: 0
[ERROR]    2026/03/31 06:46:54.788052 1       devmanager/devmanager.go:195    get card list failed for init
[ERROR]    2026/03/31 06:46:54.788101 1       main.go:203    init devmanager failed, err: auto init failed, err: get card list failed for init

故障 pod 驱动检查

[root@master1 ~]# kubectl -n kube-system exec -it daemonsets/ascend-device-plugin -- bash -c 'find /usr/local/Ascend/driver -name libdcmi.so 2>/dev/null; echo $LD_LIBRARY_PATH'
Defaulted container "device-plugin-01" out of: device-plugin-01, init-permission (init)
/usr/local/Ascend/driver/lib64/driver/libdcmi.so
command terminated with exit code 137
[root@master1 ~]# ps -ef | grep -E 'dmp_daemon|slogd' | grep -v grep
root       21578       1  0 Mar30 ?        00:00:19 /usr/sbin/rsyslogd -n -i/var/run/rsyslogd.pid

检查服务状态?

[root@master1 ~]# systemctl status ascend-dmi
Unit ascend-dmi.service could not be found.
[root@master1 ~]# systemctl status ascend-dkms
Unit ascend-dkms.service could not be found.
[root@master1 ~]# systemctl status npu-smi
Unit npu-smi.service could not be found.
[root@master1 ~]# find / -name dmp_daemon 2>/dev/null
[root@master1 ~]# find / -name slogd 2>/dev/null
[root@master1 ~]# ls -l /var/dmp_daemon /var/slogd 2>/dev/null
[root@master1 ~]# 

dcmi 问题,需硬件排查

检查卡获取

#include <stdlib.h>
#include <stdio.h>
#include "dcmi_interface_api.h"

int my_get_card_list();

int main(int argc,char *argv[])
{
    my_get_card_list();
    return 0;
}

int my_get_card_list()
{
    printf("\n==================================card id info list=========================\n");
    dcmi_init();
    int card_num = 0;
    int card_list[16] = {0};
    int ret = dcmi_get_card_list(&card_num, card_list, 16);
    if (ret != DCMI_OK) {
        printf("dcmi get card list failed ret=%d\n", ret);
    }
    printf("card_num=%d, card_list:[",card_num);
    for (int i = 0; i < card_num; i++) {
        printf("%d ", card_list[i]);
    }
}
cc ./test1.c -o test1 -I /usr/local/dcmi -L /usr/local/dcmi -ldcmi

-I头文件(.h)搜索路径
-L库文件(.so/.a)搜索路径
-l链接的库名(去掉 lib 前缀)
nerdctl run --rm \
  -v /usr/local/Ascend:/usr/local/Ascend \
  -v /usr/local/dcmi:/usr/local/dcmi \
  -v $(pwd):/build \
  ubuntu:18.04 bash -c "
    sed -i -e 's@http*://ports.ubuntu.com/\? @http://10.17.31.217:8081/repository/mirror-ubuntu-ports/@g' \
           -e 's@http*://ports.ubuntu.com@http://10.17.31.217:8081/repository/mirror-ubuntu-ports@g' \
           /etc/apt/sources.list
    apt update && apt install -y gcc
    cd /build
    cc ./test1.c -o test1 \
      -I /usr/local/dcmi \
      -L /usr/local/dcmi \
      -L /usr/local/Ascend/driver/lib64/common \
      -L /usr/local/Ascend/driver/lib64/driver \
      -ldcmi \
      -Wl,-rpath,/usr/local/Ascend/driver/lib64/common \
      -Wl,-rpath,/usr/local/Ascend/driver/lib64/driver \
      -Wl,-rpath,/usr/local/dcmi
  "

分析二进制:

[root@master1 ascend_debug]# ldd ./test1 | grep -i dcmi
        libdcmi.so => /usr/local/Ascend/driver/lib64/driver/libdcmi.so (0x0000ffffa6dd0000)
[root@master1 ascend_debug]# LD_DEBUG=libs ./test1 2>&1 | grep -i dcmi
    284830:     find library=libdcmi.so [0]; searching
    284830:      search path=/usr/local/Ascend/driver/lib64/common/tls/aarch64/atomics:/usr/local/Ascend/driver/lib64/common/tls/aarch64:/usr/local/Ascend/driver/lib64/common/tls/atomics:/usr/local/Ascend/driver/lib64/common/tls:/usr/local/Ascend/driver/lib64/common/aarch64/atomics:/usr/local/Ascend/driver/lib64/common/aarch64:/usr/local/Ascend/driver/lib64/common/atomics:/usr/local/Ascend/driver/lib64/common:/usr/local/Ascend/driver/lib64/driver/tls/aarch64/atomics:/usr/local/Ascend/driver/lib64/driver/tls/aarch64:/usr/local/Ascend/driver/lib64/driver/tls/atomics:/usr/local/Ascend/driver/lib64/driver/tls:/usr/local/Ascend/driver/lib64/driver/aarch64/atomics:/usr/local/Ascend/driver/lib64/driver/aarch64:/usr/local/Ascend/driver/lib64/driver/atomics:/usr/local/Ascend/driver/lib64/driver:/usr/local/dcmi/tls/aarch64/atomics:/usr/local/dcmi/tls/aarch64:/usr/local/dcmi/tls/atomics:/usr/local/dcmi/tls:/usr/local/dcmi/aarch64/atomics:/usr/local/dcmi/aarch64:/usr/local/dcmi/atomics:/usr/local/dcmi            (RUNPATH from file ./test1)
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/tls/aarch64/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/tls/aarch64/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/tls/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/tls/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/aarch64/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/aarch64/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/common/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/tls/aarch64/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/tls/aarch64/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/tls/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/tls/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/aarch64/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/aarch64/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/atomics/libdcmi.so
    284830:       trying file=/usr/local/Ascend/driver/lib64/driver/libdcmi.so
    284830:      search path=/usr/local/Ascend/driver/lib64/common:/usr/local/Ascend/driver/lib64/driver:/usr/local/dcmi/tls/aarch64/atomics:/usr/local/dcmi/tls/aarch64:/usr/local/dcmi/tls/atomics:/usr/local/dcmi/tls:/usr/local/dcmi/aarch64/atomics:/usr/local/dcmi/aarch64:/usr/local/dcmi/atomics:/usr/local/dcmi          (RUNPATH from file ./test1)
    284830:       trying file=/usr/local/dcmi/tls/aarch64/atomics/libc.so.6
    284830:       trying file=/usr/local/dcmi/tls/aarch64/libc.so.6
    284830:       trying file=/usr/local/dcmi/tls/atomics/libc.so.6
    284830:       trying file=/usr/local/dcmi/tls/libc.so.6
    284830:       trying file=/usr/local/dcmi/aarch64/atomics/libc.so.6
    284830:       trying file=/usr/local/dcmi/aarch64/libc.so.6
    284830:       trying file=/usr/local/dcmi/atomics/libc.so.6
    284830:       trying file=/usr/local/dcmi/libc.so.6
    284830:     calling init: /usr/local/Ascend/driver/lib64/driver/libdcmi.so
    284830:     calling fini: /usr/local/Ascend/driver/lib64/driver/libdcmi.so [0]
[root@master1 ascend_debug]# readlink -f /usr/local/dcmi/libdcmi.so
/usr/local/dcmi/libdcmi.so
[root@master1 ascend_debug]# readlink -f /usr/local/Ascend/driver/lib64/driver/libdcmi.so
/usr/local/Ascend/driver/lib64/driver/libdcmi.so
[root@master1 ascend_debug]# sha256sum /usr/local/dcmi/libdcmi.so /usr/local/Ascend/driver/lib64/driver/libdcmi.so
13a38cae84bad0f06367ff9280016e372c0608ca16465b5ae5f000d3844ee401  /usr/local/dcmi/libdcmi.so
13a38cae84bad0f06367ff9280016e372c0608ca16465b5ae5f000d3844ee401  /usr/local/Ascend/driver/lib64/driver/libdcmi.so

跟踪 strace

宿主机跑
strace -f -o /tmp/host.strace -e trace=file,ioctl ./test1
容器里跑
strace -f -o /tmp/container.strace -e trace=file,ioctl ./test1

新增挂载继续跟踪

volumeMounts:
        - name: hdc-basic
          mountPath: /etc/hdcBasic.cfg
          readOnly: true
        - name: localtime
          mountPath: /etc/localtime
          readOnly: true
      volumes:
      - name: hdc-basic
        hostPath:
          path: /etc/hdcBasic.cfg
          type: File
      - name: localtime
        hostPath:
          path: /etc/localtime
          type: File

跟踪并查看日志

kubectl -n kube-system exec -it ascend-device-plugin-69q5t -c device-plugin-01 -- bash

strace -f -o /tmp/container.strace -e trace=file,ioctl ./test1

root@ascend-device-plugin-69q5t:/tmp# strace -f -o /tmp/container.strace -e trace=file,ioctl ./test1

==================================card id info list=========================
card_num=0, card_list:[

root@ascend-device-plugin-69q5t:/tmp# cat /var/log/nputools_LOG_INFO.log > /tmp/nputools_LOG_INFO.log
root@ascend-device-plugin-69q5t:/tmp# cat /var/log/nputools_LOG_ERR.log > /tmp/nputools_LOG_ERR.log
cat: /var/log/nputools_LOG_ERR.log: No such file or directory
root@ascend-device-plugin-69q5t:/tmp# cat /tmp/nputools_LOG_INFO.log 
[2026/04/01 11:18:12][0583][root][127.0.0.1][dcmi_api.c,dcmi_board_init,86]:dcmi board init success. device_count=1.
[2026/04/01 11:18:12][0583][root][127.0.0.1][dcmi_api.c,dcmi_init,119]:dcmi init all success.

检查代码2

#include <stdio.h>
#include <stdlib.h>
#include "dcmi_interface_api.h"

#ifndef DCMI_OK
#define DCMI_OK 0
#endif

/* 头文件里没看到这个声明,手动补一个 */
extern int dcmi_get_card_num_list(int *card_num, int *card_list, int list_length);

static void print_list(const char *name, int ret, int num, int *list) {
    printf("%s ret=%d num=%d list=[", name, ret, num);
    for (int i = 0; i < num; ++i) {
        printf("%d ", list[i]);
    }
    printf("]\n");
}

int main(void) {
    int ret = dcmi_init();
    printf("dcmi_init ret=%d\n", ret);
    if (ret != DCMI_OK) {
        return 1;
    }

    int card_num = 0;
    int card_list[16] = {0};

    ret = dcmi_get_card_list(&card_num, card_list, 16);
    print_list("dcmi_get_card_list", ret, card_num, card_list);

    int card_num2 = 0;
    int card_list2[16] = {0};
    ret = dcmi_get_card_num_list(&card_num2, card_list2, 16);
    print_list("dcmi_get_card_num_list", ret, card_num2, card_list2);

    for (int i = 0; i < card_num && i < 16; ++i) {
        int dev_num = -1;
        ret = dcmi_get_device_num_in_card(card_list[i], &dev_num);
        printf("dcmi_get_device_num_in_card card=%d ret=%d dev_num=%d\n",
               card_list[i], ret, dev_num);
    }

    return 0;
}

主机编译

cc ./test2.c -o test2 -I /usr/local/dcmi -L /usr/local/dcmi -ldcmi

容器编译

nerdctl run --rm \
  -v /usr/local/Ascend:/usr/local/Ascend \
  -v /usr/local/dcmi:/usr/local/dcmi \
  -v $(pwd):/build \
  ubuntu:18.04 bash -c "
    sed -i -e 's@http*://ports.ubuntu.com/\? @http://10.17.31.217:8081/repository/mirror-ubuntu-ports/@g' \
           -e 's@http*://ports.ubuntu.com@http://10.17.31.217:8081/repository/mirror-ubuntu-ports@g' \
           /etc/apt/sources.list
    apt update && apt install -y gcc
    cd /build
    cc ./test2.c -o test2 \
      -I /usr/local/dcmi \
      -L /usr/local/dcmi \
      -L /usr/local/Ascend/driver/lib64/common \
      -L /usr/local/Ascend/driver/lib64/driver \
      -ldcmi \
      -Wl,-rpath,/usr/local/Ascend/driver/lib64/common \
      -Wl,-rpath,/usr/local/Ascend/driver/lib64/driver \
      -Wl,-rpath,/usr/local/dcmi
  "

拷入容器运行

kubectl -n kube-system cp ./test2 ascend-device-plugin-69q5t:/tmp/
# 主机运行
[root@master1 ascend_debug]# ./test2
dcmi_init ret=0
dcmi_get_card_list ret=0 num=1 list=[176 ]
dcmi_get_card_num_list ret=0 num=1 list=[176 ]
dcmi_get_device_num_in_card card=176 ret=0 dev_num=1

# 容器运行
root@ascend-device-plugin-69q5t:/tmp# ./test2 
dcmi_init ret=0
dcmi_get_card_list ret=0 num=0 list=[]
dcmi_get_card_num_list ret=0 num=0 list=[]

虚拟机场景

经过许老师认真定位,最终发现是因为非裸金属环境。虚拟机场景需要定制镜像。

根据官网文档

如果在虚拟机场景下部署Ascend Device Plugin,需要在Ascend Device Plugin的镜像中安装systemd,推荐在Dockerfile中加入RUN apt-get update && apt-get install -y systemd命令进行安装。

为了使用 nerdctl 构建镜像首先安装 buildkit

wegt https://github.com/moby/buildkit/releases/download/v0.29.0/buildkit-v0.29.0.linux-arm64.tar.gz
tar zxvf buildkit-v0.29.0.linux-arm64.tar.gz
cp bin/* /usr/local/bin/

之后找一个新终端启动 buildkit ,这里是为了 nerdctl 构建 image, 如果不需要则不用启动。

buildkitd --oci-worker=false --containerd-worker=true --containerd-worker-namespace=k8s.io 

Dockerfile 如下:

镜像源部分按需修改


FROM hub.oepkgs.net/openfuyao/ascendhub/ascend-k8sdeviceplugin:v6.0.0

替换 apt 镜像源

RUN sed -i \ -e ‘s@http://ports.ubuntu.com/\? @http://10.17.31.217:8081/repository/mirror-ubuntu-ports/@g‘ \ -e ‘s@http://ports.ubuntu.com@http://10.17.31.217:8081/repository/mirror-ubuntu-ports@g‘ \ /etc/apt/sources.list

安装 systemd

RUN apt-get update && \ apt-get install -y –no-install-recommends systemd systemd-sysv && \ apt-get clean && \ rm -rf /var/lib/apt/lists/*

设置 systemd 为 init

STOPSIGNAL SIGRTMIN+3


> STOPSIGNAL SIGRTMIN+3 是告诉容器运行时(containerd/docker)停止这个容器时应该发送哪个信号。
> 如果你的容器里 不跑 systemd 作为主进程(比如 entrypoint 是业务程序),这行可以删掉,没有任何作用。
> 如果确实用 systemd 管理容器内服务,保留它能避免 kubectl delete pod 时等待 30 秒超时再强杀的问题。

构建命令如下:

nerdctl build \ –namespace k8s.io \ -t hub.oepkgs.net/openfuyao/ascendhub/ascend-k8sdeviceplugin:v6.0.0-systemd \ -f Dockerfile \ .


之后将出问题的镜像替换为新构建的镜像即可。

npu-operator 有同样的问题,一样修改即可。

## 修复确认
> 最终在 node 中能看到 npu 资源即成功。

[root@master1 ~]# kubectl describe node master1 Name: master1 Roles: control-plane,master,node,worker Labels: accelerator=huawei-Ascend310P beta.kubernetes.io/arch=arm64 beta.kubernetes.io/os=linux … servertype=Ascend310P-8 workerselector=dls-worker-node Annotations: baseDeviceInfos: {“Ascend310P-0”:{“IP”:””,”SuperDeviceID”:0}} … Capacity: cpu: 16 ephemeral-storage: 129724184Ki huawei.com/Ascend310P: 1 hugepages-1Gi: 0 hugepages-2Mi: 0 hugepages-32Mi: 0 hugepages-64Ki: 0 memory: 32595632Ki pods: 110 Allocatable: cpu: 16 ephemeral-storage: 119553807777 huawei.com/Ascend310P: 1 hugepages-1Gi: 0 hugepages-2Mi: 0 hugepages-32Mi: 0 hugepages-64Ki: 0 memory: 32493232Ki pods: 110 … Allocated resources: (Total limits may be over 100 percent, i.e., overcommitted.) Resource Requests Limits


cpu 15565m (97%) 16910m (105%) memory 17492Mi (55%) 30900Mi (97%) ephemeral-storage 0 (0%) 0 (0%) hugepages-1Gi 0 (0%) 0 (0%) hugepages-2Mi 0 (0%) 0 (0%) hugepages-32Mi 0 (0%) 0 (0%) hugepages-64Ki 0 (0%) 0 (0%) huawei.com/Ascend310P 0 0 …