个人 VPS 自部署的长期主义:干净与安全
我开始认真折腾个人 self-host,大概已经有五六年了。严格意义上开始管理我自己的 VPS 的时间点,应该还可以往前追溯。最初的动机其实很简单:有些服务我每天都在用,比如密 码管理器、音乐播放、笔记同步、个人网站、代码仓库等等。既然如此,为什么不干脆自己 部署?这样一来数据在自己手里,也不用被各种平台订阅和涨价牵着走。 这几年一路走下来,说实话,self-host 并不省心。我要维护系统、备份数据、关心安全、 偶尔还要救火。但它给我的回报也很明确: 数据完全掌控在自己手里 不再被订阅价格牵着走 对系统、网络和安全的理解明显更深 现在我一台服务器跑着十几个服务,成本只是一台服务器的钱。在这两年取消了不少第三方 平台的订阅后,算下来,整体成本可能反而更低。对我来说,这种“复杂但可控”的状态,反 而比依赖一堆外部服务更安心。当然不这适合所有人,我感觉有:有一定技术背景的人、喜 欢折腾的人以及愿意为稳定与掌控付出时间的人,这三者合一就适合这样。 到现在,我并不觉得自己成了什么“专家”,但有到现在两件事的感受越来越强烈:安全性 以及服务器环境的整洁程度,这也是这篇文章的两个主线。这两个问题,几乎决定了 你这台服务器上的各类服务能不能长期稳定地用下去。然后就是需要迁移的时候,比如换了 一个更便宜或者更好的 VPS 供应商,这个时候的工作量(我自己就换了好几次,知道真的 换不动了才稳定下来)。 草莽阶段 最开始的时候,我的做法非常朴素。我会直接在宿主机上安装服务,然后安装 Nginx 或者 Apache2(httpd)。比如用 Nginx 做反向代理,Nginx 这类 Web 服务器 的好处是配置文 件可以拆开,每个站点一份,用域名来区分各个服务而不是端口号,看起来井井有条。那时 候我觉得这套方案挺优雅的。 但随着服务慢慢变多,问题开始一点点浮现。有的服务需要特定版本的依赖,我很难在同一 个环境安装不同版本的各种依赖库或者数据库。而有的会在系统里留下各种临时文件;配置 文件散落在 /etc、数据放在 /srv,日志又在 /var。一开始你还能记得“这是哪个服 务的”,时间一长就完全混在一起了。 这期间,当我想删除一个不用的服务时,我已经不敢确定自己删得干不干净。特别是有的服 务提供“一键安装脚本”,安装的时候一时畅快,等到了你想摆脱它的时候,你会发现当初的 脚本不知道做了什么改动,如何复原都毫无头绪。你很难确认还有没有残留的配置、后台进 程,甚至不知道删错东西会不会影响别的服务。这种不确定感,本身就是一个风险,对于强 迫症来说,每每想起简直是如鲠在喉。 容器化阶段 后来我把几乎所有服务都迁移到了 Docker 里。而且,到现在越来越倾向于将所有能用的到 的服务或者工具软件容器化,再去部署。有些不提供标准的容器化部署方案的,但又很想用 的服务,我会直接自己进行容器化包装,形成自己的容器化的解决方案。 真正改变体验的不是 Docker 本身,而是 Docker Compose。容器这东西,本质上就不 应该被当成“长期维护对象”,它随时都可以被删掉、重建,你需要意识到这个。早期的时 候,我觉得 Docker Compose 配置文件麻烦,为什么不用命令直接创建一个容器,然后让这 个容器长期运行。后来等我要更新,或者这个服务依赖多个底层的其他容器的时候,问题就 逐渐浮现出来。如果当初将对服务的部署写下来,写到一个配置文件中,然后每次就执行这 个配置文件就能完美复现原先的部署,不需要自己额外配置什么 Bridge 网络,或者什么 hostname,亦或者是重启策略。这个节省了大量的时间和精力,并且配置的时候出错的可能 性,也大大降低了。 ...
GpgFrontend的前世今生:我与一个开源加密工具的成长之路
缘起:偶遇 GnuPG(GPG) 我在 2021 年 4 月左右第一次接触 GPG。那时我还只是个对计算机世界充满好奇的学生, 对密码学的理解也仅停留在“密码就是加密字符串”这样的表层概念。但当我第一次读到非对 称加密的原理——一个公钥可以让所有人加密信息,而只有私钥拥有者才能解密——我被深 深震撼了。这种“数学意义上的信任”让我着迷。它优雅、严谨、不可逆。那一刻,我意识 到,这种技术的美感并不亚于任何艺术创作。于是我开始学习 GPG 命令行的各种操作,从 密钥生成到签名验证,反复实验,琢磨其中的逻辑。 但与此同时,我也发现了问题:**命令行的门槛实在太高。**特别是有的时候,想快点做一 些简单操作却要输入一串命令,让我感觉“不爽”。对非技术用户来说,GPG 的命令行是一片 晦涩的荒原,几乎挡住了所有想尝试的人。我开始想,如果能为 GPG 做一个简单易用的图 形界面,让它像一台看得见摸得着的“密码机机”那样可视化与便捷;如果每个人都可以简单 地拥有者与一台耐用且小巧的“密码机”,也许会有更多人愿意尝试。这个想法当时看起来微 不足道,但正是它,开启了我后来整整数年的技术旅程。 “密码机”让人立刻联想到某种坚固、可靠的小设备,就像早期的机械密码机(如 Enigma)那样充满仪式感。它承载的不仅是技术,更是一种对安全的信念。这种机器功能 不求繁多,却必须精准可靠。用户按下一个按钮,期望它执行的动作必须**可预期且安全 **。这种对“可控性”的追求,就是“密码机”的灵魂。 我开始寻找现成的解决方案。那个时候已经有一些 GUI 工具,比如 Kleopatra。但它们的 很多都界面复杂、功能太多,甚至还引入了 X.509 证书体系,这在当时一下子劝退了我。 我希望的是一个更轻、更纯粹的东西。后来我发现了一个叫 gpg4usb 的项目,它非常接近我理 想中的形态——轻量、可携带、跨平台。然而遗憾的是,它早已停止更新,只支持 GPG 1.4 版本。那意味着它与现代系统的兼容性问题几乎无法回避,也无法支持子密钥机制。 我开始琢磨:**能不能在它的基础上重写一部分,让它重新焕发生命?**在 5 月份左右, 我花了大约半个月的时间,在宿舍里读它的源码,摸索 GPG 的接口调用方式,研究 Qt 框 架与跨平台兼容。那是一个孤独却极其充实的过程。每当我让一个新功能成功运行时,心里 的成就感都难以言喻。当我终于让它在 GPG2 环境下运行起来,并修复了不少原有的 Bug 后,我决定给这个焕然一新的项目一个新名 字:GpgFrontend——GPG 的图形前端,也是我人 生中的第一个完整开源项目。 亮相:从宿舍代码到 GitHub 上的光点 我将第一个版本上传到 GitHub 后,并没有期待太多关注。毕竟这是个极小众的领域,GPG 本身就不是大众常用的东西。但几天后,我收到了第一个 issue。那是一位用户,提到程序 在某个操作系统上无法运行。他不仅提供了日志,还提出了修改建议。那一刻,我第一次感 受到某种力量,愿意继续为这一个项目投入时间。 ...
二零二三年四月第二周技术周报
原文经过 AI 润色,非本人文风。 在全国范围(如广州、深圳、上海、北京)部署大量客户端的配置下发服务中,面对节假 日、活动上线等高并发场景,传统的单地数据中心架构难以支撑。本文分享我当属在“配置 下发服务”层面进行的多地部署、数据层优化及缓存机制升级,阐述其技术背景、方案设 计、落地实践与收获。 背景 我服务的客户端分布在广州、深圳、上海、北京等全国多个城市,通常需要根据运营策略获 取各种动态或静态配置,比如节日活动、功能开关、故障临时屏蔽等。这些配置由服务端统 一下发,客户端根据自身属性(地域、版本等)拉取对应内容。 随着客户端规模扩大,特别是在活动上线、客户端批量启动的场景中,配置拉取请求会在短 时间内剧增,导致服务端QPS暴涨,严重时甚至会拖垮主服务节点。我虽在容器层实现了全 国多地的 Kubernetes Workload 部署,但数据层(包括数据库和 Redis)仍集中在广州。 所有请求即便从北京或成都的容器发出,最终还是要跨地域访问广州的数据中心,延迟高、 压力集中、体验差。 此外,虽然服务中使用了容器内缓存机制,但考虑到容器本身应保持无状态,其运行内存不 适合长时间、海量地持有配置缓存。一旦配置数据规模扩大至几百兆甚至上 GB 级别,运维 和资源成本都急剧上升。 为了解决这些问题,我决定从数据层和缓存层入手,进行更彻底的多地部署改造。 设计思路 基于上述挑战,我设计的方案主要包括: 主数据库实例(写 + 读)部署在广州,运营人员在这个实例上进行配置管理、写入 操作。 各地部署 只读副本(Read‑Only)数据库实例,例如上海、北京、成都等地。各地 容器连接对应地区的只读实例以拉取配置。 Redis 亦做多地部署:每个地域有自己的 Redis 实例,容器拉取缓存优先从本地 Redis;若缓存未命中,再访问本地只读数据库或主数据库。 在配置下发平台中,对不同 Kubernetes Workload 打标签(如地域、版本、标签等), 客户端根据标签拉取对应配置。运营人员仍在统一平台管理配置。配置平台写入主数据 库,然后同步至只读副本,各地容器读取对应地域的副本。 架构优势 读请求分散到各地只读副本,减轻主数据库压力。 客户端拉配置网络路径变短、延迟下降、体验提升。 配置写入集中在主实例,确保配置管理统一、操作简便。 架构符合“写少、读多”的典型场景:操作多为配置读取,写入(新增活动、屏蔽功能等) 相对少。 实施 根据上面的设计,我继续保留广州作为主写中心,同时在全国多个区域(如北京、上海、成 都)部署只读副本,配合就近的 Redis 实例,实现配置服务的分布式读请求调度。主数据 库仍由运营人员维护和写入更新,通过异步同步机制,将数据分发到各地只读副本。各地的 容器服务则连接本地副本,从而避免跨地域拉取配置数据。 Redis 也采用相似策略,在每个部署地域都设立实例,服务端优先从 Redis 缓存中获取所 需配置,未命中时才回源数据库。这样既减轻数据库压力,也提升了配置拉取效率。为了让 客户端能快速命中就近服务节点,我基于 DNS 做了地域级别的接入调度,同时结合 Kubernetes Workload 的标签机制,自动下发对应配置。所有运营配置仍在部署在广州的 “统一平台”进行集中管理,通过主数据库推送同步,容器按标签识别自身身份,拉取所需数 据。 ...
先行动而非先推理
很多时候,我在做事前都会去考虑很久利弊得失。就比如:我去做了这件事会遇到哪些情 况?这些情况下我该怎么办?那些情况下会不如人意?我用的这些个方法真的好吗?等等。 不得不说,这伴随了我的很多的决策,特别是在一些比较大的决策下,这些事先的思考真的 有用处。所以,我在做很多事情之前,都会去想一下这些问题。如果我的结论是:这件事情 目前条件不成熟,或者这件事情很麻烦,亦或者这件事情目前前置条件没达到。那么我就不 会去做这件事情,以免投入了很多,却收效甚微。但是,最近我就发现,我这样将这个方法 扩大化并不好。因为很多事情,对我来说是新的,在我现有的经历和经验下,对这些事情的 认知还处于比较幼稚的阶段。这种情况下,我无法正确地去推理上面说的这些问题,从而去 得出正确的结论。 具体来说,利用常识是真的能枚举一些可以预见的情况的,理论上,这些情况会发生。但 是,实际上,一件事情,或者一个项目,这些理论上会发生的事情都有它们会发生的概率。 这些概率又由许许多多的前置变量决定,如果对这件事情的认知不足,很难说对这些变量抓 大放小,找出主要。这样就容易放大一些变量的影响,最终导致预测的事情其实并不会发 生。另外,在很多情况下,我也会对一件事物的性质和反应对出某种推断,特别是对一类人 的推断,比如说我觉得某些人不友善,某些人友善这样。但实际上,在你真正地接触这些人 后,你才能知道他们是否真如你想得那样友善。或者说,他们对于你和他们接触的真正反 应,只有你去接触后才知道。这些事情是无法进行预先推断的,除非建立在你经常和别人打 交道的基础上。所以,在很多时候,我对于某些事物,某些人群的判断,是先入为主的,是 从别人那里听来的,模糊的,加入了自己想象的一种判断。大多数时候,这些判断并不准 确。如果我想让这些判断准确,那么我就要先行动起来,去做这些事情。 先行动,也就是说在对事情有个轮廓,模糊的前提下,优先行动起来,放低姿态去尝试,然 后再根据反馈的结果看看是否可行。如果反馈良好,完全可以继续行动。如果反馈不好,也 不必立即收手,可以分析一下原因,或者拿着这些具体的案例问问其他过来人。不论哪一种 情况,都比先不动,然后想一大堆,这样如何那样如何,来得具体、实在与真切。很多我们 认为实践中会遇到的比较大的问题,往往会在真正的实践中找到思路。反之,在实践中真正 遇到的大的问题,在想法中往往预料不到。所以,先激活自己,去迈出第一步,在对事情认 知比较模糊的情况下,比什么都重要。 先推理,我认为是建立在对事情已经有一个比较成熟的把握的情况下,特别是在这个事情所 述的领域,我并不是一个纯粹的新手。而推理的准确度,建立在我在这个领域的学习与实践 的成果的叠加。也就是说,推理需要真真切切的样本,这些样本需要自己得到,而不是纯粹 从别人那里“听来”,更重要地是自己通过实践“寻来”。这种情况下,有了这些样本作为基 础,对事情的推理预测的把握才能更大,对于事物的认知才能更加准确。这样,后续就能不 断靠推理来规避一些在这该领域下的陷阱,从而让自己能够跑得更快更稳。 如果综合上面的论述,我可以发现,总是应该先去行动,因为推理没有行动的结果作为支 撑,是摇摇欲坠的。先行动而非先推理,确实是可以作为我在对于一些自己并不熟悉的领域 的一种行事的习惯,从而避免先入为主,然后想太多,最后什么都没做,白白错过了可能的 机遇。
让树莓派 5 跑上“香草”主线内核:精简裁剪、交叉编译与部署全流程
摘要 本文系统记录了我如何将树莓派 5 打造成主线 Linux 内核(Vanilla Kernel)的极简开发 与测试平台——包括仓库极简裁剪思路、定制化内核配置、交叉编译与自动部署脚 本、overlay 移除技巧,以及长期与主线社区同步维护的实战心得。适合关注 Linux 主 线、ARM64 适配、内核开发、Upstream 贡献或喜欢折腾内核的极客们参考和实践。 背景与动机 最近一段时间,我在深入研究 Linux Kernel 的理论和架构,准备正式切入操作系统内核领 域。因此,特意购买了树莓派 5 作为开发板,用于学习和实践内核开发。然而刚上手就发 现,目前树莓派 5 并不能直接跑主线 Linux 内核,只能依赖树莓派团队维护的内核仓 库。当时最新的分支是 rpi-6.14.y(现在是 rpi-6.16.y),仓库的维护方式是“动态拉取上游主线代码+rebase+大量自定义补丁”,涉及 上万行的修改和历史反复变动。 这些定制补丁对于官方内核在树莓派上稳定运行、兼容所有功能确实非常重要,但对我这种 纯粹想研究主线内核特性、关注内核本身的开发者来说,这些补丁反而变成了负担: 主线同步困难,merge 经常出冲突, 版本历史频繁重写,难以回溯和比较, 验证主线新特性或做反馈变得不直观。 我对比了 x86 开发板,发现它们大多能直接运行主线内核,但价格昂贵,性价比远不如树 莓派 5。因此,我开始思考,如何让树莓派 5 也能顺畅运行主线内核,真正实现随时体验 和测试内核最新特性,并为 ARM64 平台反馈问题、贡献补丁提供便利。我的目标很明确: 用最少的补丁、最贴近 upstream 的方式,让树莓派 5 成为个人主线内核实验和学习的理 想 ARM64 平台。 仓库结构与修改范围 我的做法十分简单却又直奔核心——只保留最必要的驱动与补丁,其余全部紧跟主线。仅对 drivers/ 目录做补丁,聚焦核心硬件支持,主要包括: SD 卡驱动:保证树莓派能正常引导,内核能加载根文件系统。虽然启动固件自带部 分初始化功能,但主线内核仍需驱动挂载根文件系统。 有线网卡(Ethernet):方便通过 SSH 实现远程登录、下载新内核镜像、远程开发 和调试,是内核开发过程中最重要的 IO 通道之一。 UART 串口:作为内核调试和 early print 的输出窗口,可以直接观察内核日志,判 断启动和运行状态。 ...
搭建个人链式VPN网络:高效隐私保护、设备无限制管理与远程访问
在某些场景中,我们可能需要搭建一个链式 VPN 结构,即先让内网设备连接到自建的 WireGuard 服务器(如个人 VPS),再通过该服务器将所有流量转发到另一个 VPN(公司、 学校或商业 VPN 服务)后才进入互联网。搭建这种「VPN 套 VPN」的网络架构,有以下明 显优势: 强大的隐私保护:内网设备的数据流量首先通过自己搭建的 WireGuard 加密隧道传输至 个人 VPS,再由 VPS 二次通过商业或公司 VPN 进行转发,最终从该 VPN 的 IP 出口访 问互联网。这种方式彻底避免了使用 VPS 自身 IP 地址直接访问目标网络,大大降低了 个人 IP 被跟踪或泄露的风险。 集中化便捷管理:所有客户端设备统一连接到单个 WireGuard 服务,免去逐一配置每台 设备的麻烦。更重要的是,它还能巧妙绕过一些商业 VPN 服务商设定的账号设备数量限 制,一次付费,所有设备共享。 灵活便捷,轻松切换:由于商业或公司 VPN 配置仅在自建 WireGuard 服务器端修改一次 即可生效,下游客户端(手机、电脑等)无需单独调整。这使得更换或重新配置上游 VPN 时更加迅速和便捷,大大提高日常维护的效率。 打造专属虚拟局域网,实现跨设备互通:一旦所有设备连接到自建的 WireGuard VPN 服 务器后,将自动获得统一的 VPN 虚拟局域网 IP 地址(例如:10.8.0.0/16)。处于该网 络中的设备能够直接互相通信,无需额外设置。 这种便捷性意味着无论你身处何处,都能轻松实现: 远程 SSH 登录:随时访问家中或办公室的服务器。 私有 NAS 媒体访问:在外轻松访问家中 NAS 存储的媒体文件。 远程办公:随时随地安全访问公司内部网络资源。 方案 经过本人的探索和实践,我逐步摸索出了自己的一套链式 VPN 方案。总体来看,我的方案 完全基于 Docker 部署,所用到的核心组件有: ...
关于对事物定性判断的一些体会
在现在,生活中充斥着各种信息。我能够从各种消息来源获取各类信息,比如新闻、社交平 台、长短视频等等。在这些信息中,包含着对周遭世界的各种变化的概要情况,或者是细节 分析。其中不妨也有一些基于现在的情况,对一件事情进行定论和判断。有的分析往左边 说,有的分析往右边说,有的分析自称“中立”。总之,众说纷纭,莫衷一是。我也是很好 奇,我该如何判断这件事情的性质?哪一些是“对”的,哪一些是“错”的? 很多时候,我发现人们对一件事情的判断,其实是处处受到其自身所处的状态的影响。自身 所处的状态又受到其社会地位、人际关系、经济实力、自身经历等影响。而且,对于一件事 情,若非当事人,旁观者从各种消息中得到的总是这个事件的某个描述,描述者也将会有意 无意在描述中渗入一些倾向性,这是不可避免的。比如说,虽然这个描述者说得都是事实, 但是对于事实的描述也可以有侧重。有些他觉得重要的或者有意义的细节,他会多说。而另 一些他认为不重要的,当然就会少说,甚至直接忽略。一件事情,除了其发生的时候的过程 以外,还涉及到各种背景,包括但不限于参与者各自的背景,还有事情发生之前的各种关联 性事件,甚至当事人在某一时刻闪过的想法。如果要在描述中,把这些事情都交代详尽,那 是不可能的,更不用说有些细节根本难以探知。 很多时候我注意到,对于一件事情、一个事物或者一种现象,从两个完全不同的立场出发, 完全可以各自发展出一套非常有道理且充满正义的说法。然而从这两个不同的立场看对方, 往往会轻易给出非常尖锐的批评,双方都可以援引各种实际的例子。有趣的是,这些批评可 以互相套用,用在各自身上,从切换一下立场,然后按照这个立场的模式进行一套有逻辑的 推理,即可很好对号入座。所以,我尝试对我身边发生的事情、我感兴趣的大事、我忧虑的 事情和我日常的小事等等进行这样的正反分析,几乎都能得出两个相反的判断和结论。在这 段时间内,我陷入了一种分裂。我经常苦苦思索,到底哪一种说法是对的,按照事情的发 展,哪一种立场可以笑到最后。我也渴望,一种中立、理性、客观的立场。然而,每当我认 为某个人阐述的这种立场非常客观,非常中立,一段时间后就会有针对持有这种立场的人的 批评之声。要么就是,客观到味同嚼蜡,我都不想继续继续听下去。我为什么会这样,我常 常反思。 在长达几年的断断续续地思考后,我最终得出我自己的结论,寻找一种”中立“且”客观“的立 场观点,或者是对一件事物去苦苦追寻”真相“都毫无意义。这样反而会让自己活得非常累。 根本的原因在于,我自己是有自己的立场的,不需要再去强迫自己接受其他人的立场。我的 立场来源于个人喜好、社会地位、经济实力、过往经历、经验见识等等,这些也是我上面所 提到的影响个人对于事物判断的几个影响点。所以,我会在某些时候,对于一些声音感觉 很”客观“,对于另一些声音觉得很反感。那是因为,其实对于这些事情早已经在自己的内心 产生的自己的判断。那为什么我不能去乐呵呵地接受所有的声音?我承认,我可以去听取所 有的声音,并思考其他的声音中包含的那些道理,然后尝试去加深自己对于一件事情的认 识,但其实很难做到去开心地接受。然后,让我去站在我自己的对立面去思考,完全可以, 但是对于我自己并无意义,因为我毕竟无法成为对立面的那一些人,很难按照他们的立场来 做或者享受这样的立场带来的利益。 我更加倾向于认为,每个人都有自己的基本法,基本法是按照自己的个人情况量身定制,是 自己对于周遭世界该怎么样才会“完美”的朴素认识。我自己是立法者,为了对于一件事情进 行认识和判断,我可以通过基本法衍生出一些实际的法律。我自己也是法官,根据基本法衍 生出的现行法律,对一件事情进行定性和判断。我是否要去参考其他人的定性和判断,可 以,但是不是必须,应该更多地秉持着一种认识世界上的思想多样性的态度。别人如何判 断,那是基于其他人的基本法,他们的基本法可能与我有相似之处,也可能与我大相径庭, 完全相同的可能性非常小。那既然不是同一套基本法,那么各自也会衍生出自己的一套实际 的法律。既然他们与我所处的体系都不同,我为什么要完全接受一种立场? 对待日常生活中,那些自称的”中立者“的态度,基于我自己的这套观点,也有了一些清晰的 方法应对。这些中立者,其中一类,无非是宣称自己所述的是事情的真相,但是前面已经说 过,一件事情的真相是很难原汁原味的还原的,各类描述都带有自己的侧重,而且总有不为 人知的细节未曾曝光。既然这样,那空谈真相还有何意义,即使事情有一个绝对的争相,那 理论上也只有神知道了。所以我觉得,我大可放下“追求真相”的精神洁癖,基于我自己所知 道的,去给出我自己的看法。还有一类,说自己的观点很“中立”和“客观”,这是可笑的,因 为他也是一个人,有自己的基本法,如何能抛却七情六欲,成为一个神仙,从而来达到“中 立”和“客观”。如果有,那就有一种相对的中立和客观,那就是穷尽世界上的所有的立场与 其对应的思想观点,以及他们对于这件事情的看法,然后全部精确记录并阐述。有些“中立 者”正在向着这方面努力,这类人,我觉得确实是往正确的方向在走。但是,我作为一个 人,听取这么多的立场有什么意义,难道我能随心所遇地切换自己的人生,来去实地体会和 践行这些思想观点和判断?类似地,就比如一个问题有即使多种解决的方法,但是最终我还 是会用一种方法去解决它,选择哪一种方法完全是个人的事情。所以在这里,我认为某个事 情该怎么判断,听从自己基于自己立场的判断即可,在自己的这个参考系下,这就是中立和 客观的了。其他的参考系下的看法,又与自己何干?当然,我们也可以多看看其他的参考 系,但这绝不能作为一个强制的要求。如果自己想真正独立,那么维护自己的参考系的独立 性,捍卫自己的是立场、观点和偏好,是非常重要的。 我也注意到,对于基本法某些部分或者条文有比较多相似性的一些人,是完全可能聚合成一 个团体的。一个人独自战斗地久了,找到和自己有相似观点的人,还是一件很欣喜的事情 的。对于一些事情,一个人完全可以去发现和寻找自己的团体,不一定要加入和做些什么实 际的事情,也可以是一种内心的靠拢。很多时候,人们会为了一件事情的看法,相互攻讦, 给不同立场的人扣上一顶帽子。如果这个时候,一个人能够站在一个自己舒服的团体下,那 可以减轻很多压力并有可能产生一种安定感。很多情况下,我觉得“站队”是对个人有利的。 更多的人如果能通过“站队”,共同努力去维护和传播一种相似的立场,才能让一种“共同立 场”持续存在并产生力量去真正推动现实中的一些事情。这样才有可能,在大家去解决某个 社会性问题时去考虑这种立场,然后去采取这种立场提供的方法或者部分思路。如果真能这 样,个人才能够在这种情况下获得更多的利益,让自己觉得自己的观点偏好与自己所喜爱的 有了更多的意义。 ...
二零二三年四月第一周技术周报
看了一下当时的周报,值得说的事情也就是关于日志整理的事情,然后就是关于一个配置下 发服务的多地部署。关于多地部署的事情,下一篇技术周报再详细说明。 首先来说说,日志方面的事情。现在的话,线上有很多服务。然后目前 DEBUG 的日志,是 用了腾讯云的 CLS。而目前存在一些问题,这些服务的日志有些分散,不太好找。如果一个 请求跨越了多个服务,有可能需要研发去跨越几个日志集然后来定位问题。然后就是,这些 服务技术栈并不统一,并没有一个统一的日志监控框架来监控这些服务的状态。目前追踪跨 服务的请求使用 TraceID,TraceID 是在请求进入网关服务或者入口服务的时候自动生成 的。TraceID 有可能是纯数字,也有可能是数字带字母。后续的服务之间的请求基本上会带 有 TraceID 这个字段,用于追踪请求。然而,也还有很多请求内容里不带 TraceID,导致 服务没办法从请求本身来拿到 TraceID,只能索性重新生成一个,所以这些请求在各个服务 之间的 TraceID 都会不同。总而言之,理论上,正常情况下,一个 TraceID 将伴随一个请 求,从进入追踪范围到出追踪范围为止。 另外还有个问题,就是这些个日志集中,日志内带有的字段也不太统一,有写日志集的请求 内容的字段叫 X,有些又叫 Y。有些字段日志的结构上有,实际输出的日志中却没有填写。 如此这些,都增加了新的研发上手定位 BUG 原因的学习成本。上述这些问题,都很让人头 疼。 还有一个就是降本增效的问题,在目前的已有日志的基础上需要看看还有哪些日志可以精简 优化的。整个部门,每个月的单单存储和整理日志成本都十多万。而我负责的这一块的业务 量又占大头,日志量也是非常大的,每天大概有 540 亿条日志。所以,我需要在这一块多 下功夫。 对于当前的的 TraceID 问题,我采取先把同样技术栈服务的 TraceID 的生成方式和传递方 式都改为统一的。对于 Java 技术栈的服务,用的无非都是 Spring Boot 框架。所以我对 每个服务都增加了一个统一的拦截器,来拦截请求和响应。对于请求,这些 Java 服务处理 的都是文本类型的请求,基本上都是 JSON 格式编码的。所以,无外乎就是检查 JSON 字段 中是否有相应的 traceId 字段,如果有的话,那就拿来用。具体就是把 TraceID 放置到线 程私有化存储中(MDC),伴随着这个线程处理。这个方法也有局限性,有些请求会在某些 步骤上进行异步处理,转移到其他线程池的线程上处理了。这样,TraceID 的追踪就丢失 了。我的办法是写一个包装类,接管所有异步处理的需求,参数与调用方式和原来一模一 样,然后在其中检查上一个线程中是否含有 TraceID 字段,然后把 TraceID 字段先放到包 装类产生的对象中。切换到另一个线程后,这个线程先看看包装类对象中是否有上一个线程 存下的 TraceID,如果有就提取这个 TraceID 然后记录到自己的私有化存储中。这样 TraceID 的追踪就不会中断了。 ...
二零二三年三月第四周技术周报
本周有个插入的事项,就是说我们目前在使用某云服务商的 API,对于其中的 API 鉴权, 那边有更安全的方案。原先,我们应用调取云产品的功能时,是通过一个 AccessKey 和 AccessSecret 来的。在调用的时候,需要在 SDK 中提供这两个值,然后就可以在应用中使 用 SDK 提供的云产品 API 了。原先的安全要求是不能在代码中存储这两个值,公司会有某 种自动扫描机制,通过这个机制可以检测代码仓库中的敏感信息,这两个鉴权用的字段也当 然囊括在内。所以,我们一般都会把这两个值写在配置文件中,安全性会更好一些,也方便 更改。 而现在他们说这两个值是静态的,无法在泄露的情况下快速进行更换。毕竟我们服务一般都 有几十上百个容器,而目前来说,启动配置虽然已经集中于各种配置中心里了,但是在技术 上是无法实现秒级别的动态更改的,必须在修改配置后重新启动容器。而容器的启动是无法 通过一次性重启所有容器来完成,必须进行分批重启以保证服务的平稳,没有十几分钟甚至 几十分钟是搞不定的。 所以云提供商的技术团队提出了一个新的方案,通过向我们提供一些必要的而且泄露后不容 易造成直接影响的凭据(好像是一个密钥文件),然后通过在线认证的机制下发动态的验证 信息。这个过程比我上文提到的验证机制复杂许多,特别是我们需要在容器中纳入这样一个 配置文件,或者直接在 JAR 包中纳入。然后我们需要引入一个 SDK,来专门做这个动态验 证信息的获取,然后将这个信息通过某种方式传递给云产品的 SDK,最后才能实现云产品 API 的调用。 这个具体的过程并不是我来跟进,我把这些问题交给其他同事了,由他们来具体执行。我只 是作为云账号权限的控制者,为他们提供密钥文件并给他们提供正确的方向即可。
二零二三年三月第三周技术周报
本周值得注意的事项就是,测试人员那边反馈在测试环境下,某些账号突然无法正常使用的 问题。目前整个账号的体系还是在沿用老的系统。而在当前老系统中,账号信息使用某种特 殊的数据存储分成多个模块存储,每个模块相当于一张表。每张表都各有侧重,有些侧重于 与其他账号之间的关联关系,有些侧重于查询等等。这些账号无法使用的问题在于,其中最 重要的一张表也就是主表,这个用户的记录消失了。在其他表中该用户的相关记录还是存在 的。 这就引发了我的各种猜想,是否是测试人员测试的过程中的环境弄混了?或者是某个服务的 配置有问题导致了部分应该写在测试环境的数据写到了生产环境去了?说代码写得有问题导 致了某些极端情况下,用户的主表信息写入失败?通过调查发现,最终要的查询表中,数据 结构还是完整的,而且能够反推出用户注册时候的微信账号。查询表只有在账号创建的时候 才会去写,后续基本不会再修改了。而主表中的数据应该是和查询表在用户注册的时候一块 写入。 对于这个问题,后续推断应该是数据模块发生了某种问题导致数据丢失。而在我咨询数据模 块相关维护人员的时候,我还没说清楚问题,他们就说是测试环境并不保证服务的可用性, 可能发生任何问题。那么这个问题可能就无解了,因为如果是数据模块这层问题,我作为使 用方基本没有任何能力来自己解决。除非我能替换掉这个数据模块,使用更加稳定可靠的解 决方案。那对于这个问题,我只能够然同事手动清理查询表中的索引关系,然后在让用户重 新进入应用。当用户重新进入应用的时候,由于没有索引就相当于没有这个用户,此时就会 重新创建用户。后续几个月遇到了十几例相同的问题,没有办法,让同事添加了一段自动处 理这个问题的业务代码,以免去每次手动处理的麻烦。 这个问题第一次出现将近一年后,由于某个重要客户反馈了这个问题。而又正好这个重要客 户当时半个月之内反馈的我们的各种问题有点多,领导对此十分困扰,所以对于我这个问题 他特别重视。领导后续找到我让我推动解决这件事,我同意执行,因为我也想知道问题具体 是什么。现在能够确定的是,问题出现在数据存储层,而这一层又不是我们直接控制,我们 只是使用这样一个产品。这个产品已经很老了,维护人员很少了,而且还有其他事情。他们 不愿意来主动寻找问题,也是情有可原的。所以想要推动他们的维护人员来解决,就只能抓 住具体且直接的证据来证明他们的问题在哪里。 我以当时的了解,知道数据层分为缓存和落盘数据库,而且经过一年在各种问题的解决过程 中学习,我也能很熟练调取分析落盘数据库里的数据了。我调取了落盘数据库中的用户 ID 的倒序情况,发现从某个用户 ID 开始,后面就没有了。最近一年,用户报问题的用户 ID 以及新注册的用户 ID 是远远大于这个 ID 的。我又去日志中获取了一个最新注册的用户 ID,查询数据层,发现是有的。说明缓存中是有正常数据的,但是由于某种原因数据并没有 落到数据库中,而是在缓存满后直接消失了。 这已经能够说明问题了。于是我将这些调查记录截图发给数据层产品的维护人员,并提了一 个工单,推动他们来解决这个问题。该问题的脉络我已经提前分析地很清楚明了了,而且又 有直接的证据,他们也开始认真对待这个问题。后面确实,他们重启某个模块后,这个问题 就好了。而且最终他们承认问题与缓存层落库的机制有关系。