高性能Docker(txt+pdf+epub+mobi电子书下载)


发布时间:2021-03-06 16:39:04

点击下载

作者:艾伦·埃斯皮诺萨 (Allan Espinosa)

出版社:电子工业出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

高性能Docker

高性能Docker试读:

前言

Docker是一款很好的用于构建和部署应用的工具。可移植的容器格式使我们可以在任何地方运行代码,从开发者工作站到著名的云计算提供商。Docker的工作流使开发、测试和部署更加容易和快速。然而,Docker核心和最佳实践的持续改善是很重要的,可帮助你实现Docker最大的潜在价值。本书的主要内容

凡是对Docker有基本理解的工程师都可以按顺序一章一章地阅读本书。对Docker具有深入理解或者在生产环境中部署过应用的技术领导者们,可以直接阅读第8章的内容,了解Docker是如何适应已有应用的。以下是本书介绍的一系列主题。

第1章,简单介绍了如何搭建和运行Docker,介绍了贯穿本书都会用到的搭建步骤。

第2章,介绍了为什么调优Docker镜像是很重要的,介绍了多个调优小窍门,从而改善可部署性和Docker容器的性能。

第3章,介绍了如何自动化搭建Docker宿主机,并讨论了自动化的重要性以及它是如何促进Docker容器的大规模部署的。

第4章,介绍了如何使用Graphite搭建监控系统和使用ELK搭建日志系统。

第5章,介绍了如何使用Apache JMeter来创建负载,并测试Docker容器的性能。本章回顾了第4章中搭建的监控系统,并分析了若干Docker应用的性能基准结果,例如响应时间和吞吐量。

第6章,介绍了如何配置和部署基于Nginx的负载均衡容器。同时,也介绍了如何使用负载均衡器来水平扩展Docker应用的性能和可部署性。

第7章,介绍了典型Linux系统中的通用调试工具是如何调试Docker容器的。并介绍了每种工具是如何工作的以及是如何读取运行中的Docker容器的诊断信息的。

第8章,综合了前面几章中的性能优化方法,并介绍了如何在生产环境中使用Docker部署任何一个Web应用。你需要做什么准备

你需要一台安装了最新版本内核的Linux工作站作为Docker 1.10.0的宿主机。本书使用Debian Jessie 8.2作为基础操作系统来安装和搭建Docker。本书的目标读者

本书是为想要在生产环境中部署Docker应用和架构的开发者和运维者而写。如果你已经了解了Docker的基本知识,并且想进一步学习Docker的话,那么这本书就是适合你的。惯例

在本书中,我们使用了不同的文本格式,用以区分不同类型的信息。以下是这些格式的例子以及它们的具体含义。

文本格式的代码、数据库表名、目录名、文件名、文件扩展名、路径名、URL、用户输入、Twitter用户名都是用以下格式书写的:“我们会使用--link:来创建源容器source到另一个容器webapp的连接”。

代码块的格式如下: FROM ubuntu:14.04 MAINTAINER Docker Education Team RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get \ install -y -q python-all python-pip ADD ./webapp/requirements.txt /tmp/requirements.txt RUN pip install -qr /tmp/requirements.txt ADD ./webapp /opt/webapp/ WORKDIR /opt/webapp EXPOSE 5000 CMD ["python", "app.py"]

当我们希望着重表示代码块中的某一部分时,这一部分就会被设置为粗体。 import os from flask import Flask app = Flask( name ) @app.route('/') def hello(): provider = str(os.environ.get('PROVIDER', 'world')) return 'Hello '+provider+'!' if name == ' main ': # Bind to PORT if defined, otherwise default to 5000. port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)

