部署 Deno 应用程序全流程指南
1. 部署前的准备与容器化基础
在开始部署 Deno 应用程序之前,我们需要进行一系列的准备工作。首先,Deno 会下载一系列必要的文件,如:
Download https://deno.land/std@0.83.0/hash/_wasm/hash.ts
Download https://deno.land/std@0.83.0/hash/hasher.ts
Download https://deno.land/std@0.83.0/hash/_wasm/wasm.js
Download https://deno.land/std@0.83.0/encoding/hex.ts
Download https://deno.land/std@0.83.0/encoding/base64.ts
之后会检查 file:///app/src/index.ts
文件。同时,还会下载并加载 deno_mongo
插件:
INFO downloading deno plugin "deno_mongo" from "https://github.com/manyuanrong/deno_mongo/releases/download/v0.13.0/libdeno_mongo.so"
INFO load deno plugin "deno_mongo" from local "/app/.deno_plugins/deno_mongo_3bbff9a1cd76f3d988b3ca28c7163c3f.so"
当这些准备工作完成后,应用程序会在 http://localhost:8080
上运行。这里执行的是 museums-api
镜像的 0.0.1 版本,将容器的 8080 端口绑定到主机的 8080 端口。
拥有包含应用程序的 Docker 镜像有多种用途,其中之一就是部署应用程序。此外,这个 Docker 镜像还可以用于在特定版本下运行和调试应用程序。
1.1 在容器内运行终端
我们可以在 Docker 镜像内执行终端,这对于调试或在特定版本的应用程序中尝试某些操作非常有用。具体操作是使用 -it
标志,它允许我们与镜像内的终端建立交互式连接,并将想要首先在镜像内执行的命令名称作为参数传递。例如,执行以下命令:
$ docker run -p 8080:8080 -it museums-api:0.0.1 sh
这将运行 museums-api:0.0.1
镜像,将其 8080 端口绑定到主机的 8080 端口,并在其中以交互式终端执行 sh
命令。在执行 sh
命令后,我们可以执行其他命令,如 ls
查看文件列表:
/app # ls
Dockerfile certificate.pem config.staging.yaml
index.html lock.json
README.md config.dev.yaml heroku.yml
key.pem src
由于我们有一个交互式 shell 连接到这个容器,我们还可以运行 Deno 命令,例如查看 Deno 版本:
/app # deno --version
deno 1.7.2 (release, x86_64-unknown-linux-gnu)
v8 8.9.255.3
typescript 4.1.3
/app #
这为开发和调试提供了更多的可能性,我们可以检查应用程序在特定版本下的运行情况。
以下是在容器内操作的流程图:
graph TD;
A[启动容器] --> B[执行 sh 命令];
B --> C[执行 ls 命令查看文件列表];
B --> D[执行 Deno 命令查看版本];
2. 在 Heroku 上构建和运行应用程序
我们的目标是找到一种简单、自动化且可复制的方式来部署应用程序。在前面的步骤中,我们已经创建了容器镜像,接下来要创建一个管道,以便在代码更新时自动构建和部署应用程序。我们将使用 git
作为代码的源,并通过它触发管道构建。
选择的部署平台是 Heroku,它旨在通过提供一系列工具简化开发人员和公司的部署过程,消除常见的障碍,如机器配置和大型 CI 基础设施的设置。
在开始之前,需要完成以下准备工作:
- 创建 Heroku 账户: https://signup.heroku.com/dc
- 安装 Heroku CLI: https://devcenter.heroku.com/articles/heroku-cli
2.1 在 Heroku 上创建应用程序
在 Heroku 上创建应用程序需要以下步骤:
1. 登录 Heroku CLI :
$ heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/cli/...
Logging in... done
Logged in as your-login-email@gmail.com
- 初始化 Git 仓库 :由于 Heroku 部署基于
git
,而当前文件夹可能不是 Git 仓库,所以需要进行初始化:
$ git init
Initialized empty Git repository in /Users/alexandre/dev/museums-api/.git/
- 在 Heroku 上创建应用程序 :
$ heroku create
Creating app... done, boiling-dusk-18477
https://boiling-dusk-18477.herokuapp.com/ | https://git.heroku.com/boiling-dusk-18477.git
创建应用程序时,Heroku CLI 会自动在当前文件夹中创建一个 Git 仓库,并创建一个名为 heroku
的远程仓库,我们需要将代码推送到这个远程仓库以触发部署过程。
以下是在 Heroku 上创建应用程序的步骤表格:
| 步骤 | 操作 | 命令示例 |
| ---- | ---- | ---- |
| 1 | 登录 Heroku CLI | $ heroku login
|
| 2 | 初始化 Git 仓库 | $ git init
|
| 3 | 创建 Heroku 应用程序 | $ heroku create
|
2.2 构建和运行 Docker 镜像
默认情况下,Heroku 只是尝试通过运行代码来使应用程序可用。但我们希望使用容器来运行应用程序,这需要更多的配置。Heroku 提供了一个名为 heroku.yml
的文件,允许我们定义代码更改时的操作。具体配置步骤如下:
1. 创建 heroku.yml
文件 :在仓库根目录创建 heroku.yml
文件,并添加以下内容以使用 Docker 构建镜像:
build:
docker:
web: Dockerfile
- 定义运行应用程序的命令 :在同一文件中添加以下内容,定义 Heroku 执行应用程序的命令:
build:
docker:
web: Dockerfile
run:
web: deno run --allow-net --unstable --allow-env --allow-read --allow-write --allow-plugin src/index.ts
由于 Heroku 出于安全最佳实践不使用根权限运行命令,而 Deno 在使用插件时需要根权限,所以需要在 heroku.yml
中明确定义此命令。
3. 设置应用程序类型为容器 :
$ heroku stack:set container
Setting stack to container... done
- 添加文件到版本控制并推送到 Heroku :
$ git add .
$ git commit -m "Configure heroku"
$ git push heroku master
执行 git push heroku master
后,会触发 Docker 镜像的构建过程,最后将镜像推送到 Heroku 的内部镜像注册表。
以下是构建和运行 Docker 镜像的流程图:
graph TD;
A[创建 heroku.yml 文件] --> B[定义构建和运行命令];
B --> C[设置应用程序类型为容器];
C --> D[添加文件到版本控制];
D --> E[提交文件];
E --> F[推送到 Heroku];
3. 配置应用程序以进行部署
目前,当代码推送到 git
时,应用程序会开始构建镜像并进行部署,但应用程序尚未正常工作,这是因为缺少必要的配置。
3.1 支持多环境配置
我们的应用程序目前总是加载开发环境的配置文件 config.dev.yml
,这是不符合生产需求的。我们需要修改代码以支持多环境配置,具体步骤如下:
1. 修改 src/index.ts
文件 :确保将环境变量 DENO_ENV
传递给 load
函数:
const config = await loadConfiguration(Deno.env.get("DENO_ENV"));
这样在开发环境中,即使 DENO_ENV
未定义,应用程序也能正常运行,同时在生产环境中可以加载不同的配置文件。
2. 创建生产配置文件 :创建 config.production.yml
文件,目前除端口外,它与 config.dev.yml
差异不大。在生产环境中,我们让应用程序运行在 9001 端口:
web:
port: 9001
为了在本地测试,可以设置 DENO_ENV
变量为 production
来运行应用程序:
$ DENO_ENV=production deno run --allow-net --unstable --allow-env --allow-read --allow-write --allow-plugin src/index.ts
运行后,如果应用程序端口变为 9001,则说明正确加载了生产配置文件。
3.2 设置环境变量
应用程序依赖于一些环境变量提供的配置值,如 JSON Web Token (JWT) 密钥和 MongoDB 凭证。在 Heroku 中,可以使用 config
命令来设置这些变量:
1. 设置环境变量 :
$ heroku config:set MONGODB_PASSWORD=your-password MONGODB_USERNAME=your-username JWT_KEY=your-jwt-key DENO_ENV=production
同时,要确保在 config.production.yml
文件中正确配置 MongoDB 集群的 clusterURI
和 database
。
2. 提交更改并推送到 Heroku :
$ git commit -am "Configure environment variables and DENO_ENV"
$ git push heroku master
以下是配置环境变量的步骤表格:
| 步骤 | 操作 | 命令示例 |
| ---- | ---- | ---- |
| 1 | 设置环境变量 | $ heroku config:set MONGODB_PASSWORD=your-password MONGODB_USERNAME=your-username JWT_KEY=your-jwt-key DENO_ENV=production
|
| 2 | 提交更改 | $ git commit -am "Configure environment variables and DENO_ENV"
|
| 3 | 推送到 Heroku | $ git push heroku master
|
3.3 从环境中获取应用程序端口
Heroku 在运行 Docker 镜像时,不允许我们设置应用程序运行的端口,而是通过设置 PORT
环境变量来指定应用程序应运行的端口。我们需要修改应用程序,让其优先从环境变量中获取端口。
修改 src/config/index.ts
文件,确保它从环境变量中读取 PORT
变量,并覆盖配置文件中的端口设置:
type Configuration = {
web: {
port: number;
};
cors: {
// ...
};
};
export async function load(
env = "dev",
): Promise<Configuration> {
const configuration = parse(
await Deno.readTextFile(`./config.${env}.yaml`),
) as Configuration;
return {
...configuration,
web: {
...configuration.web,
port: Number(Deno.env.get("PORT")) || configuration.web.port,
},
// ...
};
}
这样,应用程序会优先使用 PORT
环境变量中的端口值,若该变量未设置,则使用配置文件中的默认端口。
以下是从环境中获取应用程序端口的流程图:
graph TD;
A[读取配置文件] --> B[获取环境变量 PORT];
B --> C{PORT 是否存在};
C -- 是 --> D[使用 PORT 作为端口];
C -- 否 --> E[使用配置文件中的端口];
4. 测试与监控部署后的应用程序
完成上述配置后,我们可以测试应用程序是否正常工作。可以通过访问 Heroku 仪表板(如 https://dashboard.heroku.com/apps/boiling-dusk-18477
),点击“Open App” 按钮,或者直接访问应用程序的 URL 来进行测试。
如果使用 MongoDB Atlas,需要配置其网络访问,允许从 “anywhere” 进行连接,因为 Heroku 的免费层运行在共享集群上,我们无法知道运行应用程序的机器的固定 IP 地址。具体配置可参考: https://docs.atlas.mongodb.com/security/ip-access-list 。
为了进一步测试部署是否成功,可以对代码进行修改并重新部署。例如,将 src/web/index.ts
中的 “Hello World” 消息改为 “Hello Deno World!”:
app.use((ctx) => {
ctx.response.body = "Hello Deno World!";
});
然后提交更改并推送到 Heroku:
$ git commit -am "Change Hello World message"
$ git push heroku master
再次访问应用程序 URL,应该能看到更新后的消息。
此外,Heroku 提供了一些有用的监控和管理功能:
- 日志查看 :在 Heroku 仪表板上,点击 “More” 按钮,选择 “View logs”,可以实时查看应用程序的日志,这对于轻量级监控应用程序的运行情况非常有用。
- 活动记录 :通过 Heroku 仪表板的 “Activity” 部分,可以查看应用程序的部署历史记录,了解每次部署的时间和部署者。
通过以上步骤,我们成功地部署了 Deno 应用程序,并创建了一个完整的基础设施,使得未来的迭代能够轻松地推送给用户。同时,我们也学习了如何根据不同的环境加载配置,如何设置环境变量,以及如何利用 Heroku 的监控功能来管理应用程序。