Skip to content
Our Sponsors
Open in Anthropic

title: 部署到生产环境 - ElysiaJS head:

    • meta
    • property: 'og:title' content: 部署到生产环境 - ElysiaJS
    • meta
    • name: 'description' content: 本页面
    • meta
    • property: 'og:description' content: Elysia 可以通过向构造函数传递对象来进行配置。我们可以通过向构造函数传递对象来配置 Elysia 的行为。

部署到生产环境

本页面是关于如何将 Elysia 部署到生产环境的指南。

集群模式

Elysia 默认是单线程的。为了利用多核 CPU,我们可以在集群模式下运行 Elysia。

让我们创建一个 index.ts 文件,从 server.ts 导入我们的主服务器,并根据可用的 CPU 核心数派生多个工作进程。

ts
import cluster from 'node:cluster'
import os from 'node:os'
import process from 'node:process'

if (cluster.isPrimary) {
  	for (let i = 0; i < os.availableParallelism(); i++)
    	cluster.fork()
} else {
  	await import('./server')
  	console.log(`Worker ${process.pid} started`)
}
ts
import { Elysia } from 'elysia'

new Elysia()
	.get('/', () => 'Hello World!')
	.listen(3000)

这将确保 Elysia 在多个 CPU 核心上运行。

TIP

在 Bun 上的 Elysia 默认使用 SO_REUSEPORT,这允许多个实例监听同一个端口。这只在 Linux 上有效。

编译为二进制文件

我们建议在部署到生产环境之前运行构建命令,因为它可能会显著减少内存使用和文件大小。

我们建议使用以下命令将 Elysia 编译为单个二进制文件:

bash
bun build \
	--compile \
	--minify-whitespace \
	--minify-syntax \
	--target bun \
	--outfile server \
	src/index.ts

这将生成一个可移植的二进制文件 server,我们可以运行它来启动我们的服务器。

将服务器编译为二进制文件通常比开发环境显著减少 2-3 倍的内存使用。

这个命令有点长,让我们分解一下:

  1. --compile 将 TypeScript 编译为二进制文件
  2. --minify-whitespace 移除不必要的空白字符
  3. --minify-syntax 压缩 JavaScript 语法以减少文件大小
  4. --target bun 为 Bun 运行时优化二进制文件
  5. --outfile server 将二进制文件输出为 server
  6. src/index.ts 我们服务器的入口文件(代码库)

要启动我们的服务器,只需运行二进制文件。

bash
./server

一旦二进制文件编译完成,你不需要在机器上安装 Bun 来运行服务器。

这很好,因为部署服务器不需要安装额外的运行时来运行,使得二进制文件具有可移植性。

目标平台

你也可以添加 --target 标志来为目标平台优化二进制文件。

bash
bun build \
	--compile \
	--minify-whitespace \
	--minify-syntax \
	--target bun-linux-x64 \
	--outfile server \
	src/index.ts

以下是可用目标平台的列表:

TargetOperating SystemArchitectureModernBaselineLibc
bun-linux-x64Linuxx64glibc
bun-linux-arm64Linuxarm64N/Aglibc
bun-windows-x64Windowsx64-
bun-windows-arm64Windowsarm64-
bun-darwin-x64macOSx64-
bun-darwin-arm64macOSarm64N/A-
bun-linux-x64-muslLinuxx64musl
bun-linux-arm64-muslLinuxarm64N/Amusl

为什么不使用 --minify

Bun 确实有 --minify 标志可以压缩二进制文件。

但是如果我们使用 OpenTelemetry,它会将函数名减少到单个字符。

这使得跟踪变得比应该的更困难,因为 OpenTelemetry 依赖于函数名。

但是,如果你不使用 OpenTelemetry,你可以选择使用 --minify 代替

bash
bun build \
	--compile \
	--minify \
	--outfile server \
	src/index.ts

权限

一些 Linux 发行版可能无法运行二进制文件,如果你在 Linux 上,我们建议为二进制文件启用可执行权限:

bash
chmod +x ./server

./server

未知的随机中文错误

如果你尝试将二进制文件部署到服务器但无法运行并出现随机中文字符错误。

这意味着你运行的机器不支持 AVX2

不幸的是,Bun 需要具有 AVX2 硬件支持的机器。

据我们所知,没有解决方法。

编译为 JavaScript

如果你无法编译为二进制文件或者你正在 Windows 服务器上部署。

你可以将你的服务器打包为一个 JavaScript 文件。

bash
bun build \
	--minify-whitespace \
	--minify-syntax \
	--outfile ./dist/index.js \
	src/index.ts

这将生成一个单一的可移植 JavaScript 文件,你可以部署到你的服务器上。

bash
NODE_ENV=production bun ./dist/index.js

Docker

在 Docker 上,我们建议始终编译为二进制文件以减少基础镜像开销。

以下是使用 Distroless 镜像和使用二进制文件的示例镜像。

dockerfile
FROM oven/bun AS build

WORKDIR /app

# 缓存包安装
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 来部署生产服务器。

由于 OpenTelemetry 依赖猴子补丁 node_modules/<library>。为了让插桩正常工作,我们需要指定要插桩的库是外部模块,以将其排除在打包之外。

例如,如果你使用 @opentelemetry/instrumentation-pg 来插桩 pg 库。我们需要将 pg 排除在打包之外,并确保它正在导入 node_modules/pg

为了实现这一点,我们可以将 pg 指定为外部模块,使用 --external pg

bash
bun build --compile --external pg --outfile server src/index.ts

这告诉 bun 不要将 pg 打包到最终输出文件中,而是在运行时从 node_modules 目录导入。因此在生产服务器上,你也必须保留 node_modules 目录。

建议将生产服务器中应该可用的包在 package.json 中指定为 dependencies,并使用 bun install --production 来仅安装生产依赖。

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"
	}
}

然后在运行构建命令后,在生产服务器上

bash
bun install --production

如果 node_modules 目录仍然包含开发依赖,你可以删除 node_modules 目录并重新安装生产依赖。

Monorepo

如果你在 Monorepo 中使用 Elysia,你可能需要包含依赖的 packages

如果你使用 Turborepo,你可以将 Dockerfile 放在应用程序目录中,如 apps/server/Dockerfile。这也适用于其他 monorepo 管理器,如 Lerna 等。

假设我们的 monorepo 使用 Turborepo,结构如下:

  • apps
    • server
      • Dockerfile(在此处放置 Dockerfile)
  • packages
    • config

然后我们可以在 monorepo 根目录(而不是应用程序根目录)构建我们的 Dockerfile:

bash
docker build -t elysia-mono .

使用如下 Dockerfile:

dockerfile
FROM oven/bun:1 AS build

WORKDIR /app

# 缓存包
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 为每次部署分配一个随机端口,可以通过 PORT 环境变量访问。

我们需要修改我们的 Elysia 服务器以接受 PORT 环境变量以符合 Railway 端口要求。

而不是固定端口,我们可以使用 process.env.PORT 并在开发时提供回退。

ts
new Elysia()
	.listen(3000) 
	.listen(process.env.PORT ?? 3000) 

这应该允许 Elysia 拦截 Railway 提供的端口。

TIP

Elysia 自动将主机名分配为 0.0.0.0,这与 Railway 兼容