魔法合体:Remix 和 Vercel/AWS 双重部署
* 一个前提条件:你的应用没有和 AWS 或者 Vercel 的各种数据库生态高度融合。
看了一下,网上似乎没有相关教程。
大概是在国庆期间吧,Vercel 在大陆访问特别不稳,然后当时恰巧成功注册了 AWS 账号,就想,能不能把自己手里面一个 Remix 应用迁移到 AWS 上。AWS 在国庆期间大陆访问还是挺稳的。
但是国庆都过完了,结果咕咕咕到现在才开工。
准备工作
你需要一个 AWS CLI,里面的 AWS 凭据需要有效并且具有 AdministratorAccess
权限。目前这个是官方文档推荐的做法。
需要注意的是,Architect(后文需要使用的工具)不支持包含非 ASCII 字符的路径,所以先确保项目路径里面不含非 ASCII 字符。(都什么年代了还在用传统 ASCII?快来试试我这款最新最热 Unicode(x
基本操作
Architect 主要是用于使用 AWS 构建数据库支持的 Web 应用程序。因为它与 Remix 有相关的集成,所以这里就选用了它。
Architect 默认的初始化工具似乎并不能识别 pnpm,所以这里提供一种手动安装 architect 的方法。
首先安装这些依赖包:
pnpm install @architect/architect @remix-run/architect # 安装 architect 和相关包
然后在项目根目录创建 app.arc
文件(Architect 配置文件):
# app.arc
@app
your-application-name
@http
/*
method any
src backend
@aws
# profile default
region us-west-2
architecture arm64
这个配置文件的意思是:
@app
:这个应用名称叫your-application-name
,会影响到最终部署到 AWS 上的一堆资源名称;@http
:对于路由/*
,转发全部 HTTP 方法(method any
)到backend
目录下的 handler;@aws
:这个应用将会部署在俄勒冈州(us-west-2
),并运行于arm64
架构的机器上。为什么选 arm 架构呢?因为比 x86 便宜。
那么,自然,你需要一个 backend
目录。叫什么其实随意啦,把上面的配置项对应着改一下就好啦。
在 backend
目录下创建 index.js
:
/* backend/index.js */
const { createRequestHandler } = require("@remix-run/architect");
exports.handler = createRequestHandler({
build: require("./build"),
});
以及对应的 config.arc
:
# backend/config.arc
@aws
runtime nodejs18.x
# 单位为 MB,与分配的 vCPU 成正比,参见 AWS 文档
memory 256
# timeout 单位为秒
timeout 15
这个意思是,从 backend
目录下导入 build
(Remix 构建好的 bundle),然后用 @remix-run/architect
转译一下交给 Architect 处理。
那么我们肯定需要修改一下 Remix 的构建设置,以将构建输出到 backend/build
文件夹下。但是因为我们仍然需要构建到 Vercel 上,可以考虑使用环境变量区分:
/* remix.config.cjs */
module.exports = {
ignoredRouteFiles: ["**/.*"],
serverModuleFormat: "cjs",
browserBuildDirectory: "./public/build",
serverBuildPath: process.env.ARC ? "./backend/build/index.js" : undefined,
publicPath: process.env.ARC ? "/_static/build/" : undefined,
};
这个配置的意思是,如果定义了环境变量 ARC 并且值为真,则将 server bundle 构建到 backend/build/index.js
文件,并且所有资源的静态 assets 会从 URL /_static/build/
下访问,而不是原来的默认设置。静态资源需要重写到 /_static/
下,这个是 Architect 的特性。所以还要注意你的 favicon.ico,建议使用 HTML 的 <link rel="icon">
设置图标。
然后这个项目就大概能用了。
构建并测试
构建的时候,只需要将环境变量 ARC
设置成 1
就可以正常构建了:
ARC=1 pnpm build
最后 pnpm arc sandbox
可以查看效果。
可以考虑把这一系列操作都写进脚本或者 package.json/scripts
。
环境变量
环境变量啥的看文档啦。这个不是本文的重点。
部署
这里只说 AWS 的部署了。很简单,就一个指令:
# 预备环境
pnpm arc deploy --staging -v --prune
# 生产环境
pnpm arc deploy --production -v --prune
记住,一定把 sam.yaml
和 sam.json
写进 .gitignore
!!!环境变量什么的都会存在里面,而且 Architect 也不需要这些文件判断环境变量!!!
奇技淫巧
这部分内容有些 project-related。
- 我的项目里页脚有一个链接「Powered by Vercel」,但是如果我项目部署在 AWS 上就不就 ntr 了吗(x
- 我要读取
public
目录下的脚本,但是 Vercel 和 Architect 的静态资源目录不一样诶。
所以可以考虑在 loader
中读取 ARC_ENV
环境变量,服务端进行一个特判就好啦。
export const loader = () => {
return process.env.ARC_ENV ?? null;
};
export default function App() {
const arcEnv = useLoaderData<string | null>();
return (
<>
...
{arcEnv ? (
<script src="/_static/clarity.js"></script>
) : (
<script src="/clarity.js"></script>
)}
</>
);
}
需要注意的是,在 arc sandbox
的时候,仍然需要手动设置 ARC_ENV
环境变量。
ARC_ENV=development pnpm arc sandbox
关于 Prisma
现在存在一个问题,就是 Prisma ORM 在这套方案上会出现一些大问题,似乎是因为没法跑 prisma generate
。暂时不知道怎么解决,求教qwq
持续集成
贴一份 GitHub Actions 的一个模板。
name: AWS deploy
on: [ push, pull_request ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Cancel previous runs
uses: styfle/[email protected]
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Setup pnpm and Install dependencies
uses: pnpm/action-setup@v2
with:
version: 8
run_install: |
- recursive: true
args: [--frozen-lockfile, --strict-peer-dependencies]
- name: Build for AWS
run: ./awsbuild.sh
- name: Arc hydrate
run: pnpm arc hydrate
- name: Staging deploy
if: github.ref != 'refs/heads/main'
run: pnpm arc deploy --staging -v --prune
env:
AWS_ACCESS_KEY_ID: {{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: {{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Production deploy
if: github.ref == 'refs/heads/main'
run: pnpm arc deploy --production -v --prune
env:
AWS_ACCESS_KEY_ID: {{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: {{ secrets.AWS_SECRET_ACCESS_KEY }}
上面的 YAML 请自行替换环境变量语法,因为这个 Markdown 解析器太傻了甚至会解析代码块里的美元符号。
关于 Architect 的更多用法,还是看文档好一些啦。