nestjs系列教程docker本地测试部署(二)

2023年 10月 7日 58.1k 0

nestjs项目用docker部署,但是因为是从前端过来的刚接触后端,所以项目开发完了想先在本地电脑上面测试一下用docker。
项目地址 nestmall,下载完项目以后,先在本地运行一下,项目的运行不了解的可以参考另一篇教程 【ToDo】
说说我对docker的理解,docker可以理解成一个VMware的集合,以前我弄过VMware虚拟机系统,docker就是可以很方便的创建多个虚拟机系统,docker里面最重要的可能就是containerimage这两个概念了,image可以理解成系统的包,比如装window系统的时候,需要大白菜的iso镜像(其实我认为就是压缩包),然后呢在镜像的基础上安装系统,docker的image就是这个东西,而container就是对应的系统,我们是可以进入这个容器里面做一下Linux系统的一下命令操作,当然这个是很微型的系统,很多命令没有,比如我这个MySQL的容器,里面有MySQL的命令,里面的目录也和Linux很像。

在这里插入图片描述
好了,上面的废话说完了,下面进入正题,我的电脑是Mac的,所以只能基于我电脑来说了。

安装

从官网下载,安装后 Terminal 中敲下 docker,有使用说明出来的话大多情况下说明已经安装成功了。

docker -v
Docker version 20.10.14, build a224086

Dockerfile

在项目的根目录下创建Dockerfile文件,里面的内容如下

FROM node:14-alpine

# 初始化工作目录
RUN mkdir -p /app/server
WORKDIR /app/server

# 复制 package.json
COPY package*.json /app/server/

# 安装依赖
RUN npm install

# 复制文件
COPY . /app/server/

RUN npm run build

# 开启 Dev
CMD ["npm", "run", "start:prod"]

简单解释一下

  • FROM 为这次构建流程指定基础镜像,同时必须是第一条指令,因为后续所有的操作都必须基于基础镜像之上
  • WORKDIR 此命令是设置当前工作目录,如果目录不存在,则会直接创建,设置完毕之后,所有操作的路径都将处于当前指定路径之下,可以理解成CD命令
  • COPY 顾名思义,这是一个复制的命令,但是 COPY 只能复制宿主机本地的文件。
  • RUN 在构建镜像过程中执行的命令
  • CMD 在容器启动时候执行的命令
    当然还有许多的参数,我项目里面只用了这些。
    另外还有两个地方需要说明一下
  • EXPOSE:是暴露端口给宿主机(也就是我电脑)比如80或者3000什么的,我这里之所以没用是因为在docker-compose文件里面配置了
  • CMD ["npm", "run", "start:prod"] 运行这个命令是因为项目里面配置了环境变量,需要cross-env按环境变量来取值

docker-compose

Docker Compose 允许我们在一个文件里描述应用需要的服务(容器),比如我项目里面用了MySQL和Redis容器,还有上面创建的项目镜像,如何把这些容器打包一起运行呢,这就需要docker-compose了

这里必须指定版本
version: '3.0'

services:
  # docker容器启动的redis
    redis: # 服务名称
    container_name: redis # 容器名称
    image: redis:6.2 # 使用官方镜像
    # 配置redis.conf方式启动
    command: redis-server /usr/local/etc/redis/redis.conf --requirepass 123456 --appendonly yes # 设置redis登录密码 123456、--appendonly yes:这个命令是用于开启redis数据持久化
    # 无需配置文件方式启动
    # command: redis-server  --appendonly yes # 设置redis登录密码 123456
    ports:
      - 6389:6379 # 本机端口:容器端口
    restart: on-failure # 自动重启
    volumes:
      - /Users/jackwang/Documents/nodejserve/deploy/redis/redis.conf:/usr/local/etc/redis/redis.conf # 把redis的配置文件挂载到宿主机
      - /Users/jackwang/Documents/nodejserve/deploy/redis/db:/usr/local/var/db/redis/ # 把redis的配置文件挂载到宿主机
    environment:
      - TZ=Asia/Shanghai # 解决容器 时区的问题
    networks:
      - my-server

  mysql:
    container_name: mysql
    image: mysql:8.0.20 # 使用官方镜像
    ports:
      - 3307:3306 # 本机端口:容器端口
    restart: on-failure
    environment:
      - MYSQL_ROOT_PASSWORD=123123qq # root用户密码
    networks:
      - my-server

  my-mall: # nest服务
    container_name: my-mall
    build: # 根据Dockerfile构建镜像
      context: .
      dockerfile: Dockerfile
    ports:
      - 3000:3000
    restart: on-failure # 设置自动重启,这一步必须设置,主要是存在mysql还没有启动完成就启动了node服务
    networks:
      - my-server
    depends_on: # node服务依赖于mysql和redis
      - redis
      - mysql

# 声明一下网桥  my-server。
# 重要:将所有服务都挂载在同一网桥即可通过容器名来互相通信了
# 如egg连接mysql和redis,可以通过容器名来互相通信
networks:
  my-server:

