docker/01-开启新篇章-Docker入门.md

50 KiB
Raw Permalink Blame History

01 开启新篇章-Docker入门

千锋高教部云计算书籍:http://www.fengyunedu.cn/originalMaterials

一、容器介绍

容器(container)其实是一种沙盒技术。顾名思义,沙盒就是能够像一个集装箱一样,把你的应用"装"起来的技术。这样,应用与应用之间,就因为有了边界而不至于相互干扰;而被装进集装箱的应用,也可以被方便地搬来搬去,这其实是 PaaS 最理想的状态。

问题:容器的本质到底是什么?

容器的本质是进程。容器就是未来云计算系统中的进程;容器镜像就是这个系统里的".exe"安装包。Kubernetes 就是操作系统!

1、Docker介绍

image-20200206195538531

1.1、Docker公司认知

官网地址docker.io docker.com

公司名称原名dotCloud 14年改名为docker

容器产品docker 16年已经被更名为Moby

docker-ce docker-ee

1.2、容器简史

和虚拟机一样,容器技术也是一种资源隔离的虚拟化技术。我们追溯它的历史,会发现它的技术雏形早已有之。

容器概念始于 1979 年提出的 UNIX chroot它是一个 UNIX 操作系统的系统调用,将一个进程及其子进程的根目录改变到文件系统中的一个新位置,让这些进程只能访问到这个新的位置,从而达到了进程隔离的目的。

2000 年的时候 FreeBSD 开发了一个类似于 chroot 的容器技术 Jails这是最早期也是功能最多的容器技术。Jails 英译过来是监狱的意思,这个“监狱”(用沙盒更为准确)包含了文件系统、用户、网络、进程等的隔离。

2001 Linux 也发布自己的容器技术 Linux VServer2004 Solaris 也发布了 Solaris Containers两者都将资源进行划分形成一个个 zones又叫做虚拟服务器。

2005 年推出 OpenVZ它通过对 Linux 内核进行补丁来提供虚拟化的支持,每个 OpenVZ 容器完整支持了文件系统、用户及用户组、进程、网络、设备和 IPC 对象的隔离。

2007 年 Google 实现了 Control Groups( cgroups ),并加入到 Linux 内核中,这是划时代的,为后期容器的资源配额提供了技术保障。

2008 年基于 cgroups 和 linux namespace 推出了第一个最为完善的 Linux 容器 LXC。

2013 年推出到现在为止最为流行和使用最广泛的容器 Docker相比其他早期的容器技术Docker 引入了一整套容器管理的生态系统,包括分层的镜像模型,容器注册库,友好的 Rest API。

2014 年 CoreOS 也推出了一个类似于 Docker 的容器 RocketCoreOS 一个更加轻量级的 Linux 操作系统,在安全性上比 Docker 更严格。

2016 年微软也在 Windows 上提供了容器的支持Docker 可以以原生方式运行在 Windows 上,而不是需要使用 Linux 虚拟机。

基本上到这个时间节点,容器技术就已经很成熟了,再往后就是容器云的发展,由此也衍生出多种容器云的平台管理技术,其中以 kubernetes 最为出众,有了这样一些细粒度的容器集群管理技术,也为微服务的发展奠定了基石。因此,对于未来来说,应用的微服务化是一个较大的趋势。

1.3、Docker容器需求成因

其一,这是技术演进的一种创新结果。

其二,这是人们追求高效生产活动的一种工具。

随着软件开发的发展,相比于早期的集中式应用部署方式,现在的应用基本都是采用分布式的部署方式,一个应用可能包含多种服务或多个模块,因此多种服务可能部署在多种环境中,如虚拟服务器、公有云、私有云等,由于多种服务之间存在一些依赖关系,所以可能存在应用在运行过程中的动态迁移问题,那这时如何保证不同服务在不同环境中都能平滑的适配,不需要根据环境的不同而去进行相应的定制,就显得尤为重要。

就像货物的运输问题一样,如何将不同的货物放在不同的运输机器上,减少因货物的不同而频繁进行货物的装载和卸载,浪费大量的人力物力。

为此人们发明了集装箱,将货物根据尺寸形状等的不同,用不同规格的集装箱装载,然后再放到运输机上运输,由于集装箱密封,只有货物到达目的地才需拆封,在运输过程能够再不同运输机上平滑过渡,所以避免了资源的浪费。

因此集装箱被誉为是运输业与世界贸易最重要的发明。

Docker 容器的思想就是采用集装箱思想为应用提供了一个基于容器的标准化运输系统。Docker 可以将任何应用及其依赖打包成一个轻量级、可移植、自包含的容器。容器可以运行在几乎所有的操作系统上。这样容器就可以跑在任何环境中因此才有了那句话Build Once, Run Anywhere

这种集装箱的思想我们也能从 Docker 的 Logo 中看出来,这不就是一堆集装箱吗?

1.4、Docker VS 传统

部署模式区别

传统的部署模式: 安装(包管理工具或者源码包编译)->配置->运行

Docker部署模式 复制->运行

容器和虚拟机区别

  1. 容器提供了基于进程的隔离,而虚拟机提供了资源的完全隔离。

  2. 虚拟机可能需要一分钟来启动,而容器只需要一秒钟或更短。

  3. 容器使用宿主操作系统的内核,而虚拟机使用独立的内核

  4. 容器只是一个进程,而虚拟机不是

Docker对服务器端开发/部署带来的变化:

实现更轻量级的虚拟化,方便快速部署

对于部署来说可以极大的减少部署的时间成本和人力成本

Docker支持将应用打包进一个可以移植的容器中重新定义了应用开发测试部署上线的过程核心理念就

是 Build once, Run anywhere

1标准化应用发布docker容器包含了运行环境和可执行程序可以跨平台和主机使用

2节约时间快速部署和启动VM启动一般是分钟级docker容器启动是秒级

3方便构建基于SOA架构或微服务架构的系统通过服务编排更好的松耦合

