Docker简明教程

【编者的话】使用Docker来写代码更高效并能有效提升自己的技能。Docker能打包你的开发环境,消除包的依赖冲突,并通过集装箱式的应用来减少开发时间和学习时间。

Docker作为一把瑞士军刀在DevOps扮演的角色已经被很多文档提到过了。但实际上Docker管理应用容器比在云上部署服务器更加有用。Docker旨在在很多通用开发场景里显著提升开发效率。本教程从一个开发者的视角阐述Docker如何有用,我会介绍Docker,解释基本的概念和术语,并列举几个实际动手操作过的例子。

你能发现:

  • Docker能使Node.js Web应用开发更便捷。
  • 你能在Apache Tomcat服务器上用Docker快速创建并测试Java EE应用。
  • Docker能加快通过Bottle框架开发、测试、部署Python web应用的时间。
  • 你能使用Docker容器在几分钟内创建并测试一个三层架构的带有NoSQL数据库的Node.js应用。

你能在单台Linux系统上获取上述提到的所有好处,而不需预先做任何配置。在完成以下的例子后,你能很好的感受到通过Docker容器来增加日常开发的效率的乐趣。

Docker基本概念

Docker是一个容器管理器。容器打包一个应用以及它的依赖关系。你能从仓库的每个镜像里实例化出一个容器并在主机上各自隔离的环境里运行这些容器。因为容器运用的虚拟技术是轻量级的,所以你能同时运行多个容器。

你能使用Docker容器管理器做如下工作:

  • 从镜像运行容器
  • 监视和管理正在运行的容器的性能
  • 停止运行的容器
  • 在运行的容器里执行额外的任务
  • 启动和重启运行的容器
  • 执行其他管理任务

容器化的应用几乎能在任何地方运行:在PC桌面、在服务器、在公有云甚至是在移动设备。

Linux是目前最广泛使用和支持的Docker平台。Docker使用的容器化技术(轻量级虚拟化)在Linux平台相当成熟,使用了最近的Linux特性如控制组和命名空间。

如何运行Docker

可以通过Docker命令行运行Docker容器,运行时有很多选项和参数。如下是运行一个容器化应用的简单命令:

docker run -d mysql:5.5

Docker的公共仓库是Docker Hub。用户能注册并创建自己的镜像仓库,并将镜像给其他人用。例如,你能找到如下镜像:Tomcat server、Node.js以及最流行的开源数据库。Docker Hub运营的宗旨跟GitHub一样,在这里,APP镜像被全球的DevOps以及开发者共享或者协同创建。

当运行docker run -d mysql:5.5命令的时候,如果本地没有,MySQL(5.5版本)的镜像会自动从Docker Hub下载下来。

创建容器化镜像

你可以使用Docker命令行客户端来创建镜像。方式之一是通过使用Dockerfile—一个含有关于如何安装、构建以及配置应用和依赖关系的文件本件。

另外一种使用Docker命令行客户端来创建镜像的方式是通过手工交互式的命令。你能在一个运行的容器里安装和配置一个应用和它的依赖关系,并提交和保存这个容器作为一个镜像。

一个Docker镜像由好几层组成,每一层大致等效于这个应用在安装的时候写到磁盘的改变。Docker管理这些层并存储有效的数据。例如,一个MySQL的镜像的各层,可能由Linux OS、Perl、共享库、MySQL安装文件、MySQL的基本配置文件这些层组成:

当你用Dockerfile创建一个容器或者手工交互式命令定制化一个容器,一般你不需要重头开始,而是会通过已有的Dockerfile或者Docker Hub里已有的镜像来做调整。这样而言,开发者们能够通过在创建和管理一系列有用的镜像的过程中分工和协作。

推送镜像到仓库(例如Docker Hub)的命令是docker push。下载镜像到本地的命令是docker pull。

开始之前

在开始前,你需要一台安装了Docker的Linux服务器。要安装Docker,请按照安装说明选择跟自己相符的操作系统版本进行。

如果你没有可用的Linux服务器,你可以使用云。大部分的云主机提供了可直接运行的虚拟机,这些虚拟机能通过SSH协议或者基于Web的终端在几分钟内连上。这些主机是典型的跟Docker兼容的主机。

本教程里提到的例子假设你已经在一个Linux服务器上运行了Docker。如果你已经在MAC或者其他非Linux上运行着Docker,你的容器应该运行在一个额外的虚拟机上(通常是VirtualBox)。这种情况下,你需要按照下面提到的修改下命令。

