一、博客选型

二、方案选择

方案一 Github ( public repo ) + Hugo + Github Action
方案二 GitHub ( privacy repo ) + Cloudflare Pages ( Hugo actions ) + Cloudflare R2 (免费图床)
方案三 Tailscale 转发 homelab docker Halo/Typecho/Wordpress 到公网vps
方案四 公网 vps 直接部署 docker Wordpress + Cloudflare CDN 加速
方案五 homelab docker Halo/Wordpress + Cloudflare tunnel + Cloudflare R2

结论: 经过一个月左右的使用测试,最终选择了方案二。

方案一

方案二是方案一的升级版,你不需要额外在写 GitHub Action 的 yaml 文件 ,也不需要公开 blog repo。
方案二还能用到 Cloudflare CDN 线路加速,方案一真的没有一点优势。剥离的只剩下 Code Storage 的基础价值。

方案三

当前范围内动态博客的 Markdown 适配的样式,都不太满意。
Halo 相较于长时间停更后再次恢复开源维护的 Typecho 来说,做的会更加完善一些。
Wordpress 部署好后,需要设置的东西挺多的,弃用 Wordpress 的原因其实有两点,
一个是 Markdown 页面的文章样式始终达不到想要的最简单的效果,
还有页面美化的问题,在不同客户端下,需要挨个调试。
如果你追求 Markdown 的一些极简的美化效果,又不想特别折腾,那 Wordpress 不适合你。

方案四

基于这个方案的原因主要是方案三内部,Tailscale 转发 docker Halo, Typecho, Wordpress,
在使用 Nginx 跳转会出现 URL 链接回源 IP 不正常的问题,
虽然方案三可以将资源使用尽量控制在内网里,减少公网vps的开销,多层 nginx 转发单纯只为降低成本,
在投入的时间成本和公网 URL 的加载效率面前,问题几乎无法规避,折腾的只剩下满目疮痍。
由此诞生了方案四,vps 直接套 Cloudflare 的免费 CDN 加速。
最终 Wordpress 的实际测试使用演示效果,并不太适合自己。

方案五

方案五算是方案三,方案四的升级版本,既控制了成本,同时又撇去了繁杂的 nginx 多层转发配置。
一开始还挺高兴的,功能性和 URL 加载效率还是可以的 ,但是 Cloudflare tunnel 在国内的稳定性方面,
实际测试下来,不太满意。tunnel 作为 dev 环境是够用的。放在国外其实也是很 nice 的产品,
但是国内使用不太稳定。具体出现 Cloudflare tunnel 线路代理会偶尔出现中断的问题。
这个是我不能接受的。虽然博客站点加载速度,及各项成本控制已经是最优,但是在白天打开站点维护的过程中,
偶尔出现线路中断。本来想着补救方案的,考虑使用 Tailscale funnel 替代 Cloudflare tunnel 的,
实际测试下来 Tailscale funnel 效果要比 Cloudflare tunnel 差很多,方案五直接弃用。最终还是回到了方案二上。

重点介绍方案二: 原计划后续使用 obsidian-git 持续发布的,考虑到笔记和博客内容的隔离及便利性, 不要随便加戏。最终还是回归初始,使用 vscode 加内置的 typora 做极简的持续发布。因为 Hugo 主题的不同, Markdown 渲染的外观不是你本地md文件看到的那样,强烈建议发布前本地 hugo server 预览调整下格式。 在台式机 Win11 和笔记本 Win11 上均安装好 Hugo , Hugo-extend , 测试了下 Hugo。无论是备份恢复,迁移,都非常方便。 还有 Hugo 主题 Markdown 适配美观,套上 Cloudflare CDN, URL 加载速度极快。方案可持续性挺好的。

三、图床选择

早期想着博客先直接用 Github 的图床就行,在慢慢找寻替代就好。可 Github 站点的图床在国内的很多 ISP 线路下,为屏蔽的重灾区。不想在博客里面出现图片加载失败的问题。个人理想的图床需要满足几个特点:

  • 保证加载速度, 安全稳定
  • 存储方便
  • 合理的付费政策