4节约成本以前一个虚拟机至少需要几个G的磁盘空间docker容器可以减少到MB级

5方便持续集成通过与代码进行关联使持续集成非常方便

6可以作为集群系统的轻量主机或节点在IaaS平台上已经出现了CaaS通过容器替代原来的主机。

云的分层(分类)

IAAS Infrastructure as a service

PAAS Platform

SAAS Software

DSAAS Data Storage

CAAS Container

Docker 优势

1、交付物标准化

Docker是软件工程领域的"标准化"交付组件,最恰到好处的类比是"集装箱"。

集装箱将零散、不易搬运的大量物品封装成一个整体,集装箱更重要的意义在于它提供了一种通用的封装货物的

标准,卡车、火车、货轮、桥吊等运输或搬运工具采用此标准,隧道、桥梁等也采用此标准。以集装箱为中心的

标准化设计大大提高了物流体系的运行效率。

传统的软件交付物包括:应用程序、依赖软件安装包、配置说明文档、安装文档、上线文档等非标准化组件。

Docker的标准化交付物称为"镜像",它包含了应用程序及其所依赖的运行环境,大大简化了应用交付的模式。

2、一次构建多次交付

类似于集装箱的"一次装箱,多次运输"Docker镜像可以做到"一次构建,多次交付"。当涉及到应用程序多副本

部署或者应用程序迁移时更能体现Docker的价值。

3、应用隔离

集装箱可以有效做到货物之间的隔离使化学物品和食品可以堆砌在一起运输。Docker可以隔离不同应用程序之间的相互影响但是比虚拟机开销更小。

总之,容器技术部署速度快,开发、测试更敏捷;提高系统利用率,降低资源成本。

Docker的度量

Docker是利用容器来实现的一种轻量级的虚拟技术从而在保证隔离性的同时达到节省资源的目的。Docker的

可移植性可以让它一次建立到处运行。Docker的度量可以从以下四个方面进行

1隔离性

Docker采用libcontainer作为默认容器代替了以前的LXC。libcontainer的隔离性主要是通过内核的命名空

间来实现 的有pid、net、ipc、mnt、uts命令空间将容器的进程、网络、消息、文件系统和主机名进行隔

离。

2可度量性

Docker主要通过cgroups控制组来控制资源的度量和分配。

3移植性

Docker利用AUFS来实现对容器的快速更新。

AUFS是一种支持将不同目录挂载到同一个虚拟文件系统下的文件系统支持对每个目录的读写权限管理。AUFS具有层

的概念,每一次修改都是在已有的只写层进行增量修改,修改的内容将形成新的文件层,不影响原有的层。

4安全性

安全性可以分为容器内部之间的安全性;容器与托管主机之间的安全性。

容器内部之间的安全性主要是通过命名空间和cgroups来保证的。

容器与托管主机之间的安全性主要是通过内核能力机制的控制可以防止Docker非法入侵托管主机。

Docker容器使用AUFS作为文件系统有如下优势

1节省存储空间

多个容器可以共享同一个基础镜像存储。

2快速部署

如果部署多个来自同一个基础镜像的容器时,可以避免多次复制操作。

3升级方便

升级一个基础镜像即可影响到所有基于它的容器。

4增量修改

可以在不改变基础镜像的同时修改其目录的文件,所有的更高都发生在最上层的写操作层,增加了基础镜像的可共享内容。

1.5、容器的应用场景

1. 简化配置

这是Docker公司宣传的Docker的主要使用场景。虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台软件、系统Docker在降低额外开销的情况下提供了同样的功能。它能让你将运行环境和配置放在代码中然后部署同一个Docker的配置可以在不同的环境中使用这样就降低了硬件要求和应用环境之间耦合度。

2. 代码流水线管理

前一个场景对于管理代码的流水线起到了很大的帮助。代码从开发者的机器到最终在生产环境上的部署需要经过很多的中间环境。而每一个中间环境都有自己微小的差别Docker给应用提供了一个从开发到上线均一致的环境让代码的流水线变得简单不少。

3. 提高开发效率

这就带来了一些额外的好处Docker能提升开发者的开发效率。如果你想看一个详细一点的例子可以参考Aater在DevOpsDays Austin 2014 大会或者是DockerCon上的演讲。

不同的开发环境中,我们都想把两件事做好。一是我们想让开发环境尽量贴近生产环境,二是我们想快速搭建开发环境。

理想状态中要达到第一个目标我们需要将每一个服务都跑在独立的虚拟机中以便监控生产环境中服务的运行状态。然而我们却不想每次都需要网络连接每次重新编译的时候远程连接上去特别麻烦。这就是Docker做的特别好的地方开发环境的机器通常内存比较小之前使用虚拟的时候我们经常需要为开发环境的机器加内存而现在Docker可以轻易的让几十个服务在Docker中跑起来。

4. 隔离应用

有很多种原因会让你选择在一个机器上运行不同的应用,比如之前提到的提高开发效率的场景等。

我们经常需要考虑两点一是因为要降低成本而进行服务器整合二是将一个整体式的应用拆分成松耦合的单个服务译者注微服务架构。如果你想了解为什么松耦合的应用这么重要请参考Steve Yege的这篇论文文中将Google和亚马逊做了比较。

5. 整合服务器

正如通过虚拟机来整合多个应用Docker隔离应用的能力使得Docker可以整合多个服务器以降低成本。由于没有多个操作系统的内存占用以及能在多个实例之间共享没有使用的内存Docker可以比虚拟机提供更好的服务器整合解决方案。

6. 调试能力

Docker提供了很多的工具这些工具不一定只是针对容器但是却适用于容器。它们提供了很多的功能包括可以为容器设置检查点、设置版本和查看两个容器之间的差别这些特性可以帮助调试Bug。你可以在《Docker拯救世界》的文章中找到这一点的例证。

7. 多租户环境

另外一个Docker有意思的使用场景是在多租户的应用中它可以避免关键应用的重写。我们一个特别的关于这个场景的例子是为物联网的应用开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂很难处理重新规划这样一个应用不但消耗时间也浪费金钱。

