手记

基于Docker的nodejs应用开发部署

前言

Q:用一句话来评论使用docker的感受?
A: “有毒会上瘾”。

用了docker之后不管做什么都会想部署在docker上之前由于穷只会买一台ECS这就导致了把应用、数据库、缓存等等这些鸡蛋都装在一个篮子里有了docker之后就可以把这些放在单独的container(容器中了

准备工作

这里并不会详细介绍docker的安装只会大致提一下个人的经验。
首先并不建议直接在本地安装docker因为docker在使用过程中产生的中间文件真的很多很大很杂对初学者一直不停做实验来说尤为严重什么image都想拉下来跑一跑会占用很大的磁盘空间
因此还是建议使用虚拟机来作为docker运行环境
建议使用vagrant + virtualbox的组合当然了其实vagrant就是基于virtualbox的windows系统或者mac通杀

其次安装docker,由于穷嗯。。还是因为穷所以选择安装社区版也就是Docker CE,这里以centos为例

安装完成执行sudo systemctl start docker启动即可

想要查看docker是否已经启动可以通过sudo docker version

注意如果是使用vagrant ssh来进入虚机使用用户是vagrant,这样在使用docker命令时都需要在前面加上sudo会比较繁琐可以执行以下命令将vagrant加入docker用户组中这样以后就不用每次执行docker命令都在前面加sudo

sudo groupadd docker
sudo gpasswd -a vagrant docker

创建node应用

首先使用npm init命令初始化package.json,然后使用npm install koa redis命令安装koaredis客户端,创建app.js文件内容如下

const Koa = require('koa')
const redis = require('redis')
const client = redis.createClient({
    host:  process.env.redis_server || '127.0.0.1', // 如果环境变量传值则读取环境变量的否则连接本机
    port: 6379,
    db: 0
})

client.on('error', err => {
    console.log(err)
})

const app = new Koa()

app.use(async (ctx, next) => {
    client.incr('cnt')
    
    try {
        const cnt = await new Promise((resolve, reject) => {
            client.get('cnt', (err, res) => {
                if (err) {
                    console.log(err)
                    reject(err)
                } else {
                    resolve(res)
                }
            })
        })
        ctx.body = `hello docker ${cnt}`
    } catch (err) {
        console.log(err)
        ctx.body = `ooooops~~${err}`
    }
    
})


console.log('server is runing at port:8088')
app.listen('8088')

在初始化redis客户端时host的地址首先会尝试从环境变量获取否则就读取本地这是为了方便本地开发调试。
在实际使用中其实可能并不知道这个node application实际连接的redis地址是什么因此就需要在运行容器时传入对应的参数了。

然后在package.json中的scripts添加一行命令"start": "node app.js",这时执行npm start就可以启动这个简单的node应用了在访问8088端口时会自动为该页面访问次数增加1同时展示出目前的访问次数

编写Dockerfile

在项目根目录创建Dockerfile文件内容如下

FROM node
LABEL maintainer="70458055@qq.com"
LABEL description="nodejs image demo with koa"
LABEL version="1.0"
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . ./
EXPOSE 8088
CMD [ "npm", "start"]

FROM

说明这个image希望基于Docker Hub上的node这个镜像也可以指定希望的版本

LABEL

描述性质的字段(和metadata类似)这里指定了以下信息

  • maintainer 创作者信息
  • description: 描述信息
  • version: 版本信息

WORKDIR

可以类似的认为是cd命令指进入后面指定的文件夹当指定文件夹不存在时会自动创建该文件夹
建议使用绝对路径
这里指进入/usr/src/app这个目录作为工作目录

COPY

COPY还有个类似用法的命令ADD都是把本地文件复制到image中大部分时候他们的作用是相同的COPY要由于ADD但是ADD除了有复制功能外还有解压缩功能
这里是指把当前文件夹的package.jsonpackage-lock.json拷贝到image的当前工作目录中去
这里的第二个COPY是把项目文件复制到image对应的工作目录下。注意会添加一个.dockerignore文件同.gitignore类似里面记录了需要忽略的文件

node_modules
npm-debug.log

RUN

image中要执行的命令需要注意的是每执行一行都会为image增加一层(Layer)因此建议如果有多条命令需要执行最好把命令都使用&连接起来
这里没什么说的就是在image中执行npm install安装依赖

EXPOSE

向外暴露哪个端口
因为这个项目是监听8088端口的因此这里向外暴露的端口也是8088这样就可以在容器外通过访问8088端口来访问这个应用了

CMD

设置容器启动后自动执行的命令和参数
ShellExec两种格式
这里使用的是Exec格式即数组中第一个是命令后面的都是参数这里用来启动应用

创建镜像(build image)

在和Dockerfile同级的文件夹执行以下命令用来使用Dockerfile创建镜像文件
docker build -t 你的docker hub用户名/image名 .
上面命令最后的.切记莫忘

会发现docker会一层一层的创建镜像在命令执行完成后使用docker image ls会发现该镜像已经在本地镜像列表中了

运行容器

首先执行
docker run -d --name redis-server redis
创建一个名为redis-server的容器
注意这里并不需要使用-p 6379:6379来对外暴露6379端口

其次执行以下命令运行node应用的容器
run docker run -d --name node-demo --link redis-server -p 8088:8088 -e redis_server=redis-server nickylau82/node-demo(之前创建的镜像名称)

依次解释下各个参数的含义
-d: 以后台的形式运行
--name: 给容器起个自定义名称需本地唯一)
--link: 关联到别的容器上这里以link的形式关联到redis容器这样两个容器之间的网络就通了
-p: 将容器的8088端口暴露给本地的8088端口
-e: 设置环境变量redis_server,其值为link的容器网络

注意设置好-e之后可以在容器中直接使用env命令查看所有环境变量
node中使用process.env.变量名的方式获取环境变量

至此基于单机多容器的node应用就部署完成了

可以在虚机中使用curl 127.0.0.1:8080来查看返回结果

或者可以使用docker network相关命令获取虚机的ip相关信息在本机访问虚机的ip+端口访问这里就不赘述。

发布

最后除了使用docker push命令发布外其实最推荐的是关联github,上传Dockerfile自动build发布因为这样在Docker Hub上可以看到Dockerfile文件下载的用户会知道这个image里面没有非法内容

方法就是在Docker Hub选择new repository,然后关联上传了Dockerfilegithub地址即可

最后附上本文对应的image

1人推荐
随时随地看视频
慕课网APP