解释一下上面的东西,大部分我觉得应该都是理解

  • ports: 这里,本机端口:容器端口 比如MySQL 3307:3306,为啥,因为容器就相当于小型系统了,这个系统咋和我电脑通信呢,比如我现在要连接容器的MySQL数据库,我链接localhost@3306就是链接我电脑的MySQL了,跟容器没关系了,所以需要端口映射一下,当我访问localhost@3307连接数据库的时候,就知道应该是链接容器的MySQL数据库了,用navicate链接3307端口就连上了(这时候MySQL得运行着呢才行)
    在这里插入图片描述
  • networks 这个就是容器之间通信需要的,我理解就是局域网,比如一个公司,多台电脑就是多个容器,那容器之间怎么交流通信呢,都放在一个网段里面,这个参数就是这样,my-server是名字

比如我在Redis容器里面ping一下MySQL,mysql就是容器名字

ping mysql
PING mysql (172.19.0.3) 56(84) bytes of data.
64 bytes from mysql.projec-nest_my-server (172.19.0.3): icmp_seq=1 ttl=64 time=0.126 ms
64 bytes from mysql.projec-nest_my-server (172.19.0.3): icmp_seq=2 ttl=64 time=0.085 ms
64 bytes from mysql.projec-nest_my-server (172.19.0.3): icmp_seq=3 ttl=64 time=0.152 ms
64 bytes from mysql.projec-nest_my-server (172.19.0.3): icmp_seq=4 ttl=64 time=0.116 ms
64 bytes from mysql.projec-nest_my-server (172.19.0.3): icmp_seq=5 ttl=64 time=0.064 ms

.dockerignore

这个文件其实没啥说的,就是忽略那些文件到容器里面,比如上面copy命令,如果不弄这个文件,就把所有的文件都copy到工作目录

.git
node_modules
npm-debug
build

最后在项目终端运行docker-compose up一下正常的话,项目就应该运行

这是我电脑客户端运行成功样子
在这里插入图片描述

QA 下面是我开发中的遇到的问题和经验,本来想写在文章里面的,但是感觉有点乱,所以写在最后把,因为有的人可能不会遇到,所以就单独总结一下吧
  • docker使用的时候,常用的几个命令只是我用的多,我测试的时候,经常用下面几个,删了装,装了删的
    • docker image ls 查看所有镜像
    • docker container ls 查看所有容器
    • docker start/stop mysql 启动或停止服务
    • docker container stop 容器ID 停止容器
    • docker container rm 容器ID 删除容器
    • docker image rm 镜像ID 如果要删除镜像,必须先停止容器,删除容器
    • docker-compose up 服务(比如MySQL) 这条命令就是执行docker-compose文件,如果直接docker-compose up就是构建所有的镜像和运行容器
  • Redis一直报ERROR [ExceptionHandler] connect ECONNREFUSED 127.0.0.1:6379错误
  • 通过docker-compose文件其实也能看出来,我配置的本机端口是6389,host是redis,但是报错也是提示上面,当时没看出,后来经人提醒才反应过来,我配置的参数根据没起作用,他还是默认值
    如果按照官网文档来写,也是不行,他会提示类型错误

    下面是官方文档的写法,这种写法连本地运行都报错。后来我根据RedisClientOptions这个参数修改了代码

    import * as redisStore from 'cache-manager-redis-store';
    import { CacheModule, Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    
    @Module({
      imports: [
        CacheModule.register({
          store: redisStore,
          host: 'localhost',
          port: 6379,
        }),
      ],
      controllers: [AppController],
    })
    export class ApplicationModule {}
    
    

    下面是我的代码,文件位置在src/modules/redis-cache/redis-cache.module.ts ,configService就是配置的变量

        CacheModule.registerAsync({
          isGlobal: true,
          imports: [ConfigModule],
          inject: [ConfigService],
          useFactory: async (configService: ConfigService) => {
            console.log(
              'configService.get',
              `redis://${configService.get('redis.host')}:${configService.get(
                'redis.port',
              )}/${configService.get('redis.db')}`,
            );
            return {
              store: redisStore,
              ...configService.get('redis'),
            } as RedisClientOptions;
          },
        }),
    

    文件位置src/config/production.yml

    # redis 配置
    redis:
      host: redis
      port: 6379
      db: 0
      password: '123456'
      token_expire: 1800000
    

    通过上面的配置,就可以链接redis了,但这里有我遇到的一个问题,就是Redis里面我开始配置的port是6389,因为我觉得端口映射的就是6389啊,但其实到了容器中,真正链接已经变了容器之间的IP和端口了,实际上已经不通过宿主机了,所以端口还是6379,如果是用宿主机客户端链接,那就得用6389

  • Cannot find module './dto/registerDto' or its corresponding type declarations,再构建的过程中,我遇到了这样的错误,但是在本地运行的时候是没问题的,其实报错的原因很简单,就是引用目录./dto/RegisterDto'应该大写,当让我纠结的是为什么本地运行不报错呢
    在这里插入图片描述
    在这里插入图片描述
  • 相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论