命令行输入或输出的格式如下: dockerhost$ docker inspect -f "{{ .NetworkSettings.IPAddress }}" \ source 172.17.0.15 dockerhost$ docker inspect -f "{{ .NetworkSettings.IPAddress }}" \ destination 172.17.0.28 dockerhost$ iptables -L DOCKER Chain DOCKER (1 references) target prot opt source destination ACCEPT tcp -- 172.17.0.28 172.17.0.15 tcp dpt:5000 ACCEPT tcp -- 172.17.0.15 172.17.0.28 tcp spt:5000警告或者重要注解会出现在这样的图标之后。提示和技巧将会出现在这样的图标之后。下载示例代码

你可以从http://www.broadview.com.cn下载所有已购买的博文视点书籍的示例代码文件。勘误表

虽然我们已经尽力谨慎地确保内容的准确性,但错误仍然存在。如果你发现了书中的错误,包括正文和代码中的错误,请告诉我们,我们会非常感激。这样,你不仅帮助了其他读者,也帮助我们改进后续的出版。如发现任何勘误,可以在博文视点网站相应图书的页面提交勘误信息。一旦你找到的错误被证实,你提交的信息就会被接受,我们的网站也会发布这些勘误信息。你可以随时浏览图书页面,查看已发布的勘误信息。1 准备Docker宿主机

Docker使应用交付到客户更加便捷。它通过简单地创建和运行容器这种形式,简化了代码从开发环境到生产环境部署过程中的工作流。本章将快速学习如何准备一个基于Docker的开发环境,具体的操作步骤如下:

• 准备一个Docker宿主机

• Docker镜像的基本操作

• 运行Docker容器

本章的大部分内容是我们已经熟悉的知识,并且可以在Docker官方文档网站上查阅到。这里将介绍的是与Docker宿主机有关的部分命令和在后续几章中将使用到的交互操作。准备一个Docker宿主机

假定读者熟悉如何创建一个Docker宿主机。对于本书的大部分章节,除非有特别说明的情况,我们都将在如下环境中运行示例:

• 操作系统:Debian 8.2 Jessie

• Docker版本:1.10.0

下面的命令显示了操作系统和Docker的版本: $ ssh dockerhost dockerhost$ lsb_release –a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 8.2 (jessie) Release: 8.2 Codename: jessie dockerhost$ docker version Client: Version: 1.10.0 API version: 1.21 Go version: go1.4.2 Git commit: a34a1d5 Built: Fri Nov 20 12:59:02 UTC 2015 OS/Arch: linux/amd64 Server: Version: 1.10.0 API version: 1.21 Go version: go1.4.2 Git commit: a34a1d5 Built: Fri Nov 20 12:59:02 UTC 2015 OS/Arch: linux/amd64

如果还没有创建Docker环境,可以按照Docker官方网站上的说明进行安装,网址是:https://docs.docker.com/installation/debian。下载示例代码你可以从你的账户中下载所有已购买的Packt书籍中的代码,地址是:http://www.packtpub.com。无论你是在什么地方购买的这本书,都可以访问http://www.packtpub.com/support并注册,文件将直接以邮件方式发送给你。使用Docker镜像

Docker镜像包含了我们的应用和相应辅助其运行的组件,例如操作系统、运行环境、开发库等。它们被下载并部署到Docker宿主机上,以Docker容器的形式运行应用。本节将涉及在使用Docker镜像时需用到的几个命令:

• docker build

• docker images

• docker push

• docker pull本节中的相关材料在Docker文档网站上可以访问,网址是:https://docs.docker.com/userguide/dockerimages。编译Docker镜像我们将采用Docker Education小组的training/webapp项目的Dockerfile来创建一个Docker镜像。接下来的步骤将展示如何编译这个网络应用。