使用Docker可以为每一个租户的应用层的多个实例创建隔离的环境这不仅简单而且成本低廉当然这一切得益于Docker环境的启动速度和其高效的命令。

8. 快速部署

在虚拟机之前引入新的硬件资源需要消耗几天的时间。虚拟化技术将这个时间缩短到了分钟级别。而Docker通过为进程仅仅创建一个容器而无需启动一个操作系统再次将这个过程缩短到了秒级。这正是Google和Facebook都看重的特性。

2、Docker容器历史解读【扩展阅读】

PaaS

从过去以物理机和虚拟机为主体的开发运维环境,向以容器为核心的基础设施的转变过程,并不是一次温和的改革,而是涵盖了对网络、存储、调度、操作系统、分布式原理等各个方面的容器化理解和改造。

2013 年的后端技术领域,已经太久没有出现过令人兴奋的东西了。曾经被人们寄予厚望的云计算技术,也已经从当初虚无缥缈的概念蜕变成了实实在在的虚拟机和账单。而相比于如日中天 AWS 和盛极一时的 OpenStack以 Cloud Foundry 为代表的开源 PaaS 项目,却成为了当时云计算技术中的一股清流。

当时Cloud Foundry 项目已经基本度过了最艰难的概念普及和用户教育阶段吸引了包括百度、京东、华为、IBM 等一大批国内外技术厂商,开启了以开源 PaaS 为核心构建平台层服务能力的变革。如果你有机会问问当时的云计算从业者们他们十有八九都会告诉你PaaS 的时代就要来了!

这个说法其实一点儿没错,如果不是后来一个叫 Docker 的开源项目突然冒出来的话。

事实上,当时还名叫 dotCloud 的 Docker 公司,也是这股 PaaS 热潮中的一份子。只不过相比于 Heroku、Pivotal、Red Hat 等 PaaS 弄潮儿们dotCloud 公司实在是太微不足道了,而它的主打产品由于跟主流的 Cloud Foundry 社区脱节,长期以来也无人问津。眼看就要被如火如荼的 PaaS 风潮抛弃dotCloud 公司却做出了这样一个决定:开源自己的容器项目 Docker。

显然,这个决定在当时根本没人在乎。

"容器"这个概念从来就不是什么新鲜的东西,也不是 Docker 公司发明的。即使在当时最热门的 PaaS 项目 Cloud Foundry 中,容器也只是其最底层、最没人关注的那一部分。说到这里,正好以当时的事实标准 Cloud Foundry 为例,来解说一下 PaaS 技术。

PaaS 项目被大家接纳的一个主要原因,就是它提供了一种名叫"应用托管"的能力。 在当时,虚拟机和云计算已经是比较普遍的技术和服务了,那时主流用户的普遍用法,就是租一批 AWS 或者 OpenStack 的虚拟机,然后像以前管理物理服务器那样,用脚本或者手工的方式在这些机器上部署应用。

当然,这个部署过程难免会碰到云端虚拟机和本地环境不一致的问题,所以当时的云计算服务,比的就是谁能更好地模拟本地服务器环境,能带来更好的"上云"体验。而 PaaS 开源项目的出现,就是当时解决这个问题的一个最佳方案。

举个例子,虚拟机创建好之后,运维人员只需要在这些机器上部署一个 Cloud Foundry 项目,然后开发者只要执行一条命令就能把本地的应用部署到云上,这条命令就是:

$ cf push " 我的应用 "

是不是很神奇?

事实上,像 Cloud Foundry 这样的 PaaS 项目,最核心的组件就是一套应用的打包和分发机制。 Cloud Foundry 为每种主流编程语言都定义了一种打包格式,而"cf push"的作用,基本上等同于用户把应用的可执行文件和启动脚本打进一个压缩包内,上传到云上 Cloud Foundry 的存储中。接着Cloud Foundry 会通过调度器选择一个可以运行这个应用的虚拟机,然后通知这个机器上的 Agent 把应用压缩包下载下来启动。

这时候关键来了由于需要在一个虚拟机上启动很多个来自不同用户的应用Cloud Foundry 会调用操作系统的 Cgroups 和 Namespace 机制为每一个应用单独创建一个称作"沙盒"的隔离环境,然后在"沙盒"中启动这些应用进程。这样,就实现了把多个用户的应用互不干涉地在虚拟机里批量地、自动地运行起来的目的。

这,正是 PaaS 项目最核心的能力。 而这些 Cloud Foundry 用来运行应用的隔离环境,或者说"沙盒",就是所谓的"容器"。

而 Docker 项目,实际上跟 Cloud Foundry 的容器并没有太大不同所以在它发布后不久Cloud Foundry 的首席产品经理 James Bayer 就在社区里做了一次详细对比,告诉用户 Docker 实际上只是一个同样使用 Cgroups 和 Namespace 实现的"沙盒"而已,没有什么特别的黑科技,也不需要特别关注。

然而短短几个月Docker 项目就迅速崛起了。它的崛起速度如此之快,以至于 Cloud Foundry 以及所有的 PaaS 社区还没来得及成为它的竞争对手,就直接被宣告出局了。那时候,一位多年的 PaaS 从业者曾经如此感慨道:这简直就是一场"降维打击"啊。

事实上Docker 项目确实与 Cloud Foundry 的容器在大部分功能和实现原理上都是一样的,可偏偏就是这剩下的一小部分不一样的功能,成了 Docker 项目接下来"呼风唤雨"的不二法宝。

这个功能,就是 Docker 镜像。

恐怕连 Docker 项目的作者 Solomon Hykes 自己当时都没想到,这个小小的创新,在短短几年内就如此迅速地改变了整个云计算领域的发展历程。

前面已经介绍过PaaS 之所以能够帮助用户大规模部署应用到集群里,是因为它提供了一套应用打包的功能。可就是这个打包功能,却成了 PaaS 日后不断遭到用户诟病的一个"软肋"。

