[{"content":"作为一个写 Python 的 LLM 方向博士生,我的 DevOps 经验约等于零。这篇文章完整记录了这个网站从一台裸的 1GB 内存 VPS 到你现在看到的样子的全过程——包括中间那个让我一度怀疑人生的 SSH 玄学问题(剧透:凶手不是校园网)。\n整体架构 服务器:DigitalOcean 新加坡机房,1 vCPU / 1GB RAM / 25GB SSD,$6/月 主页(/):PRISM —— Next.js + Tailwind 的学术主页模板,静态导出 博客(/posts/):Hugo + PaperMod —— 你正在看的这个 Web 服务:Nginx 同时托管两套静态产物 HTTPS:Let\u0026rsquo;s Encrypt 免费证书,自动续期 部署:本地改完 Markdown/TOML,跑一个 deploy.sh 全站更新 两套静态站点生成器听起来奇怪,其实分工清晰:PRISM 负责\u0026quot;学术名片\u0026quot;的精致门面,Hugo 负责高频写作的顺手体验。Nginx 眼里它们只是同一个目录树下的 HTML 文件,运行时内存开销约等于零——1GB 小机器的完美归宿。\n第一个坑:SSH 连不上,凶手竟是本机 故事从一个经典报错开始:\nkex_exchange_identification: Connection closed by remote host SSH 的 22 端口连不上,我一度把 sshd 挪到 443 端口\u0026quot;苟活\u0026quot;。后来系统排查时发现了更诡异的现象:连服务器上根本没开的端口,TCP 握手也能\u0026quot;成功\u0026quot;:\n$ nc -zv 157.230.252.157 12345 # 这个端口明明是关闭的 Connection to 157.230.252.157 port 12345 succeeded! # ??? 一个关闭的端口不可能接受连接——除非\u0026quot;接受\u0026quot;连接的根本不是服务器。真相:本机的 Clash(TUN 模式)接管了全部出站 TCP,它会先无条件\u0026quot;假装接受\u0026quot;连接,再去找代理节点转发。而当时选中的节点恰好挂了,于是所有流量被静默黑洞,表现出来就是\u0026quot;TCP 通了但 SSH 握手超时\u0026quot;的灵异现场。\n修复:给 VPS 的 IP 加一条 DIRECT 直连分流规则——去自己服务器的 SSH 流量本来就不该绕道代理:\nprepend-rules: - IP-CIDR,157.230.252.157/32,DIRECT,no-resolve - DOMAIN-SUFFIX,hector.software,DIRECT 教训:遇到\u0026quot;网络玄学\u0026quot;,先怀疑本机代理,再怀疑网络环境,最后才怀疑服务器。\nSSH 加固:迁往高位端口的\u0026quot;永不自锁\u0026quot;流程 443 端口要留给 HTTPS(一个 TCP 端口同时只能被一个进程监听),SSH 最终落户 22022。迁移遵循一个铁律——任何时刻都保留一条已验证可用的登录通道:\nsshd_config 写两条 Port(旧 443 + 新 22022),重启 sshd——已建立的连接是独立进程,不受影响; 开新终端实测 22022 能登录; 确认后才删掉 443 的监听。 改端口防不了定向攻击(真正的安全靠密钥登录 + 禁用密码),但能避开 99% 的扫描器噪音。配合 UFW 防火墙(只放行 22022/80/443)和 2GB swap(小内存机器防 OOM),基础加固完成。\nDNS:两条记录 在域名注册商处添加:\n类型 主机 值 说明 A @(留空) 157.230.252.157 域名 → 服务器 IP 的直接映射 CNAME www hector.software 别名,永远跟着主域名走 TTL 设 300 秒,五分钟左右全球生效。www 用 CNAME 的好处:以后换服务器只需要改一条 A 记录。\nHTTPS:Let\u0026rsquo;s Encrypt 到底在\u0026quot;验证\u0026quot;什么 浏览器地址栏的小锁,本质是一张由证书颁发机构(CA)签发的证书,证明\u0026quot;这个域名的 HTTPS 由其真正的控制者提供\u0026quot;。Let\u0026rsquo;s Encrypt 是免费的 CA,签发全自动,流程叫 ACME 协议的 HTTP-01 挑战:\n本机的 certbot 向 Let\u0026rsquo;s Encrypt 说:\u0026ldquo;我声称控制 hector.software,请给我签证书\u0026rdquo;; Let\u0026rsquo;s Encrypt 回复:\u0026ldquo;那你把这个随机令牌放到该域名的 /.well-known/acme-challenge/ 路径下\u0026rdquo;; certbot 把令牌写进 Nginx 的网站目录; Let\u0026rsquo;s Encrypt 的验证服务器从公网访问 http://hector.software/.well-known/acme-challenge/\u0026lt;令牌\u0026gt;——能取到,就证明发起请求的人确实控制着这个域名指向的服务器; 验证通过,证书签发,certbot 自动改好 Nginx 配置并加上 HTTP→HTTPS 的 301 跳转。 一行命令的事:\ncertbot --nginx -d hector.software -d www.hector.software \\ --non-interactive --agree-tos -m \u0026lt;email\u0026gt; --redirect 证书有效期 90 天,systemd 的 certbot.timer 每天检查两次、到期前自动续签——装完就可以忘掉它。\n部署管线:写完即发布 所有源码都在本地(Mac),服务器只负责构建和托管:\n#!/usr/bin/env bash # deploy.sh 的核心逻辑 rsync -az --delete prism-content/ server:/opt/prism-site/content*/ # 主页内容 rsync -az --delete hector-site/ server:/opt/hector-site/ # 博客源码 ssh server \u0026#39;cd /opt/prism-site \u0026amp;\u0026amp; npm run build \u0026amp;\u0026amp; rsync out/ /var/www/site/\u0026#39; ssh server \u0026#39;cd /opt/hector-site \u0026amp;\u0026amp; hugo --minify -d /var/www/site/posts\u0026#39; 写一篇新博客 = 新建一个 .md 文件 + 跑一次 ./deploy.sh,十几秒后全网可见。没有数据库、没有后台、没有需要半夜爬起来重启的服务。\n成本备注 服务器来自 GitHub Student Pack 的 DigitalOcean 赠金。一个值得所有学生党注意的细节:赠金有过期日,过期即作废——去 Billing 页确认你的截止时间,别让它白白蒸发。多余的赠金拿去开按小时计费的 GPU Droplet 跑实验是不错的归宿(切记用完 Destroy,关机不等于停止计费)。\n这个网站的全部搭建过程(包括这篇文章)由 Claude Code 协助完成。下一篇大概会是正经的论文笔记,敬请期待。\n","permalink":"https://hector.software/posts/building-this-site/","summary":"\u003cp\u003e作为一个写 Python 的 LLM 方向博士生,我的 DevOps 经验约等于零。这篇文章完整记录了这个网站从一台裸的 1GB 内存 VPS 到你现在看到的样子的全过程——包括中间那个让我一度怀疑人生的 SSH 玄学问题(剧透:凶手不是校园网)。\u003c/p\u003e\n\u003ch2 id=\"整体架构\"\u003e整体架构\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e服务器\u003c/strong\u003e:DigitalOcean 新加坡机房,1 vCPU / 1GB RAM / 25GB SSD,$6/月\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e主页(\u003ccode\u003e/\u003c/code\u003e)\u003c/strong\u003e:\u003ca href=\"https://github.com/xyjoey/PRISM\"\u003ePRISM\u003c/a\u003e —— Next.js + Tailwind 的学术主页模板,静态导出\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e博客(\u003ccode\u003e/posts/\u003c/code\u003e)\u003c/strong\u003e:\u003ca href=\"https://gohugo.io/\"\u003eHugo\u003c/a\u003e + \u003ca href=\"https://github.com/adityatelange/hugo-PaperMod\"\u003ePaperMod\u003c/a\u003e —— 你正在看的这个\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eWeb 服务\u003c/strong\u003e:Nginx 同时托管两套静态产物\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHTTPS\u003c/strong\u003e:Let\u0026rsquo;s Encrypt 免费证书,自动续期\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e部署\u003c/strong\u003e:本地改完 Markdown/TOML,跑一个 \u003ccode\u003edeploy.sh\u003c/code\u003e 全站更新\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e两套静态站点生成器听起来奇怪,其实分工清晰:PRISM 负责\u0026quot;学术名片\u0026quot;的精致门面,Hugo 负责高频写作的顺手体验。Nginx 眼里它们只是同一个目录树下的 HTML 文件,运行时内存开销约等于零——1GB 小机器的完美归宿。\u003c/p\u003e\n\u003ch2 id=\"第一个坑ssh-连不上凶手竟是本机\"\u003e第一个坑:SSH 连不上,凶手竟是本机\u003c/h2\u003e\n\u003cp\u003e故事从一个经典报错开始:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-text\" data-lang=\"text\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003ekex_exchange_identification: Connection closed by remote host\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eSSH 的 22 端口连不上,我一度把 sshd 挪到 443 端口\u0026quot;苟活\u0026quot;。后来系统排查时发现了更诡异的现象:\u003cstrong\u003e连服务器上根本没开的端口,TCP 握手也能\u0026quot;成功\u0026quot;\u003c/strong\u003e:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e$ nc -zv 157.230.252.157 \u003cspan style=\"color:#ae81ff\"\u003e12345\u003c/span\u003e   \u003cspan style=\"color:#75715e\"\u003e# 这个端口明明是关闭的\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eConnection to 157.230.252.157 port \u003cspan style=\"color:#ae81ff\"\u003e12345\u003c/span\u003e succeeded!   \u003cspan style=\"color:#75715e\"\u003e# ???\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e一个关闭的端口不可能接受连接——除非\u0026quot;接受\u0026quot;连接的根本不是服务器。真相:本机的 Clash(TUN 模式)接管了全部出站 TCP,它会先无条件\u0026quot;假装接受\u0026quot;连接,再去找代理节点转发。而当时选中的节点恰好挂了,于是所有流量被静默黑洞,表现出来就是\u0026quot;TCP 通了但 SSH 握手超时\u0026quot;的灵异现场。\u003c/p\u003e","title":"从零搭建 hector.software:一个博士生的完整建站记录"}]