工程项目
把一台云服务器变成个人博客
从一台空的阿里云 ECS 开始,一步步搭建 Gitee 私有仓库 + Markdown 内容源 + 定时拉取自动部署的纯静态个人博客,最终成品是现在的「蚂蚁石窟」。
为什么要做这个项目
我一直想有一个完全属于自己的个人博客空间。不需要复杂的后台、不需要动态渲染、不需要数据库——只需要一个安静的地方,让我用 Markdown 写作,写完推送上去就能自动上线。
市面上各种博客平台:要么广告多、要么不自由、要么数据不归自己、要么需要自己折腾一堆东西。索性,买了一台阿里云 1核2G的 ECS,从零开始搭一个。
目标很明确: - 纯静态,Nginx 直接返回 HTML,性能好、安全成本低 - Markdown 写作,本地编辑,Git 管理版本 - 自动部署,push 上去 2 分钟内自动上线,不用我手动登录服务器 - 风格极简,纸张底色,复古排版,舒服耐看
这篇文章记录从空服务器到可用博客的全过程,以及后续功能的演进。
---
第一步:基础环境搭建
确认系统
买完 ECS,拿到 IP 和 root 密码,第一件事就是 SSH 上去确认环境:
``bash ssh root@47.109.110.244 lsb_release -a ``
Ubuntu 22.04.5 LTS,Python 3 自带,Git 也有,够用了。不需要装 Docker,也不需要装 Node.js——越简单越稳定。
安装 Nginx
``bash apt update && apt install nginx ``
配置好站点目录:
``bash mkdir -p /var/www/personal-blog chown -R root:root /var/www/personal-blog ``
配置域名和 HTTPS
域名已经买好了 jiazui.xin,DNS 解析到 ECS IP。
Nginx 配置分几步: 1. HTTP 80 端口,先把站点挂起来 2. 用 certbot 申请 Let's Encrypt 免费证书 3. 配置 HTTPS 443,强制跳转 HTTP → HTTPS 4. 开启 TLS 1.2/1.3,配置现代加密套件
域名备案也走完了流程: - ICP 备案:渝ICP备2026014363号-1 - 公安备案:渝公网安备50010502504730号
---
第二步:设计部署流程
为什么选 "Gitee + crontab 定时拉取"?
我想要的工作流是: - 本地写作 → 写完 commit - push 到 Gitee → 服务器自动拉取、自动构建、自动同步 - 不用我动手,2分钟后刷新就能看到新内容
这个方案有几个好处: - Gitee 免费私有仓库,国内速度快 - crontab 定时拉取,最简单可靠,不需要 webhook 也不需要驻留进程 - Python 一键构建,纯静态输出,Nginx 直接 serve
架构图:
`` [本地Windows] 写作 Markdown → git commit → git push ↓ [Gitee 私有仓库] ↓ (每 2 分钟 git pull) [阿里云 ECS /opt/personal-blog-source] git pull → python build.py → rsync dist/ → /var/www/personal-blog → nginx reload ↓ [/var/www/personal-blog] Nginx 站点根目录 ↓ [访客浏览器] 直接访问 HTTPS ``
部署脚本
deploy.sh 很简单,就是把几个步骤串起来:
``bash #!/bin/bash cd /opt/personal-blog-source git pull >> /var/log/personal-blog-deploy.log 2>&1 python3 build.py >> /var/log/personal-blog-deploy.log 2>&1 rsync -a --delete dist/ /var/www/personal-blog/ nginx -s reload date >> /var/log/personal-blog-deploy.log ``
添加到 crontab:
`` */2 * * * * /opt/personal-blog-source/deploy.sh ``
每两分钟拉一次,有新内容就更新,没新内容也不折腾。
---
第三步:内容与页面结构演进
V1:单页极简版
最初首页就是几个板块入口,点开才看得到内容。页面结构很简单:
首页→ 板块入口板块页→ 文章列表文章页→ 正文阅读
好处是干净,坏处是访客进来不知道最近更了什么,得点进板块才能看到。
V2:主页三级结构定型
后来调整为现在的结构:
- 首页:板块入口 + 最近更新列表
- 板块页:该板块下所有文章列表,含标题、日期、简介
- 文章页:Markdown 渲染后的正文
这样访客打开首页就能看到最新更新,点进去直接读,体验更好。
---
第四步:实现「最近更新」功能(2026-07-24)
需求
用户反馈希望在首页就能看到最新发布的内容,不用点进各个板块找:
在主页面中,各子页面的入口下方,新增一个"最近更新"的列表,该列表的功能是直接在主页面上显示三条发布时间最新的内容标题,并且用户可以直接打开浏览,"摘抄"中内容不参与排序、展示。
设计要点
1. 数量:只展示 3 条,保持首页简洁不拥挤 2. 排序:按文章 date 倒序,最新的在前 3. 排除:quotes(摘抄)板块不参与排序和展示 4. 样式:和板块列表使用相同样式,保持全站风格一致 5. 信息完整:每条要包含日期、标题、简介,还有"阅读全文"链接
实现过程
build.py:在生成首页前,遍历所有板块收集文章: - 跳过 quotes 板块 - 读取每篇文章的元数据(date、title、summary、slug) - 按 date 倒序排序,取前三 - 传给首页模板渲染
templates/home.html: - 在 section-grid(板块入口网格)下方新增一块 - 使用 eyebrow 标题样式突出"最近更新" - 复用和板块页相同的 entry-list + entry-row 结构
style.css: - 给最近更新容器增加轻微阴影,和板块卡片风格统一 - 复用全站现有的色值和间距,不引入新设计语言
效果
- 自动更新:新增文章并 push → 2 分钟后自动出现在列表
- 自动淘汰:第四条新增后,最早的那条自动挤出列表
- 摘抄内容始终不参与,符合设计约束
---
第五步:安全加固
博客上线后,做了一些基础的 Nginx 加固:
- 限制请求频率:
limit_req_zone单 IP 每秒 5 请求,防 CC - 限制连接数:
limit_conn_zone单 IP 最多 10 连接 - 封禁异常 UA:爬虫、扫描器直接 444 断连
- 拒绝异常方法:PUT、DELETE、TRACE 等方法直接拒绝
- 不封 IP:只限速不封 IP,避免误杀正常访客
不做: - 不用 fail2ban 动态封 IP,减少维护成本 - 不上 WAF,简单问题用 Nginx 配置就能解决
---
遇到的问题与解决
1. 中文编码乱码:PowerShell 默认 GBK,写入 UTF-8 模板会乱码 → Python 重新写入 UTF-8 解决 2. 缓存旧 HTML:修改模板后,dist 目录残留旧文件 → 构建前先删 dist 目录 3. SSH 连接慢:Ubuntu 默认 DNS 反向解析 → 关掉 UseDNS no 解决
---
复盘与总结
从一台空服务器到可用博客,其实并不复杂。核心就是抓住几个要点:
- 简单就是稳定:用 Python 标准库就能构建,不用引入一堆依赖
- 自动化部署:写完不用管,push 上去等两分钟就能看
- 渐进式演进:先能用,再慢慢加功能,最近更新就是后来才加的
- 权限分离:服务器只有拉取权限,所有改动都在本地,不容易乱
现在这个博客已经跑起来了,写读书笔记、工程项目、随笔摘抄都够用。后续有新想法再慢慢加功能。
整个过程最舒服的地方就是:写完 Markdown,git push,然后就完事了。不需要我登录服务器,不需要我手动构建,一切自动完成。这就是我想要的写作体验。