Docker进阶与实战(txt+pdf+epub+mobi电子书下载)


发布时间:2020-10-23 15:56:22

点击下载

作者:华为Docker实践小组

出版社:机械工业出版社

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

Docker进阶与实战

Docker进阶与实战试读:

前言

为什么要写这本书

在计算机技术日新月异的今天,Docker也算是其中异常璀璨的一员了。它的生态圈涉及内核、操作系统、虚拟化、云计算、DevOps等热门领域,受众群体也在不断扩大。

Docker在国内的发展如火如荼,短短一两年时间里就陆续出现了一批关于Docker的创业公司。华为公司作为国内开源领域的领导者,对Docker也有很大的投入,我们认为有必要把自己的知识积累和实践经验总结出来分享给广大开发者。除了吸引更多的人投入到Docker的生态建设以外,我们也希望通过本书帮助更多的读者更好、更快地掌握Docker关键技术。关于本书

目前市场已经有一些不错的Docker入门图书,但多侧重于入门和具体的应用,本书会介绍一些Docker关键技术原理和高级使用技巧,适合有一定基础的读者。另外,本书会对Docker涉及的各个模块、关系和原理进行系统梳理,帮助读者对Docker加深认识,更好地应用Docker部署生产环境,最大程度安全有效地发挥Docker的价值。

本书不仅适合一般的Docker用户,也适合Docker生态圈中的开发者,希望它可以成为一本Docker进阶的图书,帮助读者快速提升。

本书是由华为整个Docker团队合作完成的,笔者包括(排名不分先后):邓广兴、胡科平、胡欣蔚、黄强、雷继棠、李泽帆、凌发科、刘华、孙远、谢可杨、杨书奎、张伟、张文涛、邹钰。本书的内容

本书的定位是有一定Docker基础的读者,所以在基本的概念和使用上,我们不会花过多的篇幅讲解,而是给出相应有价值的链接,作为读者的延伸阅读。

在内容上,除了对Docker进行系统的梳理外,同时还会对Docker背后的核心技术(即容器技术)及其历史进行介绍,进一步帮助读者更好地理解Docker。

章节划分则以功能模块为粒度,对每一个重要的模块进行了深入分析和讲解,同时也为热门领域单独开辟了章节。在每一章的最后都会讲解一些高级用法、使用技巧或实际应用中遇到的问题。虽然各章节的内容相对独立,但也会有一些穿插的介绍和补充,以帮助读者融会贯通,系统深入地理解Docker的每一个细节。

另外,本书的笔者都是一线的开发者和Docker社区活跃的贡献者,因此书中还专门准备了一个章节来介绍参与Docker开发的流程和经验。同时,伴随Docker的发展,Docker生态圈也在不断扩大并吸引了越来越多的人的关注。Docker集群管理和生态圈的介绍也将作为本书重点章节详细讲解。此外,Docker测试也是比较有特色的内容,分享了笔者在测试方面的经验。最后,附录中所包含的常用的Docker相关信息,可供读者需要时查询。

本书的内容和代码都是基于Docker 1.8版本的。在代码示例中,使用“#”开头的命令表示以root用户执行,以“$”开头的命令表示以普通用户执行。勘误和支持

由于笔者水平有限,编写的时间也很仓促,书中难免会出现一些错误或者不准确的地方,恳请读者批评指正。读者可以把书中发现的问题或建议发送到邮箱docker@huawei.com,我们会尽快回复大家的疑问,并把收集的信息整理修正。致谢

本书是由整个Docker团队协作完成的,由于繁忙的工作书稿撰写几度中止。感谢我们的项目经理裴斐月女士,正是她的整体协调和督促,以及与出版社的大量沟通,才促成了本书的出版。感谢李泽帆,他不仅参与了本书的写作,而且承担了全书的审读工作,给出了大量有价值的建议。还要感谢Stephen Li、陈佳波、杨开封、胡欣蔚和张殿芳,以及其他华为公司主管对我们写书的大力支持,感谢机械工业出版社的编辑耐心专业的指导和审核。最后,感谢我们每一位家人的支持陪伴,我们的工作因为有了家人的支持和期待才变得更有意义。华为Docker实践小组2015年11月第1章 Docker简介1.1 引言1.1.1 Docker的历史和发展

自从2013年年初一个叫dotCloud的PaaS服务供应商将一个内部项目Docker开源之后,这个名字在短短几年内就迅速成为一个热词。似乎一夜之间,人人都开始谈论Docker,以至于这家公司干脆出售了其所持有的PaaS平台业务,并且改名为Docker.Inc,从而专注于Docker的开发和推广。

