Node.js开发人员宝典:上手Docker从这里开始
难度等级:初学者
要求:Mac OS X(本教程假设你在使用Mac,但你也能找到Windows或Ubuntu的安装指示,直接跳过“安装”章节)。
Docker刚过了第二个生日,但它仍是一项“新兴”的强大技术。与本人交流过的许多开发人员对它已有所耳闻,但并没有实际用过它。Docker让你可以做一些确实很酷的事情,比如可以迅速测试开发中的应用程序,所在环境与质量保证/测试/生产环境一模一样;或者与其他开发人员共享该应用程序,以便尽快轻松熟悉情况。说到Docker,一个常打的比方就是它好比是现实生活中的集装箱或乐高积木:它提供了一种基本单元,同时提供了一种不管是什么硬件,让应用程序都易于移植、易于迁移的方法。
我在本教程中将大致介绍一下Docker以及为何想要使用它、如何安装它,然后我们要着手安装一个Node容器,并且在该窗口里面建立一个Express入门级应用程序。这篇教程内容蛮长!官方的Docker入门指南可以让你更快地上手,我在这里的目的是,解释这个过程中每一步出现的情况。
我们将介绍:
- 引言(Docker的性质及为何使用它)
- 安装•Docker Hub和Docker文件
- Docker Pull:获取Ubuntu映像
- Docker Run:运行我们的Ubuntu映像,及访问容器
- Docker Commit:安装node、npm和express,并提交变更内容
- Docker Push:推回容器,那样别人就能使用它
注意:我将以下列格式指代在你自己的终端中执行的命令:
$ command
并以下列格式指代容器里面的命令:
$ root: command
引言
现在你可能已经听说过Docker。每天总会有专业报章提到它,或者你在推特/IRC上看到别人谈论它。它的人气在过去几年急剧飙升;大多数云服务提供商已经支持它。如果你对它感到很好奇,但是还没有用过它,那么这篇教程蛮适合你。
闲话少说,那么Docker到底是什么东东?这么说吧,Docker可能指几个方面:
Docker客户程序:这是在我们的机器上运行的组件。它是docker二进制代码;只要我们打开终端,输入$ docker pull或$ docker run,就要与之交互。它连接至处理所有繁重任务的docker守护程序,而守护程序可能在同一个主机上(以Linux为例),也可能在远地(我们与VirtualBox虚拟机进行交互)。
Docker守护程序:这是处理诸多繁重任务的组件,比如构建、运行和分发Docker容器。
Docker映像:docker映像好比是我们应用程序的蓝图。继续说说上面的集装箱/乐高积木那个比方,docker映像是用于实际构建真正实例的蓝图。映像可能是Ubuntu之类的操作系统,也有可能是你的Web应用程序及其所有的必要程序包都已安装的Ubuntu。
Docker容器:容器是用docker映像构建而成的,它们是集装箱/乐高积木的真正实例。它们可以启动、运行、停止、删除和移动。
Docker Hub(注册中心):Docker注册中心是一台托管的注册中心服务器,可以存放Docker映像。Docker公司提供了一个公共Docker注册中心,名为Docker Hub,我们会在本教程中用到它,但Docker公司提供了开放源代码的整个系统,那样人们可以在自己的服务器上运行,以私密方式存储映像。
我们已经明确了Docker的不同部分,下面介绍你可能想要使用它的几个理由:
- 简化开发环境的配置;
- 在类似质量保证/测试/生产环境的环境中,迅速测试应用程序(相比虚拟机,开销较小);
- 与其他开发人员共享你的应用程序和环境,这样便于别人尽快熟悉情况。
- 能够构建不同的容器(这在调试中极其有用)
安装
运行容器、因而运行Docker需要Linux机器。由于我们使用的是Mac,这就意味着我们需要虚拟机。为了让安装过程来得更容易,我们可以使用Boot2Docker,它可以安装Boot2Docker管理工具VirtualBox,并在Docker已安装的情况下在里面安装虚拟机。
boot2docker
直接浏览到该链接,下载最新版本的Boot2Docker,并安装它(截至本文截稿时最新版本是Boot2Docker-1.5.0.pkg)。
https://github.com/boot2docker/osx-installer/releases/latest
安装完毕后,进入到你的Applications文件夹,打开Boot2Docker。那会在VirtualBox里面打开一个新的终端,运行几个命令,这些命令的作用基本上就是启动已经安装了Docker的虚拟机,然后设置几个环境变量,那样我们就能从终端访问该虚拟机。如果你不想总是打开Boot2Docker以便与Docker进行交互,只要运行下面几个命令:
# 如果你还没有新的虚拟机,创建一个 $ boot2docker init # 启动虚拟机 $ boot2docker start # 设置必需的环境变量 $ $(boot2docker shellinit)
现在输入:
$ docker run hello-world
这会让Docker从Docker Hub下载hello-world映像,并启动基于它的容器。你的终端应该会给出类似如下的输出结果:
Hello from Docker. This message shows that your installation appears to be working correctly.
好了!Docker已安装完毕。
要是你遇到什么问题,欢迎留言交流,也可以在此(https://docs.docker.com/installation/mac/)参阅Docker的官方安装指示。
Docker文件和Docker Hub
在探讨下一步操作之前,我认为明白我们在执行$ docker run hello-world后发生的事情很重要,那样你不仅仅是复制粘贴接下来的指示。docker run是我们用来启动基于映像的容器的基本命令,同时将命令传送给它。在这里,我们说“Docker,启动基于映像hello-world的容器,不需要额外的命令。”然后,它从Docker Hub下载映像,并且启动VirtualBox虚拟机里面基于该映像的容器。但是hello-world映像来自哪里呢?这时候,Docker Hub有了用武之地。就像我们之前在引言部分提到的那样,Docker Hub是公共注册中心,含有与Docker一起使用的容器映像,以及由Docker、其他公司和个人构建的容器映像。你在这里可以找到我们刚执行的hello-world映像:
Docker Hub Hello-World映像(https://registry.hub.docker.com/u/library/hello-world/)
每个映像都是使用Docker文件构建的。在hello-world映像的描述部分,你能找到Docker文件的链接(https://github.com/docker-library/hello-world/blob/master/Dockerfile),该文件只有短短3行:
FROM scratch COPY hello / CMD [“/hello”]
Docker文件就是文本文件,含有如何构建容器映像方面的Docker指示。映像就好比是机器快照,而容器好比是机器的实际的运行实例。Docker文件总是会有这种格式:
INSTRUCTION arguments
所以在我们的hello-world例子中,我们可以看一下含有Docker文件的GitHub软件库的根。该映像是用另一个名为“scratch”的映像构建的(所有的Docker文件都是以FROM指示开头),然后将hello文件复制到系统的根,最后运行hello。你还能在这里找到hello文件的内容(https://github.com/docker-library/hello-world/blob/master/hello.asm),包括我们在终端中刚看到的输出结果。
Docker Pull:下载Ubuntu映像
我们已知道了安装的Docker已正确设置好,不妨开始使用它!下一步是获得Ubuntu映像。想找到映像,我们可以进入到Docker Hub网站(https://hub.docker.com),或者只要在终端中运行:
$ docker search ubuntu
这会给出名称中含有Ubuntu的所有映像的列表。我的终端中显示如下:
输出结果按每个映像软件库中的星号数量来排序。你能看到上面有Official(官方)栏和Automated(自动)栏。
•官方映像是由docker-library项目维护的映像,并得到Docker团队的接受。这意味着,它们遵守这里的几个准则(https://docs.docker.com/docker-hub/official_repos/),其中一些位于git软件库;对该软件库至少要有只读权,以便用户能查看其内容。你可以依赖那些映像,顺利使用Docker。另外,与你需要使用USERNAME/IMAGE_NAME来获取的其他映像相反,这些映像完全只要由IMAGE_NAME(比如Ubuntu)用命令来指代。所有Docker文件都呈现在这种组织体系中(https://github.com/docker-library)。
•自动栏是指自动构建(Automated Build)映像。这完全意味着,映像是通过GitHub或BitBucker软件库里面的Docker文件构建的;对它进行更改后,它会自动更新。
不妨下载官方的Ubuntu映像:
$ docker pull ubuntu
$ docker pull IMAGE_NAME命令是明确下载映像的方式,但如果你使用$ docker run IMAGE_NAME命令,也能下载,就是Docker找不到你所指的那个映像。
Docker Run:运行我们的Ubuntu映像,访问容器
我们已经有了Ubuntu映像(我们的蓝图)。现在不妨启动基于该映像的新容器,并将命令传送给它:
$ docker run ubuntu /bin/echo ‘Hello world’
这在你的终端中应该会给出消息“Hello World”。我们刚启动了一个运行Ubuntu完全隔离的实例的容器,并执行了命令,这很好,但是其实没多大用处。
于是,现在不妨运行新的容器以及Ubuntu,并连接到它:
$ docker run -i -t ubuntu
请注意:run命令很庞大(查看$ docker help run),我们会在下一篇博文中更深入全面地介绍。
-t旗标在我们的新容器里面分配了伪终端(pseudo-tty)或终端,-i旗标让我们可以获取容器的标准流(STDIN),从而建立交互性连接。如果一切顺利,你应该可以连接至容器里面的终端,并显示这样的结果:
$ root@c9989236296d:/#
运行ls –ls,就会发现运行中的命令就在Ubuntu系统的根里面。
终端ubuntu
我认为,有必要暂停一分钟,考虑一下我们刚才执行的操作。这正是容器的出色方面之一。我们刚下载并启动了运行Ubuntu的容器。这一切在短短5分钟内发生的吗(取决于你的互联网连接速度)?拿这与下载虚拟机Ubuntu映像、启动新虚拟机作一下比较。后者可能是不是要花15分钟至30分钟?然后,构建、停止及重启新虚拟机,这个过程要花多久?如果你把这些时间都算上去,就会发现使用容器可以节省大量时间!
Docker Commit:安装node、npm和express,提交变更内容
好了,鉴于我们已在运行中的Ubuntu容器里面,不妨安装运行节点应用程序所需要的工具(切记:你只需要执行$ root:后面的部分):
$ root: apt-get update $ root: apt-get install nodejs $ root: apt-get install nodejs-legacy $ root: apt-get install npm
请注意:我们需要安装nodejs-legacy,才能运行express-generator模块。
运行node -v应该会得出输出结果:
$ root: node -v v0.10.25
节点安装完毕后,我们可以直接从npm安装express generator模块:
$ root: npm install -g express-generator
现在我们已有了容器,而且我们所需的一切都安装在里面。不妨接下来退出容器:
$ root: exit
我们退出容器后,Docker会停止运行它。我们可以使用$ docker ps命令列出容器,不妨这么做:
$ docker ps -a
终端容器
$ docker ps命令默认情况下只显示运行中的容器,于是我们传递-a旗标,那样我们就能查看刚退出的Ubuntu容器。
现在,我们可以使用该容器来构建别人可以使用的新映像。为此,我们可以使用commit命令:
$ docker commit -a "Your Name <[email protected]>" -m "node and express" CONTAINER_ID node-express:0.1
commit命令只有几个参数。-a旗标设定作者,你可以使用-m旗标设定消息,最后我们可以引用容器ID和所创建容器的名称,这里是node-express。我们还可以在映像名称后面添加:0.1,为映像设定标签。如果我们运行:
$ docker images
我们应该会看到:
终端容器
太棒了,你刚构建了第一个Docker映像!
现在,不妨为刚创建的映像添加另一个标签。运行:
$ docker tag node-express:0.1 node-express:latest
给映像标记特定的版本是个好做法,那样人家知道自己在到底运行哪个映像。添加latest标签大有帮助,那样别人在下载映像时就能用名称来指代你的映像(我们的例子中是node-express),Docker会自动下载latest标签版本。如果你再次运行$ docker images,就能看到我们的映像有两行,但它们都有同一个ID,这意味着它们并不占用硬盘中的任何额外空间。
现在我们就可以启动想与映像一同使用的多个容器!不妨删除旧的容器:
$ docker ps -a $ docker rm YOUR_CONTAINER_ID
不妨运行基于新映像的容器,使用-i -t旗标连接到它,将主机(VirtualBox)的端口8080暴露为容器(虚拟机)的端口3000:
$ docker run -i -t -p 8080:3000 node-express
不妨使用我们已安装的express-generator,构建一个新的Node.js应用程序:
$ root: express mynodeapp
按终端中的指示操作后,进入到应用程序文件夹,安装依赖项,启动该应用程序:
$ root: cd mynodeapp $ root: npm install $ root: npm start
现在,我们已有了一个在容器里面运行的Node.js应用程序,暴露端口3000。想查看这个应用程序,我们需要找到Boot2Docker虚拟机IP地址,为此打开另一个终端,运行:
$ boot2docker ip 192.168.59.103
另外切记:我们实际上暴露了容器的端口8080,以便访问端口3000。于是,进入到浏览器,打开:
192.168.59.103:8080 website express
现在,你可能会想:光拥有一个运行中的应用程序就要费这老大的劲!我早已有了开发环境,可以在30秒内完成这一切!没错,确实如此,但是在本教程中,我们运行的一个超级简单的应用程序根本没有许多依赖项。你在运行依赖项要多得多的实际项目时,可能需要开发环境拥有不同的程序包、Python、Redis、MongoDB、Postgres、Node.jsK io.js等。牵涉太多的方面,结果会让在你的计算机中正常运行的应用程序在另一台机器((或在质量保证/测试/生产环境)中却无法正常运行,这就是Docker大受欢迎的主要原因。回到本教程的引言部分,通过提供一种独立于硬件而执行的基本单元,该基本单元还能轻松运行、移动和共享,Docker绝对改变了我们开发、测试及共享应用程序的方式。
Docker Push:推送容器映像,那样别人就能使用它
现在不妨共享我们这个“出色的”Ubuntu映像(已安装了node、npm和express-generator),那样别人也能使用它。退出运行中的Node应用程序和容器:
# Ctrl+C to stop our node app $ root: exit
直接进入到Docker Hub:http://hub.docker.com,设立一个免费帐户;
之后,回到终端,运行:
$ docker login
鉴于我们已通过命令行接口(CLI)登录上去,可以将映像推送到Docker Hub。不妨先对它进行更名,并为它添加用户名,所以就像添加标签:
$ docker tag node-express your_docker_hub_username/node-express $ docker rmi node-express $ docker push your_docker_hub_username/node-express
大功告成!现在,拥有Docker的人都可以执行: