2025-12-30 22:47:42
在新加坡的第五年整。
今年和欣好像一直在找房子。之前住在湖畔的一个 HDB,房东决定要卖房子,所以我们不续租了,从湖畔的 HDB 搬到了碧山的 Condo。住了不到一年,年底又搬了一次家。第一次住完全没有家具的房子,又花了时间去买沙发,床,桌子,组装家具,搬家,年底要处理的事情太多太多。
买了一台大电视,三星的 S95F,被惊艳到了,OLED 屏幕,还有不反光的特性,效果非常好。于是年底的假期大部分时间都和欣窝在沙发上看电视,买了 HBO,把过去看过的很多电影又看了一遍。把《权力的游戏》也从头开始看了一遍。
今年玩的游戏不多,为了准备玩《GTA 6》买了一台电脑放在客厅,但是这游戏居然又跳票了。缝合怪《潜水员戴夫》拿到了完美通关,《荒野大镖客2》玩到了 50% 左右,然后和欣一起玩《双人成行》,还没有通关。
又买了一台电子书设备和微信读书会员,看了莫言的《檀香刑》,《酒国》以及一些杂文集,一些余华和刘震云的小说,以及其他一些专业书。
学会了一个新的技能:双拼。不过感觉刚刚够得到之前全拼的速度,再用一段时间速度应该更快。
旅行。年前我和欣带爸妈来新加坡和普吉岛旅行。普吉岛去过 3 次了,但是一次博客都没有写过,拖着拖着就有一些原因不想写了。9 月份去上海「旅行」了一趟。在上海生活了这么多年,其实并没有作为游客去过一些地方,这次回去,体验了脱口秀(笑得肚子疼),逛了南京路(和当初上学的时候很不一样了,傣妹居然还活着,其他的店大部分都换了),看了一个剧。年底准备了关西的旅行,但是因为其他事情取消了。
今年工作上的难度越来越高。我比较擅长用技术来解决问题,但是今年公司的管理风格向重视流程转移,试图通过流程上的规范来提高整体的可用性,导致审批越来越多,流程越来越复杂,一个简单的 API 调用需要花费之前数倍的成本来实现。
另一方面,在金融方面的发展带来了更多的合规要求,从而带来了巨量的运维工作。可高层在「降本增效」方面的努力没有停止的迹象,仅仅靠运维工作是没有「绩效」的,如果要拿到比较好的绩效以及晋升,就需要拿出时间去做亮点项目。这就造成了另一个矛盾点,也造成了另一部分人的离职。身边的同事越来越少。
在《凤凰项目》一书中,安全部门的主管发现项目不需要安全团队就可以通过一项审计,意识到自己的工作可有可无,在酒吧喝得酩酊大醉。他问运维部门的主管:「我们就真的对你们一点帮助也没有吗?」运维主管尽管很想安慰他,但是又不想说谎,只好说:「对不起,一点也没有。」
每当安全团队要我们把一个毫无敏感信息的 API 用最高的安全等级来要求的时候,我就会想起来这一段。
尽管如此,今年还是做了不少值得骄傲的事情:
在产品方面,不再负责 Service Mesh 项目了。我从加入公司就开始维护这个项目,经历了几十个版本,也经历过它造成的(我加入之后)公司最大的故障,经历了几代开发团队变迁。开发团队的同事技术方面无可挑剔,产品在近几年没有出过重大事故,也支持了业务的需求。不足在产品的易用性上,配置过于复杂,难以理解,学习成本高。整体架构需要改变,现在是 daemonset 部署模式,难以实现资源隔离和审计。
产品运维的工作中心转到了 SDN 上面,也是我感兴趣的方面。SDN 也有很多问题:易用性和控制面的可用性太差;组件太多过于复杂;需要硬件兼容软件而不是软件兼容硬件,等等。这些都是需要解决的问题。
明年的工作会尝试一下结合 eBPF 来做架构上的可观测性,通过图数据库自动整理软件的依赖。
就写这么多吧,明年还是继续在网络的领域耕耘,学习和分享更多的知识。
其他的年终总结列表:
2025-12-27 14:13:38
我的姥姥今年去世了,没有痛苦。我们之前都预料到了这一天的到来,大家在五月份都陆续去看望了她,大家也都知道这是告别。
小姨十年前告诉我,姥姥诊断出了肺癌。但是年纪太大,家里商量之后,决定不治疗,让她安享晚年,想吃什么就吃什么,想做什么就做什么。姥姥晚年喜欢抽烟,吃肉。每次去看望姥姥,我都带两条烟,十斤肉,或者红包。
这几年每一次去看姥姥,她下炕的次数越来越少,头发越来越花白,和她说话的声音需要越来越大。
姥姥的去世,我没有为她感到伤心,只为我以后再也见不到她了而伤心。至少她的晚年可以抽烟吃肉,没有把时间都花在了 ICU 里面。《长命百岁》里面提到, 现代医学专注于延长人类的寿命,但是这些寿命的质量也同样重要。人固有一死,我希望自己离开世界的时候也可以这样。
十年之后,姨们过年聚在一起,终于觉得当初的诊断不对,应该不是肺癌。但是也没有必要再去纠结了,管它是什么吧。
姥姥裹小脚,有六个女儿,一个儿子,重男轻女,我上高中的时候,过年的压岁钱姥姥就只给我和哥哥了,姐姐和妹妹没有。到了晚年,姥姥和姥爷俩人依然住在那诸城县边上的一个村子里,在那里生活了近一个世纪。姥姥和姥爷的感情是很多人都羡慕的。
姥姥的一生几乎都住在那几间房子里,没有去过太远的地方,可能没有出过省,没有吃过什么山珍海味。但也许她的一生是幸福的。这又让我思考起来那个这一年我不断思考的问题:「怎么度过这一生才是值得的呢?」
一路走好,姥姥。
2025-12-12 23:53:21
[剧透警告]
今年又来玩了 Advant of Code1,最近看 C 语言的代码比较多,就用尽量用 C 语言做。

