浅谈使用Docker对应用进行容器化

  Docker为应用程序的打包和运行提供了一种优雅的方式。使用喜欢的Linux系统,几分钟之内就能将Docker安装好并作为服务运行起来。构建、运行、停止、启动、调查、修改或者用其他的方式操作容器非常容易,说实话,很棒。

  Docker的简单易用使其成为当今最流行的开源项目之一。但是作为数据中心容器化核心的Docker却引起了极大的震动,其潜力无异于重新发明了个人和公司(或大或小)创建、测试、部署和管理其最关键应用程序的方式。

  使用容器化技术也可以让应用程序向云环境的部署变得更为高效。就像容器本身一样,运行容器的操作系统也能够被瘦身。因为容器已经持有应用程序运行所需的大部分依赖,所以这些用于容器的新型宿主机操作系统就不再需要包含所有依赖了。

  1.了解容器化应用的优缺点

  Docker提供了一种创建和运行已经在容器中进行了配置的应用程序的方法。想要真正理解它,先了解容器化应用不是什么是很有帮助的。

  1.1 容器化应用不是直接在宿主机上运行的应用

  运行应用程序的传统方法会将应用程序直接安装到宿主计算机的文件系统上并从那里运行它。从应用程序的视角看来,其环境包含宿主机的进程表、文件系统、IPC设施、网络接口、端口及设备。

  要让应用程序运行起来,通常需要安装与应用程序搭配的额外软件包。一般来说,这不是问题。但有些情况下,可能想在同一个系统上运行相同软件包的不同版本,这就可能会引起冲突。

  应用程序与应用程序之间也会以某种方式发生冲突。如果应用程序是服务,它可能会默认绑定特定的网络端口。在服务启动时,它可能还会读取公共配置文件。这会导致无法在同一宿主机上运行该服务的多个实例,或者至少非常棘手;这还让那些想要绑定到同一端口的其他服务难以运行。

  直接在宿主机上运行应用程序还有一个缺点,那就是难以迁移应用程序。如果宿主机需要关机或者应用程序需要更多计算能力——超出宿主机所能提供的,那么从宿主计算机上获取所有依赖并将其迁移到另一台宿主机上绝非易事。

  1.2 容器化应用不是直接在虚拟机上运行的应用

  创建虚拟机来运行应用程序能够克服直接在宿主机操作系统上运行应用程序所具有的缺点。虽然虚拟机位于宿主机上,但它作为独立的操作系统运行,它包括自己的内核、文件系统、网络接口等。这样可以很容易将几乎所有东西保存在独立于宿主机的操作系统中。

  因为虚拟机是独立的实体,所以不会出现那种直接在硬件上运行应用程序所产生的缺乏灵活性的弊端。可以在宿主机上启动10个不同的虚拟机来运行应用程序10次。虽然每个虚拟机上的服务监听了同一个端口号,但是因为每个虚拟机拥有不同的IP地址,所以并不会引起冲突。

  同样地,如果需要关闭宿主计算机,可以将虚拟机迁移到其他宿主机上(如果虚拟化环境支持迁移)或者直接关闭虚拟机并在新宿主机上再次启动它。

  一个虚拟机运行一个应用程序的实例的缺点是耗费资源。你的应用程序可能只需要几兆字节磁盘空间来运行,但是整个虚拟机却要耗费许多GB的空间。再者,虚拟机的启动时间和CPU占用几乎肯定会比应用程序自身消耗的高得多。

  容器提供了另一种在宿主机上或虚拟机内直接运行应用程序的方式,这种方式能使应用程序更快、可移植性更好,并且更具可扩展性。

  1.3 了解容器的优点

  就运行应用程序而言,容器有望灵活高效地使用资源。

  灵活性来自容器可以包含其所需的全部文件。如同运行于虚拟机中的应用程序,其可以拥有自己的配置文件和依赖库,还可以拥有自己的网络接口——这些网络接口不同于宿主机上配置的那些网络接口。因此,与在虚拟机上运行应用程序一样,容器化应用比直接安装的应用程序更容易迁移,而且因为应用程序运行所栖身的每个容器均拥有独立的网络接口,所以也不会争用同一端口号。

  就启动时间、磁盘空间占用和处理能力而言,容器既没有运行独立的操作系统,也没有包含运行整个操作系统所需的大量软件。这是因为容器只包含运行应用程序所需的软件,以及其他想随容器一起运行的工具和少量描述容器的元数据。

  与虚拟机不同,Docker容器并不包含独立的内核(kernel)。Docker容器中运行的命令会出现在宿主机的进程表中,多数情况下,看起来非常像系统中运行的其他进程。然而,这两种环境中应用程序运行的差异与这两个应用程序所看到的不同世界有着极大的关系。

  文件系统:容器拥有自己的文件系统,默认情况下,它无法看到宿主机系统的文件系统。该规则的一个例外是,有些文件(如/etc/hosts和/etc/resolv.conf)可能会被自动挂载到容器中。另一个例外是,当运行容器镜像时,可以显式地将宿主机的目录挂载到容器中。进程表:Linux宿主机上可能运行着成百上千的进程。然而,默认情况下,容器内的进程无法看到宿主机的进程表,容器拥有自己的进程表。因此,你启动容器时所运行的应用程序,其进程在容器内被分配的PID为1。从容器内,进程无法看到宿主机上运行的其他进程——如果这些进程没有在容器内启动的话。网络接口:默认情况下,Docker守护进程通过DHCP从一组私有IP地址集中确定IP地址。除了使用DHCP,Docker也支持其他网络模式,例如,允许容器使用其他容器的网络接口,直接使用宿主机的网络接口,或者不使用网络接口。依据你的统招选择,你可以将容器内的端口暴露给宿主机的同一端口或不同端口。IPC设施:容器内运行的进程不能与宿主机系统上运行的进程间通信(IPC)设施直接交互。你可以将宿主机上的IPC设施暴露给容器,但这不是默认的。每个容器都有自己的IPC设施。设备:容器内的进程无法直接看到宿主机系统上的设备。不过,当容器启动时可以设置一个特殊权限选项来授予该项权限。

  如你所见,Docker容器可以在宿主机的视线内运行,但其运行方式限制了容器越过边界所能看到的宿主机内容(除非明确地开放了那些视图)。

  1.4 了解容器化应用面临的挑战

  容器化应用面临的挑战在于它们不同于那些不在容器中的应用程序。每个Linux系统中,用于启动和停止服务以及查看出错消息的设施已就位。Linux还提供了监控服务和轮换日志文件的方法。

  对于运行虚拟机而言,整个虚拟化平台(如OpenStack和Red Hat企业虚拟化)被构建来启动、停止和使用虚拟机。虽然管理容器组的工具已在努力构建中,但大多数尚处于萌芽阶段。诸如Kubernetes和OpenShift这样的项目正在完成部署和管理容器组的框架。

  Docker容器被打包为容器镜像。为了能将容器镜像保存在registry中并用docker命令管理它们,人们已经进行了大量的工作。然而,用于管理Docker镜像的工具远未像用来管理Linux软件包(如基于Linux RPM或Deb的系统)的工具那样成熟。

  能够验证镜像来自何处的工具、确定镜像是否被篡改的工具以及查看容器中具体安装了哪些软件包和这些软件包的版本的工具才刚刚开发。目前,要意识到,多数情况下很难完全保证从Docker Hub Registry上随便获取的镜像可以安全使用。

  使用容器的另一个挑战来自这样一个事实:就其本质而言,容器默认情况下无法看到其他容器。因此,要是你想让自己的容器与其他容器紧密配合要怎么办呢?例如,你可能有一个Web服务器,你想让这个服务器访问你的数据库服务器。

  让容器看到彼此的一些解决方案是Docker中能让你将容器连接到一起的特性,以及让你在pod的容器之间识别出服务的使用方和提供方的Kubernetes的特性。更多容器管理工具正被推出来解决这些问题。但切记,它们尚处于早期开发阶段,而且容器管理的几乎各个领域都有多个工具在开发,有时还相互矛盾。

  2.了解容器的组成

  Docker是Docker项目开发的一种容器格式。docker命令能够运行、停止、启动、调查容器,还能操纵容器。docker命令也可以作为服务守护进程运行,处理管理Docker容器的请求。默认情况下,这个Docker服务会从Docker Hub Registry获取你请求的镜像。虽然你无需知道更多就可以开始使用,但接下来会依次给出一些额外的信息。

  2.1 Docker项目

  Docker项目(docker)为Docker开发提供了一个中心。它将Docker称为“一个针对分布式应用开发者和系统管理员的开放平台”,旨在简化应用程序的开发和分发。

  Solomon Hykes是Docker的创始人和CTO。他将Docker要在软件行业做的事情与物理集装箱为航运业所做的事情进行了对比。无论是运输汽车、圆桶、箱子,还是运输钢琴,只要使用标准的集装箱来运输这些不同类型的物品,用来运送和处理它们的工具也可以被标准化。

  因此,就其核心而言,Docker项目提供了一种软件容器的格式并创建了一个专为使用该格式的软件而搭建的简单的基础设施。随着项目的进行,它开始超出其最初所关注的稳定Docker格式以及提供工具来管理单个容器。

  如今,Docker项目正将其范围扩展至包含配置与编排工具,来帮助人们部署和管理成组的容器。该项目还研究管理计算资源的方式,并提供高可用性的方式来帮助运行Docker容器。随着这些工具变得可用,它们将不得不与谷歌和Red Hat这样的公司正开发的更成熟的容器编排工具(包括本书涵盖的Kubernetes项目的工具)正面交锋。

  然而,目前Docker项目的最大成就是Docker容器格式、管理单个容器的工具,以及在Docker客户端和registry之间拉取和推送Docker容器镜像的能力。Docker项目管理的*registry被称为Docker Hub Registry。

  2.2 Docker Hub Registry

  Docker Hub Registry(

  registry.hub.docker)提供了个人与组织保存和开发其Docker容器镜像的地方。当你的Linux系统上安装了Docker的时候,如果你请求的Docker容器镜像尚未在你的系统中,默认情况下Docker会查看Docker Hub Registry。

  通过注册Docker用户账号,你可以拥有自己的Docker repository,你可以把Docker容器镜像推送到这里。之后,只要连接了因特网,你就可以从任何运行Docker的系统拉取这些镜像。

  Linux发行版和应用项目在Docker Hub Registry上有官方的repository。除了Docker容器镜像本身,多数情况下,你也能在Docker Hub Registry上查找使用这些镜像的说明以及用来构建这些镜像的Dockerfile文件。对于不想公开共享的容器镜像,也有办法创建你自己的Docker Registry,你可以用它来私密地保存镜像或者直接从Docker项目购买安全的容器存储空间。

  你也许注意到,当描述Docker存储和传输软件所用的格式时,会出现“镜像”和“容器”这样的词。当使用Docker时,理解镜像和容器之间的差别是非常关键的。

  2.3 Docker镜像和容器

  容器化的目的是将应用程序运行需要的所有组件集合在一个单一而独立的单元中。对于Docker而言,这个单元被称作Docker镜像。镜像之中是容器要运行的应用程序以及应用程序执行所需的库、配置文件、可执行程序或者其他组件。

  镜像是一个静态单元,它要么在repository中,要么在安装了Docker的本地文件系统中,并等着被运行。与将Docker镜像存储到repository中不同,将Docker镜像保存到文件系统时,它会被保存为一个tarball文件。这个tarball可以像其他文件那样传输,之后可以在运行Docker的本地系统上被导入并作为容器运行。

  像Red Hat Enterprise Linux、Ubuntu、Fedora和CentOS这样的主要Linux发行版都提供了官方的基础镜像,你可以使用它们构建自己的Docker镜像。即使不是程序员,也可以获取基础镜像、向其中添加现有应用程序并将其制作为自己的镜像。要做到这一点只需创建Dockerfile并在其上运行docker build命令。

  Docker容器这个术语指的是Docker镜像的运行实例,或者更为准确地说,是一个已运行的镜像的实例,因为它此刻可能正在运行、暂停或者已经停止。开始使用Docker时,镜像和容器之间的区别非常关键。需要理解这个区别的原因在于,处理镜像和处理容器有着不同的命令。

  例如,当要查看本地系统的镜像列表时,要输入docker images;要查看正在运行的容器列表,要输入docker ps(或是docker ps -a,查看不再运行但还保存在你的系统中的容器的列表)。

  想从镜像运行容器,要用docker run命令。想停止正在运行的减肥方法,要用docker stop命令。在容器停止后,可以使用docker start命令再次启动已停止的容器。只是要暂停容器中的所有进程,要用docker pause命令。之后输入docker unpause来再次启动已暂停的容器。谨记,docker run从原始镜像运行一个新容器,而docker start从容器被停止时的状态重新启动一个容器(例如,你添加的软件或修改的文件还在那儿)。

  当使用容器时,你会注意到所有给出的示例都有一个共同点。它们中的每一个都要使用docker命令来调用。

  2.4 docker命令

  docker命令是你与Docker容器和镜像直接打交道所使用的主要命令。实际上,在Docker软件的一些软件包中,docker``只是为数不多的几个可执行命令之一。

  一旦安装了Docker软件,只要启动Docker服务就能开始使用docker命令。docker命令内置的一个不错的特性是Tab自动补全(如果你是从默认的bash shell运行Docker的话)。因此,一旦Docker服务运行起来,你就可以作为root用户输入docker,紧跟着按下两次Tab键来查看可用的docker子命令:

  # docker

  attach exec inspect port rmi tag

  build export kill ps run top

  commit help load pull save unpause

  cp history login push search version

  create images logout rename start wait

  diff import logs restart stats

  events info pause rm stop查找Docker组件的信息:使用docker version展示Docker特性的版本信息。使用docker info查看运行Docker的系统的信息。docker help查看可以与docker命令一起使用的命令和选项。使用docker history展示镜像的历史。使用docker inspect查看镜像或容器的信息。使用docker port列出容器的端口映射。

  操作正在运行的容器:使用docker ps列出正在运行的容器。使用docker attach将另一个命令附加到正在运行的容器上。使用docker exec在正在运行的容器中执行命令。使用docker inspect审查容器的元数据。使用docker cp从容器向宿主机系统复制文件。使用docker diff检查容器从启动后其文件系统所做的改变。

  操作镜像:使用docker images列出系统上的镜像。使用docker run运行镜像。使用docker pull从registry向本地系统拉取镜像。使用docker push将镜像推送到registry中。使用docker save将镜像保存为tarball。使用docker load从tarball文件加载本地镜像。使用docker export从容器中将文件系统导出成本地系统的tarball文件。操作Docker registry:使用docker search在registry中搜索镜像。使用docker login登录到Docker Hub Registry(你就能用自己的账号来推送和拉取镜像)。使用docker logout从Docker Hub Registry登出。修改现存的镜像:使用docker tag为镜像添加一个名字。使用docker rename修改镜像的名字。修改容器的状态:使用docker stop停止正在运行的容器。使用docker start启动一个已经停止的容器。使用docker pause暂停容器。使用docker unpause重新启动一个已经暂停的镜像。使用docker kill向容器发送kill信号或其他信号。使用docker restart停止并重新启动容器。查看Docker的活动:使用docker events查看Docker服务器的事件。使用docker top查看容器的进程活动。使用docker logs查看由容器产生的日志消息。使用docker stats查看容器的CPU和内存使用统计。使用docker wait查看容器直到它停止,然后打印它的退出代码。创建镜像和容器:使用docker build从头构建镜像。使用docker commit从容器创建镜像。使用docker create从镜像创建容器但不运行它。使用docker import将文件系统导入镜像中。删除容器和镜像:使用docker rm删除已停止的容器。使用docker rmi删除镜像。

  尽管Docker是专为帮你以最少的麻烦让容器运行起来而设计的,本书在这里会指导你完成Docker的初次体验,并指出你可能无法自己发现的有趣特性。之后,本书会带你进入Docker中一些很少被绘制的水域——与多容器的部署和管理相关。换句话说,本书为你提供了一种方法来进行Docker探险。

  3 探究容器

  由于围绕Docker和容器的开发节奏很快,现在任何书籍所能做的最好的就是将你带到一条正确的路上。本书中,这就意味着一开始会给出一套靠谱示例来说明当前Docker和精选的一组支持工具是如何工作的。之后,这条大路就会展示位于地平线上的新特性和新工具。

  无论你是想使用和管理容器,还是担负容器开发的任务,本书会让你以所有人都需要的一些事情起步,包括以下几项。

  搭建Docker:许多完整的Linux系统和一些面向容器的特定Linux系统上都可以使用Docker。因此,本书开始部分的说明会帮你选择一个或多个这样的系统并展示如何启动Docker服务。搭建Docker registry:Docker把容器镜像保存在registry中并使镜像可以被拉取(下载)到运行Docker的系统中。因此,你可以学习如何创建自己的私有Docker registry并用它保存自己的容器镜像。

  如果你想要使用和管理容器,我会为你介绍不同的方法来使用:

  普通的单个容器;一组容器(使用Kubernetes和其他工具来管理它们)。

  无论你是创建容器化应用还是只运行容器化应用,它都有助于理解将底层操作系统特性提供给容器的方法。你应该了解的用来支持容器的操作系统的辅助特性包括以下几个。

  宿主机权限:设计上限制了容器可以操纵宿主机的范围。开放宿主机权限能让容器直接访问宿主机系统的特性,如宿主机的进程表、设备、特定的CPU以及IPC命名空间。本书稍后会用Fedora Atomic Host演示那些专为访问和修改宿主机系统而设计的容器(被称为超级特权容器)。存储:你可以在Docker容器中使用挂载来连接宿主机的存储空间,而不是将数据保存在容器中。网络:有特殊的规则和选项来从容器内管理宿主机的网络接口。

  作为软件开发人员,容器既限制了你也让你得以*。你有能力确保你的应用程序的所有文件都以一种立即可以运行的形式与容器打包在一起。但新的挑战浮现出来,这些挑战要求你重新思考应对下列事项的开发方式。

  高效地处理容器层;通过测试、开发和生产阶段指引软件增强;在多个容器间划分服务;在运行时环境中部署、启动和停止容器;处理你的应用程序所需要的宿主机系统的辅助服务。

  Docker有许多强大的特性并且当前可以很好地构建和运行单个容器。但Docker周围的世界并非停滞不前。甚至此时,数以百计的人们每天都在努力扩展用Docker所能完成的工作。

  相当数量的工具即将出现,用以支持企业级容器的开发和部署。同样地,人们在不断地创造聪明的容器,并将这些容器以及用于构建这些容器的Dockerfile提供给想要使用和扩展它们的人。

  4 小结

  通过将应用程序(以及应用程序运行所需要的全部东西)与运行该应用程序的宿主机解耦,Docker容器提供了一种简单、优雅的应用程序部署和运行方式。较之于将应用程序直接安装在宿主机上所获得的灵活性,Docker容器能够提供更大的使用灵活性。而且,与虚拟机相比,Docker容器对系统资源(如CPU使用、内存消耗以及磁盘空间使用)的要求很少。

  本文描述了构成Docker的不同组件。这些组件由Docker项目管理(docker),包括Docker Hub Registry(它保存Docker镜像)、Docker服务、docker命令以及你要用到的镜像和容器。

  本书是Linux系统及云环境上运行Docker的实用指南。书中全面讲解创建、运行、停止、启动、保存和管理容器的具体方法,同时也涉及了多容器管理的主题。

  本书内容分成5个部分。第一部分开启容器之旅,介绍开始使用Docker容器所需了解的知识;第二部分关注单个容器,主要介绍如何通过docker命令直接使用单个容器;第三部分主题是在云环境上运行容器,描述如何运行超级特权容器(SPC),以及如何使用Cockpit在云环境或者本地环境下跨多个宿主机管理容器;第四部分重点是管理多容器,探究容器的编排(利用Kubernates);第五部分专注于开发容器,描述一些开发Docker容器的建议和技巧,最后通过展示一些Dockerfile文件阐述如何构建容器。

  阅读本书不需要读者了解Docker或者容器化方面的知识,读者可以将本书作为Docker的入门书。同时本书也适合系统管理员、软件开发人员、运维人员和技术*者。

上一篇:深度好文:一个30岁男人转型码农的平凡之路


下一篇:大数据初学者入门指南,及需要知道的51个大数据术语