出现这个问题的根本原因是,一旦用上了 PaaS用户就必须为每种语言、每种框架甚至每个版本的应用维护一个打好的包。这个打包过程没有任何章法可循更麻烦的是明明在本地运行得好好的应用却需要做很多修改和配置工作才能在 PaaS 里运行起来。而这些修改和配置,并没有什么经验可以借鉴,基本上得靠不断试错,直到你摸清楚了本地应用和远端 PaaS 匹配的"脾气"才能够搞定。

最后结局就是,"cf push"确实是能一键部署了,但是为了实现这个一键部署,用户为每个应用打包的工作可谓一波三折,费尽心机。

而Docker 镜像解决的,恰恰就是打包这个根本性的问题。 所谓 Docker 镜像,其实就是一个压缩包。但是这个压缩包里的内容,比 PaaS 的应用可执行文件 + 启停脚本的组合就要丰富多了。实际上,大多数 Docker 镜像是直接由一个完整操作系统的所有文件和目录构成的,所以这个压缩包里的内容跟你本地开发和测试环境用的操作系统是完全一样的。

这就有意思了:假设你的应用在本地运行时,能看见的环境是 CentOS 7.2 操作系统的所有文件和目录,那么只要用 CentOS 7.2 的 ISO 做一个压缩包,再把你的应用可执行文件也压缩进去,那么无论在哪里解压这个压缩包,都可以得到与你本地测试时一样的环境。当然,你的应用也在里面!

这就是 Docker 镜像最厉害的地方:只要有这个压缩包在手,你就可以使用某种技术创建一个"沙盒",在"沙盒"中解压这个压缩包,然后就可以运行你的程序了。

更重要的是,这个压缩包包含了完整的操作系统文件和目录,也就是包含了这个应用运行所需要的所有依赖,所以你可以先用这个压缩包在本地进行开发和测试,完成之后,再把这个压缩包上传到云端运行。

在这个过程中,你完全不需要进行任何配置或者修改,因为这个压缩包赋予了你一种极其宝贵的能力:本地环境和云端环境的高度一致!

这,正是 Docker 镜像的精髓。

那么,有了 Docker 镜像这个利器PaaS 里最核心的打包系统一下子就没了用武之地最让用户抓狂的打包过程也随之消失了。相比之下在当今的互联网里Docker 镜像需要的操作系统文件和目录,可谓唾手可得。

所以,你只需要提供一个下载好的操作系统文件与目录,然后使用它制作一个压缩包即可,这个命令就是:

$ docker build 我的镜像

一旦镜像制作完成,用户就可以让 Docker 创建一个"沙盒"来解压这个镜像,然后在"沙盒"中运行自己的应用,这个命令就是:

$ docker run " 我的镜像 "

当然docker run 创建的"沙盒",也是使用 Cgroups 和 Namespace 机制创建出来的隔离环境。

所以Docker 项目给 PaaS 世界带来的"降维打击",其实是提供了一种非常便利的打包机制。这种机制直接打包了应用运行所需要的整个操作系统,从而保证了本地环境和云端环境的高度一致,避免了用户通过"试错"来匹配两种不同运行环境之间差异的痛苦过程。

而对于开发者们来说,在终于体验到了生产力解放所带来的痛快之后,他们自然选择了用脚投票,直接宣告了 PaaS 时代的结束。

不过Docker 项目固然解决了应用打包的难题,但正如前面所介绍的那样,它并不能代替 PaaS 完成大规模部署应用的职责。

遗憾的是,考虑到 Docker 公司是一个与自己有潜在竞争关系的商业实体,再加上对 Docker 项目普及程度的错误判断Cloud Foundry 项目并没有第一时间使用 Docker 作为自己的核心依赖,去替换自己那套饱受诟病的打包流程。

