介绍

也许你曾经使用过 github page 或者其他的静态网站托管服务部署自己的博客或者前端页面。但是只有前端还远远算不上一个网站。这篇文章就来介绍一下如何一分钱不花的部署一个后端服务。

我们使用的平台叫做 heroku,如果你没有听说过它的话,没有关系,heroku 是一个老牌的云平台提供商。你可以通过它非常简单的部署一个在线应用。并且只需要注册就可以领取一定量的免费服务器使用额度(截至到 2021/3/26,heroku 提供的免费额度为 550 小时/月),作为个人服务,这些时长已经足够我们使用的了。

heroku 自我介绍
本文将介绍如何使用 heroku 部署一个 nodejs 后端服务,当然 heroku 还支持其他语言,你可以点击 这里查看它所支持的所有编程语言。

注册 & 安装工具
现在我们就来准备一下需要的工具,首先我们打开 heroku - 注册 来注册我们的账号。注意有可能存在网站打不开的情况,请自备梯子。注册完成后会让你进行邮箱验证,没什么好说的。

注册完成之后我们就可以安装工具,想要部署 Heroku 应用,我们需要用到 heroku 命令行工具,你可以在 这里 找到下载链接。除了 heroku 之外,我们还需要用到 git 和 node,安装完成后我们在终端里执行如下命令来检查是否安装成功:

查看 heroku 是否安装完成

1
$ heroku -v

heroku/7.59.3 linux-x64 node-v12.21.0

查看 git 是否安装完成

1
$ git --version

git version 2.25.1

查看 node 是否安装完成

1
$ node -v

v16.13.1
需要的工具都准备好了,我们在终端中执行如下命令来登录 heroku,登录时会要求提供邮箱密码,依次填写即可:

1
$ heroku login -i

输出如下:

1
2
3
4
heroku: Enter your login credentials
Email: your.email.here@gmail.com
Password: *************
Logged in as your.email.here@gmail.com

至此,我们的准备工作就已经全部完成了,下面就来部署我们的第一个应用:

部署第一个应用

首先,我们先准备要部署的应用,在终端中执行如下命令来拉取项目(这个项目是 heroku 提供的 node 部署示例 ):

本地测试

1
2
3
4
$ git clone https://github.com/heroku/node-js-getting-started.git 
$ cd node-js-getting-started
$ npm install
$ npm start

创建的应用

1
2
3
$ heroku create
# Creating app... done, ⬢ xxx-xxx-xxx
# https://xxx-xxx-xxx.herokuapp.com/ | https://git.heroku.com/xxx-xxx-xxx.git
  • 由于我们并没有指定名称,所以随机给我们分配了一个,本文的名字叫做 xxx-xxx-xxx。你也可以指定要创建的新服务名称,如 heroku create my-first-app,但是注意这个名字不能重名,否则会提示无法创建。

  • 在部署之前我们先简单介绍一下, heroku 拥有一个远程的 git 仓库,在执行了 create 操作后,heroku 会在远程仓库中新建一个用于我们应用的专属仓库(上面输出中就可以看到 ),想要部署的时候我们只需要 往这个远程仓库中提交代码,keroku 就会自动根据配置运行构建并部署服务。

  • heroku 还会自动帮我们绑定这个远程仓库,我们可以通过 git remote -v 进行查看。并且,以后这个文件夹就是我们的服务管理目录了,只需要切换到这个目录中,执行的所有 heroku 命令都会自动使用当前应用作为目标。

    1
    2
    3
    4
    5
    $ git remote -v
    # heroku https://git.heroku.com/xxx-xxx-xxx.git (fetch) <--- 就是这两个
    # heroku https://git.heroku.com/xxx-xxx-xxx.git (push)
    # origin git@github.com:heroku/node-js-getting-started.git (fetch)
    # origin git@github.com:heroku/node-js-getting-started.git (push)

    此时我们就可以通过 在线仪表盘 看到我们刚才创建的应用了。不过由于我们还没有上传任何代码,所以现在还无法访问服务。

部署应用

接下来我们就来部署应用,执行如下命令即可。

1
$ git push heroku main

这里要注意推送的分支名要一致,如果你项目默认分支名为 master,这时就要把上面的 main 替换成 master。

执行命令后我们就可以通过 remote 输出看到 heroku 执行了构建。等到上传完成后,我们的服务就已经准备好啦。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Enumerating objects: 541, done.
# Counting objects: 100% (541/541), done.
# Delta compression using up to 8 threads
# Compressing objects: 100% (403/403), done.
# Writing objects: 100% (541/541), 245.22 KiB | 81.74 MiB/s, done.
# Total 541 (delta 102), reused 541 (delta 102), pack-reused 0
# remote: Compressing source files... done.
# remote: Building source:
# remote:
# remote: -----> Building on the Heroku-20 stack
# remote: -----> Determining which buildpack to use for this app
# remote: -----> Node.js app detected
# # ...
# remote: -----> Compressing...
# remote: Done: 32.8M
# remote: -----> Launching...
# remote: Released v3
# remote: https://xxx-xxx-xxx.herokuapp.com/ deployed to Heroku
# remote:
# remote: Verifying deploy... done.
# To https://git.heroku.com/xxx-xxx-xxx.git
# * [new branch] main -> main