对于Docker,目前的定义是一个开源的容器引擎,可以方便地对容器(关于容器,将在第2章详细介绍)进行管理。其对镜像的打包封装,以及引入的Docker Registry对镜像的统一管理,构建了方便快捷的“Build,Ship and Run”流程,它可以统一整个开发、测试和部署的环境和流程,极大地减少运维成本。另外,得益于容器技术带来的轻量级虚拟化,以及Docker在分层镜像应用上的创新,Docker在磁盘占用、性能和效率方面相较于传统的虚拟化都有非常明显的提高,所以理所当然,Docker开始不断蚕食传统虚拟化的市场。

随着Docker技术的迅速普及,Docker公司持续进行融资,并且其估值也在不断攀升,同时,Docker公司也在不断地完善Docker生态圈,这一切使得Docker正慢慢成为轻量级虚拟化的代名词。在可预见的未来,很可能需要不断地刷新对Docker的定义。

目前Docker已加入Linux基金会,遵循Apache 2.0协议,其代码托管于[Github](https://github.com/docker/docker)。1.1.2 Docker的架构介绍

要了解Docker,首先要看看它的架构图,如图1-1所示。图1-1 Docker架构图

从图1-1可知,Docker并没有传统虚拟化中的Hypervisor层。因为Docker是基于容器技术的轻量级虚拟化,相对于传统的虚拟化技术,省去了Hypervisor层的开销,而且其虚拟化技术是基于内核的Cgroup和Namespace技术,处理逻辑与内核深度融合,所以在很多方面,它的性能与物理机非常接近。

在通信上,Docker并不会直接与内核交互,它是通过一个更底层的工具Libcontainer与内核交互的。Libcontainer是真正意义上的容器引擎,它通过clone系统调用直接创建容器,通过pivot_root系统调用进入容器,且通过直接操作cgroupfs文件实现对资源的管控,而Docker本身则侧重于处理更上层的业务。提示

Libcontainer的详细介绍可参见第9章。

Docker的另一个优势是对层级镜像的创新应用,即不同的容器可以共享底层的只读镜像,通过写入自己特有的内容后添加新的镜像层,新增的镜像层和下层的镜像一起又可以作为基础镜像被更上层的镜像使用。这种特性可以极大地提高磁盘利用率,所以当你的系统上有10个大小为1GB的镜像时,它们总共占用的空间大小可能只有5GB,甚至更少。另外,Docker对Union mount的应用还体现在多个容器使用同一个基础镜像时,可极大地减少内存占用等方面,因为不同的容器访问同一个文件时,只会占用一份内存。当然这需要使用支持Union mount的文件系统作为存储的Graph Driver,比如AUFS和Overlay。1.2 功能和组件

Docker为了实现其所描述的酷炫功能,引入了以下核心概念:

·Docker客户端

·Docker daemon

·Docker容器

·Docker镜像

·Registry

下面就分别来简单地介绍一下。1.2.1 Docker客户端

Docker是一个典型的C/S架构的应用程序,但在发布上,Docker将客户端和服务器端统一在同一个二进制文件中,不过,这只是对于Linux系统而言的,在其他平台如Mac上,Docker只提供了客户端。

Docker客户端一般通过Docker command来发起请求,另外,也可以通过Docker提供的一整套RESTful API来发起请求,这种方式更多地被应用在应用程序的代码中。1.2.2 Docker daemon

Docker daemon也可以被理解成Docker Server,另外,人们也常常用Docker Engine来直接描述它,因为这实际上就是驱动整个Docker功能的核心引擎。

简单地说,Docker daemon实现的功能就是接收客户端发来的请求,并实现请求所要求的功能,同时针对请求返回相应的结果。在功能的实现上,因为涉及了容器、镜像、存储等多方面的内容,daemon内部的机制会复杂很多,涉及了多个模块的实现和交互。1.2.3 Docker容器

在Docker的功能和概念中,容器是一个核心内容,相对于传统虚拟化,它作为一项基础技术在性能上给Docker带来了极大优势。

在功能上,Docker通过Libcontainer实现对容器生命周期的管理、信息的设置和查询,以及监控和通信等功能。而容器也是对镜像的完美诠释,容器以镜像为基础,同时又为镜像提供了一个标准的和隔离的执行环境。

在概念上,容器则很好地诠释了Docker集装箱的理念,集装箱可以存放任何货物,可以通过邮轮将货物运输到世界各地。运输集装箱的邮轮和装载卸载集装箱的码头都不用关心集装箱里的货物,这是一种标准的集装和运输方式。类似的,Docker的容器就是“软件界的集装箱”,它可以安装任意的软件和库文件,做任意的运行环境配置。开发及运维人员在转移和部署应用的时候,不用关心容器里装了什么软件,也不用了解它们是如何配置的。而管理容器的Docker引擎同样不关心容器里的内容,它只要像码头工人一样让这个容器运行起来就可以了,就像所有其他容器那样。

容器不是一个新的概念,但是Docker在对容器进行封装后,与集装箱的概念对应起来了,它之所以被称为“软件界的创新和革命”,是因为它会改变软件的开发、部署形态,降低成本,提高效率。Docker真正把容器推广到了全世界。1.2.4 Docker镜像

与容器相对应,如果说容器提供了一个完整的、隔离的运行环境,那么镜像则是这个运行环境的静态体现,是一个还没有运行起来的“运行环境”。

相对于传统虚拟化中的ISO镜像,Docker镜像要轻量化很多,它只是一个可定制的rootfs。Docker镜像的另一个创新是它是层级的并且是可复用的,这在实际应用场景中极为有用,多数基于相同发行版的镜像,在大多数文件的内容上都是一样的,基于此,当然会希望可以复用它们,而Docker做到了。在此类应用场景中,利用Unionfs的特性,Docker会极大地减少磁盘和内存的开销。

Docker镜像通常是通过Dockerfile来创建的,Dockerfile提供了镜像内容的定制,同时也体现了层级关系的建立。另外Docker镜像也可以通过使用docker commit这样的命令来手动将修改后的内容生成镜像,这些都将在后续的章节详细介绍。1.2.5 Registry

在前面提到的镜像中,曾指出Docker通过容器集装箱可以很方便地转运软件,其实,Registry也在其中扮演了重要的角色。

Registry是一个存放镜像的仓库,它通常被部署在互联网服务器或者云端。通常,集装箱是需要通过邮轮经行海洋运输到世界各地的,而互联网时代的传输则要方便很多,在镜像的传输过程中,Registry就是这个传输的重要中转站。假如我们在公司将一个软件的运行环境制作成镜像,并上传到Registry中,这时就可以很方便地在家里的笔记本上,或者在客户的生产环境上直接从Registry上下载并运行了。当然,对Registry的操作也是与Docker完美融合的,用户甚至不需要知道Registry的存在,只需要通过简单的Docker命令就可以实现上面的操作。

Docker公司提供的官方Registry叫Docker Hub,这上面提供了大多数常用软件和发行版的官方镜像,还有无数个人用户提供的个人镜像。其实,Registry本身也是一个单独的开源项目,任何人都可以下载后自己部署一个Registry。因为免费的Docker Hub功能相对简单,所以多数企业会选择自己部署Docker Registry后二次开发,或者购买功能更强大的企业版Docker Hub。1.3 安装和使用1.3.1 Docker的安装

Docker的安装和使用有一些前提条件,主要体现在体系架构和内核的支持上。对于体系架构,除了Docker一开始就支持的x86-64,其他体系架构的支持则一直在不断地完善和推进中,用户在安装前需要到Docker官方网站查看最新的支持情况。对于内核,目前官方的建议是3.10以上的版本,除了内核版本以外,Docker对于内核支持的功能,即内核的配置选项也有一定的要求(比如必须开启Cgroup和Namespace相关选项,以及其他的网络和存储驱动等)。如果你使用的是主流的发行版,那通常它们都已经打开了,如果使用的是定制化的内核,Docker源码中提供了一个检测脚本(目前的路径是./contrib/check-config.sh)来检测和指导内核的配置。

在满足前提条件后,安装就非常的简单了,对于多数主流的发行版,通常只需要一条简单的命令即可完成安装,比如在Ubuntu下,可以使用如下命令安装:$ sudo apt-get install docker.io

当然,实际情况可能会相对复杂些,比如,虽然Ubuntu中通常自带了Docker,但用户常常需要使用最新版本的Docker,以至于不得不对其进行升级。对于安装和升级,以及不同发行版上的操作方法,官方网站上提供了更加详细的说明,本书不做过多的赘述,下面的链接给出了常用发行版的安装方法:

·[Ubuntu](http://docs.docker.com/installation/ubuntulinux/)

·[Fedora](http://docs.docker.com/installation/fedora/)

·[Debian](http://docs.docker.com/installation/debian/)

·[Centos](http://docs.docker.com/installation/centos/)

·[Gentoo](http://docs.docker.com/installation/gentoolinux/)

·[Arch Linux](http://docs.docker.com/installation/archlinux/)

·[Windows](http://docs.docker.com/installation/windows/)

·[Mac OS X](http://docs.docker.com/installation/mac/)

另外,用户也可以直接获取Docker binary来运行,http://docs.docker.com/installation/binaries/网址介绍了获取的方法。虽然这样更简单,但还是推荐使用完整安装的方式,因为通过软件包安装的Docker,除了有可执行文件之外,还包括了Shell自动完成脚本、man手册、服务运行和配置脚本等内容,可以帮助用户更好地配置和使用Docker。提示

Docker还有一些其他更方便的安装方式,这将在后面的章节中详细介绍。1.3.2 Docker的使用

对于Docker的使用,可以花整本书来介绍其中的各种细节、使用技巧和实战经验等,本节更希望告诉读者学习使用的方法,而对于使用技巧和实战经验会在本书的其他部分贯穿说明。

对于初学者,官方提供的[tryit](https://www.docker.com/tryit/)是最好的快速入门途径,建议每一个初次接触Docker的用户都可以试一试。对于有一定经验的用户,在使用中遇到问题或者不确定具体的用法时,可以通过以下途径来查看帮助信息。

1)在控制台直接运行docker,这样会列出Docker支持的所有命令和一些通用的参数,如下:$ dockerUsage: docker [OPTIONS] COMMAND [arg...] docker daemon [ --help | ... ] docker [ -h | --help | -v | --version ]A self-sufficient runtime for containers.Options: --config=~/.docker Location of client config files -D, --debug=false Enable debug mode -H, --host=[] Daemon socket(s) to connect to -h, --help=false Print usage -l, --log-level=info Set the logging level --tls=false Use TLS; implied by --tlsverify --tlscacert=~/.docker/ca.pem Trust certs signed only by this CA --tlscert=~/.docker/cert.pem Path to TLS certificate file --tlskey=~/.docker/key.pem Path to TLS key file --tlsverify=false Use TLS and verify the remote -v, --version=false Print version information and quitCommands: attach Attach to a running container build Build an image from a Dockerfile commit Create a new image from a container's changes cp Copy files/folders from a container to a HOSTDIR or to STDOUT create Create a new container diff Inspect changes on a container's filesystem events Get real time events from the server exec Run a command in a running container export Export a container's filesystem as a tar archive history Show the history of an image images List images import Import the contents from a tarball to create a filesystem image info Display system-wide information inspect Return low-level information on a container or image kill Kill a running container load Load an image from a tar archive or STDIN login Register or log in to a Docker registry logout Log out from a Docker registry logs Fetch the logs of a container pause Pause all processes within a container port List port mappings or a specific mapping for the CONTAINER ps List containers pull Pull an image or a repository from a registry push Push an image or a repository to a registry rename Rename a container restart Restart a running container rm Remove one or more containers rmi Remove one or more images run Run a command in a new container save Save an image(s) to a tar archive search Search the Docker Hub for images start Start one or more stopped containers stats Display a live stream of container(s) resource usage statistics stop Stop a running container tag Tag an image into a repository top Display the running processes of a container unpause Unpause all processes within a container version Show the Docker version information wait Block until a container stops, then print its exit codeRun 'docker COMMAND --help' for more information on a command.

2)在控制台执行“docker+命令+--help”,比如“docker start--help”,这样会列出docker start命令支持的所有参数,如下:–$ docker start -helpUsage: docker start [OPTIONS] CONTAINER [CONTAINER...]Start one or more stopped containers -a, --attach=false Attach STDOUT/STDERR and forward signals --help=false Print usage -i, --interactive=false Attach container's STDIN

3)使用man命令查看帮助文档。对于通过rpm包等方式安装的Docker,一般都会默认安装对应的man文档,此时可通过“man+docker+command”的方式查看子命令的帮助文档,比如“man docker start”,通常man手册中包含的帮助信息会更丰富一些,通过完整地阅读man手册,基本上就可以掌握该命令的常规用法。DOCKER(1) JUNE 2014 DOCKER(1)NAME docker-start - Start one or more stopped containersSYNOPSIS docker start [-a|--attach[=false]] [--help] [-i|--interactive[=false]] CONTAINER [CONTAINER...]DESCRIPTION Start one or more stopped containers.OPTIONS -a, --attach=true|false Attach container's STDOUT and STDERR and forward all signals to the process. The default is false. --help Print usage statement -i, --interactive=true|false Attach container's STDIN. The default is false.See also docker-stop(1) to stop a running container.HISTORY April 2014, Originally compiled by William Henry (whenry at redhat dot com) based on docker.com source material and internal work. June 2014, updated by Sven Dowideit (SvenDowideit@home.org.au)1.4 概念澄清

本书的附录A是关于Docker常见问题的解答,但对于Docker基本概念方面的问题,希望读者可以在阅读完本章后就有清晰的认识,所以本节会针对与Docker概念息息相关的几个常见问题进行说明。1.4.1 Docker在LXC基础上做了什么工作

首先我们需要明确LXC的概念,但这常常有不同的认识。LXC目前代表两种含义:

·LXC用户态工具(https://github.com/lxc/lxc)。

·Linux Container,即内核容器技术的简称。

这里通常指第二种,即Docker在内核容器技术的基础上做了什么工作。简单地说,Docker在内核容器技术(Cgroup和Namespace)的基础上,提供了一个更高层的控制工具,该工具包含以下特性。

·跨主机部署。Docker定义了镜像格式,该格式会将应用程序和其所依赖的文件打包到同一个镜像文件中,从而使其在转移到任何运行Docker的机器中时都可以运行,并能够保证在任何机器中该应用程序执行的环境都是一样的。LXC实现了“进程沙盒”,这一重要特性是跨主机部署的前提条件,但是只有这一点远远不够,比如,在一个特定的LXC配置下执行应用程序,将该应用程序打包并拷贝到另一个LXC环境下,程序很有可能无法正常执行,因为该程序的执行依赖于该机器的特定配置,包括网络、存储、发行版等。而Docker则将上述相关配置进行抽象并与应用程序一同打包,所以可以保证在不同硬件、不同配置的机器上Docker容器中运行的程序和其所依赖的环境及配置是一致的。

·以应用为中心。Docker为简化应用程序的部署过程做了很多优化,这一目的在Docker的API、用户接口、设计哲学和用户文档中都有体现,其Dockerfile机制大大简化和规范了应用的部署方法。

·自动构建。Docker提供了一套能够从源码自动构建镜像的工具。该工具可以灵活地使用make、maven、chef、puppet、salt、debian包、RPM和源码包等形式,将应用程序的依赖、构建工具和安装包进行打包处理,而且当前机器的配置不会影响镜像的构建过程。

·版本管理。Docker提供了类似于Git的版本管理功能,支持追踪镜像版本、检验版本更新、提交新的版本改动和回退版本等功能。镜像的版本信息中包括制作方式和制作者信息,因此可以从生产环境中回溯到上游开发人员。Docker同样实现了镜像的增量上传下载功能,用户可以通过获取新旧版本之间新增的镜像层来更新镜像版本,而不必下载完整镜像,类似Git中的pull命令。

·组件重用。任何容器都可以用作生成另一个组件的基础镜像。这一过程可以手动执行,也可以写入自动化构建的脚本。例如,可以创建一个包含Python开发环境的镜像,并将其作为基础镜像部署其他使用Python环境进行开发的应用程序。

·共享。Docker用户可以访问公共的镜像Registry,并向其中上传有用的镜像。Registry中同样包含由Docker公司维护的一些官方标准镜像。Docker Registry本身也是开源的,所以任何人都可以部署自己的Registry来存储并共享私有镜像。

·工具生态链。Docker定义了一系列API来定制容器的创建和部署过程并实现自动化。有很多工具能够与Docker集成并扩展Docker的能力,包括类PaaS部署工具(Dokku、Deis和Flynn)、多节点编排工具(Maestro、Salt、Mesos、OpenStack nova)、管理面板(Docker-ui、OpenStack Horizon、Shipyard)、配置管理工具(Chef、Puppet)、持续集成工具(Jenkins、Strider、Travis)等。Docker正在建立以容器为基础的工具集标准。1.4.2 Docker容器和虚拟机之间有什么不同

容器与虚拟机是互补的。虚拟机是用来进行硬件资源划分的完美解决方案,它利用了硬件虚拟化技术,例如VT-x、AMD-V或者privilege level(权限等级)会同时通过一个hypervisor层来实现对资源的彻底隔离;而容器则是操作系统级别的虚拟化,利用的是内核的Cgroup和Namespace特性,此功能完全通过软件来实现,仅仅是进程本身就可以与其他进程隔离开,不需要任何辅助。

Docker容器与主机共享操作系统内核,不同的容器之间可以共享部分系统资源,因此容器更加轻量级,消耗的资源也更少。而虚拟机会独占分配给自己的资源,几乎不存在资源共享,各个虚拟机实例之间近乎完全隔离,因此虚拟机更加重量级,也会消耗更多的资源。我们可以很轻松地在一台普通的Linux机器上运行100个或者更多的Docker容器,而且不会占用太多系统资源(如果容器中没有执行运算任务或I/O操作);而在单台机器上不可能创建100台虚拟机,因为每一个虚拟机实例都会占用一个完整的操作系统所需要的所有资源。另外,Docker容器启动很快,通常是秒级甚至是毫秒级启动。而虚拟机的启动虽然会快于物理机器,但是启动时间也是在数秒至数十秒的量级。

因此,可以根据需求的不同选择相应的隔离方式。如果需要资源完全隔离并且不考虑资源消耗,可以选择使用虚拟机;而若是想隔离进程并且需要运行大量进程实例,则应该选择Docker容器。1.5 本章小结

本章对Docker的基本概念、组成和使用方法做了介绍,使读者对Docker有一个整体的认识,后续的章节会对本章提到的内容展开更详细的讲解,让读者对Docker有全面且细致的理解。第2章 关于容器技术

在第1章对Docker的介绍中,已经知道容器技术是Docker的一项基础技术,而在当前对Docker的火热讨论中,容器也时常跟Docker一起被提及。作为Docker的进阶书籍,有必要对容器技术做一些探讨,以深刻理解Docker与相关技术之间的关联。2.1 容器技术的前世今生2.1.1 关于容器技术

容器技术,又称为容器虚拟化,从字面上看它首先是一种虚拟化技术。在如今的技术浪潮下,虚拟化技术层出不穷,包括硬件虚拟化、半虚拟化、操作系统虚拟化等。本书不会对虚拟化技术展开介绍,只需要知道容器虚拟化是一种操作系统虚拟化,是属于轻量级的虚拟化技术即可。又因为在实现原理上,每一种虚拟化技术之间都有较大的差别,所以即使没有虚拟化的技术背景,也是可以单独来学习容器虚拟化的。

容器技术之所以受欢迎,一个重要的原因是它已经集成到了Linux内核中,已经被当作Linux内核原生提供的特性。当然在其他平台上也有相应的容器技术,但本书讨论的以及Docker涉及的都是指Linux平台的容器技术。

对于容器,目前并没有一个严格的定义,但普遍认可的说法是,它首先必须是一个相对独立的运行环境,在这一点上,有点类似虚拟机的概念,但又没有虚拟机那样彻底。另外,在一个容器环境内,应该最小化其对外界的影响,比如不能在容器中把host上的资源全部消耗掉,这就是资源控制。

一般来说,容器技术主要包括Namespace和Cgroup这两个内核特性。

·Namespace又称为命名空间(也可翻译为名字空间),它主要做访问隔离。其原理是针对一类资源进行抽象,并将其封装在一起提供给一个容器使用,对于这类资源,因为每个容器都有自己的抽象,而它们彼此之间是不可见的,所以就可以做到访问隔离。

·Cgroup是control group的简称,又称为控制组,它主要是做资源控制。其原理是将一组进程放在一个控制组里,通过给这个控制组分配指定的可用资源,达到控制这一组进程可用资源的目的。

实际上,Namespace和Cgroup并不是强相关的两种技术,用户可以根据需要单独使用它们,比如单独使用Cgroup做资源控制,就是一种比较常见的做法。而如果把它们应用到一起,在一个Namespace中的进程恰好又在一个Cgroup中,那么这些进程就既有访问隔离,又有资源控制,符合容器的特性,这样就创建了一个容器。

对于Namespace和Cgroup,后面的章节会做详细的介绍。2.1.2 容器技术的历史

上文提到容器技术属于一种操作系统虚拟化,事实上,其最早的原型可以简化为对目录结构的简单抽象,如图2-1所示。图2-1 容器技术最早原型

图2-1所示为在普通的目录结构中创建一个完整的子目录结构。这种抽象化目录结构的出现最早源于1982年,那时通过chroot技术把用户的文件系统根目录切换到某个指定的目录下,实现了简单的文件系统视图上的抽象或虚拟化。但是这种技术只是提供了有限的文件系统隔离,并没有任何其他隔离手段,而且人们后来发现这种技术并不安全,用户可以逃离设定的根目录从而访问host上的文件。

针对上面提到的安全性问题,在2000年,内核版本2.3.41引入了pivot_root技术,它可以有效地避免chroot带来的安全性问题。今日的容器技术,比如LXC、Docker等,也都是使用了pivot_root来做根文件系统的切换。然而pivot_root也仅仅是在文件系统的隔离上做了一些增强,并没有在其他隔离性上有所提高。

同样在2000年左右,市场上出现了一些商用的容器技术,比如Linux-VServer和SWsoft(现在的Odin)开发的Virtuozzo,虽然这些技术相对当时的XEN和KVM,有明显的性能提升,但是因为各种原因,并未在当时引起市场太多的关注。注意

这里只讨论Linux系统上的容器技术,同时期还有很多有名的非Linux平台的容器技术,比如FreeBSD的jail、Solaris上的Zone等。

到了2005年,同样是Odin公司,在Virtuozzo的基础上发布了OpenVZ技术,同时开始推动OpenVZ中的核心容器技术进入Linux内核主线,而此时IBM等公司也在推动类似的技术,最后在社区的合作下,形成了目前大家看到的Cgroup和Namespace,这时,容器技术才开始逐渐进入大众的视野。

对于Namespace,其各个子系统进入内核的版本号及贡献公司如表2-1所示。表2-1 Namespace内核版本支持说明

User Namespace在3.8版本重新实现。

对于Cgroup,其各个子系统进入内核的版本号及贡献公司如表2-2所示。表2-2 Cgroup内核版本支持注意

以上只列举了早期主要的子系统,较新的子系统如net cls、hugetlb等并未列出。

整个容器的发展历史可以通过图2-2来展示。图2-2 容器发展历史

随着容器技术在内核主线中的不断成熟和完善,2013年诞生的Docker真正让容器技术得到了全世界技术公司和开发人员的关注,相信容器技术的未来一定会比它的前世和今生更加精彩。2.2 一分钟理解容器2.2.1 容器的组成

上文已多次提及,容器的核心技术是Cgroup+Namespace,但光有这两个抽象的技术概念是无法组成一个完整的容器的。在2.1.2节也提到过最早的容器概念就包括了对文件目录视图的抽象隔离,而所有的这一切,都需要有工具来驱动,需要有一个工具来提供用户可操作的接口,来创建一个容器。所以笔者认为,对于Linux容器的最小组成,可以由以下公式来表示:

容器=cgroup+namespace+rootfs+容器引擎(用户态工具)

其中各项的功能分别为:

·Cgroup:资源控制。

·Namespace:访问隔离。

·rootfs:文件系统隔离。

·容器引擎:生命周期控制。

目前市场上所有Linux容器项目都包含以上组件。2.2.2 容器的创建原理

至此对容器的描述还一直停留在文件和概念的层面,本小节将通过简单的代码抽象,清晰地展现容器的创建原理,使读者对容器有更深刻的理解。

代码一:pid = clone(fun, stack, flags, clone_arg);(flags: CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWNET | CLONE_NEWIPC | CLONE_NEWUTS |… )

代码二:echo $pid > /sys/fs/cgroup/cpu/tasksecho $pid > /sys/fs/cgroup/cpuset/tasksecho $pid > /sys/fs/cgroup/blkio/tasksecho $pid > /sys/fs/cgroup/memory/tasksecho $pid > /sys/fs/cgroup/devices/tasksecho $pid > /sys/fs/cgroup/freezer/tasks

代码三:fun(){… pivot_root("path_of_rootfs/", path);… exec("/bin/bash");… }

·对于代码一,通过clone系统调用,并传入各个Namespace对应的clone flag,创建了一个新的子进程,该进程拥有自己的Namespace。根据以上代码可知,该进程拥有自己的pid、mount、user、net、ipc、uts namespace。

·对于代码二,将代码一中产生的进程pid写入各个Cgroup子系统中,这样该进程就可以受到相应Cgroup子系统的控制。

·对于代码三,该fun函数由上面生成的新进程执行,在fun函数中,通过pivot_root系统调用,使进程进入一个新的rootfs,之后通过exec系统调用,在新的Namespace、Cgroup、rootfs中执行“/bin/bash”程序。

通过以上操作,成功地在一个“容器”中运行了一个bash程序。对于Cgroup和Namespace的技术细节,将在以下两节详细描述。2.3 Cgroup介绍2.3.1 Cgroup是什么

Cgroup是control group的简写,属于Linux内核提供的一个特性,用于限制和隔离一组进程对系统资源的使用,也就是做资源QoS,这些资源主要包括CPU、内存、block I/O和网络带宽。Cgroup从2.6.24开始进入内核主线,目前各大发行版都默认打开了Cgroup特性。

从实现的角度来看,Cgroup实现了一个通用的进程分组的框架,而不同资源的具体管理则是由各个Cgroup子系统实现的。截止到内核4.1版本,Cgroup中实现的子系统及其作用如下:

·devices:设备权限控制。

·cpuset:分配指定的CPU和内存节点。

·cpu:控制CPU占用率。

·cpuacct:统计CPU使用情况。

·memory:限制内存的使用上限。

·freezer:冻结(暂停)Cgroup中的进程。

·net_cls:配合tc(traffic controller)限制网络带宽。

·net_prio:设置进程的网络流量优先级。

·huge_tlb:限制HugeTLB的使用。

·perf_event:允许Perf工具基于Cgroup分组做性能监测。

在Cgroup出现之前,只能对一个进程做一些资源控制,例如通过sched_setaffinity系统调用限定一个进程的CPU亲和性,或者用ulimit限制一个进程的打开文件上限、栈大小等。另外,使用ulimit可以对少数资源基于用户做资源控制,例如限制一个用户能创建的进程数。而Cgroup可以对进程进行任意的分组,如何分组是用户自定义的,例如安卓的应用分为前台应用和后台应用,前台应用是直接跟用户交互的,需要响应速度快,因此前台应用对资源的申请需要得到更多的保证。为此安卓将前台应用和后台应用划分到不同的Cgroup中,并且对放置前台应用的Cgroup配置了较高的系统资源限额。提示

从1.6版本开始,Docker也支持ulimit,读者可以查阅相关Docker文档及Linux用户手册。2.3.2 Cgroup的接口和使用

Cgroup的原生接口通过cgroupfs提供,类似于procfs和sysfs,是一种虚拟文件系统。以下用一个实例演示如何使用Cgroup。(1)挂载cgroupfs

命令如下:––# mount t cgroup o cpuset cpuset /sys/fs/cgroup/cpuset

首先必须将cgroupfs挂载起来,这个动作一般已经在启动时由Linux发行版做好了。可以把cgroupfs挂载在任意一个目录上,不过标准的挂载点是/sys/fs/cgroup。注意

实际上sysfs里面只有/sys/fs/cgroup目录,并且sysfs是不允-许用户创建目录的,这里可以将tmpfs挂载上去,然后在tmpfs上创建目录。(2)查看cgroupfs# ls /sys/fs/cgroup/cpusetcgroup.clone_children cpuset.memory_pressurecgroup.procs cpuset.memory_pressure_enabledcgroup.sane_behavior cpuset.memory_spread_pagecpuset.cpu_exclusive cpuset.memory_spread_slabcpuset.cpus cpuset.memscpuset.effective_cpus cpuset.sched_load_balancecpuset.effective_mems cpuset.sched_relax_domain_levelcpuset.mem_exclusive notify_on_releasecpuset.mem_hardwall release_agentcpuset.memory_migrate tasks

可以看到这里有很多控制文件,其中以cpuset开头的控制文件都是cpuset子系统产生的,其他文件则由Cgroup产生。这里面的tasks文件记录了这个Cgroup的所有进程(包括线程),在系统启动后,默认没有对Cgroup做任何配置的情况下,cgroupfs只有一个根目录,并且系统所有的进程都在这个根目录中,即进程pid都在根目录的tasks文件中。注意

实际上现在大多数Linux发行版都是采用的systemd,而systemd使用了Cgroup,所以在这些发行版上,当系统启动后看到的cgroupfs是有一些子目录的。(3)创建Cgroup# mkdir /sys/fs/cgroup/cpuset/child

通过mkdir创建一个新的目录,也就创建了一个新的Cgroup。(4)配置Cgroup# echo 0 > /sys/fs/cgroup/cpuset/child/cpuset.cpus# echo 0 > /sys/fs/cgroup/cpsuet/child/cpuset.mems

接下来配置这个Cgroup的资源配额,通过上面的命令,就可以限制这个Cgroup的进程只能在0号CPU上运行,并且只会从0号内存节点分配内存。(5)使能Cgroup# echo $$ > /sys/fs/cgroup/cpuset/child/tasks

最后,通过将进程id写入tasks文件,就可以把进程移动到这个Cgroup中。并且,这个进程产生的所有子进程也都会自动放在这个Cgroup里。这时,Cgroup才真正起作用了。提示

$$表示当前进程。另外,也可以把pid写入cgroup.procs中,两者的区别是写入tasks只会把指定的进程加到child中,写入cgroup.procs则会把这个进程所属的整个线程组都加到child中。2.3.3 Cgroup子系统介绍

对实际资源的分配和管理是由各个Cgroup子系统完成的,下面介绍几个主要的子系统。

1.cpuset子系统

cpuset可以为一组进程分配指定的CPU和内存节点。cpuset一开始是用在高性能计算(HPC)上的,在NUMA架构的服务器上,通过将进程绑定到固定的CPU和内存节点上,来避免进程在运行时因跨节点内存访问而导致的性能下降。当然,现在cpuset也广泛用在了kvm和容器等场景上。

cpuset的主要接口如下。

·cpuset.cpus:允许进程使用的CPU列表(例如0~4,9)。

·cpuset.mems:允许进程使用的内存节点列表(例如0~1)。

2.cpu子系统

cpu子系统用于限制进程的CPU占用率。实际上它有三个功能,分别通过不同的接口来提供。

·CPU比重分配。这个特性使用的接口是cpu.shares。假设在cgroupfs的根目录下创建了两个Cgroup(C1和C2),并且将cpu.shares分别配置为512和1024,那么当C1和C2争用CPU时,C2将会比C1得到多一倍的CPU占用率。要注意的是,只有当它们争用CPU时cpu share才会起作用,如果C2是空闲的,那么C1可以得到全部的CPU资源。

·CPU带宽限制。这个特性使用的接口是cpu.cfs_period_us和cpu.cfs_quota_us,这两个接口的单位是微秒。可以将period设置为1秒,将quota设置为0.5秒,那么Cgroup中的进程在1秒内最多只能运行0.5秒,然后就会被强制睡眠,直到进入下一个1秒才能继续运行。

·实时进程的CPU带宽限制。以上两个特性都只能限制普通进程,若要限制实时进程,就要使用cpu.rt_period_us和cpu.rt_runtime_us这两个接口了。使用方法和上面类似。

3.cpuacct子系统

cpuacct子系统用来统计各个Cgroup的CPU使用情况,有如下接口。

·cpuacct.stat:报告这个Cgroup分别在用户态和内核态消耗的CPU时间,单位是USER_HZ。USER_HZ在x86上一般是100,即1 USER_HZ等于0.01秒。

·cpuacct.usage:报告这个Cgroup消耗的总CPU时间,单位是纳秒。

·cpuacct.usage_percpu:报告这个Cgroup在各个CPU上消耗的CPU时间,总和也就是cpuacct.usage的值。

4.memory子系统

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

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载