反倒是一些机敏的创业公司,纷纷在第一时间推出了 Docker 容器集群管理的开源项目(比如 Deis 和 Flynn它们一般称自己为 CaaS即 Container-as-a-Service用来跟"过时"的 PaaS 们划清界限。

而在 2014 年底的 DockerCon 上Docker 公司雄心勃勃地对外发布了自家研发的"Docker 原生"容器集群管理项目 Swarm不仅将这波"CaaS"热推向了一个前所未有的高潮,更是寄托了整个 Docker 公司重新定义 PaaS 的宏伟愿望。

在 2014 年的这段巅峰岁月里Docker 公司离自己的理想真的只有一步之遥。

2013~2014 年,以 Cloud Foundry 为代表的 PaaS 项目,逐渐完成了教育用户和开拓市场的艰巨任务,也正是在这个将概念逐渐落地的过程中,应用"打包"困难这个问题,成了整个后端技术圈子的一块心病。

Docker 项目的出现,则为这个根本性的问题提供了一个近乎完美的解决方案。这正是 Docker 项目刚刚开源不久,就能够带领一家原本默默无闻的 PaaS 创业公司脱颖而出,然后迅速占领了所有云计算领域头条的技术原因。

而在成为了基础设施领域近十年难得一见的技术明星之后dotCloud 公司则在 2013 年底大胆改名为 Docker 公司。不过,这个在当时就颇具争议的改名举动,也成为了日后容器技术圈风云变幻的一个关键伏笔。

之前说到,伴随着 PaaS 概念的逐步普及,以 Cloud Foundry 为代表的经典 PaaS 项目,开始进入基础设施领域的视野,平台化和 PaaS 化成了这个生态中的一个最为重要的进化趋势。

就在对开源 PaaS 项目落地的不断尝试中,这个领域的从业者们发现了 PaaS 中最为棘手也最亟待解决的一个问题:究竟如何给应用打包?

遗憾的是,无论是 Cloud Foundry、OpenShift还是 Clodify面对这个问题都没能给出一个完美的答案反而在竞争中走向了碎片化的歧途。

而就在这时,一个并不引人瞩目的 PaaS 创业公司 dotCloud却选择了开源自家的一个容器项目 Docker。更出人意料的是就是这样一个普通到不能再普通的技术却开启了一个名为"Docker"的全新时代。

Docker 项目的崛起,是不是偶然呢?这个以"鲸鱼"为注册商标的技术创业公司,最重要的战略之一就是:坚持把"开发者"群体放在至高无上的位置。

相比于其他正在企业级市场里厮杀得头破血流的经典 PaaS 项目们Docker 项目的推广策略从一开始就呈现出一副"憨态可掬"的亲人姿态,把每一位后端技术人员(而不是他们的老板)作为主要的传播对象。

简洁的 UI有趣的 demo"1 分钟部署一个 WordPress 网站""3 分钟部署一个 Nginx 集群",这种同开发者之间与生俱来的亲近关系,使 Docker 项目迅速成为了全世界 Meetup 上最受欢迎的一颗新星。

在过去的很长一段时间里,相较于前端和互联网技术社区,服务器端技术社区一直是一个相对沉闷而小众的圈子。在这里,从事 Linux 内核开发的极客们自带"不合群"的"光环",后端开发者们啃着多年不变的 TCP/IP 发着牢骚,运维更是天生注定的幕后英雄。

而 Docker 项目,却给后端开发者提供了走向聚光灯的机会。就比如 Cgroups 和 Namespace 这种已经存在多年却很少被人们关心的特性,在 2014 年和 2015 年竟然频繁入选各大技术会议的分享议题,就因为听众们想要知道 Docker 这个东西到底是怎么一回事儿。

而 Docker 项目之所以能取得如此高的关注,一方面正如前面所说的那样,它解决了应用打包和发布这一困扰运维人员多年的技术难题;而另一方面,就是因为它第一次把一个纯后端的技术概念,通过非常友好的设计和封装,交到了最广大的开发者群体手里。

在这种独特的氛围烘托下,你不需要精通 TCP/IP也无需深谙 Linux 内核原理,哪怕只是一个前端或者网站的 PHP 工程师,都会对如何把自己的代码打包成一个随处可以运行的 Docker 镜像充满好奇和兴趣。

这种受众群体的变革,正是 Docker 这样一个后端开源项目取得巨大成功的关键。这也是经典 PaaS 项目想做却没有做好的一件事情PaaS 的最终用户和受益者,一定是为这个 PaaS 编写应用的开发者们,而在 Docker 项目开源之前PaaS 与开发者之间的关系却从未如此紧密过。

解决了应用打包这个根本性的问题,同开发者与生俱来的的亲密关系,再加上 PaaS 概念已经深入人心的完美契机,成为 Docker 这个技术上看似平淡无奇的项目一举走红的重要原因。

一时之间,"容器化"取代"PaaS 化"成为了基础设施领域最炙手可热的关键词,一个以"容器"为中心的、全新的云计算市场,正呼之欲出。而作为这个生态的一手缔造者,此时的 dotCloud 公司突然宣布将公司名称改为"Docker"。

这个举动在当时颇受质疑。在大家印象中Docker 只是一个开源项目的名字。可是现在,这个单词却成了 Docker 公司的注册商标,任何人在商业活动中使用这个单词,以及鲸鱼的 Logo都会立刻受到法律警告。

Docker 项目在短时间内迅速崛起的三个重要原因:

  1. Docker 镜像通过技术手段解决了 PaaS 的根本性问题;

  2. Docker 容器同开发者之间有着与生俱来的密切关系;

  3. PaaS 概念已经深入人心的完美契机。

2.1、何为Paas

PaaS 项目成功的主要原因

是它提供了一种名叫"应用托管"的能力。

paas之前主流用户的普遍用法是租一批 AWS 或者 OpenStack 的虚拟机,然后像以前管理物理服务器那样,用脚本或者手工的方式在这些机器上部署应用。

这个部署过程会碰到云端虚拟机和本地环境不一致的问题,所以当时的云计算服务,比的就是谁能更好地模拟本地服务器环境,能带来更好的"上云"体验。而 PaaS 开源项目的出现,就是当时解决这个问题的一个最佳方案。

PaaS 如何部署应用

虚拟机创建好之后,运维人员只需要在这些机器上部署一个 Cloud Foundry 项目,然后开发者只要执行一条命令就能把本地的应用部署到云上,这条命令就是:

  [root@qfedu.com ~]# cf push " 应用 "

PaaS 项目的核心组件

像 Cloud Foundry 这样的 PaaS 项目,最核心的组件就是一套应用的打包和分发机制。 Cloud Foundry 为每种主流编程语言都定义了一种打包格式,而"cf push"的作用,基本上等同于用户把应用的可执行文件和启动脚本打进一个压缩包内,上传到云上 Cloud Foundry 的存储中。接着Cloud Foundry 会通过调度器选择一个可以运行这个应用的虚拟机,然后通知这个机器上的 Agent 把应用压缩包下载下来启动。

由于需要在一个虚拟机上启动很多个来自不同用户的应用Cloud Foundry 会调用操作系统的 Cgroups 和 Namespace 机制为每一个应用单独创建一个称作"沙盒"的隔离环境,然后在"沙盒"中启动这些应用进程。这就实现了把多个用户的应用互不干涉地在虚拟机里批量自动地运行起来的目的。

这正是 PaaS 项目最核心的能力。 而这些 Cloud Foundry 用来运行应用的隔离环境,或者说"沙盒",就是所谓的"容器"。

Cloud Foundry是当时非常主流非常火的一个PaaS项目

2.2、Docker对Paas的降维打击

Docker 镜像

Docker 项目确实与 Cloud Foundry 的容器在大部分功能和实现原理上都是一样的,可偏偏就是这剩下的一小部分不一样的功能,成了 Docker 项目接下来"呼风唤雨"的不二法宝。这个功能,就是 Docker 镜像。

恐怕连 Docker 项目的作者 Solomon Hykes 自己当时都没想到,这个小小的创新,在短短几年内就如此迅速地改变了整个云计算领域的发展历程。

PaaS的问题

PaaS 之所以能够帮助用户大规模部署应用到集群里,是因为它提供了一套应用打包的功能。可就是这个打包功能,却成了 PaaS 日后不断遭到用户诟病的一个"软肋"

根本原因:

一旦用上了 PaaS用户就必须为每种语言、每种框架甚至每个版本的应用维护一个打好的包。这个打包过程没有任何章法可循更麻烦的是明明在本地运行得好好的应用却需要做很多修改和配置工作才能在 PaaS 里运行起来。而这些修改和配置,并没有什么经验可以借鉴,基本上得靠不断试错,直到你摸清楚了本地应用和远端 PaaS 匹配的"脾气"才能够搞定。

最后结局是,"cf push"确实是能一键部署了,但是为了实现这个一键部署,用户为每个应用打包的工作可谓一波三折,费尽心机。

而Docker 镜像解决的,恰恰就是打包这个根本性的问题。

Docker 镜像的精髓

所谓 Docker 镜像,其实就是一个压缩包。但是这个压缩包里的内容,比 PaaS 的应用可执行文件 + 启停脚本的组合就要丰富多了。实际上,大多数 Docker 镜像是直接由一个完整操作系统的所有文件和目录构成的,所以这个压缩包里的内容跟你本地开发和测试环境用的操作系统是完全一样的。

这就有意思了:假设你的应用在本地运行时,能看见的环境是 CentOS 7.2 操作系统的所有文件和目录,那么只要用 CentOS 7.2 的 ISO 做一个压缩包,再把你的应用可执行文件也压缩进去,那么无论在哪里解压这个压缩包,都可以得到与你本地测试时一样的环境。当然,你的应用也在里面!

这就是 Docker 镜像最厉害的地方:只要有这个压缩包在手,你就可以使用某种技术创建一个"沙盒",在"沙盒"中解压这个压缩包,然后就可以运行你的程序了。

更重要的是,这个压缩包包含了完整的操作系统文件和目录,也就是包含了这个应用运行所需要的所有依赖,所以你可以先用这个压缩包在本地进行开发和测试,完成之后,再把这个压缩包上传到云端运行。

在这个过程中,你完全不需要进行任何配置或者修改,因为这个压缩包赋予了你一种极其宝贵的能力:本地环境和云端环境的高度一致!

这,正是 Docker 镜像的精髓。

那么,有了 Docker 镜像这个利器PaaS 里最核心的打包系统一下子就没了用武之地最让用户抓狂的打包过程也随之消失了。相比之下在当今的互联网里Docker 镜像需要的操作系统文件和目录,可谓唾手可得。

所以,你只需要提供一个下载好的操作系统文件与目录,然后使用它制作一个压缩包即可,这个命令就是:

[root@qfedu.com ~]# docker build " 镜像 "

镜像制作完成,用户就可以让 Docker 创建一个"沙盒"来解压这个镜像,然后在"沙盒"中运行自己的应用,这个命令就是:

[root@qfedu.com ~]# docker run " 镜像 "

Docker 项目给 PaaS 世界带来的"降维打击"

其实是提供了一种非常便利的打包机制。这种机制直接打包了应用运行所需要的整个操作系统,从而保证了本地环境和云端环境的高度一致,避免了用户通过"试错"来匹配两种不同运行环境之间差异的痛苦过程。

二、Docker安装

1、Docker版本解读

moby、docker-ce与docker-ee

最早时docker是一个开源项目主要由docker公司维护。

2017年3月1日起docker公司将原先的docker项目改名为moby并创建了docker-ce和docker-ee。

三者关系

moby是继承了原先的docker的项目是社区维护的的开源项目谁都可以在moby的基础打造自己的容器产品

docker-ce是docker公司维护的开源项目是一个基于moby项目的免费的容器产品

docker-ee是docker公司维护的闭源产品是docker公司的商业产品。

版本维护

moby project由社区维护docker-ce project是docker公司维护docker-ee是闭源的。

Docker-ce发布计划

v1.13.1之后,发布计划更改为:

Edge:  月版本每月发布一次命名格式为YY.MM维护到下个月的版本发布
  
Stable: 季度版本每季度发布一次命名格式为YY.MM维护4个月

版本获取

要使用免费的dockerhttps://github.com/docker/docker-ce上获取

要使用收费的dockerhttps://www.docker.com/products/docker-enterprise上获取

2、官方安装方案

docker-ce的release计划跟随moby的release计划可以使用下面的命令直接安装最新的docker-ce

[root@qfedu.com ~]# curl -fsSL https://get.docker.com/ | sh

如果是centos上面的安装命令会在系统上添加yum源:/etc/yum.repos.d/docker-ce.repo

[root@qfedu.com ~]# wget https://download.docker.com/linux/centos/docker-ce.repo
[root@qfedu.com ~]# mv docker-ce.repo /etc/yum.repos.d
[root@qfedu.com ~]# yum install -y docker-ce

或者直接下载rpm安装

[root@qfedu.com ~]# wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.09.0.ce-1.el7.centos.x86_64.rpm
[root@qfedu.com ~]# yum localinstall docker-ce-17.09.0.ce-1.el7.centos.x86_64.rpm

3、Docker加速器

使用Aliyun Docker Yum源安装Docker

删除已安装的Docker

[root@qfedu.com ~]# yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine

配置阿里云Docker Yum源

[root@qfedu.com ~]# yum install -y yum-utils device-mapper-persistent-data lvm2 git
[root@qfedu.com ~]# yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

如何安装指定版本

查看Docker版本

[root@qfedu.com ~]# yum list docker-ce --showduplicates

安装较旧版本比如Docker 17.03.2) 需要指定完整的rpm包的包名并且加上--setopt=obsoletes=0 参数

