<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>经验总结 on 小葱的学术基站</title><link>https://xiaocong.site/categories/tricks/</link><description>Recent content in 经验总结 on 小葱的学术基站</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Tue, 14 Apr 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://xiaocong.site/categories/tricks/index.xml" rel="self" type="application/rss+xml"/><item><title>记一次 QE 编译排障：从 FHC 收敛坑到 oneAPI/MKL 与 AOCL 优化</title><link>https://xiaocong.site/tricks/build_qe_with_oneapi/</link><pubDate>Tue, 14 Apr 2026 00:00:00 +0800</pubDate><guid>https://xiaocong.site/tricks/build_qe_with_oneapi/</guid><description>&lt;h1 id="记一次-qe-编译排障从-fhc-收敛坑到-oneapimkl-与-aocl-优化"&gt;&lt;a href="#%e8%ae%b0%e4%b8%80%e6%ac%a1-qe-%e7%bc%96%e8%af%91%e6%8e%92%e9%9a%9c%e4%bb%8e-fhc-%e6%94%b6%e6%95%9b%e5%9d%91%e5%88%b0-oneapimkl-%e4%b8%8e-aocl-%e4%bc%98%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;记一次 QE 编译排障：从 FHC 收敛坑到 oneAPI/MKL 与 AOCL 优化
&lt;/h1&gt;&lt;p&gt;作为一个刚开始认真折腾第一性原理计算的新人，我原本以为编译 &lt;code&gt;Quantum ESPRESSO&lt;/code&gt; 只是“把依赖装好，然后 &lt;code&gt;cmake &amp;amp;&amp;amp; make&lt;/code&gt;”这么简单。结果这一轮下来，我对 HPC 环境、数值库、编译器、运行时链接，以及“同样的版本号不代表同样的行为”这句话，有了非常痛的体会。&lt;/p&gt;
&lt;p&gt;这篇文章记录一次完整的踩坑过程：我为什么重新在 CPU 集群上编译 QE，为什么一度被“同一个构型、同样版本的 QE，却在不同环境下一个能收敛一个不收敛”这件事气到，以及最后是如何把问题逐渐收束到 &lt;strong&gt;Libxc 的 FHC 选项&lt;/strong&gt;、&lt;strong&gt;oneAPI + MKL&lt;/strong&gt; 的优化路径，以及 &lt;strong&gt;AOCC 并不等于 AOCL&lt;/strong&gt; 这几个关键点上的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一起因29654-也不是万能的"&gt;&lt;a href="#%e4%b8%80%e8%b5%b7%e5%9b%a029654-%e4%b9%9f%e4%b8%8d%e6%98%af%e4%b8%87%e8%83%bd%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;一、起因：2×9654 也不是万能的
&lt;/h2&gt;&lt;p&gt;我最开始的想法其实很朴素：手里明明有 AMD EPYC 9654 这样的 CPU 节点，跑一些 QE 单点构型重标注，应该不会太慢吧？&lt;/p&gt;
&lt;p&gt;但实际测试下来，我发现我的 case 跑得并不理想。尤其是批量构型一多，单个 case 的耗时就很刺眼。于是我开始想：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;既然 CPU 集群上已经装好了 &lt;code&gt;Intel oneAPI&lt;/code&gt; 和 &lt;code&gt;MKL&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;而我这批计算本质上又是 CPU 密集型；&lt;/li&gt;
&lt;li&gt;那么是否应该在 Intel CPU 集群上重新编译一版 QE，走 &lt;code&gt;oneAPI + MKL&lt;/code&gt; 路线，看看能不能把性能和稳定性一起拉起来？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这个想法后来证明完全正确。只是我没想到，真正先把我绊倒的，不是性能，而是&lt;strong&gt;收敛性&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二最让我气愤的一幕同一个构型同版本-qe居然一个收敛一个不收敛"&gt;&lt;a href="#%e4%ba%8c%e6%9c%80%e8%ae%a9%e6%88%91%e6%b0%94%e6%84%a4%e7%9a%84%e4%b8%80%e5%b9%95%e5%90%8c%e4%b8%80%e4%b8%aa%e6%9e%84%e5%9e%8b%e5%90%8c%e7%89%88%e6%9c%ac-qe%e5%b1%85%e7%84%b6%e4%b8%80%e4%b8%aa%e6%94%b6%e6%95%9b%e4%b8%80%e4%b8%aa%e4%b8%8d%e6%94%b6%e6%95%9b" class="header-anchor"&gt;&lt;/a&gt;二、最让我气愤的一幕：同一个构型、同版本 QE，居然一个收敛一个不收敛
&lt;/h2&gt;&lt;p&gt;最开始我遇到的问题不是“算得慢”，而是“&lt;strong&gt;算不下来&lt;/strong&gt;”。&lt;/p&gt;
&lt;p&gt;症状很典型：某些构型在一个环境里可以继续往下跑，而在另一个环境里却报类似下面的错误：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error in routine c_bands (1):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;too many bands are not converged
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;或者更直接一点，在第一步、前几步就崩掉。&lt;/p&gt;
&lt;p&gt;最让我难受的是：&lt;strong&gt;QE 版本号明明一样。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我一度非常恼火。因为在一个 DFT 新手的直觉里，版本一样、输入一样、赝势一样，结果至少不应该差这么离谱。后来我才真正认识到，在 HPC 科学计算里，所谓“环境”从来都不只是一个版本号。它背后至少还包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编译器；&lt;/li&gt;
&lt;li&gt;MPI 实现；&lt;/li&gt;
&lt;li&gt;BLAS/LAPACK/FFT 后端；&lt;/li&gt;
&lt;li&gt;外部库的版本与编译选项；&lt;/li&gt;
&lt;li&gt;动态库还是静态库；&lt;/li&gt;
&lt;li&gt;运行时究竟加载了哪一份库。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也正是在这一轮排查中，我第一次真正理解了：&lt;strong&gt;“软件版本相同”根本不等于“数值路径相同”。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="三第一轮排查怀疑-mpi怀疑脚本怀疑链接最后聚焦到-libxc"&gt;&lt;a href="#%e4%b8%89%e7%ac%ac%e4%b8%80%e8%bd%ae%e6%8e%92%e6%9f%a5%e6%80%80%e7%96%91-mpi%e6%80%80%e7%96%91%e8%84%9a%e6%9c%ac%e6%80%80%e7%96%91%e9%93%be%e6%8e%a5%e6%9c%80%e5%90%8e%e8%81%9a%e7%84%a6%e5%88%b0-libxc" class="header-anchor"&gt;&lt;/a&gt;三、第一轮排查：怀疑 MPI、怀疑脚本、怀疑链接，最后聚焦到 Libxc
&lt;/h2&gt;&lt;p&gt;起初我的排查路线比较朴素，主要看这些：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;srun&lt;/code&gt; 和 &lt;code&gt;mpirun&lt;/code&gt; 的差异；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pw.x&lt;/code&gt; 究竟链接的是哪一套 MPI；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; 是否正确；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;libxc&lt;/code&gt; 是否真的被启用；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MKL&lt;/code&gt; 是否被正确加载；&lt;/li&gt;
&lt;li&gt;运行时到底吃的是哪份 &lt;code&gt;libxc&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其中一个重要发现是：&lt;strong&gt;不同节点上的 &lt;code&gt;pw.x&lt;/code&gt; 二进制其实根本不是同一个构建结果&lt;/strong&gt;。
虽然源码版本一致，但哈希值不同，依赖链也不同。换句话说，我之前以为自己在比较“同一程序在不同机器上的表现”，实际上是在比较“两套不同构建结果的 QE”。&lt;/p&gt;
&lt;p&gt;进一步查下去，我开始逐渐把怀疑集中到 &lt;code&gt;Libxc&lt;/code&gt; 上，尤其是对 &lt;code&gt;SCAN&lt;/code&gt; 这类泛函的支持。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="四关键转折fhc-才是那个真正影响能不能算的元凶之一"&gt;&lt;a href="#%e5%9b%9b%e5%85%b3%e9%94%ae%e8%bd%ac%e6%8a%98fhc-%e6%89%8d%e6%98%af%e9%82%a3%e4%b8%aa%e7%9c%9f%e6%ad%a3%e5%bd%b1%e5%93%8d%e8%83%bd%e4%b8%8d%e8%83%bd%e7%ae%97%e7%9a%84%e5%85%83%e5%87%b6%e4%b9%8b%e4%b8%80" class="header-anchor"&gt;&lt;/a&gt;四、关键转折：FHC 才是那个真正影响“能不能算”的元凶之一
&lt;/h2&gt;&lt;p&gt;后来最关键的线索出现了：
我发现自己之前的 &lt;code&gt;libxc&lt;/code&gt; 编译里，&lt;strong&gt;FHC 是开启的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而在后续排查中，我注意到一个非常重要的事实：对于 &lt;code&gt;SCAN&lt;/code&gt; 这类 &lt;code&gt;meta-GGA&lt;/code&gt; 泛函，&lt;code&gt;libxc&lt;/code&gt; 的 &lt;strong&gt;Fermi hole curvature（FHC）&lt;/strong&gt; 选项可能直接影响数值稳定性。更具体地说，&lt;strong&gt;它不是“算得快不快”的问题，而是“同一个构型到底能不能稳定收敛”的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这就解释了为什么我会遇到那么诡异的现象：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入文件一样；&lt;/li&gt;
&lt;li&gt;赝势一样；&lt;/li&gt;
&lt;li&gt;QE 版本号一样；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但某个构型在一套环境里会在前几步就报 &lt;code&gt;too many bands are not converged&lt;/code&gt;，而在另一套环境里却能正常收敛。&lt;/p&gt;
&lt;p&gt;后来当我重新编译一套 &lt;strong&gt;关闭 FHC 的 libxc&lt;/strong&gt; 后，原本不稳定的构型居然就能继续稳稳往下跑，并最终完成收敛。这一刻我真的有点震撼：
&lt;strong&gt;一个编译选项，足以决定“同一个构型到底是算得下来，还是算不下来”。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;也正因为这个经历，我现在对 &lt;code&gt;libxc&lt;/code&gt; 的态度已经完全变了。以前总觉得它只是“QE 的一个依赖”，现在我会把它视为：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;对某些泛函来说，&lt;code&gt;libxc&lt;/code&gt; 的版本与编译方式，本身就是数值模型的一部分。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="五第二个坑cmake-升太新也会出问题"&gt;&lt;a href="#%e4%ba%94%e7%ac%ac%e4%ba%8c%e4%b8%aa%e5%9d%91cmake-%e5%8d%87%e5%a4%aa%e6%96%b0%e4%b9%9f%e4%bc%9a%e5%87%ba%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;五、第二个坑：CMake 升太新，也会出问题
&lt;/h2&gt;&lt;p&gt;解决完 &lt;code&gt;libxc/FHC&lt;/code&gt; 这条主线后，我又踩到了另一个很典型、但一开始并不显眼的坑：&lt;strong&gt;CMake 版本太新。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;因为系统自带的 CMake 太旧，所以我当时一口气把 CMake 升到了 &lt;code&gt;4.3.1&lt;/code&gt;。按直觉看，这似乎是“升级工具链、拥抱新版本”的标准操作。但实际情况是，新版 CMake 对一些旧模块、旧写法更严格，结果导致 QE 构建中的某些模块开始报兼容性问题。&lt;/p&gt;
&lt;p&gt;最直接的影响包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内置 LAPACK 相关模块报错；&lt;/li&gt;
&lt;li&gt;MBD 相关模块出现兼容性问题；&lt;/li&gt;
&lt;li&gt;某些旧语法需要额外策略兼容。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后的处理思路不是回退一切，而是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;放弃 QE 内置数学库路线&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;直接切到 &lt;strong&gt;oneAPI + MKL&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;FFT 路线指定为 &lt;code&gt;Intel_DFTI&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;再通过追加&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-DCMAKE_POLICY_VERSION_MINIMUM=3.5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;让新版 CMake 对旧模块“网开一面”。&lt;/p&gt;
&lt;p&gt;这一段很有代表性。它说明一件事：
&lt;strong&gt;“新”不一定直接等于“更适合生产环境”。&lt;/strong&gt;
在科学计算软件生态里，一个过新的构建工具，反而可能把原本就不够现代化的上层项目折腾出额外兼容性问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="六我最后采用的生产路线oneapi--mkl--no-fhc-libxc"&gt;&lt;a href="#%e5%85%ad%e6%88%91%e6%9c%80%e5%90%8e%e9%87%87%e7%94%a8%e7%9a%84%e7%94%9f%e4%ba%a7%e8%b7%af%e7%ba%bfoneapi--mkl--no-fhc-libxc" class="header-anchor"&gt;&lt;/a&gt;六、我最后采用的生产路线：oneAPI + MKL + no-FHC libxc
&lt;/h2&gt;&lt;p&gt;在 Intel CPU 集群上，我最终确定下来的主线是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;编译器：&lt;code&gt;mpiicx / mpiifx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MPI：Intel MPI&lt;/li&gt;
&lt;li&gt;BLAS/LAPACK：MKL&lt;/li&gt;
&lt;li&gt;FFT：&lt;code&gt;Intel_DFTI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Libxc：单独编译，&lt;strong&gt;关闭 FHC&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;QE：CMake 构建&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我的环境变量大致是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Libxc 相关路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;LIBXC_SRC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/libxc_src&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;LIBXC_BUILD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/libxc_src/build&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;LIBXC_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/libxc/6.2.2-nofhc-ifx&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Quantum ESPRESSO (QE) 相关路径&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;QE_SRC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/q-e-qe-7.3.1&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;QE_BUILD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/q-e-qe-7.3.1/build&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="n"&gt;QE_PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/opt/qe-7.3.1-ifx-cmake-nofhc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;最终使用的 QE 配置命令核心部分是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cmake &amp;#34;$QE_SRC&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_C_COMPILER=mpiicx \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_Fortran_COMPILER=mpiifx \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_BUILD_TYPE=Release \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_C_FLAGS=&amp;#34;-O2&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_Fortran_FLAGS=&amp;#34;-O2&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DQE_ENABLE_MPI=ON \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DQE_ENABLE_OPENMP=ON \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DQE_ENABLE_LIBXC=ON \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DQE_BLAS_INTERNAL=OFF \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DQE_LAPACK_INTERNAL=OFF \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DQE_FFTW_VENDOR=Intel_DFTI \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_PREFIX_PATH=&amp;#34;$LIBXC_PREFIX&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DLibxc_ROOT=&amp;#34;$LIBXC_PREFIX&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_INSTALL_PREFIX=&amp;#34;$QE_PREFIX&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_INSTALL_RPATH=&amp;#34;$LIBXC_PREFIX/lib;/opt/intel/oneapi/mpi/2021.11/lib;/opt/intel/oneapi/compiler/2024.0/lib&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_INSTALL_RPATH_USE_LINK_PATH=ON \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -DCMAKE_POLICY_VERSION_MINIMUM=3.5
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;而 &lt;code&gt;libxc&lt;/code&gt; 这边，最关键的一条就是：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-DDISABLE_FHC=ON
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这条看似不起眼，但在我的这批 &lt;code&gt;SCAN&lt;/code&gt; 构型上，几乎就是决定生死的选项。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="七一个很容易误判的点没报缺库不代表你真的理解了链接方式"&gt;&lt;a href="#%e4%b8%83%e4%b8%80%e4%b8%aa%e5%be%88%e5%ae%b9%e6%98%93%e8%af%af%e5%88%a4%e7%9a%84%e7%82%b9%e6%b2%a1%e6%8a%a5%e7%bc%ba%e5%ba%93%e4%b8%8d%e4%bb%a3%e8%a1%a8%e4%bd%a0%e7%9c%9f%e7%9a%84%e7%90%86%e8%a7%a3%e4%ba%86%e9%93%be%e6%8e%a5%e6%96%b9%e5%bc%8f" class="header-anchor"&gt;&lt;/a&gt;七、一个很容易误判的点：没报缺库，不代表你真的理解了链接方式
&lt;/h2&gt;&lt;p&gt;中间我还遇到一个非常容易让人误判的问题：
有时候 &lt;code&gt;ldd pw.x&lt;/code&gt; 里看不到 &lt;code&gt;libxc.so&lt;/code&gt;，但程序并不报缺库，而且照样能跑。&lt;/p&gt;
&lt;p&gt;这让我一开始很困惑，以为是不是 &lt;code&gt;libxc&lt;/code&gt; 根本没连上。后来才意识到，这里还要区分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;动态链接&lt;/strong&gt;：&lt;code&gt;ldd&lt;/code&gt; 能直接看到 &lt;code&gt;libxc.so&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态链接&lt;/strong&gt;：&lt;code&gt;ldd&lt;/code&gt; 看不到，但构建和符号里实际上已经吃进去了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于我这种刚开始折腾 DFT 环境的人来说，这又是一个很有教育意义的坑：
&lt;strong&gt;“程序能跑”和“你知道它为什么能跑”是两件事。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;科学计算环境里，真正麻烦的从来不是出错，而是那种“看起来能跑，但你并没有完全搞清楚依赖链”的状态。因为那会让后续复现和迁移都变得非常危险。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="八另一个重要认知我其实只是用了-aocc-编译器并没有真正用到-aocl"&gt;&lt;a href="#%e5%85%ab%e5%8f%a6%e4%b8%80%e4%b8%aa%e9%87%8d%e8%a6%81%e8%ae%a4%e7%9f%a5%e6%88%91%e5%85%b6%e5%ae%9e%e5%8f%aa%e6%98%af%e7%94%a8%e4%ba%86-aocc-%e7%bc%96%e8%af%91%e5%99%a8%e5%b9%b6%e6%b2%a1%e6%9c%89%e7%9c%9f%e6%ad%a3%e7%94%a8%e5%88%b0-aocl" class="header-anchor"&gt;&lt;/a&gt;八、另一个重要认知：我其实只是用了 AOCC 编译器，并没有真正用到 AOCL
&lt;/h2&gt;&lt;p&gt;当 Intel 这套新环境跑顺之后，我又回头看了一眼自己之前在 AMD 9654 节点上的构建，结果发现了一个相当扎心的事实：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;我之前只是用了 &lt;strong&gt;AOCC 编译 QE&lt;/strong&gt;，但根本没有真正用 &lt;strong&gt;AOCL&lt;/strong&gt;。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;也就是说，我当时走的是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AOCC 编译器；&lt;/li&gt;
&lt;li&gt;但 BLAS/LAPACK 用的是 &lt;strong&gt;internal&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;FFT 用的也是 &lt;strong&gt;internal&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;难怪性能那么一般。&lt;/p&gt;
&lt;p&gt;这一点后来让我印象特别深。因为在很多人（包括之前的我）的直觉里，会下意识地把：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“用了 AMD 编译器”&lt;/li&gt;
&lt;li&gt;和“用了 AMD 优化数学库”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两件事混为一谈。&lt;/p&gt;
&lt;p&gt;但实际上完全不是一回事。&lt;/p&gt;
&lt;h3 id="aocc-是编译器"&gt;&lt;a href="#aocc-%e6%98%af%e7%bc%96%e8%af%91%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;AOCC 是编译器
&lt;/h3&gt;&lt;h3 id="aocl-才是数学库加速栈"&gt;&lt;a href="#aocl-%e6%89%8d%e6%98%af%e6%95%b0%e5%ad%a6%e5%ba%93%e5%8a%a0%e9%80%9f%e6%a0%88" class="header-anchor"&gt;&lt;/a&gt;AOCL 才是数学库加速栈
&lt;/h3&gt;&lt;p&gt;如果你只是用了 AOCC，却仍然把：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;QE_BLAS_INTERNAL=ON&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QE_LAPACK_INTERNAL=ON&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;QE_FFTW_VENDOR=Internal&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;写进 CMake 里，那本质上你并没有把 9654 这颗大 CPU 真正榨出来。&lt;/p&gt;
&lt;p&gt;这也是我后来特别想写下来的原因之一。因为它真的很容易让人产生一种错觉：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“我明明已经在高端节点上编译了，为什么还是不快？”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;答案可能并不是 CPU 不够强，而是你根本没有把正确的后端接上去。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="九结果生产效率一下子进入了另一个量级"&gt;&lt;a href="#%e4%b9%9d%e7%bb%93%e6%9e%9c%e7%94%9f%e4%ba%a7%e6%95%88%e7%8e%87%e4%b8%80%e4%b8%8b%e5%ad%90%e8%bf%9b%e5%85%a5%e4%ba%86%e5%8f%a6%e4%b8%80%e4%b8%aa%e9%87%8f%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;九、结果：生产效率一下子进入了另一个量级
&lt;/h2&gt;&lt;p&gt;当 oneAPI + MKL + no-FHC 的 Intel 版本跑通后，我再回头看自己的批量任务，心态已经完全不一样了。&lt;/p&gt;
&lt;p&gt;以前看着几千个构型，会本能地觉得：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这是不是又要跑很多天？&lt;/li&gt;
&lt;li&gt;哪个节点上挂了怎么办？&lt;/li&gt;
&lt;li&gt;收敛不稳会不会拖垮整批任务？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但当单个 24 原子构型在 4 核下不到两分钟就能算完时，量级立刻变了。
一旦批量并发开起来，原来觉得非常沉重的 DFT 重标注任务，突然就变成了一个可以认真规划、快速交付的工作流。&lt;/p&gt;
&lt;p&gt;这一刻我真正体会到：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;HPC 科学计算里的“调优”，从来不是锦上添花，而是决定你能不能把一件事从“理论可行”变成“工程上可执行”。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="十这次最大的收获不是把-qe-编译成功而是学会了如何看待环境"&gt;&lt;a href="#%e5%8d%81%e8%bf%99%e6%ac%a1%e6%9c%80%e5%a4%a7%e7%9a%84%e6%94%b6%e8%8e%b7%e4%b8%8d%e6%98%af%e6%8a%8a-qe-%e7%bc%96%e8%af%91%e6%88%90%e5%8a%9f%e8%80%8c%e6%98%af%e5%ad%a6%e4%bc%9a%e4%ba%86%e5%a6%82%e4%bd%95%e7%9c%8b%e5%be%85%e7%8e%af%e5%a2%83" class="header-anchor"&gt;&lt;/a&gt;十、这次最大的收获，不是把 QE 编译成功，而是学会了如何看待“环境”
&lt;/h2&gt;&lt;p&gt;如果只是把结论写成一句话，那就是：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;科学计算里，版本号只是入口，环境才是本体。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这次折腾之后，我对下面几件事的理解都比以前深得多：&lt;/p&gt;
&lt;h3 id="1-libxc-不是无脑依赖"&gt;&lt;a href="#1-libxc-%e4%b8%8d%e6%98%af%e6%97%a0%e8%84%91%e4%be%9d%e8%b5%96" class="header-anchor"&gt;&lt;/a&gt;1. &lt;code&gt;libxc&lt;/code&gt; 不是无脑依赖
&lt;/h3&gt;&lt;p&gt;它的版本、编译方式，尤其是像 &lt;code&gt;FHC&lt;/code&gt; 这样的选项，可能直接改变收敛行为。&lt;/p&gt;
&lt;h3 id="2-编译器不等于数学库"&gt;&lt;a href="#2-%e7%bc%96%e8%af%91%e5%99%a8%e4%b8%8d%e7%ad%89%e4%ba%8e%e6%95%b0%e5%ad%a6%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;2. 编译器不等于数学库
&lt;/h3&gt;&lt;p&gt;AOCC 不等于 AOCL；oneAPI 也不只是编译器，还包含了一整套数值后端生态。&lt;/p&gt;
&lt;h3 id="3-能跑不够"&gt;&lt;a href="#3-%e8%83%bd%e8%b7%91%e4%b8%8d%e5%a4%9f" class="header-anchor"&gt;&lt;/a&gt;3. “能跑”不够
&lt;/h3&gt;&lt;p&gt;真正的生产环境应该满足三件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;能跑；&lt;/li&gt;
&lt;li&gt;稳定；&lt;/li&gt;
&lt;li&gt;快。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;少任何一项，后面都要付出代价。&lt;/p&gt;
&lt;h3 id="4-hpc-调优是科研生产力的一部分"&gt;&lt;a href="#4-hpc-%e8%b0%83%e4%bc%98%e6%98%af%e7%a7%91%e7%a0%94%e7%94%9f%e4%ba%a7%e5%8a%9b%e7%9a%84%e4%b8%80%e9%83%a8%e5%88%86" class="header-anchor"&gt;&lt;/a&gt;4. HPC 调优是科研生产力的一部分
&lt;/h3&gt;&lt;p&gt;有时候，手握 9654 这样的硬件“利器”，但没有把库链、FFT、BLAS、LAPACK、MPI、外部依赖配对好，那你其实并没有真正拥有它的性能。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="结语新手也能折腾但最好别只靠运气"&gt;&lt;a href="#%e7%bb%93%e8%af%ad%e6%96%b0%e6%89%8b%e4%b9%9f%e8%83%bd%e6%8a%98%e8%85%be%e4%bd%86%e6%9c%80%e5%a5%bd%e5%88%ab%e5%8f%aa%e9%9d%a0%e8%bf%90%e6%b0%94" class="header-anchor"&gt;&lt;/a&gt;结语：新手也能折腾，但最好别只靠运气
&lt;/h2&gt;&lt;p&gt;回头看，这整个过程其实非常像我刚接触第一性原理计算时的状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对软件栈的认识是碎片化的；&lt;/li&gt;
&lt;li&gt;遇到错误时容易把问题归结为“玄学不收敛”；&lt;/li&gt;
&lt;li&gt;对 HPC 环境里“编译方式”和“运行结果”之间的关系缺乏真正的直觉。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但也正是因为这一轮完整踩坑，我第一次认真意识到：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;编译不是安装，部署不是复制，调优也不是可有可无的附加项。&lt;/strong&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;尤其是在科学计算里，很多时候“为什么同样的 case 这个环境能算、那个环境不能算”，背后未必是什么高深的理论问题，而可能就是一个你之前根本不会在意的库选项。&lt;/p&gt;
&lt;p&gt;所以如果你也在新环境里折腾 QE、CP2K、LAMMPS 这类软件，我想说的一点经验是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不要只记“命令怎么敲”；&lt;/li&gt;
&lt;li&gt;一定要顺手把&lt;strong&gt;构建参数、依赖版本、路径和踩过的坑写下来&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因为等你真的踩过一次之后就会发现，
&lt;strong&gt;最可怕的不是报错，而是你明明修好了，却不知道自己到底修好了什么。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而这，恐怕才是从“会运行程序”到“能掌控计算环境”之间，最重要的一步。&lt;/p&gt;</description></item><item><title>Github自动部署</title><link>https://xiaocong.site/tricks/github%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2/</link><pubDate>Tue, 10 Mar 2026 00:00:00 +0800</pubDate><guid>https://xiaocong.site/tricks/github%E8%87%AA%E5%8A%A8%E9%83%A8%E7%BD%B2/</guid><description>&lt;p&gt;你是否也曾经历过这样的循环：在本地写完一篇精彩的博客，然后手动运行 &lt;code&gt;hugo&lt;/code&gt; 生成静态文件，接着打开 FTP 客户端或敲下一串 &lt;code&gt;scp&lt;/code&gt; 命令，把 &lt;code&gt;public&lt;/code&gt; 文件夹传到服务器，最后还要解压、覆盖……一次两次还能忍受，但每次都要重复这套“搬砖”流程，不仅消磨写作热情，还容易出错。&lt;/p&gt;
&lt;p&gt;今天，我们来彻底终结这种低效的手动部署。借助 &lt;strong&gt;GitHub Actions&lt;/strong&gt;，我们可以打造一条完全自动化的部署流水线：&lt;strong&gt;只需在本地执行 &lt;code&gt;git push&lt;/code&gt;，几十秒后，你的云服务器上的博客就会自动更新到最新版本。&lt;/strong&gt; 全程无需手动连接服务器，一切都由 GitHub 免费提供的云虚拟机完成。&lt;/p&gt;
&lt;h2 id="自动化部署的核心原理"&gt;&lt;a href="#%e8%87%aa%e5%8a%a8%e5%8c%96%e9%83%a8%e7%bd%b2%e7%9a%84%e6%a0%b8%e5%bf%83%e5%8e%9f%e7%90%86" class="header-anchor"&gt;&lt;/a&gt;自动化部署的核心原理
&lt;/h2&gt;&lt;p&gt;整个流水线的逻辑非常清晰，像一个高效的工厂流水线：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;触发&lt;/strong&gt;：当你将代码推送到 GitHub 仓库的 &lt;code&gt;main&lt;/code&gt; 分支（或其他指定分支）时，流水线自动启动。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建环境&lt;/strong&gt;：GitHub 分配一台临时的 Ubuntu 容器，里面预装了最新的软件包。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;准备代码&lt;/strong&gt;：自动拉取你的博客源码，包括所有子模块（比如 Hugo 主题 Stack）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;生成静态网站&lt;/strong&gt;：安装指定版本的 Hugo（extended 版），运行 &lt;code&gt;hugo --minify&lt;/code&gt; 命令，生成压缩后的静态网页文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;同步到服务器&lt;/strong&gt;：通过 SSH 协议，利用 &lt;code&gt;rsync&lt;/code&gt; 工具将生成的 &lt;code&gt;public&lt;/code&gt; 目录内容增量同步到你云服务器的 Nginx 网站根目录。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;整个过程自动化、可重复、可追溯，而且完全免费。&lt;/p&gt;
&lt;h2 id="打通服务器的免密通道"&gt;&lt;a href="#%e6%89%93%e9%80%9a%e6%9c%8d%e5%8a%a1%e5%99%a8%e7%9a%84%e5%85%8d%e5%af%86%e9%80%9a%e9%81%93" class="header-anchor"&gt;&lt;/a&gt;打通服务器的“免密通道”
&lt;/h2&gt;&lt;p&gt;要让 GitHub 的机器人能够登录你的云服务器并写入文件，必须配置 SSH 密钥认证。这是整个配置中最容易让人混淆的一步，核心原则是：&lt;strong&gt;私钥留给 GitHub，公钥锁在服务器上。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;打开本地终端，执行以下命令生成一对专用的部署密钥（这里使用 &lt;code&gt;ed25519&lt;/code&gt; 算法，安全性更高，且不需要输入密码）：&lt;/p&gt;
&lt;p&gt;bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh-keygen -t ed25519 -f ~/.ssh/github_deploy_key -N &amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;执行后会生成两个文件：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~/.ssh/github_deploy_key&lt;/code&gt; —— &lt;strong&gt;私钥（钥匙）&lt;/strong&gt;：&lt;strong&gt;绝对不能泄露&lt;/strong&gt;，需要交给 GitHub。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;~/.ssh/github_deploy_key.pub&lt;/code&gt; —— &lt;strong&gt;公钥（锁）&lt;/strong&gt;：需要安装在云服务器上。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="将私钥交给-github"&gt;&lt;a href="#%e5%b0%86%e7%a7%81%e9%92%a5%e4%ba%a4%e7%bb%99-github" class="header-anchor"&gt;&lt;/a&gt;将私钥交给 GitHub
&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;在浏览器中打开你的 GitHub 仓库，依次进入 &lt;strong&gt;Settings&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Secrets and variables&lt;/strong&gt; -&amp;gt; &lt;strong&gt;Actions&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;New repository secret&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt; 填写 &lt;code&gt;SSH_PRIVATE_KEY&lt;/code&gt;（注意大小写，后面会用到）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Secret&lt;/strong&gt; 中粘贴 &lt;strong&gt;私钥文件 &lt;code&gt;github_deploy_key&lt;/code&gt; 的完整内容&lt;/strong&gt;，包括 &lt;code&gt;-----BEGIN OPENSSH PRIVATE KEY-----&lt;/code&gt; 和 &lt;code&gt;-----END OPENSSH PRIVATE KEY-----&lt;/code&gt; 这两行。&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;Add secret&lt;/strong&gt; 保存。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="将公钥锁到服务器上"&gt;&lt;a href="#%e5%b0%86%e5%85%ac%e9%92%a5%e9%94%81%e5%88%b0%e6%9c%8d%e5%8a%a1%e5%99%a8%e4%b8%8a" class="header-anchor"&gt;&lt;/a&gt;将公钥锁到服务器上
&lt;/h3&gt;&lt;p&gt;登录你的云服务器（假设你使用普通用户 &lt;code&gt;cong&lt;/code&gt; 进行部署），执行以下命令：&lt;/p&gt;
&lt;p&gt;bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 确保 ~/.ssh 目录存在且权限正确
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod 700 ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 将公钥内容追加到 authorized_keys 文件中
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys &amp;lt;&amp;lt; &amp;#39;EOF&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;（这里粘贴 github_deploy_key.pub 的完整内容）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;# 设置正确的权限
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod 600 ~/.ssh/authorized_keys
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;⚠️ 注意&lt;/strong&gt;：如果你的云服务器 SSH 端口不是默认的 22（例如改成了 2222），请在后续的 GitHub Secrets 中正确配置端口，并在本地先测试免密登录是否成功：&lt;/p&gt;
&lt;p&gt;bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ssh -i ~/.ssh/github_deploy_key -p &amp;lt;你的端口&amp;gt; &amp;lt;用户名&amp;gt;@&amp;lt;服务器IP&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;如果能够直接登录而不需要密码，说明密钥配置成功。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="编写-github-actions-工作流"&gt;&lt;a href="#%e7%bc%96%e5%86%99-github-actions-%e5%b7%a5%e4%bd%9c%e6%b5%81" class="header-anchor"&gt;&lt;/a&gt;编写 GitHub Actions 工作流
&lt;/h2&gt;&lt;p&gt;在本地博客仓库的根目录下，创建文件夹 &lt;code&gt;.github/workflows/&lt;/code&gt;，并在其中新建一个文件，例如 &lt;code&gt;deploy.yml&lt;/code&gt;。这个 YAML 文件就是告诉 GitHub Actions 如何执行部署的“剧本”。&lt;/p&gt;
&lt;p&gt;以下是一个完整且经过测试的配置示例，关键部分已添加注释：&lt;/p&gt;
&lt;p&gt;yaml&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Deploy&lt;/span&gt; &lt;span class="n"&gt;Hugo&lt;/span&gt; &lt;span class="n"&gt;Blog&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;Cloud&lt;/span&gt; &lt;span class="n"&gt;Server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt; &lt;span class="c1"&gt;# 当推送到 main 分支时触发&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ubuntu&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Checkout&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;checkout&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;v4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;submodules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;recursive&lt;/span&gt; &lt;span class="c1"&gt;# 如果使用了 git submodule（如主题），拉取完整代码&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;fetch&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;depth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Setup&lt;/span&gt; &lt;span class="n"&gt;Hugo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;peaceiris&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;actions&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;hugo&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;v3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;hugo&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;latest&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# 如果主题使用 SCSS 等特性，必须启用 extended 版本&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Build&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hugo&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;minify&lt;/span&gt; &lt;span class="c1"&gt;# 生成静态文件，输出到 public 目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Deploy&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="n"&gt;via&lt;/span&gt; &lt;span class="n"&gt;rsync&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;easingthemes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;ssh&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;deploy&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SSH_PRIVATE_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SSH_PRIVATE_KEY&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;REMOTE_HOST&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REMOTE_HOST&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="c1"&gt;# 你的服务器 IP&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;REMOTE_USER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REMOTE_USER&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="c1"&gt;# 登录用户名，如 cong&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;REMOTE_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="n"&gt;secrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REMOTE_PORT&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt; &lt;span class="c1"&gt;# SSH 端口，默认为 22&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;TARGET&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="c1"&gt;# 服务器上的网站根目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SOURCE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;public&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="c1"&gt;# 本地要同步的目录（注意末尾的斜杠）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ARGS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;avzr&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;delete&lt;/span&gt; &lt;span class="c1"&gt;# rsync 参数：归档、压缩、递归、删除多余文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;
 &lt;blockquote&gt;
 &lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：&lt;code&gt;REMOTE_HOST&lt;/code&gt;、&lt;code&gt;REMOTE_USER&lt;/code&gt;、&lt;code&gt;REMOTE_PORT&lt;/code&gt; 这些敏感信息也建议保存在 GitHub Secrets 中，避免直接写在文件里。你可以在仓库 Secrets 中分别添加 &lt;code&gt;REMOTE_HOST&lt;/code&gt;、&lt;code&gt;REMOTE_USER&lt;/code&gt;、&lt;code&gt;REMOTE_PORT&lt;/code&gt;，然后在上述文件中通过 &lt;code&gt;${{ secrets.REMOTE_HOST }}&lt;/code&gt; 引用。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="最后一步确保服务器目录权限正确"&gt;&lt;a href="#%e6%9c%80%e5%90%8e%e4%b8%80%e6%ad%a5%e7%a1%ae%e4%bf%9d%e6%9c%8d%e5%8a%a1%e5%99%a8%e7%9b%ae%e5%bd%95%e6%9d%83%e9%99%90%e6%ad%a3%e7%a1%ae" class="header-anchor"&gt;&lt;/a&gt;最后一步：确保服务器目录权限正确