部署完成后我们只需要执行如下命令启动服务即可:

1
$ heroku open

启动完成后会自动打开浏览器并访问我们的服务,这时候你应该就可以看到部署成功的网站啦:

部署完成的示例站点

是不是非常的简单,想要进行修改也很简单,我们打开 index.js,可以看到这个应用就是一个简单的 express 服务,我们新增一个 sayhello 接口并返回 hello world,修改完成后你的代码应该是这样子的:

1
2
3
4
5
6
7
8
9
10
11
const express = require('express')
const path = require('path')
const PORT = process.env.PORT || 5000

express()
.use(express.static(path.join(__dirname, 'public')))
.set('views', path.join(__dirname, 'views'))
.set('view engine', 'ejs')
.get('/', (req, res) => res.render('pages/index'))
.get('/sayhello', (req, res) => res.send('<h1>Hello World!</h1>')) // <== 添加了这一行
.listen(PORT, () => console.log(`Listening on ${ PORT }`))

然后我们正常的执行 git 提交操作,如下:

1
2
3
$ git add .
$ git commit -m "feat: sayhello"
$ git push heroku main

随后 heroku 就会开始编译并发布,等待发布完成后打开我们的站点,并在地址栏的末尾添加 /sayhello,就可以看到我们的修改了的内容

heroku 的构建与发布

当我们的 git 项目根目录下存在 package.json 文件时,heroku 就会认为这是一个 node 项目。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"name": "node-js-getting-started",
"version": "0.3.0",
...
"engines": {
"node": "14.x"
},
"dependencies": {
"ejs": "^3.1.5",
"express": "^4.17.1"
},
...
}

可以看到其中包含了一个 engines 字段,heroku 将会使用对应的 node 版本对项目执行 npm install 来安装所需的依赖。安装完成后,heroku 会访问根目录中的 Procfile 来确定要执行什么命令来启动服务。你应该可以在项目根目录下找到如下内容的 Procfile:

1
web: npm start

它的意思是启动一个 web 进程,进程的服务启动命令为 npm start。注意这里的 web 很重要,它是一个固定的进程类型,当 heroku 识别到该类型时,它会把站点的 HTTP 流量 重定向到我们启动的服务进程中。heroku 还支持其他几种进程类型,你可以在 这里 找到它们。


了解完 heroku 是如何启动我们的服务之后,我们来看一下当前服务运行的怎么样,在终端中输入 heroku ps 来进行查看:

1
2
3
4
5
6
7
8
$ heroku ps # 或者 heroku ps -a 你的应用名
# Free dyno hours quota remaining this month: 474h 57m (86%)
# Free dyno usage for this app: 0h 0m (0%)
# For more information on dyno sleeping and how to upgrade, see:
# https://devcenter.heroku.com/articles/dyno-sleeping
#
# === web (Free): npm start (1)
# web.1: up 2022/03/08 13:35:27 +0000 (~ 56m ago)

输出内容的第一句话就告诉我们了 本月剩余的可用额度还有多少,可以看到我们现在还剩下 550 小时(100%),当我们的服务在响应请求时,额度就会被消耗,而当额度用完之后,我们的服务就会被强制关闭直到下个月才能重新恢复。你可以选择 绑定你的信用卡 来将免费额度提升至 1000 小时(具体数值见官网)。

这里我们会接触到一个新名词:dyno,这个其实就是 heroku 自己的服务器容器,你可以将其简单理解成 docker 中的 container。下面是 heroku 自己对 dyno 的说明,你可以在 这里 找到它的完整介绍。


我们可以通过命令来直接操作 dyno,例如当我们想要停止服务,就可以通过如下命令来“缩放” dyno 的数量,关闭之后该服务将不会继续消耗额度 :

把该应用的 web dyno 容器数量设置为 0

1
$ heroku ps:scale web=0

实际上,我们刚才执行 heroku open 时,heroku 就会自动帮我们启动一个免费的 dyno 容器,这个操作等同于手动执行了 heroku ps:scale web=1。

还有一件事,由于我们是免费账户,所以 每个服务只能最大只能启动一个 dyno 提供服务。并且,当一个 dyno 容器超过半个小时都没有收到新请求时,他就会被销毁。不过不用担心,如果有新请求时容器会被自动重启,重启时会有几秒钟的延迟。不过对于我们的 web 后端服务来说,这并不会造成什么影响。

其他常用命令

除了上面介绍的命令外,heroku 还有下列常用命令:

  • 查看启动的服务日志:

    1
    heroku logs --tail # 或者 heroku logs --tail -a 你的应用名
  • 启动可以访问到实际项目文件的控制台:

    1
    $ heroku run sh # 或者 heroku run sh -a 你的应用名