架构概览

Obsidian 编辑笔记
    ↓ git push
CNB 仓库(代码托管 + CI/CD)
    ↓ 流水线自动触发
克隆 Quartz → npm install → npx quartz build
    ↓ rsync over SSH(通过 FRP 隧道)
本地服务器(内网,1Panel + OpenResty)
    ↓ OpenResty 反代静态文件
    ↓ FRP 穿透到公网
云服务器(Nginx Proxy Manager)
    ↓ 域名解析
用户访问

环境信息

组件说明
本地服务器内网机器,1Panel 面板,OpenResty
云服务器<云服务器IP>,Nginx Proxy Manager
代码托管CNB(cnb.cool)
内网穿透FRP
静态站点生成Quartz(Node.js)
笔记编辑Obsidian

一、本地服务器准备

1.1 安装 rsync

apt update && apt install -y rsync

1.2 确保 SSH 服务运行

systemctl status sshd
# 如果没装
apt install openssh-server
systemctl enable --now sshd

1.3 生成部署专用 SSH 密钥

ssh-keygen -t ed25519 -f ~/.ssh/cnb_deploy -C "cnb-deploy" -N ""

生成两个文件:

  • ~/.ssh/cnb_deploy — 私钥(放到 CNB 密钥仓库)
  • ~/.ssh/cnb_deploy.pub — 公钥(放到服务器上)

1.4 配置公钥认证(使用 root 用户)

cat ~/.ssh/cnb_deploy.pub >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

1.5 确保 SSH 允许 root 登录

编辑 /etc/ssh/sshd_config

PermitRootLogin yes
systemctl restart sshd

1.6 创建静态文件目录

使用 1Panel 创建站点后,静态文件目录为:

/opt/1panel/www/sites/quartz/index

二、FRP 配置

2.1 暴露 SSH 端口(用于 rsync 部署)

在 FRP 客户端配置中添加:

[[proxies]]
name = "deploy-ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = 6022

2.2 暴露 HTTP 端口(用于 Web 访问)

[[proxies]]
name = "quartzTCP"
type = "tcp"
localIP = "127.0.0.1"
localPort = 80
remotePort = 4000

2.3 重启 FRP 客户端

systemctl restart frpc

2.4 云服务器防火墙放通端口

ufw allow 6022/tcp
ufw allow 4000/tcp

2.5 验证 SSH 隧道

ssh -i ~/.ssh/cnb_deploy -p 6022 root@<云服务器IP>

三、CNB 配置

3.1 创建密钥仓库

  1. 在 CNB 组织 weixiao.space 下创建私有仓库 secrets
  2. 进入仓库 → 设置 → 开启「密钥仓库」功能
  3. 在仓库根目录创建 env.yml 文件:
SSH_PRIVATE_KEY: |
  -----BEGIN OPENSSH PRIVATE KEY-----
  (粘贴 ~/.ssh/cnb_deploy 私钥的完整内容)
  (每行前面必须有两个空格缩进)
  -----END OPENSSH PRIVATE KEY-----

获取私钥内容:

cat ~/.ssh/cnb_deploy

3.2 创建 Quartz 笔记仓库

在 CNB 组织下创建仓库存放笔记,仓库结构:

仓库根目录/
├── .cnb.yml                          ← 流水线配置
├── .quartz/                          ← Quartz 自定义配置目录
│   ├── quartz.config.ts              ← 站点配置
│   ├── quartz.layout.ts              ← 布局配置
│   ├── custom.scss                   ← 自定义样式
│   └── TwikooComments.tsx            ← 自定义组件(评论等)
├── index.md                          ← 首页(必须有)
├── 笔记1.md
├── 笔记2.md
└── 子目录/
    └── 笔记3.md

注意: 仓库根目录必须有 index.md 文件,否则 Quartz 不会生成 index.html 首页。

3.3 创建 .cnb.yml

