前言
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
命令安装koa
和redis
客户端,创建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.json
和package-lock.json
拷贝到image
的当前工作目录中去
这里的第二个COPY
是把项目文件复制到image
对应的工作目录下。注意会添加一个.dockerignore
文件同.gitignore
类似里面记录了需要忽略的文件
node_modules
npm-debug.log
RUN
在image
中要执行的命令需要注意的是每执行一行都会为image
增加一层(Layer
)因此建议如果有多条命令需要执行最好把命令都使用&
连接起来
这里没什么说的就是在image
中执行npm install
安装依赖
EXPOSE
向外暴露哪个端口
因为这个项目是监听8088
端口的因此这里向外暴露的端口也是8088
这样就可以在容器外通过访问8088
端口来访问这个应用了
CMD
设置容器启动后自动执行的命令和参数
有Shell
和Exec
两种格式
这里使用的是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
,然后关联上传了Dockerfile
的github
地址即可
最后附上本文对应的image