用 Docker Compose 搭建 Node.js 应用(Nest.js)的本地开发环境

4,767 阅读7分钟

在本地电脑上开发 Node.js 应用,需要安装一个 Node.js,还有就是准备一个应用需要的数据库管理系统。在本地安装 Node.js 你可以直接下载使用安装包,这样你的系统里只会包含一个版本的 Node.js。如果想同时拥有多个版本的 Node.js,可以使用 NVM 这个工具来管理安装在电脑上的多个版本的 Node.js,你可以在不同版本之间来回切换。

另外还有一种搭建 Node.js 应用开发环境的方法,就是使用 Docker。下面我介绍一下在本地电脑上使用 Docker 与 Docker Compose 搭建一个 Node.js 的开发环境。我会用 Nest.js 这个应用框架作为演示。

需求

在本地使用 Docker 可以去安装一个 Docker 桌面版,比如 Docker for Windows 或 Docker for Mac。在 Mac 电脑上使用 Docker 桌面版不会有太大问题,但是在 Windows 上运行 Docker 桌面版会有一些需求。

系统必须是 Windows 10 专业版或企业版,普通的 Windows 10 不行,而且需要在系统里启用 Hyper-V。

准备

下载安装 Docker 桌面版,体积挺大,需要下一会儿,下载之前你需要使用 Docker Hub 帐号登录一下才行。

  • Windows:https://docs.docker.com/docker-for-windows/install/
  • macOS:https://docs.docker.com/docker-for-mac/install/

Docker 的大部分操作需要在命令行界面下完成,所以你需要准备一个命令行界面,macOS 用户可以使用系统自带的终端(Terminal),Windows 用户建议下载安装完整版的 Cmder,然后新建一个 Bash as admin 的命令行。

配置

Docker 在创建容器的时候,需要用到一些镜像,也就是如果你的系统上还没有这些镜像的话,Docker 会自动到一个地方去下载这个镜像,保存在你的电脑上,然后基于这个镜像去创建你需要的容器。

下载这些镜像的地方默认在国外,所以我们在国内有时会比较慢,解决的方法是配置一下 Docker 让它使用国内的镜像加速地址。比如阿里云暂时就提供了这个服务,你可以使用自己的阿里云帐号登录到阿里云的容器镜像服务,在里面你会找到一个镜像加速地址,看起来像这样:https://wgaccbzr.mirror.aliyuncs.com

把你在阿里云容器镜像服务上找到的加速地址,配置到你的 Docker 的 Registry Mirrors 里面。

Nest.js 开发环境

下面我们用 Docker Compose 搭建一个在本地可以运行 Nest.js 应用的开发环境。

docker-compose.yml

Docker Compose 允许我们在一个文件里描述应用需要的服务(容器),为你要开发的项目新建一个目录,然后在根目录下创建一个 docker-compose.yml 文件。里面先添加两行代码:

version: '3'

services:

version 设置了一下要使用 Docker Compose 版本,一会儿开发环境需要的几个服务会在 services 下面定义。

.env

在 docker-compose.yml 文件里定义的服务可以使用一些环境变量,这些环境变量还有对应的值可以单独放在一个叫 .env 的文件里面,这个文件就相当于是一个配置文件。在项目根目录下面创建一个空白的文件叫:.env

准备 Nest.js 应用的命令行工具

你打算开发基于 Nest.js 框架的 Node 应用,可以先去安装这个框架提供的命令行工具(@nestjs/cli),它可以让我们使用命令去创建全新的应用还有应用里需要的一些组件。不过因为我们打算用 Docker 的方式搭建应用的开发环境,所以就不直接在电脑上去安装这个工具了,因为这需要你在电脑上安装 Node.js。

docker-compose.yml 文件里定义一个服务,它的作用就是让我们可以使用 Nest.js 框架里提供的命令行工具,打开 docker-compose.yml 文件,在 services 下面添加一个命令行工具服务:

version: '3'

services:
  cli:
    image: nestjs/cli
    volumes:
      - ./app:/workspace
    tty: true

上面定义了一个叫 cli 的服务,这个名字你可以随便定义,这个服务用的 image,也就是镜像是 nestjs/clivolumes 设置了一下数据卷的功能,意思就是让当前目录下的 app 这个目录,对应容器里的 /workspace 这个位置。把 tty 设置成 true 是为了让这个容器一直运行。

打开系统的终端(Terminal),Windows 用户推荐使用 Cmder。进入到 docker-compose.yml 文件所在的目录,然后运行服务:

cd ~/desktop/ninghao-nestjs
docker-compose up -d cli

上面执行了两条命令,第一行是进入到了 docker-compose.yml 文件所在的目录,第二行命令是在后台运行了在 docker-compose.yml 文件里定义的一个叫 cli 的服务。验证一下服务是否运行:

docker-compose ps
        Name           Command   State    Ports  
-------------------------------------------------
ninghao-nestjs_cli_1   /bin/sh   Up      3000/tcp

注意服务的 State 是 Up 表示正在运行,下面可以登入这个 cli 服务:

docker-compose exec cli /bin/sh

进来以后你的命令提示符会像这样:

/workspace #

在这个容器里我们可以使用 Nest.js 应用里的命令行工具,执行:

nest

会出现一些帮助信息:

Usage: nest [options] [command]

Options:
  -V, --version                                   output the version number
  -h, --help                                      output usage information

Commands:
  new|n [options] [name]                          Generate Nest application
  generate|g [options]  [name] [path]  Generate a Nest element
    Available schematics:

用 Nest 命令行工具创建应用

进入到创建的 cli 这个容器里面以后,可以执行 nest 命令,下面我们用这个命令去创建一个 Nest.js 项目。执行:

nest new app

会出现类似的东西:

⚡  We will scaffold your app in a few seconds..

CREATE /app/.prettierrc (51 bytes)
CREATE /app/README.md (3370 bytes)
CREATE /app/nest-cli.json (84 bytes)
...

上面就是用了 nest new 命令创建了一个项目,放在 app 目录的下面,虽然是在 cli 容器里创建的这个项目,但是我们配置了这个服务的数据卷,所以创建的项目文件也会在本地电脑上看到。也就是你在本地电脑上这个 docker-compose.yml 文件所在的目录的下面,会看到一个 app 目录,这里的东西就是创建的 Nest.js 项目。

在开发应用的时候,如果你要使用 nest 命令行工具生成项目需要的文件,你就可以进入到这个 cli 服务容器里面,然后使用 nest 命令去创建你需要的东西。

创建的项目的时候可能会提示:

Failed to execute command: git init
Git repository has not been initialized

这是因为创建完项目之后,nest 命令会去初始化一个代码仓库,但是在这个容器里并没有安装 git ,所以执行相关命令的时候就会出现问题。你可以在本地用 Git 对项目做源代码管理。

注意如果你觉得创建项目的时候速度慢,可以在进入 cli 服务里面以后,执行一下:

npm config set registry https://registry.npm.taobao.org

定义应用服务

docker-compose.yml 文件里,再定义一个运行 Nest.js 应用的服务:

  nest:
    image: node:${NODE_VERSION}
    working_dir: /home/node/app
    command: npm config set registry https://registry.npm.taobao.org
    command: npm run start:dev
    volumes:
      - ./app:/home/node/app
    ports:
      - ${APP_PORT}:3000

上面定义了一个叫 nest 的服务,因为我们创建的应用是基于 Nest.js 框架的,所以这个服务的名字叫 nest,你也可以换成自己喜欢的名字。

nest 这个服务用的 imagenode,具体的版本用了一个环境变量,NODE_VERSION,这个环境变量还有对应的值要在 .env 文件里设置一下。

working_dir 进入到工作目录,然后执行了两个 command,一个是设置了一下 npm 的安装源,这样以后安装包的时候会快一些,第二个 command 是运行了项目的开发服务。

volumes 设置了数据卷,让当前目录下的 app 这个目录,对应 nest 这个服务容器里的 /home/node/app ,我们在这个服务的这个目录的下面,执行了 npm run start:dev,这也就会运行这个 Nest.js 项目的开发服务。

ports 设置了公开的端口,就是设置一个主机(本地电脑)上的端口,让这个端口对应这个容器里的某个端口。运行了 Nest 应用的开发服务以后,会使用 3000 这个端口提供服务。${APP_PORT} 这里用了一个叫 APP_PORT 的环境变量,具体的值要在 .env 文件里设置一下:

NODE_VERSION=11.13
APP_PORT=3000

注意在 .env 文件里,我们让 APP_PORT 这个环境变量的值等于 3000,也就是公开的端口应该就是 3000:3000,也就是本地电脑上的 3000 端口对应的是这个服务里的 3000 端口。

有了这个新的 nest 服务,要再去运行一下:

docker-compose up -d nest

如果一切正常,打开浏览器,访问 http://localhost:3000,你应该会看到一个 “Hello World”。

以后你需要用 npm install 给项目安装一些 Package 的时候,可以进入到这个 nest 服务里面,使用 npm。进入这个服务可以执行:

docker-compose exec nest bash

定义数据服务

开发 Nest.js 应用支持使用多种不同类型的数据库,需要哪种数据库系统,你就去创建一个对应的服务就行了。比如我要在应用里使用 MySQL 这种数据库,所以可以在 docker-compose.yml 里面,再去定义一个 mysql 服务:

  mysql:
    image: mysql:${MYSQL_VERSION}
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    restart: always
    ports:
      - ${MYSQL_PORT}:3306
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

服务里面用到了一些环境变量,打开 .env ,在文件里去定义这些环境变量还有对应的值:

MYSQL_VERSION=5.7
MYSQL_PORT=3306
MYSQL_DATABASE=nest
MYSQL_USER=nest
MYSQL_ROOT_PASSWORD=root
MYSQL_PASSWORD=password

主要就是设置一下要使用的 MySQL 系统的版本,在本地主机上访问这个数据服务用的端口是什么,还有创建的数据库的名字、用户还有密码是什么。你可以修改 .env 文件里的这些环境变量的值,来改变数据库的配置。

上面会创建一个 5.7 版本的 MySQL,在本地主机上使用这个数据服务用的端口是 3306,数据库系统里会创建一个叫 nest 的数据库,操作这个数据库可以使用 nest 用户,对应的密码是 password,另外设置了一下数据库系统的 root 用户的密码为 root

定义好这个数据服务,需要去运行一下:

docker-compose up -d mysql

查看日志

查看容器里的输出的日志,可以执行:

docker-compose logs --follow

附录

docker-compose.yml:

version: '3'

services:
  cli:
    image: nestjs/cli
    volumes:
      - ./:/workspace
    tty: true
  nest:
    image: node:${NODE_VERSION}
    working_dir: /home/node/app
    command: npm config set registry https://registry.npm.taobao.org
    command: npm run start:dev
    volumes:
      - ./app:/home/node/app
    ports:
      - ${APP_PORT}:3000
  mysql:
    image: mysql:${MYSQL_VERSION}
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    restart: always
    ports:
      - ${MYSQL_PORT}:3306
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}

.env:

NODE_VERSION=11.13
APP_PORT=3000

MYSQL_VERSION=5.7
MYSQL_PORT=3306
MYSQL_DATABASE=nest
MYSQL_USER=nest
MYSQL_ROOT_PASSWORD=root
MYSQL_PASSWORD=password