普通视图

发现新文章,点击刷新页面。
昨天以前黑羽的个人博客

Java和SheBang

作者 黑羽
2026年3月13日 14:22

也是今天才知道,原来 Java 可以直接这么用,JDK-11 的特性,可能之前看到过没在意吧,今天就分享下。

#!/usr/bin/java --source 11public class Bang {    public static void main(final String[] args) {        System.out.println("Hello World!");    }}

在类 Unix 系统中,例如 MacOS 和 Linux ,把上面内容保存为一个文件,例如 Bang,然后赋予执行权限

chmox +x Bang

接着就可以直接执行了

./Bang 

输出

Hello World!

就像是写 Shell 脚本一样。

这个特性叫 SheBang,还很纳闷为什么叫这个名字,又问了下 AI,有兴趣的可以自己去看看

书接上文,JetBrains,Codex,和 AI 相关思考与实践

作者 黑羽
2026年3月9日 16:03

上篇文章,讲了我买了 JetBrains Idea 和 Codex 会员相关事情,这个,就讲下我是怎么使用的,和我对 AI 和 JetBrains的看法

f6cd072daa708cf7934340ea7c001463.jpg

最近看到的一张图


JetBrains 的 Agent IDE "Air"

也是今天下午,看到一个新闻,JetBrain上推出了自家的 Agent IDE,没有辜负我上文对 JetBrains 的看法,他们还是没有那么落后的,只是还缺一个时机。

现在只开放了 Mac 的下载,没有看项目具体内容,看了下 Licenses 列表,发现有 Fleet 相关字眼,推测可能就是之前的 Fleet 碎土转生了,可以推断的是使用 Java 或者 Kotlin 编写的,所以性能上,就目前我下载的体验,还是要比 Codex 桌面版这种使用 Electron 的程序好多了的。

然后界面很内容也是保持了 JetBrains 一贯的专业和美观。下面是一些截图示例,让 AI 给我写个 CRM 系统来着,中间提示词我也把 CRM 和 ORM 搞混了,不过可能是我提示词太少了,AI 还不好从 0 完成这种复杂的任务,但是总体功能的体验还是不错的。

64469c2ca21727a021b3147ec5bb6eaa.pngimage.pngimage.png

基础工具像是代码高亮,Git 管理,目前我觉得 JetBrains 还是挺无敌的。


Idea 相关 AI 使用

Idea 现在的 AI 工具还是有很多可以优化的点的,不能与 Idea 进行完美集成,比如在构建项目这块,Idea 自带了 JDK 管理,而 AI 通常会选择使用系统的,如果系统没有,就去下载一个,这很显然是一种浪费。其实 Idea 提供了一系列功能供 AI 接入,也就是 MCP Server,在 25.02 以上版本这个系统已经有了。

image.pngimage.pngimage.png

你可以把 MCP Server 的链接加入到 Idea 自身里面,让 Idea 里面的 AI 插件可以访问 Idea (听起来很奇怪,不过这个理应是原本就有的),或者加入到外部工具,让外部工具可以访问 Idea。

我是在 AI 的 AGENTS.md 加入了下面这行话,这样他编译项目什么的就会调用 Idea 的功能。

# TOOLS USE当需要运行项目和编译项目时,优先使用 idea 提供的方式,如果 idea 提供的不可用,再使用别的方式,编译项目时,如果没有指定,默认跳过测试;运行shell命令可以使用你自己的方式。

然后就是在使用 Codex 时候,现在版本会出现重新连接的情况,5 次失败后会从 WebScoket 降级到 Http,应该是现在 Codex 问题,可以在配置文件中禁用 WebScoket 来临时解决这个问题,后面 Codex 更新也许会解决。

在 Codex 的配置文件中加入下面的内容

model_provider = "openai_http"[model_providers.openai_http]name = "OpenAI HTTP only"wire_api = "responses"requires_openai_auth = truesupports_websockets = falsebase_url = "https://chatgpt.com/backend-api/codex"

如果你是使用 Idea 自带的 Codex话, Idea 的 Codex 是一个单独的程序,windows 位置在 \AppData\Local\JetBrains\IntelliJIdea2025.3\aia\codex,每次启动 Idea 都会重新解压覆盖 Codex 文件,所以改这里配置是不会生效的,每次都会被覆盖。

好在 Idea 可以支持外部 ACP ,可以把上面 Idea 内部的 Codex 复制出来,用自定义 ACP 的方式使用。
像是下面这样。

image.png

CODEX_HOME 可以覆盖 Codex 的默认配置路径,默认是在 ~/codex,我是单独区分出来了,这个专门内嵌 Ide 中用,也可以写一些 Idea 专属的提示词。

注意这样就不会自动更新了,可以自己留意下,一般 Codex Cli 提示更新的时候,ACP 插件也应该出新版本了,这个 Codex ACP 插件好像是 Zed 做的,底部有放一下相关链接。


龙虾热

这两天也是各种 OpenClaw 消息层出不穷,甚至都有政府下场,不过我觉得对于普通人还是观望吧,学习新东西是好事,但是不值得花那么多时间浪费在这种项目上。

而且大多是工具还是为了需求服务的,尽量满足自己需求就好;这句话其实很多是说给我自己听的,因为我昨天折腾 Openwrt 翻墙又熬夜了,最后还没折腾出个所以然,总是分流哪里有问题,Openwrt 太老,好多新的东西装上去也是各种问题,后面可能是要从头来一遍了,不过当下,V2rayN 的 Tun 模式我觉得也可以用用。

之前试过 OpenClaw,那时候他还刚改名,腾讯云也进行了一些技术直播,但是吧,当时 OpenClaw 连续两次改名,还完全不考虑向后兼容的,导致腾讯云文档里提到的插件都失效了,我还是知道点程序,可以改下配置文件,不过也是头疼,最后用起来也就那样。

今天也是看飞书秒哒有个,自动在虚拟机里面给你搭建个 OpenClaw,看了下虚拟机有 4G 内存,突发奇想,就算我不用飞书的 Token, 用这台虚拟机配合内网穿透,运行一个 MC 服务器岂不美哉。


还有今天让 AI 给我分析一个SQL 问题,没想到还挺有用的,没想到 MySQL 还有这种写法,不过让它写一个模块就有很多要改的了。

后面有什么再说吧,今天先到这,AI 并不能帮我写博客呀,这篇文章的很多内容也会不久后过时,后来的读者如果看到了,注意创建时间。


相关链接

购买了Idea和Codex,以及最近三两事

作者 黑羽
2026年3月8日 20:05

之前也是一直用的 Idea 破解版,前两天 Windows 上的 Idea激活不知道怎么突然没了,也是不想折腾了,想着自己也是从事开发好几年了,一直靠 Java 和Jetbrains的开发工具吃饭,是该给人家点支持了。

最近 Idea 也是涨价了,我从网上搜了下折扣码,以 75 折 1237 元入手,后面续费的价格会便宜些,所以我如果后面仕途顺利的话,我想会一直续费了。


然后就是还入手了 ChatGPT Plus ,最近 Codex 可以免费体验,试用下来感觉还行,所以直接入手了,之前一直用的 Openrouter 的 Api,价格还挺贵的,而且付费比较不方便,用起来看着 Token 消耗也是很心疼呀,现在换会员计划感觉好多了,有种不狠狠用就浪费的感觉。付费从 Google Play 的订阅支付的,可以绕过 OpenAI 的信用卡地区限制。

正好 Idea 的这次更新也带来了官方 AI 支持,支持 ACP 协议的各种 AI 工具,还有自家的 Junie AI,有一定的免费额度。注意用 Jetbrains 工具,想用 AI 功能,要把设置的地区改成未指定,不要是中国,不然就只能用千问的模型了。

对于 ACP 协议接入的 AI 工具,例如 Codex 和 Claude ,和官方 cli 支持的功能还是有很大差别的,好在模型足够的强大了,还是可以用的。


说回 AI 和 Jetbrains,最近在看一些论坛的时候,看到一个说法,说 Jetbrains 已经慢慢被淘汰了,对于 AI 的支持太过于落后,而且 AI 时代,也不需要过于强大的 IDE 了;

我对这种还是持反对的看法吧,最近研究了一阵子 AI ,不得不承认的是 AI 是个好东西,但是这种好东西是给你如虎添翼的,但是现在很多 AI 工具,走还走不稳呢就想着跑了,一堆的问题,点名最近的 OpenClaw。

