编写日期:2026-06-20 | 最后更新:2026-06-21(v2.1)
文档版本:v2.1
场景:在 Docker Nginx 容器上部署静态站点 blog.whhwsz.com
1. 服务器环境概览
1.1 基础信息
| 组件 | 详情 |
|---|---|
| 服务器 | 34.4.110.76 |
| OS | Ubuntu 22.04.5 LTS |
| Python | 3.10.12 |
| Web 服务器 | Nginx(alpine Docker 容器) |
| 容器名 | nginx |
| Web 根目录(宿主机) | /home/web/html |
| Nginx 配置目录(宿主机) | /home/web/conf.d |
| SSL 证书目录(宿主机) | /home/web/certs |
1.2 DNS 确认
bash
host blog.whhwsz.com # blog.whhwsz.com has address 34.4.110.76
1.3 运行容器
bash
docker ps # CONTAINER ID IMAGE NAMES # c3fb29374f3a nginx:alpine nginx # e9dbac0987b6 eceasy/cli-proxy-api:latest cli-proxy-api
1.4 Docker 挂载结构
宿主机 ↔ 容器映射关系:
| 宿主机路径 | 容器内路径 | 用途 |
|---|---|---|
| /home/web/html | /var/www/html | Web 根目录 |
| /home/web/conf.d | /etc/nginx/conf.d | 站点配置 |
| /home/web/nginx.conf | /etc/nginx/nginx.conf | 主配置 |
| /home/web/certs | /etc/nginx/certs | SSL 证书 |
| /home/web/letsencrypt | /var/www/letsencrypt | Let’s Encrypt 验证 |
2. 部署步骤
2.0 前置条件
SSH 已成功连接到服务器(参见 20260620_SSH连接故障排查指南_ssh_connection_troubleshooting.md)。
2.1 创建 Nginx 配置文件
bash
cat > /home/web/conf.d/blog.whhwsz.com.conf << 'EOF' server { listen 80; listen [::]:80; server_name blog.whhwsz.com; root /var/www/html; index index.html; # === 旧文件名 301 跳转(重命名后保持向后兼容)=== location = /markitdown-guide.html { return 301 /20260621_markitdown_guide.html; } location = /cli-proxy-api-guide.html { return 301 /20260619_cli_proxy_api_guide.html; } location = /blog-deployment-guide.html { return 301 /20260620_blog_deployment_guide.html; } location = /ssh-troubleshooting.html { return 301 /20260620_ssh_troubleshooting_guide.html; } location / { try_files $uri $uri/ =404; } location ^~ /.well-known/acme-challenge/ { default_type text/plain; root /var/www/letsencrypt; } } EOF
⚠️ 关键:必须用单引号 heredoc
'EOF'。否则 PowerShell 会把$uri展开为空。
2.2 校验并重载 Nginx
bash
docker exec nginx nginx -t docker exec nginx nginx -s reload
2.3 创建首页
首页为纯手工维护的静态 HTML,位于 /home/web/html/index.html。每添加新文章时手动追加 <div class="post"> 条目。
2.4 上传并转换文章
Step 1:本地脱敏
powershell
$content = Get-Content -Raw "source.md" $content = $content -replace 'sk-\S+','***' $content = $content -replace '(?:\d{1,3}\.){3}\d{1,3}','[IP REDACTED]' $content = $content -replace '(?i)(api-keys:|secret-key:|密码|机密)','***' Set-Content -Path "sanitized.md" -Value $content -Encoding UTF8
Step 2:上传到服务器
powershell
scp -P 22 -o StrictHostKeyChecking=no -i "key_file" sanitized.md user@34.4.110.76:/tmp/article.md
Step 3:安装 Pygments 并执行转换
bash
# 首次需安装 Pygments 语法高亮引擎 python3 -m pip install pygments # 执行转换(md2blog.py 位于 /tmp/,需事先上传,参见第 3 节) python3 /tmp/md2blog.py /tmp/article.md /home/web/html/yyyymmdd_article_name.html
输出特性:
| 特性 | 说明 |
|---|---|
| 语法高亮 | Pygments 自动识别 python/bash/nginx 等 |
| 代码窗口 chrome | macOS 风格红黄绿圆点 + 语言标签 |
| 表格渲染 | <table> 斑马纹 + 悬停高亮 |
| 响应式 | 移动端自适应 |
| 自动标题提取 | 从 H1 自动生成 <title> |
支持自定义输入输出路径:
bash
# 默认输入 /tmp/article.md,输出到 /home/web/html/yyyymmdd_article_name.html python3 /tmp/md2blog.py /tmp/my-article.md /home/web/html/yyyymmdd_article_name.html
配套文件:
20260620_blog.whhwsz.com部署指南_md2blog.py(路径:D:\OneDrive\文档\codex\blog\)
Step 4:更新首页索引
在 index.html 的 <div class="container"> 内按时间倒序添加新的 <div class="post"> 条目,包含标题、日期、版本号和描述。
Step 5:更新部署指南文件清单
如果新增了文章,同步更新本章节(第 6 节)的文件清单。
2.5 验证部署
bash
curl -sI https://blog.whhwsz.com # HTTP/1.1 200 OK curl -sI https://blog.whhwsz.com/20260619_cli_proxy_api_guide.html # HTTP/1.1 200 OK curl -s https://blog.whhwsz.com | head -8 # <!DOCTYPE html> # <html lang="zh-CN">
3. 添加新文章的标准流程
3.1 命名规范
- 源 Markdown 文件:
yyyymmdd_中文描述_english_desc.md(遵循工作区 AGENTS.md 规范) - 博客 HTML 文件:
yyyymmdd_descriptive_name.html(与源文件日期前缀一致) - 首页条目标题:
[YYYY-MM-DD] 文章标题 - 文章 H1 标题:必须包含
[YYYY-MM-DD]前缀(如# [2026-06-21] MarkItDown 安装与使用指南),该标题同时作为页面<title>,确保文章页自身也显示日期
3.2 操作步骤
powershell
# 1. 本地脱敏 $src = "D:\workspace\new-article.md" $dst = "D:\workspace\new-article_sanitized.md" $content = Get-Content -Raw $src $content = $content -replace 'sk-\S+','***' $content = $content -replace '(?:\d{1,3}\.){3}\d{1,3}','[IP REDACTED]' $content = $content -replace '(?i)(api-keys:|secret-key:|密码|机密)','***' Set-Content -Path $dst -Value $content -Encoding UTF8 # 2. SCP 上传脱敏的 Markdown 文件到服务器 scp -P 22 -o StrictHostKeyChecking=no -i "key_file" $dst user@34.4.110.76:/tmp/article.md # 3. 确保 md2blog.py 在服务器上存在 # 若缺失,从本地同步 scp -P 22 -o StrictHostKeyChecking=no -i "key_file" ` "D:\OneDrive\文档\codex\blog\20260620_blog.whhwsz.com部署指南_md2blog.py" ` user@34.4.110.76:/tmp/md2blog.py # 4. 服务器端转换(输出文件名以 yyyymmdd_ 开头) ssh -p 22 -i "key_file" user@34.4.110.76 ` "python3 /tmp/md2blog.py /tmp/article.md /home/web/html/yyyymmdd_article_name.html" # 5. 更新首页索引 # 在 index.html 的 <div class="container"> 内按时间倒序添加新的 <div class="post"> 条目 # 6. 如有旧文件名 → 添加 Nginx 301 跳转(参见 3.3 节)并重载
3.3 URL 兼容性
HTML 文件统一采用 yyyymmdd_ 前缀命名后,旧链接需要 301 跳转。在 Nginx 配置中添加:
nginx
location = /old-file-name.html { return 301 /yyyymmdd_new_file_name.html; }
当前已配置的跳转:
| 旧 URL | 新 URL |
|---|---|
/markitdown-guide.html |
/20260621_markitdown_guide.html |
/cli-proxy-api-guide.html |
/20260619_cli_proxy_api_guide.html |
/blog-deployment-guide.html |
/20260620_blog_deployment_guide.html |
/ssh-troubleshooting.html |
/20260620_ssh_troubleshooting_guide.html |
添加后重载 Nginx:
bash
docker exec nginx nginx -t && docker exec nginx nginx -s reload
4. 关键陷阱记录
陷阱 1:PowerShell 变量展开
powershell
# 错误:$uri 在本地被展开为空 ssh user@host "try_files $uri $uri/ =404" # 正确方案:远程 heredoc 加单引号 # cat > file << 'EOF' → bash 不做变量展开
陷阱 2:heredoc 内双引号被截断
bash
# 错误:default_type "text/plain" 在 heredoc 中被 SSH/PowerShell 截断 # 正确:去掉引号 default_type text/plain;
陷阱 3:Python + SSH 双引号冲突
powershell
# 错误:SSH 命令中包含 Python 双引号字符串 # 正确做法:本地写 .py 文件,SCP 上传,远程执行
陷阱 4:Docker exec 与宿主机路径混淆
bash
# Nginx 在容器内,不能用宿主机路径 # 错误:nginx -t → host 上找不到 # 正确:docker exec nginx nginx -t
陷阱 5:SCP 文件属主和权限
bash
# SCP 上传后文件属主 root:root,nginx 非 root 用户可能无法读取 # 如果有 403 错误,执行: chmod 644 /home/web/html/*
5. 后续优化方向
- [x] 重写 md2blog.py:支持表格、语法高亮、代码窗口 chrome
- [x] 安装 Pygments 语法高亮引擎
- [x] md2blog.py 本地备份于
D:\OneDrive\文档\codex\blog\20260620_blog.whhwsz.com部署指南_md2blog.py,发布文章时需随文上传到服务器 /tmp/ - [ ] 申请 Let’s Encrypt SSL 证书
- [ ] 配置 HTTPS 跳转
- [ ] 首页博客列表自动生成
- [ ] 搭建自动部署脚本(Git Hooks / CI 触发)
6. 文件清单
6.1 站点文件(/home/web/html/)
| 文件 | 大小 | 说明 |
|---|---|---|
| index.html | ~2.5KB | 导航首页(含全部文章索引) |
| 20260621_markitdown_guide.html | ~30KB | MarkItDown 安装与使用指南 |
| 20260620_blog_deployment_guide.html | ~42KB | 本部署指南 |
| 20260620_ssh_troubleshooting_guide.html | ~31KB | SSH 连接故障排查指南 |
| 20260619_cli_proxy_api_guide.html | ~139KB | CLIProxyAPI 指南 |
| 2026_gaokao_math.pdf | ~200KB | 高考数学真题 PDF |
6.2 工具脚本
| 文件 | 位置 | 说明 |
|---|---|---|
| md2blog.py | 服务器 /tmp/(本地备份:D:\OneDrive\文档\codex\blog\20260620_blog.whhwsz.com部署指南_md2blog.py) |
Markdown → HTML 转换器(Pygments + chrome + 图标) |
注意:
md2blog.py不在服务器上常驻,每次发布文章时需从本地同步到服务器/tmp/。
文档版本:v2.1 | 最后更新:2026-06-21(修正 md2blog.py 路径、补充命名规范、全站文件加日期前缀、301 跳转规则)
适用环境:Ubuntu 22.04 + Docker Nginx Alpine + Windows 客户端 / Codex CLI