<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>vpsjq.com</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://vpsjq.com/</id>
  <link href="https://vpsjq.com/" rel="alternate"/>
  <link href="https://vpsjq.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, vpsjq.com</rights>
  <subtitle>分享VPS服务器、Linux系统、IPv6网络、云服务器与网站搭建教程，记录服务器部署、优化、安全配置和实用工具使用经验。</subtitle>
  <title>VPS技巧与Linux运维笔记</title>
  <updated>2026-08-27T14:00:00.000Z</updated>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="XanMod内核" scheme="https://vpsjq.com/tags/XanMod%E5%86%85%E6%A0%B8/"/>
    <category term="BBR3" scheme="https://vpsjq.com/tags/BBR3/"/>
    <content>
      <![CDATA[<span id="more"></span><p>XanMod 内核内置了 BBR3 支持，装好之后理论上不需要额外操作就能用，但实际情况是安装完重启后有时候内核没有切换过去，还在跑原来的系统内核，这时候 BBR3 自然也没有生效。所以安装完之后先确认内核有没有真的切换，再去看 BBR3 的状态，顺序不能反。</p><p>在装 XanMod 之前，先确认你的 VPS 虚拟化方案支不支持自定义内核。KVM 架构的 VPS 一般没有问题，OpenVZ 的 VPS 通常不允许更换内核，装了也没用甚至会出问题。不确定自己 VPS 是什么架构的话，跑一下这条命令：</p><p>```bash<br>systemd-detect-virt<br>```</p><p>输出 <code>kvm</code> 或者 <code>none</code> 的话可以继续，输出 <code>openvz</code> 或者 <code>lxc</code> 的话就不适合装 XanMod。</p><p>XanMod 有几个不同的版本，常见的有 <code>linux-xanmod</code>、<code>linux-xanmod-edge</code> 和 <code>linux-xanmod-lts</code>。edge 版本用的是最新的内核，功能最新但稳定性相对差一点；lts 版本基于长期支持内核，稳定性更好，适合长期运行的服务器；普通版在两者之间。如果 VPS 主要跑代理节点或者网站，lts 版本是比较稳妥的选择，edge 版本适合想尝鲜或者对特定新特性有需求的情况。</p><p>安装完成后重启服务器，重启之后先确认内核有没有切换过去：</p><p>```bash<br>uname -r<br>```</p><p>输出里应该能看到 <code>xanmod</code> 字样，比如 <code>6.x.x-xanmod1</code> 这种格式。如果输出还是原来的系统内核版本，说明引导程序没有切换到 XanMod，需要手动设置默认启动内核。用 <code>grub-set-default</code> 或者直接编辑 <code>/etc/default/grub</code> 把默认内核改成 XanMod 那一条，改完跑一下 <code>update-grub</code> 再重启。</p><p>内核确认切换之后，看一下 BBR3 有没有自动生效：</p><p>```bash<br>sysctl net.ipv4.tcp_congestion_control<br>```</p><p>输出 <code>net.ipv4.tcp_congestion_control = bbr</code> 说明 BBR 已经在跑了。XanMod 装好后 BBR 通常会自动启用，但不一定是 BBR3，取决于内核版本。确认是不是 BBR3 可以用这条命令：</p><p>```bash<br>sysctl net.ipv4.tcp_available_congestion_control<br>```</p><p>如果输出里有 <code>bbr</code> 并且内核版本在 6.x 以上，跑的基本就是 BBR3。如果 BBR 没有自动启用，手动开启：</p><p>```bash<br>echo “net.ipv4.tcp_congestion_control = bbr” &gt;&gt; /etc/sysctl.conf<br>echo “net.core.default_qdisc = fq” &gt;&gt; /etc/sysctl.conf<br>sysctl -p<br>```</p><p>跑完再用 <code>sysctl net.ipv4.tcp_congestion_control</code> 确认一下，输出 <code>bbr</code> 就说明生效了。</p><p>XanMod 搭配 BBR3 的组合在代理节点服务器上用得比较多，跟<a href="https://vpsjq.com/2026/07/28/2026-07-28-002/">XanMod内核从性能优化到实际使用</a>里提到的适用场景一致，如果同时跑着 3x-ui 或者 S-UI 这类面板，BBR3 对多连接场景下的网络吞吐有一定帮助，实际效果还是要看线路本身的质量。更多 VPS 网络优化的思路可以参考<a href="https://vpsjq.com/2026/04/17/2026-04-17-006/">一键cat命令完成vps所有优化</a>。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/27/xanmod-bbr3/</id>
    <link href="https://vpsjq.com/2026/08/27/xanmod-bbr3/"/>
    <published>2026-08-27T14:00:00.000Z</published>
    <summary>XanMod内核安装后开启BBR3的完整流程，包括版本选择、安装后内核验证和BBR3启用方法。</summary>
    <title>XanMod内核搭配BBR3使用教程</title>
    <updated>2026-08-27T14:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps工具" scheme="https://vpsjq.com/categories/vps%E5%B7%A5%E5%85%B7/"/>
    <category term="3x-ui" scheme="https://vpsjq.com/tags/3x-ui/"/>
    <category term="Hysteria2" scheme="https://vpsjq.com/tags/Hysteria2/"/>
    <content>
      <![CDATA[<span id="more"></span><p>Hysteria2 是近几年比较受关注的一个代理协议，基于 QUIC 实现，在高延迟或者丢包率高的网络环境下表现比 TCP 系协议稳定一些。3x-ui 原生支持 Hysteria2，不需要额外安装什么，直接在面板里新建入站就能用。</p><p>在 3x-ui 里，Hysteria2 是一个独立的入站协议，不是在现有 VLESS 或者 VMess 入站里切换的，需要单独新建一个入站。进入面板后点左侧<strong>入站列表</strong>，点右上角<strong>添加入站</strong>，协议那一栏选 Hysteria2，这时候下面的配置项会跟其他协议有所不同。</p><p>端口可以自己填，也可以让面板随机生成一个。如果服务器上已经有其他服务在跑，建议自己指定一个没有被占用的端口，避免冲突。Hysteria2 走的是 UDP，这一点跟 VLESS、VMess 这些 TCP 协议不一样，<strong>防火墙那边要单独放行 UDP 端口</strong>，不是只开 TCP 就够了。这也是客户端连不上时最常见的原因——端口开了但只放行了 TCP，UDP 没放行，Hysteria2 就过不去。</p><p>```bash<br>ufw allow 你的端口号/udp<br>```</p><p>如果服务商有安全组，同样要去控制面板手动加一条 UDP 入站规则，光在系统防火墙放行还不够。</p><p>证书这块，Hysteria2 需要 TLS，面板提供两种方式。一种是上传自己已有的证书文件，填好证书路径和私钥路径就行，适合已经有域名证书的情况。另一种是用自签证书，面板可以直接生成，不需要域名，配置简单，但客户端那边需要关闭证书验证才能连上，不同客户端的叫法不一样，有的叫&quot;跳过证书验证&quot;，有的叫&quot;允许不安全连接&quot;，找到对应选项打开就行。</p><p>如果你已经有域名并且在 3x-ui 里配置过其他节点，证书文件路径一般已经知道了，直接填进来就能复用，不用重新申请。没有域名的话用自签证书也能正常跑，只是客户端配置上要多开一个选项。</p><p>密码那一栏填一个自己设的字符串，这个就是客户端连接时用的认证密码，每个用户可以设不同的密码，跟<a href="https://vpsjq.com/2026/08/27/3x-ui-multi-user/">3x-ui多用户管理</a>里的用户概念类似，只是 Hysteria2 用密码认证而不是 UUID。</p><p>配置填完之后点保存，入站列表里会多出一条 Hysteria2 的记录。点那一行的二维码图标，可以看到这个节点的分享链接，格式是 <code>hy2://</code> 开头的，把链接复制到支持 Hysteria2 的客户端里导入就能用。目前主流客户端里，Clash Meta、sing-box、NekoBox 这些都支持 Hysteria2，v2rayN 的部分版本也支持，导入前确认一下客户端版本。</p><p>客户端那边如果用的是自签证书，除了打开跳过证书验证之外，其他配置跟普通节点没什么区别，服务器地址填 IP 或者域名，端口填刚才设的那个，密码填刚才填的字符串，保存之后直接连就行。</p><p>连不上的时候按这个顺序排查：先确认 UDP 端口有没有在系统防火墙和服务商安全组两边都放行，再确认客户端的证书验证设置跟你用的证书类型匹配，最后看一下面板里这条入站的状态是不是正常启动的。面板安装和基本配置参考<a href="https://vpsjq.com/2026/04/30/2026-04-30-011/">3x-ui安装：MHSanaei版官方脚本与面板配置</a>，里面有端口放行和面板基本操作的说明。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/27/3x-ui-hysteria2/</id>
    <link href="https://vpsjq.com/2026/08/27/3x-ui-hysteria2/"/>
    <published>2026-08-27T12:00:00.000Z</published>
    <summary>在3x-ui面板里新建Hysteria2入站节点的完整流程，包括端口设置、证书配置和客户端连接注意事项。</summary>
    <title>3x-ui配置Hysteria2节点教程</title>
    <updated>2026-08-27T12:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps工具" scheme="https://vpsjq.com/categories/vps%E5%B7%A5%E5%85%B7/"/>
    <category term="3x-ui" scheme="https://vpsjq.com/tags/3x-ui/"/>
    <content>
      <![CDATA[<span id="more"></span><p>3x-ui的多用户不是在一个单独的用户管理页面里统一操作的，而是在每个入站节点下面单独管理。如果你要给同一个节点分配多个用户，进入面板后点左侧的<strong>入站列表</strong>，找到对应的节点，点那一行最右边的用户图标就能进入这个节点的用户列表。</p><p>刚装完面板的时候，每个入站节点默认只有一个用户。点<strong>添加用户</strong>会弹出一个表单，里面有几个字段需要填。</p><p>客户端名字默认是一串随机字符，比如 <code>a1b2c3d4</code>，这个字段可以自己改成好认的名字，比如设备名或者用途。改不改都不影响节点正常连接，只是方便自己管理。用户多了之后，列表里一眼就能看出来哪个是自己手机在用、哪个是给朋友开的，不用对着一堆随机字符猜。</p><p>UUID 那一栏默认是自动生成的，一般不需要动，每个用户的 UUID 都不一样，这也是区分不同用户流量的依据。</p><p>流量限制那栏填 GB 数字，比如填 50 就是限制这个用户总共能用 50GB。默认是空的，也就是不限流量。如果要给朋友开节点但不想让他用太多，在这里填个数字就行。流量超限之后用户不会立刻断线，而是在面板的用户列表里显示超限状态，这时候这个用户的连接就无法使用了，需要你手动在面板里重置流量或者调整限额，才能恢复正常。</p><p>到期时间填日期格式，比如 <code>2026-12-31</code>，到了这个日期之后这个用户自动失效。默认也是空的，也就是永不过期。给朋友开节点的时候可以设个到期时间，省得以后忘了还在跑。</p><p>添加完之后回到用户列表，每个用户那一行右边有一个二维码图标，点开可以看到这个用户专属的订阅链接，每个用户的链接是独立的，互不影响。把链接直接发给对方就能用，不需要对方扫码，复制链接粘贴到客户端的订阅地址里导入就行。如果对方用的是手机，扫二维码也可以，两种方式都支持。</p><p>需要注意的是，每个用户的链接只包含这一个用户的配置，不会把同一个入站下其他用户的信息带进去。所以如果你给三个朋友各开了一个用户，发给他们的是三条不同的链接，互相之间看不到对方的配置。</p><p>如果要删掉某个用户，直接在列表里点那一行的删除按钮，不会影响同一个入站下的其他用户，节点本身也不需要重启，删完立刻生效。删掉之后对方的链接就直接失效了，不需要额外操作。</p><p>用户多了之后有几个管理习惯比较省事：名字按照用途或者对象来命名，比如 <code>phone</code>、<code>laptop</code>、<code>friend-a</code> 这种，比随机字符好找很多。流量和到期时间按需要设，不是每个用户都要限制，自己用的一般不用管，给别人开的建议设个到期时间。定期去用户列表看一眼，超限的、过期的及时清理，面板不会自动删除这些用户，需要手动处理。</p><p>如果同一台服务器要同时跑多个协议，比如 VLESS Reality 和 Hysteria2 分开两个入站，每个入站下面可以各自独立管理用户，互相不影响。不同入站的用户链接格式不一样，不能混用。</p><p>面板安装和基本配置可以参考这篇：<a href="https://vpsjq.com/2026/04/30/2026-04-30-011/">3x-ui安装：MHSanaei版官方脚本与面板配置</a></p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/27/3x-ui-multi-user/</id>
    <link href="https://vpsjq.com/2026/08/27/3x-ui-multi-user/"/>
    <published>2026-08-27T10:00:00.000Z</published>
    <summary>3x-ui面板在入站列表里给单个节点添加多个用户的方法，包括客户端名字设置、流量限制、到期时间和用户链接导出。</summary>
    <title>3x-ui多用户管理：在入站列表里添加和管理用户</title>
    <updated>2026-08-27T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="文件完整性校验" scheme="https://vpsjq.com/tags/%E6%96%87%E4%BB%B6%E5%AE%8C%E6%95%B4%E6%80%A7%E6%A0%A1%E9%AA%8C/"/>
    <content>
      <![CDATA[<span id="more"></span><p>从网上下载一个ISO镜像或者软件包，尤其是从镜像站、种子网络这类不完全受控的渠道拿到的文件，装到系统里之前多一步验证，能避免装了个被人动过手脚的版本。很多教程只教到<code>sha256sum</code>这一步就停了，其实这只完成了验证工作的一半。</p><h2 id="sha256sum验证的是-完整-，不是-真实">sha256sum验证的是&quot;完整&quot;，不是&quot;真实&quot;</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sha256sum</span> 下载的文件名</span><br></pre></td></tr></table></figure><p>跑出来的这一串哈希值，如果跟官方公布的校验值对上了，说明<strong>这个文件在传输过程中没有损坏或者被篡改</strong>——但这里有个容易被忽略的漏洞：<strong>如果攻击者连官方公布的校验值本身也一起篡改了</strong>（比如你是从一个被劫持的镜像站同时拿到了文件和它对应的SHA256SUMS文件），两者对得上号，你验证得再仔细也是白搭，因为参照物本身就是假的。单纯的哈希校验，验证的是&quot;文件有没有在传输中损坏&quot;，回答不了&quot;这个文件到底是不是官方发布的原版&quot;这个更关键的问题。</p><h2 id="GPG签名验证，才是真正在确认-来源">GPG签名验证，才是真正在确认&quot;来源&quot;</h2><p>想同时验证完整性和真实性，得靠GPG签名这一层。以Ubuntu镜像为例，官方在发布ISO的同时，还会提供一份对应的<code>.gpg</code>签名文件，这份签名是用官方私钥对校验文件本身签的名，只有真正的官方才能生成这个签名。</p><p>先导入官方的公钥：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 官方公钥ID</span><br></pre></td></tr></table></figure><p>再用这个公钥验证签名文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">gpg --verify SHA256SUMS.gpg SHA256SUMS</span><br></pre></td></tr></table></figure><p>看到<code>Good signature from</code>这行，说明这份校验文件确实是官方私钥签过名的，不是中途被人偷梁换柱换成假的。这一步验证通过之后，再拿这份可信的校验文件去比对你下载的实际文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sha256sum</span> -c &lt;(grep 你的文件名 SHA256SUMS)</span><br></pre></td></tr></table></figure><p>两步叠加起来，才算是既确认了&quot;文件没坏&quot;，又确认了&quot;文件确实来自官方&quot;，单独任何一步都不能完全说明问题。</p><h2 id="平时这么严格的验证，什么时候真正用得上">平时这么严格的验证，什么时候真正用得上</h2><p>日常从大厂官方渠道装个常规软件包，被篡改的风险本来就不高，没必要每次都走这一整套流程。但遇到这几种场景，多花两分钟验证一下很值——从BT种子网络下载的镜像文件、从个人维护的第三方软件源添加的包、或者干脆是从来源不太确定的镜像站下载的东西。</p><h2 id="一个容易忽略的细节：公钥本身也得从可信渠道拿">一个容易忽略的细节：公钥本身也得从可信渠道拿</h2><p>验证流程里还有个环节容易被跳过——<code>gpg --recv-keys</code>这一步，是从密钥服务器拉取官方公钥，如果这个公钥本身就是假的（比如从一个不可信的地方手动导入了一个伪造的公钥），后面所有的验证步骤都会&quot;验证成功&quot;，但验证的是攻击者伪造的一整套签名体系，看着流程走完了，结果照样是假的。密钥服务器本身通常是分布式、多方维护的，比单一站点可信度更高，但如果条件允许，最好还是从软件官方网站直接确认一下公钥指纹（fingerprint）对不对得上，而不是盲目信任密钥服务器返回的第一个结果。</p><h2 id="顺带一提">顺带一提</h2><p>之前写<a href="https://vpsjq.com/2026/04/30/2026-04-30-004/">Helium浏览器</a>那篇提到过&quot;只从官方GitHub下载，避免被篡改的安装包&quot;，这条建议背后的具体操作方法，其实就是这篇讲的这一套——不管是给服务器装软件包，还是给手机装一个第三方浏览器APK，验证来源可信这件事的思路是通用的。如果是在排查服务器是否已经被入侵，类似的多方交叉验证思路，可以参考<a href="https://vpsjq.com/2026/08/15/vps-brute-force-check/">VPS被暴力破解怎么查有没有被入侵</a>那篇。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/26/linux-file-integrity-verification/</id>
    <link href="https://vpsjq.com/2026/08/26/linux-file-integrity-verification/"/>
    <published>2026-08-26T10:00:00.000Z</published>
    <summary>sha256sum只能验证文件没损坏，验证不了文件是不是真的来自官方，想两者都确认，得靠GPG签名验证这一步。</summary>
    <title>下载的Linux软件包，怎么确认没被人动过手脚</title>
    <updated>2026-08-26T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps工具" scheme="https://vpsjq.com/categories/vps%E5%B7%A5%E5%85%B7/"/>
    <category term="Fail2ban验证" scheme="https://vpsjq.com/tags/Fail2ban%E9%AA%8C%E8%AF%81/"/>
    <content>
      <![CDATA[<span id="more"></span><p>Fail2ban装完，<code>systemctl status fail2ban</code>显示绿色的<code>active (running)</code>，看着一切正常，从此就不再管它，默认它一直在尽职尽责地拦截暴力破解。这个假设有风险——<strong>Fail2ban显示&quot;运行正常&quot;、甚至显示&quot;已封禁某个IP&quot;，都不代表它真的在起作用</strong>，一些常见的配置疏漏会让它变成一个只会报告好消息、实际什么都没拦住的摆设。</p><h2 id="改了SSH端口，但没同步告诉Fail2ban">改了SSH端口，但没同步告诉Fail2ban</h2><p>这是最容易被忽略、后果也最严重的一个坑。很多人图安全，把SSH默认端口从22改成别的数字，却忘了Fail2ban的对应规则里，<code>action</code>配置那一行<strong>写死了<code>port=ssh</code></strong>（对应端口22）。攻击者其实是冲着你改过的新端口来的，Fail2ban监控日志确实检测到了暴力破解、确实执行了&quot;封禁&quot;动作，<code>fail2ban-client status sshd</code>看着显示封禁成功——但它实际下发的防火墙规则，拦的是22端口，跟攻击者真正在用的端口完全不搭边，等于什么都没拦住，只是自己心理上以为安全了。</p><p>改了SSH端口，记得同步改一下<code>jail.local</code>里对应规则的端口设置：</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[sshd]</span></span><br><span class="line"><span class="attr">enabled</span> = <span class="literal">true</span></span><br><span class="line"><span class="attr">port</span> = 你改过的实际端口号</span><br><span class="line"><span class="attr">logpath</span> = /var/log/auth.log</span><br></pre></td></tr></table></figure><h2 id="系统时区变过，日志时间跟Fail2ban对不上">系统时区变过，日志时间跟Fail2ban对不上</h2><p>如果服务器中途调整过时区（或者之前排查过NTP同步问题动过手脚），日志里记录的时间戳如果跟Fail2ban自己感知的当前时间对不上，会导致它在判断&quot;某个IP在规定时间窗口内失败了几次&quot;这个逻辑上完全错乱，实际效果就是日志明明在报警，Fail2ban却始终判断&quot;还没到触发阈值&quot;，安静地什么都不做。这跟之前写的<a href="https://vpsjq.com/2026/08/23/vps-ntp-time-drift/">VPS的时间为什么会跑偏</a>是同一类隐患的连锁反应——时间本身出问题，牵连的往往不只是日志时间戳好不好看，依赖时间判断逻辑的安全工具会跟着一起失灵。</p><h2 id="封禁动作报告成功，防火墙规则却压根没插进去">封禁动作报告成功，防火墙规则却压根没插进去</h2><p>还有一种更隐蔽的失效——<code>fail2ban-client status sshd</code>显示某个IP已经在封禁列表里，回头用<code>iptables -L</code>一查，对应的拦截链条根本不存在，日志里能翻到<code>No chain/target/match by that name</code>这类报错。这种情况下Fail2ban自己以为完成了任务，实际防火墙层面什么规则都没真正生效，攻击者畅通无阻。定期交叉验证一下两边的状态是否一致，比只看Fail2ban自己的汇报靠谱：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">fail2ban-client status sshd</span><br><span class="line">iptables -L -n | grep f2b</span><br></pre></td></tr></table></figure><p>两边如果对不上号，说明封禁动作在防火墙这一层出了问题，需要单独排查。</p><h2 id="验证Fail2ban到底有没有在正常读日志">验证Fail2ban到底有没有在正常读日志</h2><p>不用干等着真实攻击发生才能验证，直接拿现有日志测一下正则匹配规则能不能正常识别：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf</span><br></pre></td></tr></table></figure><p>如果匹配数量是0，问题可能出在日志格式跟预设的正则对不上——这种情况在用了Docker之类会给日志额外插入字段的环境下尤其常见，日志格式跟原生系统的默认格式有出入，Fail2ban的默认过滤规则可能压根认不出来。</p><h2 id="顺带一提">顺带一提</h2><p>这种&quot;工具装了、开关打开了、看着一切正常，实际却完全没起作用&quot;的情况，跟之前写的<a href="https://vpsjq.com/2026/08/20/linux-ulimit-not-working/">ulimit设置了却不生效</a>是同一种性质的陷阱——配置本身可能一个字都没写错，但环境或者关联的另一项设置对不上，整套东西就变成了摆设。装完安全工具，花两分钟做一次真实验证，比事后翻日志才发现从来没生效过要踏实得多。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/25/fail2ban-verify-working/</id>
    <link href="https://vpsjq.com/2026/08/25/fail2ban-verify-working/"/>
    <published>2026-08-25T20:00:00.000Z</published>
    <summary>Fail2ban装上就以为高枕无忧，实际可能一次攻击都没真正拦住，几个常见的静默失效原因和验证方法。</summary>
    <title>装了Fail2ban，怎么确认它真的在拦截攻击</title>
    <updated>2026-08-25T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps工具" scheme="https://vpsjq.com/categories/vps%E5%B7%A5%E5%85%B7/"/>
    <category term="Termux保活" scheme="https://vpsjq.com/tags/Termux%E4%BF%9D%E6%B4%BB/"/>
    <content>
      <![CDATA[<span id="more"></span><p>用手机Termux连着VPS跑一个耗时的任务，切个微信回来一看，会话断了，任务也没了，跟没跑过一样。这不是Termux不稳定，是安卓系统的省电机制在暗中动手——手机为了省电，会主动清理它认为&quot;不重要&quot;的后台进程，Termux作为一个终端应用，很容易被系统划进这个清理名单。</p><h2 id="第一层防护：唤醒锁-电池白名单">第一层防护：唤醒锁+电池白名单</h2><p>最基础的两步，跑长任务之前先做好：</p><p>在Termux菜单里点&quot;Acquire Wakelock&quot;，或者直接敲命令：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">termux-wake-lock</span><br></pre></td></tr></table></figure><p>这个操作能阻止CPU进入深度睡眠，任务跑完之后记得释放：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">termux-wake-unlock</span><br></pre></td></tr></table></figure><p>同时去手机系统设置里，把Termux的电池优化策略改成&quot;不受限制&quot;（不同手机厂商叫法不一样，可能是&quot;无限制&quot;&quot;允许后台活动&quot;之类），避免系统按默认的激进策略清理它。</p><h2 id="第二层防护：把任务放进tmux，而不是直接跑在主会话里">第二层防护：把任务放进tmux，而不是直接跑在主会话里</h2><p>单纯开着wake-lock还不够稳妥，配合<a href="https://vpsjq.com/2026/08/15/vps-ssh-disconnect-process-killed/">之前写过的SSH断连处理方法</a>，把任务放进<code>tmux</code>会话里跑，就算Termux本身被系统重启或者意外重开，只要底层进程没被真正杀死，重新连回去还能接上：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">tmux new -s 任务名</span><br></pre></td></tr></table></figure><h2 id="安卓12-13上，做完前两步可能还是不够">安卓12/13上，做完前两步可能还是不够</h2><p>这是最容易被忽略、也最让人困惑的一层——<strong>安卓12和13新增了一个叫&quot;Phantom Process Killer&quot;（幽灵进程终结者）的机制</strong>，就算唤醒锁开着、电池优化也关掉了，这套新机制依然可能把你的后台会话干掉，因为它管的是另一套判定逻辑，不完全受传统的电池优化设置约束。</p><p>遇到这种情况，需要通过ADB命令单独放宽这个限制（需要电脑连接手机执行，或者用Termux本身的ADB无线调试功能）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">adb shell <span class="string">&quot;/system/bin/device_config put activity_manager max_phantom_processes 2147483647&quot;</span></span><br></pre></td></tr></table></figure><p>这条命令把系统允许的&quot;幽灵进程&quot;数量上限调到接近无限大，绕开这套新的杀后台机制。</p><h2 id="一个容易被忽视的反向陷阱">一个容易被忽视的反向陷阱</h2><p>设置好前面这些之后，<strong>千万别手动开启手机的&quot;省电模式&quot;</strong>——这个模式会直接覆盖掉唤醒锁的效果，不管你之前设置得多仔细，一旦手动打开省电模式，后台任务照样立刻被杀，等于前功尽弃。</p><p>如果想让tmux会话在手机重启之后也能自动恢复，可以研究一下<code>tmux-resurrect</code>这个插件，能把会话状态保存下来，重启后自动还原。</p><h2 id="顺带一提">顺带一提</h2><p>这种&quot;系统默认倾向于清理你没有特别保护的东西&quot;的思路，跟Linux里<a href="https://vpsjq.com/2026/08/19/linux-kill-9-cannot-kill-process/">kill -9都杀不死一个进程</a>那篇讲的其实是完全相反的情况——一个是&quot;太容易被杀掉&quot;，一个是&quot;怎么杀都杀不掉&quot;，两种极端背后都是系统在某种机制下的默认行为，了解清楚各自的逻辑，才知道该往哪个方向去应对。如果手机上除了Termux还跑着<a href="https://vpsjq.com/2025/07/24/mdpings-%E5%8F%A6%E4%B8%80%E4%B8%AA%E6%89%8B%E6%9C%BA%E6%8E%A2%E9%92%88app/">Mdpings这类需要常驻后台的探针App</a>，同样的保活思路也适用，都得先过电池优化这一关。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/25/termux-background-keep-alive/</id>
    <link href="https://vpsjq.com/2026/08/25/termux-background-keep-alive/"/>
    <published>2026-08-25T20:00:00.000Z</published>
    <summary>手机切个后台，Termux里正在跑的SSH连接和脚本就没了，安卓的省电机制是元凶，但设了唤醒锁和电池白名单可能还不够。</summary>
    <title>Termux后台总被系统杀掉，正在跑的任务怎么保住</title>
    <updated>2026-08-25T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps技巧" scheme="https://vpsjq.com/categories/vps%E6%8A%80%E5%B7%A7/"/>
    <category term="Docker-IPv6" scheme="https://vpsjq.com/tags/Docker-IPv6/"/>
    <content>
      <![CDATA[<span id="more"></span><p>VPS本身IPv6配置得妥妥当当，<code>ping -6</code>一测宿主机畅通无阻，装进容器里的服务却怎么都连不上IPv6，<code>docker exec</code>进去一试，IPv6网络对这个容器来说压根不存在。折腾半天以为是容器内部网络设置有问题，其实<strong>Docker默认根本不会把宿主机的IPv6能力传给容器</strong>，这是设计上的默认行为，不是哪里配错了。</p><h2 id="Docker压根没把IPv6当默认选项">Docker压根没把IPv6当默认选项</h2><p>Docker对IPv6的支持长期不如IPv4积极，容器网络默认走的是私有的IPv4段，宿主机有没有IPv6跟容器能不能用IPv6，是两件完全不搭界的事。想让容器拿到IPv6能力，得手动改Docker的配置文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">vi /etc/docker/daemon.json</span><br></pre></td></tr></table></figure><p>加上这两项：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;ipv6&quot;</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;fixed-cidr-v6&quot;</span><span class="punctuation">:</span> <span class="string">&quot;fd00:db8:1::/64&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p><code>fixed-cidr-v6</code>给的是一个私有IPv6网段，容器之间用这个网段互相通信；<code>fd00::/8</code>这类前缀是IPv6里对应IPv4私有地址（<code>10.0.0.0/8</code>那种）的等价物，专门留给内网场景用，照抄这个格式就行，不用自己纠结换成别的网段。</p><p>改完重启Docker服务让配置生效：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart docker</span><br></pre></td></tr></table></figure><h2 id="确认有没有真的生效">确认有没有真的生效</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker network inspect bridge</span><br></pre></td></tr></table></figure><p>看输出里<code>EnableIPv6</code>这一项是不是<code>true</code>，<code>IPAM.Config</code>里有没有出现你刚才配的那个IPv6网段。这一步能省掉后面很多冤枉排查——如果这里显示没生效，再怎么折腾容器内部网络设置都是白费功夫。</p><h2 id="光配置daemon-json，可能还不够">光配置daemon.json，可能还不够</h2><p><strong>如果宿主机的网关分配的是私网IPv6</strong>（不是公网直连），上面这套配置一般能直接生效；但如果宿主机拿到的是公网IPv6网关，情况会复杂一些，理论上还得配合<code>iptables</code>（准确说是IPv6对应的<code>ip6tables</code>）做地址映射才能真正打通，光改daemon.json不一定够用，具体取决于VPS服务商分配IPv6的方式。</p><p>确认改动生效后，实际测试一下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run --network=bridge --<span class="built_in">rm</span> -it busybox ping -6 -c4 google.com</span><br></pre></td></tr></table></figure><h2 id="只是临时用一下，也可以走host网络模式">只是临时用一下，也可以走host网络模式</h2><p>如果不想动全局的Docker配置，只是某个容器临时需要用IPv6，还有个更简单的旁路方案——用host网络模式跑这个容器，直接借用宿主机自己的网络协议栈，宿主机能访问IPv6，容器立刻就能访问，不需要额外配置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">docker run --network=host 镜像名</span><br></pre></td></tr></table></figure><p>缺点是host模式下容器和宿主机共用同一套网络命名空间，端口映射这些跟bridge模式的逻辑不一样，适合临时验证或者单容器场景，如果是多容器长期跑的生产环境，还是建议老老实实按上面的方式把daemon.json配好，更规范也更好维护。</p><h2 id="顺带一提">顺带一提</h2><p>这种&quot;宿主机能力齐全，但容器/子系统默认不继承&quot;的情况，跟之前写的<a href="https://vpsjq.com/2026/08/17/docker-restart-policy/">VPS重启后Docker容器为什么没有自动启动</a>是同一个脾气——Docker很多行为默认都偏保守，不会自作主张帮你把宿主机的能力透传进去，重启策略是这样，IPv6支持也是这样，新装的服务多留意一下默认值，别想当然。如果这台VPS本身的IPv6配置就有问题（不只是Docker层面），可以先看<a href="https://vpsjq.com/2026/08/24/ipv6-ip6tables-not-working/">VPS防火墙规则设置了，IPv6那边却像没设一样</a>那篇，排查一下宿主机这一层的IPv6是不是真的健康，再往Docker这一层深入。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/25/docker-ipv6-not-working/</id>
    <link href="https://vpsjq.com/2026/08/25/docker-ipv6-not-working/"/>
    <published>2026-08-25T10:00:00.000Z</published>
    <summary>宿主机IPv6配置得再完美，Docker容器默认也不会继承这个能力，得手动改daemon.json开启才行，这是Docker的默认行为，不是配置出了问题。</summary>
    <title>VPS配了IPv6，Docker容器里却死活连不上</title>
    <updated>2026-08-25T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="IPv6" scheme="https://vpsjq.com/categories/IPv6/"/>
    <category term="ip6tables" scheme="https://vpsjq.com/tags/ip6tables/"/>
    <content>
      <![CDATA[<span id="more"></span><p>辛苦用<code>iptables</code>把防火墙规则配得严严实实，只放行指定端口、指定IP，自我感觉安全性拉满，结果压根没意识到——<strong>这些规则从头到尾只管住了IPv4这一半，IPv6完全是另一套独立体系，你写的东西对它一个字都不生效</strong>。</p><h2 id="iptables和IPv6，压根不是一回事">iptables和IPv6，压根不是一回事</h2><p><code>iptables</code>工作在<code>AF_INET</code>这个协议族上，专门为IPv4设计，对IPv6流量完全没有感知能力。IPv6走的是独立协议栈路径，得靠专门的<code>ip6tables</code>来管，两者共享底层netfilter框架，但分别注册、分别生效，语法长得几乎一样，管的却是两拨完全不搭界的流量。只用<code>iptables</code>配规则、以为IPv6也顺带被保护了，是个非常容易踩、后果却不轻的误区。</p><p>配置IPv6对应规则，思路照搬IPv4那一套，工具换成<code>ip6tables</code>就行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT</span><br><span class="line">ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT</span><br><span class="line">ip6tables-save &gt; /etc/iptables/rules.v6</span><br></pre></td></tr></table></figure><h2 id="更让人后背发凉的反过来的情况">更让人后背发凉的反过来的情况</h2><p>如果说&quot;规则没生效&quot;只是让你以为自己被保护了、实际没有，那还只是白忙活一场；更麻烦的是<strong>不少发行版的<code>ip6tables</code>默认策略，压根不是&quot;拒绝&quot;，而是&quot;完全放行&quot;（ACCEPT）</strong>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ip6tables -L INPUT</span><br></pre></td></tr></table></figure><p>如果看到默认策略显示<code>ACCEPT</code>，意味着从这台VPS装好系统那一刻起，IPv6方向的流量就一直毫无过滤地大门敞开，而你可能从来没有主动碰过<code>ip6tables</code>这个东西，甚至根本不知道它的存在——不是&quot;配置失误留了漏洞&quot;，是<strong>从起点开始就没设防</strong>。</p><h2 id="ICMPv6不能照搬IPv4的思路一刀切">ICMPv6不能照搬IPv4的思路一刀切</h2><p>排查安全时另一个常见冲动是&quot;干脆把ICMPv6也全部堵死，图省事&quot;。这个思路在IPv4上问题不大，<strong>但在IPv6里行不通</strong>——ICMPv6承担的职责比IPv4的ICMP重要得多，邻居发现（NDP，相当于IPv6版本的ARP）、路由通告（地址自动配置全靠它）、路径MTU发现，全部依赖它正常工作。一刀切全部拦掉，轻则地址自动配置失败，重则触发之前写过的<a href="https://vpsjq.com/2026/08/21/ipv6-pmtu-blackhole/">PMTU黑洞问题</a>——同样是&quot;连接能建立，数据传不动&quot;的诡异症状，追根溯源发现是自己手贱把ICMPv6堵死了。</p><h2 id="用UFW能省心不少">用UFW能省心不少</h2><p>如果嫌<code>iptables</code>和<code>ip6tables</code>两套分开管理麻烦，UFW（Uncomplicated Firewall）在<code>/etc/default/ufw</code>里把<code>IPV6</code>设成<code>yes</code>之后，会自动帮你同步维护IPv6对应的规则，不用两边分别敲命令，出这类&quot;顾此失彼&quot;漏洞的概率也小得多。</p><h2 id="怎么确认自己中招没有">怎么确认自己中招没有</h2><p>从外部找一台能访问IPv6的设备，实测一下端口连通性，同时对照本机监听情况：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 从外部测试</span></span><br><span class="line">nc -zv 服务器IPv6地址 22</span><br><span class="line"></span><br><span class="line"><span class="comment"># 本机确认监听状态</span></span><br><span class="line">netstat -tulnp | grep :22</span><br></pre></td></tr></table></figure><p>如果IPv4访问一切正常、限制生效，IPv6那边却怎么连都能连上（哪怕是本该被拦住的端口），基本可以确定就是这个坑。</p><h2 id="顺带一提">顺带一提</h2><p>这种&quot;IPv6单独游离在常规配置体系之外&quot;的情况，跟之前写的<a href="https://vpsjq.com/2026/08/20/linux-disable-ipv6-not-working/">改了disable_ipv6，IPv6却没被真正关掉</a>是同一大类问题——很多人管理VPS习惯性只按IPv4思路走一遍，IPv6要么被漏掉、要么行为跟预期完全不一样。如果是排查安全问题时发现了类似的IPv6漏防情况，建议连着<a href="https://vpsjq.com/2026/08/15/vps-brute-force-check/">VPS被暴力破解怎么查有没有被入侵</a>那篇一起走一遍，确认漏洞暴露的这段时间没被人趁虚而入。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/24/ipv6-ip6tables-not-working/</id>
    <link href="https://vpsjq.com/2026/08/24/ipv6-ip6tables-not-working/"/>
    <published>2026-08-24T20:00:00.000Z</published>
    <summary>iptables只管IPv4，IPv6得靠单独的ip6tables，很多发行版默认还把IPv6策略设成完全放行，等于留了个自己都不知道的后门。</summary>
    <title>VPS防火墙规则设置了，IPv6那边却像没设一样</title>
    <updated>2026-08-24T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps技巧" scheme="https://vpsjq.com/categories/vps%E6%8A%80%E5%B7%A7/"/>
    <category term="Crontab不执行" scheme="https://vpsjq.com/tags/Crontab%E4%B8%8D%E6%89%A7%E8%A1%8C/"/>
    <content>
      <![CDATA[<span id="more"></span><p>写了个脚本，手动<code>bash script.sh</code>跑一遍好好的，扔进crontab设置成定时任务，到点却什么反应都没有，日志里也不报错，就是安安静静地什么都没发生，排查起来比报错还让人摸不着头脑。</p><h2 id="手动跑和定时跑，压根不是同一个环境">手动跑和定时跑，压根不是同一个环境</h2><p>这是最常见也最容易被忽略的根源——<strong>crontab执行任务时用的环境变量，跟你SSH登录之后手动跑命令用的环境变量，是两套完全不同的东西</strong>。你平时登录之后能直接敲<code>node</code>、<code>python3</code>、<code>docker</code>这些命令，靠的是<code>~/.bashrc</code>、<code>/etc/profile</code>这些文件里定义好的<code>PATH</code>，把这些工具所在的目录加了进去。但cron执行任务的时候，<strong>根本不会去读取这些登录相关的配置文件</strong>，它用的是一套非常精简的默认环境，<code>PATH</code>通常只有<code>/usr/bin:/bin</code>这么两个目录，你手动装的那些工具，如果装在<code>/usr/local/bin</code>或者别的自定义路径下，在cron的世界里压根就&quot;不存在&quot;，脚本一执行到那条命令，直接报&quot;command not found&quot;，只不过这个报错默认没地方看，你压根不知道它失败了。</p><h2 id="一个简单的验证办法">一个简单的验证办法</h2><p>不确定是不是这个原因，加一条临时任务，把cron实际能看到的环境变量导出来看看：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">* * * * * <span class="built_in">env</span> &gt; /tmp/cron_env.log</span><br></pre></td></tr></table></figure><p>等一分钟之后打开这个文件，对比一下跟你正常登录之后执行<code>env</code>看到的结果，PATH这一项基本一眼就能看出差在哪。</p><h2 id="怎么解决">怎么解决</h2><p>最省事、也最推荐的做法是<strong>脚本里所有命令都用绝对路径</strong>，不依赖PATH去帮你找：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/usr/local/bin/node /home/user/script.js</span><br></pre></td></tr></table></figure><p>而不是简简单单写<code>node script.js</code>寄希望于PATH自动找到它。找命令的绝对路径，用<code>which</code>查一下就行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">which</span> node</span><br></pre></td></tr></table></figure><p>如果不想每条命令都改成绝对路径，也可以在脚本开头显式声明PATH，或者干脆把需要的环境变量手动<code>export</code>一遍：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> PATH=<span class="variable">$PATH</span>:/usr/local/bin</span><br></pre></td></tr></table></figure><h2 id="别忘了这几个更基础的排查点">别忘了这几个更基础的排查点</h2><p>除了PATH这个最典型的坑，还有几个同样常见、优先级甚至更靠前的问题，一并确认一下：</p><ul><li><strong>crond这个服务本身有没有在跑</strong>：<code>systemctl status crond</code>（或者<code>cron</code>，看发行版），服务都没启动，配再多定时任务也是白搭</li><li><strong>脚本有没有执行权限</strong>：<code>chmod +x script.sh</code>，缺了这一步，手动<code>bash</code>执行能绕过去，cron直接调用可能会失败</li><li><strong>时间字段有没有写错</strong>：crontab的五个时间字段格式很容易手滑写错（比如把&quot;每2小时&quot;错写成&quot;每小时的第2分钟&quot;），跟命令本身没关系，纯粹是排班表写错了</li></ul><h2 id="顺带查日志">顺带查日志</h2><p>想知道任务到底执行了没有、失败在哪一步，给命令末尾加上日志重定向，比干等着猜靠谱得多：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">* * * * * /path/to/script.sh &gt;&gt; /tmp/script.log 2&gt;&amp;1</span><br></pre></td></tr></table></figure><h2 id="顺带一提">顺带一提</h2><p>这种&quot;手动跑正常，交给系统自动执行就失灵&quot;的情况，跟之前写的<a href="https://vpsjq.com/2026/08/23/nginx-config-not-taking-effect/">VPS改了Nginx配置，网站却还是老样子</a>其实是同一类思路——不管是Nginx的配置生效，还是cron的执行环境，自动化的那一层跟你手动操作时的环境、状态都不完全一样，出问题的时候别光盯着命令本身对不对，环境差异往往才是真正的元凶。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/24/crontab-not-executing/</id>
    <link href="https://vpsjq.com/2026/08/24/crontab-not-executing/"/>
    <published>2026-08-24T10:00:00.000Z</published>
    <summary>脚本手动跑没问题，扔进crontab就死活不执行，多半是cron的执行环境跟登录shell不是一回事，PATH变量差了一大截。</summary>
    <title>VPS的Crontab任务设置了却没执行</title>
    <updated>2026-08-24T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps技巧" scheme="https://vpsjq.com/categories/vps%E6%8A%80%E5%B7%A7/"/>
    <category term="Nginx配置生效" scheme="https://vpsjq.com/tags/Nginx%E9%85%8D%E7%BD%AE%E7%94%9F%E6%95%88/"/>
    <content>
      <![CDATA[<span id="more"></span><p>改好<code>nginx.conf</code>，保存、刷新浏览器，页面纹丝不动，跟没改过一模一样。这种情况排查起来容易陷入死循环——反复检查配置文件哪里写错了，其实配置文件本身可能一个字都没错，问题出在别的环节。</p><h2 id="第一层：Nginx不会自己发现配置变了">第一层：Nginx不会自己发现配置变了</h2><p>Nginx跟很多现代化服务不一样，<strong>它不会实时监控配置文件有没有被修改</strong>，改完文件之后，得手动发送一个信号告诉它&quot;该重新读配置了&quot;：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nginx -s reload</span><br></pre></td></tr></table></figure><p>不发这个信号，Nginx会一直按内存里旧的那份配置继续跑，不管文件本身改成什么样。这是最常见、也是最容易被忽略的第一层原因。</p><h2 id="第二层：reload了，但语法错误让它悄悄回滚">第二层：reload了，但语法错误让它悄悄回滚</h2><p>如果确实执行过<code>reload</code>，但配置文件里有语法错误，Nginx有个挺贴心也挺容易让人摸不着头脑的保护机制——<strong>先检查新配置的语法，发现有问题，直接放弃这次加载，继续用上一次能正常跑的旧配置顶着</strong>，服务不会中断，但你的改动压根没生效，而且不会有特别显眼的提示。改之前养成习惯，先测试语法：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nginx -t</span><br></pre></td></tr></table></figure><p>看到<code>syntax is ok</code>和<code>test is successful</code>两行都出现，再执行reload，能避免这个坑。</p><h2 id="第三层：不是所有改动reload都管用">第三层：不是所有改动reload都管用</h2><p>大部分配置调整reload就够了，但有几种情况例外。如果改的是<code>listen</code>指令里监听的IP地址（不只是端口），部分版本下光reload可能不完全生效，得用完整重启：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">systemctl restart nginx</span><br></pre></td></tr></table></figure><p>还有个更隐蔽的情况——如果<code>upstream</code>配置的后端地址写的是域名而不是IP，域名对应的IP如果变了，<code>reload</code>并不会重新解析这个域名拿到新IP，因为DNS解析结果在Nginx内部是有缓存的，这种情况同样得靠完整重启才能刷新。</p><h2 id="第四层：服务端真的没问题，是浏览器在使唤你">第四层：服务端真的没问题，是浏览器在使唤你</h2><p>前面三层都排除了，服务端配置也确认生效了，网页看着还是没变化，这时候问题可能压根不在服务器这边——<strong>浏览器把旧版本的页面缓存住了</strong>，尤其是CSS、JS这类静态资源，浏览器默认会缓存很长时间。强制刷新（<code>Ctrl+Shift+R</code>或者手机端清一下浏览器缓存）通常就能解决，或者干脆换个无痕窗口访问确认。</p><h2 id="一套排查顺序">一套排查顺序</h2><p>遇到这种情况，按这个顺序过一遍，基本能定位到问题出在哪一层：先确认执行过<code>reload</code>；再用<code>nginx -t</code>确认配置语法本身没错、且reload真的成功应用了；如果改的是监听地址或者upstream域名这类特殊指令，直接换成<code>restart</code>更保险；最后如果服务端一切正常，浏览器强制刷新排除缓存干扰。</p><h2 id="顺带一提">顺带一提</h2><p>这种&quot;配置文件确实改了，但正在运行的进程压根不知道&quot;的情况，跟之前写的<a href="https://vpsjq.com/2026/08/20/linux-ulimit-not-working/">ulimit设置了却不生效</a>是同一类问题的另一个变种——运行中的进程不会主动感知配置文件的变化，改完之后总得有个明确的动作（reload、restart，或者给systemd服务单独配置）去告诉它&quot;该更新了&quot;，这条规律在很多Linux服务上都成立，不只是Nginx。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/23/nginx-config-not-taking-effect/</id>
    <link href="https://vpsjq.com/2026/08/23/nginx-config-not-taking-effect/"/>
    <published>2026-08-23T20:00:00.000Z</published>
    <summary>改完Nginx配置文件网站没变化，问题可能出在忘了reload、语法错误静默回滚、某些指令reload不彻底生效，或者纯粹是浏览器缓存作祟。</summary>
    <title>VPS改了Nginx配置，网站却还是老样子</title>
    <updated>2026-08-23T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="vps技巧" scheme="https://vpsjq.com/categories/vps%E6%8A%80%E5%B7%A7/"/>
    <category term="NTP时间同步" scheme="https://vpsjq.com/tags/NTP%E6%97%B6%E9%97%B4%E5%90%8C%E6%AD%A5/"/>
    <content>
      <![CDATA[<span id="more"></span><p>VPS上跑<code>date</code>一看，时间跟手机差了好几分钟甚至几小时，日志时间戳全乱套，排查问题时间线都对不上。装个NTP同步以为解决了，<code>timedatectl status</code>一查，<code>NTP enabled: yes</code>看着挺正常，可下面一行<code>NTP synchronized: no</code>——开关打开了，但压根没真同步成功。</p><h2 id="开关打开不等于同步成功">开关打开不等于同步成功</h2><p><code>timedatectl set-ntp true</code>这条命令做的事，只是<strong>告诉系统&quot;去试着同步&quot;</strong>，不代表真的连上了时间服务器、拿到了准确时间。<code>enabled: yes</code>和<code>synchronized: yes</code>是两码事，前者是开关状态，后者才是真正的结果。同步没成功，常见原因是网络到时间服务器不通、配置的服务器地址本身有问题，或者防火墙把NTP用的端口挡住了。</p><h2 id="VPS的时间问题，跟物理机不是一回事">VPS的时间问题，跟物理机不是一回事</h2><p>物理服务器的时钟依赖主板上的硬件晶振，误差是那种缓慢的、可预期的&quot;漂移&quot;，攒够了偏差量再靠NTP慢慢纠正。但VPS的情况完全不同——<strong>虚拟机压根没有独立可靠的硬件时钟，它对&quot;时间流逝&quot;的感知，本质上是靠宿主机分配的CPU调度周期堆出来的</strong>。如果物理宿主机负载很重、这台VPS抢不到及时的CPU时间片，虚拟机的时钟不是慢慢漂移，而是会直接<strong>跳跃</strong>——一下子跳过去好几秒甚至更多，这跟之前写的<a href="https://vpsjq.com/2026/08/17/vps-cpu-steal-time/">VPS的CPU使用率明明很低为什么系统还是卡</a>讲的其实是同一个根源：宿主机调度延迟。CPU被偷走的时候，你的程序在干等；时钟被偷走的时候，你的系统时间直接失真，两者都是虚拟化调度延迟这一件事在不同层面的体现。</p><h2 id="chrony比传统ntpd更适合这种场景">chrony比传统ntpd更适合这种场景</h2><p>正因为VPS遇到的是&quot;跳跃&quot;而不是单纯的&quot;漂移&quot;，处理方式也得跟着变。<code>ntpd</code>更擅长应付缓慢渐进的偏差，遇到大幅度跳变调整起来比较迟钝；<code>chrony</code>（RHEL 8以上已默认弃用ntpdate、只用chronyd）对突发跳变的适应能力明显更强，是目前更推荐在虚拟化环境下用的方案：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">yum install chrony -y</span><br><span class="line">systemctl <span class="built_in">enable</span> chronyd</span><br><span class="line">systemctl start chronyd</span><br></pre></td></tr></table></figure><p>装好后手动确认同步状态：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">chronyc tracking</span><br><span class="line">chronyc sources -v</span><br></pre></td></tr></table></figure><h2 id="光同步系统时间还不够，硬件时钟也得对齐">光同步系统时间还不够，硬件时钟也得对齐</h2><p>NTP同步成功只解决了系统时间，服务器重启的时候，系统会去读一次硬件时钟（RTC）作为初始值，如果硬件时钟本身跟系统时间对不上，重启之后时间又会跳回错的那个值。同步一次，顺手也把硬件时钟校准了：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">hwclock --systohc</span><br></pre></td></tr></table></figure><p>另外确认一下硬件时钟存的是不是UTC（推荐用UTC，避免时区换算带来额外的混乱）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">timedatectl set-local-rtc 0</span><br></pre></td></tr></table></figure><h2 id="时间偏差特别大的时候，chrony默认调整策略可能太慢">时间偏差特别大的时候，chrony默认调整策略可能太慢</h2><p>chrony默认倾向于&quot;温和纠正&quot;，如果发现偏差特别大，慢慢调整可能要花很长时间才能追上。配置文件里加一条<code>makestep</code>，允许在偏差超过阈值的情况下直接跳到正确时间，而不是慢慢挪：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">makestep 1.0 3</span><br></pre></td></tr></table></figure><p>这条的意思是，如果时间偏差超过1秒，且是在chrony启动后的前3次更新之内，允许直接步进调整，不用等着慢慢靠近。</p><h2 id="顺带一提">顺带一提</h2><p>这种&quot;网上抄来的一键优化脚本里塞了一堆参数，各管各的、互相之间关系没讲清楚&quot;的情况，跟之前写的<a href="https://vpsjq.com/2026/08/18/bbr-no-improvement/">为什么开了BBR网速却感觉一点没提升</a>是类似的坑——时间同步、网络加速这些配置经常被打包在同一份VPS初始化脚本里一股脑塞给你，跑之前多花两分钟弄清楚每一项具体在解决什么问题，比照单全收更靠谱。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/23/vps-ntp-time-drift/</id>
    <link href="https://vpsjq.com/2026/08/23/vps-ntp-time-drift/"/>
    <published>2026-08-23T10:00:00.000Z</published>
    <summary>VPS的时间漂移比物理机严重得多，装了NTP同步开关打开也不代表真同步成功，这背后跟虚拟化调度延迟脱不了干系。</summary>
    <title>VPS的时间为什么会跑偏，NTP装了却没用</title>
    <updated>2026-08-23T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="IPv6" scheme="https://vpsjq.com/categories/IPv6/"/>
    <category term="IPv6临时地址" scheme="https://vpsjq.com/tags/IPv6%E4%B8%B4%E6%97%B6%E5%9C%B0%E5%9D%80/"/>
    <content>
      <![CDATA[<span id="more"></span><p><code>ip -6 addr</code>今天看的地址，隔几天再查，后半截莫名其妙变了，第一反应容易往坏处想——是不是配置被人动过手脚，或者哪个服务偷偷改了网络设置。其实大概率什么都没被入侵，这是IPv6一个专门设计出来的隐私保护特性在正常工作，只是这个特性放在服务器身上，通常并不是你想要的。</p><h2 id="最早的IPv6地址，其实是个-永久身份证">最早的IPv6地址，其实是个&quot;永久身份证&quot;</h2><p>IPv6地址后64位（接口标识符）最早的生成方式叫EUI-64，直接把网卡的MAC地址原样嵌进去，好处是简单、不用协调就能保证唯一，副作用却很致命——<strong>这段后缀跟MAC地址死死绑定，不管设备换到哪个网络，暴露出来的这一截永远一样</strong>，等于给每台设备贴了张终身不变、全世界可见的身份证。安全研究者早就指出这是个隐私灾难，任何一方留意这个特征值，就能跨网络持续追踪同一台设备。</p><h2 id="Privacy-Extensions就是为了解决这个问题而生的">Privacy Extensions就是为了解决这个问题而生的</h2><p>为了堵上这个漏洞，后来的标准（RFC 4941，现已被RFC 8981取代）给SLAAC加了Privacy Extensions机制——<strong>设备不再老实用MAC地址生成固定后缀，而是随机生成一段临时接口标识符</strong>，默认&quot;首选&quot;有效期约1天，完全失效前还有2天宽限期，快到期前系统自动生成新的随机地址顶替上去。<strong>地址隔一天左右自动换一次，是设计好该有的正常行为</strong>，不是故障也不是被篡改。</p><h2 id="服务器和普通设备的需求正好相反">服务器和普通设备的需求正好相反</h2><p>关键点在于：<strong>Privacy Extensions启用后，设备身上会同时存在两个地址</strong>——一个稳定地址负责让外界持续找到你；一个不断轮换的临时地址，专门用在设备<strong>主动发起</strong>的连接上，防止被追踪。这套设计是为终端设备（笔记本、手机）量身定制的。</p><p>但VPS的定位正好反过来——<strong>服务器存在的意义就是要被稳定地找到</strong>，域名解析、白名单、日志分析全指望这个地址长期不变。如果系统默认给VPS也开着这个特性，服务器主动向外发起的连接会不断换&quot;马甲&quot;，排查问题时日志里同一台机器顶着好几个不同IPv6地址，只会徒增困惑，没有实际隐私收益。</p><h2 id="怎么确认现在是不是开着，以及怎么关">怎么确认现在是不是开着，以及怎么关</h2><p>先查一下当前的设置：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sysctl net.ipv6.conf.all.use_tempaddr</span><br></pre></td></tr></table></figure><p>返回值是<code>2</code>表示已经开启，且优先用临时地址发起新连接；<code>0</code>是完全关闭；<code>1</code>是生成但不优先使用。如果这台VPS上确实显示的是<code>2</code>，关掉它，让服务器的出站连接固定用稳定地址：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">sysctl -w net.ipv6.conf.all.use_tempaddr=0</span><br><span class="line">sysctl -w net.ipv6.conf.default.use_tempaddr=0</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;net.ipv6.conf.all.use_tempaddr=0&quot;</span> &gt;&gt; /etc/sysctl.conf</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;net.ipv6.conf.default.use_tempaddr=0&quot;</span> &gt;&gt; /etc/sysctl.conf</span><br></pre></td></tr></table></figure><p>需要澄清一点：这个设置管的是<strong>服务器自己主动发起连接时用哪个地址</strong>，跟&quot;别人怎么访问你这台服务器&quot;是两码事——外部访问用的是你DNS里AAAA记录指向的那个稳定地址，关掉Privacy Extensions不会影响别人正常连进来，只会让你自己这台机器对外发起的连接不再莫名其妙换地址。</p><h2 id="顺带一提">顺带一提</h2><p>如果是在排查<a href="https://vpsjq.com/2026/08/15/vps-brute-force-check/">VPS被暴力破解怎么查有没有被入侵</a>这类安全问题的过程中，看到日志里同一台自己的机器顶着不同IPv6地址出现，先别急着往&quot;被入侵&quot;这个方向想，回头查一下是不是这个特性在起作用。想确认自己这台VPS当前对外呈现的地址类型和质量，也可以配合<a href="https://vpsjq.com/2025/05/06/%E6%8E%A8%E8%8D%90%E4%B8%80%E4%B8%AAip%E5%B7%A5%E5%85%B7/">推荐几个IP工具</a>那篇提到的工具查一下，确认关闭Privacy Extensions之后地址确实稳定下来了。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/22/ipv6-privacy-extensions-changing/</id>
    <link href="https://vpsjq.com/2026/08/22/ipv6-privacy-extensions-changing/"/>
    <published>2026-08-22T10:00:00.000Z</published>
    <summary>IPv6地址过段时间自动改变不是入侵或者配置出错，是Privacy Extensions这个隐私保护特性在起作用，但服务器场景通常并不需要它。</summary>
    <title>为什么VPS的IPv6地址过一阵子会自己变，是不是被入侵了</title>
    <updated>2026-08-22T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="IPv6" scheme="https://vpsjq.com/categories/IPv6/"/>
    <category term="PMTU黑洞" scheme="https://vpsjq.com/tags/PMTU%E9%BB%91%E6%B4%9E/"/>
    <content>
      <![CDATA[<span id="more"></span><p><code>ping -6</code>一测，延迟正常、一个包都不丢，看着IPv6配置得妥妥的；结果打开一个网站，页面加载到一半就卡死不动，SSH倒是能连上，登录成功之后敲命令却半天没反应。小包一路畅通，大包寸步难行，这种诡异的&quot;选择性失灵&quot;，在IPv6下有个专门的名字——<strong>PMTU黑洞</strong>。</p><h2 id="IPv6做了一个跟IPv4不一样的设计决定">IPv6做了一个跟IPv4不一样的设计决定</h2><p>IPv4时代，如果一个数据包大小超过了某段链路能承受的上限（MTU），中间的路由器可以直接把这个包<strong>拆开</strong>（分片）之后继续转发，麻烦是麻烦，但好歹能自己想办法解决。IPv6在设计上彻底废掉了路由器这个能力——<strong>中间路由器不允许分片</strong>，遇到超过链路MTU的包，唯一能做的就是把包直接丢弃，然后回发一个ICMPv6&quot;Packet Too Big&quot;消息告诉发送方&quot;你这个包太大了，改用这个尺寸重新发&quot;。这是个刻意的取舍：简化了路由器的工作、提升了转发效率，但代价是把&quot;发现最优包大小&quot;这件事完全甩给了两端，整个流程必须依赖这条&quot;回复消息&quot;能顺畅地传回发送方，这套机制叫路径MTU发现（PMTUD）。</p><h2 id="黑洞出在哪一步">黑洞出在哪一步</h2><p>麻烦就出在这条&quot;回复消息&quot;上——路径上只要有<strong>任何一个</strong>防火墙或者中间设备，出于&quot;安全考虑&quot;或者单纯配置疏忽，把ICMPv6这类消息不分青红皂白地拦截或者丢弃掉，发送方就永远收不到&quot;包太大了&quot;这个提醒，只会一直傻乎乎地按原来的大尺寸继续发送，这些包在半路被反复丢弃，TCP只能靠自己超时之后重传来兜底，慢得让人抓狂，严重的时候干脆卡死不动。</p><p>这也是为什么<code>ping</code>能通但网站打不开——<code>ping</code>用的探测包体积很小，怎么都不会撞上MTU这道坎，畅通无阻；而网页的实际内容数据、SSH认证成功之后的正常会话数据，动辄接近甚至超过完整MTU尺寸，一旦触发&quot;包太大需要缩小&quot;这个反馈机制，恰好又被沿途某个防火墙拦下了这条反馈消息，就成了黑洞——连接建立起来了（因为握手包够小），真正传数据的时候却卡死。有人真的用<code>tcpdump</code>抓包验证过这个现象，能清楚看到对端发回的<code>ICMP6, packet too big, mtu 1285</code>这条消息，说明路径中某一段链路的MTU只有1285字节，远小于常见的1500。</p><h2 id="怎么确认自己遇到的是这个问题">怎么确认自己遇到的是这个问题</h2><p>对照几个典型症状，如果你现在的情况能对上大半，基本可以锁定就是PMTU黑洞：TCP连接能正常建立（因为握手包小）；网页加载到一半卡住不动；SSH能连上，认证成功后却卡死；文件下载开始几KB之后就停滞不前。</p><h2 id="怎么解决">怎么解决</h2><p>核心思路是<strong>确保ICMPv6的&quot;Packet Too Big&quot;这条消息（Type 2）在整条路径上畅通无阻</strong>，检查自己VPS上的防火墙规则，明确放行这类消息：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">ip6tables -I INPUT 1 -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT</span><br><span class="line">ip6tables -I OUTPUT 1 -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT</span><br></pre></td></tr></table></figure><p>如果自己这边的防火墙确认没问题，问题还是没解决，说明黑洞出在你控制不了的上游链路某处，这种情况下自己这台VPS改配置帮不上忙，只能等对方网络修复，或者换一条不经过这段问题链路的路由。</p><h2 id="顺带一提">顺带一提</h2><p>同样是&quot;IPv6看着配置好了，实际却在拖后腿&quot;这类问题，之前写的<a href="https://vpsjq.com/2026/08/21/ipv6-happy-eyeballs-slow/">为什么VPS各种连接、下载都很慢，罪魁祸首可能是IPv6</a>讲的是另一种情况——那篇是连接建立阶段就被拖慢，这篇是连接建立之后传数据被卡死，两种症状容易混在一起，遇到问题先分清楚是&quot;连不上&quot;还是&quot;连上了传不动&quot;，排查方向完全不同。如果确认问题出在SSH这个具体场景，另外还有一篇专门讲<a href="https://vpsjq.com/2026/08/15/ssh-connection-slow/">VPS SSH连接很慢是什么原因</a>，讲的是跟MTU完全无关的另外两个常见原因，也可以对照排除。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/21/ipv6-pmtu-blackhole/</id>
    <link href="https://vpsjq.com/2026/08/21/ipv6-pmtu-blackhole/"/>
    <published>2026-08-21T20:00:00.000Z</published>
    <summary>ping用的是小包能畅通无阻，网页、SSH会话这类大包却卡住不动，这是IPv6下一种叫PMTU黑洞的经典问题，根源在防火墙悄悄拦截了一条关键的反馈通道。</summary>
    <title>为什么IPv6配置好了，Ping能通，网站却怎么都打不开</title>
    <updated>2026-08-21T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="Happy-Eyeballs" scheme="https://vpsjq.com/tags/Happy-Eyeballs/"/>
    <content>
      <![CDATA[<span id="more"></span><p>带宽测过、路由也查过，数据看着都正常，但VPS上敲个<code>wget</code>就是要愣个一两秒才开始下载，SSH连接偶尔也要卡一下，说不上哪里坏了，就是整体感觉&quot;慢半拍&quot;。这种笼统的慢，很多时候压根不是带宽或者CPU的问题，是<strong>系统每次新建连接的时候，先去尝试了一个根本走不通的IPv6，白白等了一截超时时间</strong>。</p><h2 id="系统本来是有防呆设计的">系统本来是有防呆设计的</h2><p>现代操作系统和大部分应用，处理&quot;域名同时解析出IPv4和IPv6两个地址&quot;这种情况时，用的是一套叫<strong>Happy Eyeballs</strong>（RFC 8305定义）的机制——同时发起IPv4和IPv6两路连接尝试，优先偏向IPv6，但哪个先连通就用哪个，不会傻等一条线路。这个设计的初衷就是为了应对&quot;IPv6配置得七零八落&quot;这种普遍存在的现实情况，理论上就算你的IPv6是坏的，也只会因为要等一下IPv6失败的信号，多花大概两三百毫秒，不至于让人有明显感知。</p><h2 id="但这套防呆机制，不是所有软件都老实实现了">但这套防呆机制，不是所有软件都老实实现了</h2><p>问题就出在这——Happy Eyeballs是个&quot;建议标准&quot;，不是所有客户端软件都规规矩矩照着实现。真实翻过车的例子：Node.js底层网络库undici，压根没实现这套机制，一旦域名同时解析出IPv4和IPv6，它会先死磕IPv6，<strong>等到完整的连接超时时间耗尽</strong>才肯回落到IPv4，这个等待时间可以是好几秒，远不是设计初衷里那两三百毫秒的&quot;温和延迟&quot;。像<code>curl</code>这类正确实现了Happy Eyeballs的工具，同样的网络环境下访问就完全正常，反而更容易让人误以为是&quot;某个具体命令有问题&quot;，而不是往IPv6这个方向去想。</p><h2 id="什么样的IPv6配置最容易踩这个坑">什么样的IPv6配置最容易踩这个坑</h2><p>最麻烦的不是&quot;完全没有IPv6&quot;，也不是&quot;IPv6配置得很干净&quot;，而是<strong>看着有、实际走不通</strong>的这种半吊子状态：</p><ul><li>有IPv6地址，但没有默认路由，包发出去了却压根不知道该往哪转发</li><li>防火墙<strong>默默丢弃</strong>IPv6流量而不是明确拒绝——静默丢包比直接拒绝更麻烦，因为拒绝能让客户端立刻知道这条路走不通，静默丢包只能靠等超时才能确认失败</li><li>服务器本身AAAA记录已经解析出去了，但IPv6网络配置其实还没真正弄好</li></ul><p>排查思路，先确认这台机器的IPv6是不是真的能用：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ip -6 addr show scope global</span><br><span class="line">ip -6 route show default</span><br><span class="line">ping -6 baidu.com</span><br></pre></td></tr></table></figure><p>如果地址显示不完整（只有link-local这种本地链路地址）、或者压根没有默认路由、或者ping不通，基本可以确定就是这个&quot;半吊子IPv6&quot;在拖后腿。</p><h2 id="怎么解决">怎么解决</h2><p>思路只有两条，选一条走到底，最忌讳的就是停在中间这个半吊子状态：<strong>要么把IPv6配置彻底修好</strong>（补上默认路由、确认防火墙没有静默丢包），<strong>要么干脆利落地关掉</strong>，让系统压根不去尝试这条走不通的路，连Happy Eyeballs那两三百毫秒的等待都省了。关闭方式具体怎么操作、有哪些容易被忽略的细节（比如<code>lo</code>接口漏关、NetworkManager在背后跟sysctl打架），可以看之前写的<a href="https://vpsjq.com/2026/08/20/linux-disable-ipv6-not-working/">为什么改了disable_ipv6，IPv6却还是没有被真正关掉</a>那篇，把这一步做干净了，&quot;莫名其妙变慢&quot;这个问题往往也就跟着消失了。</p><p>如果这台VPS本身就是IPv6 only、或者IPv6是核心需求不能一关了之，那思路就完全反过来了，得把IPv6彻底修通而不是关掉，具体排查方法可以看<a href="https://vpsjq.com/2026/07/22/2026-07-22-005/">IPv6 VPS服务无法访问的常见原因与排查方法</a>那篇。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/21/ipv6-happy-eyeballs-slow/</id>
    <link href="https://vpsjq.com/2026/08/21/ipv6-happy-eyeballs-slow/"/>
    <published>2026-08-21T10:00:00.000Z</published>
    <summary>系统同时解析出IPv4和IPv6地址后会优先尝试IPv6，如果IPv6配置是半吊子状态，每次新建连接都要多等一截超时时间才会回落到IPv4。</summary>
    <title>为什么VPS各种连接、下载都很慢，罪魁祸首可能是IPv6</title>
    <updated>2026-08-21T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="disable_ipv6" scheme="https://vpsjq.com/tags/disable-ipv6/"/>
    <content>
      <![CDATA[<span id="more"></span><p>某些场景下想彻底关掉IPv6（比如某个服务对IPv6支持有问题，干脆关掉省心），常见做法是改<code>/etc/sysctl.conf</code>加一行<code>net.ipv6.conf.all.disable_ipv6=1</code>，跑一下<code>sysctl -p</code>，结果<code>ip -6 addr</code>一查，IPv6地址还在，跟没改过一样。要么就是反过来——真关掉了，过阵子发现SSH的某个功能用不了了，或者邮件服务死活启动不起来，两头都能踩坑。</p><h2 id="“all-不等于-所有”">“all&quot;不等于&quot;所有”</h2><p>第一个坑出在这个参数的名字容易让人误会。<code>net.ipv6.conf.all.disable_ipv6=1</code>看着像是&quot;关闭所有接口的IPv6&quot;，实际上它只是给<strong>新建立的接口</strong>设置默认值，加上作用到已经存在的常规网卡上；但<strong>回环接口（lo）不受它管</strong>，得单独再加一行：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">net.ipv6.conf.all.disable_ipv6 = 1</span><br><span class="line">net.ipv6.conf.default.disable_ipv6 = 1</span><br><span class="line">net.ipv6.conf.lo.disable_ipv6 = 1</span><br></pre></td></tr></table></figure><p>社区里翻车的人不少都是只写了第一行，觉得<code>all</code>听着应该管得够全了，结果<code>lo</code>接口上的IPv6死活关不掉。</p><h2 id="NetworkManager会在背后跟你唱反调">NetworkManager会在背后跟你唱反调</h2><p>如果系统用的是NetworkManager管理网络（常见于RHEL/CentOS这类发行版），就算sysctl这边配置全部写对了，NetworkManager有自己的一套逻辑，<strong>可能会在网卡重新连接的时候，把IPv6又悄悄打开</strong>，跟sysctl的设置对着干。这种情况下光改sysctl没用，还得额外用<code>nmcli</code>单独告诉NetworkManager本身也别管IPv6：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">nmcli connection modify <span class="string">&quot;连接名&quot;</span> ipv6.method <span class="string">&quot;disabled&quot;</span></span><br></pre></td></tr></table></figure><p>两边都设置了，才算真正锁死。</p><h2 id="真想彻底关掉，得从内核启动参数下手">真想彻底关掉，得从内核启动参数下手</h2><p>如果sysctl这条路来回折腾还是不干净，更彻底的办法是在GRUB里加内核启动参数<code>ipv6.disable=1</code>，这样重启之后IPv6模块压根不会被内核加载。这里有个有意思的连锁反应——如果之前sysctl.conf里还留着<code>net.ipv6.conf.all.disable_ipv6=1</code>这一行没清理，下次<code>sysctl -p</code>反而会报错，提示<code>cannot stat /proc/sys/net/ipv6/conf/all/disable_ipv6: No such file or directory</code>，因为内核层面IPv6压根不存在了，这个参数自然也没了，两种关闭方式叠加，变成新的报错，得把旧配置一并清掉。</p><h2 id="关掉之后，别的服务可能跟着遭殃">关掉之后，别的服务可能跟着遭殃</h2><p>IPv6被真正关掉之后，一些服务默认监听<code>::1</code>这种IPv6回环地址，会直接启动失败——比较典型的是Postfix邮件服务，配置文件里<code>inet_interfaces</code>默认写法可能依赖IPv6回环，得手动改成指向IPv4的<code>127.0.0.1</code>才能正常跑起来；SSH的X11转发功能同样可能受影响，需要在<code>sshd_config</code>里加上<code>AddressFamily inet</code>明确指定只用IPv4。这些副作用平时不会显现，只有真把IPv6关掉那一刻才会冒出来，排查起来容易让人摸不着头脑，表面上看跟&quot;关IPv6&quot;这个操作完全不沾边。</p><h2 id="顺带一提">顺带一提</h2><p>这种&quot;配置文件明明改了，实际却没在预期的地方生效&quot;的情况，跟之前写的<a href="https://vpsjq.com/2026/08/20/linux-ulimit-not-working/">ulimit设置了却不生效</a>是同一类问题的不同变种——不是配置写错了，是<strong>改的那个地方，压根管不到你以为它管的那个东西</strong>。如果反过来是VPS本身IPv6 only、根本没有IPv4可以选择性关闭，遇到的会是完全不同方向的连接问题，可以看<a href="https://vpsjq.com/2026/07/22/2026-07-22-005/">IPv6 VPS服务无法访问的常见原因与排查方法</a>那篇，一个是&quot;关不掉&quot;，一个是&quot;只有它、还连不上&quot;，别搞混排查方向。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/20/linux-disable-ipv6-not-working/</id>
    <link href="https://vpsjq.com/2026/08/20/linux-disable-ipv6-not-working/"/>
    <published>2026-08-20T20:00:00.000Z</published>
    <summary>net.ipv6.conf.all.disable_ipv6设置了却没生效，或者生效了却搞崩了SSH转发和邮件服务，这个内核参数比看起来复杂得多。</summary>
    <title>为什么改了disable_ipv6，IPv6却还是没有被真正关掉</title>
    <updated>2026-08-20T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="ulimit文件描述符" scheme="https://vpsjq.com/tags/ulimit%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6/"/>
    <content>
      <![CDATA[<span id="more"></span><p>服务并发一上来，日志里开始刷屏<code>Too many open files</code>，查了一圈发现是文件描述符（也就是<code>nofile</code>）限制不够用，照着教程改了<code>/etc/security/limits.conf</code>，重新登录敲一下<code>ulimit -a</code>，数字确实变大了，满心以为解决了，结果服务重启之后照样报同样的错，跟没改过一模一样。</p><h2 id="你改对了地方，但改错了对象">你改对了地方，但改错了对象</h2><p>问题的根源在于——<code>/etc/security/limits.conf</code>这个文件，<strong>从来就不是给系统服务用的</strong>。它的官方文档说得很直白：“This file sets the resource limits for the users logged in via PAM…It does not affect resource limits of the system services.”（这个文件设置的是通过PAM登录的用户的资源限制，不影响系统服务的资源限制。）</p><p>你改完这个文件，重新SSH登录一次，确实能看到<code>ulimit -a</code>里的数字变了——但那只是<strong>你这次登录会话</strong>通过PAM认证之后继承到的限制，跟你用<code>systemctl start</code>、<code>docker run</code>这类方式启动的后台服务完全是两码事。这些服务压根不是通过你的登录会话拉起来的，自然也就轮不到<code>limits.conf</code>来管它们。有人真的做过对比实验：改完<code>limits.conf</code>，自己的shell里<code>ulimit -a</code>显示正常，但MySQL服务本身查询它实际生效的<code>open_files_limit</code>，数值纹丝不动还是原来的1024。</p><h2 id="systemd是故意这么设计的，不是漏掉了">systemd是故意这么设计的，不是漏掉了</h2><p>更准确地说，这不是&quot;没生效&quot;，是<strong>systemd在设计上就故意无视这个全局配置文件</strong>，官方文档原话是&quot;Systemd does not support global limits, the file is intentionally ignored&quot;（systemd不支持全局限制，这个文件被有意忽略）。systemd有自己独立的一套限制机制，得单独针对每个服务去配置。</p><h2 id="正确的改法：给具体服务单独配置">正确的改法：给具体服务单独配置</h2><p>以Nginx为例，给它专门建一个systemd覆盖配置，不用去动主配置文件：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /etc/systemd/system/nginx.service.d/</span><br><span class="line"><span class="built_in">cat</span> &gt; /etc/systemd/system/nginx.service.d/override.conf &lt;&lt; <span class="string">EOF</span></span><br><span class="line"><span class="string">[Service]</span></span><br><span class="line"><span class="string">LimitNOFILE=100000</span></span><br><span class="line"><span class="string">EOF</span></span><br><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl restart nginx</span><br></pre></td></tr></table></figure><p>把<code>nginx</code>换成你实际要调整的服务名（比如<code>docker</code>、<code>mysqld</code>，或者你在跑的某个代理面板对应的服务名），思路都一样。</p><h2 id="还有两层隐藏的天花板">还有两层隐藏的天花板</h2><p>就算这一步配对了，也可能碰到另外两层限制：一是内核本身有个硬顶<code>/proc/sys/fs/nr_open</code>，你设置的数值不能超过这个内核级上限；二是CentOS 7上曾经存在过一个已知的systemd bug（低于240版本），<code>LimitNOFILE</code>这个参数即使写了也不生效，需要额外手动干预才能真正吃到配置。遇到配置写对了但还是不生效的情况，这两个方向值得往下查。</p><h2 id="顺带一提">顺带一提</h2><p>如果这台VPS上跑着<a href="https://vpsjq.com/2026/04/16/wunao-yijian-tanzhen/">Uptime Kuma</a>这类需要同时维持大量监控连接的探针服务，文件描述符不够用的问题会比一般场景更容易碰到，配置的时候可以适当留足余量。之前写<a href="https://vpsjq.com/2026/08/15/vps-disk-space-full/">VPS磁盘空间满了怎么排查</a>那篇里提到用<code>lsof</code>排查文件占用的方法，跟这篇讲的文件描述符本质上是同一套底层机制，遇到跟&quot;打开的文件太多&quot;相关的报错，这两篇可以放一起对照着看。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/20/linux-ulimit-not-working/</id>
    <link href="https://vpsjq.com/2026/08/20/linux-ulimit-not-working/"/>
    <published>2026-08-20T10:00:00.000Z</published>
    <summary>修改了limits.conf、ulimit -a也确认生效了，systemd管理的服务却依然报错文件句柄不够，因为systemd根本不看这个配置文件。</summary>
    <title>ulimit 设上限仍报 Too many open files 排查</title>
    <updated>2026-08-20T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="进程状态D" scheme="https://vpsjq.com/tags/%E8%BF%9B%E7%A8%8B%E7%8A%B6%E6%80%81D/"/>
    <content>
      <![CDATA[<span id="more"></span><p>一个进程行为异常，一般人的应对顺序是<code>kill</code>、不行就<code>kill -9</code>——后者理论上是终极武器，连进程自己都没机会拒绝，操作系统直接把它强制终止。结果偏偏有些时候，<code>kill -9</code>敲下去，<code>ps</code>一查，那个进程还稳稳地待在那儿，跟没发生过一样，第一次遇到这种情况会怀疑自己是不是敲错了命令。</p><h2 id="kill其实只是-传话-，不是-处决">kill其实只是&quot;传话&quot;，不是&quot;处决&quot;</h2><p><code>kill</code>命令本质上是给目标进程发一个信号，再交给操作系统内核去处理。内核收到信号之后，正常情况下会中断进程当前在做的事，转去处理这个信号——但这里有个前提：<strong>进程得处于一个&quot;能被打断&quot;的状态</strong>。</p><p>Linux进程有好几种状态，其中两种睡眠状态是关键：一种是<strong>可中断睡眠（S状态）</strong>，进程在等某个事件的时候，收到信号会立刻被唤醒去响应，这是绝大多数进程平时的正常状态；另一种是<strong>不可中断睡眠（D状态，也就是<code>top</code>/<code>ps</code>里显示的那个字母），这种状态的进程完全不接收任何外来信号，不管是普通的<code>kill</code>、<code>kill -9</code>还是<code>kill -15</code>，通通不管用</strong>。之前写<a href="https://vpsjq.com/2026/08/18/linux-load-average-explained/">Load Average很高是不是要出问题了</a>那篇提到的，被算进负载却根本不占CPU的那批进程，正是这个D状态。</p><h2 id="为什么内核要设计成-不可中断">为什么内核要设计成&quot;不可中断&quot;</h2><p>D状态出现的场景，通常是进程正在等待磁盘、网络这类硬件IO返回结果，而这个等待处在内核态一个很关键的环节里——为了保证数据一致性（比如磁盘正在写入的数据不能被半路打断），内核干脆把这类等待设计成任何信号都传不进去，这不是bug，是故意的保护机制，只是这个保护机制的副作用就是&quot;连SIGKILL都杀不掉&quot;。</p><h2 id="一个真实会发生的场景">一个真实会发生的场景</h2><p>比较典型的情况是挂载了NFS这类网络文件系统，如果NFS服务端突然断开或者不响应了，客户端这边正在往这个挂载点写数据（比如执行<code>mv</code>、<code>cp</code>这类命令），进程就会卡在等待IO返回的环节，直接进入D状态。这时候你<code>kill -9</code>它，命令执行不报错，但进程原地不动，<code>ps -ef</code>看它状态栏赫然写着<code>D</code>，怎么杀都杀不掉，跟撞了铁板一样。</p><h2 id="真正能解决问题的办法">真正能解决问题的办法</h2><p>D状态进程能恢复正常，唯一靠谱的路子是<strong>让它等的那个IO资源重新可用</strong>——上面NFS的例子里，就是把NFS服务端连接恢复，进程发现自己等的数据终于来了，会自己顺利跑完然后正常退出，不需要你手动杀它。如果这个IO资源短期内没法恢复（服务端确实挂了、修不好），比较现实的办法就是<strong>直接重启这台机器</strong>，虽然简单粗暴，但相当于把内核里所有卡住的状态一次性清空，比研究怎么绕过信号机制强行终止要省事得多。</p><p>理论上也存在更极客的做法——写一个内核模块，直接在内核层面遍历进程列表，把D状态进程的状态位强行改掉再杀，网上确实能找到这类实现。但这属于直接动内核数据结构，风险不小，除非你完全清楚自己在干什么、并且这个进程你确认可以安全放弃，不然不建议普通场景下这么折腾。</p><h2 id="跟Load-Average那篇是同一个根">跟Load Average那篇是同一个根</h2><p>看到这里应该能明白，之前那篇讲的&quot;Load Average虚高但CPU不忙&quot;、这篇讲的&quot;kill -9杀不掉进程&quot;，说的其实是同一批D状态进程的两种不同表现——它们不占CPU却被算进负载，它们不听任何指挥却稳稳占着系统资源。下次再遇到进程杀不掉，先别怀疑自己敲错命令，用<code>ps -ef</code>看看它是不是显示着一个<code>D</code>，如果是，问题的根源在等的那个IO资源上，不在你这条<code>kill</code>命令上。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/19/linux-kill-9-cannot-kill-process/</id>
    <link href="https://vpsjq.com/2026/08/19/linux-kill-9-cannot-kill-process/"/>
    <published>2026-08-19T20:00:00.000Z</published>
    <summary>kill -9本该是终极大招，但遇到D状态的进程完全无效，因为内核压根不会把信号传递给这类进程，这跟Load Average虚高是同一个根源。</summary>
    <title>为什么kill -9都杀不死一个进程</title>
    <updated>2026-08-19T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="TIME_WAIT" scheme="https://vpsjq.com/tags/TIME-WAIT/"/>
    <content>
      <![CDATA[<span id="more"></span><p>服务器跑着跑着突然开始报&quot;无可用端口&quot;、连接建立不上，<code>netstat -an | grep TIME_WAIT | wc -l</code>一查，几万个连接卡在TIME_WAIT状态动弹不得。这种情况在跑代理面板、反向代理这类需要频繁建立短连接的服务上特别常见，很多人第一反应是去搜&quot;TIME_WAIT优化&quot;，抄一段网上流传很广的内核参数配置，结果有的抄对了，有的抄了个更大的坑。</p><h2 id="TIME-WAIT不是浪费，是保护机制">TIME_WAIT不是浪费，是保护机制</h2><p>TCP连接主动关闭的那一方（通常是发起连接的客户端，也可能是主动断开的服务端，比如反向代理去连后端），断开之后不会立刻释放这个端口，而是停留在TIME_WAIT状态一段时间（标准时长是2倍的MSL，Linux下这个总时长通常在1分钟左右）。这段时间不是白等的，它在防两件事：一是防止这个连接里还在网络上漂着的延迟旧数据包，被误认成属于后面复用了同一个四元组（本地IP、本地端口、远端IP、远端端口）的新连接；二是保证四次挥手最后那个ACK包如果丢了，对方重传FIN的时候，这边还能正常响应，不会导致连接没法正常关闭。</p><p>这套机制存在了几十年，是TCP协议本身故意这么设计的，不是Linux的失误。</p><h2 id="什么情况下会真的端口不够用">什么情况下会真的端口不够用</h2><p>TIME_WAIT状态的连接占用的端口不能被同样四元组的新连接复用，如果短时间内建立、断开连接的频率特别高（典型场景就是代理服务处理大量客户端的短连接请求），TIME_WAIT堆积的速度超过它自然过期释放的速度，可用端口池就会被迅速耗尽——本地端口范围默认大概是2.8万个左右，<code>tcp_max_tw_buckets</code>这个参数（默认1.8万左右）同时也在限制系统能同时保留的TIME_WAIT连接总数，两者取较小值，就是这台机器实际能扛住的上限。</p><h2 id="网上流传的-修复方法-，一半好用一半是坑">网上流传的&quot;修复方法&quot;，一半好用一半是坑</h2><p>搜TIME_WAIT优化，十有八九会同时搜到两个参数打包出现：<code>tcp_tw_reuse</code>和<code>tcp_tw_recycle</code>，很多老教程让你把两个一起打开，这个建议<strong>只对了一半</strong>。</p><p><code>tcp_tw_reuse</code>相对安全，打开之后能让本机在满足条件（主要靠TCP时间戳判断新旧连接不会冲突）的情况下，快速复用还处于TIME_WAIT状态的端口去发起新连接，主要在你的服务器作为客户端主动连出去的场景（比如反向代理连后端）时有效：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sysctl -w net.ipv4.tcp_tw_reuse=1</span><br></pre></td></tr></table></figure><p><code>tcp_tw_recycle</code>就是那个坑了——这个参数在<strong>Linux 4.12版本已经被彻底移除</strong>，但很多老教程写的时候它还在，抄这些老教程在新内核上会直接报错设置不了；更要命的是，就算是在还保留这个参数的老系统上，打开它对<strong>处于NAT网络后面的客户端极度不友好</strong>——同一个NAT出口下的不同用户，因为时间戳判断逻辑的缺陷，会出现连接被错误拒绝、访问不了服务的情况，公网服务打开这个参数，等于把一部分NAT后面的正常用户直接拒之门外。这不是危言耸听，这个坑连大厂都踩过，后来的内核版本干脆把这个参数直接删掉了断绝后患。</p><p><strong>线上服务不建议打开<code>tcp_tw_recycle</code></strong>，如果是老教程抄来的配置文件里还留着这一行，建议删掉。</p><h2 id="更稳妥的应对方式">更稳妥的应对方式</h2><p>除了<code>tcp_tw_reuse</code>，把<code>tcp_max_tw_buckets</code>适当调大也是常见做法，给系统更大的TIME_WAIT缓冲空间：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sysctl -w net.ipv4.tcp_max_tw_buckets=55000</span><br></pre></td></tr></table></figure><p>如果这台VPS上跑的是<a href="https://vpsjq.com/2025/12/23/%E5%A4%9A%E5%8D%8F%E8%AE%AE%E4%BB%A3%E7%90%86%E4%B8%80%E9%94%AE%E8%84%9A%E6%9C%AC-v2-0-%E8%87%AA%E5%AE%9A%E4%B9%89%E5%A4%9A%E5%8D%8F%E8%AE%AE%E5%85%B1%E5%AD%98/">多协议代理一键脚本</a>这类需要处理大量并发连接的服务，这几个参数值得留意一下，别等到端口耗尽报错了才想起来查。跟之前<a href="https://vpsjq.com/2026/08/18/bbr-no-improvement/">为什么开了BBR网速却感觉一点没提升</a>一样，这类内核网络参数经常被打包在同一份&quot;VPS优化脚本&quot;里一股脑塞给你，弄清楚每个参数具体在做什么、哪些能抄哪些不能抄，比照单全收更重要。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/19/linux-time-wait-port-exhaustion/</id>
    <link href="https://vpsjq.com/2026/08/19/linux-time-wait-port-exhaustion/"/>
    <published>2026-08-19T10:00:00.000Z</published>
    <summary>TIME_WAIT不是bug而是TCP协议的保护机制，但高并发短连接场景下会导致端口耗尽，网上流传的tcp_tw_recycle修复方法反而更危险。</summary>
    <title>Linux服务器为什么会积累大量TIME_WAIT连接，端口都不够用了</title>
    <updated>2026-08-19T10:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="Load-Average" scheme="https://vpsjq.com/tags/Load-Average/"/>
    <content>
      <![CDATA[<span id="more"></span><p>跑一下<code>uptime</code>，Load Average显示8点多，第一反应是CPU要炸了，赶紧<code>top</code>一看，CPU使用率却只有百分之十几，两个数字对不上，人都懵了——到底是该慌还是不该慌？</p><h2 id="这个数字从一开始就不是-CPU使用率">这个数字从一开始就不是&quot;CPU使用率&quot;</h2><p>Load Average统计的是<strong>过去一段时间内，处于可运行状态（Running）和不可中断等待状态（Uninterruptible Sleep，也就是<code>top</code>/<code>ps</code>里显示的D状态）的进程平均数量</strong>。前半句&quot;可运行状态&quot;好理解，是真的在抢CPU的进程；后半句这个D状态才是问题所在——它指的是进程正在等磁盘、网络这类IO操作返回结果，而且这个等待<strong>不能被信号打断</strong>（内核为了保证数据一致性，故意设计成这样，比如磁盘数据还没读完，不能被其他操作插队打断）。</p><p>问题就出在这——<strong>D状态的进程根本不需要CPU，纯粹是在等IO</strong>，但Linux硬是把它算进了Load Average这个本该反映&quot;CPU有多忙&quot;的指标里。所以看到Load Average很高，你其实完全不知道这是CPU真的不够用了，还是磁盘/网络卡住了一堆进程在傻等，这个数字从设计上就没法单独说明问题。</p><h2 id="为什么会这样设计，还得从1993年说起">为什么会这样设计，还得从1993年说起</h2><p>这不是bug，是个有历史渊源的设计决定。查资料的时候翻到一个挺有意思的细节：早在1993年，Linux的设计者就讨论过这个问题，当时的逻辑是——<strong>D状态的等待通常很短暂，很快就会恢复，干脆算作&quot;约等于在排队等CPU&quot;</strong>。有人还做过一个很直观的验证：把系统的磁盘从快的换成慢的，Load Average会跟着涨——单纯换个慢一点的硬盘，跟CPU有没有多忙半点关系都没有，涨的完全是D状态进程数量。这个设计决定从三十多年前定下来，一直沿用到今天，也就一直带着这个容易让人误判的副作用活到了现在。</p><h2 id="真正该看的是CPU使用率和D状态谁在唱主角">真正该看的是CPU使用率和D状态谁在唱主角</h2><p>遇到Load Average偏高，先别急着断定是CPU不够用，跑一下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">top</span><br></pre></td></tr></table></figure><p>看<code>%us</code>（用户进程占用）和<code>%sy</code>（系统进程占用）这两项，如果确实很高，那是真的CPU吃紧了；如果这两项都不高，反倒是<code>%wa</code>（等待IO）这一项很显眼，说明大概率是磁盘IO在拖后腿，不是CPU的问题。</p><p>想更直接地揪出到底是哪些进程在D状态里干等着：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ps aux | grep <span class="string">&quot; D &quot;</span></span><br></pre></td></tr></table></figure><p>看到的进程通常是在等磁盘读写、等网络存储（比如挂载了NFS）响应，顺着这些进程往下查，能更快定位到真正的瓶颈在哪。</p><h2 id="一个大致的判断参考">一个大致的判断参考</h2><p>经验上，<strong>Load Average低于CPU核心数，一般不用太担心</strong>；超过核心数的70%左右，响应速度可能会开始感觉变慢；持续超过核心数好几倍，才是真正需要重视的信号。但记住这个数字终究是&quot;运行中+等待IO&quot;的混合体，光看这一个数字下结论，跟只看体温不看具体哪里发炎一样，容易找错方向。</p><p>之前写过的<a href="https://vpsjq.com/2026/08/17/vps-cpu-steal-time/">VPS的CPU使用率明明很低为什么系统还是卡</a>和这篇讲的是两码事——那篇是CPU被虚拟化层抢走了，这篇是IO把进程卡住了，症状看着都是&quot;感觉很卡&quot;，但排查方向完全不一样，混着看容易把自己绕晕。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/18/linux-load-average-explained/</id>
    <link href="https://vpsjq.com/2026/08/18/linux-load-average-explained/"/>
    <published>2026-08-18T20:00:00.000Z</published>
    <summary>Load Average不是CPU使用率，它把等待磁盘IO的进程也算了进去，这个设计从1993年就埋下了让无数人误判的伏笔。</summary>
    <title>Linux服务器的Load Average很高，是不是要出问题了</title>
    <updated>2026-08-18T20:00:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>vpsjq.com</name>
    </author>
    <category term="Linux优化" scheme="https://vpsjq.com/categories/Linux%E4%BC%98%E5%8C%96/"/>
    <category term="BBR原理" scheme="https://vpsjq.com/tags/BBR%E5%8E%9F%E7%90%86/"/>
    <content>
      <![CDATA[<span id="more"></span><p>网上一搜BBR教程，标题动不动就是&quot;网速提升3倍&quot;“涡轮增压”，跟着敲完三行命令，兴冲冲测个速，发现数字跟开之前差不多，甚至一模一样。很多人到这一步就得出结论：&quot;BBR就是个噱头，没用。&quot;这个结论下得有点冤——问题往往不在BBR本身，而在对它的期待从一开始就搞错了方向。</p><h2 id="BBR到底在优化什么">BBR到底在优化什么</h2><p>BBR是一种拥塞控制算法，它管的是<strong>数据包在网络出现拥堵时该怎么应对</strong>，不是给你的服务器凭空变出更多带宽。传统的Cubic算法看到丢包就本能地大幅减速，哪怕丢包只是网络抖动一下，跟真正的拥堵没关系，它也会误判，结果该跑的带宽没跑满。BBR不这么干，它会持续估算这条链路真实的带宽上限和往返时间，尽量把发送速率精确地贴着这个上限走，不会因为一点风吹草动就自废武功。</p><p>节点论坛上有个说法挺形象：&quot;垃圾线路，你用BBR，50分，调优TCP，55分；好的线路，你用BBR，90分，调优TCP，95分。&quot;意思很直接——<strong>BBR是给线路本身的质量做加法，不是从0变出100</strong>。这条链路物理上能跑多快是有天花板的，BBR顶多帮你更接近这个天花板，天花板本身有多高，它说了不算。</p><h2 id="什么情况下BBR基本感觉不出差别">什么情况下BBR基本感觉不出差别</h2><p>如果你测速的目标是同城、甚至同一个数据中心内部的节点，延迟本来就低到几毫秒，链路干净得几乎不丢包，这种场景下Cubic和BBR的表现差距很小——因为压根没什么&quot;拥堵&quot;需要被更聪明地应对，两种算法交出的答卷自然相差无几。BBR真正大放异彩的场景，是<strong>跨洋、跨运营商、延迟动辄上百毫秒、偶尔还丢包</strong>的链路，这种情况下传统算法的保守应对方式会白白浪费掉大量本该能用上的带宽，BBR的优势才真正体现出来。拿同城测速的结果去否定BBR，本身就是拿错了考卷。</p><h2 id="除了原理，还有几个技术上的坑">除了原理，还有几个技术上的坑</h2><p>除了期望值搞错，也有不少是<strong>BBR压根没真正生效</strong>导致的：</p><p><code>sysctl</code>设置完<code>tcp_congestion_control=bbr</code>，不代表配置全对了，配套的队列调度算法<code>qdisc</code>同样得设成<code>fq</code>，不然内核实际用的还是默认的<code>pfifo_fast</code>，BBR跑起来的效果会打折扣：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sysctl net.core.default_qdisc</span><br></pre></td></tr></table></figure><p>确认输出是<code>fq</code>（或者较新内核用<code>fq_codel</code>），不是别的。</p><p>老内核压根不支持BBR，<code>sysctl</code>命令执行时不报错，看着像是设置成功了，重启之后拿命令一验证，发现又悄悄变回了<code>cubic</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sysctl net.ipv4.tcp_congestion_control</span><br></pre></td></tr></table></figure><p>如果验证结果不是<code>bbr</code>，说明内核版本不够，得先升级内核才行，不是配置写错了。</p><p>还有个容易被忽略的情况——如果这台VPS上同时跑着好几条代理连接，物理带宽早就被这几条连接瓜分完了，单条连接测出来的速度自然上不去，这不是BBR的锅，是带宽本身已经被占满了。</p><h2 id="该怎么判断BBR到底有没有用">该怎么判断BBR到底有没有用</h2><p>最靠谱的办法不是跟别人的&quot;提升3倍&quot;截图比，是<strong>用自己这台机器的实际线路，开BBR前后各测一次，对比同样的目标、同样的时间段</strong>。如果这台VPS本身线路质量就一般，之前提过的<a href="https://vpsjq.com/2026/08/17/vps-cpu-steal-time/">CPU使用率明明很低系统却卡</a>那篇里讲的超售问题，同样会拖累网络表现，BBR再怎么调也调不出一条干净的物理线路；测速工具的选择也有讲究，具体可以看<a href="https://vpsjq.com/2026/08/15/vps-speedtest-tools/">VPS网络测速用什么工具最好</a>，用对工具测对场景，才能看出BBR到底帮没帮上忙。</p>]]>
    </content>
    <id>https://vpsjq.com/2026/08/18/bbr-no-improvement/</id>
    <link href="https://vpsjq.com/2026/08/18/bbr-no-improvement/"/>
    <published>2026-08-18T10:00:00.000Z</published>
    <summary>BBR不是万能加速器，它优化的是拥塞控制方式，不能凭空变出物理带宽，搞清楚这个原理才知道为什么有的机器开了没感觉。</summary>
    <title>为什么开了BBR，网速却感觉一点没提升</title>
    <updated>2026-08-18T10:00:00.000Z</updated>
  </entry>
</feed>
