用 Astro + Docker 把博客部署到本地服务器

技术 · 约 7 分钟 · 更新于

Astro + Docker 发布链路

图示展示从 Markdown 提交到 Cloudflare Tunnel 公网访问的完整发布链路。

这篇记录这个博客站本身的部署方案:用 Astro 构建纯静态文件,塞进 nginx 镜像,用 Docker Compose 跑在本地服务器上。

整体思路

  1. astro build 产出静态文件到 dist/
  2. pagefind --site dist 给 dist/ 生成搜索索引
  3. 把 dist/ 拷进 nginx:alpine 镜像
  4. docker compose up -d 映射 1020:80 端口

多阶段 Dockerfile

构建与运行分离,最终镜像只含 nginx 和静态文件,体积很小:

FROM node:22-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build          # astro build && pagefind

FROM nginx:alpine
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

nginx 静态路由

Astro 默认按目录输出 HTML(/blog/foo/ → dist/blog/foo/index.html)。对应的 nginx 路由:

location / {
    try_files $uri $uri/ $uri.html =404;
}

$uri/ 让目录请求命中其下的 index.html,$uri.html 兼容无尾斜杠的旧链接。

一键启动

# 构建 + 启动
docker compose up -d --build

# 访问
curl http://服务器IP:1020

日常更新文章

# 写完新 .md 后,重新构建并重启
docker compose up -d --build

因为用了多阶段构建,每次改动都会在容器内重新 npm ci + 构建,所以更新很省心。

完整发布架构

线上链路可以拆成四层:

Cloudflare Tunnel
        ↓
本地服务器 localhost:1020
        ↓
Nginx 静态容器 :80
        ↓
Astro dist/ + Pagefind index

构建阶段和运行阶段完全分离:Node 镜像只在构建时存在,最终运行镜像只保留 Nginx 和静态文件。这样既减少镜像体积,也降低生产容器攻击面。

构建阶段到底产出了什么

npm run build 实际上包含两步:

  1. Astro 根据 Markdown Content Collections 生成静态 HTML、OG 图片、RSS 和 sitemap。
  2. Pagefind 扫描带有 data-pagefind-body 的页面,生成浏览器端搜索索引。

构建后建议检查:

find dist -maxdepth 2 -type f | sort
curl -I http://127.0.0.1:1020/
curl -I http://127.0.0.1:1020/rss.xml

Nginx 配置要点

除了 try_files,生产配置还应该关注缓存和安全响应头:

location /_astro/ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

location / {
    try_files $uri $uri/ $uri.html =404;
    add_header X-Content-Type-Options nosniff always;
    add_header Referrer-Policy strict-origin-when-cross-origin always;
}

带 hash 的 Astro 静态资源可以长期缓存;HTML 不建议设置过长缓存,否则新文章发布后用户可能看不到最新内容。nosniff 可以减少浏览器 MIME 猜测风险,Referrer-Policy 限制跨域泄露完整路径。

Cloudflare Tunnel 的边界

Tunnel 只负责网络转发,不参与 Astro 的 URL 生成:

  • 回源地址可以是 http://localhost:1020。
  • canonical、RSS、sitemap、OG 必须使用 https://blog.gotomarry.xyz。
  • Cloudflare 侧建议开启 HTTPS、压缩、缓存静态资源和基础 DDoS 防护。
  • 不要把管理端口直接暴露到公网,只暴露 Tunnel 或 Nginx 服务。

内容发布流程

推荐把发布过程固定成脚本化流程:

# 1. 本地检查 Markdown 和构建
npm run build
npm run check

# 2. 构建镜像
docker compose build \
  --build-arg NPM_REGISTRY=https://repo.huaweicloud.com/repository/npm/

# 3. 重启并检查健康状态
docker compose up -d
docker ps --filter name=personal-blog
curl -f https://blog.gotomarry.xyz/

实际使用时可以把这些命令放进 Makefile 或 CI,避免漏掉 Pagefind、sitemap 或健康检查。

回滚与备份

静态站点回滚很简单:保留上一个镜像 tag 即可。

docker tag personal-blog:latest personal-blog:backup-$(date +%Y%m%d)
# 新版本异常时
 docker run -d --name personal-blog-rollback -p 1020:80 personal-blog:backup-20260914

文章源文件和 public/images 应该由 Git 版本管理;Docker 镜像不是源代码备份。每次发布前保留构建日志,出错时可以快速定位是内容、Astro、Pagefind 还是 Nginx 问题。

监控和验收清单

检查项命令/方法
容器健康docker ps 显示 healthy
首页curl -f https://blog.gotomarry.xyz/
RSS浏览器打开 /rss.xml,检查链接为正式域名
Sitemap检查不存在 localhost
搜索搜索一篇文章标题
移动端Chrome DevTools 检查 390px 宽度
缓存检查 /_astro/ 响应头
回滚定期演练旧镜像启动和切换
阳熙

Java / 保险业务系统开发 · 无锡

← 返回文章列表

评论

还没有评论,来留第一条吧。

首次评论会做人机校验