main:
  push:
    - name: Build and Deploy Quartz
      docker:
        image: node:22
        volumes:
          - /root/.npm
      imports:
        - https://cnb.cool/weixiao.space/secrets/-/blob/main/env.yml
      stages:
        - name: Setup Quartz and Build
          script: |
            git clone https://github.com/jackyzha0/quartz.git /tmp/quartz
            cd /tmp/quartz
            npm install
 
            # 复制笔记到 content
            rm -rf content
            cp -r /workspace content
 
            # 复制 .quartz 目录中的自定义配置
            cp /workspace/.quartz/quartz.config.ts .
            cp /workspace/.quartz/quartz.layout.ts .
            cp /workspace/.quartz/custom.scss quartz/styles/custom.scss
            cp /workspace/.quartz/TwikooComments.tsx quartz/components/TwikooComments.tsx
 
            npx quartz build
 
        - name: Deploy to server
          script: |
            mkdir -p ~/.ssh
            echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
            chmod 600 ~/.ssh/id_ed25519
            apt-get update && apt-get install -y rsync openssh-client
            rsync -avz --delete \
              -e "ssh -p 6022 -o StrictHostKeyChecking=no" \
              /tmp/quartz/public/ root@<云服务器IP>:/opt/1panel/www/sites/quartz/index/

3.4 imports 格式说明

https://cnb.cool / weixiao.space / secrets / -/blob/main / env.yml
    协议             组织名          仓库名     固定格式       文件路径

对应关系:

GitHub ActionsCNB
Settings → Secrets 添加变量密钥仓库的 env.yml 里写变量
${{ secrets.SSH_PRIVATE_KEY }}$SSH_PRIVATE_KEY

四、1Panel 本地站点配置

4.1 OpenResty 站点配置

通过 1Panel 创建静态站点 quartz,配置文件参考:

server {
    listen 80;
    listen 4000;
    server_name 127.0.0.1 <本地服务器IP> weixiao.space <云服务器IP>;
    index index.php index.html index.htm default.php default.htm default.html;
 
    access_log /www/sites/quartz/log/access.log main;
    error_log /www/sites/quartz/log/error.log;
 
    location ~ ^/(\.user.ini|\.htaccess|\.git|\.env|\.svn|\.project|LICENSE|README.md) {
        return 404;
    }
 
    location ^~ /.well-known/acme-challenge {
        allow all;
        root /usr/share/nginx/html;
    }
 
    root /www/sites/quartz/index;
    error_page 404 /404.html;
}

注意: server_name 需要包含你的域名,否则通过域名访问时 OpenResty 无法匹配。

4.2 静态文件目录映射

1Panel 中站点的静态文件目录为:

宿主机: /opt/1panel/www/sites/quartz/index
容器内: /www/sites/quartz/index

rsync 部署时目标路径使用宿主机路径。


五、云服务器 Nginx Proxy Manager 配置

5.1 关键问题:Docker 网络

NPM 通常运行在 Docker 容器中,容器内的 127.0.0.1 指向容器自身而非宿主机。因此需要使用 Docker 网关 IP。

查看 Docker 网关地址:

ip addr show docker0
# 通常为 172.17.0.1