VsCode 这种编辑器,虽然免费而且早就有了对 AI 的支持,但是上面的功能真的是一言难尽,没有几个真正好用的,第三方插件的质量也是参差不齐。

JetBrains 让我看到一种工匠精神,认真最好每一处细节,我研究过他们的Teriminal Engine 和 内嵌 jecf 的 jvm,这些都是开源的,拆分出来都是非常不错的项目,也算是为计算机的发展添砖加瓦了,而不是像现在的 AI 一样产屎。

所以我觉得世界上需要这种认真做事的人和公司。


说说找工作。

前几天简单投了一些,基本也是没什么后续了,一个可能是现在行情吧,还有我觉得 BOSS 直聘现在也是挺乱的,上面有很多挂人头卖狗肉的公司,有些公司上面几千个岗位在招聘是认真的吗,看起来就像那些外包猎头,既然充了钱,就尽量使劲用,使劲给自己引流的感觉。

甚至还有人后面电话联系我要不要简历培训,所以这些人又把我的信息给卖给了别的公司? 说起来这些机构的人也是说是,遇到脾气好的直接挂了电话,脾气差的铁定给你一顿劈头臭骂,不过这也是他们应得的。让我想起之前刷到的一个视频,说是苏州那些,看起了光鲜亮丽的写字楼,其实里面要么空置,要么是一些搞这种电话生意的,想想我们公司这片园区,其实也差不多。


说说生活。

年后自己给车打了蜡,擦的手都快断了,不过就当是锻炼了,效果还是真不错,之前车漆感觉很涩,现在感觉光滑了不少,而且上面的灰尘也很容易擦掉,洗车好洗了一点,价格也比去洗车店低了不少,关键洗车店还要排队就挺烦的。

南京这边商城的人还是一如既往的多,最近又开了一些自助火锅,去吃了一家,也是挺实惠的,现在虽然说是经济下行,其他地方可以省省钱,吃个饭对于大部分人还是挺轻松的,对于我来说也是少有的放松方式。

image.pngimage.pngimage.png

商场的人们

购买了Idea和Codex,以及最近三两事

作者 黑羽
2026年3月8日 20:05

之前也是一直用的 Idea 破解版,前两天 Windows 上的 Idea激活不知道怎么突然没了,也是不想折腾了,想着自己也是从事开发好几年了,一直靠 Java 和Jetbrains的开发工具吃饭,是该给人家点支持了。

最近 Idea 也是涨价了,我从网上搜了下折扣码,以 75 折 1237 元入手,后面续费的价格会便宜些,所以我如果后面仕途顺利的话,我想会一直续费了。


然后就是还入手了 ChatGPT Plus ,最近 Codex 可以免费体验,试用下来感觉还行,所以直接入手了,之前一直用的 Openrouter 的 Api,价格还挺贵的,而且付费比较不方便,用起来看着 Token 消耗也是很心疼呀,现在换会员计划感觉好多了,有种不狠狠用就浪费的感觉。付费从 Google Play 的订阅支付的,可以绕过 OpenAI 的信用卡地区限制。

正好 Idea 的这次更新也带来了官方 AI 支持,支持 ACP 协议的各种 AI 工具,还有自家的 Junie AI,有一定的免费额度。注意用 Jetbrains 工具,想用 AI 功能,要把设置的地区改成未指定,不要是中国,不然就只能用千问的模型了。

对于 ACP 协议接入的 AI 工具,例如 Codex 和 Claude ,和官方 cli 支持的功能还是有很大差别的,好在模型足够的强大了,还是可以用的。


说回 AI 和 Jetbrains,最近在看一些论坛的时候,看到一个说法,说 Jetbrains 已经慢慢被淘汰了,对于 AI 的支持太过于落后,而且 AI 时代,也不需要过于强大的 IDE 了;

我对这种还是持反对的看法吧,最近研究了一阵子 AI ,不得不承认的是 AI 是个好东西,但是这种好东西是给你如虎添翼的,但是现在很多 AI 工具,走还走不稳呢就想着跑了,一堆的问题,点名最近的 OpenClaw。

VsCode 这种编辑器,虽然免费而且早就有了对 AI 的支持,但是上面的功能真的是一言难尽,没有几个真正好用的,第三方插件的质量也是参差不齐。

JetBrains 让我看到一种工匠精神,认真最好每一处细节,我研究过他们的Teriminal Engine 和 内嵌 jecf 的 jvm,这些都是开源的,拆分出来都是非常不错的项目,也算是为计算机的发展添砖加瓦了,而不是像现在的 AI 一样产屎。

所以我觉得世界上需要这种认真做事的人和公司。


说说找工作。

前几天简单投了一些,基本也是没什么后续了,一个可能是现在行情吧,还有我觉得 BOSS 直聘现在也是挺乱的,上面有很多挂人头卖狗肉的公司,有些公司上面几千个岗位在招聘是认真的吗,看起来就像那些外包猎头,既然充了钱,就尽量使劲用,使劲给自己引流的感觉。

甚至还有人后面电话联系我要不要简历培训,所以这些人又把我的信息给卖给了别的公司? 说起来这些机构的人也是说是,遇到脾气好的直接挂了电话,脾气差的铁定给你一顿劈头臭骂,不过这也是他们应得的。让我想起之前刷到的一个视频,说是苏州那些,看起了光鲜亮丽的写字楼,其实里面要么空置,要么是一些搞这种电话生意的,想想我们公司这片园区,其实也差不多。


说说生活。

年后自己给车打了蜡,擦的手都快断了,不过就当是锻炼了,效果还是真不错,之前车漆感觉很涩,现在感觉光滑了不少,而且上面的灰尘也很容易擦掉,洗车好洗了一点,价格也比去洗车店低了不少,关键洗车店还要排队就挺烦的。

南京这边商城的人还是一如既往的多,最近又开了一些自助火锅,去吃了一家,也是挺实惠的,现在虽然说是经济下行,其他地方可以省省钱,吃个饭对于大部分人还是挺轻松的,对于我来说也是少有的放松方式。

image.pngimage.pngimage.png

商场的人们

Nat小鸡+Tailscale+PVE,打造家里云平台

作者 黑羽
2026年2月9日 15:33

还是之前折腾 PVE,个人 NAS 的一些话题,‘家里云’这个词也是最近在b站看到的,指的是把服务部署在家里,就不用买阿里云的服务器了嘛,所以就叫‘家里云’;

Nat小鸡,是共享一个 IP 的服务器,会便宜不少,‘小鸡’就是有些人会用服务器在虚拟出子虚拟机出来,或者使用 Docker,LXD 之类的进程隔离技术,所以叫小鸡。

好的,名词解释到此结束。


在之前,我写过一个文章,关于使用 tailscale peer relay,很抱歉的是,在体验了一段时间后,发现这项技术在国内并不好用。

原因很简单,固定端口导致的运营商限制。UDP 本身面临着运营商很严重的 QOS(服务质量保证,人曰:限速),本身运营商也不会一刀切,毕竟使用 UDP 的服务很多,包括远程桌面,视频会议呀之类的,但是像是使用 Wireguard 这种很明显的 UDP 服务,并且是 C2C 的这种连接,没在白名单内,运营商识别到了还是会限速的。

前两天我用 Tailscale 的时候(没用 Peer Relay )发现,文件传输速度会从 3M/s->100k/s->3M/s,而后面的升幅我使用 tailscale status查看详情时, 发现连接端口变了,可能是运营商会对单个端口大流量进行管控,所以就是我前面得出 Peer Relay 在国内并不好用的原因。

关于国内如何突破运营商 UDP 限制我也参考了一些文章,大多都是使用例如 phantunudp2raw 这种方式,将 UDP 加入一些额外的头伪装成 TCP,当然这需要在装对应的客户端和服务端,进行对应配置,对于我这种有多个终端,并且使用很多端口的来说,是不太可能的事,也很明显违反了最初使用 Tailscale 的初衷;


然后又回到自建中转了,不知道什么时候看到了 Nat 小鸡这词,所以去搜了一下,发现各家的价格都不会很贵,虽然看起来都是一些野鸡厂商,但是对于我们这种只是转发数据来说,而且数据都是加密的,没什么不妥。一个月 15 左右就可以了,流量限制 500G ,如果流量不够用,也可以买多个,还能做个负载均衡。

具体厂家就不打广告了,随便搜都有,像是飞牛 NAS 这种提供的穿透服务,其实也差不多这个价格,自己买 Nat 小鸡部署穿透服务,你还获得个额外个 2c2g 的服务器,也可以顺便部署个 MC 游戏服玩玩。

