记录一下云主机的搭建流程。
1. 架构与目录设计
对于个人项目,将 API、Nginx 配置和前端代码放在一个仓库(Monorepo)中管理是最省心的方案,方便统一版本和迁移。
1.1 目录结构
my-project/ # 项目根目录
├── .github/workflows/ # CI/CD 脚本
├── api/ # (预留) Golang API 服务
├── blog/ # Hugo 博客源码
│ ├── public/ # 编译后的静态文件
│ └── themes/ # Git Submodule 管理的主题
├── nginx/ # Nginx 配置
│ ├── conf.d/ # 站点配置
│ └── nginx.conf # 全局配置
└── docker-compose.yml # 服务编排
2. 服务端环境准备 (Host)
2.1 安装 Docker
在 Linux 云主机上执行:
curl -fsSL [https://get.docker.com](https://get.docker.com) | bash
2.2 编写 docker-compose.yml
为了方便记忆和排查,采用路径对齐策略:将宿主机的 ./blog/public 挂载到容器内的 /var/www/blog/public,保持逻辑一致。
同时配置了日志大小限制,防止容器长时间运行占满服务器硬盘。
version: '3.8'
services:
nginx:
image: nginx:alpine
container_name: my-nginx
ports:
- "80:80"
- "443:443"
volumes:
# 配置挂载
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/conf.d:/etc/nginx/conf.d:ro
# 站点挂载 (关键:路径对齐,避免 404/403)
- ./blog/public:/var/www/blog/public:ro
# SSL 证书挂载 (直接使用宿主机证书)
- /etc/letsencrypt:/etc/letsencrypt:ro
# ACME 验证目录
- ./nginx/html:/usr/share/nginx/html
# 生产环境必备:限制日志大小
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
restart: always
2.3 首次部署与启动
在配置 CI/CD 之前,需要先手动在服务器上把服务跑起来。
- 拉取代码:
cd /var/www
git clone [https://github.com/your-username/my-project.git](https://github.com/your-username/my-project.git)
- 启动服务:
cd my-project
docker compose up -d
3. SSL 证书解决方案
采用 Host 模式:在宿主机申请证书,通过挂载共享给容器。
3.1 申请证书 (Certbot)
# 安装 certbot
apt-get install certbot # 或 yum install certbot
# 首次申请 (替换为你的实际域名)
certbot certonly --webroot -w /var/www/my-project/nginx/html -d [www.example.com](https://www.example.com)
3.2 自动续期 (Cron)
Let’s Encrypt 有效期 90 天,配置 Cron 任务实现自动续期并重载 Nginx。
编辑定时任务 crontab -e:
# 每天凌晨 3 点检查,只有续期成功才重载 Nginx (无需重启容器)
0 3 * * * certbot renew --quiet --deploy-hook "docker compose -f /var/www/my-project/docker-compose.yml exec nginx nginx -s reload"
4. Hugo 本地开发流
4.1 初始化与主题
使用 git submodule 管理主题,好处是能轻松拉取主题的后续更新,且保持 Git 仓库整洁。
# 在 blog 目录下
git submodule add [https://github.com/adityatelange/hugo-PaperMod](https://github.com/adityatelange/hugo-PaperMod) themes/PaperMod
4.2 配置文件修改 (hugo.toml)
theme = 'PaperMod'
baseURL = '[https://www.example.com/](https://www.example.com/)'
5. CI/CD 自动化部署 (GitHub Actions)
为了兼顾效率与稳定,我设计了两条独立的流水线。
5.1 准备工作:配置 Secrets
在 GitHub 仓库的 Settings -> Secrets and variables -> Actions 中添加:
HOST_IP: 云主机 IPSSH_USER: 登录用户名 (如 root)SERVER_SSH_KEY: 私钥内容 (对应服务器上的公钥)
5.2 流线一:内容更新 (deploy.yml)
- 场景:写新博客。
- 逻辑:编译 Hugo -> SCP 传输文件。
- 优势:速度快,无需重启容器。
# .github/workflows/deploy.yml
name: Deploy Content
on:
push:
paths: ['blog/**'] # 只监听博客目录
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true # 必须拉取主题子模块
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: 'latest'
extended: true
- name: Build
run: cd blog && hugo --minify
- name: Deploy to Server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.HOST_IP }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
source: "blog/public/"
target: "/var/www/my-project/"
strip_components: 0
5.3 流线二:架构变更 (deploy-infra.yml)
- 场景:修改
nginx.conf或docker-compose.yml。 - 逻辑:SSH 登录 -> 拉取代码 -> 重建容器。
# .github/workflows/deploy-infra.yml
name: Deploy Infrastructure
on:
push:
paths:
- 'docker-compose.yml'
- 'nginx/**'
jobs:
update-infra:
runs-on: ubuntu-latest
steps:
- name: SSH Remote Commands
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST_IP }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /var/www/my-project
# 强制同步代码
git fetch --all
git reset --hard origin/master
# 重建容器 (应用新配置)
docker compose up -d --remove-orphans --build
# 清理垃圾镜像
docker image prune -f
6. 主机迁移指南
这套方案最大的优势是可移植性。若需要更换服务器,仅需三步恢复:
- 环境安装:在新主机安装 Docker 和 Git。
- 数据恢复:
- 将备份的 SSL 证书目录
/etc/letsencrypt恢复到新主机。 git clone仓库代码。
- 一键启动:
docker compose up -d
7. 总结
通过这套方案,我们不仅拥有了一个高性能的静态博客,更获得了一套标准化的 DevOps 基础设施:
- 工程化管理:Monorepo 让前后端与配置管理井井有条。
- 极致自动化:写文章秒级部署,改架构自动重启,彻底解放双手。
- 高稳定性:Docker 隔离环境,日志限制与自动 SSL 续期确保了服务的长期健康运行。