躬行笔记
type: channel
link: https://www.fuzhoupyy.work/
description: 古人学问无遗力，少壮工夫老始成。 纸上得来终觉浅，绝知此事要躬行。
items: 线上灰度发布翻车后，我用Nginx金丝雀发布救了一命
items: 项目交付报告：Amazon Linux 2023 环境下 PHP 5.6 并行部署实施方案
items: 抛弃Nginx？还是干掉Kong？聊聊为什么Apache APISIX现在这么火，看完这篇你就懂了！
items: 我就输了个ls，Linux底层居然背着我干了这么多事？
items: 踩坑无数！Linux命令直接跑和crontab定时跑，差别竟然这么大？
items: Linux运维神器？抛弃黑窗口，这款高颜值Web管理工具让我真香了！
items: NLB WebSocket 连接问题排查与解决方案
items: SSH 连接故障处理报告
items: Zabbix 图表字体显示故障修复报告
items: 听说你觉得VPN简单？手撸一遍IPsec，咱俩再聊聊人生

线上灰度发布翻车后，我用Nginx金丝雀发布救了一命
type: item
date: 2025-12-05T14:04:58+00:00
description: 客户差点因为一次全量发布把整个独立站系统搞崩了。那天晚上加班到凌晨两点，冷汗都出来了。找到我们做架构优化，我们团队花了一周时间研究了Nginx的金丝雀发布方案，现在分享给大家，希望你们别踩我踩过的坑。说实话，金丝雀发布这个名字听起来挺高大上的，其实原理很简单。就像以前矿工下井前会带只金丝雀，如果有毒气金丝雀先挂了，矿工就知道危险了。我们发布新版本也是这个道理，先让一小部分用户试用新版本，有问题马上回滚，不至于全军覆没。为什么要用金丝雀发布我之前在一家创业公司待过，那时候发布新版本就是简单粗暴，直接全量上线。有一次产品经理非要在周五下午发布一个"小功能"，结果周末两天我都在公司处理线上问题，女朋友差点跟我分手。传统的发布方式问题太多了。你想啊，一个新功能开发完，测试环境跑得好好的，但生产环境的数据量、并发量、网络环境完全不一样。等你发现问题的时候，可能已经有几万个用户在骂娘了。金丝雀发布就不一样了，它可以让你：先给5%的用户试用新版本，观察一段时间如果没问题，再逐步放量到20%、50%、100%一旦发现问题，立刻切回旧版本整个过程用户基本无感知我现在的公司用了金丝雀发布之后，线上事故率直接降了70%，这个数据不是吹的。Nginx实现金丝雀发布的几种姿势Nginx实现金丝雀发布其实有好几种方法，我都试过，每种都有自己的适用场景。基于权重的流量分配这是最简单的一种方式，适合你刚开始尝试金丝雀发布的时候用。原理就是通过upstream的权重参数，把流量按比例分配到不同版本的服务器上。upstream backend { server 192.168.1.10:8080 weight=95; # 旧版本 server 192.168.1.11:8080 weight=5; # 新版本 } server { listen 80; server_name api.example.com; location / { proxy_pass http://backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这个配置的意思是，100个请求里面，95个会打到旧版本服务器，5个会打到新版本。你可以根据实际情况调整这个比例。不过这种方式有个问题，就是同一个用户的请求可能一会儿打到旧版本，一会儿打到新版本，体验不太好。我之前就遇到过用户投诉说页面样式一会儿变一会儿不变的情况，后来才发现是这个原因。基于Cookie的灰度发布为了解决上面那个问题，我们可以用Cookie来做用户粘性。简单说就是，一个用户第一次访问的时候，我们给他打个标记，后续的请求都根据这个标记来决定走哪个版本。upstream backend_v1 { server 192.168.1.10:8080; } upstream backend_v2 { server 192.168.1.11:8080; } server { listen 80; server_name api.example.com; location / { set $backend "backend_v1"; # 如果Cookie中有canary标记，走新版本 if ($http_cookie ~* "canary=true") { set $backend "backend_v2"; } # 随机给5%的新用户打上canary标记 set $random_canary ""; if ($http_cookie !~* "canary") { set $random_canary "${random_canary}A"; } # 生成1-100的随机数 set $rand_num $request_id; if ($random_canary = "A") { # 这里简化处理，实际可以用Lua脚本 add_header Set-Cookie "canary=false; Path=/; Max-Age=86400"; } proxy_pass http://$backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这个配置看起来有点复杂，但逻辑很清楚。用户第一次访问的时候，我们随机决定他是不是金丝雀用户，然后种个Cookie。后续请求都会带着这个Cookie，保证同一个用户始终访问同一个版本。但是这个方案还是有点粗糙，因为Nginx原生不支持生成随机数，我们需要借助一些技巧或者第三方模块。基于Header的灰度发布有时候我们想更精确地控制哪些用户走新版本，比如内部员工、测试账号、特定地区的用户等等。这时…

项目交付报告：Amazon Linux 2023 环境下 PHP 5.6 并行部署实施方案
type: item
date: 2025-12-05T14:04:19+00:00
description: 文档版本：1.0实施环境：Amazon Linux 2023 (AL2023)目标组件：PHP 5.6.40 (配合 Nginx)核心策略：源码编译 + 依赖沙盒化 + 运行时环境隔离1. 核心挑战与技术背景 (Challenges)在 AL2023 上部署 PHP 5.6 并非简单的软件安装，而是一次跨越 10 年技术代差的系统集成。主要面临以下四大核心技术冲突：OpenSSL 版本断层 (Critical)冲突点：AL2023 内置 OpenSSL 3.0，API 已完全重构；PHP 5.6 仅支持 OpenSSL 1.0.x。后果：直接编译会报 libssl 相关符号未定义，无法建立 HTTPS 连接。Glibc 头文件变更冲突点：AL2023 的 Glibc (2.34+) 彻底移除了 xlocale.h；PHP 依赖的旧版 ICU 库硬编码了该头文件。后果：编译 ICU 时中断，提示 fatal error: xlocale.h: No such file or directory。编译器标准不兼容冲突点：系统默认 GCC 11 使用 C++17 标准；旧版 ICU 代码不符合新语法规范。后果：编译过程中出现大量 C++ 语法警告和错误。链接库冲突冲突点：PHP 5.6 需要 GNU 版本的 libiconv，而现代 Linux 将其整合在 glibc 中，链接器无法自动识别。后果：make 阶段报错 undefined reference to libiconv_open。2. 详细执行过程 (Execution Steps)为了解决上述冲突，我们采用了 “沙盒化依赖构建” 策略：将所有旧版依赖库安装到 /usr/local 下的独立目录，不污染系统原有环境。第一阶段：基础环境准备安装编译工具，并建立目录软链接以适配 AL2023 的文件结构。# 1. 安装开发工具包 dnf groupinstall "Development Tools" -y dnf install -y libxml2-devel sqlite-devel libcurl-devel \ libpng-devel libjpeg-devel freetype-devel bzip2-devel \ libxslt-devel systemd-devel libzip-devel gettext-devel gcc-c++ # 2. 创建 www 用户 id -u www &>/dev/null || useradd -s /sbin/nologin -M www # 3. 修复 Freetype 路径 (AL2023 路径变更适配) mkdir -p /usr/local/include ln -sf /usr/include/freetype2 /usr/local/include/freetype第二阶段：编译“古董”依赖库 (沙盒化)策略：手动编译 OpenSSL 1.0.2、ICU 58、Libiconv，并锁定安装路径。1. OpenSSL 1.0.2u (解决 SSL 冲突)cd /usr/local/src wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2u.tar.gz tar -zxvf openssl-1.0.2u.tar.gz && cd openssl-1.0.2u ./config --prefix=/usr/local/ssl-1.0.2 --openssldir=/usr/local/ssl-1.0.2 shared zlib make -j$(nproc) && make install2. Libmcrypt (系统缺失补充)cd /usr/local/src wget https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz tar -zxvf libmcrypt-2.5.8.tar.gz && cd libmcrypt-2.5.8 ./configure --prefix=/usr/local make -j$(nproc) && make install3. ICU4C 58.3 (解决 Intl 扩展问题)关键操作：使用 sed 修复头文件引用，指定 C++11 标准。cd /usr/local/src wget https://github.com/unicode-org/icu/releases/download/release-58-3/icu4c-58_3-src.tgz tar -zxvf icu4c-58_3-…

抛弃Nginx？还是干掉Kong？聊聊为什么Apache APISIX现在这么火，看完这篇你就懂了！
type: item
date: 2025-12-04T15:33:58+00:00
description: 昨晚又是加班，不为别的，就为了给客户提供无微不至的服务。有个微服务上线的配置写错了，导致流量没切过去，大半夜的还得爬起来改 Nginx 配置，reload，提心吊胆盯着日志看有没有报错。那一刻我就在想，咱们做运维的，日子真不该过得这么苦逼。也就是这时候，我想到了今天想跟大伙聊的这个东西——Apache APISIX。最近群里总有人问我：“现在那个 APISIX 好像很火啊，到底是干啥的？我有 Nginx 了，还要它干嘛？”这问题问得好。咱们很多做运维的，手里拿着 Nginx 这把锤子，看什么都是钉子。虽然 Nginx 确实是神，但在云原生这一波浪潮下，这尊神有时候也显得有点“腿脚不便”。今天咱们不整那些虚头巴脑的概念，我就结合我自己的踩坑经历，给大家好好唠唠，这个 Apache APISIX 到底是何方神圣，为啥我现在强烈建议大家都在生产环境试一试。官网地址：https://apisix.apache.org/zh/docs/apisix/getting-started/README/从“改配置重启”的噩梦说起咱们先别急着说 APISIX，先回想一下，咱们平时是怎么用 Nginx 的。业务要加个新域名？改 nginx.conf。后端服务扩容了，IP 变了？改 upstream。要做个黑白名单防刷？改配置，加 Lua 脚本。改完之后呢？nginx -t 测试一下，然后 nginx -s reload。看起来很流畅是吧？但是兄弟们，当你的业务量上来之后，当你的 upstream 有几千个的时候，当你一天要变更几百次配置的时候，这个 reload 就是个定时炸弹。我之前遇到过一个坑，Nginx 在高并发下 reload，新的 worker 进程起来了，旧的 worker 还在处理长连接，这时候系统负载会瞬间飙升，甚至导致这一瞬间的请求处理延迟极高，客户端直接超时。那种感觉，就像是给一辆正在高速公路上飞奔的赛车换轮胎，太特么吓人了。而且，它是静态的。哪怕你只是想改一个限流的参数，从每秒 100 改成 200，你都得重载进程。这时候你可能会说：“那我用 Kong 啊，Kong 不是动态的吗？”没错，Kong 是个好东西，也是基于 OpenResty 搞的。但是当年我用 Kong 的时候，最让我头疼的是它那个数据库依赖。Kong 默认很多配置是扔在 PostgreSQL 或者 Cassandra 里的。我就想问问，做一个网关，为啥要我维护一个关系型数据库？一旦 DB 挂了，或者 DB 响应慢了，网关就跟着抖。虽然后来 Kong 也有了 DB-less 模式，但那套架构对我来说，还是重。这就轮到 APISIX 出场了。APISIX 到底是什么？用官方的话说，它是一个动态、实时、高性能的 API 网关。用我的话说，它就是一个“甚至不需要你 reload，就能随意控制流量、随意插拔插件的超级 Nginx”。它底子还是 Nginx（确切地说是 OpenResty），所以性能上你完全不用担心，Nginx 能扛多少，它基本就能扛多少。甚至在某些场景下，因为它的路由算法优化得好，性能比原生的 Nginx 配置还要猛。但它最牛逼的地方在于两个字：动态。它是怎么做到的？这就得提它的架构了。APISIX 放弃了传统的数据库，转而拥抱了 etcd。做 K8s 的兄弟对 etcd 肯定不陌生，它是 K8s 的大脑。APISIX 选 etcd 做配置中心简直是神来之笔。为什么？快：etcd 是基于 Raft 协议的，数据一致性强，而且对这种 KV 类型的配置读取速度极快。推送机制：这是重点！Nginx 读取配置是启动时读文件的，而 APISIX 是通过 etcd 的 watch 机制。一旦你在 etcd 里改了配置（或者通过 APISIX 的 Admin API 改了配置），etcd 会瞬间把变更推送到所有的 APISIX 节点。整个过程毫秒级，而且是热更新。不需要 nginx -s reload！不需要 nginx -s reload！不需要 nginx -s reload！重要的事情说三遍。你的长连接不会断，你的业务没有任何感知，配置就生效了。安装教程APISIX 可以借助 quickstart 脚本快速安装并启动：curl -sL https://run.api7.ai/apisix/quickstart | sh该命令启动 apisix-quickstart 和 etcd 两个容器，APISIX 使用 etcd 保存和同步配置。APISIX 和 etcd 容器使用 Docker 的 host 网络模式，因此可以从本地直接访问。如果一切顺利，将输出如下信息：✔ APISIX is ready!验证你可以通过 curl 来访问正在运行的 APISIX 实例。比…

我就输了个ls，Linux底层居然背着我干了这么多事？
type: item
date: 2025-12-03T14:54:14+00:00
description: 大家好，我是悠悠。昨晚熬大夜，盯着黑漆漆的屏幕发呆。手里捧着那杯续命的冰美式，眼神空洞地看着终端里那个闪烁的光标。不知道你们有没有过这种时刻，脑子里突然蹦出一个极其无聊但又细思极恐的问题：我特么就在键盘上敲了个 l，又敲了个 s，然后回车，这屏幕上怎么就出来文件列表了？这事儿看着简单得令人发指，对吧？连刚才路过的保洁阿姨可能都知道 ls 是干啥的。但是，兄弟们，咱们干技术的不能只看表面。这就好比你按了一下冲水马桶，水哗啦流走了，你觉得挺自然，但地底下那复杂的管道系统、化粪池处理逻辑、市政管网压力平衡，那可是一套庞大的工程。Linux 也是一样。这一个简简单单的 ls，背后涉及了从硬件中断、内核调度、进程复制、文件系统读取到显卡渲染等一系列骚操作。那一瞬间，CPU 跑过的指令数可能比你这辈子吃过的米粒还多。今天反正也没啥故障，咱们就闲下来，哪怕是钻牛角尖也好，把这层窗户纸捅破，看看当我们敲下 ls 的时候，Linux 到底在底下偷偷摸摸干了啥。一、 那个并不简单的“连接”咱先别急着敲命令，故事得从你连上服务器那一刻说起。你坐在工位上，用着 SecureCRT 或者 Xshell，或者像我一样装X用 iTerm2，连上了一台几千公里外的 CentOS 服务器。这时候，服务器上的 sshd 守护进程（daemon）正百无聊赖地监听着 22 端口。突然，你的连接请求像个愣头青一样撞了进来。sshd 一看有客到，立马精神了，它不会亲自接待你，毕竟它还得在门口站岗接客。于是，它通过 fork() 系统调用，生了个孩子（子进程）。这个子进程就是专门为你服务的。接下来的加密协商、密钥交换这堆烂事儿我就不展开了，那是密码学的范畴。重点来了，认证通过后，这个 sshd 子进程还得干个体力活：它得给你准备环境。它会去读 /etc/passwd，看看你这小子到底是用 bash 还是 zsh，还是哪个反人类的 sh。通常咱们都是 /bin/bash 对吧？于是，sshd 子进程再次变身，调用 execve()，把自己脑子里的代码替换成了 /bin/bash 的代码。这时候，你屏幕上那个黑框框的背后，其实跑的就是一个 Bash 进程了。这个 Bash 刚醒过来，起床气还挺大，它得穿衣服洗漱。它会疯狂地去读一堆配置文件：/etc/profile、~/.bash_profile、~/.bashrc、/etc/bashrc……咱们平时给服务器配环境变量，搞得乱七八糟的，就是这时候生效的。如果这时候哪个文件里写了个 sleep 10，你登录的时候就得卡在那儿怀疑人生。最后，Bash 准备好了，它调用 write() 系统调用，向你的屏幕输出了一个提示符：[root@localhost ~]#然后，它调用 read()，阻塞在那里，静静地等着你临幸。二、 指尖的舞蹈：从键盘到内核现在，你的手指悬在键盘上方，准备敲下 l。当你按下 'l' 键的那一微秒，键盘内部的电路闭合，产生了一个扫描码（Scan Code）。键盘控制器一看，哟，来活了，立马向 CPU 发送一个硬件中断。CPU 正忙着算别的进程的圆周率呢，被这中断一搞，不得不停下手里的活，去执行键盘中断处理程序。操作系统内核（Kernel）接管了现场，它从端口读取扫描码，把它转换成 ASCII 码的 'l'。但这还没完。这个 'l' 现在还在内核的输入缓冲区里。你的 SSH 客户端（比如 Xshell）通过网络把这个字符发给了服务器。服务器网卡收到包，又是一顿中断处理，内核把数据包拆开，扔给了伪终端主设备（PTY Master）。这时候，Bash 进程之前不是在 read() 那儿傻等吗？内核一看，有数据来了，立马把 Bash 叫醒：“别睡了，起来干活！”Bash 醒过来，读到了这个 'l'。注意，这时候屏幕上还没显示 'l' 呢！Bash 拿到 'l' 后，它得决定怎么办。通常情况下，它会把这个字符“回显”给你。也就是说，Bash 又调用 write() 把 'l' 发回给你的 SSH 客户端，你的屏幕上这才出现了一个字母 'l'。这就是为什么有时候网络卡顿，你敲了半天键盘，屏幕上没反应。因为那个字符得去服务器溜达一圈再回来，你才能看见。同理，你敲下 's'，也是这么个流程。三、 回车那一刻：解析与查找当你按下回车键（Enter）的时候，那才是高潮的开始。Bash 读到了换行符 \n，它知道，这哥们儿输入完了，该我干活了。首先，Bash 会把你输入的字符串 ls 拿来进行切割和分析。这叫词法分析。它得搞清楚，哪部分是命令，哪部分是参数，哪部分是选项。对于 ls 这种简单命令，没啥好切的。但 Bash 也是个心机boy，它得先查查户口：是不是绝对路径？ 你输的不是 /bin/ls，所以它得接着找。是不是别名（Al…

踩坑无数！Linux命令直接跑和crontab定时跑，差别竟然这么大？
type: item
date: 2025-12-02T14:48:30+00:00
description: 上周三凌晨两点，我被电话吵醒了。运维群里炸了锅，说数据备份脚本又没执行成功。我迷迷糊糊爬起来一看，脚本明明手动跑得好好的啊，怎么放到crontab里就不行了呢？这种事情遇到的次数多了，我发现很多人都有这个困惑。明明在终端里敲命令执行得好好的，结果写到定时任务里就各种报错、各种不生效。今天就跟大家聊聊这个话题，把我这些年踩过的坑都给你们讲清楚。环境变量这个大坑说实话，90%的crontab问题都出在环境变量上。你在终端里直接执行命令的时候，用的是你登录shell的完整环境，PATH、HOME、USER这些变量都是配置好的。但crontab呢？它运行的时候环境变量少得可怜。我记得有次写了个Python脚本，里面用到了虚拟环境。手动执行的时候：python /home/ops/backup.py一点问题都没有，因为我的.bashrc里配置了Python的路径，还激活了虚拟环境。结果放到crontab里：0 2 * * * python /home/ops/backup.py死活不行！后来才发现，crontab执行的时候，它压根不知道python在哪儿，PATH里只有/usr/bin和/bin这几个基础路径。解决办法其实也简单，要么在crontab里写绝对路径：0 2 * * * /usr/local/bin/python3 /home/ops/backup.py要么在脚本开头就把环境变量加载进来：#!/bin/bash source /etc/profile source ~/.bashrc /usr/local/bin/python3 /home/ops/backup.py不过这里有个细节，crontab默认的PATH通常是：/usr/bin:/bin你可以在crontab文件开头自己定义：PATH=/usr/local/bin:/usr/bin:/bin SHELL=/bin/bash 0 2 * * * python /home/ops/backup.py工作目录的问题这个坑我也踩过。有个脚本里用了相对路径读取配置文件：with open('config.ini', 'r') as f: config = f.read()手动在脚本所在目录执行，当然没问题。但crontab执行的时候，默认工作目录是用户的HOME目录，不是脚本所在的目录！所以它找不到config.ini文件。当时排查这个问题花了我半个小时，因为日志里只显示"FileNotFoundError"，我还以为是权限问题呢。后来才反应过来是路径的事。有两种解决方案，一种是在crontab里先cd到脚本目录：0 2 * * * cd /home/ops && python backup.py另一种是在脚本里获取脚本所在目录，然后切换过去：import os script_dir = os.path.dirname(os.path.abspath(__file__)) os.chdir(script_dir)我个人更喜欢后者，因为这样脚本的可移植性更好。输出重定向和日志直接在终端执行命令，所有输出都会打印到屏幕上，你能立刻看到结果。但crontab执行的时候，如果你不做任何处理，输出会通过邮件发送给用户。问题是很多服务器根本没配置邮件服务，这些输出就直接丢失了。我见过太多次脚本报错了，但因为没有日志记录，根本不知道哪里出了问题。所以我现在写定时任务，一定会加上输出重定向：0 2 * * * /home/ops/backup.sh >> /var/log/backup.log 2>&1这里的2>&1是把标准错误也重定向到标准输出，这样错误信息也能记录到日志文件里。有时候你不想保留历史日志，只想看最新一次的执行结果：0 2 * * * /home/ops/backup.sh > /var/log/backup.log 2>&1用单个>就会覆盖之前的内容。还有个技巧，如果你完全不想要输出：0 2 * * * /home/ops/backup.sh > /dev/null 2>&1不过我不太建议这么做，万一出问题了连个日志都没有。Shell环境的差异这个问题比较隐蔽。你在终端里执行命令，用的可能是bash、zsh或者其他shell，而且是交互式shell。但crontab执行的时候，用的是非交互式的sh或者bash。交互式shell和非交互式shell加载的配置文件是不一样的。比如bash，交互式登录shell会读取/etc/profile、~/.bash_profile、~/.bash_login、~/.profile这些文件，但非交互式shell只会读取~/.bashrc（而且还得看情况）。我之前有个脚本用到了alias定义的命令别名，手动执行没问题，crontab里就报"c…

Linux运维神器？抛弃黑窗口，这款高颜值Web管理工具让我真香了！
type: item
date: 2025-12-01T15:54:27+00:00
description: 今天咱不聊那些高大上的云原生架构，也不扯什么微服务治理的深奥理论，咱聊点接地气的，聊点能让你早点下班、少敲两个命令的实实在在的东西。事情是这样的。前两天，我也忘了是周几，反正是个想摸鱼的下午。有个刚入行的小兄弟跑来问我：“哥，那个LVM扩容怎么搞啊？我不小心把vg给弄没了……” 我当时心里就一万只羊驼奔腾而过，但看着他那无辜又惊恐的眼神，我只能叹口气，打开终端帮他收拾烂摊子。那时候我就在想，Linux 这玩意儿，对新手确实不太友好。满屏幕的黑底白字，敲错一个字符可能就是删库跑路。虽说咱们做运维的，敲命令是基本功，要是连个 ls 都要想半天那确实说不过去。但是！咱们能不能稍微让自己过得舒服点？是不是所有的操作都必须在那一行行的代码里死磕？以前我也觉得，真正的 Linux 玩家就该住在 Shell 里，用 GUI（图形界面）那是“弱鸡”的表现。直到我遇到了 Cockpit。说真的，这玩意儿彻底改变了我对 Linux 管理工具的看法。以前那些 Webmin 啥的，界面丑得像上个世纪的产物，而且还重，装上去系统负载先高一截。但 Cockpit 不一样，它是红帽（Red Hat）亲生的，现在 CentOS 8、RHEL 8 以后基本都原生自带了，就冲这“皇族”血统，咱也得瞧瞧不是？这篇文有点长，但我保证都是干货，咱们一边唠一边看，这东西到底神在哪。咱们先搞清楚，Cockpit 到底是啥？官方文档：https://cockpit-project.org/running.html简单说，它就是一个通过浏览器管理 Linux 服务器的 Web 界面。别急着喷，“切，不就是个网页版 SSH 嘛”。哎，还真不完全是。Cockpit 最牛的地方在于它的轻量级和零依赖感。很多管理工具，你装上它，它就在后台起一堆进程，在那疯狂吃你的内存，还要接管你的配置文件，一旦它挂了，或者你不想用它了，卸载的时候能把你系统搞崩。Cockpit 不是这样的。它其实就是个皮。它直接调用系统的 API（主要是 Systemd 和 D-Bus）。这就意味着，你在 Cockpit 里点了一个“重启服务”，实际上它就是在后台帮你执行了 systemctl restart xxx。你在终端里手动改了网络配置，Cockpit 界面上立马就能同步更新，因为它俩看的是同一个底层数据。这就像是你既可以用遥控器换台（Cockpit），也可以走到电视机前按按钮换台（命令行），这两者不冲突。这就是我最喜欢它的地方——它不绑架我的系统。装它，比煮泡面还简单咱都不用怎么教，但为了文章完整性，我还是得啰嗦两句。如果你用的是 CentOS 7 之后的版本，或者 Ubuntu 20.04+，这货基本都在官方源里躺着呢。我这边用的是debain13：. /etc/os-release echo "deb http://deb.debian.org/debian ${VERSION_CODENAME}-backports main" > \ sudo tee /etc/apt/sources.list.d/backports.list sudo apt update apt install cockpit完了。就这两行。注意啊，这里启动的是 cockpit.socket。这又是一个细节！平时没人访问的时候，它基本不占资源，只有当你浏览器发起连接请求的时候，Systemd 才会把它唤醒。这设计，绝了，省资源省到家了。在 Ubuntu 上也差不多，apt install cockpit 完事。装好之后，你拿个浏览器，输入 https://IP:9090。这时候浏览器会弹个大红叉，说证书不安全。那是肯定的，自签名的证书嘛，点“高级” -> “继续访问”就行。然后你就看到了登录界面，用你系统的 root 账号或者普通账号登进去。第一眼：这颜值，我爱了登进去之后，那个仪表盘（Dashboard）是真的清爽。左边一列导航栏，右边是实时的 CPU、内存、网络、磁盘 I/O 的波形图。这图表不是那种卡顿的静态图，是实时流动的，看着那个 CPU 曲线在那跳动，说实话，有一种莫名的治愈感。特别是当服务器负载高的时候，你一眼就能看出是 CPU 爆了还是内存炸了，不用再去敲 top 然后眯着眼睛找那一行跳动的数字。而且它顶部有个红条或者是蓝条，系统如果有问题，比如有服务挂了，右上角立马给你亮个感叹号，点进去就能直接看到是哪个服务在作妖。核心功能大起底：它能干啥？光好看没用，咱得看疗效。1. 存储管理：新手的救星，老手的福音这是我最推崇的功能，没有之一。Linux 的存储管理对很多人来说是个噩梦。什么物理卷（PV）、卷组（VG）、逻辑卷（LV），还有文件系统格式化、挂载点……这一套流程下来，老手有时候手滑都能敲错，更别说新手了。在 …

NLB WebSocket 连接问题排查与解决方案
type: item
date: 2025-11-29T01:48:00+00:00
description: 问题背景需要通过Network Load Balancer (NLB) 支持WebSocket连接，但在配置过程中遇到连接失败问题。环境信息协议: WebSocket (OCPP 1.6)后端服务端口: 9521NLB监听端口: 80NLB 创建步骤（AWS 控制台）1. 创建目标组进入 EC2 控制台 → 左侧菜单 目标组 (Target Groups)点击 创建目标组 (Create target group)配置基本信息：目标类型: 实例 (Instances)目标组名称: xxx-nlb-tg协议: TCP端口: 9521 ⚠️ 注意：必须是后端服务实际监听的端口VPC: 选择 vpc协议版本: 默认配置健康检查：健康检查协议: TCP ⚠️ 重要：WebSocket服务器使用TCP检查健康检查间隔: 30秒健康阈值: 3不健康阈值: 3点击 下一步 (Next)注册目标：选择实例端口: 9521点击 包含为待处理项 (Include as pending below)点击 创建目标组 (Create target group)2. 创建 Network Load Balancer进入 EC2 控制台 → 左侧菜单 负载均衡器 (Load Balancers)点击 创建负载均衡器 (Create load balancer)选择 Network Load Balancer → 点击 创建 (Create)基本配置：负载均衡器名称: test-nlb方案: 面向互联网 (Internet-facing)IP地址类型: IPv4网络映射：VPC: 选择 vpc-xxxxxx可用区:✅ us-east-1d → 子网 subnet-xxxx✅ us-east-1e → 子网 subnet-0xxxxxf18监听器和路由：协议: TCP端口: 80默认操作: 转发至目标组 xxxx-tg`点击 创建负载均衡器 (Create load balancer)记录生成的 DNS名称: xxx-4xxxxxeast-1.amazonaws.com3. 启用跨可用区负载均衡 ⚠️ 重要选择刚创建的 NLB xxxx-nlb点击 属性 (Attributes) 标签点击 编辑 (Edit)找到 跨可用区负载均衡 (Cross-zone load balancing)选择 开启 (On)点击 保存更改 (Save changes)说明: 如果后端实例只部署在一个可用区，必须启用此选项，否则会导致50%的连接失败4. 配置 Route53 DNS 解析进入 Route 53 控制台 → 托管区域 (Hosted zones)选择域名 xxxxxx点击 创建记录 (Create record)配置记录：记录名称: xxxx记录类型: CNAME值: xxxxxxx.amazonaws.com`TTL: 300秒路由策略: 简单路由点击 创建记录 (Create records)5. 验证配置进入目标组 xxxnlb-tg` → 目标 (Targets) 标签确认目标状态为 healthy (可能需要等待1-2分钟)如果状态为 initial 或 unhealthy，检查：端口是否正确健康检查协议是否为 TCP实例安全组是否允许端口问题排查过程问题 1: 端口配置错误现象: 目标健康检查失败，WebSocket连接超时原因:目标组配置端口为 9520实际后端服务监听端口为 9521解决方案（控制台操作）:进入 EC2 控制台 → 目标组 → 选择 xxx-tg点击 目标 (Targets) 标签选中错误端口的目标 → 点击 注销 (Deregister)点击 注册目标 (Register targets)选择实例 i-xxxx端口覆盖: 输入 9521点击 包含为待处理项 → 注册待处理目标问题 2: 健康检查失败现象: 目标状态一直为 initial，无法变为 healthy原因:9521端口是纯WebSocket服务器HTTP健康检查路径 /asdsadasg 返回 404响应: 404 WebSocket Upgrade Failure解决方案（控制台操作）: 修改为TCP健康检查进入 EC2 控制台 → 目标组 → 选择 xxxxnlb-tg点击 健康检查 (Health checks) 标签点击 编辑 (Edit)修改配置：健康检查协议: 选择 TCP删除健康检查路径（TCP不需要）点击 保存更改 (Save changes)等待30-60秒，目标状态应变为 healthy验证:在目标组的 目标 (Targets) 标签查看状态状态应显示为 healthy 且带绿色图标问题 3: 跨可用区连接不稳定现象:NLB有两个IP地址 (x.x.…

SSH 连接故障处理报告
type: item
date: 2025-11-29T01:47:00+00:00
description: 故障概述故障时间: 2025年11月28日 15:12 - 15:27故障服务器: x.x.x.x故障现象: 无法通过 SSH 密钥认证登录 ec2-user 账户错误信息: Permission denied (publickey,gssapi-keyex,gssapi-with-mic)故障排查过程第一阶段：私钥文件权限问题 (15:12-15:14)初始错误:WARNING: UNPROTECTED PRIVATE KEY FILE! Permissions 0644 for 'xxxxx.pem' are too open.问题分析:私钥文件权限为 0644（所有用户可读）SSH 安全机制要求私钥文件仅所有者可访问解决措施:chmod 400 /home/test/xxxx.pem结果: 权限警告消失，但认证仍然失败第二阶段：密钥匹配性验证 (15:15-15:18)排查步骤:确认正确的私钥文件路径验证私钥与服务器公钥是否匹配验证命令:# 本地生成公钥指纹 ssh-keygen -y -f /home/test/xxxx.pem # 服务器端查看授权公钥 cat /home/ec2-user/.ssh/authorized_keys验证结果: 公钥完全匹配，确认私钥正确第三阶段：服务器端配置问题定位 (15:18-15:25)关键发现:通过查看服务器 SSH 日志 (/var/log/secure)，发现两个关键错误：目录权限错误:Authentication refused: bad ownership or modes for directory /home/ec2-user密钥算法不支持:userauth_pubkey: key type ssh-rsa not in PubkeyAcceptedAlgorithms [preauth]根本原因分析原因1：用户主目录权限配置错误问题详情:/home/ec2-user 目录的所有权或权限不符合 SSH 安全要求SSH 要求用户主目录及 .ssh 目录必须有严格的权限控制SSH 权限要求:用户主目录: 755 权限，所有者为用户本人.ssh 目录: 700 权限，所有者为用户本人authorized_keys 文件: 600 权限，所有者为用户本人原因2：SSH 服务器禁用了 RSA 算法问题详情:服务器 SSH 配置中未包含 ssh-rsa 算法客户端使用的是 RSA 类型的密钥新版本 OpenSSH (8.8+) 默认禁用了 SHA-1 相关的 RSA 算法解决方案完整修复命令在服务器端以 root 用户执行：# 1. 修复 ec2-user 主目录权限 chown ec2-user:ec2-user /home/ec2-user chmod 755 /home/ec2-user # 2. 修复 .ssh 目录权限 chmod 700 /home/ec2-user/.ssh chmod 600 /home/ec2-user/.ssh/authorized_keys chown -R ec2-user:ec2-user /home/ec2-user/.ssh # 3. 启用 ssh-rsa 算法支持 echo "PubkeyAcceptedAlgorithms +ssh-rsa" >> /etc/ssh/sshd_config # 4. 重启 SSH 服务 systemctl restart sshd验证结果修复后成功连接：ssh -o IdentitiesOnly=yes -i xxxx.pem ec2-user@x.x.x.x连接成功，问题完全解决。经验总结关键排查方法客户端调试: 使用 ssh -v 参数查看详细连接过程服务器日志: 检查 /var/log/secure 或 journalctl -u sshd 获取服务器端错误权限验证: 系统性检查文件和目录权限密钥验证: 使用 ssh-keygen -y 验证密钥对匹配性常见 SSH 认证失败原因问题类型症状解决方法私钥权限过开放UNPROTECTED PRIVATE KEY FILEchmod 400 私钥文件主目录权限错误bad ownership or modeschmod 755 ~ + chown user:user ~.ssh 目录权限错误Authentication refusedchmod 700 ~/.sshauthorized_keys 权限错误认证失败无明确提示chmod 600 ~/.ssh/authorized_keys密钥算法不支持key type not in PubkeyAcceptedAlgorithms修改 sshd_config 启用算法密钥不匹配认证失败验证并更换正确…

Zabbix 图表字体显示故障修复报告
type: item
date: 2025-11-29T01:46:00+00:00
description: 故障概述故障时间： 2025-11-29 08:55 - 09:09 故障系统： Zabbix 7.4 监控系统 故障现象： Zabbix 图表无法显示坐标轴和图例，仅显示绿色折线图 影响范围： 所有监控图表的可读性受到严重影响 故障等级： 中等（功能性故障，不影响数据采集）故障表现视觉症状图表中只显示绿色折线和网格背景X轴（时间轴）完全缺失Y轴（数值轴）完全缺失图例信息不显示图表标题和说明文字缺失用户影响无法识别监控指标的具体数值无法确定时间范围无法区分多条监控曲线图表失去实际使用价值故障诊断过程第一阶段：初步排查（08:55-09:00）1. 系统日志检查# Zabbix Server 日志 tail -f /var/log/zabbix/zabbix_server.log结果： 仅显示正常的 housekeeper 清理任务，无异常错误2. PHP-FPM 日志检查tail -f /var/log/php-fpm/error.log结果： 显示正常的进程重启，无明显错误3. 字体文件检查ls -la /usr/share/zabbix/assets/fonts/发现：simhei.ttf (9.7 MB) - 存在simfang.ttf (10.5 MB) - 存在NotoSansCJK-Regular.ttc (19.9 MB) - 存在文件权限正常：-rw-r--r--初步结论： 字体文件存在且权限正常，问题不在文件本身第二阶段：深度分析（09:00-09:06）4. Web服务器错误日志分析tail -30 /var/log/nginx/error.log关键发现： 大量 PHP 错误信息PHP Warning: imagettfbbox(): Could not find/open font in /usr/share/zabbix/ui/include/graphs.inc.php on line 533 PHP Warning: imagettftext(): Could not find/open font in /usr/share/zabbix/ui/include/graphs.inc.php on line 486错误频率： 每次加载图表产生数百条相同错误 错误位置： graphs.inc.php 的字体渲染函数5. Zabbix 配置文件检查cat /usr/share/zabbix/ui/include/defines.inc.php | grep -i font配置内容：define('ZBX_FONTPATH', realpath('assets/fonts')); define('ZBX_GRAPH_FONT_NAME', 'simhei'); define('ZBX_FONT_NAME', 'simhei');问题识别： ZBX_FONTPATH 使用了相对路径 realpath('assets/fonts')6. 字体加载机制分析sed -n '480,490p' /usr/share/zabbix/ui/include/graphs.inc.php代码逻辑：$ttf = ZBX_FONTPATH.'/'.ZBX_FONT_NAME.'.ttf'; imagettftext($image, $fontsize, $angle, $x, $y, $color, $ttf, $string);拼接结果： realpath('assets/fonts') + '/' + 'simhei' + '.ttf'第三阶段：根因定位（09:06）核心问题分析问题1：相对路径解析失败realpath('assets/fonts') 在 PHP-FPM 环境下无法正确解析PHP-FPM 的工作目录与 Nginx 的文档根目录不一致导致相对路径返回 false 或空字符串问题2：字体路径拼接错误当 ZBX_FONTPATH 解析失败时最终路径变成：/simhei.ttf 或 simhei.ttfPHP GD 库无法找到字体文件问题3：错误处理缺失Zabbix 代码未对字体加载失败进行有效处理仅产生 PHP Warning，不中断图表生成导致图表显示不完整但不报致命错误解决方案修复步骤步骤1：修改字体路径为绝对路径sudo sed -i "s|define('ZBX_FONTPATH',.*|define('ZBX_FONTPATH', '/usr/share/zabbix/assets/fonts'); // where to search for font|" /usr/share/zabbix/ui/include/defines.inc.php修改前：define('ZBX_FONTPATH', re…

听说你觉得VPN简单？手撸一遍IPsec，咱俩再聊聊人生
type: item
date: 2025-11-27T13:42:23+00:00
description: 前两天有个刚做网络的小兄弟问我：“现在不都流行SSL VPN或者WireGuard了吗？IPsec这种‘老古董’是不是没必要学了？”我当时差点一口老血喷屏幕上。兄弟，你这就好比在说，现在开自动挡的车多了，手动挡的原理就不重要了？IPsec这玩意儿，就像是网络世界的“重型装甲车”，它是笨重，配置是繁琐，但在Site-to-Site（站点对站点）的连接上，它依然是无可撼动的霸主。你看看现在的公有云，AWS也好，阿里云也罢，跟IDC做专线互联备份的时候，哪个推荐的不是IPsec？别看WireGuard现在火得一塌糊涂，真到了复杂的企业级环境，还得是IPsec坐镇。今天咱们就来扒一扒IPsec的底裤，顺便教大家用开源神器StrongSwan搭建一套企业级的VPN。为什么IPsec让人头大？咱们先不谈配置，先聊聊为什么这玩意儿难懂。绝大多数人晕菜，是因为被IKE、ISAKMP、ESP、AH、Phase 1、Phase 2这些缩写给绕晕了。其实，你把IPsec想象成“押镖”。你要把一车黄金（数据包）从北京运到上海。路上有土匪（黑客），你不能直接运。第一种方式：AH（Authentication Header）这就像是在车厢上贴了个封条，上面盖了皇帝的印（签名）。到了上海，一看封条没坏，就知道东西没被换过，也是朝廷发出来的。但是！ 封条只能保证东西没被换，它不加密。黄金还是黄金，土匪在路边能看个精光。而且，最坑爹的是，AH会对整个IP头做签名。大家都知道现在网络到处都是NAT（地址转换），路由器一改IP地址，签名就这就对不上了，封条就“碎”了。所以，在现在的网络环境里，AH基本就是个废柴，很少用。第二种方式：ESP（Encapsulating Security Payload）这个厉害了。它不仅贴封条，还把黄金装进了保险箱（加密）。土匪截了车，只能看到一个铁箱子，里面是啥完全不知道。ESP最骚的操作是“隧道模式”。它不光把数据加密，它还把原来的车牌（原IP头）也给拆下来扔进保险箱，然后在外面挂个新车牌（新IP头）。这就是为什么我们在隧道里抓包，只能看到两个网关之间的公网IP在通信，完全看不到内网IP。那个让人抓狂的协商过程：IKE真正让人掉头发的，是IKE（Internet Key Exchange）。你想啊，两个网关（路由器或服务器）素不相识，怎么建立信任？怎么商量用什么密码？直接在公网上喊：“喂！密码是123456！”那不找死吗？所以得谈判。这个谈判过程就是IKE。第一阶段（Phase 1）：建立管理连接这就像两个外交官见面，先得在一个绝对安全的屋子里坐下来。他们要确认对方身份（Pre-Shared Key预共享密钥，或者证书），然后商量：“咱俩待会怎么聊天？用什么加密算法？”这一步要是通了，就建立了一个叫ISAKMP SA的东西。你可以理解为，外交官的保密电话接通了。这里有个大坑。Phase 1有两种模式：主模式（Main Mode）和野蛮模式（Aggressive Mode）。名字听着挺唬人。主模式慢，一来一回6个包，但是安全，它会隐藏身份信息。野蛮模式快，3个包搞定，但是它会把身份ID明文广播出去。以前为了快，很多老设备喜欢用野蛮模式，现在为了安全，大家基本都切回主模式了。第二阶段（Phase 2）：建立数据连接外交官电话通了（Phase 1成功），现在开始聊正事：怎么运黄金。这时候商量的才是真正用来加密用户数据的密钥（IPsec SA）。这就是所谓的“快速模式”（Quick Mode）。这也是为什么你看日志的时候，经常看到Phase 1过了，Phase 2报错。通常是因为两边的“感兴趣流”（Subnet，也就是你要通的内网网段）没对上。左边说我要通192.168.1.0/24，右边配置写的是192.168.2.0/24，聊不到一块去，崩了。NAT-T：穿越NAT的救命稻草刚才说了，AH过不了NAT。其实ESP过NAT也费劲。因为NAT路由器会修改端口号，而IPsec设计之初是基于IP层的，没端口号这概念（ESP协议号是50）。为了解决这个问题，聪明的工程师搞了个NAT-T（NAT Traversal）。简单说，就是把ESP数据包再包一层UDP头部，通常是用UDP 4500端口。这样NAT路由器一看：“哦，是UDP啊，改端口我会！”于是，IPsec就能在NAT环境里活下来了。敲黑板： 如果你的VPN服务器在防火墙后面，记得防火墙除了开UDP 500（IKE协商用），还得开UDP 4500（NAT穿透用）。好多人配半天不通，就是因为4500端口没开。实战：用StrongSwan搭建从阿里云到本地IDC的隧道理论扯多了容易困，咱们来点实操。Linux下做IPsec，以前有Openswan、Libreswan，但现在StrongSwan…