对于 Tailscale,自己买个上面的 NAT 小鸡,部署个自建 Derper,各个终端下载个 Tailscale,对于 Linux 服务器,也是就一行命令的事。现在自建 Derper 也不要什么域名了,也不需要强制 80 端口,当然也就不需要什么备案了。
具体就下面几行代码就可以用docker部署一个derper:

services:  derper:    image: fredliang/derper    environment:      - DERP_DOMAIN=127.0.0.1 # replaced with your ip      - DERP_CERT_MODE=manual    network_mode: host    # 在tailscale后台添加配置 https://login.tailscale.com/admin/acls/file# "derpMap": {#   "OmitDefaultRegions": true,#   "Regions": {#     "900": {#       "RegionID":   900,#       "RegionCode": "custom",#       "RegionName": "custom",#       "Nodes": [#         {#           "Name":             "custom",#           "RegionID":         900,#           "HostName":         "your-ip",#           "CertName":         "your-can-find-this-content-in-derp-logs"#         },#       ],#     },#   },# },

具体见下面参考文件,我这就不赘述了。


这里就实现自己的内网互通了,实现真正的“家里云”平台;相对就我半个月的使用而言,也是非常的稳定的,也不会被运营商限制,毕竟我通过正经的机房中转的,运营商如果阻段机房的连接,可能会影响到真正的企业用户,引起投诉;现在也可以直接使用 immich 随时随地同步我手机的照片,观看家里的摄像头监控,不用通过萤石那满是广告的 App 。

Tailscale 毕竟只能自己使用,要是想把你的服务共享给朋友,也可以在小鸡上转发几个端口过去。我这里使用的是 rinetd 这个服务,直接 apt install rinetd 即可,然后修改 /etc/rinetd.conf, 例如我是下面的配置,把我的MC 服务器和 Immich 通过 NAT 小鸡穿透出去,就可以不需要 Tailscale 也能通过转发后的 IP 端口访问了。

0.0.0.0 29195 100.80.240.26 2283# 100.80.240.26 是tailscale的内网ip,因为小鸡也装了tailscale0.0.0.0 27310 100.64.158.94 191320.0.0.0 27310/udp 100.64.158.94 19132/udp

然后重启服务 service rinetd restart

ok,今天的分享就到这里,今天是除夕,提前祝大家新年快乐。


最近我的硬盘也坏了一块,SMART 提示有坏道,里面的数据我迁移到了另一块上面,最近硬盘很贵也舍不得换了,买了 8 年的 115 会员,合计 800,把不重要的数据都转移到了 115 上面,硬盘又空出来了很多空间;旧的硬盘我都带回了老家,想着可以用他们存一些可有可无的冷数据,反正有些坏道也不代表硬盘完全不可用。

然后给老家买了个小机柜,200 块钱也不是很贵,现在机柜里面还很空,只有录像机,交换机,光猫,还有一个小电脑,下次回家再给它美化下,加点新装备。

OK,后面再分享新的东西。

IMG_20260216_201926.jpgIMG_20260216_201934.jpg

参考文章:

AI 操作手机,试了下

作者 黑羽
2026年1月11日 20:53

折腾了一下午,主要是 Python 环境的问题,下面是演示视频。


项目地址

本来我是在自己电脑上部署的它那个 autoglm-phone-9b 模型,发现根本不行,上下文太小了,他们官网模型部署有免费额度,就用了他们的。

实话说,项目本身我觉得还是挺可以的,就是模型太差劲,让它给我刷多邻国,它也不会,让它给我打开小红书刷视频,直接给我打开抖音了,不过抖音刷视频然后点赞倒是可以的。

这种自动化操作在 AI 火起来之前就有,像是 autojs 之类的,也有一些程序通过 ADB 模拟点击实现自动化,可能那些黑产也是再熟悉不过了。

挺有意思的产品,后面模型进步下,能帮你挂机打游戏就好玩了,不过还是没有啥生产力用户,费用也是个大头,豆包手机那种,估计就是亏本找小白鼠。

使用Claude

作者 黑羽
2026年1月10日 19:14

Claude 安装官方文档已经很详细了,还有中文。

不过大陆用户是用不了它们的服务的,可以使用代理站,例如 Openroute或者其他。

Openroute 的文档里有说明,可以添加下面的环境变量来使用他们的代理。

# Set these in your shell (e.g., ~/.bashrc, ~/.zshrc)export ANTHROPIC_BASE_URL="https://openrouter.ai/api"export ANTHROPIC_AUTH_TOKEN="$OPENROUTER_API_KEY"export ANTHROPIC_API_KEY="" 

不过还漏掉一个,由于 Claude 启用初始化的时候要连接它们的服务器,我们在这一步就会被拒绝,无法进行下去,所以需要在用户目录添加 .claude.json 文件,如果已经有了就直接修改,添加如下参数:

{  "hasCompletedOnboarding": true}

意思是告诉Claude,我们已经完成了初始化,继续后面的。

Openroute 的模型也是不便宜,所以我用了字节火山的试试,首月9块钱。不过目前感觉质量不太行。

下面的项目就是用它修改的,我整了半天,连个 Dockerfile 都能搞错。

image.png

Claude 会帮你完成很多操作,包括执行构建命令,但是万一 AI 犯病了,可能把你当前电脑环境搞的一团糟,所有还有个方法是在 Docker 中运行,挂载当前项目到 Docker 中。

Github 中看到一个项目 claude-docker,不过这个只支持 Mac,还预装了一些 MCP,我感觉不需要,就删了,还增加了 Linux 支持,
项目地址在这

使用方法如下

# 1. 克隆项目git clone https://github.com/thetbw/claude-dockercd claude-docker# 2. 在项目下添加一个.env文件并编辑,比如上面的自定义代理,就可以加在这里# 使用代理的话,还需要在当前项目目录下添加一个上述的.claude.json来跳过初始化cp .env.example .envnano .env  # Add any optional configs# 3. 安装,这里的安装支持把脚本添加到你的环境变量sudo ./src/install.sh# 4. 在任何目录,输入claude-docker就可以执行了。cd ~/your-projectclaude-docker

image.png

最后,试了下 Mac 上的一个docker管理工具 OrbStack 还挺好用的,比如Docker Desktop 看着舒服多了。

2026-01-03补充:发现 docker 将要原生支持 ai agent 的沙盒访问,其实就是和我说的这个做同样的事情,那么后面这个其实就没啥用了。
原文参见 https://www.docker.com/blog/docker-sandboxes-a-new-approach-for-coding-agent-safety/


现在都说用 AI 来降本增效,但对于打工人来说,就是有少赚了钱又多干了很多活。就拿刚才的 AI 来说,要是用 Claude 官方的来说,一个月几百块钱了,我没注意具体多少钱,还没做什么事,公司也很少会帮你报销这些钱,但是有些公司是有内部的一些采购吧。

从上次说试试一些AI来看,我还没有深度使用什么,之前用过几次Roo Code,写一个小功能就要几块钱,一顿折腾今天饭钱就没了,而且真的一言难尽,自己还要有更多的心智负担,原不如熟练掌握一个技能有用,所以感觉,还是得自己多学习呀。

后面顺着这次火山模型的包月,再试一个月看看吧,这天我打字都感觉冻手...

年度计划日历

作者 黑羽
2026年1月7日 23:36

前几天说了 感觉github的活动墙很适合做日历,最近在拼多多找到了类似的,可以搜索年度计划日历找到,只要两块钱。

image.png

周末都被高亮标记出来了,每个方格还蛮大的,可以用笔标记重要的某天,做了某事,或者未来几天的计划,可以很清晰的看到距离。

写在新年开始

作者 黑羽
2026年1月3日 17:03

才知道南京原来有地方可以放烟花的,在江宁电影小镇这边,有几片很大的空地。

image.png

今年也是跟一位摄影认识的朋友一起跨年的,现在这边放了烟花,然后去新街口凑凑热闹。

DSC00254.jpgDSC00229.jpg
DSC00226.jpgDSC00185.jpg
DSC00099.jpgDSC00064.jpg

中间还看到个失火的,不过旁边都是枯草,烧了也没啥,烧到车子的话就严重了。
IMG_20251231_211721.jpg


跨年的新街口,也是第一次跨年来这地方,上年好像是不给的,有保安看着,今年明显就宽松了不少。

DSC00272.jpgDSC00280.jpg
DSC00305.jpgDSC00310.jpg

