Docker 是个轻量级的虚拟化解决方案,可以将你的应用和所需的运行环境打包起来,部署至其他环境,保证开发或生产环境的统一。
对于开发人员来说,Docker 可以减少初次搭建开发环境的麻烦;
对于运维人员来说,Docker 使自动化、规模化部署变得更加简单。
Docker 入门
本文不涉及太多的入门知识,假若你未曾听过 Docker,请先浏览官方资源:
1. 观看 Docker 介绍视频
2. 动手完成 10 分钟 在线交互教程
下载并更新 Ubuntu 镜像
本文中 NodeJS 应用是部署在 Ubuntu 下的,所以要先通过以下命令,下载 Ubuntu 镜像,并更新保存。
# 从 Docker Hub 下载官方维护的 Ubuntu
docker pull ubuntu:14.10
# 下载完毕后,先更新软件源
docker run ubuntu:14.10 apt-get update
# 上面命令会生成一个container,先获得其 id
# -l:显示最新的 container
doocker ps -l
# 提交改动,并覆盖原有的 Ubuntu 镜像
docker commit <container_id> ubuntu:14.10
从创建简单的 NodeJS 应用镜像入手
我们先来创建一个基于 ExpressJS 的简单 NodeJS 应用,以下是文件目录结构和步骤。
目录结构
├── docker-node-hello/ │ ├── index.js │ ├── package.json │ ├── Dockerfile
文件创建步骤
创建文件夹
mkdir ~/docker-node-hello && cd $_
创建 index.js
var express = require('express') var app = express() app.get('/', function (req, res) { res.send('Hello World!') }) var server = app.listen(3001, function () { var host = server.address().address var port = server.address().port console.log('Example app listening at http://%s:%s', host, port) })
创建 package.json
{ "name": "docker-node-hello", "private": true, "version": "0.0.1", "description": "Node.js Hello world app on Ubuntu using docker", "dependencies": { "express": "4.x.x" } }
创建 Dockerfile 配置文件
# 设置基础镜像 FROM ubuntu:14.10 # 如果上个步骤已经更新软件源,这步可以忽略 RUN apt-get update # 安装 NodeJS 和 npm RUN apt-get install -y nodejs npm # 将目录中的文件添加至镜像的 /srv/hello 目录中 ADD . /srv/hello # 设置工作目录 WORKDIR /srv/hello # 安装 Node 依赖库 RUN npm install # 暴露 3001 端口,便于访问 EXPOSE 3001 # 设置启动时默认运行命令 CMD ["nodejs”, “/srv/hello/index"]
构建镜像
# 通过该命令,按照 Dockerfile 所配置的信息构建出镜像
# -t 镜像的名称
# --rm 构建成功后,删除临时镜像(每执行一行 Dockerfile 中的命令,就会创建一个临时镜像)
docker build --rm -t node-hello .
# 检查镜像是否创建成功
docker images
运行镜像
# 对于 Mac 或 Windows 来说,要先查看 boot2docker 的 IP
boot2docker ip
# 运行刚刚创建的镜像
# -p 设置端口,格式为「主机端口:容器端口」
docker run -p 3001:3001 node-hello
访问网页
若终端打印出 Example app listening at http://...
,则部署成功。
可以用浏览器访问 http://<boot2docker ip>:3001
,或运行 curl -s "$(boot2docker ip):3001"
。
创建完整的 NodeJS 应用镜像(含 Mongodb / Git)
通过上面的例子,我们知道了整个 Docker 镜像的基本创建过程。
但实际项目中,项目代码往往是通过 Git 来管理的,而且还会连接到一些数据库,如 Mongodb。
本例子中,我们将尝试部署一个 MEAN 架构的 NodeJS 应用。
什么是 MEAN 架构?
MEAN 表示 Mongodb / ExpressJS / AngularJS / NodeJS,是目前流行的网站应用开发组合,涵盖前端至后台。由于这些框架用的语言都是 Javascript,所以又戏称 Javascript Fullstack。
目录结构
├── docker-node-full/ │ ├── start.sh │ ├── Dockerfile
文件创建步骤
创建文件夹
mkdir ~/docker-node-full && cd $_
创建 Dockerfile 配置文件
# 设置基础镜像 FROM ubuntu:14.10 # 安装 NodeJS 和 npm RUN apt-get install -y nodejs npm # 由于 apt-get 下载的 Node 实际上是 nodejs,所以要创建一个 node 的快捷方式 RUN ln -s /usr/bin/nodejs /usr/bin/node # 安装 Git RUN apt-get install -y git # 安装 Mongodb(来自官方教程) RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/mongodb.list RUN apt-get update RUN apt-get install -y mongodb-org # 设置工作目录 WORKDIR /srv/full # 清空已存在的文件(如果有) RUN rm -rf /srv/full # 通过 Git 下载准备好的 MEAN 架构的网站代码 RUN git clone https://github.com/chuyik/fullstack-demo-dist.git . # 安装 NodeJS 依赖库 RUN npm install --production # 创建 mongodb 数据文件夹 RUN mkdir -p /data/db # 暴露端口(分别是 NodeJS 应用和 Mongodb) EXPOSE 5566 27017 # 设置 NodeJS 应用环境变量 ENV NODE_ENV=production PORT=5566 # 添加启动脚本 ADD start.sh /tmp/ RUN chmod +x /tmp/start.sh # 设置启动时默认运行命令 CMD ["bash", "/tmp/start.sh"]
创建 start.sh 启动脚本
# 后台启动 Mongodb mongod --fork --logpath=/var/log/mongo.log --logappend # 运行 NodeJS 应用 npm start
构建镜像
# 通过该命令,按照 Dockerfile 所配置的信息构建出镜像
docker build --rm -t node-full .
# 检查镜像是否创建成功
docker images
运行镜像
# 对于 Mac 或 Windows 来说,要先查看 boot2docker 的 IP
boot2docker ip
# 运行刚刚创建的镜像
# -p 设置端口,格式为「主机端口:容器端口」
docker run -p 5566:5566 node-full
访问网页
若终端打印出以下命令,则部署成功。
about to fork child process, waiting until server is ready for connections.
forked process: 9
child process started successfully, parent exiting
[email protected] start /srv
node server/app.js
Express server listening on 5566, in production mode
可以用浏览器访问 http://<boot2docker ip>:5566
,或运行 curl -s "$(boot2docker ip):5566"
。
保存 Mongodb 数据文件
由于 Mongodb 服务运行在 Docker 容器 (container) 中,所以数据也在里面,但这并不利于数据管理和保存。因此,可以通过一些方法,将 Mongodb 数据文件保存在容器的外头。
磁盘映射
这个是最简单的方式,在 docker run
命令当中,就有磁盘映射的参数 -v
。
# -v 磁盘映射,格式为「主机目录:容器目录」
docker run -p 5566:5566 -v /var/mongodata:/data/db node-full
但这个命令在 Mac 和 Windows 中执行失败,因为 boot2docker 的虚拟机不支持。
所以,可以将数据保存在 boot2docker 内,并设置共享文件夹便于 Mac 或 Windows 访问。
Mac 的磁盘映射方案
# 在 boot2docker 中,运行该命令
boot2docker ssh
# 进入 bash 后,创建存放 Mongodb 数据库文件的目录
sudo mkdir -p /mnt/sda1/dev
# Mac 用户通过 brew 安装 sshfs
brew install sshfs
# 通过 sshfs,把 /mnt/sda1/dev 挂载到 Mac 中
# 注:boot2docker 的默认用户为 docker/tcuser
echo tcuser | sshfs [email protected]:/mnt/sda1/dev /var/mongodata -p 2022 -o password_stdin
# 运行镜像测试(映射 boot2docker 的目录)
docker run -p 5566:5566 -v /mnt/sda1/dev:/data/db node-full
优化建议
将 NodeJS 和 Mongodb 分开
上个例子中,我们把 NodeJS 和 Mongodb 放进了同一个镜像中,但本着一次只做一件事的原则,将其放进不同的镜像可能会更好。但分开后,NodeJS 和 Mongodb 运行的容器该如何关联呢?这里不展开介绍,有兴趣可以查阅该博文。
使用进程管理工具
上个例子中,我们通过 bash 脚本来运行多个命令,但也可以用一些进程管理工具来配置进程,如 Supervisor。整合的方式可以查阅官方教程 Using Supervisor with Docker。
一些有用的 Docker 命令
查看后台运行容器的日志
docker ps -l
docker logs <container_id>
开始和关闭容器的运行
docker stop <container_id>
docker start -i <container_id>
docker restart -i <container_id>
运行镜像的bash
# -i 将命令的输出信息重定向到 stdout
# -t 开启 tty 终端,这样就能够输入指令
docker run -it ubuntu:14.10 bash
导入和导出镜像
docker save ubuntu:14.10 > ubuntu_14.10.tar
sudo docker ubuntu:14.10 < ubuntu_14.10.tar
停止和删除所有容器
来自 Fabio Rehm
docker stop $(docker ps -a -q)
docker rm $(docker ps -a -q)
参考文章 Credits
Dockerizing a Node.js Web App
利用Docker构建开发环境 | UC技术博客
Dockerfile - Docker Documentation
Command line - Docker Documentation
Using Supervisor - Docker Documentation
How to create a Docker + Node.js + MongoDB + Varnish environment
How to share folders with docker containers on OSX