检查Docker版本

运行如下命令检查Docker版本:

docker --version

如果你正运行1.8.3或者更高的版本,你的系统已经是完整的了。即使要用Java、Tomcat、Node.js、Python、Apache CouchDB容器做开发,也不需要安装或者卸载这些软件。这就是用Docker做开发的魅力。

下载镜像

第一次从Docker Hub下载一个镜像,你必须先下载它到自己本地的仓库。下载可能很耗时,这取决于你Internet网速,但随后的镜像使用将会非常快。

运行下面的命令下载所有你在本例子中将会用到的镜像:

docker pull node:0.10.40

docker pull java

docker pull tomcat:8

docker pull webratio/ant

docker pull python:3.5

docker pull frodenas/couchdb

如果想知道更多这些镜像的信息,请访问Docker Hub上nodejavatomcatwebratio/antpythonfrodenas/couchdb各自的相关页面。

当完成以上步骤,就可以开始用Docker来运行一个Node.js应用了。

快速建立并开发服务器端JavaScript代码

代码包中(下载地址)的JavaScript目录包含了用Node.js写的建网上商店的源代码。这个应用使用的是Express Web框架和Jade Node模板引擎(关于这个应用的更详细信息,见部署一个Node.js应用到云端)。通过使用Docker,你能在你的电脑上运行这个应用而不用装Node.js以及依赖包,并排查包冲突的问题。

main程序在app.js里面。CSS和其他JavaScript文件在javascript/public/static里面。Jade模板在javascript/view中。

运行如下命令来安装这个应用的Node.js依赖包:

docker run -it --rm --name lllnode -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:0.10.40 npm install

这条命令启动了Node容器,在容器内挂载当前的APP目录,并在应用内运行npm安装:

  • 主命令是docker run node:0.10.40,这会创建一个容器并运行node:0.10.40这个镜像。
  • -it选项让Docker分配一个伪终端(可选的参数为-d,该参数要求Docker在后台运行容器)。
  • -rm参数表示清除,意味着只要你退出容器,Docker就会删除容器。如果不指定这个参数,容器会以停止状态一直存在磁盘上,你能从停止状态重新运行它。尽管,重启容器对于那些忘了删除容器的用户来说是一件挺繁琐切容易遗忘的事。如果你不用--rm选项,你可以通过docker ps -a这条命令在任何时候查看有多少停止状态的容器。
  • --name lllnode选项指明了容器的名字,命令在其他Docker命令里应用容器的时候非常有用。如果不给容器命名,Docker会给容器分配一个名字-这个名字通常没有说明意义。可以通过容器的ID(一个长的,可读性很差的十六进制数)来引用容器。
  • -v "$PWD":/usr/src/myapp选项创建了一个卷挂载点,将当前宿主机的工作目录($PWD)映射为容器内的/usr/src/myapp。应用的Node.js的源代码(或者其他放在当前工作目录的Node.js代码)就能在容器内被访问了。
  • -w /usr/src/myapp选项设定了目前正在运行的命令的工作目录。本例中,工作目录改成了挂载点。
  • 末尾的npm install命令会在容器的工作目录运行。这样的效果就是,你不用安装Node.js或任何的前置条件,就可以在容器的工作目录运行npm install命令。

在命令的输出结果里你可以看到所有的npm安装过程中的依赖关系:

在依赖包被安装后,就可以在容器里运行应用了。使用如下的命令

docker run -it --rm --name lllnode --env PORT=8011 --net="host" -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:0.10.40 node app.js

这条命令跟之前跑的一条相似。但需要注意这条命令在容器里跑的命令是node app.js,这会启动应用。

仔细看看如下app.js的内容,你会发现如下行:

var appEnv = cfenv.getAppEnv();
app.listen(appEnv.port, appEnv.bind, function() {
console.log("server starting on " + appEnv.url);
});

这几行内容使得Express web server会监听环境变量PORT指定的端口。为了在容器里设置这个变量,刚才敲的Docker命令使用了--env PORT=8011这个选项。

此外,命令的--net="host"选项导致了容器使用了宿主机的内部网络。这个应用正在监听宿主机的8011端口。

在浏览器里输入http://localhost:8011/,你就能看到有刚才的容器运行产生的Lauren's Lovely Landscapes的页面。

你可以在任何时候退出容器修改代码,然后再重新运行容器—可以在几秒钟内完成,而不影响开发环境。

