avatar

目录
NestJS Docker GitHub Action CI/CD

Docker 部署 NestJS RESTful API

获取源代码

VPS 上获取 GitHub 代码:

shell
1
2
3
4
5
6
7
8
# 创建工作目录
mkdir -p /var/www

# 获取代码
git clone https://github.com/w3cTim/nest-webstack.git

# 跳转工作目录
cd /var/www/nest-webstack/server

创建 Dockerfile,打包镜像

项目里 admin 文件夹为前端,server 文件夹为后端;后端分管理端、客户端,工作空间使用 Monorepo(单体仓库)模式;先部署 server,创建 Dockerfile 文件,它是一个配置文件,类似 package.json,定义了如何生成镜像。

shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# dockerfile

# 基于 node lts-alpine 版本镜像,并通过构建阶段命名,将有 node 环境的阶段命名为 build-stage
#(包含 alpine 的镜像版本相比于 latest 版本更加小巧,更适合作为 docker 镜像使用)
FROM node:lts-alpine

# 自定义镜像的默认工作目录
WORKDIR /nestwebstack

# 复制当前目录所有文件到 镜像中 workdir 中
COPY . .

# 如果是国内服务器 设置淘宝源
# RUN npm config set registry https://registry.npm.taobao.org

# 运行 npm install 安装依赖
RUN npm install

# 运行 nest build 编译 admin、server 服务
RUN npm run build_admin
RUN npm run build_server

# 编译后只需要生产依赖
# 后测试,没必要,还浪费时间
# RUN rm -rf node_modules
# RUN npm install --production

# 暴露的端口 3008:客户端 api; 3009:管理端 api
EXPOSE 3008 3009

# 定参
ENTRYPOINT [ "npm" ,"run"]
# 变参 默认启动 客户端 api
# 如要运行管理端 api 在 docker run image 后加 start:prod_admin 参数,参考下面部署说明
CMD ["start:prod_server"]

再创建 .dockerignore,它的作用于 .gitignore 类似,打包时忽略哪些文件,不打包到 image。

shell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# .dockerignore

# 这里注意 NestJS 打包后还需要依赖 node_modules,所以不能忽略
# 这里多次测试了打包,忽不忽略 node_modules 打包后的 image 大小居然没变化,头大。。
# 且创建容器时,从镜像里直接复制过去速度肯定快些
# node_modules

# ** 匹配任意数量目录(包括零)的特殊通配符字符串
**/.git
**/.gitignore
# **/bin
# **/charts
# **/docker-compose*
# **/Dockerfile*
npm-debug.log
LICENSE
README.md

再执行下面命令打包镜像

shell
1
docker build -t webstack:1.0.1 --nocache .

-t 为生成镜像的名称(tag)及版本,别忘了 .,表示当前目录。

成功打包后:通过 docker images 查看所有镜像;

查看所有镜像

生成客服端 API 容器

shell
1
docker run -d -p 3008:3008 -v webstack_server:/nestWebStack --name webstack_server 83599aafade5

-v 映射数据卷为 webstack_server 如果数据卷不存在,Docker 会帮你自动创建,并将容器内部自带的文件存储到默认的存放路径中:/var/lib/docker/volumes/数据卷名称/_data/nestwebstack 为 Dockerfile 中 WORKDIR 定义目录。

生成管理端 API 容器

shell
1
docker run -d -p 3009:3009 -v webstack_admin:/nestWebStack --name webstack_admin 83599aafade5 start:prod_admin

start:prod_admin 将覆盖 Dockerfile 定义的 CMD 命令

配置项目环境变量

容器成功创建,但项目还未配置环境变量文件,无法读取配置项,容器会自动停止;需把 .evn 环境变量拷贝到数据卷中,再启动容器,也可先创建好数据卷,把 .evn 先拷贝进去,这样就不用再重启容器了这一步。

该项目使用的 MongoDB 数据库也是在当前服务器的一个容器。Docker 容器之间的通信可以分为单主机容器之间的通信和跨主机容器之间的通信;单主机容器之间通信默认使用的 bridge 模式,这张 bridge network 的图,表明容器之间可以互通,还可以链接互联网。其他模式,参考官方文档,这里不 BB 了。

Docker network bridge 模式

通过下面命令查看具体容器的 bridge 模式的 IP

shell
1
docker inspect --format='{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <docker_mongodb_name>

建议是使用 Portainer 来管理 Docker,很方便就可以查看和管理容器信息。

Portainer

知道 MongoDB 的 IP 后,在环境变量里修改数据库连接,优化连接效率。

Docker Compose 管理多个容器

后端容器已经生成,但是操作还是有点麻烦,先要生成镜像,再一个一个生成容器。这时就可以使用 Docker Compose 批量管理容器,Docker Compose 是啥?可以先看看 Docker 入门官方文档 https://docs.docker.com/compose/

安装 Docker Compose

  1. 下载当前稳定版 Docker Compose

    shell
    1
    sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
  2. 赋予可执行权限

    shell
    1
    sudo chmod +x /usr/local/bin/docker-compose
  3. 创建软链 (快捷方式😂)

    shell
    1
    sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
  4. 检查是否安装成功

    shell
    1
    docker-compose

    提示操作命令说明等信息,说明安装成功,更多命令查看:https://docs.docker.com/compose/reference/overview/

    docker-compose

创建 docker-compose.yml

yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 参考:https://docs.docker.com/compose/compose-file/
# 请以官方文案为准,国内很多文章都过时了,有很多坑。。
# 这里版本号对你应用的 Docker Engine release 不能乱写
version: '3.8'

