Remix Speed Metal 栈

了解更多关于 Remix 栈

npx create-remix --template Girish21/speed-metal-stack

Remix 博客 📖

这个博客启动模板很大程度上受到了 Kent C. Dodds 实现的 kentcdodds.com 的启发。您可以在 2021 年我如何构建了一个现代网站 中了解更多关于其架构和理念的信息。

架构 💡

website-architecture

重要事项 🚧

Fly 要求所有应用都必须具有全局唯一的名称,我们使用了目录名称和随机哈希作为应用名称。当然,您可以在使用 Fly CLI 启动应用之前随时更改它。但这并不是什么大问题,因为您可以通过将指向 Fly 内部 URL 的 CNAME 记录添加到您的自定义域名,将 Fly 内部 URL 重新分配到任何自定义域名。我们稍后将在将应用部署到生产环境时看到这一点。

快速入门

# run database migrations and set up the initial blog
npm run setup
# run the initial build for the development server
npm run build
# start the development server and run other processes in parallel in watch mode
npm run dev

可用脚本

  • build - 编译并构建 Express 服务器、Remix 应用和 Tailwind,并以 production 模式运行
  • dev - 启动 Express 服务器、Remix 观察器和 Tailwind CLI,并以观察模式运行
  • format - 在代码库上运行 Prettier 并修复可修复的问题
  • lint - 在代码库上运行 ESLint
  • new:blog - 从命令行创建新的博客文章模板
  • start - 启动 Express 服务器(仅在运行 npm run build 后执行)
  • test - 运行 vitest
  • test:e2e:dev - 在开发模式下启动 Cypress 运行器
  • test:e2e:run - 在 CI 模式下启动 Cypress 运行器
  • typecheck - 在代码库上运行类型检查

Fly 设置 🛠

  1. 安装 Fly

  2. 注册并登录 Fly

    flyctl auth signup
    

数据库 🗃

我们在这个模板中使用 SQLite 作为数据库。SQLite 是一个快速的数据库引擎,是持久化数据而不使用 Postgres 等高级数据库引擎的绝佳选择。

安装 ⚙️

SQLite 在所有 Mac 上都预装了。您可以查看其他操作系统的官方安装指南 SQLite 安装指南

为什么我们需要数据库 ❓

我们使用 MDX-Bundler 编译 MDX 文件,而 MDX-Bundler 在内部使用 esbuild 来编译 MDX 文件。虽然 esbuild 非常快,但在此过程中仍然存在明显的延迟,这不利于网站的性能和用户体验。并且,当数据没有变化时,每次请求都编译 MDX 似乎是浪费时间和性能。因此,我们可以缓存编译后的 MDX,只有在我们知道内容发生更改时才重新编译它。

Prisma △

我们在这个模板中使用 Prisma 作为 ORM。要创建 SQLite 数据库并初始化数据库模式,请运行

npx prisma migrate dev

上述命令将提示您输入迁移名称,您可以将其命名为 initial migration。此命令还将安装 Prisma Client 用于与数据库交互。

开发 💻

我们可以启动我们的开发服务器,并运行迁移并将 SQLite 数据库填充初始模式。然后,在终端的新选项卡中,运行以下命令。

npm run dev

此命令同时启动四个进程。

  • Remix 开发服务器在开发模式下启动,并在文件更改时重新构建资源。
  • Tailwind CLI 在样式更改时重新构建样式表
  • 一个 MSW 服务器,它拦截对 GitHub 的 API 调用,并从本地提供内容,而不是调用远程 API
  • 文件观察器监视 content 目录并重新构建资源。

相关文件 🔍

部署 🚀

初始设置 👀

在继续部署我们的应用之前,我们需要执行一些步骤

  • 创建一个 GitHub 账户 GitHub
  • 在 Fly 上创建一个新的应用
flyctl launch --name YOUR_APP_NAME --copy-config --no-deploy

⚠️ 请记住不要部署,因为我们还有一些设置步骤需要完成!

环境变量和密钥 🤫