第二天一早,南京下了小雪,不过中午就化了,本来昨天就睡的就晚,不过看到下雪了还是一大早就出门了,紫金山还是有不少的雪的。

DSC00464.jpgDSC00560.jpg
DSC00575.jpgDSC00587.jpg
DSC00580.jpgDSC00609.jpg
DSC00649.jpgDSC00693.jpg

回到家也是很晚了,这两天也是上了高强度,后面缓了一天,今天才静下心写点东西,假期也是快结束了。

这两天南京商场里的人是真的多,想吃个饭都要拍半天的队,各家主流餐饮店门口都是人,也是外边大家都有空,外边又太冷,都挤在了商场里面。


照例来点上年总结与新年企划,写博客就是有这点好处,刚才翻了下我的博客列表,上年貌似没有写什么,不过看了下当时的内容,也能想象出当时的情况和在做什么。

传送门:

上年还是给自己列了各个目标的,想想都太宽泛了,比如英语,摄影,社交之类,没有具体的目标。

今年就没事追求了,因为感觉那些都是虚的,个人的想法变化的太快了,有些只是一时兴起。

今年就是

  • 存钱-到 10w 吧
  • 有新的工作然后趋于稳定
  • 去日本旅行吧,今年策划了一下,发现太晚了,今年早做点打算。

应该除了工作都还算好实现吧,工作就是很大的靠外部因素了,现在也是可以准备简历了。


还有一个特别想写的,就是一些长期在做的事情。就想上面说的,有些事情就是一时兴起,但有些却是一直在坚持的,有哪些是一直坚持的呢。

飞书日记

image.png

也算是有三年了,之前也有使用其他记录方式,比如teambition,有些自建文档,后面也是都放弃了。

immich

image.png

使用 immich 管理照片,也是用了两年了,为了支持他们,买了一个他们的会员,他们本身是免费的,会员只是会展示一个支持者标记。

FreshRSS

Rss 类软件也是层出不穷,包括之前关注的 diygod 开发的 folo ,感觉rss这种简单的就挺好,其实也不需要什么复杂的东西,这样才能保持长久。

现在我还是更关注一个产品能活多久,后续的迁移成本,而不是他理念多么先进,就是所谓的追稳吧,不过先进的产品还是得去了解的。

FreshRss 的 Github 主页 上有一些支持他们 Api 的第三方客户端,之前我一直使用的是 Fluent Reader,不过这个也是几年没更新了,在我更新新的 FreshRss 的时候,他们已经不支持拉取了。

如果你是使用 mac 和ios 的话,推荐使用 Readkit 这个软件,部分功能收费,免费的部分我目前感觉还行,安卓可以使用 capyreader , windows 目前还没看到什么好用又好看的客户端,只能使用网页端了。

image.png

Tailscale

tailscale 打通了我的整个网络,算是很低成本又方便入门的方式了,要是后面能有消息混淆之类的功能将会无敌,虽然有其他的开源工具,但是鉴于 tailscale 的使用门槛极低,以及越来越高的迁移成本,我还是会一直用下去的。


也有一些新开始用的产品,还没用到一些时日,后面时间久了可以再来谈谈我的看法。

然后是一些我之前折腾的,后面就放弃了。

  • cloudreve: 一个自建网盘,后面我整了自己的nas了,所以很久没用了
  • node-red:基于节点的低代码工具,现在ai时代,写个脚本其实也不那么难。也没那么多事情需要处理,也是放弃了。
  • partiner:用于管理docker的,现在基本使用docker compose, dokploy, 1panel等工具了
  • 最后是各种ai工具,现在ai变化太快了,他们有些慢慢跟不上了,就看谁能撑到最后了。

想写的太多,思绪太乱,今天的就到这里,后面随便找个电影看就可以睡觉了。

感觉github的活动墙很适合做日历

作者 黑羽
2025年12月30日 09:55

今天看到 gitea 的热力图的时候,突然觉得,一年挺短的,现在就到年底了,也突然感慨到,这么一张图表,尽然简单的把一年的工作都展示了。

image.png

从6月开始时因为我从那时候才搭建的。

再回到传统的日期,我觉得展示的内容太少了,你能看到最多内容的也就是当月视图了。

image.png

所以总感觉一年很长,这才一月,后面还有二月,一个月30来天呢。

但是对比下上面的热力图来看,一年其实只有短短的几十周,而对于打工人来说,只有周末的时间是完全属于你的,不算节假日和调休的话,还是在你有双休的情况下。

所以我觉得要是有个日历,是用类似 github 活动热力图这种方式展示,可以点击空白的地方创建日程,过去的日期都涂黑,每月在用不同的浅色颜色区分一下,周末再加重下展示,就好了。

因为你可以直观地看到一整年的进度,我觉得应会激发点个人的危机意识吧,看着这个图一点点的全涂黑,也就意味着今年在慢慢过去,你的年初 flag 还剩下多少呢。

image.png

用豆包简单生成了个示例,它没给我把月份什么加上,让它给我生成点复杂的配色也是不行了,ai 还是没那么可靠。


感觉我的这个想法和之前看到的人生进度条有点类似,都是想直观的展示时间的流逝,谷歌随便搜索了两个,类似下面这样。

image.png

https://www.gwlin.com/generated/life-progress/

image.png

https://www.flyneko.com/tools/life-progress

写在新年之前

作者 黑羽
2025年12月27日 22:56

这几年一直都有记录的习惯,一直都是在飞书上面,不过记录多是偏向一些负面的东西,心情不好的时候会写写,可能心情好玩的开心的时候,也不想去写这些东西了吧。
image.png

飞书还是我这几年用的最舒服的知识库,就是加载慢了一点,但是功能是真的多,对于个人还是免费。

关于这种应用,我最关系的还是它变卦了怎么办,毕竟我的数据都在上面,虽然不像企业那样有价值。今年就是经历了腾讯的coding宣布放弃的事件,公司也是重新迁移了代码库,耽误了一点时间。好在飞书这里提供了api,我整了个定时任务,每天备份我的飞书文档成pdf到本地,就算哪天飞书不在了,未来的某一天我还是可以看到这几年我的个人记录。

就像上面我的飞书概览一样,每年我都是会更新下概览,不过这两天我觉得没什么大区别。

学业方面算是我的今年最大的缺陷,家里条件也就那样,支撑不了自己脱产学习,下班的话真的没有那个精力,只想刷视频了。

博客是我今天觉得最好的,更新了不少,感觉自己越来越能写了,至于有多少人看我觉得并不是重要的。

摄影上半年其实没啥进展,下半年拍了很多,尤其是最近一两个,也是给我整自信了。


我觉得这两年没有看到什么成长,更多的还是心境上面,也是我最近感受到的,我想用一句话总结它,叫“润物细无声”。怎么说,就是有时候自己所做的事情当时感觉没什么,但是时间长了,回头看一下,就会觉得真的不一样,怎么变化会这么大。

这里表现在很多方面,之前没能看懂的技术,突然某一天感觉茅塞顿开,自己也不知道怎么就理解了;之前自己觉得很尴尬或者很紧张的事情,也是不知道哪一时刻就觉得无所谓了;还有自己在投资理财方面,肯定是亏了钱,但是自己突然没有想以前那么对陌生的事物感觉这么害怕了,但是还是因该保持该有的敬畏之心。


这两年也是每年都会感觉到行情不好,年初的时候我换了个租房的地方,大概一季度能省2000块钱吧,环境和之前没什么差别,甚至还好了点。

自己也是终于攒了一点钱,还被各个朋友分别借走了一点,下年就是把这些钱拿回来,也不打算借给别人了,这时候觉得自己人际关系窄也并不是什么坏事,以后的这种借钱的可能还会有吧,而且自己也不打算维护什么关系了。

我也是来现在这个公司几年了,上年之前,每年年底领导都会有个和各个员工的单独谈话,上年是没有的,今年估计也是没有了,本来就是在上年的年底,我就想说所谓涨薪的事情,但是谈话没了,我也不知道在哪说了,干脆就不说了。今年也是吧,反正下年我打算找新工作了,涨不涨薪也无所谓了,找不到更好的新工作的话就是我没本事,我也没什么好说的。

上了几年班,也是麻木了。


今年下半年也是内存硬盘狂涨,好在上半年,我就配了个32g内存的电脑,也买了两块14t硬盘用作nas上,下年应该暂时是够用了,只要我不乱搞那些没意义的折腾的话。