1.首先,我们将通过如下命令来克隆webapp的Git仓库(https://github.com/docker-training/webapp): dockerhost$ git clone https://github.com/docker-training/webapp.git training-webapp Cloning into 'training-webapp'... remote: Counting objects: 45, done. remote: Total 45 (delta 0), reused 0 (de..., pack-reused 45 Unpacking objects: 100% (45/45), done. Checking connectivity... done.

2.然后,在执行下面的命令之后,使用docker build来编译Docker镜像: dockerhost$ cd training-webapp dockerhost$ docker build -t hubuser/webapp . Sending build context to Docker daemon 121.3 kB Sending build context to Docker daemon Step 0 : FROM ubuntu:14.04 Repository ubuntu already being ... another client. Waiting. ---> 6d4946999d4f Step 1 : MAINTAINER Docker Education Team ---> Running in 0fd24c915568 ---> e835d0c77b04 Removing intermediate container 0fd24c915568 Step 2 : RUN apt-get update ---> Running in 45b654e66939 Ign http://archive.ubuntu.com trusty InRelease ... Removing intermediate container c08be35b1529 Step 9 : CMD python app.py ---> Running in 48632c5fa300 ---> 55850135bada Removing intermediate container 48632c5fa300 Successfully built 55850135bada-t标签用于给镜像做标签,标记为hubuser/webapp。用/给容器做标签对于推送Docker镜像是一个重要的考虑,这部分内容将在后续章节中介绍。关于docker build命令的更多细节可以查阅相关文档,网址是:https://docs.docker.com/reference/commandline/ build或者运行docke build--help命令。

3.最后,来确认一下镜像已经在我们的Docker宿主机中,使用docker images命令即可: dockerhost$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE hubuser/webapp latest 55850135 5 minutes ago 360 MB Ubuntu 14.04 6d494699 3 weeks ago 188.3 MB推送Docker镜像到资源库

我们已经制作完Docker镜像,接下来将其推送到资源库中,用于共享和在其他Docker宿主机上部署。Docker的默认配置是将镜像推送到Docker Hub。Docker Hub是Docker公司管理的一个开放资源库,任何人都可以推送和共享他们的镜像到这个资源库里。将镜像推送到资源库的步骤如下所示。

1.在推送到Docker Hub之前,需要用docker login命令来获得授权,命令如下: dockerhost$ docker login Username: hubuser Password: ******** Email: hubuser@hubemail.com WARNING: login credentials saved in /home/hubuser/.dockercfg. Login Succeeded2.现在可以推送镜像到Docker Hub了。如前一节中所述,标签/ 用于区分资源库中的镜像。通过docker push命令将镜像推送到Docker Hub中,执行情况如下:如果还没有Docker Hub的账号,可 dockerhost$ docker push 以参照说明去注册一个账号,网址hubuser/webapp是:https://hub.docker.com/account/signup。 The push refers to a repository [hubuser/webapp] (len: 1) Sending image list Pushing repository hubuser/webapp (1 tags) 428b411c28f0: Image already pushed, skipping ... 7d04572a66ec: Image successfully pushed 55850135bada: Image successfully pushed latest: digest: sha256:b00a3d4e703b5f9571ad6a... size: 2745

我们已经成功将Docker镜像推送到Docker Hub,它可以在Docker Hub中被搜索到。同样,我们可以在该镜像的Docker Hub的页面中获得更多关于它的信息。在本例中,该镜像的Docker Hub的网址是https://hub.docker.com/r/hubuser/webapp,如下图所示。更多关于推送Docker镜像到资源库的信息可以通过执行docker push--help查看,或者参阅官方文档,网址是:https://docs.docker.com/reference/commandline/push。尽管Docker Hub是一个存放Docker镜像的很好的地方,但很多情况下我们希望自己管理镜像资源库。例如,当我们希望节省拉取镜像到Docker宿主机的带宽时。另外一个原因可能是我们的Docker宿主机在数据中心内部,而防火墙阻断了其与互联网的连接。在第2章中,我们将进一步讨论如何运行自己的Docker registry,以便拥有一个私有的Docker镜像资源库。从资源库中拉取Docker镜像

一旦Docker镜像编译完成并推送到资源库中,如Docker Hub,我们就可以拉取镜像到宿主机上。当在开发环境的Docker宿主机上编译完Docker镜像,并希望在云端生产环境中的宿主机上部署时,这个工作流是非常有用的。这避免了在其他Docker宿主机上重新编译镜像。拉取镜像同样被用于从Docker Hub获取其他现有Docker镜像来构建我们自己的Docker镜像。于是,相对于之前做过的克隆Git资源库并在另外的Docker宿主机上重新编译,我们可以直接拉取它。拉取hubuser/webapp这个Docker镜像的操作如下所示。

1.首先,清空现有Docker宿主机的镜像,以确保我们将从Docker Hub中拉取该镜像,操作如下: dockerhost$ dockerhost rmi hubuser/webapp

2.然后,我们将通过docker pull来下载这个镜像,操作如下: dockerhost$ docker pull hubuser/webapp latest: Pulling from hubuser/webapp e9e06b06e14c: Pull complete ... b37deb56df95: Pull complete 02a8815912ca: Already exists Digest: sha256:06e9c1983bd6d5db5fba376ccd63bfa529e8d02f23d5 Status: Downloaded newer image for hubuser/webapp:latest

3.最后,确认已经成功下载到该镜像,操作如下: dockerhost$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE ubuntu 14.04 6d494699 3 weeks ago 188.3 MB hubuser/webapp latest 2a8815ca 7 weeks ago 348.8 MB关于拉取Docker镜像的更多细节,可以通过执行docker pull--help命令或者查阅官方文档,网址是:https://docs.docker.com/reference/commandline/pull。运行Docker容器

由于已经拉取或者编译了Docker镜像,我们可以通过docker run命令运行并测试它们。本节将使用如下Docker命令来获得Docker宿主机上正在运行的容器的更多信息,并且它们将贯穿在接下来的几个章节中,命令如下:

• docker ps

• docker inspect全部命令的更详细信息可以通过执行docker run--help命令或查阅官方文档,网址是:https://docs.docker.com/reference/commandline/run。暴露容器端口在training/webapp 例子中,它的Docker容器是作为一个Web服务器运行的。为了使它可以服务容器外的访问,Docker需要知道该应用的绑定端口。Docker将这种情况叫作暴露端口。本节将介绍在运行容器时如何暴露端口信息。

回到我们之前编译的training /webapp Docker镜像,该应用运行一个基于Python Flask的Web应用,它监听在5000端口,见webapp/app.py文件中的高亮显示部分: import os from flask import Flask app = Flask( name ) @app.route('/') def hello(): provider = str(os.environ.get('PROVIDER', 'world')) return 'Hello '+provider+'!' if __name_______== '__main__': # Bind to PORT if defined, otherwise default to 5000. port = int(os.environ.get('PORT', 5000)) app.run(host='0.0.0.0', port=port)

相应的,通过Dockerfile中的EXPOSE指令,Docker镜像使得Docker宿主机了解到应用监听在5000端口,具体示例如下: FROM ubuntu:14.04 MAINTAINER Docker Education Team RUN apt-get update RUN DEBIAN_FRONTEND=noninteractive apt-get \ install -y -q python-all python-pip ADD ./webapp/requirements.txt /tmp/requirements.txt RUN pip install -qr /tmp/requirements.txt ADD ./webapp /opt/webapp/ WORKDIR /opt/webapp EXPOSE 5000 CMD ["python", "app.py"]

既然我们已经掌握了Docker如何暴露容器的端口,那么接下来用如下命令来运行hubuser/webapp容器:

1.运行带–d标签的docker run命令来启动容器作为一个后台进程,操作如下: dockerhost$ docker run --name ourapp -d hubuser/webapp

2.通过docker ps命令确认运行的容器暴露了5000端口给Docker宿主机,操作如下: dockerhost:~/training-webapp$ docker ps CONTAINER ID IMAGE ... STATUS PORTS NAMES df3e6b788fd8 hubuser... Up 4 seconds 5000/tcp ourapp

除了EXPOSE的说明,已暴露的端口可以在运行时通过--expose=[]标签进行覆盖。例如,使用如下命令可以让hubuser/webapp应用暴露端口4000-4500: dockerhost$ docker run -d --expose=4000-4500 \ --name app hubuser/webapp dockerhost $ docker ps CONTAINER ID IMAGE ... PORTS NAMES ca4dc1da26d hubuser/webapp:latest ... 4000-4500/tcp,5000/tcp app df3e6b788fd8 hubuser/webapp:l... 5000/tcp ourap

这个特殊的docker run标志调试应用时十分有用。例如,假定我们的Web应用使用端口4000-4500,但是,我们并不希望这些端口在生产环境中都是可访问的,那么可以用--expose=[]临时启动一个用于调试的容器。更多关于如何使用这类技巧来解决Docker容器的问题,将在第7章中进行详细介绍。发布容器端口

暴露端口只是使该端口在容器内可用,而对于那些服务Docker宿主机以外的应用,它们的端口则需要被发布出去。docker run命令提供-P和-p标签来发布容器内暴露的端口。本节将介绍如何使用这两个标签发布端口到Docker宿主机。--publish-all

-P或者--publish-all标签发布容器内所有已暴露的端口到Docker宿主机上的随机高位端口,具体端口范围定义在/proc/sys/net/ipv4/ip_local_port_range中。下面的步骤将继续使用hubuser/webapp的Docker镜像,并发布其暴露的端口。

1.首先,执行下面的命令运行容器,并发布所有已暴露的端口: dockerhost$ docker run -P –d --name exposed hubuser/webapp

2.接下来,确认Docker宿主机发布端口32771将访问请求转向Docker容器暴露的5000端口。输入docker ps命令来确认结果,操作如下: dockerhost$ docker ps CONTAINER ID IMAGE ... PORTS NAMES 508cf1fb3e5 hubuser/webapp:latest ... 0.0.0.0:32771->5000/tcp exposed

3.同样可以验证分配的端口32771在Docker宿主机配置的临时端口范围之内: dockerhost$ cat /proc/sys/net/ipv4/ip_local_port_range 32768 61000

4.此外,可以通过如下命令来确认Docker宿主机确实监听在分配的32771端口上: dockerhost$ ss -lt 'sport = *:32771' State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 :::32771 :::*

5.最后,可以验证Docker宿主机的32771端口确实被映射到运行的Docker容器中,方法是通过确认HTTP请求的返回信息是training/webapp这个Python应用的结果,操作如下: $ curl http://dockerhost:32771 Hello world!--publish

-p或者--publish标签可将容器端口发布到Docker宿主机。如果容器的端口没有主动暴露,那么这个容器也可以被暴露端口。根据文档说明,-p标签可以采用如下格式发布容器端口:

• containerPort

• hostPort:containerPort

• ip::containerPort

• ip:hostPort:containerPort

通过指明hostPort,可以指定映射到Docker宿主机上的某个端口而不是随机分配一个临时端口。通过指明ip,可以限定从某个Docker宿主机的网络接口接收连接并返回相应数据包给映射的Docker容器端口。回到hubuser/webapp这个例子,通过如下命令将映射这个Python应用暴露的5000端口到Docker宿主机回环网络接口的80端口: $ ssh dockerhost dockerhost$ docker run -d -p 127.0.0.1:80:5000 training/webapp dockerhost$ curl http://localhost Hello world! dockerhost$ exit logout Connection to dockerhost closed. $ curl http://dockerhost curl: (7) Failed connect to dockerhost:80; Connection refused

在调用docker run命令之后,Docker宿主机应用只能通过http://localhost响应HTTP请求。链接容器

上一节介绍的端口发布功能只允许容器间通过Docker宿主机的端口进行通信。另一种直接链接容器的方法是使用容器链接功能。容器链接在一起后,可以使源容器向目标容器发送消息,并且使通信中的容器以一种更安全的方式进行相互发现。更多关于链接容器的内容可以在Docker文档网站上查阅,地址是:https://docs.docker.com/userguide/dockerlinks。在本节中,我们将使用--link标签来安全地链接容器。接下来通过一个例子演示如何链接容器,具体步骤如下。

1.首先,确认hubuser/webapp容器运行时仅暴露了指定端口。我们将创建一个容器,叫作source,作为源容器。创建源容器的操作如下: dockerhost$ docker run --name source –d hubuser/webapp

2.接下来,我们将创建一个目标容器。使用--link: 创建一个链接,从source源容器指向webapp目标容器,操作如下: dockerhost$ docker run -d --link source:webapp \ --name destination busybox /bin/ping webapp

3.最后,现在通过检查新创建的目标容器destination来确认这个链接已经被建立,操作如下: dockerhost$ docker inspect -f "{{ .HostConfig.Links }}" \ destination [/source:/destination/webapp]

在链接过程中到底发生了什么呢?Docker宿主机在两个容器间创建了一个安全通道。我们可以通过Docker宿主机的iptable来确认,操作如下: dockerhost$ docker inspect -f "{{ .NetworkSettings.IPAddress }}" \ source 172.17.0.15 dockerhost$ docker inspect -f "{{ .NetworkSettings.IPAddress }}" \ destination 172.17.0.28 dockerhost$ iptables -L DOCKER Chain DOCKER (1 references) target prot opt source destination ACCEPT tcp -- 172.17.0.28 172.17.0.15 tcp dpt:5000 ACCEPT tcp -- 172.17.0.15 172.17.0.28 tcp spt:5000

在前一个iptable中,Docker宿主机允许目标容器destination(172.17.0.28)访问源容器source(172.17.0.28)的5000端口的向外链接。第二个iptable的内容显示允许源容器source接收从destination容器向其5000端口发出的链接。

除了Docker宿主机在两个容器间建立安全链接,Docker宿主机通过如下两种形式暴露了源容器的信息给目标容器:

• 环境变量

• /etc/hosts中的条目

这两种信息源将在下一节中作为容器交互的例子进一步进行分析。交互式容器

通过指定-i标签,可以使一个容器在前台运行,并接到标准输入流上。通过与-t标签联合使用,可以给容器添加一个虚拟的终端。利用这两者,我们可以使Docker容器像一个可交互的进程,类似于普通的shell窗口。这个特性在需要调试或者检查Docker容器内部时非常有用。接着上一节的例子,我们将调试当容器被链接到一起时它们内部发生了什么变化,步骤如下。

1.首先,建立一个可交互的容器会话并链接到之前运行的source源容器,操作如下: dockerhost$ docker run -i -t --link source:webapp \ --name interactive_container \ busybox /bin/sh / #

2.接下来,我们检查暴露给目标容器的环境变量,操作如下: / # env | grep WEBAPP WEBAPP_NAME=/interactive_container/webapp WEBAPP_PORT_5000_TCP_ADDR=172.17.0.15 WEBAPP_PORT_5000_TCP_PORT=5000 WEBAPP_PORT_5000_TCP_PROTO=tcp WEBAPP_PORT_5000_TCP=tcp://172.17.0.15:5000 WEBAPP_PORT=tcp://172.17.0.15:5000一般来说,相互链接的容器间会创建如下环境变量。• 对每个容器均有:_NAME=/container_name/ alias_name• 对每个已暴露端口的URL都有:_PORT__。同时,它作为唯一前缀被添加到如下环境变量中。• _ADDR:源容器的IP地址• _PORT:已暴露的端口• _PROTO:已暴露的端口采用的协议,TCP或UDP• _PORT:源容器暴露的第一个端口

3.然后,在相互链接的容器中,目标容器的发现特性是/etc/hosts文件。webapp容器的别名被映射到源容器的IP地址同时,源容器的名字也被映射到相同的IP地址。下面的代码片段来自于可交互容器会话中的/etc/hosts文件,它包含如下映射: 172.17.0.29 d4509e3da954 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 172.17.0.15 webapp 85173b8686fc source

4.最后,我们使用别名来链接源容器。在接下来的例子中,将通过创建一个HTTP请求到源容器的别名webapp,实现访问源容器中运行的Web应用,操作如下: / # nc webapp 5000 GET / Hello world! / #配合docker commit命令,容器交互同样可以用于创建容器。但是,这是一个枯燥的过程并且这种开发过程的规模局限于单个开发者。因此,请使用docker build命令,并用版本控制工具来管理Dockerfile。小结

希望通过本章可让大家熟悉本书中将使用的大部分命令。首先准备一个可以进行Docker容器交互的Docker宿主机。然后,编译、下载、上传各式各样的Docker镜像,用于开发和部署容器到开发环境和生产环境。最后,通过编译或者下载Docker镜像来运行Docker容器。此外,我们还介绍了如何与运行中的容器进行通信和交互操作。

在下一章中,你将学会如何优化Docker镜像。那么,继续努力吧!2 优化Docker镜像

既然我们已经编译并部署了Docker容器,那么就可以开始享受它带来的便利了。统一标准的包格式方便开发者和系统管理员相互配合,从而简化了管理应用代码的工作。Docker的容器格式使我们可以快速迭代应用程序的版本并与其他人共享。开发、测试和部署时间得到了降低,这归功于Docker容器的轻量和启动速度。Docker容器的移植能力使我们可以将应用程序从物理服务器上拓展到云主机上。

但是,我们将发现它逐渐背离我们最初使用Docker的原因。由于经常需要下载应用程序对应的最新Docker镜像运行库,使得开发时间在逐渐增加。而Docker Hub的速度拖慢了部署的时间。更糟的是,Docker Hub有可能宕机,而我们就无法部署了。Docker镜像的尺寸已经达到GB级别了,对于如此大的镜像,一次简单的更新可能就要耗费一天。

本章将讨论那些Docker容器尺寸超出控制范围的场景,并针对这些问题给出了相应的解决方法,具体包括如下内容:

• 降低镜像部署时间

• 降低镜像编译时间

• 减小镜像的尺寸降低部署时间

随着时间的推移,我们构建的Docker容器的尺寸变得越来越大。在现有Docker宿主机中更新运行的容器是没有问题的:Docker会合理利用Docker镜像层,这些镜像层是随着我们应用的增长而创建的。但是,考虑下我们准备水平扩展应用的情况:这意味着要部署更多的Docker容器到额外的Docker宿主机。每一个新的Docker宿主机需要下载我们创建的所有镜像层。本节将介绍一个“大”Docker应用是如何影响在新Docker宿主机上的部署时间的。首先,我们来构建这个Docker应用问题场景,步骤如下。

1.编写Dockerfile创建一个“大”Docker镜像: FROM debian:jessie RUN dd if=/dev/urandom of=/largefile bs=1024 count=524288

2.接下来,编译这个Dockerfile,并附上标签hubuser/largeapp,操作如下: dockerhost$ docker build -t hubuser/largeapp.

3.检查这个Docker镜像的尺寸。从下面的输出中我们可以看到,它占用662MB空间,操作如下: dockerhost$ docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE hubuser/largeapp latest 450e3123 5 minutes ago 662 MB debian Jessie 9a61b6b1 4 days ago 125.2 MB

4.通过time命令记录它上传到Docker Hub和从Docker Hub拉取的时间,操作如下: dockerhost$ time docker push hubuser/largeapp The push refers to a repository [hubuser/largeapp] (len: 1) 450e319e42c3: Image already exists 9a61b6b1315e: Image successfully pushed 902b87aaaec9: Image successfully pushed Digest: sha256:18ef52e36996dd583f923673618483a4466aa2d1d0d6ce 9f0... real 11m34.133s user 0m0.164s sys 0m0.104s dockerhost$ time docker pull hubuser/largeapp latest: Pulling from hubuser/largeapp 902b87aaaec9: Pull complete 9a61b6b1315e: Pull complete 450e319e42c3: Already exists Digest: sha256:18ef52e36996dd583f923673618483a4466aa2d1d0d6ce 9f0... Status: Downloaded newer image for hubuser/largeapp:latest real 2m56.805s user 0m0.204s sys 0m0.188s

从上面高亮显示的时间值上可以看出,当执行docker push命令时,上传镜像到Docker Hub花费了大量时间。在部署过程中,docker pull拉取我们新建的镜像到生产环境的Docker宿主机同样花费了很长的时间。而上传和下载的时间长短依赖于Docker宿主机与Docker Hub之间的网络。如果Docker Hub停机,我们就不能根据需求部署新的Docker容器,也不能部署现有容器到额外的Docker宿主机。

为了利用Docker的快速分发特性和便捷部署能力,上传和下载镜像的稳定性是十分重要的。幸运的是,我们可以运行自己私有的Docker registry用于存储和分发Docker镜像,而不再依赖公共的Docker Hub服务。接下来介绍如何创建私有Docker registry,并确认它在性能方面的提升,操作如下所示。

1.通过下面的命令来运行私有Docker registry。它将运行在tcp://dockerhost:5000: dockerhost$ docker run -p 5000:5000 -d registry:2

2.接下来,让我们确认Docker镜像的部署速度已经被提升。首先,对先前创建的镜像加个标签,用于将它推送到本地Docker registry,操作如下: dockerhost$ docker tag hubuser/largeapp \ dockerhost:5000/largeapp

3.观察推送同样的Docker镜像到我们新建的Docker registry的速度有多快。测试显示,现在推送Docker镜像的速度至少是之前速度的10倍: dockerhost$ time docker push dockerhost:5000/largeapp The push refers to a ...[dockerhost:5000/largeapp] (len: 1) ... real 0m52.928s user 0m0.084s sys 0m0.048s

4.首先确保已经移除了之前编译的镜像,然后再测试从本地Docker registry拉取Docker镜像的性能。测试显示,拉取Docker镜像的速度是之前拉取速度的30倍: dockerhost$ docker rmi dockerhost:5000/largeapp \ hubuser/largeapp Untagged: dockerhost:5000/largeapp:latest Untagged: hubuser/largeapp:latestDeleted: 549d099c0edaef424edb6cfca8f16f5609b066ba744638990daf3b43... dockerhost$ time docker pull dockerhost:5000/largeapp latest: Pulling from dockerhost:5000/largeapp 549d099c0eda: Already exists 902b87aaaec9: Already exists 9a61b6b1315e: Already exists Digest: sha256:323bed623625b3647a6c678ee6840be23616edc357dbe07c5a0 c68b62dd52ecf Status: Downloaded newer image for dockerhost:5000/largeapp:latest real 0m10.444s user 0m0.160s sys 0m0.056s

性能提升如此之大的原因在于我们通过本地局域网络上传和拉取镜像,它节省了Docker宿主机的带宽,使得部署时间变短。最关键的是,我们的部署不再需要依赖Docker Hub。为了部署Docker镜像到其他的Docker宿主机,我们需要创建安全的Docker registry。关于创建它的详细内容已经超出了本书的讨论范围,更多关于创建Docker registry的细节可以在官方网站查阅,地址是:https://docs.docker.com/registry/deploying。改善镜像编译时间

Docker镜像是开发者工作时生成的主要构件。简化Docker文件和加速容器技术使得我们可以快速迭代应用开发。但是,一旦编译Docker镜像所需时间不受控制,那么使用Docker带来的好处就将逐渐消失。本节我们将讨论某些花费大量时间构建Docker镜像的案例。然后,我们将给出几种技巧来解决这些问题。采用registry镜像

构建镜像的时间有一大部分花费在获取上游镜像的过程。假定我们有如下所述的Dockerfile: FROM java:8u45-jre

它将下载和编译java:8u45-jre这个镜像。当使用另外一个Docker宿主机,或者java:8u45-jre这个镜像在Docker Hub上更新了,我们的编译时间将显著增加。配置一个本地的registry镜像将降低这类镜像的编译时间。当每个开发者都有自己的Docker宿主机时,这将是一个

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载