此模板带有 GitHub Actions 工作流,可自动将应用部署到 Fly.io。首先,我们需要设置我们的 GitHub Actions 和 Fly 应用的一些密钥。让我们现在就开始吧。

要将构建镜像从 GitHub Action 推送到远程 Fly 注册表,我们需要一个来自 Fly 的访问令牌。我们可以使用 Fly 命令行生成它,运行

flyctl auth token

该命令将生成一个访问令牌。然后,您可以通过访问 GitHub 存储库的 设置 页面 https://github.com/:owner/:repo/settings/secrets/actions 并点击 新的存储库密钥 将此令牌添加到您的 GitHub Actions 密钥中。接下来,GitHub 将提示您输入密钥和值。密钥应为 FLY_API_TOKEN,值将是命令行生成的令牌。

我们还需要将 Fly 应用名称设置为密钥,密钥应为 FLY_APP_NAME,值将是 fly.toml 中指定的应用名称。

现在我们需要在我们的 Fly 应用中设置密钥。

由于我们是在需要时从 GitHub 获取内容,而不是预先构建所有页面,因此我们需要一个来自 GitHub 的访问令牌来调用 GitHub API 并获取内容。此外,GitHub 不会限制应用频繁调用 GitHub API。因此,您可以在 个人访问令牌 中生成一个访问令牌。然后,您可以复制生成的令牌并将其设置为应用的密钥。我们可以通过运行以下命令来实现

flyctl secrets set GITHUB_TOKEN={GITHUB_TOKEN}

我们还需要一个密钥来签署我们的会话。我们可以通过运行以下命令来实现

flyctl secrets set SESSION_SECRETS=$(openssl rand -hex 32)

如果 openssl 不可用,您可以使用像 1Password 这样的密码生成服务生成一个安全的令牌。

最后一个需要的密钥是用于在 GitHub Action 和我们部署在远程服务器上的应用之间安全通信的令牌,因为我们需要一个面向公众的 API 来进行此通信。

openssl rand -hex 32

我们必须将此密钥设置为 GitHub Actions 密钥的一部分和一个 Fly 密钥。密钥应为 REFRESH_TOKEN。您可以在 GitHub 中创建一个新的 Actions 密钥,并通过运行以下命令为 Fly 应用创建一个新的密钥。

flyctl secrets set REFRESH_TOKEN={GENERATED_PASSWORD}

卷 💾

我们还需要在 Fly 中创建一个卷来持久化我们的应用数据(SQLite 数据库),以便 Fly 可以跨部署和容器重启持久化存储的数据。同样,我们可以使用 Fly 命令行来实现。

flyctl volumes create data --region [REGION] --size 1

注意:REGION 应为启动应用时选择的区域。您可以通过运行 flyctl regions list 检查选择的区域。

需要注意的是,卷绑定到某个区域中的应用,不能在同一区域中的应用之间共享,也不能跨多个区域共享。

您可以了解更多关于 Fly 卷的信息 此处

推送到生产环境 🥳

我们已经准备好进行第一次部署了。GitHub Actions 工作流配置为在推送到 main 分支时运行。因此,让我们将本地分支 main 推送到远程,触发工作流。

一旦所有检查都通过并且部署完成,您就可以运行

flyctl info

以获取当前应用的 URL 和 IP 地址。应用 URL 将为 https://YOUR_APP_NAME.fly.dev。您可以访问该 URL,网站应该在线。就是这样。您已经使用 Remix 部署了您的博客!

添加自定义域名 🔖

要向应用添加自定义域名,您首先必须从域名注册商处购买域名,您可以选择您喜欢的任何一个。一些流行的选项包括 Domain.comGoogleCloudflare

购买域名后,我们可以添加 DNS 记录以指向域名或创建子域名并将其指向 Fly 应用 URL。我们可以通过使用 CNAME 选项添加 DNS 记录并将 Fly URL https://YOUR_APP_NAME.fly.dev 输入其中来实现。

我们还必须在 Fly 上使用域名创建 SSL 证书。我们可以通过运行以下命令来实现