[root@qfedu.com ~]# yum install -y --setopt=obsoletes=0 \
   docker-ce-17.03.2.ce-1.el7.centos.x86_64 \
   docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch

安装Docker最新版

[root@qfedu.com ~]# yum install docker-ce -y

Centos8安装Docker报错解决方案

注意最新版的centos stream8已经不报错

官方docker rpm包地址

https://download.docker.com/linux/centos/7/x86_64/stable/Packages/
  1. 有关runc

image-20200311135754712

  1. 有关containerd.io

image-20200311135807381

启动Docker服务

# systemctl enable docker
# systemctl start docker 

查看Docker版本及启动状态

# docker -v
Docker version 20.10.17, build 100c701
# docker version
Client: Docker Engine - Community
 Version:           20.10.17
 API version:       1.41
 Go version:        go1.17.11
 Git commit:        100c701
 Built:             Mon Jun  6 23:03:11 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.17
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.17.11
  Git commit:       a89b842
  Built:            Mon Jun  6 23:01:29 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.6
  GitCommit:        10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
 runc:
  Version:          1.1.2
  GitCommit:        v1.1.2-0-ga916309
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

查看Docker运行状态

# docker info 
Client:
 Context:    default
 Debug Mode: false
 Plugins:
  app: Docker App (Docker Inc., v0.9.1-beta3)
  buildx: Docker Buildx (Docker Inc., v0.8.2-docker)
  scan: Docker Scan (Docker Inc., v0.17.0)

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 0
 Server Version: 20.10.17
 Storage Driver: overlay2
  Backing Filesystem: xfs
  Supports d_type: true
  Native Overlay Diff: true
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: cgroupfs
 Cgroup Version: 1
 Plugins:
  Volume: local
  Network: bridge host ipvlan macvlan null overlay
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
 Swarm: inactive
 Runtimes: runc io.containerd.runc.v2 io.containerd.runtime.v1.linux
 Default Runtime: runc
 Init Binary: docker-init
 containerd version: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
 runc version: v1.1.2-0-ga916309
 init version: de40ad0
 Security Options:
  seccomp
   Profile: default
 Kernel Version: 4.18.0-294.el8.x86_64
 Operating System: CentOS Stream 8
 OSType: linux
 Architecture: x86_64
 CPUs: 8
 Total Memory: 7.749GiB
 Name: ori
 ID: WYAH:V6N3:NCDH:L462:FCI2:52LN:XTMZ:DWZ2:FPOM:PV2H:SE3H:7XVZ
 Docker Root Dir: /var/lib/docker
 Debug Mode: false
 Registry: https://index.docker.io/v1/
 Labels:
 Experimental: false
 Insecure Registries:
  127.0.0.0/8
 Live Restore Enabled: false

