作为一个写 Python 的 LLM 方向博士生,我的 DevOps 经验约等于零。这篇文章完整记录了这个网站从一台裸的 1GB 内存 VPS 到你现在看到的样子的全过程——包括中间那个让我一度怀疑人生的 SSH 玄学问题(剧透:凶手不是校园网)。

整体架构

  • 服务器:DigitalOcean 新加坡机房,1 vCPU / 1GB RAM / 25GB SSD,$6/月
  • 主页(/):PRISM —— Next.js + Tailwind 的学术主页模板,静态导出
  • 博客(/posts/):Hugo + PaperMod —— 你正在看的这个
  • Web 服务:Nginx 同时托管两套静态产物
  • HTTPS:Let’s Encrypt 免费证书,自动续期
  • 部署:本地改完 Markdown/TOML,跑一个 deploy.sh 全站更新

两套静态站点生成器听起来奇怪,其实分工清晰:PRISM 负责"学术名片"的精致门面,Hugo 负责高频写作的顺手体验。Nginx 眼里它们只是同一个目录树下的 HTML 文件,运行时内存开销约等于零——1GB 小机器的完美归宿。

第一个坑:SSH 连不上,凶手竟是本机

故事从一个经典报错开始:

kex_exchange_identification: Connection closed by remote host

SSH 的 22 端口连不上,我一度把 sshd 挪到 443 端口"苟活"。后来系统排查时发现了更诡异的现象:连服务器上根本没开的端口,TCP 握手也能"成功":

$ nc -zv 157.230.252.157 12345   # 这个端口明明是关闭的
Connection to 157.230.252.157 port 12345 succeeded!   # ???

一个关闭的端口不可能接受连接——除非"接受"连接的根本不是服务器。真相:本机的 Clash(TUN 模式)接管了全部出站 TCP,它会先无条件"假装接受"连接,再去找代理节点转发。而当时选中的节点恰好挂了,于是所有流量被静默黑洞,表现出来就是"TCP 通了但 SSH 握手超时"的灵异现场。

修复:给 VPS 的 IP 加一条 DIRECT 直连分流规则——去自己服务器的 SSH 流量本来就不该绕道代理:

prepend-rules:
  - IP-CIDR,157.230.252.157/32,DIRECT,no-resolve
  - DOMAIN-SUFFIX,hector.software,DIRECT

教训:遇到"网络玄学",先怀疑本机代理,再怀疑网络环境,最后才怀疑服务器。

SSH 加固:迁往高位端口的"永不自锁"流程

443 端口要留给 HTTPS(一个 TCP 端口同时只能被一个进程监听),SSH 最终落户 22022。迁移遵循一个铁律——任何时刻都保留一条已验证可用的登录通道:

  1. sshd_config 写两条 Port(旧 443 + 新 22022),重启 sshd——已建立的连接是独立进程,不受影响;
  2. 开新终端实测 22022 能登录;
  3. 确认后才删掉 443 的监听。

改端口防不了定向攻击(真正的安全靠密钥登录 + 禁用密码),但能避开 99% 的扫描器噪音。配合 UFW 防火墙(只放行 22022/80/443)和 2GB swap(小内存机器防 OOM),基础加固完成。

DNS:两条记录

在域名注册商处添加:

类型主机说明
A@(留空)157.230.252.157域名 → 服务器 IP 的直接映射
CNAMEwwwhector.software别名,永远跟着主域名走

TTL 设 300 秒,五分钟左右全球生效。www 用 CNAME 的好处:以后换服务器只需要改一条 A 记录。

HTTPS:Let’s Encrypt 到底在"验证"什么

浏览器地址栏的小锁,本质是一张由证书颁发机构(CA)签发的证书,证明"这个域名的 HTTPS 由其真正的控制者提供"。Let’s Encrypt 是免费的 CA,签发全自动,流程叫 ACME 协议的 HTTP-01 挑战:

  1. 本机的 certbot 向 Let’s Encrypt 说:“我声称控制 hector.software,请给我签证书”;
  2. Let’s Encrypt 回复:“那你把这个随机令牌放到该域名的 /.well-known/acme-challenge/ 路径下”;
  3. certbot 把令牌写进 Nginx 的网站目录;
  4. Let’s Encrypt 的验证服务器从公网访问 http://hector.software/.well-known/acme-challenge/<令牌>——能取到,就证明发起请求的人确实控制着这个域名指向的服务器;
  5. 验证通过,证书签发,certbot 自动改好 Nginx 配置并加上 HTTP→HTTPS 的 301 跳转。

一行命令的事:

certbot --nginx -d hector.software -d www.hector.software \
  --non-interactive --agree-tos -m <email> --redirect

证书有效期 90 天,systemd 的 certbot.timer 每天检查两次、到期前自动续签——装完就可以忘掉它。

部署管线:写完即发布

所有源码都在本地(Mac),服务器只负责构建和托管:

#!/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 'cd /opt/prism-site && npm run build && rsync out/ /var/www/site/'
ssh server 'cd /opt/hector-site && hugo --minify -d /var/www/site/posts'

写一篇新博客 = 新建一个 .md 文件 + 跑一次 ./deploy.sh,十几秒后全网可见。没有数据库、没有后台、没有需要半夜爬起来重启的服务。

成本备注

服务器来自 GitHub Student Pack 的 DigitalOcean 赠金。一个值得所有学生党注意的细节:赠金有过期日,过期即作废——去 Billing 页确认你的截止时间,别让它白白蒸发。多余的赠金拿去开按小时计费的 GPU Droplet 跑实验是不错的归宿(切记用完 Destroy,关机不等于停止计费)。


这个网站的全部搭建过程(包括这篇文章)由 Claude Code 协助完成。下一篇大概会是正经的论文笔记,敬请期待。