主题
部署到生产环境
¥Deploy to production
本页是如何将 Elysia 部署到生产的指南。
¥This page is a guide on how to deploy Elysia to production.
编译为二进制文件
¥Compile to binary
我们建议在部署到生产环境之前运行构建命令,因为它可以显著减少内存使用量和文件大小。
¥We recommend running a build command before deploying to production as it could potentially reduce memory usage and file size significantly.
我们建议使用以下命令将 Elysia 编译为单个二进制文件:
¥We recommend compiling Elysia into a single binary using the command as follows:
bash
bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--outfile server \
src/index.ts
这将生成一个可移植的二进制文件 server
,我们可以运行它来启动服务器。
¥This will generate a portable binary server
which we can run to start our server.
与开发环境相比,将服务器编译为二进制文件通常可以显著减少 2-3 倍的内存使用量。
¥Compiling server to binary usually significantly reduces memory usage by 2-3x compared to development environment.
这条命令有点长,我们来分解一下:
¥This command is a bit long, so let's break it down:
- --compile 将 TypeScript 编译为二进制文件
- --minify-whitespace 删除不必要的空格
- --minify-syntax 压缩 JavaScript 语法以减小文件大小
- --outfile server 将二进制文件输出为
server
- src/index.ts 我们服务器的入口文件(代码库)
要启动服务器,只需运行二进制文件即可。
¥To start our server, simply run the binary.
bash
./server
编译二进制文件后,你无需在计算机上安装 Bun
即可运行服务器。
¥Once binary is compiled, you don't need Bun
installed on the machine to run the server.
这非常棒,因为部署服务器无需安装额外的运行时即可运行,从而使二进制文件可移植。
¥This is great as the deployment server doesn't need to install an extra runtime to run making binary portable.
目标
¥Target
你还可以添加 --target
标志,以针对目标平台优化二进制文件。
¥You can also add a --target
flag to optimize the binary for the target platform.
bash
bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--target bun-linux-x64 \
--outfile server \
src/index.ts
以下是可用目标列表:
¥Here's a list of available targets:
目标 | 操作系统 | 架构 | 现代 | 基线 | Libc |
---|---|---|---|---|---|
bun-linux-x64 | Linux | x64 | ✅ | ✅ | glibc |
bun-linux-arm64 | Linux | arm64 | ✅ | N/A | glibc |
bun-windows-x64 | Windows | x64 | ✅ | ✅ | * |
bun-windows-arm64 | Windows | arm64 | ❌ | ❌ | * |
bun-darwin-x64 | macOS | x64 | ✅ | ✅ | * |
bun-darwin-arm64 | macOS | arm64 | ✅ | N/A | * |
bun-linux-x64-musl | Linux | x64 | ✅ | ✅ | musl |
bun-linux-arm64-musl | Linux | arm64 | ✅ | N/A | musl |
为什么不使用 --minify
¥Why not --minify
Bun 确实有 --minify
标志可以最小化二进制文件。
¥Bun does have --minify
flag that will minify the binary.
但是,如果我们使用 OpenTelemetry,它会将函数名称缩减为一个字符。
¥However if we are using OpenTelemetry, it's going to reduce a function name to a single character.
由于 OpenTelemetry 依赖于函数名,这使得跟踪变得比应有的更困难。
¥This makes tracing harder than it should as OpenTelemetry relies on a function name.
但是,如果你不使用 OpenTelemetry,你可以选择使用 --minify
。
¥However, if you're not using OpenTelemetry, you may opt in for --minify
instead
bash
bun build \
--compile \
--minify \
--outfile server \
src/index.ts
权限
¥Permission
某些 Linux 发行版可能无法运行该二进制文件,如果你使用的是 Linux,我们建议你启用二进制文件的可执行权限:
¥Some Linux distros might not be able to run the binary, we suggest enabling executable permission to a binary if you're on Linux:
bash
chmod +x ./server
./server
未知随机中文错误
¥Unknown random Chinese error
如果你尝试将二进制文件部署到服务器,但无法运行并出现随机中文错误。
¥If you're trying to deploy a binary to your server but unable to run with random chinese character error.
这意味着你正在运行的机器不支持 AVX2。
¥It means that the machine you're running on doesn't support AVX2.
遗憾的是,Bun 需要支持 AVX2
硬件的机器。
¥Unfortunately, Bun requires a machine that has AVX2
hardware support.
据我们所知,没有其他解决方法。
¥There's no workaround as far as we know.
编译为 JavaScript
¥Compile to JavaScript
如果你无法编译为二进制文件或你正在 Windows 服务器上部署。
¥If you are unable to compile to binary or you are deploying on a Windows server.
你也可以将服务器打包到 JavaScript 文件中。
¥You may bundle your server to a JavaScript file instead.
bash
bun build \
--minify-whitespace \
--minify-syntax \
--outfile ./dist/index.js \
src/index.ts
这将生成一个可移植的 JavaScript 文件,你可以将其部署到你的服务器上。
¥This will generate a single portable JavaScript file that you can deploy on your server.
bash
NODE_ENV=production bun ./dist/index.js
Docker
在 Docker 上,我们建议始终编译为二进制文件以减少基础镜像的开销。
¥On Docker, we recommended to always compile to binary to reduce base image overhead.
以下是使用 Distroless 二进制镜像的示例镜像。
¥Here's an example image using Distroless image using binary.
dockerfile
FROM oven/bun AS build
WORKDIR /app
# Cache packages installation
COPY package.json package.json
COPY bun.lock bun.lock
RUN bun install
COPY ./src ./src
ENV NODE_ENV=production
RUN bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--outfile server \
src/index.ts
FROM gcr.io/distroless/base
WORKDIR /app
COPY --from=build /app/server server
ENV NODE_ENV=production
CMD ["./server"]
EXPOSE 3000
OpenTelemetry
如果你使用 OpenTelemetry 部署生产服务器。
¥If you are using OpenTelemetry to deploys production server.
由于 OpenTelemetry 依赖于 monkey-patching node_modules/<library>
。为了使 make instrumentations 正常工作,我们需要将要被 instrument 的库指定为外部模块,以将其排除在打包之外。
¥As OpenTelemetry rely on monkey-patching node_modules/<library>
. It's required that make instrumentations works properly, we need to specify that libraries to be instrument is an external module to exclude it from being bundled.
例如,如果你使用 @opentelemetry/instrumentation-pg
来检测 pg
库。我们需要将 pg
从打包包中排除,并确保它导入 node_modules/pg
。
¥For example, if you are using @opentelemetry/instrumentation-pg
to instrument pg
library. We need to exclude pg
from being bundled and make sure that it is importing node_modules/pg
.
为了使其正常工作,我们可以使用 --external pg
将 pg
指定为外部模块。
¥To make this works, we may specified pg
as an external module with --external pg
bash
bun build --compile --external pg --outfile server src/index.ts
这告诉 bun 不要将 pg
打包到最终输出文件中,而是在运行时从 node_modules
目录导入。所以,在生产服务器上,你还必须保留 node_modules
目录。
¥This tells bun to not pg
bundled into the final output file, and will be imported from the node_modules
directory at runtime. So on a production server, you must also keeps the node_modules
directory.
建议在 package.json
中将生产服务器中应可用的软件包指定为 dependencies
,并使用 bun install --production
仅安装生产依赖。
¥It's recommended to specify packages that should be available in a production server as dependencies
in package.json
and use bun install --production
to install only production dependencies.
json
{
"dependencies": {
"pg": "^8.15.6"
},
"devDependencies": {
"@elysiajs/opentelemetry": "^1.2.0",
"@opentelemetry/instrumentation-pg": "^0.52.0",
"@types/pg": "^8.11.14",
"elysia": "^1.2.25"
}
}
然后在生产服务器上运行构建命令后,
¥Then after running a build command, on a production server
bash
bun install --production
如果 node_modules 目录仍然包含开发依赖,你可以删除 node_modules 目录并重新安装生产依赖。
¥If the node_modules directory still includes development dependencies, you may remove the node_modules directory and reinstall production dependencies again.
Monorepo
如果你使用 Elysia 和 Monorepo,则可能需要包含依赖 packages
。
¥If you are using Elysia with Monorepo, you may need to include dependent packages
.
如果你使用 Turborepo,则需要将 Dockerfile 放在你的应用目录中,例如 apps/server/Dockerfile。这可能适用于其他 monorepo 管理器,例如 Lerna 等。
¥If you are using Turborepo, you may place a Dockerfile inside an your apps directory like apps/server/Dockerfile. This may apply to other monorepo manager such as Lerna, etc.
假设我们的 monorepo 使用的是 Turborepo,其结构如下:
¥Assume that our monorepo are using Turborepo with structure as follows:
apps
server
- Dockerfile(在此处放置 Dockerfile)
packages
- config
然后我们可以在 monorepo 根目录(而不是应用根目录)上构建 Dockerfile:
¥Then we can build our Dockerfile on monorepo root (not app root):
bash
docker build -t elysia-mono .
Dockerfile 如下:
¥With Dockerfile as follows:
dockerfile
FROM oven/bun:1 AS build
WORKDIR /app
# Cache packages
COPY package.json package.json
COPY bun.lock bun.lock
COPY /apps/server/package.json ./apps/server/package.json
COPY /packages/config/package.json ./packages/config/package.json
RUN bun install
COPY /apps/server ./apps/server
COPY /packages/config ./packages/config
ENV NODE_ENV=production
RUN bun build \
--compile \
--minify-whitespace \
--minify-syntax \
--outfile server \
src/index.ts
FROM gcr.io/distroless/base
WORKDIR /app
COPY --from=build /app/server server
ENV NODE_ENV=production
CMD ["./server"]
EXPOSE 3000
Railway
Railway 是流行的部署平台之一。
¥Railway is one of the popular deployment platform.
Railway 为每个部署分配一个随机端口,可通过 PORT
环境变量访问。
¥Railway assigns a random port to expose for each deployment, which can be accessed via the PORT
environment variable.
我们需要修改 Elysia 服务器以接受 PORT
环境变量,从而符合 Railway 端口的要求。
¥We need to modify our Elysia server to accept the PORT
environment variable to comply with Railway port.
我们可以使用 process.env.PORT
而不是固定端口,并在开发过程中提供回退。
¥Instead of a fixed port, we may use process.env.PORT
and provide a fallback on development instead.
ts
new Elysia()
.listen(3000)
.listen(process.env.PORT ?? 3000)
这应该允许 Elysia 拦截 Railway 提供的端口。
¥This should allows Elysia to intercept port provided by Railway.
提示
Elysia 会自动将主机名分配给 0.0.0.0
,这与 Railway 兼容。
¥Elysia assign hostname to 0.0.0.0
automatically, which works with Railway