今年的游戏没怎么玩,好可惜显卡没有涨价,不然我可能会把我的显卡买了,换成intel的专业卡,因为我想玩虚拟化就缺那玩意。

今年也是填了一些其他设备,第一次用mac,感觉还行吧,第一次用4k显示器,感觉windows在4k下也是挺细腻呀。之前旧的电脑放公司了,公司都是我自己的设备,自费上班了属于是。

相机也是填了个还行的镜头,但是出门拍照真的不好拿,现在有馋那种卡片机了,至于为啥不用手机,我是用手机的,但就想要那一点点的仪式感,还有一点点的画质提升和真实性。


展望一下下年吧,虽然国内过年都是农历年,但是我觉得阳历年也挺好,起码他很准时,工作节奏其实都是按照阳历年来的。

之前下年多攒写钱吧,如果可以的话,来点桃花运也不是不行,但是我的抠门样,肯定不要坑我的钱就好。

今年的b站年度总结,我发现我视频看的太多了,每天上班刷论坛的时间也很多,下年希望能减少一点吧,自己的自制力真的不行。

今天喝了点啤酒写这个博文,因为今天不写完我肯定又要拖了,往后拖的感觉真不好也,也不知道怎么搞,希望下年可以更专注吧,能抛弃todo list这种玩意更好,这玩意也是挺让人焦虑的,但是没有又不行,会更焦虑。

之前写文章都是检查下错别字,这次就不检查了,如有错别字,就自行脑补吧。

通过SSH远程执行命令,以及多服务器管理

作者 黑羽
2025年12月22日 16:48

SSH 除了可以直接连接远程服务器,以终端形式显示外,也可以直接用来执行命令,只需要把命令放到 ssh 的参数后面即可,例如:

ssh root@example "echo Hello"

这对于执行一些临时命令,例如运行备份之类的,还挺有用。

对于很长的或者多个命令,可以使用 << 多行文本,例如

  ssh root@example << HERE    echo "HELLO"    echo $USER  HERE

但是对于上面的第二个命令,$USER,存在转义问题,$USER 会在本地执行,而不是在服务器上。

解决方式是可以添加引号来避免本地提前解释执行。

  ssh root@example << 'HERE'    echo "HELLO"    echo $USER  HERE

此处是来自 POSIX 的规范,linux 和 mac 同样适用,具体出处我也不清楚,可以参考文章底部最后一个链接,和自行 ai 搜索


还有一种解决方法,也是我想说的,来自今天搜索 rpcssh,发现的一个十多年的帖子。

declare 命令可以返回变量或者方法的定义,使用这种方法,就可以自己不用写相关转义了。例如本地有个下面的方法:

hello() {    echo "Hello, world, I'm coming from $(uname -n)."}

可以这样执行

ssh root@example"$(declare -f hello); hello"

$(declare -f hello) 会自动把本地定义的这个hello方法的内容获取出来,并且发送到目标服务器时自动转义,然后在远程服务器执行这个hello方法。

注意这种方法只能用于 shell 中定义的方法,不能是二进制文件

原作者写了一个叫rpcsh的方法,用于把本地方法发送到远程,挺简洁方便的,我就直接放在下面了

# rpcsh -- Runs a function on a remote host# This function pushes out a given set of variables and functions to# another host via ssh, then runs a given function with optional arguments.# Usage:#   rpcsh -h remote_host [ -p ssh-port ] -u remote_login -v "variable list" \#     -f "function list" -m mainfunc## The "function list" is a list of shell functions to push to the remote host# (including the main function to execute, and any functions that it calls)# Use the "variable list" to send a group of variables to the remote host.# Finally "mainfunc" is the name of the function (from "function list") # to execute on the remote side.  Any additional parameters specified gets# passed along to mainfunc.rpcsh() {    if ! args=("$(getopt -l "rmthost:,rmthostport:,rmtlogin:,pushvars:,pushfuncs:,rmtmain:" -o "h:p:u:v:f:m:A" -- "$@")")    then        exit 1    fi    sshvars=( -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null )    eval set -- "${args[@]}"    while [ -n "$1" ]    do        case $1 in            -h|--rmthost) rmthost=$2; shift; shift;;            -p|--rmtport) sshvars=( "${sshvars[@]}" -p $2 ); shift; shift;;            -u|--rmtlogin) rmtlogin=$2; shift; shift;;            -v|--pushvars) pushvars=$2; shift; shift;;            -f|--pushfuncs) pushfuncs=$2; shift; shift;;            -m|--rmtmain) rmtmain=$2; shift; shift;;            -A) sshvars=( "${sshvars[@]}" -A ); shift;;            -i) sshvars=( "${sshvars[@]}" -i $2 ); shift; shift;;            --) shift; break;;        esac    done    rmtargs=( "$@" )    ssh ${sshvars[@]} ${rmtlogin}@${rmthost} "        $(declare -p rmtargs 2>/dev/null)        $([ -n "$pushvars" ] && declare -p $pushvars 2>/dev/null)        $(declare -f $pushfuncs 2>/dev/null)        $rmtmain \"\${rmtargs[@]}\"    "}

原地址 https://gist.github.com/derekp7/9978986


有什么用呢,如果你有多个服务器,需要维护什么备份脚本什么,按照传统的方法,比如把脚本上传到服务器,或者git,当你的脚本更新时候,所有的服务器上的文件都需要更新,你需要一个个登录过去获取最新文件,这显然不是很方便。

当然你可以使用类似宝塔面板这种,可以在一个地方控制好几个服务器,这显然又增加了复杂性,增加了依赖。

还有就是一个干净服务器,通常需要装一些东西,进行初始化,例如我想安装dockerfail2ban,然后配置相关参数,每次都要这么设置都挺费事的,就可以用上面的方法。

例如下面的脚本

