Docker 部署 VitePress 博客
使用 Docker Compose 在云服务器上部署 VitePress 博客
一、方案架构
1.1 访问流程
┌─────────────────────────────────────────────────────────────────────┐
│ 完整访问流程 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 用户浏览器 │
│ │ │
│ ▼ │
│ https://blog.hoseahu.cn ──→ 云服务器 Nginx (443端口) │
│ │ │ │
│ │ ┌──────┴──────┐ │
│ │ │ Docker 容器 │ │
│ │ └──────┬──────┘ │
│ ▼ │ │
│ 返回博客页面 ┌────| 静态文件目录 │ │
│ ┌───┴────└─────────────┴────────┐ │
│ │ /data/html │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘1.2 组件说明
| 组件 | 作用 | 为什么用 |
|---|---|---|
| Nginx | Web 服务器,托管静态文件 | 高性能,配置简单 |
| Docker | 容器化部署 | 隔离环境,一键部署 |
| Let's Encrypt | 免费 HTTPS 证书 | 安全,自动化 |
| Certbot | 申请/续期证书 | 官方推荐 |
1.3 流量走向
二、准备工作
2.1 服务器要求
| 项目 | 要求 | 说明 |
|---|---|---|
| 系统 | Ubuntu 24.04 | 常用 Linux 发行版 |
| Docker | 已安装 | 容器运行时 |
| Docker Compose | 已安装 | 编排 Docker 容器 |
| 域名 | 已解析 | 指向服务器 IP |
2.2 检查 Docker
bash
# 检查 Docker 版本
docker --version
docker compose version2.3 域名解析
在域名服务商添加解析:
| 记录类型 | 主机记录 | 记录值 |
|---|---|---|
| A | blog | 111.230.145.190 |
2.4 开放端口
bash
# 防火墙开放端口
sudo ufw allow 22/tcp # SSH(必须)
sudo ufw allow 80/tcp # HTTP(证书申请需要)
sudo ufw allow 443/tcp # HTTPS(博客访问)
# 腾讯云安全组也要开放这些端口!三、原理讲解
3.1 为什么需要 Nginx?
┌────────────────────────────────────────────────────────────┐
│ 直接跑 VitePress │
│ docker run -p 3000:3000 viki/vitepress │
│ │
│ 用户访问:http://IP:3000 ❌ 端口丑,不安全 │
└────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────┐
│ Nginx 反向代理 │
│ │
│ 用户访问:https://blog.hoseahu.cn ✅ 域名+HTTPS │
│ │
│ 优势: │
│ - 隐藏端口 3000 │
│ - HTTPS 加密 │
│ - 静态文件缓存 │
└────────────────────────────────────────────────────────────┘3.2 为什么需要 SSL 证书?
┌─────────────────────────────────────────┐
│ HTTP vs HTTPS │
├─────────────────────────────────────────┤
│ HTTP(明文) │
│ 用户 → 服务器 │
│ 密码/内容被中间人可见 ❌ │
│ │
│ HTTPS(加密) │
│ 用户 ═══════════════════ 服务器 ✅ │
│ (TLS 加密,无法窃听) │
└─────────────────────────────────────────┘四、目录结构
~/blog/
├── docker-compose.yml # Docker 容器编排配置
├── nginx/
│ └── nginx.conf # Nginx 配置文件
└── data/
├── html/ # 博客静态文件(构建产物)
│ ├── index.html
│ ├── assets/
│ └── ...
└── certs/ # SSL 证书(自动生成)
├── fullchain.pem
├── privkey.pem
└── ...五、部署步骤
5.1 安装 Docker(如未安装)
bash
# 安装 Docker(官方一键脚本)
curl -fsSL https://get.docker.com | sh
# 启动 Docker 并设置开机自启
sudo systemctl enable --now docker
# 把当前用户加入 docker 组
sudo usermod -aG docker $USER
newgrp docker
# 验证安装
docker --version5.2 创建目录
bash
# 创建所有需要的目录
mkdir -p ~/blog/{nginx,data/{html,certs}}5.3 配置 Nginx(临时配置,用于证书申请)
bash
# 临时 Nginx 配置(仅 80 端口,用于证书申请)
cat > ~/blog/nginx/nginx.conf << 'EOF'
server {
listen 80;
server_name blog.hoseahu.cn;
root /var/www/html;
index index.html;
location /.well-known/ {
root /var/www/html;
}
location / {
try_files $uri $uri/ =404;
}
}
EOF5.4 配置 Docker Compose(临时配置)
bash
cat > ~/blog/docker-compose.yml << 'EOF'
services:
nginx:
image: nginx:latest
container_name: blog-nginx
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./data/html:/var/www/html:ro
- ./data/certs:/etc/nginx/certs:ro
restart: unless-stopped
EOF
# 启动容器
cd ~/blog
docker compose up -d5.5 申请 SSL 证书
bash
# 申请 Let's Encrypt 证书
docker run --rm \
-v $(pwd)/data/certs:/etc/letsencrypt \
-v $(pwd)/data/html:/var/www/html \
certbot/certbot certonly \
--webroot -w /var/www/html \
-d blog.hoseahu.cn \
--email your-email@example.com \
--agree-tos --non-interactive
# 停止容器
docker compose down成功后的输出:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/blog.hoseahu.cn/fullchain.pem
Key is saved at: /etc/letsencrypt/live/blog.hoseahu.cn/privkey.pem5.6 配置完整的 Nginx(SSL)
bash
cat > ~/blog/nginx/nginx.conf << 'EOF'
server {
listen 80;
server_name blog.hoseahu.cn hoseahu.cn;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name blog.hoseahu.cn hoseahu.cn;
ssl_certificate /etc/nginx/certs/live/blog.hoseahu.cn/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/live/blog.hoseahu.cn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
root /var/www/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
}
EOF5.7 配置完整的 Docker Compose 并启动
bash
cat > ~/blog/docker-compose.yml << 'EOF'
services:
nginx:
image: nginx:latest
container_name: blog-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./data/html:/var/www/html:ro
- ./data/certs:/etc/nginx/certs:ro
restart: unless-stopped
EOF
# 启动
docker compose up -d
# 查看状态
docker compose ps5.8 验证部署成功
bash
# 测试 HTTPS 访问
curl -I https://blog.hoseahu.cn
# 应该返回:HTTP/2 200六、部署博客静态文件
6.1 本地构建 VitePress
在本地电脑上操作:
bash
# 进入你的 VitePress 项目目录
cd your-vitepress-project
# 安装依赖
npm install
# 构建静态文件
npm run docs:build6.2 上传到云服务器
bash
# 推送静态文件到云服务器
scp -r docs/.vitepress/dist/* user@111.230.145.190:/home/user/blog/data/html/
# 或使用 rsync 增量同步
rsync -avz --delete docs/.vitepress/dist/ user@111.230.145.190:/home/user/blog/data/html/七、CI/CD 自动部署
7.1 原理
┌─────────────────────────────────────────────────────────────┐
│ CI/CD 流程 │
├─────────────────────────────────────────────────────────────┤
│ 1. 开发者 push 代码 │
│ │ │
│ ▼ │
│ 2. Gitea 检测到 push │
│ │ │
│ ▼ │
│ 3. Runner 执行 Workflow │
│ │ │
│ ┌───┴───┐ │
│ ▼ ▼ │
│ 4. 构建 5. 推送静态文件 │
│ 静态文件 到云服务器 │
│ │ │
│ ▼ │
│ 6. 博客自动更新 │
└─────────────────────────────────────────────────────────────┘7.2 Workflow 配置
yaml
# .gitea/workflows/deploy.yml
name: Deploy Blog
on:
push:
branches: [main]
paths: ['docs/**']
jobs:
deploy:
runs-on: self-hosted
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Build
run: |
npm install
npm run docs:build
- name: Deploy
env:
SSH_KEY: ${{ secrets.SSH_KEY }}
HOST: ${{ secrets.HOST }}
USER: ${{ secrets.USER }}
run: |
rsync -avz --delete \
-e "ssh -i ~/.ssh/id_ed25519 -o StrictHostKeyChecking=no" \
docs/.vitepress/dist/ \
$USER@$HOST:/home/$USER/blog/data/html/八、管理维护
8.1 常用命令
bash
# 启动博客
cd ~/blog && docker compose up -d
# 停止博客
docker compose down
# 重启博客
docker compose restart
# 查看日志
docker logs blog-nginx -f8.2 SSL 证书续期
Let's Encrypt 证书有效期 90 天,需要手动或自动续期:
bash
# 手动续期
cd ~/blog
docker run --rm \
-v $(pwd)/data/certs:/etc/letsencrypt \
-v $(pwd)/data/html:/var/www/html \
certbot/certbot renew \
--webroot --webroot-path /var/www/html --non-interactive
# 重载 Nginx
docker exec blog-nginx nginx -s reload8.3 自动续期脚本
bash
cat > ~/blog/renew-ssl.sh << 'EOF'
#!/bin/bash
cd ~/blog
docker run --rm \
-v $(pwd)/data/certs:/etc/letsencrypt \
-v $(pwd)/data/html:/var/www/html \
certbot/certbot renew \
--webroot --webroot-path /var/www/html --non-interactive
docker exec blog-nginx nginx -s reload
EOF
chmod +x ~/blog/renew-ssl.sh
# 添加到 crontab(每月 1 号凌晨 3 点执行)
crontab -e
# 添加:0 3 1 * * /home/user/blog/renew-ssl.sh九、故障排查
常见问题
| 问题 | 原因 | 解决方法 |
|---|---|---|
| 访问显示 502 | Nginx 无法找到静态文件 | 检查 data/html/ 是否有文件 |
| 访问显示 400 | SSL 证书路径错误 | 检查 data/certs/ 是否有证书 |
| 证书申请失败 | 域名未解析/端口未开放 | 检查 DNS 和防火墙 |
| 样式丢失 | 静态资源路径错误 | 检查 Nginx 配置 |
调试命令
bash
# 1. 检查容器是否运行
docker ps
# 2. 检查端口占用
sudo lsof -i :80
sudo lsof -i :443
# 3. 检查 Nginx 配置
docker exec blog-nginx nginx -t
# 4. 查看错误日志
docker exec blog-nginx cat /var/log/nginx/error.log
# 5. 检查静态文件
docker exec blog-nginx ls -la /var/www/html/十、总结
部署成果
| 项目 | 值 |
|---|---|
| 博客地址 | https://blog.hoseahu.cn |
| 静态文件目录 | ~/blog/data/html/ |
| SSL 证书 | Let's Encrypt (自动续期) |
关键命令
| 操作 | 命令 |
|---|---|
| 启动 | docker compose up -d |
| 停止 | docker compose down |
| 查看日志 | docker logs blog-nginx |
| 重载 Nginx | docker exec blog-nginx nginx -s reload |
| 更新博客 | rsync -avz docs/.vitepress/dist/ ~/blog/data/html/ |