几秒内从Node.js切换到Java EE

现在你应该确信了Docker能够在几秒内启动并运行Node.js的应用,你可能想知道它怎么处理更庞大一些的开发,如Java EE开发。

这里假设你已经读过了我的"用Vaadin进行全栈式Java web开发"章节并且乐于尝试J2EE Vaadin的例子。但你并不想安装JDK和破坏目前系统的已有环境,那么Docker就可以来救场!

代码里的Java目录的ArticleViewer.war包是一个由多个Vaadin UI组件构成的应用。能够在Tomcat 8服务器上运行这个war包。首先从之前下载好的镜像里启动实例:

docker run -it --rm -p 8888:8080 --name=tomcat_server -v $PWD:/mnt tomcat:8

这条命令你应该很熟了。容器的名字叫做tomcat_server。容器里的工作目录指定的是/mnt。

打开一个浏览器并输入http://localhost:8888/,你就会看到熟悉的Tomcat 8的欢迎界面。

注意你是使容器运行在交互模式(通过-it选项)下的,在这个模式下,日志会持续不断在标准输出界面出现,这有利于开发。

将应用附到运行的容器里

现在,容器内的/mnt目录是工作目录,ActiveViewer.war就在这个目录。

复制ActiveViewer.war包从/mnt目录到Tomcat 8的webapps目录,这个war包就会自动被部署:

docker exec tomcat_server cp /mnt/ArticleViewer.war /usr/local/tomcat/webapps

这次没有用docker run命令,因为tomcat_server容器已经在运行,反而,这次需要切换到运行的容器内部,执行一个拷贝命令。因此,需要执行docker exec命令,并在末尾加上supplying the cp /mnt/ArticleViewer.war /usr/local/tomcat/webapps。

观察ArticleViewer应用被部署后窗口的日志。

现在在浏览器里输入应用的URL—http://localhost:8888/ArticleViewer/,点击左侧列表的一篇文章,查看右侧的内容。

停下来思考一会儿。你没有在系统上安装JDK或者Tomcat 8,但你依然可以在几秒内在一个Tomcat 8服务器上运行Vaadin应用的代码—感谢Docker。

开发构建Java Web应用

正如你所见,你能通过Docker轻松的部署一个Web应用来做测试。同样地,你也能通过Docker从Java源代码里编译和构建Web应用—同样不需要破坏你的开发环境或者花很长时间预先安装环境。

代码下载的包切换目录到java/LaurenLandscapesJava。这个目录下面是一个Lauren's Lovely Landscapes应用的Java代码版本。

build.xml文件是一个标准的Apache Ant生成的文件,包含了编译代码和构建WAR包的说明。通过使用webratio镜像,你能快速编译生成war包,当代码发生改变的时候:

docker run -it --rm -v "$PWD":/mnt webratio/ant bash -c 'cd /mnt; ant'

现在这条Docker命令你应该完全熟悉了。这条命令会在挂载到容器内的目录/mnt下面运行ant生成工具。ant编译在src子目录下的所有Java代码,并生成一个WAR文件,将它放置在dist子目录下面,命名为lauren.war。

复制lauren.war包,从java/LaurenLandscapesJava/dist到Java目录

cp dist/lauren.war ..

你可以用下面的命令部署lauren.war应用到Tomcat服务器上:

docker exec tomcat_server cp /mnt/lauren.war /usr/local/tomcat/webapps

这条命令会运行在tomcat_server容器内并复制lauren.war包到Tomcat的webapps子目录。

在浏览器里输入http://localhost:8888/lauren,你能看到运行在你的容器化的Tomcat上的Lauren's Lovely Landscape。

从Java切换到Python

设想你是一个全才—你有从Java代码里跳出并使用Python的Bottle Web框架的可能性。帮你尽快探索Python Web应用开发同时又没有安装环境和解决依赖的麻烦—Docker又帮了你大忙。

切换到代码包的python/Laurens.Lovely.Landscapes目录。在那个目录下面是Lauren's Lovely Landscapes的Python版本是用Bottle web框架写的。模板在views子目录。

不需要安装Python在你的系统里,通过下面的Docker命令运行这个Python应用:

docker run -it --rm --name lllpython -p 8000:8000 -v "$PWD":/usr/src/myapp -w 
/usr/src/myapp python:3.5 python wsgi.py

在浏览器里输入http://localhost:8000/,你就能看到Lauren's Lovely Landscapes的Python版本了。