#!/bin/bashset -euo pipefail# 定义远程服务器列表(可根据实际需求修改)REMOTE_SERVERS=("192.168.2.1" "192.168.2.2")# 默认SSH端口DEFAULT_SSH_PORT=22# 默认SSH登录用户DEFAULT_SSH_USER="root"# ===================== 核心远程执行函数 =====================rpcsh() {    local rmthost="" rmtport="${DEFAULT_SSH_PORT}" rmtlogin="${DEFAULT_SSH_USER}"    local pushvars="" pushfuncs="" rmtmain="" sshvars=() rmtargs=()    local use_ssh_agent=0 ssh_identity=""    # 解析命令行参数    if ! args=$(getopt -o h:p:u:v:f:m:Ai: --long rmthost:,rmthostport:,rmtlogin:,pushvars:,pushfuncs:,rmtmain: -- "$@"); then        echo "ERROR: 参数解析失败" >&2        return 1    fi    eval set -- "${args}"    sshvars=( -q -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ConnectTimeout=10 )    while [[ $# -gt 0 ]]; do        case "$1" in            -h|--rmthost)                rmthost="$2"                shift 2                ;;            -p|--rmtport)                rmtport="$2"                sshvars+=( "-p" "$2" )                shift 2                ;;            -u|--rmtlogin)                rmtlogin="$2"                shift 2                ;;            -v|--pushvars)                pushvars="$2"                shift 2                ;;            -f|--pushfuncs)                pushfuncs="$2"                shift 2                ;;            -m|--rmtmain)                rmtmain="$2"                shift 2                ;;            -A)                use_ssh_agent=1                sshvars+=( "-A" )                shift                ;;            -i)                ssh_identity="$2"                sshvars+=( "-i" "$2" )                shift 2                ;;            --)                shift                rmtargs=( "$@" )                break                ;;            *)                echo "ERROR: 未知参数 $1" >&2                return 1                ;;        esac    done    # 校验必要参数    if [[ -z "${rmthost}" || -z "${rmtmain}" ]]; then        echo "ERROR: 必须指定远程主机(--rmthost/-h)和执行函数(--rmtmain/-m)" >&2        return 1    fi    # 构建远程执行脚本    local remote_script=""    # 传递参数数组    remote_script+="$(declare -p rmtargs 2>/dev/null); "    # 传递指定变量    if [[ -n "${pushvars}" ]]; then        remote_script+="$(declare -p ${pushvars} 2>/dev/null); "    fi    # 传递指定函数    if [[ -n "${pushfuncs}" ]]; then        remote_script+="$(declare -f ${pushfuncs} 2>/dev/null); "    fi    # 执行主函数    remote_script+="${rmtmain} \"\${rmtargs[@]}\""    # 执行SSH远程命令    echo "INFO: 连接到 ${rmtlogin}@${rmthost}:${rmtport} 执行 ${rmtmain}"    ssh "${sshvars[@]}" "${rmtlogin}@${rmthost}" "${remote_script}"}# ===================== 业务功能函数 =====================# 安装Docker(含Docker Compose)install_docker() {    echo "===== 开始安装Docker ====="    if command -v docker &>/dev/null; then        echo "INFO: Docker已安装,跳过安装步骤"        return 0    fi    # 卸载旧版本    apt-get remove -y docker docker-engine docker.io containerd runc || true    # 设置仓库    apt-get update    apt-get install -y ca-certificates curl gnupg lsb-release    mkdir -p /etc/apt/trusted.gpg.d    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg    echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list >/dev/null    # 安装Docker Engine    apt-get update    apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin    # 启动并开机自启    systemctl enable --now docker    # 验证安装    if docker --version &>/dev/null; then        echo "SUCCESS: Docker安装完成"    else        echo "ERROR: Docker安装失败" >&2        return 1    fi}# 安装fail2baninstall_fail2ban() {    echo "===== 开始安装fail2ban ====="    if command -v fail2ban-server &>/dev/null; then        echo "INFO: fail2ban已安装,跳过安装步骤"        return 0    fi    apt-get update    apt-get install -y fail2ban    systemctl enable --now fail2ban    if fail2ban-server --version &>/dev/null; then        echo "SUCCESS: fail2ban安装完成"    else        echo "ERROR: fail2ban安装失败" >&2        return 1    fi}# 配置fail2ban(SSH防护为例)config_fail2ban() {    echo "===== 开始配置fail2ban ====="    local config_file="/etc/fail2ban/jail.local"    # 备份原有配置    if [[ -f "${config_file}" ]]; then        cp "${config_file}" "${config_file}.bak.$(date +%Y%m%d%H%M%S)"    fi    # 写入基础配置    cat > "${config_file}" << EOF[DEFAULT]ignoreip = 127.0.0.1/8 192.168.0.0/16bantime  = 86400findtime = 3600maxretry = 5banaction = iptables-multiportbackend = systemd[sshd]enabled = trueport = sshfilter = sshdlogpath = /var/log/auth.logmaxretry = 3EOF    # 重启服务    systemctl restart fail2ban    if systemctl is-active --quiet fail2ban; then        echo "SUCCESS: fail2ban配置完成并已重启"    else        echo "ERROR: fail2ban配置后启动失败" >&2        return 1    fi}# 服务器备份(示例:备份/etc目录到/backup)server_backup() {    echo "===== 开始服务器备份 ====="    local backup_dir="/backup/$(date +%Y%m%d)"    local backup_file="${backup_dir}/etc_backup.tar.gz"    # 创建备份目录    mkdir -p "${backup_dir}"    # 备份/etc目录    tar -zcf "${backup_file}" /etc \        --exclude=/etc/ssh/ssh_host_* \        --exclude=/etc/machine-id \        --exclude=/etc/resolv.conf    # 校验备份文件    if [[ -f "${backup_file}" && $(stat -c%s "${backup_file}") -gt 1024 ]]; then        echo "SUCCESS: 备份完成,文件路径: ${backup_file}"        # 可选:保留最近7天备份        find /backup -type d -mtime +7 -delete    else        echo "ERROR: 备份失败" >&2        return 1    fi}# ===================== 脚本入口/帮助信息 =====================# 显示帮助信息show_help() {    cat << EOF服务器管理脚本 - server_manager用法:  $0 [选项] <命令> [命令参数]可用命令:  install_docker      安装Docker及Docker Compose  install_fail2ban    安装fail2ban  config_fail2ban     配置fail2ban(SSH防护)  server_backup       执行服务器备份(默认备份/etc目录)  help                显示此帮助信息远程执行选项:  -r                  远程执行模式(需配合以下参数)  -h <IP>             远程服务器IP(必填)  -p <端口>           远程SSH端口(默认:22)  -u <用户>           远程SSH登录用户(默认:root)  -i <密钥文件>       SSH私钥文件路径  -A                  启用SSH Agent转发示例:  # 本地执行备份  $0 server_backup  # 远程执行备份(单个服务器)  $0 -r -h 192.168.2.1 server_backup  # 远程执行Docker安装(指定用户和端口)  $0 -r -h 192.168.2.2 -u ubuntu -p 2222 install_docker  # 显示帮助  $0 helpEOF}# 主执行逻辑main() {    local remote_mode=0    local remote_host="" remote_port="${DEFAULT_SSH_PORT}" remote_user="${DEFAULT_SSH_USER}"    local ssh_identity="" use_agent=0    local command="" command_args=()    # 解析顶层参数    while [[ $# -gt 0 ]]; do        case "$1" in            -r)                remote_mode=1                shift                ;;            -h)                remote_host="$2"                shift 2                ;;            -p)                remote_port="$2"                shift 2                ;;            -u)                remote_user="$2"                shift 2                ;;            -i)                ssh_identity="$2"                shift 2                ;;            -A)                use_agent=1                shift                ;;            help|--help|-h)                show_help                exit 0                ;;            *)                # 第一个非选项参数作为命令,剩余作为命令参数                command="$1"                shift                command_args=( "$@" )                break                ;;        esac    done    # 校验命令是否为空    if [[ -z "${command}" ]]; then        echo "ERROR: 必须指定要执行的命令" >&2        show_help        exit 1    fi    # 校验命令是否存在    if ! declare -f "${command}" &>/dev/null; then        echo "ERROR: 未知命令 '${command}'" >&2        show_help        exit 1    fi    # 执行逻辑:本地/远程    if [[ ${remote_mode} -eq 1 ]]; then        # 远程执行模式        if [[ -z "${remote_host}" ]]; then            echo "ERROR: 远程执行模式必须指定 -h <远程服务器IP>" >&2            exit 1        fi        # 构建rpcsh参数        local rpcsh_args=(            -h "${remote_host}"            -p "${remote_port}"            -u "${remote_user}"            -m "${command}"            -f "${command}"  # 传递要执行的函数到远程        )        [[ -n "${ssh_identity}" ]] && rpcsh_args+=( -i "${ssh_identity}" )        [[ ${use_agent} -eq 1 ]] && rpcsh_args+=( -A )        rpcsh_args+=( -- "${command_args[@]}" )        # 执行远程调用        rpcsh "${rpcsh_args[@]}"    else        # 本地执行模式        echo "INFO: 本地执行命令 ${command}"        "${command}" "${command_args[@]}"    fi}# 启动主程序main "$@"

之后就可以按照下面方法使用

# 显示帮助(可用命令)server_manager help# 本地执行备份server_manager server_backup# 远程执行备份server_manager -r -h 192.168.2.1 server_backup# 远程执行Docker安装(指定用户、端口、SSH密钥)server_manager -r -h 192.168.2.2 install_docker# 远程配置fail2ban(启用SSH Agent转发)server_manager -r -h 192.168.2.1 config_fail2ban

注意的是上面脚本我是ai生成的,我只是提供了思路,请不要直接使用。

服务器可以自己本地维护一个列表,输入-r时给出列表选择哪个服务器执行,就可以一个脚本管理多个服务器了。

像是后面脚本新增功能,哪里代码有问题,都只需要改本地的。也可以本地用定时任务定时执行,不需要每个服务器都部署定时任务。

虽然性能上肯定有损耗,但是好在没有任何第三方依赖,所有linux mac都能用,啥第三方软件也不用装(当然还是要openssh)


参考文章

KASM,还不错的云桌面产品

作者 黑羽
2025年12月4日 20:34

最近在这个叫 KASM 的玩意,这个很久之前就见过,感觉很复杂就没弄,最近短暂的有空,搭建了一个玩玩。

KASM 和核心功能来自一个叫 KasmVNC 的产品,这个是开源的,虽然名字里有 VNC 这两个字,但是他并不是传统的 VNC ,它比VNC有着更好的性能,支持GPU加速,直接剪贴板,文件上传下载等。

接触这个是因为之前了解的另一个项目,docker-baseimage-gui,一个预装了虚拟屏幕和 noVNC 的 docker 镜像,有一个应用是 stardew-multiplayer-docker,在docker上运行的星露谷物语,可以在浏览器上通过 VNC 控制。

