Skip to content

域名部署实战:docs.ljdou.cn

本文完整记录 docs.ljdou.cn 技术文档站从零到可访问的部署过程,作为静态站点接入 Traefik 的标准范例。

目标链路

GitHub 源码 → VitePress 构建 → 静态文件 → Nginx 容器 → K8s Service/Endpoints → Ingress → Traefik → docs.ljdou.cn

目录结构

服务器统一目录:

/www/server/ljdou-docs/
├── repo -> /www/server/go_project/ljdou-docs   # 源码(软链)
├── dist/                                        # 生产静态文件
├── releases/                                    # 版本回滚预留
├── nginx.conf                                   # Nginx 配置
├── k8s/                                         # K8s 资源清单
│   ├── service.yaml / endpoints.yaml / ingress.yaml
│   ├── clusterissuer.yaml
│   └── middleware-https-redirect.yaml
└── .deploy/                                     # CI 部署密钥

1. 构建 VitePress

bash
cd /www/server/go_project/ljdou-docs
npm ci
npm run docs:build        # 产物在 docs/.vitepress/dist/

public 目录位置

VitePress 的 public/ 必须放在 docs/public/(与 docs/.vitepress/ 同级),放在仓库根目录不会被拷贝到产物,导致 favicon/logo 丢失。

2. Nginx 容器

nginx.conf(注意 cleanUrls 的处理):

nginx
server {
    listen 3001;
    server_name _;
    root /usr/share/nginx/html;
    index index.html;

    location / {
        # VitePress cleanUrls:
        #   /roadmap  → /roadmap.html
        #   /bigdata/ → /bigdata/index.html
        try_files $uri $uri.html $uri/ =404;
    }

    error_page 404 =404 /404.html;
    location = /404.html { internal; }

    location /assets/ {
        expires 7d;
        add_header Cache-Control "public, immutable";
    }
}

启动容器(不对外发布端口,只走 Traefik):

bash
docker run -d --name ljdou-docs \
  --network ljdou-home_default \
  --restart unless-stopped \
  -v /www/server/ljdou-docs/dist:/usr/share/nginx/html:ro \
  -v /www/server/ljdou-docs/nginx.conf:/etc/nginx/conf.d/default.conf:ro \
  nginx:alpine

3. K8s Service + Endpoints

Docker 容器不在 K8s 管理内,通过无头 Service + 手工 Endpoints 桥接:

yaml
# service.yaml —— clusterIP: None 表示无头服务
apiVersion: v1
kind: Service
metadata:
  name: ljdou-docs
  namespace: default
spec:
  clusterIP: None
  ports:
    - name: http
      port: 3001
      targetPort: 3001
---
# endpoints.yaml —— 指向 Docker 容器 IP
apiVersion: v1
kind: Endpoints
metadata:
  name: ljdou-docs
  namespace: default
subsets:
  - addresses:
      - ip: 172.19.0.6        # docker inspect 获取容器 IP
    ports:
      - name: http
        port: 3001

Endpoints 的坑

Docker 容器重启可能导致 IP 变化 → Endpoints 失效 → 502。务必固定容器 IP(见安全加固篇)。

4. Ingress

Traefik 路由与 Middleware 中的完整示例,核心是 host: docs.ljdou.cn + TLS 注解。

5. 验证

bash
# 容器直连
curl http://172.19.0.6:3001/roadmap        # 200(cleanUrls 生效)

# 域名访问
curl -I http://docs.ljdou.cn/              # 301 → https
curl -I https://docs.ljdou.cn/             # 200

# 证书信息
echo | openssl s_client -connect docs.ljdou.cn:443 -servername docs.ljdou.cn 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

关键经验

  1. 容器不发布宿主端口:Traefik 通过桥接网络 IP 直接访问容器,无需 -p 映射公网端口
  2. rsync 部署注意 bind mountrsync --delete 只删目录内文件、不删目录本身,不会破坏挂载
  3. 先验容器后建路由:每次改动后先 curl 容器 IP,再验证域名,逐层排查

基于 VitePress 构建