问题1

docker info的时候报如下错误

bridge-nf-call-iptables is disabled

解决

追加如下配置,然后重启系统

[root@qfedu.com ~]# vim /etc/sysctl.conf  
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 1

问题2

虚拟机ping百度也能ping通但是需要等好几秒才出结果关键是下载镜像一直报错如下

[root@qfedu.com ~]# docker pull daocloud.io/library/nginx
Using default tag: latest
Error response from daemon: Get https://daocloud.io/v2/: dial tcp: lookup daocloud.io on 192.168.1.2:53: read udp  192.168.1.189:41335->192.168.1.2:53: i/o timeout

解决

我的虚拟机用的网关和dns都是虚拟机自己的.1或者.2把DNS改成8.8.8.8问题就解决了ping百度也秒出结果

  [root@qfedu.com ~]# vim /etc/resolv.conf
  nameserver 8.8.8.8

4、国内源镜像源

  1. Daocloud
  2. Aliyun
  3. 网易蜂巢

三、Docker核心概念

1、Docker系统

Docker系统有两个程序Docker服务端和Docker客户端

Docker服务端

是一个服务进程,管理着所有的容器。

docker engine

Docker客户端

扮演着docker服务端的远程控制器可以用来控制docker的服务端进程。

2、Docker核心组件

Docker 镜像 - Docker images

Docker 仓库 - Docker registeries

Docker 容器 - Docker containers

容器组成要素

名称空间 namespace

资源限制 cgroups

文件系统 overlay2(UnionFS)

2.1、Docker 仓库

用来保存镜像可以理解为代码控制中的代码仓库。同样的Docker 仓库也有公有和私有的概念。

公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供使用。这些镜像可以是自己创建或者在别人的镜像基础上创建。Docker 仓库是 Docker 的分发部分。

2.2、Docker 镜像

image-20200311140939156

Docker 镜像是 Docker 容器运行时的只读模板,每一个镜像由一系列的层 (layers) 组成。Docker 使用 UnionFS 来将这些层联合到单独的镜像中。UnionFS 允许独立文件系统中的文件和文件夹(称之为分支)被透明覆盖形成一个单独连贯的文件系统。正因为有了这些层的存在Docker 是如此的轻量。当你改变了一个 Docker 镜像,比如升级到某个程序到新的版本,一个新的层会被创建。因此,不用替换整个原先的镜像或者重新建立(在使用虚拟机的时候你可能会这么做),只是一个新的层被添加或升级了。现在你不用重新发布整个镜像,只需要升级,层使得分发 Docker 镜像变得简单和快速。

在 Docker 的术语里,一个只读层被称为镜像,一个镜像是永久不会变的。

由于 Docker 使用一个统一文件系统Docker 进程认为整个文件系统是以读写方式挂载的。 但是所有的变更都发生顶层的可写层,而下层的原始的只读镜像文件并未变化。由于镜像不可写,所以镜像是无状态的。

每一个镜像都可能依赖于由一个或多个下层的组成的另一个镜像。下层那个镜像是上层镜像的父镜像。

镜像名称组成

registry/repo:tag

基础镜像

一个没有任何父镜像的镜像,谓之基础镜像

镜像ID

所有镜像都是通过一个 64 位十六进制字符串 (内部是一个 256 bit 的值)来标识的。 为简化使用,前 12 个字符可以组成一个短ID可以在命令行中使用。短ID还是有一定的碰撞机率所以服务器总是返回长ID。

2.3、Docker 容器