想想还挺有意思,如果把程序都隔离到 docker 里面,通过浏览器来访问,这样既不用限定设备,局域网随意访问,内网穿透之后远程也可以访问了,岂不挺好,github 上也有了一些例如 docker-wechat 的项目,就是通过 docker 运行微信 linux 版,然后用 noVNC 在网页上显示,也可以实现微信多开了。

KASM 就是这样一个项目,他打包了很多常用的镜像(可惜并不是国内常用),然后有个管理平台,可以在用户需要的时候创建对应的 docker 容器,通过 web 界面访问;我想要折腾这种东西的一个出发点,就是有时候手上做到一半的东西,可以直接放在那,后面我换个电脑,可以接着继续开始,不用各种同步。


image.png
image.png

配合 PWA 还挺有模有样的。

image.png

还有支持游戏手柄,打印机,摄像头等穿透,就可以看出来和传统VNC的不同之处了。

image.png

后台就是基本的那一套功能了,添加自定义镜像源,管理用户可以访问的镜像,限制资源,让 docker 扩容等。不过要说的是,他这个后台并不是开源的,只是镜像本省是开源的,不过功能也是免费的就是了。

感觉也可以搞个云游戏之类的东西,把自己的电脑分享给朋友一起玩,之前b站上有过类似的视频,叫什么多人一机之类。

这些功能都是基于 web 的,其实不需要安装 kasm 这个后台也可以单独用docker 运行的,例如 https://hub.docker.com/u/kasmweb 仓库下,有很多 kasm 使用的镜像,里面有运行说明,也可以基于这些镜像定制,比如安装自己的软件之类的。

安装的话直接官网的教程就好了
https://docs.kasm.com/docs/install/single_server_install

不过要说的几点:

  • 最好单独一个机子上安装,不要有其他东西,因为安装过程中会安装一些docker 插件,还会重启 docker,避免发生一些兼容问题。
  • 最好单独从 docker 官网的源安装 docker,虽然安装脚本会自动安装docker,但是是从包管理器的,有些发行的包管理器内的 docker 太老了。
  • 可以在 wsl 上装,但是不支持 windows 的那个 docker desktop,需要wsl 内单独安装 docker
  • nvidia 的 gpu 加速会很麻烦

最后折腾这个的一个感想,就是 nvidia 的 gpu 真的是麻烦,想搞 linux 玩,还是乖乖用 amd 或者 intel 的显卡吧,用 nvidia 就是浪费生命。windows 也真是牛逼,搞了个 GPU-PV 的东西,可以不需要 GPU 虚拟化就把显卡给 WSL 用,不过也是扣扣搜搜,东西只给你一半,折腾起来也是费事,看到github 上有过一个项目 Linux-GPU-V-Scripts-for-Hyper-V,把 WSL 内的驱动复制到 Hyper-v 中,实现虚拟机内使用显卡,其实 WSL 也是一个特殊的虚拟机罢了。

最近有看了 intel 最近新出的显卡,intel b50,虽然性能不是很强,但是它的定位是专业卡,所有显卡特性基本上全支持了,而且支持虚拟化,不需要折腾任何东西,较新的 linux 内核直接内置驱动了,后面想搞一个玩玩,不过京东这 3k 块钱的售价,还是等什么时候便宜一下吧。

最后一直在折腾这种乱七八糟的小玩意,后面很长一段时间应该不会去折腾了,这是最近最后一个折腾分享了吧,希望是,还是得专注于有价值的事情上面(专心搞钱)。

其他:摄影项目因为服务器过期不打算续了,只是当初不知道它跑在那个服务器上,数据我都是定时备份的,后面找个时间迁移一下。

docker修改存储位置,以及遇到的问题

作者 黑羽
2025年11月26日 15:20

默认docker存储都是放在 /var/lib/docker 这个目录下面,随着使用时间越来越大。你可以使用 docker image prune 来清理不用的镜像。

做为一个 NAS 玩家,我打算直接把docker文件夹存到 NAS 的存储上。

使用 SMB 挂载目录的话可能会遇到一些问题,因为 SMB 不支持所有的 POSIX 标准,所以我是直接挂载了一个远程硬盘到虚拟机中。系统盘保持足够的小,方便后面备份和快照,而这个数据盘就不需要快照和备份了。


更改 docker 存储目录也是很简单。

首先关闭docker

service docker stop

更改docker配置

nano /etc/docker/daemon.json

添加data-root配置,类似下面这样,我是放到 /mnt/docker_data 目录

{  "data-root": "/mnt/docker_data"}

移动原来的docker数据

rsync -aHAX /var/lib/docker/ /mnt/docker_data/docker/# 或使用下面的直接移动:# mv /var/lib/docker /mnt/docker_data/

然后重启docker

service docker start

然后这样基本上就完了


如果只是这样也没什么好写的了,说下我遇到的其他问题。

之前我是一直用上面的方法,最近用这个方法的时候,用 KASM 拉取镜像的时候,还是提示空间不足,而且df -h显示的空间也不对,类似下面这样

KASM 感觉还蛮有意思的,有兴趣的可以去搜搜,是一个云桌面平台,后面我摸熟了可能会写个文章介绍一下。

FilesystemSizeUsedAvailUse%Mounted on
udev3.9G03.9G0%/dev
tmpfs795M2.2M792M1%/run
/dev/sda118G13G4.1G76%/
tmpfs3.9G03.9G0%/dev/shm
tmpfs5.0M05.0M0%/run/lock
/dev/sda15124M12M113M10%/boot/efi
/dev/sdb1492G3.3G463G1%/mnt/docker_data
overlay18G13G4.1G76%/mnt/docker_data/rootfs/overlayfs/xxx
tmpfs88M088M0%/run/user/0

明明 /mnt/docker_data 显示有 492G 空间,为什么 dockeroverlayfs 显示和系统根分区一样是 18G 呢。

后面也是问了 AI 才知道,原来 docker 自身的存储和依赖的 containerd 可能是分开的。我看了下,的确系统有个 containerd 服务。

按照docker的指引,修改 containerd 存储路径。

nano /etc/containerd/config.toml
root = "/mnt/docker_data/containerd"state = "/run/containerd"
systemctl restart docker

结尾,如果有遇到相同问题的,恰好搜到我这个文章,可以作为一个思路。如果从零开始,刚好看到我这个文章,不建议照抄命令,还是说只能作为思路的参考。

另外上面的大多数命令,例如 rsync -aHAX ,发送给 AI ,他都可以给你解释每一个参数的用处,建议在网上看到的教程,都了解每个参数的含义了,再进行操作。

docker的不同版本,每个发行版,都是不一样的,例如 docker compose,有些发行版上是用 docker compose 命令,有些是 docker-compose,另外发行版自身的包管理器和官方的源,都是有不一样的,建议判断下具体的环境,还有时间,综合做出决定。

【摘抄】天堂与地狱

作者 黑羽
2025年11月25日 14:38

某人死后,灵魂来到一个地方,当他进门的时候,守门人对他说:“你不是贪吃吗?这里有的是东西随你吃。你不是贪睡吗?这里睡多久也没人打扰。你不是爱玩吗?这里有各种娱乐由你选择。你不是讨厌工作,不喜欢受拘束吗?这里保证没有事做,更没人管你。”

于是此人高高兴兴留了下来。吃完就睡,睡够就玩,边吃边玩。但是三个月下来,他渐渐觉得有点不是滋味,于是跑去见守门人:“这种日子过几天倒还不错,但是时间长了,不见得好。因为玩的太多,我对娱乐已经提不起什么兴趣;吃得太饱,是我身体不断发胖;睡得太久,头脑又变得迟钝,您能不能给我一点儿工作,早晨催我起床啊!” 守门人摇摇头说:“对不起!这里没有工作,跟没人催你早起。”

又过了三个月,这人实在太难受了,于是他又跑到守门人面前哭诉:“这种日子我实在是受不了了,如果你再不给我工作,我宁愿下地狱。”

“你以为这是天堂吗?这里本来就是地狱啊!” 守门人大笑道,“他使你没有理想,没有创造,没有前途,逐渐腐化。这种心灵得煎熬,要比上刀山下油锅的皮肉之苦,更令人无法忍受啊!”

来自 刘墉 《人生海海,自在独行》

traefik.me

作者 黑羽
2025年11月15日 22:19

traefik.me 是一种通配符域名,可以把类似 xxx.traefik.me 这种域名解析到 xxx 这个 ip 上面。

例如可以使用 192-168-1-1.traefik.me 访问你局域网的 192.168.31.1 这个 ip 地址,为什么要这么脱裤子放屁呢,主要因为直接使用 ip 地址对于浏览器有一系列限制,而且通常你还要加上端口。