5.2 添加 Proxy Host

  1. 进入 NPM 管理界面(:81
  2. 添加 Proxy Host:
    • Domain Names: weixiao.space(你的域名)
    • Forward Hostname: 172.17.0.1(Docker 网关 IP,不是 127.0.0.1)
    • Forward Port: 4000
  3. SSL 标签可配置 Let’s Encrypt 证书

5.3 域名解析

将域名 A 记录解析到云服务器 IP <云服务器IP>


六、Quartz 自定义配置

6.1 quartz.config.ts

.quartz/quartz.config.ts 中配置站点标题、字体、主题颜色等:

import { QuartzConfig } from "./quartz/cfg"
import * as Plugin from "./quartz/plugins"
 
const config: QuartzConfig = {
  configuration: {
    pageTitle: "微笑的笔记",
    enableSPA: true,
    enablePopovers: true,
    analytics: null,
    locale: "zh-CN",
    baseUrl: "weixiao.space",
    ignorePatterns: ["private", "templates", ".obsidian", ".quartz"],
    defaultDateType: "modified",
    theme: {
      fontOrigin: "googleFonts",
      cdnCaching: true,
      typography: {
        header: "Noto Serif SC",       // 衬线标题字体
        body: "LXGW WenKai",           // 霞鹜文楷正文字体(Google Fonts 自动加载)
        code: "JetBrains Mono",        // 代码字体
      },
      // 温暖文艺主题配色
      colors: {
        lightMode: {
          light: "#FAF6F0",            // 奶油色背景
          lightgray: "#E8E0D8",        // 暖石灰边框
          gray: "#B8A99A",             // 暖灰褐
          darkgray: "#5C4F43",         // 深暖棕正文
          dark: "#3A302A",             // 胡桃色标题
          secondary: "#B85C3C",        // 陶土色链接
          tertiary: "#8B9E7C",         // 鼠尾草绿高亮
          highlight: "rgba(184, 92, 60, 0.12)",
          textHighlight: "#F5D98E88",  // 琥珀金高亮
        },
        darkMode: {
          light: "#1E1A17",            // 暖黑胡桃
          lightgray: "#3A332D",        // 暖深棕边框
          gray: "#6B5F54",             // 暖灰棕
          darkgray: "#D4CABC",         // 暖羊皮纸文字
          dark: "#EDE6DB",             // 暖象牙标题
          secondary: "#D4845C",        // 柔化陶土
          tertiary: "#A3B592",         // 亮鼠尾草绿
          highlight: "rgba(212, 132, 92, 0.15)",
          textHighlight: "#D4A04488",  // 琥珀金暗色
        },
      },
    },
  },
  plugins: {
    transformers: [
      Plugin.FrontMatter(),
      Plugin.CreatedModifiedDate({
        priority: ["frontmatter", "git", "filesystem"],
      }),
      Plugin.SyntaxHighlighting(),
      Plugin.ObsidianFlavoredMarkdown({ enableInHtmlEmbed: false }),
      Plugin.GitHubFlavoredMarkdown(),
      Plugin.TableOfContents(),
      Plugin.CrawlLinks({ markdownLinkResolution: "shortest" }),
      Plugin.Description(),
      Plugin.Latex({ renderEngine: "katex" }),
    ],
    filters: [Plugin.RemoveDrafts()],
    emitters: [
      Plugin.AliasRedirects(),
      Plugin.ComponentResources(),
      Plugin.ContentPage(),
      Plugin.FolderPage(),
      Plugin.TagPage(),
      Plugin.ContentIndex(),
      Plugin.Assets(),
      Plugin.Static(),
      Plugin.NotFoundPage(),
    ],
  },
}
 
export default config

6.2 custom.scss

.quartz/custom.scss 中覆盖 Quartz 默认样式。流水线会将它复制到 quartz/styles/custom.scss,编译时在 base.scss 之后加载,因此同选择器的规则会覆盖默认值。

最简配置只需引入基础样式即可:

@use "./base.scss";

如需自定义排版、引用块、代码块等样式,直接在后面追加 CSS/SCSS 规则。

6.3 常用自定义项

改什么在哪改
站点标题quartz.config.tspageTitle
语言quartz.config.tslocale
域名quartz.config.tsbaseUrl
字体quartz.config.tstypography 中的 header / body / code
主题颜色quartz.config.tscolors 中的 lightMode / darkMode
忽略文件夹quartz.config.tsignorePatterns
链接解析quartz.config.tsCrawlLinksmarkdownLinkResolutionshortest 兼容 Obsidian)
布局/组件quartz.layout.ts → 调整页面布局、添加自定义组件
排版/组件样式custom.scss → 追加 CSS/SCSS 规则覆盖默认样式
评论系统参考 Quartz 集成 Twikoo 评论系统

七、日常使用流程

# 1. 在 Obsidian 中编辑笔记
 
# 2. 推送到 CNB
git add .
git commit -m "update notes"
git push
 
# 3. CNB 自动触发流水线:构建 → 部署
 
# 4. 等待约 1-2 分钟,访问域名查看更新

八、排错清单

问题排查方法
流水线找不到密钥仓库确认仓库已开启「密钥仓库」功能,确认 imports 路径带 https://
npm install 失败npm install 而非 npm ci(后者需要 package-lock.json)
找不到 package.json确认流水线中先 clone Quartz 再 cd 到 Quartz 目录
rsync 远程找不到命令本地服务器安装 rsync:apt install -y rsync
SSH 连接被拒检查 FRP 隧道、SSH 配置、密钥格式
403 Forbidden检查文件权限 chmod -R 755
404 无首页确保仓库根目录有 index.md
NPM 502 Bad GatewayDocker 容器内 127.0.0.1 指向容器自身,改用 Docker 网关 IP(172.17.0.1)
Apache 默认页面systemctl stop apache2 && systemctl disable apache2