当用Python做开发的时候,你可以在简单修改完代码后,重新运行上面的命令来做测试。运行Docker容器和运行本地的开发工具速度是一样快的。

添加并运行一个单独的数据库

大部分现在的Web应用涉及三层架构:浏览器作为client;中间件应用服务器例如Express、Tomcat 8、Bottle;底层数据库。通过使用Docker,你能快速添加一个数据库而不是在你的系统里安装它。

在这个最后的例子里,你会添加Apache CouchDB数据库到你的开发环境里。你能使用CouchDB API来测试CouchDB或者Cloudant,这些API跟IBM Cloudant NoSQL数据库100%兼容。

切换到代码包的database目录。这个目录里是Lauren's Lovely Landscapes应用的高级Node.js版本。这个版本从CouchDB里抓取了商店的目录信息。网上商城根据版画的目录动态的改变外观。(你在这里能找到更多的代码)

用如下的Docker命令运行Apache CouchDB的实例

docker run -d --name couchdb -p 5984:5984 -e COUCHDB_USERNAME=user -e COUCHDB_PASSWORD=abc123 -e COUCHDB_DBNAME=prints -v $PWD/data:/data frodenas/couchdb

这条命令使用了COUCHDB_USERNAME、COUCHDB_PASSWORD和 COUCHDB_DBNAME三个环境变量来配置实例已满足代码里设的值。当前的工作目录被挂载到容器内的/data目录。CouchDB会写数据到这个目录,这就使得重启容器后数据不会丢失。

在这个例子中,你运行容器是以-d选项而不是-it --rm选项。-d选项会在后台启动数据库。你可以使用docker ps命令看到所有后台的容器。

接着,安装应用的Node.js依赖:

docker run -it --rm --name llldepends -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:0.10.40 npm install

Apache CouchDB实例不会保留任何数据。使用如下命令来运行dataseeder.js代码,这会在Apache CouchDB的实例里创建一个文件:

docker run -it --rm --name dataseeder --net="host" -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:0.10.40 node dataseeder.js

在端口8011运行lllnode应用

docker run -it --rm --name lllnode --env PORT=8011 --net="host" -v "$PWD":/usr/src/myapp -w /usr/src/myapp node:0.10.40 node app.js

在浏览器里输入http://localhost:8011/打开Lauren's Lovely Landscapes这个应用商店:

你能看到澳大利亚的照片已经没有现货了。产品类以红色高亮突出显示并且不能被选中。

如果要模仿为澳洲的相片补货,需要访问CouchDB数据库并修改文件。

CouchDB提供了一个简单的图形化用户界面,叫做Futon,来访问存储的文件。在浏览器里输入http://127.0.0.1:5984/_utils/打开Futon的图形界面:

在Futon里,选中prints这列。打开文件并扩展条目3,双击澳大利亚的print条目,打开文件编辑。将quan的值由0修改为3。点击右边绿色的检查按钮,然后点击左边顶部的保存按钮。

随着刚才数据库的修改,重新在浏览器里输入http://localhost:8011/,你会看到现在商店里的货都是有现货的。澳大利亚的相片那栏不再是红色并且是可被选中的了。

当你工作中用到三层架构时,Docker可以简化开发流程并且使得底层数据库的部署更加容器。

总结

在这个竞争的年代,在你的开发过程中你不可能游离于Docker工具之外。通过砍掉反复安装和卸载繁琐的开发工具以及依赖包的步骤,Docker极大的缩短了你的开发时间。好好的利用Docker来提升你的开发水平和开发效率吧。

原文链接:Docker: A boon for the modern developer(翻译:王瀛)

==================================================
译者介绍
王瀛:惠普cloud&automation部门实施工程师。