Docker 容器和文件夹很类似一个Docker容器包含了所有的某个应用运行所需要的环境。每一个 Docker 容器都是从 Docker 镜像创建的。Docker 容器可以运行、开始、停止、移动和删除。每一个 Docker 容器都是独立和安全的应用平台Docker 容器是 Docker 的运行部分。

2.4、Docker镜像和容器区别

Docker镜像

要理解Docker镜像和docker容器之间的区别确实不容易。

假设Linux内核是第0层那么无论怎么运行Docker它都是运行于内核层之上的。这个Docker镜像是一个只读的镜像位于第1层它不能被修改或不能保存状态。

一个Docker镜像可以构建于另一个Docker镜像之上这种层叠关系可以是多层的。第1层的镜像层我们称之为基础镜像Base Image其他层的镜像除了最顶层我们称之为父层镜像Parent Image。这些镜像继承了他们的父层镜像的所有属性和设置并在Dockerfile中添加了自己的配置。

Docker镜像通过镜像ID进行识别。镜像ID是一个64字符的十六进制的字符串。但是当我们运行镜像时通常我们不会使用镜像ID来引用镜像而是使用镜像名来引用。

要列出本地所有有效的镜像,可以使用命令

[root@qfedu.com ~]# docker images

镜像可以发布为不同的版本这种机制我们称之为标签Tag

如上图所示neo4j镜像有两个版本lastest版本和2.1.5版本。

可以使用pull命令加上指定的标签

[root@qfedu.com ~]# docker pull ubuntu:14.04
[root@qfedu.com ~]# docker pull ubuntu:12.04

Docker容器

Docker容器可以使用命令创建

[root@qfedu.com ~]# docker run imagename

它会在所有的镜像层之上增加一个可写层。这个可写层有运行在CPU上的进程而且有两个不同的状态运行态Running和退出态 Exited。这就是Docker容器。当我们使用docker run启动容器Docker容器就进入运行态当我们停止Docker容器时它就进入退出态。

当我们有一个正在运行的Docker容器时从运行态到停止态我们对它所做的一切变更都会永久地写到容器的文件系统中。要切记对容器的变更是写入到容器的文件系统的而不是写入到Docker镜像中的。

我们可以用同一个镜像启动多个Docker容器这些容器启动后都是活动的彼此还是相互隔离的。我们对其中一个容器所做的变更只会局限于那个容器本身。

如果对容器的底层镜像进行修改,那么当前正在运行的容器是不受影响的,不会发生自动更新现象。

如果想更新容器到其镜像的新版本,那么必须当心,确保我们是以正确的方式构建了数据结构,否则我们可能会导致损失容器中所有数据的后果。

64字符的十六进制的字符串来定义容器ID它是容器的唯一标识符。容器之间的交互是依靠容器ID识别的由于容器ID的字符太长我们通常只需键入容器ID的前4个字符即可。当然我们还可以使用容器名但显然用4字符的容器ID更为简便。

3、名字空间

名字空间是 Linux 内核一个强大的特性。每个容器都有自己单独的名字空间,运行在其中的应用都像是在独立的操作系统中运行一样。名字空间保证了容器之间彼此互不影响。

  1. pid 名字空间

不同用户的进程就是通过 pid 名字空间隔离开的,且不同名字空间中可以有相同 pid。所有的 LXC 进程在 Docker中的父进程为Docker进程每个 LXC 进程具有不同的名字空间。同时由于允许嵌套,因此可以很方便的实现嵌套的 Docker 容器。

  1. net 名字空间

有 了 pid 名字空间, 每个名字空间中的 pid 能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 名字空间实现的,每个 net 名字空间有独立的 网络设备, IP 地址, 路由表, /proc/net 目录。这样每个容器的网络就能隔离开来。Docker 默认采用 veth 的方式,将容器中的虚拟网卡同 host 上的一 个Docker 网桥 docker0 连接在一起。

  1. ipc 名字空间

容器中进程交互还是采用了 Linux 常见的进程间交互方法(interprocess communication - IPC), 包括信号量、消息队列和共享内存、socket、管道等。然而同 VM 不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 名字空间中的进程间交互,因此需要在 IPC 资源申请时加入名字空间信息,每个 IPC 资源有一个唯一的 32 位 id。

  1. mnt名字空间

类似 change root将一个进程放到一个特定的目录执行。mnt 名字空间允许不同名字空间的进程看到的文件结构不同,这样每个名字空间 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个名字空间中的容器在 /proc/mounts 的信息只包含所在名字空间的 mount point。

  1. uts 名字空间

UTS("UNIX Time-sharing System") 名字空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非主机上的一个进程。

  1. user 名字空间

每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。

四、镜像管理

搜索镜像

这种方法只能用于官方镜像库,例如搜索基于 Centos 操作系统的镜像

[root@qfedu.com ~]# docker search centos

按星级搜索镜像,例如查找 star 数至少为 100 的镜像,默认不加 s 选项找出所有相关 ubuntu 镜像

[root@qfedu.com ~]# docker search ubuntu -f stars=100  

拉取镜像

[root@qfedu.com ~]# docker pull centos

查看本地镜像

[root@qfedu.com ~]# docker image list

查看镜像详情

[root@qfedu.com ~]# docker image inspect 镜像id

只查看所有镜像的id

[root@qfedu.com ~]# docker images -q 

查看镜像制作的过程相当于Dockerfile

[root@qfedu.com ~]# docker history daocloud.io/ubuntu

删除镜像 删除一个或多个多个之间用空格隔开可以使用镜像名称或id

[root@qfedu.com ~]# docker rmi daocloud.io/library/mysql

强制删除 如果镜像正在被使用,可用--force强制删除

[root@qfedu.com ~]# docker rmi docker.io/ubuntu:latest --force

删除所有镜像

[root@qfedu.com ~]# docker rmi $(docker images -q)

给镜像打tag

[root@qfedu.com ~]# docker tag daocloud.io/ubuntu daocloud.io/ubuntu:v1