说起来也是缘分,这台 DigitalOcean 的小机器跟了我整整5年了,一个月就5块钱,便宜得不行。但是吧,最近越来越感觉有点力不从心了,内存不够用,硬盘空间也紧张,索性一咬牙升级到了15块钱一个月的配置。既然都要折腾了,那干脆把所有服务都重新整理一遍,该扔的扔,该优化的优化。
至于旧博客的那些文章嘛…说实话,翻了一遍发现大部分都已经过时了,有些技术栈都已经淘汰了,留着也没啥意义,索性全扔了,轻装上阵。
为什么要折腾这一套
说白了吧,我就一个需求:不管在哪儿,只要有网,就能访问我的文件。
听起来简单对吧?但实际操作起来真的挺烦的。家里有两台群晖(一台916+一台更老的型号),还有一台 Ubuntu 工作站,平时在家用用还行,但是一旦出门在外想访问点东西,那就麻烦了。外网访问这个事儿,一直都是个让我头疼的问题。
之前的方案都有啥问题
一开始图省事,直接用了 LNMP 一键安装包。这玩意儿确实方便,当年装 WordPress 的时候一键搞定,nginx、MySQL、PHP 全给你配好了,HTTPS 证书也能自动申请,文档还挺全,基本上没碰到解决不了的问题。但是吧,后来不用 WordPress 了,改用静态博客了,MySQL 和 PHP 就成了摆设,每次还得更新、打补丁,感觉有点浪费资源。
然后是 frp 内网穿透这一套。原理很简单,就是把群晖的 5000 端口通过 frp 映射到 VPS 上的某个端口,然后再用 nginx 做个反向代理,包上 HTTPS,这样在外网就能访问了。用了挺长时间,整体还算稳定。
至于直连嘛…我也试过。但问题是,家里一边是 ATT 的光纤,公司或者外面一边可能是 Comcast 的电缆,这两家运营商之间的互联质量真的是…一言难尽。带宽忽高忽低,时不时还断线,用起来真的很烦。
OpenVPN 我也折腾过一阵子,想着能不能直接组个虚拟局域网,但实测下来速度还不如 frp + nginx 的组合。最后发现,虽然 frp 映射 + nginx 反代这套方案速度比不上直连,但胜在稳定,基本上不会断,延迟也能接受,就这么用下来了。
下面这张图大概展示了我当时的网络结构,各个节点之间的速度和延迟都标注在上面了:

旧方案踩过的那些坑
问题1:依赖地狱,真的是地狱
一开始用 apt 装各种软件,相安无事用了好一阵子,感觉挺好的。结果有一天突然想给群晖配个 WebDAV 服务,方便手机直接访问文件,这时候问题来了:LNMP 一键包里的 nginx 居然不支持 WebDAV 模块!
这就尴尬了。要么重新编译 nginx,要么就放弃这个功能。我寻思着都折腾到这份上了,那就编译吧。结果这一编译,各种依赖问题就来了,缺这个库缺那个库,编译参数也得研究半天,中间踩了一堆坑,折腾了大半天才搞定。那会儿就在想,要是有个更简单的方式就好了。
问题2:服务管理,一团乱麻
这个也挺让人头疼的。你看啊:
- nginx 要重启或者重新加载配置,得用
nginx reload或者systemctl restart nginx - frps 呢,我用的是
supervisor来管理,又是另一套命令
理论上是可以把这些都整合到一个统一的管理工具里,比如全都用 systemd 或者全都用 supervisor。但是吧,这又是一个新的维护点,得去写配置文件,得去调试,想想就觉得累。而且每次要改点什么,都得想一想”这个服务是用什么管理的来着?”,真的很烦。
解决方案:干脆全部 Docker 化算了
说到底,我就是懒。而且说实话,DigitalOcean 开的这个小机器,默认就是 root 登录,我平时也不在服务器上干啥别的事儿,就跑几个简单的服务。有点风险的服务呢,包一层 Docker 隔离一下,反正也没人会专门来搞我这个小破站,流量也不大,就自己用用。
所以我就想啊,既然都这样了,那还纠结啥性能啊、高可用啊这些东西,简单才是王道。
Docker 这玩意儿的好处太明显了:
- 有风险的服务?包一层 Docker,隔离开来,就算出问题也不会影响宿主机
- 服务挂了?直接
docker restart,几秒钟就起来了 - 要更新版本?拉个新 image,重新跑一个 container,旧的删掉就完事儿
- 配置文件?直接挂载进去,想改就改,不用进容器里折腾
而且最重要的是,我不用再去管什么依赖冲突、系统环境污染这些破事儿了。每个服务都在自己的小盒子里,互不干扰,清清爽爽。
1. Nginx 容器化
直接用官方镜像,映射配置文件和 SSL 证书:
docker run --restart=on-failure:10 \
-v $PWD/nginx/:/etc/nginx/ \
-v $PWD/:/home/ \
-v /root/.acme.sh/yicheng.ren/:/ssl/yicheng.ren/ \
--network host \
--name nginx-web nginx
2. frps 容器化
frp 是 Go 写的,一开始下载二进制文件用,每次更新都要手动替换,烦。
多阶段构建,镜像只有 10MB:
FROM golang:latest
COPY . /build/
WORKDIR /build
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o frps ./cmd/frps
FROM scratch
COPY --from=0 /build/conf/frps.ini /
COPY --from=0 /build/frps /
ENTRYPOINT ["/frps","-c","/frps.ini"]
跑起来:
docker build -t frps:dev .
docker run --restart=unless-stopped -d \
--network host \
-v /root/docker/frps/frps.ini:/frps.ini \
--name frps frps:dev
3. HTTPS 证书
继续用 acme.sh 申请 Let’s Encrypt 证书。
泛域名证书真香:一个 *.yicheng.ren 证书搞定所有子域名,不用像以前每加一个 vhost 就申请一次。
4. Nginx 配置
配置文件分离,结构清晰:
├── nginx.conf # 主配置
└── vhost/
├── xbb.yicheng.ren.conf
├── yicheng.ren.conf
└── ...others.conf
nginx.conf 里放通用配置:
http {
gzip on;
# HTTP 强制跳转 HTTPS
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
# HTTPS 默认配置
server {
listen 443 ssl http2 default_server;
server_name _;
index index.html;
root /home/wwwroot/default;
ssl_certificate /ssl/yicheng.ren/fullchain.cer;
ssl_certificate_key /ssl/yicheng.ren/yicheng.ren.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256";
ssl_session_cache builtin:1000 shared:SSL:10m;
location ~ /\. {
deny all;
}
access_log /home/wwwlogs/access.log;
}
include vhost/*.conf;
}
反代群晖就加个 proxy_pass:
location / {
proxy_pass http://127.0.0.1:20001;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_buffering off;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
}
搞定。
博客重建:从 Org-mode 到 Markdown 的心路历程
说起来这个博客,也是一段历史了。以前我特别喜欢用 Emacs 的 Org-mode 写各种个人文档,笔记啊、待办事项啊、甚至代码片段都用 Org-mode 管理,所以当时建博客的时候,很自然地就选了 Nikola + Org-mode 这个组合。
能用吗?能用。有问题吗?也没啥大问题。但是吧,用的人真的太少了,基本上属于小众中的小众。
这就带来几个很现实的问题:
- 主题少得可怜:我试了好多主题,要么太丑,要么功能不全,要么就是年久失修没人维护了,找了半天也没找到一个特别满意的
- 文档和社区支持不足:碰到问题想搜一搜,基本上搜不到什么有用的信息,Stack Overflow 上相关的问题也寥寥无几
- 我又懒得自己撸前端:要是我前端技术好,自己撸一个主题也行,但问题是我对前端真的不太熟,也没啥动力去深入学,最后就找了个将就能用的主题凑合着用了
结果5年过去了,再看看现在的技术趋势,Markdown 已经彻底一统江湖了。公司内部文档全都迁移到 Markdown 了,各种协作工具也都支持 Markdown,写技术文档也基本上都是 Markdown 格式。而且 Org-mode 转 Markdown 的插件这几年也有了不少改进,虽然还是有些小毛病,但比以前好多了。
所以我就想,算了,不折腾了,直接跟随主流,用 Jekyll + Markdown 得了。
然后就开始找主题,这次就顺利多了,毕竟 Jekyll 的生态太成熟了。最后找到了一个特别简洁的主题:TMaize Blog,看着挺顺眼的,功能也够用。
评论系统的话,我选了 Gitalk,这玩意儿挺有意思的,直接把 GitHub Issues 当后端,省得自己再去搭个评论服务器,而且对于技术博客来说,读者基本上都有 GitHub 账号,用起来也方便。
小测试
代码高亮效果:
#include <iostream>
using namespace std;
int main() {
cout << "Hello World";
return 0;
}
完美。
总结一下这次折腾
回头看看这次迁移,其实核心思路就几个:
- Docker 化一切能 Docker 化的服务:这样服务之间隔离开来,管理起来也简单,不用担心依赖冲突,不用担心系统环境被污染,真的省心很多
- 配置文件模块化:把 nginx 的配置文件拆分成多个小文件,每个域名一个配置文件,这样以后要加新站点或者改配置,都很方便,不用在一个大文件里翻来翻去
- 泛域名证书真香:以前每加一个子域名就得申请一次证书,现在一个泛域名证书搞定所有子域名,省事儿
- Markdown 跟随主流:不再纠结小众工具了,直接用 Markdown,生态好,工具多,碰到问题也容易找到解决方案
前前后后折腾了大概一天吧,总算是把所有服务都理顺了,测试了一下各个功能都正常,心里踏实多了。
接下来还想做点啥:
- 优化一下 frp 的配置:看看能不能再提升一点速度,虽然现在也够用,但谁会嫌快呢
- 给博客加个搜索功能:文章多了以后,没有搜索真的不方便,得研究一下怎么加个静态站点搜索
- 最重要的:多写点有用的东西:博客搭好了,不能光放在那儿吃灰,得多写点技术总结、踩坑记录什么的,也算是给自己留个备忘
新的开始嘛,继续折腾呗。反正折腾这事儿,对我来说也是一种乐趣。