调研了阿里云,腾讯云的付费 OSS,以及三方的 Backblaze B2,Cloudflare R2 最终选了 Cloudflare R2。Win11 本地配置安装 PicGo 连接 Cloudflare R2, 并在本地使用 Dropbox 做图床的备份目录。基本上各种免费额度,都够自己很长一段时间使用了。

注意: PicGo配置 Cloudflare R2 s3的时候,注意图片上传路径,可测试,调整到自己满意的路径格式。自己当前使用的是:

{year}/{md5}.{extName}

想用存储桶的不同目录去区分图片类型的,但是看到PicGo的s3插件当前不支持多路径的上传方式。

四、部署环境

阿里云域名 测试.top域名/(9块RMB开了一年) dns解析改去了Cloudflare
Cloudflare R2 使用付费服务里面免费额度 需要信用卡绑定开通
Github 创建一个private repo 连接Cloudflare R2

阿里云的域名可以不要的,Cloudflare pages默认会提供初始的 *.pages.dev 的域名, 同时你可自定义这个基础的域名为自己的喜欢的 xxx.pages.dev 的名字,并直接可以从公网访问。 这点做的和 Google blogger 一样支持免费自定义公网域名,还是很 nice 的功能。

当然添加自己的 *.xx.top 的阿里云域名是推荐的,方便绑定及更换自己的理想博客域名。 同时域名可以用作为 Cloudflare R2 注册创建一个子域名 xximg.xx.top ,方便图片科学可读的 URL 引用。 从现有的信息来看,.com的10年期限域名,在 Cloudflare 的注册购买价格,普遍还是非常实惠的,一般在 91.50 美元。 个人是不太推荐,在不太确定自己需要域名做什么的时候,去购买三,五年期限的高价域名, 为了尽量少躺坑,可考虑最便宜的,期限一年的购买使用就可以了。

五、实验记录

操作期间整理的一些配置记录,但愿对你建站有所启发,能节省方案的选型时间和试错成本。

方案三 方案三

方案五 方案五

测试用的docker-compose wordpress demo

version: '3'

services:
  db:
    image: mysql:5.7
    container_name: db
    volumes:
      - ./db_data:/var/lib/mysql
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: "WordpDDDs**"
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress1234

  wordpress:
    container_name: wp
    depends_on:
      - db
    image: wordpress:latest
    ports:
      - "8333:80"
    restart: always
    volumes:
      - ./wordpress:/var/www/html
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: wordpress1234
      WORDPRESS_DB_NAME: wordpress

测试用的docker-compose halo demo

version: "3"

services:
  halo:
    image: halohub/halo:2.7
    container_name: halo
    restart: on-failure:3
    depends_on:
      - halodb
    volumes:
      - ./:/root/.halo2
    ports:
      - "8090:8090"
    healthcheck:
      test: ["CMD", "cURL", "-f", "http://localhost:8090/actuator/health/readiness"]
      interval: 30s
      timeout: 5s
      retries: 5
    command:
      - --spring.r2dbc.URL=r2dbc:pool:postgresql://halodb/halo
      - --spring.r2dbc.username=halo
      # PostgreSQL 的密码,请保证与下方 POSTGRES_PASSWORD 的变量值一致。
      - --spring.r2dbc.password='openpostgresql@2023'
      - --spring.sql.init.platform=postgresql
      # 外部访问地址,请根据实际需要修改
      - --halo.external-URL=http://localhost:8090/
      # 初始化的超级管理员用户名
      - --halo.security.initializer.superadminusername=admin
      # 初始化的超级管理员密码
      - --halo.security.initializer.superadminpassword=P@88w0rd@20@#
  halodb:
    image: postgres:15.3
    container_name: halodb
    restart: on-failure:3
    volumes:
      - ./db:/var/lib/postgresql/data
    # ports:
    #   - "5432:5432"
    expose:
      - "5432"
    healthcheck:
      test: [ "CMD", "pg_isready" ]
      interval: 10s
      timeout: 5s
      retries: 5
    environment:
      - POSTGRES_PASSWORD='openpostgresql@2023'
      - POSTGRES_USER=halo
      - POSTGRES_DB=halo
      - PGUSER=halo

