做者:huashioupython
segmentfault.com/a/1190000019462392算法
题外话docker
最近对Docker和Kubernetes进行了一番学习,前两天作了一次技术分享,回去听了一遍本身演讲的录音,发现单单PPT作好仍是远远不够的,没有提早准备好逻辑严谨的讲稿,在讲的时候出现了卡壳、漏掉技术点、逻辑矛盾的问题。为了解决这个问题,我打算之后在作技术分享前,都按着PPT的内容先写成博客,理顺表达逻辑。另外,我以为每次技术分享使用的PPT都应该尽量的作好,由于你不知道将来会不会还要拿来再讲几遍。本文以PPT+讲稿的方式编写,权当对本身此次技术分享作个记录,欢迎你们拍砖。ubuntu
1. Docker出现的背景segmentfault
在日常的研发和项目场景中,如下状况广泛存在:
后端
我的开发环境浏览器
为了作大数据相关项目,须要安装一套CDH集群,常见的作法是在本身电脑里搭建3台与CDH版本对应的虚拟机,把CDH集群装起来后,考虑到之后颇有可能还要使用一个干净的CDH集群,为了不之后重复安装环境,一般会对整套CDH集群作一个备份,这样电脑里就有6个虚拟机镜像了。另外,后面在学习其余技术时,好比学习Ambari大数据集群,那么为了避免破坏已有的虚拟机环境,又要从新搭建3台虚拟机,本机磁盘很快被一大堆的虚拟机镜像占满。tomcat
公司内部开发环境安全
公司里每每会以小团队的方式来作项目,通常由运维部门从他们管理的服务器资源中分配出虚拟机供团队内部开发测试使用。好比作一个与机器学习相关的项目:bash
小明在运维部门分配的虚拟机上搭建了一套Ambari集群,拿来跑大数据相关业务
小刚用python3写了一个机器学习算法,放到虚拟机上运行发现虚拟机里是python2,算法不兼容,因而把虚拟机里的python版本升级了,算法跑通了,但Ambari用到python的部分功能可能就报错了
小李开发了应用,放到虚拟机上启动tomcat,发现虚拟机里的是OpenJDK,致使tomcat起不来,因而又安装了一个JDK,这时候可能Ambari里的Java代码可能就报错了
小赵想利用服务器资源作性能测试,发现虚拟机严重削减了性能,最终仍是要直接找物理机来跑测试,破坏了物理机原来的环境
作完项目后,这些虚拟机上安装的东西每每变得没用了,下个项目组来仍是得新申请虚拟机从新部署软件
开发/测试/现场环境
研发人员在开发环境里写好了代码作好测试后,提交给测试部门,测试人员在测试环境跑起来发现有BUG,研发人员说在开发环境没这个BUG,和测试人员屡次扯皮解决BUG后发布版本,发到现场在生产环境部署后,又发现有BUG,这下轮到工程人员和测试人员扯皮。有时候为了兼容特殊的现场环境,还须要对代码进行定制化修改,拉出分支,这样致使了每次到现场升级都是一场噩梦
升级或迁移项目
在每次发版本要升级到现场时,若是现场起了多个tomcat应用,那么须要对每一个tomcat都先停掉,替换war包,而后再起起来,轮流着作,不只繁琐并且很容易出错,若是遇到升级后出现严重BUG,还要手工作回退。另外,若是项目想上云,那么在云上部署后要从新进行一轮测试,若是后面考虑还云厂商,可能相同的测试还要再进行一次(好比更换了数据存储组件),费时费力。
总结以上列举的全部场景,他们存在的一个共同的问题是:没有一种既可以屏蔽操做系统差别,又可以以不下降性能的方式来运行应用的技术,来解决环境依赖的问题。Docker应运而生。
2. Docker是什么
Docker是一种应用容器引擎。首先说一下何为容器,Linux系统提供了Namespace和CGroup技术实现环境隔离和资源控制,其中Namespace是Linux提供的一种内核级别环境隔离的方法,能使一个进程和该进程建立的子进程的运行空间都与Linux的超级父进程相隔离,注意Namespace只能实现运行空间的隔离,物理资源仍是全部进程共用的,为了实现资源隔离,Linux系统提供了CGroup技术来控制一个进程组群可以使用的资源(如CPU、内存、磁盘IO等),把这两种技术结合起来,就能构造一个用户空间独立且限定了资源的对象,这样的对象称为容器。Linux Container是Linux系统提供的容器化技术,简称LXC,它结合Namespace和CGroup技术为用户提供了更易用的接口来实现容器化。LXC仅为一种轻量级的容器化技术,它仅能对部分资源进行限制,没法作到诸如网络限制、磁盘空间占用限制等。dotCloud公司结合LXC和如下列出的技术实现了Docker容器引擎,相比于LXC,Docker具有更加全面的资源控制能力,是一种应用级别的容器引擎。
Chroot:该技术能在container里构造完整的Linux文件系统;
Veth:该技术可以在主机上虚拟出一张网卡与container里的eth0网卡进行桥接,实现容器与主机、容器之间的网络通讯;
UnionFS:联合文件系统,Docker利用该技术“Copy on Write”的特色实现容器的快速启动和极少的资源占用,后面会专门介绍该文件系统;
Iptables/netfilter:经过这两个技术实现控制container网络访问策略;
TC:该技术主要用来作流量隔离,限制带宽;
Quota:该技术用来限制磁盘读写空间的大小;
Setrlimit:该技术用来限制container中打开的进程数,限制打开的文件个数等
也正是由于Docker依赖Linux内核的这些技术,至少使用3.8或更高版本的内核才能运行Docker容器,官方建议使用3.10以上的内核版本。
3. 与传统虚拟化技术的区别
传统的虚拟化技术在虚拟机(VM)和硬件之间加了一个软件层Hypervisor,或者叫作虚拟机管理程序。Hypervisor的运行方式分为两类:
直接运行在物理硬件之上。如基于内核的KVM虚拟机,这种虚拟化须要CPU支持虚拟化技术;
运行在另外一个操做系统。如VMWare和VitrualBox等虚拟机。
由于运行在虚拟机上的操做系统是经过Hypervisor来最终分享硬件,因此虚拟机Guest OS发出的指令都须要被Hypervisor捕获,而后翻译为物理硬件或宿主机操做系统可以识别的指令。VMWare和VirtualBox等虚拟机在性能方面远不如裸机,但基于硬件虚拟机的KVM约能发挥裸机80%的性能。这种虚拟化的优势是不一样虚拟机之间实现了彻底隔离,安全性很高,而且可以在一台物理机上运行多种内核的操做系统(如Linux和Window),但每一个虚拟机都很笨重,占用资源多并且启动很慢。
Docker引擎运行在操做系统上,是基于内核的LXC、Chroot等技术实现容器的环境隔离和资源控制,在容器启动后,容器里的进程直接与内核交互,无需通过Docker引擎中转,所以几乎没有性能损耗,能发挥出裸机的所有性能。但因为Docker是基于Linux内核技术实现容器化的,所以使得容器内运行的应用只能运行在Linux内核的操做系统上。目前在Window上安装的docker引擎实际上是利用了Window自带的Hyper-V虚拟化工具自动建立了一个Linux系统,容器内的操做其实是间接使用这个虚拟系统实现的。
4. Docker基本概念
Docker主要有以下几个概念:
引擎:建立和管理容器的工具,经过读取镜像来生成容器,并负责从仓库拉取镜像或提交镜像到仓库中;
镜像:相似于虚拟机镜像,通常由一个基本操做系统环境和多个应用程序打包而成,是建立容器的模板;
容器:可看做一个简易版的Linxu系统环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子;
仓库:集中存放镜像文件的场所,分为公共仓库和私有仓库,目前最大的公共仓库是官方提供的Docker Hub,此外国内的阿里云、腾讯云等也提供了公共仓库;
宿主机:运行引擎的操做系统所在服务器。
5. Docker与虚拟机、Git、JVM的类比
为了让你们对Docker有更直观的认识,下面分别进行三组类比:
上图中Docker的镜像仓库相似于传统虚拟机的镜像仓库或存放镜像的本地文件系统,Docker引擎启动容器来运行Spark集群(容器内包含基础的Linux操做系统环境),类比于虚拟机软件启动多个虚拟机,在虚拟机内分别运行Spark进程,二者区别在于Docker容器内的应用在使用物理资源时,直接与内核打交道,无需通过Docker引擎。
Docker的仓库思想与Git是相同的。
Docker的口号是“Build,Ship,and Run Any App,Anywhere”,也就是能够基于Docker构建、装载和运行应用程序,一次构建处处运行。Java的口号是“Write Once,Run Anywhere”,即一次编写处处运行。Java是基于JVM适配操做系统的特色来屏蔽系统的差别,Docker则是利用内核版本兼容性的特色来实现一次构建导出运行,只要Linux系统的内核是3.8或更高的版本,就都能把容器跑起来。
固然,正如Java中若是应用代码使用了JDK10的新特性,基于JDK8就没法运行同样,若是容器内的应用使用了4.18版本的内核特性,那么在CentOS7(内核版本为3.10)启动容器时,虽然容器可以启动,但里面应用的功能是没法正常运行的,除非把宿主机的操做系统内核升级到4.18版本。
6. Docker镜像文件系统
Docker镜像采用分层存储格式,每一个镜像可依赖其余镜像进行构建,每一层的镜像可被多个镜像引用,上图的镜像依赖关系,K8S镜像实际上是CentOS+GCC+GO+K8S这四个软件结合的镜像。这种分层结构能充分共享镜像层,能大大减小镜像仓库占用的空间,而对用户而言,他们所看到的容器,实际上是Docker利用UnionFS(联合文件系统)把相关镜像层的目录“联合”到同一个挂载点呈现出来的一个总体,这里须要简单介绍一个UnionFS是什么:
UnionFS能够把多个物理位置独立的目录(也叫分支)内容联合挂载到同一个目录下,UnionFS容许控制这些目录的读写权限,此外对于只读的文件和目录,它具备“Copy on Write(写实复制)”的特色,即若是对一个只读的文件进行修改,在修改前会先把文件复制一份到可写层(多是磁盘里的一个目录),全部的修改操做其实都是对这个文件副本进行修改,原来的只读文件并不会变化。
其中一个使用UnionFS的例子是:Knoppix,一个用于Linux演示、光盘教学和商业产品演示的Linux发行版,它就是把一个CD/DVD和一个存在在可读写设备(例如U盘)联合挂载,这样在演示过程当中任何对CD/DVD上文件的改动都会在被应用在U盘上,不改变原来的CD/DVD上的内容。
UnionFS能够把多个物理位置独立的目录(也叫分支)内容联合挂载到同一个目录下,UnionFS容许控制这些目录的读写权限,此外对于只读的文件和目录,它具备“Copy on Write(写实复制)”的特色,即若是对一个只读的文件进行修改,在修改前会先把文件复制一份到可写层(多是磁盘里的一个目录),全部的修改操做其实都是对这个文件副本进行修改,原来的只读文件并不会变化。UnionFS有不少种,其中Docker中经常使用的是AUFS,这是UnionFS的升级版,除此以外还有DeviceMapper、Overlay二、ZFS和 VFS等。Docker镜像的每一层默认存放在/var/lib/docker/aufs/diff目录中,当用户启动一个容器时,Docker引擎首先在/var/lib/docker/aufs/diff中新建一个可读写层目录,而后使用UnionFS把该可读写层目录和指定镜像的各层目录联合挂载到/var/lib/docker/aufs/mnt里的一个目录中(其中指定镜像的各层目录都以只读方式挂载),经过LXC等技术进行环境隔离和资源控制,使容器里的应用仅依赖mnt目录中对应的挂载目录和文件运行起来。
利用UnionFS写实复制的特色,在启动一个容器时, Docker引擎实际上只是增长了一个可写层和构造了一个Linux容器,这二者都几乎不消耗系统资源,所以Docker容器可以作到秒级启动,一台服务器上可以启动上千个Docker容器,而传统虚拟机在一台服务器上启动几十个就已经很是吃力了,并且虚拟机启动很慢,这是Docker相比于传统虚拟机的两个巨大的优点。
当应用只是直接调用了内核功能来运做的状况下,应用自己就能直接做为最底层的层来构建镜像,但由于容器自己会隔绝环境,所以容器内部是没法访问宿主机里文件的(除非指定了某些目录或文件映射到容器内),这种状况下应用代码就只能使用内核的功能。可是Linux内核仅提供了进程管理、内存管理、文件系统管理等一些基础且底层的管理功能,在实际的场景中,几乎全部软件都是基于操做系统来开发的,所以每每都须要依赖操做系统的软件和运行库等,若是这些应用的下一层直接是内核,那么应用将没法运行。因此实际上应用镜像每每底层都是基于一个操做系统镜像来补足运行依赖的。
Docker中的操做系统镜像,与日常安装系统时用的ISO镜像不一样。ISO镜像里包含了操做系统内核及该发行版系统包含的全部目录和软件,而Docker中的操做系统镜像,不包含系统内核,仅包含系统必备的一些目录(如/etc /proc等)和经常使用的软件和运行库等,可把操做系统镜像看做内核之上的一个应用,一个封装了内核功能,并为用户编写的应用提供运行环境的工具。应用基于这样的镜像构建,就可以利用上相应操做系统的各类软件的功能和运行库,此外,因为应用是基于操做系统镜像来构建的,就算换到另外的服务器,只要操做系统镜像中被应用使用到的功能能适配宿主机的内核,应用就能正常运行,这就是一次构建处处运行的缘由。
下图形象的表现出了镜像和容器的关系:
上图中Apache应用基于emacs镜像构建,emacs基于Debian系统镜像构建,在启动为容器时,在Apache镜像层之上构造了一个可写层,对容器自己的修改操做都在可写层中进行。Debian是该镜像的基础镜像(Base Image),它提供了内核Kernel的更高级的封装。同时其余的镜像也是基于同一个内核来构建的(如下的BusyBox是一个精简版的操做系统镜像):
这时候就会有一个问题,应用基于操做系统镜像来构建,那若是操做系统镜像自己就很占空间,岂不是镜像的分发不方便,并且镜像仓库占用的空间也会很大。有人已经考虑到这一点,针对不一样的场景分别构造了不一样的操做系统镜像,下面介绍几种最经常使用的系统镜像。
7. Docker基础操做系统
以上系统镜像分别适用于不一样的场景:
BusyBox:一个极简版的Linux系统,集成了100多种经常使用Linux命令,大小不到2MB,被称为“Linux系统的瑞士军刀”,适用于简单测试场景;
Alpine:一个面向安全的轻型Linux发行版系统,比BusyBox功能更完善,大小不到5MB,是官网推荐的基础镜像,因为其包含了足够的基础功能和体积较小,在生产环境中最经常使用;
Debian/Ubuntu:Debian系列操做系统,功能完善,大小约170MB,适合研发环境;
CentOS/Fedora:都是基于Redhat的Linux发行版,企业级服务器经常使用操做系统,稳定性高,大小约200MB,适合生产环境使用。
8. Docker持久化存储
根据前面介绍的容器UnionFS写实复制的特色,可知在容器里增长、删除或修改文件,其实都是对可写层里的文件副本进行了操做。在容器关闭后,该可写层也会被删除,对容器的全部修改都会失效,所以须要解决容器内文件持久化的问题。Docker提供了两种方案来实现:
把宿主机文件系统里的目录映射到容器内的目录,以下图所示。如此一来,容器内在该目录里建立的全部文件,都存储到宿主机的对应目录中,在关闭容器后,宿主机的目录依然存在,再次启动容器时还能读取到以前建立的文件,所以实现了容器的文件持久化。固然同时要明白,若是是对镜像自带文件进行了修改,因为镜像是只读的,该修改操做没法在关闭容器时保存下来,除非在修改了文件后构建一个新的镜像。
把多台宿主机的磁盘目录经过网络联合为共享存储,而后把共享存储中的特定目录映射给特定的容器,以下图所示。这样容器在重启时,仍是能读取到关闭前建立的文件。生产环境中经常使用NFS做为共享存储方案。
9. Docker镜像制做方法
镜像制做方法有两种:
经过正在运行的容器生成新镜像
当一个容器在运行时,在里面全部的修改都会体如今容器的可写层,Docker提供了commit命令,能够把正在运行的容器,叠加上可写层的修改内容,生成一个新镜像。如上图所示,在容器里新安装Spark组件的,若是关闭容器,Spark组件会随着可写层的消失而消失,若是在关闭容器以前使用commit命令生成新镜像,那么使用新镜像启动为容器时,容器里就会包含Spark组件。
这种方式比较简单,但没法直观的设置环境变量、监听端口等内容,适合在简单使用的场景运用。
经过Dockerfile文件来生成新镜像
Dockerfile是一个定义了镜像建立步骤的文件,Docker引擎经过build命令读取Dockerfile,按定义的步骤来一步步构造镜像。在研发和实施环境中,经过Dockerfile 建立容器是主流作法。下面是一个Dockerfile的例子:
FROM ubuntu/14.04 # 基础镜像MAINTAINER guest # 制做者签名RUN apt-get install openssh-server -y # 安装ssh服务RUN mkdir /var/run/sshd # 建立目录RUN useradd -s /bin/bash -m -d /home/guest guest # 建立用户RUN echo ‘guest:123456’| chpasswd # 修改用户密码ENV RUNNABLE_USER_DIR /home/guest # 设置环境变量EXPOSE 22 # 容器内默认开启的端口CMD ["/usr/sbin/sshd -D"] # 启动容器时自动启动ssh服务复制代码
Docker引擎能够根据以上Dockerfile定义的步骤,构造出一个带有ssh服务的Ubuntu镜像。
10. Docker的使用场景
Docker做为一种轻量级的虚拟化方案,应用场景十分丰富,下面收集了一些常见的场景:
做为轻量级虚拟机使用
可使用Ubuntu等系统镜像建立容器,看成虚拟机来使用,相比于传统虚拟机,启动速度更快,资源占用更少,单机能够启动大量的操做系统容器,方便进行各类测试;
做为云主机使用
结合Kubernetes这样的容器管理系统,能够在大量服务器上动态分配和管理容器,在公司内部,甚至能够取代VMWare这样的虚拟机管理平台,使用Docker容器做为云主机使用;
应用服务打包
在Web应用服务开发场景,能够把Java运行环境、Tomcat服务器打包为一个基础镜像,在修改了代码包后加入到基础镜像来构建一个新的镜像,能很方便的升级服务和控制版本;
容器云平台CaaS
Docker的出现,使得不少云平台供应商开始提供容器云的服务,简称容器即服务CaaS,如下对比一下IaaS、PaaS和SaaS:
IaaS(基础设施即服务):提供虚拟机或者其余基础资源做为服务提供给用户。用户能够从供应商那里得到虚拟机或者存储等资源来装载相关的应用,同时这些基础设施的繁琐的管理工做将由IaaS供应商来处理。其主要的用户是企业的系统管理员和运维人员;
PaaS(平台即服务):把开发平台做为服务提供给用户。用户能够在一个包括SDK,文档和测试环境等在内的开发平台上很是方便地编写应用,并且不管是在部署,或者在运行的时候,用户都无需为服务器、操做系统、网络和存储等资源的管理操心,这些繁琐的工做都由PaaS供应商负责处理。其主要的用户是企业开发人员。
SaaS(软件即服务):将应用做为服务提供给客户。用户只要接上网络,并经过浏览器,就能直接使用在云端上运行的应用,而不须要顾虑相似安装等杂事,而且免去初期高昂的软硬件投入。SaaS主要面对的是普通的用户。
CaaS(容器即服务):完成IaaS和PaaS两个层级的功能。相对于传统的IaaS和PaaS服务,CaaS对底层的支持比PaaS更灵活,而对上层应用的操控又比IaaS更容易。同时由于Docker是比VM更细粒度的虚拟化服务,因此可以对计算资源作到更高效的利用。CaaS能够部署在任何物理机,虚拟机或IaaS云之上。
持续集成和持续部署
互联网行业提倡敏捷开发,持续集成部署CI/CD即是最典型的开发模式。使用Docker容器云平台,就能实现从代码编写完成推送到Git/SVN后,自动触发后端CaaS平台将代码下载、编译并构建成测试Docker镜像,再替换测试环境容器服务,自动在Jenkins或者Hudson中运行单元/集成测试,测试经过后,立刻就能自动将新版本镜像更新到线上,完成服务升级。整个过程全自动化,一鼓作气,最大程度地简化了运维,并且保证线上、线下环境彻底一致,并且线上服务版本与Git/SVN发布分支也实现统一。
解决微服务架构的实施难题
基于Spring Cloud这样的微服务框架,可以实现微服务的管理,但微服务自己仍是须要运行在操做系统上。一个采用微服务架构开发的应用中,微服务的个数每每不少,这就致使了一台服务器上每每须要启动多个微服务来提升资源的利用率,而微服务自己可能就只能兼容部分操做系统,这就致使了就算有大量的服务器资源(操做系统可能不同),但因为微服务自己与操做系统可能相关,就不能作到让微服务在任意服务器上运行,这就带来了资源的浪费和运维的困难。利用Docker容器的环境隔离能力,让微服务运行在容器内,就可以解决以上所说的问题。
执行临时任务
有时候用户只是想执行一次性的任务,但若是用传统虚拟机的方式就要搭建环境,执行完任务后还要释放资源,比较麻烦。使用Docker容器就能够构建临时的运行环境,执行完任务后关闭容器便可,方便快捷。
多租户环境
利用Docker的环境隔离能力,能够为不一样的租户提供独占的容器,实现简单并且成本较低。
11. 总结
Docker的技术并不神秘,只是整合了前人积累的各类成果实现的应用级的容器化技术,它利用各类Linux发行版中使用了版本兼容的内核容器化技术,来实现镜像一次构建处处运行的效果,而且利用了容器内的基础操做系统镜像层,屏蔽了实际运行环境的操做系统差别,使用户在开发应用程序时,只需确保在选定的操做系统和内核版本上能正确运行便可,几乎不须要关心实际的运行环境的系统差别,大大提升效率和兼容性。但随着容器运行得愈来愈多,容器管理将会称为另外一个运维的难题,这时候就须要引入Kubernetes、Mesos或Swarm这些容器管理系统,后面有机会再介绍这些技术。