services:
webstack_server:
# restart: alwavs 已过时。。
build: # 先构建自定义镜像
context: . # 指定 Dockerfile 文件的所在路径
dockerfile: Dockerfile # 指定 Dockerfile 文件名称
image: webstack:1.0.1 # 自定义镜像名称及版本,就是上面 build 后的镜像
container_name: webstack_server
# 必须指定 network_mode 否则不会分配网络
# https://docs.docker.com/compose/compose-file/#network_mode
network_mode: bridge
ports:
- 3008:3008
volumes: # 使用标示的方式必须指定 volumes,这与直接 docker run 不同
- webstack_server:/nestwebstack
webstack_admin:
image: webstack:1.0.1
container_name: webstack_admin
network_mode: bridge
ports:
- 3009:3009
volumes: # 当然也可以直接使用绝对路径
- webstack_admin:/nestwebstack
command: start:prod_admin

# volume 可为空,为空使用 docker 引擎配置的默认驱动程序
# 也可以自定义存储到其他机器上,可以指定 volume name,不指定默认会加上 stack name 前缀
# https://docs.docker.com/compose/compose-file/#volume-configuration-reference
volumes:
webstack_admin:
name: webstack_admin
webstack_server:

编写好 docker-compose.yml 执行:

shell
1
docker-compose up -d --build
  • -d:分离模式;后台执行该服务
  • --build:在启动容器之前,先再生成镜像

到这就一行命令完成了后端容器批量创建,后期也可以使用 docker-compose 命令批量管理容器。

部署前端 Vue UI

VPS 上的 Docker 已经部署了 Nginx 容器,只需要把 Vue 项目 build 后,复制到 Nginx 站点目录即可,就不单独生成容器了。

使用 nvm 管理 Node

先安装 nvm,使用命令:

shell
1
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash

或者:

shell
1
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash

从远程下载 install.sh 脚本并执行。注意这个版本号 v0.35.3 会随着项目开发而变化;请随时通过官方最新安装命令来检查最新安装版本。

安装好脚本会提示添加环境变量:

shell
1
2
3
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion

复制提示脚本到 ~/.base_profile 再运行 source ~/.bash_profile 使当前 shell 重新加载环境变量。

.base_profile

运行 nvm 提示版本、操作命令说明等信息,则安装成功。

nvm

如果是国内可配置下淘宝源

shell
1
2
3
4
5
# Node.js 下载源
nvm node_mirror https://npm.taobao.org/mirrors/node/

# npm 下载源
nvm npm_mirror https://npm.taobao.org/mirrors/npm/

nvm 常用命令:

  • nvm ls-remote –lts:列出所有可安装的 Node.js 长期支持(lts)版本号
  • nvm install 12.18.3:安装指定版本号的 Node.js
  • nvm use 12.18.3:切换 Node.js 的版本,这个是全局的
  • nvm current:当前 Node.js 版本
  • nvm ls:列出所有已经安装的 Node.js 版本

build、部署项目

shell
1
2
3
4
5
6
7
8
9
cd ../admin
# 使用 Node 版本
nvm use 12.16.1
# 安装依赖
npm ci
# 生成项目
npm run build
# 生成后的项目复制到 Nginx 配置的站点目录
mkdir -p /home/web/website && cp -rf dist/* /home/web/website

Nginx 该怎么配置站点和反向代理 RESTful API 这里就不赘述了,教程一搜一大把。

GitHub Active CI/CD

现在后台服务已经做到打包部署,一行命令搞定,前台也只需要 Build 的后的页面拷贝到 Nginx 目录即可。做到这里,自动持续化部署已经完成一大半了,现在就差怎么来触发这些条命令了,这时就轮到 GitHub Action 登场了,具体介绍可以看下 阮代佬的入门教程;个人理解它类似数据库的触发器,当你执行代码 Git 的相关事件的时候 GitHub Actions 就会执行编写好的一系列工作流程,如抓取代码、运行测试、登录远程服务器,发布到第三方服务等等。

  1. 创建 Active,选择 GitHub 项目 ActionsNew workflow

  2. Marketplace 查找大佬们写好的相关 Workflow,Types 选择 Actions,本项目使用的 Workflow 是:

    • 远程连接 SSH 到 VPS;
    • pull 代码;
    • 调用 Docker Compse CD 后台;
    • 执行命令 CD 前台;
      所以查看 ssr 相关 Workflow,使用 Star 数较多的 SSH Remote Commands

    Workflow

  3. 在项目的 SettingsSecrets 添加 SSH 地址、用户名、密码 环境变量

    Secrets 变量

  4. 依照 SSH Remote Commands 例文,编写 script 即可

    yml
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # https://github.com/marketplace/actions/ssh-remote-commands
    # https://docs.github.com/cn/actions
    name: WebStack CI
    on: [push]
    jobs:
    build:
    name: CI/CD
    runs-on: ubuntu-latest
    steps:
    - name: executing remote ssh WebStack CI
    uses: appleboy/ssh-action@master
    with:
    host: ${{ secrets.HOST }}
    username: ${{ secrets.USERNAME }}
    password: ${{ secrets.PASSWORD }}
    script: |
    cd /var/www/nest-webstack
    git pull
    cd server
    # -d 必须添加,后台执行该服务,否则任务挂起,后面命令无法执行。
    docker-compose up -d --build
    cd ../admin
    nvm use 12.16.1
    npm ci
    npm run build
    mkdir -p /home/web/website && cp -rf dist/* /home/web/website

一切就绪!push 代码!Workflow 执行,服务器就会自动 pull 代码,Docker Compose 打包镜像、重新部署、启动容器,前端生成、发布一气呵成!~ 就是我这 1 核/1G VPS 压力有点大🤣,感兴趣的同学赶紧试试吧!

Action

文章作者: Tim
文章链接: http://w3ctim.com/post/c04d9668.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Tim

评论