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

开发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实现自动更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容