测试用的docker-compose Typecho demo

version: '3.3' 
services:
  typecho:
    image: joyqi/typecho:nightly-php8.0-apache
    container_name: typecho-server
    restart: always
    #privileged: true
    environment:
      - TYPECHO_SITE_URL=https://blog.xxx.top
      - TIMEZONE=Asia/Shanghai
      - MEMORY_LIMIT=200M
      - TYPECHO_INSTALL=1
      - TYPECHO_DB_ADAPTER=Pdo_Pgsql
      - TYPECHO_DB_HOST=postgres
      - TYPECHO_DB_PORT=5432
      - TYPECHO_DB_USER=typecho
      - TYPECHO_DB_PASSWORD="typecho@123T"
      - TYPECHO_DB_DATABASE="typecho"
      - TYPECHO_DB_PREFIX="typecho_"
      - TYPECHO_USER_NAME=auggie
      - TYPECHO_USER_PASSWORD="typecho@123T"
      - TYPECHO_USER_MAIL="xxx@163.com"
    ports:
      - 8083:80
    depends_on:
      - postgres
    volumes:
      - /var/typecho:/app/usr

  postgres:
    container_name: postgres
    image: "postgres:13"
    environment:
      POSTGRES_PASSWORD: initialpassword
      TZ: Asia/Shanghai
    volumes:
      - ./postgres:/data/postgres
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    expose:
      - "5432"
    restart: unless-stopped

typecho postgres 需要初始化引用的 init.sql

ALTER USER postgres WITH ENCRYPTED PASSWORD 'typecho@123T';
CREATE DATABASE typecho;
CREATE USER typecho WITH ENCRYPTED PASSWORD 'typecho@123T';
GRANT ALL PRIVILEGES ON DATABASE typecho TO typecho;

测试用的docker-compose Cloudflare tunnel

version: '3'
services:
  cloudflare-tunnel:
    image: cloudflare/cloudflared:latest
    container_name: cloudflare-tunnel
    hostname: cloudflare-tunnel
    restart: unless-stopped
    network_mode: "host"
    command: tunnel run
    volumes:
      - /etc/localtime:/etc/localtime:ro
    environment:
      - "TUNNEL_TOKEN=eyJ*****STNPV1l0WWpSallXSXlOVFpoT1dKbCJ9"
    #labels:
      #- "com.centurylinklabs.watchtower.enable=true"

六、参考资料

参考:阿里云域名使用Cloudflare进行CDN加速 | 卢仕焕个人博客
参考:阿里云域名如何转入Cloudflare?附cloudflare域名转入详细图文教程
参考:Account limits · Cloudflare Zero Trust docs

注1:Cloudflare主页-网站-添加站点-添加自己待更改dns的目标域名,Cloudflare 会自动分配两个 DNS IP 给你,
每个人是不同的。修改操作后,阿里云审核通过后才可使用 Cloudflare 域注册相关的 CDN 服务。

注2:域名迁移和域名 DNS 解析变更,是两个不同的东西。当前只需要阿里云域名的 DNS 解析变更而已。

注3:阿里云域名 DNS 解析改去 Cloudflare 可能会影响你现有变更的域名下已存在的解析服务,
尽量使用裸的阿里云域名或者你提前对当前的域名解析状态截个图。

注4: homelab 内网服务 docker run cloudflare tunnel 的时候,要注意添加使用 --net=host 网络模式,
否则容器无法使用宿主机的网络转发,官方文档这个部分的描述是有缺陷的,请注意。

注5:内网 x86 主机网络没有公网 IP 和 DDNS 。宿主机的 docker cloudflare tunnel 服务会转发内网 docker 服务
映射到 cloudflare tunnel web 控制台的自定义域名上,实现裸域名的公网访问。
tunnel 自定义域名需要自行在 cloudflare web 控制台配置添加。免费有额度限制,个人是足够用的。