第 9 和和第 10 天最难,尤其是第 10 天,看着挺简单,实际是从来没有听说过的整数线性规划问题2。
第 11 天一看要用 dict,放弃 C 了。用 Python 很快就过了,一个搜索加路径缓存即可。但是如何用 C 写出来完全没有思路,最后问 chatGPT,想起来还有邻接表这种巧妙的表示方法。
第 12 天最搞笑,以前是做 part1 顺利,做 part2 的时候时间爆炸。day 12 做 sample 的时候就感觉时间要爆炸了。加了几个剪枝,part1 居然过了。我想完了,part2 肯定是让我找所有的可能的情况,一定会爆炸。结果点开 part——就通关了。今年居然只有 12 天?那距离圣诞节还有 13 天呢,我做啥?

fuglede 的代码太优雅了:https://github.com/fuglede/adventofcode/tree/master/2025,不愧是做量化的。
2025-12-12 11:44:33
在和朋友一起吃饭的时候,A 提了一个有意思的问题:怎样可以把一个机房内的路由设备从互联网「隐藏」呢?
「隐藏就是没有人可以知道这个设备的 IP 地址,这还不简单,只要禁用 ICMP 就可以了」, B说。
A 说,这样是可以。很多安全团队在实施起来也确实是这么做的,但是这样并不好:机房内所有的 IP 都无法 ping 通了。这样会增加 debug 的难度,得不偿失呀!
C 说,那就依然转发 ICMP 包,但是如果是 TTL=1 的包,就不要回复 ICMP Time Exceeded 了。
B 说,人家要的是「隐藏」,要是像你说的这么做,别人还是知道中间有一个设备的存在,没有完全符合要求。

事实是这样的。假设一个简单的物理拓扑是 A -> B -> C,B 不回复 ICMP Time Exceeded,那么 traceroute 看起来就是 A ? C,可以猜测得到中间有一个路由器,但是已经禁止回复 ICMP Time Exceeded。看起来像下面这样。
C 说,traceroute 的原理是发送 TTL=1, 2, 3, … 的包,不断让路由器回复 ICMP Time Exceeded 信息,来得到每一跳的 IP 地址。要想完全隐藏,只需要:
ICMP Time Exceeded;这样就可以完全隐藏了。要达到这个目的,只需要:
A ---[TTL=1]---> B ---[TTL=1]---> C, 对于客户端的 traceroute,看起来就像:A → C。这样(理论上)好像确实可行了。三人对这个结论满意了。
后来我把这个讨论记录在了博客上(你现在正在阅读的一个),一位读者马上就发现了问题:可是这样 C 会出现两次吧!
确实是这样,假设在 A -> B -> C 的链路中:
这样 C 就出现了 2 次!
看来,B 必须完全不减 TTL,直接转发,才能隐藏自己。不过这样就有出现环路2的风险了。
2025-12-10 23:12:49
这个博客在四年前做了一次迁移1,目前是架设在一台 DigitalOcean 的 VPS 上,前面用 Cloudflare 作 CDN,得益于我选择的这两家公司非常靠谱(但是最近 Cloudflare 的事故23让我这个 CF 吹有一些尴尬),自从搭建起来之后,我几乎没有维护过。

