blog.whhwsz.com 部署指南

编写日期: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

Leave a Comment