引言
花了一个小时,从零搭建了这个博客站点。这篇文章记录完整流程——从域名购买到 HTTPS 配置,再到自动化部署。
以后自己回顾,也分享给需要的朋友。
环境概览
开始之前,先看一下要用到的资源:
| 项目 | 说明 ||------|------|| 域名 | 阿里云购买 || 服务器 | 阿里云 ECS(Alibaba Cloud Linux 3) || 架构 | Git Push → 服务器自动构建 → Nginx 静态站点 || SSH 工具 | 任意 SSH 客户端 |
整个搭建的核心思路:把复杂度收敛到服务端构建脚本,让日常使用简单到一行 git push。
一、SSH 密钥配置
首先生成本地 SSH 密钥对,把公钥传到服务器。之后连接不用每次输密码。
生成密钥
# 生成 ed25519 密钥对
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_petthinker
# 把公钥传到服务器
ssh-copy-id -i ~/.ssh/id_ed25519_petthinker root@your-server-ip
设置别名
在 ~/.ssh/config 中添加配置,之后直接 ssh petthinker 就能连接:
Host petthinker
HostName your-server-ip
User root
IdentityFile ~/.ssh/id_ed25519_petthinker
二、安装 Nginx
Alibaba Cloud Linux 用 dnf 直接安装。
安装与启动
# 安装 Nginx
ssh petthinker "dnf install -y nginx"
# 启动服务
ssh petthinker "systemctl start nginx"
常见踩坑:端口冲突
80 端口被旧的 Docker 容器占用,Nginx 无法启动的情况很常见。先检查再清理:
# 检查 80 端口占用
ssh petthinker "ss -tlnp | grep :80"
# 如果被 Docker 容器占用,停掉并删除
ssh petthinker "docker stop zlmediakit && docker rm zlmediakit"
# 重新启动 Nginx
ssh petthinker "systemctl start nginx"
养成习惯:安装服务前先用 ss -tlnp 扫一遍端口。
三、配置 HTTPS
用 Let's Encrypt 自动获取免费 SSL 证书。
获取证书
# 安装 certbot
ssh petthinker "dnf install -y certbot python3-certbot-nginx"
# 自动获取并配置证书
ssh petthinker "certbot --nginx -d yourdomain.com --non-interactive --agree-tos -m admin@yourdomain.com --redirect"
验证自动续期
证书有效期 90 天,certbot 会自动续期。确认续期任务已注册:
# 检查自动续期定时器
ssh petthinker "systemctl status certbot-renew.timer"
输出中看到 Active: active (waiting) 就说明没问题。
四、搭建文章发布系统
这是核心——选择什么方式发布文章?
方案对比
| 方案 | 优点 | 缺点 ||------|------|------|| WordPress | 功能全,生态丰富 | 重,需要数据库,维护成本高 || Hexo / Hugo | 静态站,速度快 | 需要本地环境,部署步骤多 || Git Push + 静态构建 | 极简,一行命令发布 | 需要自己写构建脚本 |
最终选择了第三种:本地用 Markdown 写文章,Git Push 触发自动构建,30 秒内网站更新。
整体架构
本地 ~/blog/posts/*.md(Markdown 写文章)
↓ git push
服务器 /var/repos/blog.git(裸仓库)
↓ post-receive hook 触发
构建脚本 /usr/local/bin/build-blog.py(Markdown → HTML)
↓ 输出
Nginx 静态站点 /var/www/blog/
↓ 用户访问
https://yourdomain.com
创建裸仓库
# 在服务器上创建 Git 裸仓库
ssh petthinker "mkdir -p /var/repos/blog.git && cd /var/repos/blog.git && git init --bare"
编写 post-receive hook
每次 push 自动触发的脚本:
cat << 'EOF' | ssh petthinker "cat > /var/repos/blog.git/hooks/post-receive"
#!/bin/bash
# 每次 git push 触发:拉取文章 → 构建 HTML
GIT_WORK_TREE=/var/www/blog-src git checkout -f master
python3.8 /usr/local/bin/build-blog.py /var/www/blog-src/posts /var/www/blog
EOF
# 授予执行权限
ssh petthinker "chmod +x /var/repos/blog.git/hooks/post-receive"
构建脚本
Python 脚本负责将 Markdown 文件渲染为 HTML 页面:
# build-blog.py — Markdown 文章 → HTML 页面
import markdown, os, sys
from datetime import datetime
POSTS_DIR = sys.argv[1] # Markdown 源目录
OUT_DIR = sys.argv[2] # HTML 输出目录
STYLE = """
/* 百度风格浅色主题 — 主题色 #4e6ef2 */
body { font-family: -apple-system, BlinkMacSystemFont, sans-serif;
max-width: 780px; margin: 0 auto; padding: 24px;
background: #f5f6f7; color: #222; }
h1 { color: #4e6ef2; border-left: 3px solid #4e6ef2; padding-left: 12px; }
pre { background: #1e1f24; color: #c9d1d9; padding: 16px; border-radius: 6px; }
a { color: #4e6ef2; }
"""
def page(title, body, date=""):
return f"""<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8">
<title>{title}</title><style>{STYLE}</style></head>
<body>
<nav><a href="/">← 首页</a></nav>
<h1>{title}</h1>
{"<p><time>" + date + "</time></p>" if date else ""}
{body}
<hr><footer>湘ICP备2023025875号-2</footer>
</body></html>"""
# 遍历 posts/*.md 生成 HTML
for f in sorted(os.listdir(POSTS_DIR)):
if not f.endswith('.md'): continue
with open(os.path.join(POSTS_DIR, f)) as fp:
md = fp.read()
# 解析 front matter(--- 包裹的元数据)
title = f.replace('.md', '')
date = datetime.now().strftime('%Y-%m-%d')
if md.startswith('---'):
_, fm, body = md.split('---', 2)
for line in fm.strip().split('\n'):
if ':' in line:
k, v = line.split(':', 1)
if k.strip() == 'title': title = v.strip()
if k.strip() == 'date': date = v.strip()
else:
body = md
html = page(title, markdown.markdown(body, extensions=['fenced_code', 'tables']), date)
out_name = os.path.join(OUT_DIR, f.replace('.md', '.html'))
with open(out_name, 'w') as fp:
fp.write(html)
部署构建脚本到服务器:
# 上传构建脚本
scp build-blog.py petthinker:/usr/local/bin/build-blog.py
本地 Git 配置
# 初始化本地仓库并关联远程
cd ~/blog
git init
git remote add origin root@your-server-ip:/var/repos/blog.git
git config core.sshCommand "ssh -i ~/.ssh/id_ed25519_petthinker"
发布文章
# 1. 写文章
vim posts/new-article.md
# 2. 提交并推送
git add posts/
git commit -m "新文章:文章标题"
# 3. 推送即发布
git push origin master
推送后 30 秒内网站自动更新。不需要登录服务器,不需要手动构建——这就是选择 Git Push 方案的最大收益。
五、Nginx 配置要点
站点配置文件 /etc/nginx/conf.d/yourdomain.com.conf:
server {
listen 443 ssl;
server_name yourdomain.com;
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
root /var/www/blog;
index index.html;
location / {
try_files $uri $uri.html $uri/ =404;
}
}
server {
listen 80;
server_name yourdomain.com;
return 301 https://$host$request_uri;
}
修改后重载
# 先测试配置语法,通过后再重载
ssh petthinker "nginx -t && systemctl reload nginx"
最终效果
搭建完成后的效果:
- HTTPS 绿色小锁 + 自动续期
- 本地 Markdown 写文章,Git Push 发布
- 30 秒内全站自动更新
- 零数据库,纯静态 HTML,零维护
- 百度风格浅色主题(#4e6ef2)
结语
| 坑 | 解法 ||----|------|| 端口冲突 Nginx 无法启动 | 先 ss -tlnp 检查端口占用 || Python 版本太老 | dnf install python38 || HTTPS 证书过期 | certbot 自动续期,systemctl status certbot-renew.timer 确认 || 文章发布麻烦 | Git Push + post-receive hook 自动化 || Nginx 配置报错 | 改配置后先 nginx -t 检查语法再 reload |
整个搭建的核心思路:把复杂度收敛到一处(构建脚本),让日常使用简单到一行命令。
评论