构建和托管静态网站
对于简单的内容呈现站点,比如着陆页、博客等,可以将站点提前转换为静态页面,通过HTTP服务器对外呈现内容。这样的好处是易于部署、对服务器资源要求很低,同时对SEO更友好。在市面上,已经有很多围绕静态站点的工具。对于最终用户而言,最好的体验就是:在编写了文本内容(比如以Markdown编写的文章)后,就会自动化地构建,再自动化地部署到服务器,然后即可通过浏览器访问。附加的基础能力,还包括版本控制等。
工具和服务 🔗
静态网站生成器 🔗
术语 JAMstack 是一种前端 Web 开发方法(构建用户与之交互的内容和界面)。JAM 分别代表 JavaScript、APIs、Markup;stack(堆栈)指的是将所有这些内容以一种方式结合起来,使开发人员能够构建应用程序和网站。它允许开发人员快速创建并有效地为用户提供静态网站。在 JAMstack 的Site Generators 页面,包含了一个 JAMstack 站点生成工具的清单。这些又被称为“静态网站生成器”(Static Site Generator,简称 SSG)。与传统的动态网站不同,静态网站在服务器上不需要运行复杂的服务器端脚本,而是直接提供预先生成好的网页文件给用户访问。
比较知名的静态网站生成器有:
- Hugo:一个使用 Go 编写的命令行工具,下载可执行文件后即可使用。可使用 Markdown 编写内容,使用 Go 标准库的模板语言编写 HTML 布局模板。功能强大,且社区活跃。
- Zola:一个使用 Rust 编写的命令行工具,无其他依赖,下载了可执行文件后即可使用。其最大的特色就是“快”,在修改了内容或者布局模板后,会立即生效。它使用了与 Jinja 类似的模板引擎 tera。不过 zola 的完成度比 Hugo 少很多。
- Gatsby:一个使用 React 来构建快速和现代站点的工具,支持服务器端渲染。它由 Javascript 和 Typescript 编写,因此需要安装 NodeJS 等工具才可以构建站点。
- jekyll:一个老牌的静态网站生成器,诞生于 2008 年。它由 Ruby 编写,使用 Liquid 模板。需要安装 Ruby 才可以构建站点。
站点托管 🔗
任何的 HTTP 服务器都可以托管静态站点。但还应该考虑其他的方面:
- 费用:免费还是付费?对于免费服务,是否有用量限制?超出免费额度后,如何收费?
- 域名:是否支持自定义域名?
- HTTPS:是否支持HTTPS访问,是否支持免费的SSL证书?
- 易用性:是否可以与第三方工具集成,自动化地部署?访问速度如何,是否有 CDN 加速?
几个比较知名的选项有:
- AWS:AWS 作为云服务,尤其是 IaaS 的领导,提供了多种云服务供用户组合使用。比如:可使用 AWS S3 托管内容,使用 AWS Cloudfront 作为 CDN 对外提供服务,AWS Certificate Manager 提供 SSL 证书,同时还需要使用 AWS IAM 服务管理权限。同时可能还需要使用一些 IaC(Infrasture as Code,基础设施即代码) 工具,比如 Terraform 管理基础设施。可以看出,AWS 尽管有一切你能想到的服务,但有一定的技术门槛,需要非常熟悉相关的多个服务。AWS 对各种服务有免费额度,超出后按量收费。
- Github Pages:使用 Github 管理站点内容,然后将其推送到代码仓库,即可使用
http://username.github.io/repository
这样的地址进行访问。Github 还允许配置自定义域名。尽管使用起来很简单,但需要注意,不同的物理位置,访问站点的速度和稳定性是不一样的,也可能出现无法访问的情况。 - Cloudflare Pages: Cloudflare Pages 是一个 JAMstack 平台,用于前端开发者构建和部署网站。它既能与 Git(GitHub 或者 GitLab) 无缝集成,也提供了API与其他工具集成。Cloudflare Pages 提供了免费计划,包括每月500次的构建,无限静态请求与无限带宽等。当然,Cloudflare 本身就是 CDN 提供商,也就提供了 CDN 加速服务。支持自定义域名和SSL证书。
https://bejamas.io/compare 里列出了一些托管工具的比较。比如 Cloudflare Pages 和 GitHub Pages 的比较。
自动化的构建和部署工具 🔗
尽管可在本机构建站点,然后在浏览器里上传内容到托管平台,但生成HTML站点和部署,毕竟是一个重复和无聊的事情。如果有工具自动做这件事,岂不美哉?对于有技术背景的开发者而言,Git 是管理文本内容极其版本的不二之选。有些托管平台与Github等代码托管平台有深度集成,可以在提交了代码之后,自动生成和部署站点;有些托管平台提供了鉴权和内容管理的API,可以通过命令行工具进行部署。
实现 🔗
以 Hugo、Cloudflare 和 Github Actions 为例,介绍构建和部署静态站点的方法。
使用 Hugo 构建站点 🔗
参考 Hugo 的官方文档,创建项目,添加内容。并使用 Git 进行版本控制。
在 Github 管理站点内容 🔗
在 Github 创建代码仓库(公开和私有仓库均可)。然后将项目内容推送到代码仓库中。
在 Cloudflare 创建 Pages 🔗
创建项目 🔗
登录 Cloudflare 后,进入 Workers & Pages
菜单项,点击 Create
后,在 Create an application
页面选择 Pages
。此时点击 Upload assets
,在 Deploy a site by uploading your project
创建项目。创建好之后,可以上传一个临时的 HTML 页面,用于验证项目创建成功。Cloudflare 会给每个项目生成一个 pages.dev
的子域名,在创建项目后,可能需要等一段时间,这个域名才能生效。
绑定域名 🔗
在项目页面里的 Custom Domain
选项卡,创建自定义域名。需要注意的是,如果项目的域名是顶级域名,那么需要在域名提供商处将名字服务器(Name Server, NS)修改为 Cloudflare 的DNS;如果是二级域名,那么可以直接在域名提供商处,添加 DNS 记录即可。
在绑定域名后,可能需要等一段时间才能生效。
通过 Github Actions 构建和部署站点 🔗
Cloudflare 官方提供了用于部署 Pages 的 cloudflare/pages-action。查看其示例,就会发现,在部署时,需要指定 apiToken
、accountId
和 projectName
。其中 projectName
就是 Cloudflare Pages 的项目名。
获取 API Token 🔗
参考文档《Create API token 》,在 User API Tokens 页面创建 API Token。出于“最小权限原则”,创建一个 Custom Token(自定义 Token),而不使用 API token template。打开 Create Custom Token
页面后,在 Permissions
里选择 Account
和 Cloudflare Pages
,同时在 Account Resources
里选择 Include
和项目所在下账户。然后设置 Token 的有效期(TTL)。
获取 Account ID 🔗
参考文章 《Find zone and account IDs 》,找到 Account ID。
在 Github Actions 中设置环境变量 🔗
在 Github 中,进入项目的 Settings
页面,打开 Security
下的 Secrets and variables
- Actions
页面,添加两个 Repository Secret:CLOUDFLARE_API_TOKEN
和 CLOUDFLARE_ACCOUNT_ID
,其内容分别为从 Cloudflare 里获取的 API Token 和 Account ID。
添加工作流定义 🔗
在代码仓库中,添加 .github/workflows/main.yaml
文档,作为构建和发布的工作流定义。内容如下:
on: [push]
jobs:
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.134.2
steps:
- name: Install Hugo CLI
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Install Dart Sass
run: sudo snap install dart-sass
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0
- name: Install Node.js dependencies
run: "[[ -f package-lock.json || -f npm-shrinkwrap.json ]] && npm ci || true"
- name: Build with Hugo
env:
HUGO_CACHEDIR: ${{ runner.temp }}/hugo_cache
HUGO_ENVIRONMENT: production
TZ: Asia/Shanghai
run: |
hugo \
--gc \
--minify \
--baseURL "${{ steps.pages.outputs.base_url }}/"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: public
path: ./public
publish:
runs-on: ubuntu-latest
name: Publish to Cloudflare Pages
needs: build
if: github.ref == 'refs/heads/main'
permissions:
contents: read
steps:
- uses: actions/download-artifact@v4
with:
name: public
path: ./public
- name: Publish to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: "your project name"
directory: public
wranglerVersion: "3"
在这个工作流定义中,包含了2个作业(Job):build
和 publish
:
build
作业构建了站点。它下载了hugo
可执行文件,然后使用hugo
构建站点,最后使用actions/upload-pages-artifact
将public
目录作为“构件”(artifact)上传publish
作业下载了“构件”之后,发布到 Cloudflare Pages 中。注意,此作业只在当前分支为main
分支时执行。
注意,这里使用两个步骤略显复杂,但是将构件和发布解耦了。这个好处是,在使用 PR 或者分支添加站点内容时,会进行构建,但不会部署。
测试 🔗
将 .github/workflows/main.yaml
提交到代码库的 main
分支后,Github Actions 就会自动地构建,然后发布到 Cloudflare。
与 Github 集成的其他方法 🔗
在 Cloudflare 的 Create an application
页面创建 Pages
时,可以 Connect to Git
按钮。其实,Cloudflare 对于 Github 有内建的集成。将 Github 账号授权给 Cloudflare 之后,当有代码提交时,Cloudflare 就会自行构建和部署。Cloudflare 所支持的构建工具可见 Framework presets 列表,包括 Hugo、Zola等。但这样会把 将 Github 用户的所有代码仓库都授权给 Cloudflare。在某些情况下,可能不太适合,因此并没有使用这种方式。
结尾 🔗
Cloudflare是一家提供 CDN、网络安全、DDoS 防御和域名服务的美国公司。在中文互联网中,Cloudflare 被称为 “赛博菩萨”、“赛博活佛”、“大善人”等。它“慷慨”地提供了很多免费的服务,被众多网友“白嫖”。经过对 Cloudflare Pages 的一番测试,开发者体验确实很棒,值得进一步探索。