&lt;/h2&gt;&lt;p&gt;GitHub Actions 将使用你在 &lt;code&gt;REMOTE_USER&lt;/code&gt; 中指定的用户身份登录服务器。如果该用户对目标目录（如 &lt;code&gt;/var/www/html&lt;/code&gt;）没有写入权限，rsync 会失败并报 &lt;code&gt;Permission denied&lt;/code&gt; 错误。&lt;/p&gt;
&lt;p&gt;假设你的部署用户是 &lt;code&gt;cong&lt;/code&gt;，网站目录为 &lt;code&gt;/var/www/html&lt;/code&gt;，请登录服务器执行：&lt;/p&gt;
&lt;p&gt;bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sudo&lt;/span&gt; &lt;span class="n"&gt;chown&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;R&lt;/span&gt; &lt;span class="n"&gt;cong&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;cong&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;www&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这条命令将目录的所有者改为 &lt;code&gt;cong&lt;/code&gt; 用户，确保部署用户有完全的读写权限。&lt;/p&gt;
&lt;h2 id="享受一键部署的畅快"&gt;&lt;a href="#%e4%ba%ab%e5%8f%97%e4%b8%80%e9%94%ae%e9%83%a8%e7%bd%b2%e7%9a%84%e7%95%85%e5%bf%ab" class="header-anchor"&gt;&lt;/a&gt;享受一键部署的畅快
&lt;/h2&gt;&lt;p&gt;所有配置完成后，将你的 Hugo 博客源码推送到 GitHub 仓库的 &lt;code&gt;main&lt;/code&gt; 分支：&lt;/p&gt;
&lt;p&gt;bash&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add .
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit -m &amp;#34;Add GitHub Actions auto deploy&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push origin main
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后打开 GitHub 仓库的 &lt;strong&gt;Actions&lt;/strong&gt; 标签页，你会看到一个新的工作流正在运行，黄色的圆圈表示进行中。等待几十秒，当它变成绿色的 ✓ 时，访问你的博客网址，内容已经是最新的了！&lt;/p&gt;
&lt;p&gt;从此以后，你可以心无旁骛地专注于写作，每次写完文章，只需执行常规的 &lt;code&gt;git add&lt;/code&gt;、&lt;code&gt;git commit&lt;/code&gt;、&lt;code&gt;git push&lt;/code&gt;，剩下的就交给 GitHub Actions 和 rsync 去完成。技术，本该如此优雅。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;延伸思考&lt;/strong&gt;：如果你有多个分支需要部署，或者想要在部署前运行测试，都可以通过修改 &lt;code&gt;on.push.branches&lt;/code&gt; 或增加新的 job 来实现。GitHub Actions 的灵活性远不止于此，等待你去发掘。&lt;/p&gt;</description></item><item><title>记一次硬核又优雅的个人学术主页建站之旅</title><link>https://xiaocong.site/tricks/build-website/</link><pubDate>Mon, 09 Mar 2026 00:00:00 +0800</pubDate><guid>https://xiaocong.site/tricks/build-website/</guid><description>&lt;h2 id="缘起为什么选择自己折腾"&gt;&lt;a href="#%e7%bc%98%e8%b5%b7%e4%b8%ba%e4%bb%80%e4%b9%88%e9%80%89%e6%8b%a9%e8%87%aa%e5%b7%b1%e6%8a%98%e8%85%be" class="header-anchor"&gt;&lt;/a&gt;缘起：为什么选择自己折腾？
&lt;/h2&gt;&lt;p&gt;为了更好地沉淀个人的项目经验与学习心得，我决定搭建一个纯粹、极简、完全属于自己的学术展示主页。没有选择臃肿的动态博客框架（如 WordPress），而是走向了“静态生成 + 容器化部署 + 自动化脚本”的硬核极客路线。&lt;/p&gt;
&lt;p&gt;这套方案不仅对服务器资源消耗极低，而且将内容的绝对控制权掌握在了自己本地的电脑里。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="-我的建站工具流-toolchain"&gt;&lt;a href="#-%e6%88%91%e7%9a%84%e5%bb%ba%e7%ab%99%e5%b7%a5%e5%85%b7%e6%b5%81-toolchain" class="header-anchor"&gt;&lt;/a&gt;🛠️ 我的建站工具流 (Toolchain)
&lt;/h2&gt;&lt;p&gt;整个网站的运转就像一条精密的流水线，涉及的工具可以分为“云端基础设施”和“本地内容生产”两部分。&lt;/p&gt;
&lt;h3 id="1-云端基础设施-server-side"&gt;&lt;a href="#1-%e4%ba%91%e7%ab%af%e5%9f%ba%e7%a1%80%e8%ae%be%e6%96%bd-server-side" class="header-anchor"&gt;&lt;/a&gt;1. 云端基础设施 (Server-Side)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;宿主机&lt;/strong&gt;：某云厂商的入门级轻量应用服务器（内存不足 2GB，所以对资源分配要求极高）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;管理面板&lt;/strong&gt;：&lt;strong&gt;CasaOS&lt;/strong&gt;。一个基于 Docker 的极简轻量级面板，作为整个服务器的“可视化物业管理”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;反向代理网关&lt;/strong&gt;：&lt;strong&gt;Nginx Proxy Manager (NPM)&lt;/strong&gt;。负责霸占服务器的 80 和 443 端口，未来等域名备案通过后，统一负责域名的流量转发和 SSL 证书（HTTPS 小绿锁）的自动续签。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;前端展示&lt;/strong&gt;：&lt;strong&gt;基础版 Nginx 容器&lt;/strong&gt;。只分配了 128MB 的内存限制，占用内部的 &lt;code&gt;8080&lt;/code&gt; 端口，纯粹用来挂载和展示静态的 HTML 网页文件。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-本地内容生产-local-side"&gt;&lt;a href="#2-%e6%9c%ac%e5%9c%b0%e5%86%85%e5%ae%b9%e7%94%9f%e4%ba%a7-local-side" class="header-anchor"&gt;&lt;/a&gt;2. 本地内容生产 (Local-Side)
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;静态网站生成器&lt;/strong&gt;：&lt;strong&gt;Hugo&lt;/strong&gt;。极其强大的 Go 语言静态博客框架，配合极简的 &lt;strong&gt;PaperMod&lt;/strong&gt; 主题，瞬间将 Markdown 转化为漂亮的网页。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;编辑器&lt;/strong&gt;：&lt;strong&gt;Typora&lt;/strong&gt;。配合 Hugo 的 Page Bundle（页面打包）机制，开启“复制图片到当前文件夹”功能，彻底告别图床失效和路径错乱的烦恼。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动化部署&lt;/strong&gt;：&lt;strong&gt;自定义 PowerShell 双子星脚本&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;new_post.ps1&lt;/code&gt;：一键选择板块（项目/随笔）并生成标准化 Markdown 模板。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;deploy.ps1&lt;/code&gt;：一键执行 Hugo 编译、打包 ZIP，并通过 SSH/SCP 自动推送到服务器并解压覆盖。实现了“本地敲完字，回车即上线”的极致体验。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="-踩坑实录与避坑指南"&gt;&lt;a href="#-%e8%b8%a9%e5%9d%91%e5%ae%9e%e5%bd%95%e4%b8%8e%e9%81%bf%e5%9d%91%e6%8c%87%e5%8d%97" class="header-anchor"&gt;&lt;/a&gt;🕳️ 踩坑实录与避坑指南
&lt;/h2&gt;&lt;p&gt;折腾的过程并非一帆风顺，期间遇到了几个非常经典的工程问题：&lt;/p&gt;
&lt;h3 id="坑-180-端口的假性占用"&gt;&lt;a href="#%e5%9d%91-180-%e7%ab%af%e5%8f%a3%e7%9a%84%e5%81%87%e6%80%a7%e5%8d%a0%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;坑 1：80 端口的“假性占用”
&lt;/h3&gt;&lt;p&gt;一开始准备部署 NPM 时，命令行提示 80 端口被占用。用 &lt;code&gt;lsof -i :80&lt;/code&gt; 查阅后，发现是云厂商自带的监控插件和元数据代理。
&lt;strong&gt;破案：&lt;/strong&gt; 仔细观察网络状态发现是 &lt;code&gt;ESTABLISHED&lt;/code&gt;（主动向外请求）而不是 &lt;code&gt;LISTEN&lt;/code&gt;（本地监听）。所以本地 80 端口其实是空闲的，直接绕过报错强行启动 Docker 即可。&lt;/p&gt;
&lt;h3 id="坑-2内存告急与-docker-的紧箍咒"&gt;&lt;a href="#%e5%9d%91-2%e5%86%85%e5%ad%98%e5%91%8a%e6%80%a5%e4%b8%8e-docker-%e7%9a%84%e7%b4%a7%e7%ae%8d%e5%92%92" class="header-anchor"&gt;&lt;/a&gt;坑 2：内存告急与 Docker 的“紧箍咒”
&lt;/h3&gt;&lt;p&gt;云服务器内存不到 2G，如果不对 Docker 容器设限，极端情况下可能会导致宿主机死机。
&lt;strong&gt;解决：&lt;/strong&gt; 放弃纯 UI 操作，改用 &lt;code&gt;docker-compose.yml&lt;/code&gt; 结合 &lt;code&gt;sudo tee&lt;/code&gt; 强力写入，在配置中加入 &lt;code&gt;deploy.resources.limits.memory&lt;/code&gt;。给 NPM 限制 512M，给静态 Nginx 限制 128M，把每一兆内存都用在刀刃上。&lt;/p&gt;
&lt;h3 id="坑-3lets-encrypt-证书申请报-internal-error"&gt;&lt;a href="#%e5%9d%91-3lets-encrypt-%e8%af%81%e4%b9%a6%e7%94%b3%e8%af%b7%e6%8a%a5-internal-error" class="header-anchor"&gt;&lt;/a&gt;坑 3：Let&amp;rsquo;s Encrypt 证书申请报 &amp;ldquo;Internal Error&amp;rdquo;
&lt;/h3&gt;&lt;p&gt;在 NPM 里配置好域名后，死活申请不下来 SSL 证书，一直报内部错误。
&lt;strong&gt;破案：&lt;/strong&gt; 域名还在 ICP 备案审核期！云厂商的骨干网在流量到达服务器前就直接拦截了所有通过 80 端口查水表的 Let&amp;rsquo;s Encrypt 验证请求。目前只能用“IP+端口”的模式曲线救国，静候管局佳音。&lt;/p&gt;
&lt;h3 id="坑-4自动化脚本乱码与页面神秘失踪"&gt;&lt;a href="#%e5%9d%91-4%e8%87%aa%e5%8a%a8%e5%8c%96%e8%84%9a%e6%9c%ac%e4%b9%b1%e7%a0%81%e4%b8%8e%e9%a1%b5%e9%9d%a2%e7%a5%9e%e7%a7%98%e5%a4%b1%e8%b8%aa" class="header-anchor"&gt;&lt;/a&gt;坑 4：自动化脚本乱码与页面“神秘失踪”
&lt;/h3&gt;&lt;p&gt;在编写 PowerShell 脚本时遇到了字符吞噬的报错，在终于把文件传到服务器后，网页列表却空空如也。
&lt;strong&gt;解决：&lt;/strong&gt; 1. 脚本报错是因为 PowerShell 默认编码问题，将 &lt;code&gt;.ps1&lt;/code&gt; 文件另存为带 BOM 的 UTF-8 即可解决。
2. 页面不显示是因为 Hugo 默认过滤草稿。将 &lt;code&gt;.md&lt;/code&gt; 文件头的 &lt;code&gt;draft: true&lt;/code&gt; 改为 &lt;code&gt;false&lt;/code&gt;，并在 &lt;code&gt;hugo.toml&lt;/code&gt; 中明确配置 &lt;code&gt;mainSections = [&amp;quot;posts&amp;quot;, &amp;quot;projects&amp;quot;]&lt;/code&gt;，文章瞬间完美呈现。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="结语"&gt;&lt;a href="#%e7%bb%93%e8%af%ad" class="header-anchor"&gt;&lt;/a&gt;结语
&lt;/h2&gt;&lt;p&gt;从底层的防火墙放行、Docker 容器编排，到前端的主题配置、本地的自动化脚本编写，这次建站犹如一次全栈工程的微缩演练。把重复的操作交给脚本，把精力的焦点还给内容，这大概就是折腾技术最大的乐趣所在。&lt;/p&gt;
&lt;p&gt;接下来，就是不断往这个“基站”里填充有价值的思想了。&lt;/p&gt;</description></item></channel></rss>