用Docker简化Nodejs开发1——开发环境

作者: 全栈顾问 | 来源:发表于2019-11-15 11:47 被阅读0次

    开发Nodejs应用通常要使用多个中间件,开发人员要把代码跑起来就要在自己的机器上把中间件安装一遍,费时费力,如果同时开发多个项目就更麻烦了,经常要改来改去。本文以一个Nodejs+MongoDB项目为例,展示Docker的基本使用方法,同时提供了编写对Docker友好代码的方法。

    项目说明

    tms-api-gw是一个API网关项目,功能是将收到的http请求根据业务规则转发到对应的服务,每次收到的请求都要记录日志,并进行计数,结果保存到mongodb中。

    希望通过Docker解决如下几个问题:

    • 简化mongodb的部署,方便开发人员在本地运行应用。
    • 对nodejs应用进行打包,实现整体发布,方便运维人员部署。

    Docker

    首先请按照官网说明安装Docker。

    参考:https://docs.docker.com/get-started/#install-docker-desktop

    使用Docker首先需要了解imagecontainer的概念,简单说,image是运行环境的模版,container是根据模版创建的实例。imageDockerfile进行定义,可以认为Dockerfile是一个批处理命令,通过执行命令,在镜像上安装包、复制文件、设置参数。有了image就可以通过run命令生成容器,生成的时候可以指定运行时的参数。如果有多个相关联的容器,可以通过docker-compose进行整体管理。docker-compose根据编排文件docker-compose.yml描述被管理的容器,通过docker-compose up命令启动,docker-compose down命令关闭,这样就不用对着每个容器单独执行命令。

    参考:https://docs.docker.com/reference/

    通常我们需要的image都已经有基础版本,可以在hub.docker.com上查找。每个镜像通常都有一堆版本,最重要的区别在于image是在哪个linux的版本上建的,建议使用alpine,因为这个版本最小。

    下面我们结合项目需求具体跑一遍Docker。

    MongoDB

    查找基础镜像。

    docker search mongo

    将镜像拉到本地。

    docker pull mongo

    生成并运行容器。

    docker run --rm --name tms-api-gw-mongo -p 27017:27017 -d mongo:latest

    --rm 当容器退出时自动删除。

    --name tms-api-gw-mongo 指定容器的名字,后面操作容器时用的到。

    -p 27017:27017 将容器内部的27017端口映射到主机的27017 端口。

    -d 是指定在后台运行。

    进入容器查看数据。

    docker exec -it tms-api-gw-mongo /bin/bash

    在容器中用exit命令退出容器。

    这时在本地就有了个可用的MongoDB实例,数据保存在容器中,每次删除容器,数据就会清除,这样就总能用一个“干净”的MongoDB进行开发。

    如果需要持久保留MongoDB中的数据,可以让容器将数据写到本地目录中,执行run命令时指定参数。

    docker run --rm --name tms-api-gw-mongo -p 27017:27017 -v $PWD/storage/mongodb:/data/db -d mongo:latest

    -v $PWD/db:/data/db 将主机中当前目录下的db挂载到容器的/data/db,作为mongo数据存储目录。

    为了管理容器需要用到几条命令:

    docker ps -a #查看全部容器,不加-a参数只显示运行的。

    docker stop container_name # 停止指定的容器

    docker rm container_name # 删除指定的容器

    Docker命令参考:https://docs.docker.com/engine/reference/commandline/cli/

    Nodejs

    先看看Nodejs官网的这篇文章:https://nodejs.org/zh-cn/docs/guides/nodejs-docker-webapp/,下面是以该文章为基础进行调整。

    制作docker镜像。

    在项目根目录新建Dockerfile文件,文件内容如下,和Nodejs官网文章不一致的地方加了注释。

    FROM node:alpine
    
    # 安装cnpm
    RUN npm install cnpm -g 
    
    WORKDIR /usr/src/app
    
    COPY package*.json ./
    
    # 只安装dependencies的包,不安装devDependencies的包;额外安装包。
    RUN cnpm install --production \
      && cnpm install log4js
    
    COPY . .
    
    # 创建放配置文件的目录
    RUN mkdir config
    
    # 设置应用的环境变量
    ENV TMS_API_GW_ENV='docker'
    
    EXPOSE 3000
    
    CMD [ "node", "./app.js" ]
    

    COPY . .是把本地当前目录下的内容复制到镜像的工作目录下/usr/src/app,但是,node_modulesconfig这些内容不需要复制,因此要建立.dockerignore文件,指定不需要复制的内容。

    .*
    node_modules
    config
    example
    

    创建镜像,注意不要丢了最后面的点

    docker build -t tms-api-gw-node .

    用docker images可以查看已有镜像。

    运行容器

    docker run --rm --name tms-api-gw-node -p 5678:3000 -v $PWD/config:/usr/src/app/config -d tms-api-gw-node

    如果我们同时开发多个项目,经常会发生端口冲突的问题,通过-p参数就可以在启动容器时指定端口了。

    同一份代码需要在多个环境中运行,包括:开发,测试,生产等,我们通常是采用配置文件让代码和运行环境解耦。利用Docker,可以把代码和代码依赖的标准环境制作成镜像,在生成容器时再指定和环境相关的配置文件,这样Docker镜像的整体就变成了一个发布单元,可以极大简化运维工作。因此,在Dockerfile中我们创建了空的config目录,通过参数-v $PWD/config:/usr/src/app/config指定使用运行环境本地的配置文件

    前面介绍项目基本情况时提到需要在mongodb中存储api访问数据,当应用和mongodb都在容器中运行时就产生了一个问题:mongodb的地址是什么?

    在本地开发环境我们通常写个localhost,但是容器中的localhost是容器并不是宿主机,应用无法访问到mongodb。为了解决这个问题,在项目中引入了环境变量,看代码config/gateway.sample.js

    let host, port
    if (process.env.TMS_API_GW_ENV === 'docker') {
      host = 'docker.for.mac.host.internal'
      port = 3000
    } else {
      host = 'localhost'
      port = 5678
    }
    module.exports = {
      ...
    }
    

    前面Dockerfile中指定了环境变量ENV TMS_API_GW_ENV='docker',代码中可以根据这个环境变量进行相应的设置,在容器中docker.for.mac.host.internal代表了宿主机的地址,否则还是用localhost,指定端口要和DockerfileEXPOSE的端口一致,这样不论是否在容器中Nodejs应用都可以访问到MongoDB。

    如果容器是在后台运行,想查看Nodejs应用输出的日志,使用如下命令:

    docker logs tms-api-gw-node

    因为镜像是以alpine为基础制作,进入容器的命令需要调整,将bash改为sh

    docker exec -it tms-api-gw-node /bin/sh

    至此我们已经可以在容器中运行Nodejs应用。

    参考:https://github.com/nodejs/docker-node/blob/master/README.md#how-to-use-this-image

    参考:一篇关于Nodejs使用Docker的最佳实践,https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md

    docker-compose

    虽然已经可以用容器把Nodejs应用跑起来,但是还是不够方便,mongodb和nodejs容器要分别启停,命令还都挺长。能不能更简单呢?可以,用docker-compose。

    参考:https://docs.docker.com/compose/compose-file/

    Docker for Mac已经包含了Compose了,所以Mac用户不用单独安装Compose了。

    创建docker-compose.yml文件。

    version: '3.7'
    services:
      app:
        build: ./
        image: tms-api-gw-node:latest
        container_name: tms-api-gw-node
        ports:
          - '5678:3000'
        volumes:
          - ./config:/usr/src/app/config
        depends_on:
          - mongodb
    
      mongodb:
        image: mongo:latest
        container_name: tms-api-gw-mongo
        ports:
          - '27017:27017'
        logging:
          driver: none
    

    上面这个文件中指定的逻辑和前面通过run命令分别运行容器是完全等效的。

    启动容器

    docker-compose up -d

    关闭容器

    docker-compose down

    更新镜像

    docker-compose build

    反复更新镜像会导致产生无效的镜像,通过下面的命令删除这些无用镜像。

    docker images
    <none> <none> cb7a87c0359b 22 minutes ago 170MB

    docker rmi $(docker images | grep "^<none>" | awk "{print $3}")
    

    提示:实际在本机写代码时并不需要将Nodejs应用做成镜像再运行,因为这样每次修改代码都要重新build,既花费时间又产生许多无用镜像。这里演示Nodejs应用镜像主要是为了下一步进行发布做准备。

    总结

    虽然Docker整体比较复杂,但是作为开发人员只需要掌握基本概念和常用命令就可以把Docker跑起来,可以极大简化本地开发环境的管理工作,建议每个开发人员都尝试一下。

    下一篇研究如何利用Docker进行Nodejs应用的部署。

    本系列其他文章

    用Docker简化Nodejs开发2——开发环境到测试环境

    用Docker简化Nodejs开发3——用webhook实现自动更新

    相关文章

      网友评论

        本文标题:用Docker简化Nodejs开发1——开发环境

        本文链接:https://www.haomeiwen.com/subject/uodkbctx.html