Nest 最佳实践:使用 docker-compose 一键启动开发环境

1,995 阅读6分钟

这篇文章和大家分享一个使用 Docker 来作为我们的 Nest.js 开发环境,这篇文章是因为我在测试一个 nest.js + prisma + postgresql 时候遇到的一个问题,这里就顺便写成文章记录一下。

先简单介绍一下 DockerDocker-Compose 的概念吧。

Docker 是一个开源的应用容器引擎,可以让开发者打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器或 Windows 机器上,也可以实现虚拟化。也就是说使用 Docker 可以非常方便的统一各种环境。

Docker-compose 是一个用于定义和运行多容器 Docker 应用程序的工具。它使用 YAML 文件来配置应用程序的服务,并管理整个应用程序的生命周期。

在使用 nest.js 开发后端的过程当中,我经常会 PostgreSQL 作为数据库,使用 Prisma 作为 ORM 框架来操作数据库。

本文将介绍如何使用 docker compose 来打造一个 nest.js 的集成开发环境。

因为日常开发过程当中,我们需要使用到数据库、缓存、消息队列等等各种中间件,如果把这些中间件一个一个的安装到我们的电脑上是非常痛苦的,有了 Docker 这个容器化工具,我们只需要将各种我们开发过程当中需要使用到的中间件都写到一个 yaml 文件当中就可以一键启动所有需要的中间件。

创建一个基本的 Nest.js 项目

接下来就开始从零开始搭建一个使用 docker-compose 作为我们的开发环境来使用 nest.js 进行开发。

文章示例当中使用 prismapostgresql 作为作为开发所需要的 ORM 框架和数据库。

创建一个新的 nestjs 项目

nest new nest-compose-test

1722176521063-M8oZ9n

开发环境

在使用 docker 作为我们的开发环境,首先我们需要创建一个 Dockerfile 让我们的代码在自定义的镜像当中运行。

这里我先贴出运行 nest 所需的 Dockerfile

FROM node:20-alpine

WORKDIR /app
RUN corepack enable
COPY package*.json ./
RUN pnpm install
COPY . .
EXPOSE 3000
CMD ["pnpm", "start:dev"]

这里我们使用 node 20 作为我们的基础镜像,然后为了充分利用 docker 分层缓存的特性,先安装依赖之后再将我们的源代码复制到镜像内,最后暴露 3000 端口供我们访问容器应用使用。

测试一下构建我们的应用,命令如下:

docker build -t nest-compose-test:0.0.1 .

如下图所示,这里可能会因为一些网络原因出现 build 不成功的情况,这可以尝试使用一些镜像。 1722177408124-Wipwnu

测试一下构建出来的镜像运行情况. 命令如下:

docker run -it --rm  -p 3000:3000 nest-compose-test:0.0.1

如果你前面的步骤都成功了,那么你得到的结果和下面的截图应该是一致的。 1722177652430-PrSJsi

打开浏览器访问 http://localhost:3000/ ,可以看到我们已经成功的访问到了我们容器内部的应用了。

1722177771041-2zYOy6

ok,到这里我们如果不使用 docker-compose 来编排我们的应用服务,只作为单独的运行镜像的话,已经完全够了。

接下来我们就使用 docker-compose 来编排我们的服务。

编写 docker-compose 文件

这里假设我们的应用依赖 postgresql,在我们的应用目录下创建一个 compose.yml 文件,写入以下内容:

services:
  web:
    build: .
    ports:
      - 3000:3000
    develop:
      watch:
        - action: sync
          path: .
          target: /app
          
  db:
    image: postgres
    restart: always
    shm_size: 128mb
    ports:
      - 5432:5432
    volumes:
      - ${DOCKER_VOLUME_DIRECTORY:-.}/data:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=123456
      - POSTGRES_DB=demo
      - TZ=Asia/Shanghai

这里我们定义了两个服务,第一个叫 web 的服务,使用了我们上面一步自定义的 docker 镜像,还定一个了一个 db 的服务,作为我们应用的数据库服务。

关于 db 服务的介绍这里我直接跳过,只需要知道我们初始化了一个 demo 的数据库并且把账号设置为 postgres,密码设置为 123456 ,有兴趣可以去 docker hub 里面查看详细介绍。

hub.docker.com/_/postgres

在 web 服务当中,使用一个特殊的配置 develop ,我详细解释一下其中的意思。

  • watch: 这里表示我们在 watch 模式下的一下设置
  • watch.action: 这里我们使用 sync 这个 action 来同步我们的源代码文件,当我们修改代码时,就会将我们新的代码同步到容器内
  • watch.path:表示我们要同步的代码的位置
  • watch.target: 表示我们同步的代码的目标位置

测试

OK,到这里我们可以测试一下效果,我们安装一下 prisma 以及随便配置一个 model ,并且在 packages.json 当中增加一条 scripts。

"prestart:dev": "prisma migrate deploy && prisma generate",

定义一个 User 的 model 对象

model User {
  id        String   @id @default(uuid())
  username  String
  password  String
  createdAt DateTime @default(now()) @map("created_at")
  udpatedAt DateTime @default(now()) @updatedAt @map("updated_at")
  isDeleted Boolean @default(false)
}

同时我们创建一个 prisma 的连接对象。

// db.ts
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

export { prisma };

因为使用同一个 docker-compose 编排的服务,都是可以直接使用服务名访问到对应的服务的。

DATABASE_URL="postgresql://postgres:123456@db:5432/demo?schema=public"

这里别忘了提前生成迁移文件。

app.module.ts 当中注册一下我们的 db 客户端,并且写一个服务测试一下效果。

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { prisma } from './db';
import { PrismaClient } from '@prisma/client';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [
    AppService,
    {
      provide: PrismaClient.name,
      useValue: prisma,
    },
  ],
})
export class AppModule {}
// app.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class AppService {
  @Inject(PrismaClient.name)
  private readonly db: PrismaClient;

  async getList() {
    await this.db.user.create({
      data: {
        username: 'demo',
        password: '123456',
      },
    });

    return this.db.user.findMany();
  }
}
// app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  async getUserList() {
    return this.appService.getList();
  }
}

然后再使用下面的命令启动起来。

sudo docker compose up

出现下面所示的输出, 即表示我们的应用已经成功的运行起来了。

1722182149430-s6RLn2

测试一下我们的接口 1722182241321-k484Mg

在终端当中按下 w ,即可进入 watch 模式,在这个模式下,我们就可以实时同步我们编写的应用代码到容器内,并且同时让他运行。

现在我们开发环境的运行效果如下: 1722182823362-hSL9sv

可以看到,我们修改代码可以立即的将代码同步到容器内。

到这里我们已经成功的使用 docker-compose 一键启动了我们的开发环境,当然我们也可以使用 docker 来做更多的事情,以后有机会再继续分享。

总结

在这篇文章当中,我们通过一个最简单的 demo 搭建一个直接使用 docker 作为我们的 nest 的开发环境,使用 docker 非常方便帮助我们集成各种开发所需要的依赖,文章当中虽然只简单的使用了 postgresql,但是像 redisnginx 等等这种服务都可以非常的方便的集成到我们的 compose.yml 文件当中去。

代码仓库地址: github.com/DimplesY/ne…