Docker简明教程的更多相关文章

  1. Docker简明教程(转)

    Docker自从诞生以来就一直备受追捧,学习Docker是一件很炫酷.很有意思的事情.我希望通过这篇文章能够让大家快速地入门Docker,并有一些学习成果来激发自己的学习兴趣.我也只是一个在Docke ...

  2. Docker简明教程(以安装wget程序为例)

    本文计划: 一.安装Docker(Centos) 二.注册Docker官网帐号 三.下载基础centos镜像,安装需要的软件和环境后,push到自己的repository 一.安装Docker(Cen ...

  3. Docker简明教程(转)

    Docker自从诞生以来就一直备受追捧,学习Docker是一件很炫酷.很有意思的事情.我希望通过这篇文章能够让大家快速地入门Docker,并有一些学习成果来激发自己的学习兴趣.我也只是一个在Docke ...

  4. [翻译] 一个kubernetes网络简明教程[Part 1]

    一个kubernetes网络简明教程[Part 1] 翻译: icebug 所有我学到的关于kubernetes网络的事情 你可能已经在kubernetes集群当中跑了一堆服务并且正在享受其带来的好处 ...

  5. 2013 duilib入门简明教程 -- 第一个程序 Hello World(3)

    小伙伴们有点迫不及待了么,来看一看Hello World吧: 新建一个空的win32项目,新建一个main.cpp文件,将以下代码复制进去: #include <windows.h> #i ...

  6. 2013 duilib入门简明教程 -- 部分bug (11)

     一.WindowImplBase的bug     在第8个教程[2013 duilib入门简明教程 -- 完整的自绘标题栏(8)]中,可以发现窗口最大化之后有两个问题,     1.最大化按钮的样式 ...

  7. 2013 duilib入门简明教程 -- 部分bug 2 (14)

        上一个教程中提到了ActiveX的Bug,即如果主窗口直接用变量生成,则关闭窗口时会产生崩溃            如果用new的方式生成,则不会崩溃,所以给出一个临时的快速解决方案,即主窗口 ...

  8. 2013 duilib入门简明教程 -- 自绘控件 (15)

        在[2013 duilib入门简明教程 -- 复杂控件介绍 (13)]中虽然介绍了界面设计器上的所有控件,但是还有一些控件并没有被放到界面设计器上,还有一些常用控件duilib并没有提供(比如 ...

  9. 2013 duilib入门简明教程 -- 事件处理和消息响应 (17)

        界面的显示方面就都讲完啦,下面来介绍下控件的响应.     前面的教程只讲了按钮和Tab的响应,即在Notify函数里处理.其实duilib还提供了另外一种响应的方法,即消息映射DUI_BEG ...

随机推荐

  1. MEF 生命周期PartCreationPolicy

    为什么要单独把这个生命周期捞出来单独说一说呢?因为我今天就被这个东东坑了一把……新加了一个界面,第二次打开界面的时候会报错“指定的元素已经是另一个元素的逻辑子元素”.好嘛,我一看,哟,感觉就是xaml ...

  2. C++11 智能指针

    C++ 11标准库引入了几种智能指针 unique_ptr shared_ptr weak_ptr C++内存管理机制是当一个变量或对象从作用域过期的时候就会从内存中将他干掉.但是如果变量只是一个指针 ...

  3. 面向对象的高级编程&IO编程

    1.给类对象绑定的函数,只对这个对象生效, 而对类绑定的对象, 所有的对象都可以调用. 栗子: def set_score(self, score): self.score = score s.set ...

  4. MySql创建树结构递归查询存储过程

    在实现F2工作流底层多数据库支持时发现Oracel和mssql都有提供递归子查询,而MySql却没有,没办法需要自己构建存储过程来提供这个递归子查询的功能. -- 当前节点及子节点 -- 参数说明:i ...

  5. WPF附加属性

    附加属性实质也是依赖属性,是说一个属性本来不属于某个对象,但由于某种需求被后来附加上的,也就是说把对象放入一个特定环境后才具有的属性 例子:人在学校有年纪和班级两个属性,人放在学校里会获得年级和班级两 ...

  6. 艺萌文件上传下载及自动更新系统(基于networkComms开源TCP通信框架)

    1.艺萌文件上传下载及自动更新系统,基于Winform技术,采用CS架构,开发工具为vs2010,.net2.0版本(可以很容易升级为3.5和4.0版本)开发语言c#. 本系统主要帮助客户学习基于TC ...

  7. IOS多媒体

    概览 随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像 ...

  8. asp.net网站运行出错:the underlying provider failed on open的解决

    在登录系统,通过linq查询时发生错误,the underlying provider failed on open,如何解决,请看: Step 1:Open Internet Information ...

  9. Yii2中request的使用

    1.普通的get和pst请求 $request = Yii::$app->request; $get = $request->get(); // equivalent to: $get = ...

  10. Codeforces Round #384 (Div. 2) 解题报告

    这场CF水题都非常的水,D题如果对树.DFS相关比较熟练的话也不难.比赛时前三题很快就过了,可是因为毕竟经验还是太少,D题就卡住了.比赛之后A题还因为没理解对题意fst了--(为什么这次就没人来hac ...