对于 traefik.me ,你还可以在前面加上一定区分 ,例如 app1-192-168-1-1.traefik.me,还是解析到 192.168.31.1 这个机器,但是你可以在nginx中对 app1 app2 来反向代理到不同的服务,由于是域名,你还可以使用 acme 实现自动 SSL。

如果你使用 tailscale 就更方便了,本身 tailscale 可以组网,直接用组网的ip,这样你的内网服务就可以在内部各处访问了,不需要折腾太多公网的东西。

例如我的,使用dokploy这个自动部署工具

image.png

service-nekotemplatespringadmindev-cnjau7-cb6638-100-118-61-63.traefik.me 是我写的一个测试服务的地址,这个是 dokploy 随机生成的,前面的一堆都可以不用看,我部署了很多程序,前面的是用来区分是哪个程序的,100-118-61-63 这个是我机器的内网地址,当访问上面的网址时候,dokploy会使用 treafik 路由,反向代理到我部署的 docker 程序,就像下面这样。

http:  routers:    service-nekotemplatespringadmindev-cnjau7-router-26:      rule: Host(`service-nekotemplatespringadmindev-cnjau7-cb6638-100-118-61-63.traefik.me`)      service: service-nekotemplatespringadmindev-cnjau7-service-26      middlewares: []      entryPoints:        - web  services:    service-nekotemplatespringadmindev-cnjau7-service-26:      loadBalancer:        servers:          - url: http://service-nekotemplatespringadmindev-cnjau7:8080        passHostHeader: true

这个使用 nginx 也可以可以实现的。

这样我就可以在任意位置,只要加入了 tailscale 组网,就可以使用上面域名访问我的服务,也不用输入端口,也可以使用 https,因为现在浏览器很多新增功能,为了安全也是要 https 才能支持。

通常支持这个功能的还有 nip.ioxip.io 这种。

补牙-以及最近三两事

作者 黑羽
2025年11月10日 11:46

之前就发现自己门牙上旁边有个黑窟窿,可能是蛀牙什么的,上周有时间,就去最近的一个医院看了一下。看了之后发现蛀牙还真不少,总共6个小的,3个大的,反倒是门牙那个很显眼的,医生说不是蛀牙,是什么牙结石之类,后面做了牙周处理。

几个小的蛀牙用树脂填的,大的医生说最好用嵌体,总共花费了7-8千块钱,医保瞬间给我清空了属于是,自己还自费了6千。本来还觉得我的决定还是挺冲动的,没有货比三家去别的医院也看看,或许医生坑我呢,也许有更好的方案,现在觉得也没必要怀疑医生吧。
也是切身体会了电影《Eden》里的那个男主,为什么决定离开城市时候把自己牙全拔光了,牙医真的是现代社会不可或缺的职业呀。

补牙之前需要把蛀牙用那个类似小电钻的东西打磨掉,照片就不放了,看的吓人呢,几个小的牙齿没什么感觉,就是能闻到烧糊的味道,3个严重点的就感觉到一整酸痛的感觉,后面的医生拍的照片可以看出来,牙齿中间被打磨出了一个很深的十字形的坑,全程也是张了很久的嘴,感觉都快脱臼了。医生用3d扫描的设备,给牙齿做了个建模,这3个要做嵌体的,还要等厂家根据3D扫描生产出来才能做,医生说要等下周了,现在先临时用一个绿色的啥玩意填充了一下。

不过说到这个临时填充的,今天早上刷牙我尽然给它刷出来了,不知道吃饭东西会不会卡进这个牙坑里面,后来又用手塞了进去,感觉最近不能吃太刺激的了,毕竟那个坑也是离神经不远了。

补完牙之后又是做的牙周,医生给打了一点镇静计,我也是全程没怎么看,只是感觉医生那什么塞进我的一个个牙缝,还有偶尔的酸痛,还有一阵阵烧糊的味道。结束之后看到我嘴里都是血,漱了漱口口后医生给上了个药,拿了漱口水,也是结束了。

还是没想到自己蛀牙这么多,以为自己还是很年轻之类的,就一堆问题,可能是之前快乐水喝多了,医院也能听到一些10来岁的小孩,在那哭的特别大声,看来这玩意跟年纪也没什么关系,之后人生又要少了一个乐趣了。

之前也是刷到过一些做根管的视频,看着就很吓人,推荐各位没看过的去搜下看下,健全的还是要注意防护呀,看完就会瞬间有了敬畏之心了。医生也是一直有推荐用什么牙线清理,这玩意我到现在还是一次没用过,之前倒是有尝试,总感觉塞不到牙齿里面,后面还是要尝试一下。


本来想着每周写一点什么的,这个打算是昨天写的,然后还约了个模特练习下拍照,也是因为是下雨,然后就是昨天早上睡了很久,大概是前天22:30就睡了,到第二天8:30,还是没睡醒,下午头昏脑涨,有点发烧的样子,中午的饭吃了两口也是吃不下了,有点想吐的。

没办法也是没出门,在家看了KT:T1的比赛,恭喜Faker的六冠王,这次比赛虽然国内没什么热度,赛事结束也是有点敷衍,但是我觉得5把都打满,也是蛮精彩的,尤其是第一把,不过后面KT感觉就一直不在状态,不知是不是因为英雄池太浅,反倒是T1越战越勇。

讲讲java的内存分析

作者 黑羽
2025年11月6日 15:09

JDK 自带了一些工具,用于监控 JVM 的运行情况,例如 jconsolejvisualvm,也有一些第三方的工具,例如 matyourkit java profiler,这里推荐 mat,非常的好用,yourkit 的则是界面更现代化一点,功能也更多,是收费的,多出来的功能其实也没什么用,因为本身也是依靠 JVM 提供的接口。

关于 JVM 的内存模型网上有很多分享了,感觉大多都是复制粘贴,这里也不想说这个,我觉得所谓的 'JVM调优',本身就是个伪命题,JVM 不需要调优,默认运行就好,更多的还是代码层面的问题,瞎折腾不如不折腾,有那工夫可以多审查下代码,或者升级 JVM ,能带来的提升都远比所谓的 '调优' 大。


今天主要想讨论的问题是,当 JVM 内存占用过高,或者出现 OOM 的时候,如何去分析。

首先当前就是转储内存了,可以设置参数 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/xxx/heapdump.hprof 在内存溢出时转储堆栈,同时利用上述软件进行分析,或者 JVM 进程还没有退出的时候,用 jmap 等工具进行转储堆栈。

这里我想到的排查思路就是,看内存占用的分布,哪些代码占用了高内存,然后再去检查对应的代码。

这里拿 mat 来说,这里我们主要使用 dominator tree 这个工具,至于为什么接下来会说。

image.png

这里我们可以清楚的看到 MyBatis 相关代码占用了非常大的内存,应该是在启动的时候解析 XML,并且缓存相应的结果,通过展开树结果,可以一层层往下进行分析。

这里说下界面上的 shalldow heapretained heap,也是我主要想分享这篇文章的原因。

shalldow heap 是指只计算对象本身直接占用的内存,例如一个 int ,一个boolean,对于引用的对象,我们都知道对象其实就是一个指针,那就只占用一个指针的大小,通常是一个int;但是在 Java 中,String 也是一个对象,也就是说,无论一个对象包含了一个多么大的 String,反应到对象的统计里,也就是一个指针的大小,这个不是我想要的结果。

反应到工具里,像是 jvisualvm 这种工具只能查看 shalldow heap 这种内存,会根据对象类型,展示对象数量和占用内存,像是下面这样。

image.png

结果就是清一色的 byte[],因为 String 本身也是由 byte[] 组成,所以这种我觉得就没什么意义。

retained heap 这种才正是我一直再找得,当初就是像找一个工具,能递归式的获得一个对象占用的所有内存,包含子对象的占用,这样才能比较方便体现哪里占用的内存最大,所以就看到了 mat

dominator tree 这种就是按照 GC Root 的顺序,自上而下遍历所有的对象,计算他们占用的大小,然后以树的形式展示在我们面前。


不过,所谓 Heap Dump,当前就只有 Heap 的情况,Java 除了 Heap 区,也有 Non-heap 区,通常对于类的元数据等相关存储,就在相关 Non-heap 区,还有一些直接内存引用,这部分内存是不好进行分析的,虽然加上参数 -XX:NativeMemoryTracking=summary 也可以打印一些信息。

❌
❌