最近后台一直提示我 PHP7.4 EOL 了,今天终于打起精神来决定升级一把。
一不做而不休,干脆直接把 4 年半前启动的这台 VPS 全升了吧:
因为我是专业的 SRE,所以这次升级读者感受不到任何区别。
整体比较顺利,唯一遇到的问题是,我用的 wordpress theme 太老了,一样的代码居然在 PHP8.3 挂了。看提示是函数参数少传了,在 PHP7.4 是 Warning,在 PHP8.4 是直接 Fatal。
本来打算放弃治疗,直接用一个新的 wordpress 官方主题得了,省心。结果一个二〇二五这些主题,都是什么玩意,行距看着都难受。又回来决定修好主题的代码。得亏 ChatGPT,没想到意外地顺利,很快就跑起来了,目前也没发现什么问题。
安全方面上顺便做了一个加强,以前的架构是:域名解析到 Cloudflare,Cloudflare proxy 到我的 Nginx,Nginx 只接受 Cloudflare 的 IP4,其他的一概拒绝。自己以为很安全了,没有人知道我的真实 IP。然后自己一查,居然早已经暴露了。

我也不知道什么时候暴露的。
这次直接用了 cloudflared,原理是,我的服务器 Nginx 只 listen localhost 的端口,我的服务器安装一个 cloudflared,cloudflared 会去主动连接 cloudflare,这样,在 cloudflare 收到请求的时候,会通过 –> cloudflared –> nginx 转发到我的机器上。有点像 FRP5 穿透。如此一来,我的 IP 完全没有暴露在公网上了。过段时间再去搜索一下,看暴露了没有。
欢迎读者留言 ; D
2025-12-06 16:01:16
最近的工作比较忙,以至于网络技术的系列文章1许久不更新了。这几天在解决的问题是镜像存储服务 Harbor2,存储的 docker image 太多了。
虽然我之前在博客里面分享了一些 Docker image 构建的技巧3,以及炫耀了构建一个最小的 Redis Docker 镜像才不到 2MiB4,但是无奈,我的博客基本没有人看,所以同事上传的 image 都非常可怕,动辄就上 G,20+ GiB 的都有。现在我们的 Harbor 存储已经是 PiB 级别了。
多余的 image 就删除就好了,问题就在于,删除 image 比较复杂。分成几个步骤:
第二步尤其重要,简单来说,image 是分层的,一层就是一个 blob,一个 image 可以引用多个 blob。比如服务 A 的 Dockerfile 开头是 From: ubuntu:24.04,另一个服务 B 的 Dockerfile 开头也是 From: ubuntu:24.04,那么这两个 image 都是引用了 ubuntu 的 blob。删除服务 A 的 image 的时候,不能把 A 的 blob 都删除,因为这样的话 ubuntu 的 base image 就连带被删除了。所以我们在删除一个 image 的时候,其实并没有释放任何空间,而只是删除了 image 对 blob 的引用。这时候还不知道哪些 blob 是可以释放的,要知道哪些 blob 可以删除,就必须扫描全部的数据库,找到没有任何引用的 blob,才可以删除。难题就在扫全表这里。
这个问题就和编程语言的 GC 问题很像,不过更加简单一些,因为引用只存在于 tag 到 blob,tag 之间和 blob 之间不存在引用,也就没有环的问题。
引用计数比较合适这个场景,因为没有环路,所以引用计数到 0 就可以直接删除,不需要扫表找孤零零的环。但是 Harbor 本身没有用这种方案,估计是因为引用记录维护起来比较难,必须准确并且处理好并发,处理不当很容易有数据误删或者出现永久的垃圾。
这是官方的代码采用的方案,基本思路是,扫描所有的 image,对它们引用的 blob 标记为在使用中。扫描完成之后,所有从未被标记过的 blob 直接删除。
如果直接用 Harbor 的 GC 方案,那么运行一次 GC 需要超过一个月的运行时间(不知道具体需要多久,因为从来没有成功跑完过)。之前的负责人设计了一个很聪明的方案,基本思路是,找到系统性能低的瓶颈,然后针对性地处理这些瓶颈。
对于前面的 3 个步骤:
这样,整体运行一次只需要一个月。
目前还是存在很多问题。我接受之后又做了一些改进:
blobMgr.CleanupAssociationsForProject 这一步其实是最费时间且多余的,后面执行 GC mark 的时候一定会运行一遍。删除这个逻辑之后只需要 0.1s 就可以删除一个 image;本质上是用最少的改动自动化原来的 GC 逻辑,目前运行一次的时间是 3 天。已经足够满足需求了,因为不需要人工执行,所以 3天和 3 个小时区别不大。
上一个负责人留下的文档详细记录的 Harbor GC 的逻辑以及改进点,比 Harbor 官方的文档还要详细。有了这些我半重写 GC 的逻辑就简单很多。
在他之前,是另一个负责 Harbor 的同事。阅读代码并找到瓶颈是需要很大的勇气的,且不一定行得通,可能花了很大的力气,最后发现这个事情做起来就只能这么慢。
但是问题还是要解决。所以他那时用了另一个有意思的方案:
这是一个很有意思的「用运维手段解决技术问题」的例子,在 SRE 的工作中,迫于没有对软件的实现的控制力,我们经常需要用运维手段来解决代码实现上的问题。