flyctl certs create [DOMAIN]

您可以在这里了解更多信息 自定义域名的 SSL

就是这样,我们已经准备好与世界分享我们的博客了!但在分享之前,还有一步需要处理。

扩展 ⚖️

有两种扩展应用的方式:纵向扩展和横向扩展。

在纵向扩展中,通过向服务器添加更多计算资源(增加 CPU/RAM)来扩展系统。Fly 支持纵向扩展,您可以在此处查看文档 扩展虚拟机

横向扩展是通过在同一区域或全球其他区域添加更多相同应用的副本实现的。Fly 支持全球多个区域,您可以在此处阅读更多信息 Fly 区域

我们的应用目前仅部署在我们运行 flyctl launch 时选择的单个区域中。这在原型设计和开发过程中是可以的,但在推向生产环境时,我们希望我们的应用能够从全球各地的区域访问,并为全球用户提供类似的性能。在这种情况下,我们可以添加更多应用的副本到全球各地,至少在目标用户众多的区域,以便应用将在更靠近用户的服务器上运行,并且所有用户都将拥有可比的性能。

由于 Fly 根据创建的卷锚定区域,我们可以通过在新区域创建卷或在同一区域添加副本的方式添加更多区域。例如,我们可以通过以下方式实现

flyctl volumes create data --region [NEW_REGION] --size 1

您可以在 Fly 区域 中查看此可用区域列表

然后通过运行以下命令增加 Fly 应用的 扩展数量

flyctl scale count 2

上述命令将扩展数量设置为 2,这意味着应用的两个实例将在创建卷的指定区域中运行。

您可以在 Fly 扩展 中了解更多关于扩展的信息。

API 模拟 🔸

我们的架构是在需要时从 GitHub 仓库获取博客内容,而不是将内容作为构建的一部分打包。因此,我们将对 GitHub 进行大量 API 调用。任何 API 都会有一些限制,例如速率限制、每分钟调用次数等。当我们撰写文章时,这个过程变得很繁琐,因为我们一直在调用 GitHub API;文章必须在 GitHub 上,以便 API 可以返回内容。这个过程并不理想。我们可以做得更好。

相反,我们可以模拟对 GitHub 的 API 请求并在本地提供文章,从而提供更好的体验。我们使用 MSW 拦截请求并返回模拟响应,而不是调用 GitHub API,该响应从本地文件系统提供内容。

代码风格检查 ⬡

此模板预配置了 ESLintPrettier 规则,并作为构建步骤集成到工作流中。例如,您可以运行 npm run lint 对项目运行 ESLint,并运行 npm run format 运行 Prettier。

样式 💅🏻

此模板预配置了 Tailwindcss,以及开发和生产期间所需的所有脚本。

模板还预配置了主题切换器,可以检测合适的主题并防止 FART

测试 🔬

此模板预配置了 JestReact 测试库 用于单元测试,以及 Cypress 用于端到端测试,并配置为作为 GitHub Actions 的一部分运行。您可以运行 npm run test 命令来运行 Jest 测试套件。以及 npm run test:e2e:run 以无头模式运行 Cypress 测试。您可以在 package.json 中查看可用的命令。

类型检查 ʦ

您可以运行 npm run typecheck 在代码库上运行 tsc。类型检查也包含在部署工作流中。

调试

一些使用命令行在 Fly 上调试应用程序的有用命令

日志

您可以使用 flyctl logs 命令从包含项目根目录中 fly.toml 文件的项目目录中检查日志。您还可以通过访问 fly.io/apps 从控制台检查日志。

控制台

您还可以使用 flyctl ssh console 命令登录远程控制台。

数据库

登录控制台后,您还可以检查 SQLite 数据库。但首先,我们必须在远程机器上安装 SQLite。我们可以使用 apt-get install sqlite3 命令来做到这一点。然后,使用 cd data 命令进入卷(注意:data 指的是从命令行创建的卷的名称)。然后运行 sqlite3 sqlite.db 命令打开数据库的命令行界面。