第12章 CentOS7 中 Docker 的安装与配置

第08章 CentOS7 中 Docker 的安装与配置

Docker 是什么?

Docker 是世界领先的软件容器平台。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HxLebuT6-1599523586877)(assets/docker_FQCRW.png)]

Docker 是 Golang 编写的, 自 2013 年推出以来,受到越来越多的开发者的关注。如果你关注最新的技术发展,那么你一定听说过 Docker。不管是云服务还是微服务(Microservices),越来越多的厂商都开始基于 Docker 作为基础设施自动化的工具。那么什么是 Docker?Docker与传统的虚拟机有什么区别?为何要采用 Docker?如何使用 Docker?

Docker 重大更新,版本升至17.03,包名仓库全换

3月2日,Docker 官方发布了一篇 blog ,宣布企业版到来。版本也从1.13.x一跃到17.03。

之后,Docker 会每月发布一个 edge 版本(17.03, 17.04, 17.05…),每三个月发布一个 stable 版本(17.03, 17.06, 17.09…),企业版(EE) 和 stable 版本号保持一致,但每个版本提供一年维护。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eRH2Kv3H-1599523586884)(assets/lifecycle.png)]

Docker 的 Linux 发行版的软件仓库也从以前的https://apt.dockerproject.org / https://yum.dockerproject.org 变更为目前的 https://download.docker.com/。软件包名变更为 docker-ce(社区版) 和 docker-ee(企业版)。

旧的仓库和包名(docker-engine)依旧可以使用,但不确定什么时候会被废弃,docker-engine 的版本号也变成了17.03.0~ce-0这种的版本号。

Docker v17.03.0-ce 版本更新内容和下载地址请查看发行日志

文章目录

8.1 概述

Docker是推动集装箱运输的公司,也是唯一能够解决混合云中每个应用程序的容器平台提供商。今天的企业面临数字化转型的压力,但受到现有应用程序和基础架构的限制,同时使云,数据中心和应用程序架构的组合日益多样化。 Docker实现了应用程序和基础架构之间的真正独立性,开发人员和IT运营商可以发挥其潜力,并创建更好的协作和创新模型。 

Docker 在 1.13 版本之后,从 2017 年的 3 月 1 日开始,版本命名规则变为如下:

项目说明
版本格式YY.MM
Stable 版本每个季度发行
Edge 版本每个月发行

同时 Docker 划分为 CE 和 EE。CE 即社区版(免费,支持周期三个月),EE 即企业版,强调安全,付费使用。

Docker CE 每月发布一个 Edge 版本 (17.03, 17.04, 17.05…),每三个月发布一个 Stable 版本(17.03, 17.06, 17.09…),Docker EE 和 Stable 版本号保持一致,但每个版本提供一年维护。

官方网站上有各种环境下的 安装指南,这里主要介绍 Docker CE 在 Linux 发行版 Cent OS 上的安装。

8.1.1 选择的自由

以您对组织有意义的方式灵活地发展您的应用程序组合。从小开始或从小开始 - Docker通过一个同样适用于新开发的平台来延长遗留应用程序的寿命。 

8.1.2 敏捷的运营

标准化和自动化跨部门构建,管理和保护应用程序的方式。由于Docker将开发人员和IT操作放在同一平台上,因此他们可以更智能,更快地协同工作。 

8.1.3 综合安全

获得一个集成的安全框架,用于提供更安全的应用程序并改进策略自动化,从而保持性能并且不会妨碍您。 

8.1.4 什么是容器平台?

容器平台是一个完整的解决方案,允许组织解决各种需求中的多个问题。它不仅仅是一项技术和编排 - 它通过提供企业运营所需的所有部分,包括整个应用程序生命周期中的安全性,治理,自动化,支持和认证,为整个组织提供可持续的利益。 Docker企业版(EE)是一个企业级容器平台,使IT领导者能够选择如何以自己的节奏经济高效地构建和管理整个应用程序组合,而无需担心架构和基础架构锁定。 

8.2 平台(Platform)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8hhxiVN-1599523586888)(assets/what-docker.png)]

8.2.1 什么是 Docker

Docker 是开源的应用容器引擎。

Docker 可以让你将所有应用软件以及它的以来打包成软件开发的标准化单元。

Docker  容器将软件以及它运行安装所需的一切文件(代码、运行时、系统工具、系统库)打包到一起,这就保证了不管是在什么样的运行环境,总是能以相同的方式运行。就好像  Java 虚拟机一样,“一次编写,到处运行(Write once, run anywhere)”,而 Docker  是“一次构建,到处运行(Build once,run anywhere)”。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8KXbsfiI-1599523586897)(assets/pIYBAFqrZHCATj1XAABH8YGDFgg501.png)]

Docker 是一种“容器即服务”(Docker Containers as a Service ,简称 CaaS),使得开发和IT运营团队可以对于应用的构建、发布、运行更加敏捷和可控。

概括的说: Docker 是为开发人员和系统管理员用于构建、发布、并运行分布式应用程序的开放式平台。该平台由 Docker  引擎(一个便携、轻巧的运行时和打包工具) 和 Docker Hub (一个共享应用程序和自动化工作流的云服务)等组成。Docker  可以使应用程序从组件迅速组装并消除了开发、质量保证和生产环境之间的摩擦问题。这样一来,IT部门可以更快地发布,而这些应用程序不管是运行在笔记本电脑、数据中心的虚拟机,还是任何的云,其运行过程和结果都是一致的。

我们再来看下 Docker 的 Logo 。很明显,这是一只鲸鱼,它托着许多集装箱。我们可以把宿主机可当做这只鲸鱼,把相互隔离的容器可看成集装箱,每个集装箱中都包含自己的应用程序。这 Logo 简直的太形象了!

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-50LBc021-1599523586901)(assets/pIYBAFqrZHGAMPWTAABAYNw4yRQ219.png)]

8.2.2 为什么要用 Docker

开发更加敏捷:Docker 让开发人员可以自由定义环境,创建和部署的应用程序更快、更容易,IT 运维人员快速应对变化也更加灵活性。

更加可控:Docker 使得开发人员保存从基础设施到应用的代码,帮助 IT 运维人管理拥有标准的、安全的、可扩展的操作环境。

高可移植性:Docker 允许自由选择,可以是从笔记本电脑到一个团队,从私人基础设施到公共云提供商。

这样,你可以专注于开发应用,其他的繁琐事交给 Docker 去做吧。

如何使用 Docker

这可真是一个大话题,如果完整阐述足够写一本书了。好在我们的目的是做入门普及,因此我们就简单讲一下 Docker 的安装、基本使用和常用命令。

8.2.3 Docker 与 Microservices 的关系

Microservices(微服务) 依赖于“基础设施自动化”,而 Docker 正是“基础设施自动化”的利器。可以说 Docker 的火爆,一定程度上也带动了微服务架构的兴起,而微服务的广泛应用也促进了 Docker 繁荣。可以说两者相辅相成。

有关微服务的介绍,可以移步至[[《简述 Microservices(微服务)》](https://waylau.com/ahout-microservices/)。

8.2.4 Docker 与 虚拟机的区别

容器与虚拟机有着类似的资源隔离和分配的优点,但不同的架构方法使容器能够更加便携,高效等。

8.2.4.1 虚拟机的架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wjbz0HB6-1599523586903)(assets/pIYBAFqrZHGAXfk_AAElcMywYzo972.png)]

每个虚拟机都包括应用程序、必要的二进制文件和库以及一个完整的客户操作系统(Guest OS),尽管它们被分离,它们共享并利用主机的硬件资源,将近需要十几个 GB 的大小。
8.2.4.2 容器的架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bx2TVy3f-1599523586904)(assets/pIYBAFqrZHGAPG_xAADiEN6Hlcc589.png)]

容器包括应用程序及其所有的依赖,但与其他容器共享内核。它们以独立的用户空间进程形式运行在主机操作系统上。他们也不依赖于任何特定的基础设施,Docker 容器可以运行在任何计算机上,任何基础设施和任何云上。

Docker 的容器利用了 LXC,管理利用了 namespaces 来做权限的控制和隔离,cgroups 来进行资源的配置,并且还通过 aufs 来进一步提高文件系统的资源利用率,而这些技术都不是 Docker 独创。
8.2.4.3 LXC
LXC 与虚拟机的不同之处在于,它是一个操作系统级别的虚拟化环境,而不是硬件虚拟化环境。他们都做同样的事情,但 LXC  是操作系统级别的虚拟化环境,虚拟环境有它自己的进程和网络空间,而不是创建一个完整成熟的虚拟机。因此,一个 LXC  虚拟操作系统具有最小的资源需求,并启动只需几秒钟。

正如你可以在下图中看到的,左侧是 LXC 虚拟的 Ubuntu ,默认安装使用 11 MB 大小。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u8MCTbzy-1599523586907)(assets/pIYBAFqrZHGAMMdAAAEoS8mrguY333.png)]

8.2.4.4 对比虚拟机总结
特性容器虚拟机
启动秒级分钟级
硬盘使用一般为MB一般为GB
性能接近原生弱于
系统支持量单机支持上千个容器一般几十个

8.2.5 Docker 的优点

8.2.5.1 轻量级

所有容器在一台机器上共享同一个操作系统内核,这样他们立即开始,并更有效地利用内存。Image 是从分层文件系统的构建,这样他们能够共享公共文件,使得磁盘使用率和 Image 的下载更加高效。

8.2.5.2 开放

Docker 容器是基于开发的标准,允许容器运行在主流的 Linux 发布版和 Microsoft 操作系统作为所有的基础设施。

8.2.5.3 安全

容器使得应用程序彼此隔离,而基础架构同时为应用程序提供了额外的保护层。

8.2.5.4 更快速的交付和部署

对开发和运维(devop)人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。

开发者可以使用一个标准的镜像来构建一套开发容器,开发完成之后,运维人员可以直接使用这个容器来部署代码。 Docker 可以快速创建容器,快速迭代应用程序,并让整个过程全程可见,使团队中的其他成员更容易理解应用程序是如何创建和工作的。 Docker 容器很轻很快!容器的启动时间是秒级的,大量地节约开发、测试、部署的时间。

8.2.5.5 更高效的虚拟化

Docker 容器的运行不需要额外的 hypervisor 支持,它是内核级的虚拟化,因此可以实现更高的性能和效率。

8.2.5.6 更轻松的迁移和扩展

Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。

8.2.5.7 更简单的管理

使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。

8.2.6 Docker能做什么?

Docker可以解决虚拟机能够解决的问题,同时也能够解决虚拟机由于资源要求过高而无法解决的问题。Docker能处理的事情包括:

  • 隔离应用依赖
  • 创建应用镜像并进行复制
  • 创建容易分发的即启即用的应用
  • 允许实例简单、快速地扩展
  • 测试应用并随后销毁它们

Docker背后的想法是创建软件程序可移植的轻量容器,让其可以在任何安装了Docker的机器上运行,而不用关心底层操作系统,就像野心勃勃的造船者们成功创建了集装箱而不需要考虑装在哪种船舶上一样。

资料来源:http://dockone.io/article/133

8.2.7 Docker究竟做了什么?

要知道Docker究竟做了什么?就要弄清楚Docker使用了哪些技术来完成它的工作,或有什么具体的命令可用,这些放在了最后一节,这里我将说明的是Docker提供的资源和抽象。 Docker两个最重要的概念是镜像容器。除此之外,链接数据卷也很重要。我们先从镜像入手。

资料来源:http://dockone.io/article/133

8.2.7.1 镜像

Docker的镜像类似虚拟机的快照,但更轻量,非常非常轻量。

创建Docker镜像有几种方式,多数是在一个现有镜像基础上创建新镜像,因为几乎你需要的任何东西都有了公共镜像,包括所有主流Linux发行版,你应该不会找不到你需要的镜像。不过,就算你想从头构建一个镜像也有好几种方法

要创建一个镜像,你可以拿一个镜像,对它进行修改来创建它的子镜像。实现前述目的的方式有两种:在一个文件中指定一个基础镜像及需要完成的修改;或通过“运行”一个镜像,对其进行修改并提交。不同方式各有优点,不过一般会使用文件来指定所做的变化。

镜像拥有唯一ID,以及一个供人阅读的名字和标签对。镜像可以命名为类似ubuntu:latest、ubuntu:precise、django:1.6、django:1.7等等。

8.2.7.2 容器

现在说容器了。你可以从镜像中创建容器,这等同于从快照中创建虚拟机,不过更轻量。应用是由容器运行的。

举个例子,你可以下载一个Ubuntu的镜像(有个叫docker registry的镜像公共仓库),通过安装Gunicorn和Django应用及其依赖项来完成对它的修改,然后从该镜像中创建一个容器,在它启动后运行你的应用。

[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rfyC1XmP-1599523586909)(assets/7e456b0341559afa06a6076625c4edde.png)]](http://dockone.io/uploads/article/20150109/7e456b0341559afa06a6076625c4edde.png) 

容器与虚拟机一样,是隔离的(有一点要注意,我稍后会讨论到)。它们也拥有一个唯一ID和唯一的供人阅读的名字。容器对外公开服务是必要的,因此Docker允许公开容器的特定端口。

[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3PJWc7c3-1599523586911)(assets/a846617cf45d4ccc36d907ef40abe592.png)]](http://dockone.io/uploads/article/20150109/a846617cf45d4ccc36d907ef40abe592.png) 

与虚拟机相比,容器有一个很大的差异,它们被设计用来运行单进程,无法很好地模拟一个完整的环境(如果那是你需要的,请看看LXC)。你可能会尝试运行runit或supervisord实例来启动多个进程,但(以我的愚见)这真的没有必要。 单进程与多进程之争非常精彩。你应该知道的是,Docker设计者极力推崇“一个容器一个进程的方式”,如果你要选择在一个容器中运行多个进程,那唯一情况是:出于调试目的,运行类似ssh的东西来访问运行中的容器,不过docker exec命令解决了这个问题。

容器和虚拟机的第二个巨大差异是:当你停止一个虚拟机时,可能除了一些临时文件,没有文件会被删除;当你停止一个Docker容器,对初始状态(创建容器所用的镜像的状态)做的所有变化都会丢失

容器是设计来运行一个应用的,而非一台机器。你可能会把容器当虚拟机用,但如我们所见,你将失去很多的灵活性,因为Docker提供了用于分离应用与数据的工具,使得你可以快捷地更新运行中的代码/系统,而不影响数据。

8.2.7.3 数据卷

数据卷让你可以不受容器生命周期影响进行数据持久化。它们表现为容器内的空间,但实际保存在容器之外,从而允许你在不影响数据的情况下销毁、重建、修改、丢弃容器。Docker允许你定义应用部分和数据部分,并提供工具让你可以将它们分开。使用Docker时必须做出的最大思维变化之一就是:容器应该是短暂和一次性的

卷是针对容器的,你可以使用同一个镜像创建多个容器并定义不同的卷。卷保存在运行Docker的宿主文件系统上,你可以指定卷存放的目录,或让Docker保存在默认位置。保存在其他类型文件系统上的都不是一个卷,稍后再具体说。

[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-36Qcfs93-1599523586913)(assets/16069b19876f5e59a5d0e77a7053fff3.png)]](http://dockone.io/uploads/article/20150109/16069b19876f5e59a5d0e77a7053fff3.png) 

卷还可以用来在容器间共享数据,建议你阅读卷的文档做进一步了解

8.2.7.4 链接

链接是Docker的另一个重要部分。

容器启动时,将被分配一个随机的私有IP,其它容器可以使用这个IP地址与其进行通讯。这点非常重要,原因有二:一是它提供了容器间相互通信的渠道,二是容器将共享一个本地网络。我曾经碰到一个问题,在同一台机器上为两个客户启动两个elasticsearch容器,但保留集群名称为默认设置,结果这两台elasticsearch服务器立马变成了一个自主集群。注:限制容器间通讯是可行的,请阅读Docker的高级网络文档做进一步了解

要开启容器间通讯,Docker允许你在创建一个新容器时引用其它现存容器,在你刚创建的容器里被引用的容器将获得一个(你指定的)别名。我们就说,这两个容器链接在了一起

因此,如果DB容器已经在运行,我可以创建web服务器容器,并在创建时引用这个DB容器,给它一个别名,比如dbapp。在这个新建的web服务器容器里,我可以在任何时候使用主机名dbapp与DB容器进行通讯。 Docker更进一步,要求你声明容器在被链接时要开放哪些端口给其他容器,否则将没有端口可用。

[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GEVPUMOw-1599523586916)(assets/9b6c96106a808c09988fb60f777ddad2.png)]](http://dockone.io/uploads/article/20150109/9b6c96106a808c09988fb60f777ddad2.png) 
8.2.7.5 Docker镜像的可移植性

在创建镜像时有一点要注意。Docker允许你在一个镜像中指定卷和端口。从这个镜像创建的容器继承了这些设置。但是,Docker不允许你在镜像上指定任何不可移植的内容。

例如,你可以在镜像里定义卷,只要它们被保存在Docker使用的默认位置。这是因为如果你在宿主文件系统里指定了一个特定目录来保存卷,其他使用这个镜像的宿主无法保证这个目录是存在的。

你可以定义要公开的端口,但仅限那些在创建链接时公开给其他容器的端口,你不能指定公开给宿主的端口,因为你无从知晓使用那个镜像的宿主有哪些端口可用。

你也不能在镜像上定义链接。使用链接要求通过名字引用其他容器,但你无法预知每个使用那个镜像的宿主如何命名容器。

镜像必须完全可移植,Docker不允许例外。

以上就是主要的部分,**创建镜像、用它们创建容器、在需要时暴露端口和创造卷、通过链接将几个容器连接在一起。**不过,这一切如何能在不引起额外支出条件下达成?

8.2.8 Docker如何完成它需要完成的东西?

两个词:cgroupsunion文件系统。Docker使用cgroup来提供容器隔离,而union文件系统用于保存镜像并使容器变得短暂。

资料来源:http://dockone.io/article/133

8.2.8.1 Cgroups

这是Linux内核功能,它让两件事情变成可能:

  • 限制Linux进程组的资源占用(内存、CPU)
  • 为进程组制作 PID、UTS、IPC、网络、用户及装载命名空间

这里的关键词是命名空间。比如说,一个PID命名空间允许它里面的进程使用隔离的PID,并与主PID命名空间独立开来,因此你可以在一个PID命名空间里拥有自己的PID为1的初始化进程。其他命名空间与此类似。然后你可以使用cgroup创建一个环境,进程可以在其中运行,并与操作系统的其他进程隔离开,但这里的关键点是这个环境上的进程使用的是已经加载和运行的内核,因此额外支出与运行其他进程几乎是一样的。Chroot之于cgroup就好像之于绿巨人(The Hulk)贝恩(Bane)毒液(Venom)的组合(译者注:本文作者非常瘦弱,后三者都非常强壮)。

8.2.8.2 Union文件系统

Union文件系统允许通过union装载来达到一个分层的积累变化。在union文件系统里,文件系统可以被装载在其他文件系统之上,其结果就是一个变化的分层的积累变化。每个装载的文件系统表示前一个文件系统之后的变化集合,就像是一个diff。

[[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nPRdoBRU-1599523586918)(assets/80f162ad554833229628f1753cd501d0.png)]](http://dockone.io/uploads/article/20150109/80f162ad554833229628f1753cd501d0.png) 

当你下载一个镜像,修改它,然后保存成新版本,你只是创建了加载在包裹基础镜像的初始层上的一个新的union文件系统。这使得Docker镜像非常轻,比如:你的DB、Nginx和Syslog镜像都可以共享同一个Ubuntu基础,每一个镜像保存的只是在它们需要的功能的基础上的变化。 截至2015年1月4日,Docker允许在union文件系统中使用aufsbtrfs设备映射(device mapper)

8.2.8.3 镜像案例

下面是一个 postgresql 镜像:

[{
"AppArmorProfile": "",
"Args": [
    "postgres"
],
"Config": {
    "AttachStderr": true,
    "AttachStdin": false,
    "AttachStdout": true,
    "Cmd": [
        "postgres"
    ],
    "CpuShares": 0,
    "Cpuset": "",
    "Domainname": "",
    "Entrypoint": [
        "/docker-entrypoint.sh"
    ],
    "Env": [
        "PATH=/usr/lib/postgresql/9.3/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "LANG=en_US.utf8",
        "PG_MAJOR=9.3",
        "PG_VERSION=9.3.5-1.pgdg70 1",
        "PGDATA=/var/lib/postgresql/data"
    ],
    "ExposedPorts": {
        "5432/tcp": {}
    },
    "Hostname": "6334a2022f21",
    "Image": "postgres",
    "MacAddress": "",
    "Memory": 0,
    "MemorySwap": 0,
    "NetworkDisabled": false,
    "OnBuild": null,
    "OpenStdin": false,
    "PortSpecs": null,
    "StdinOnce": false,
    "Tty": false,
    "User": "",
    "Volumes": {
        "/var/lib/postgresql/data": {}        
    },
    "WorkingDir": ""
},
"Created": "2015-01-03T23:56:12.354896658Z",
"Driver": "devicemapper",
"ExecDriver": "native-0.2",
"HostConfig": {
    "Binds": null,
    "CapAdd": null,
    "CapDrop": null,
    "ContainerIDFile": "",
    "Devices": null,
    "Dns": null,
    "DnsSearch": null,
    "ExtraHosts": null,
    "IpcMode": "",
    "Links": null,
    "LxcConf": null,
    "NetworkMode": "",
    "PortBindings": null,
    "Privileged": false,
    "PublishAllPorts": false,
    "RestartPolicy": {
        "MaximumRetryCount": 0,
        "Name": ""
    },
    "SecurityOpt": null,
    "VolumesFrom": [
        "bestwebappever.dev.db-data"
    ]
},
"HostnamePath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hostname",
"HostsPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/hosts",
"Id": "6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da",
"Image": "aaab661c1e3e8da2d9fc6872986cbd7b9ec835dcd3886d37722f1133baa3d2db",
"MountLabel": "",
"Name": "/bestwebappever.dev.db",
"NetworkSettings": {
    "Bridge": "docker0",
    "Gateway": "172.17.42.1",
    "IPAddress": "172.17.0.176",
    "IPPrefixLen": 16,
    "MacAddress": "02:42:ac:11:00:b0",
    "PortMapping": null,    
    "Ports": {
        "5432/tcp": null
    }
},
"Path": "/docker-entrypoint.sh",
"ProcessLabel": "",
"ResolvConfPath": "/mnt/docker/containers/6334a2022f213f9534b45df33c64437081a38d50c7f462692b019185b8cbc6da/resolv.conf",
"State": {
    "Error": "",
    "ExitCode": 0,
    "FinishedAt": "0001-01-01T00:00:00Z",
    "OOMKilled": false,
    "Paused": false,
    "Pid": 21654,
    "Restarting": false,
    "Running": true,
    "StartedAt": "2015-01-03T23:56:42.003405983Z"
},
"Volumes": {
    "/var/lib/postgresql/data": "/mnt/docker/vfs/dir/5ac73c52ca86600a82e61279346dac0cb3e173b067ba9b219ea044023ca67561",
    "postgresql_data": "/mnt/docker/vfs/dir/abace588b890e9f4adb604f633c280b9b5bed7d20285aac9cc81a84a2f556034"
},
"VolumesRW": {
    "/var/lib/postgresql/data": true,
    "postgresql_data": true
}
}
]

也就是说,镜像只是一个json,它指定了从该镜像运行的容器的特性,union装载点保存在哪里,要公开什么端口等等。每个镜像与一个union文件系统相关联,每个Docker上的union文件系统都有一个上层,就像是计算机科技树(不像其他树有一大堆的家族)。如果它看起来有点吓人或有些东西串不起来,不要担心,这只是出于教学目的,你并不会直接处理这些文件。

8.2.8.4 容器案例

容器之所以是短暂的,是因为当你从镜像上创建一个容器,Docker会创建一个空白的union文件系统加载在与该镜像关联的union文件系统之上。

由于union文件系统是空白的,这意味着没有变化会被应用到镜像的文件系统上,当你创建一些变化时,文件就能体现出来,但是当容器停止,该容器的union文件系统会被丢弃,留下的是你启动时的原始镜像文件系统。除非你创建一个新的镜像,或制作一个卷,否则你所做的变化在容器停止时都会消失。

卷所做的是在容器内指定一个目录,以便在union文件系统之外保存它。

这是一个bestwebappever的容器:

[{
"AppArmorProfile": "",
"Args": [],
"Config": {
    "AttachStderr": true,
    "AttachStdin": false,
    "AttachStdout": true,
    "Cmd": [
        "/sbin/my_init"
    ],
    "CpuShares": 0,
    "Cpuset": "",
    "Domainname": "",
    "Entrypoint": null,
    "Env": [
        "DJANGO_CONFIGURATION=Local",
        "HOME=/root",
        "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
        "TALPOR_ENVIRONMENT=local",
        "TALPOR_DIR=/opt/bestwebappever"
    ],
    "ExposedPorts": {
        "80/tcp": {}
    },
    "Hostname": "44a87fdaf870",
    "Image": "talpor/bestwebappever:dev",
    "MacAddress": "",
    "Memory": 0,
    "MemorySwap": 0,
    "NetworkDisabled": false,
    "OnBuild": null,
    "OpenStdin": false,
    "PortSpecs": null,
    "StdinOnce": false,
    "Tty": false,
    "User": "",
    "Volumes": {
        "/opt/bestwebappever": {}
    },
    "WorkingDir": "/opt/bestwebappever"
},
"Created": "2015-01-03T23:56:15.378511619Z",
"Driver": "devicemapper",
"ExecDriver": "native-0.2",
"HostConfig": {
    "Binds": [
        "/home/german/bestwebappever/:/opt/bestwebappever:rw"
    ],
    "CapAdd": null,
    "CapDrop": null,
    "ContainerIDFile": "",
    "Devices": null,
    "Dns": null,
    "DnsSearch": null,
    "ExtraHosts": null,
    "IpcMode": "",
    "Links": [
        "/bestwebappever.dev.db:/bestwebappever.dev.app/db",
        "/bestwebappever.dev.redis:/bestwebappever.dev.app/redis"
    ],
    "LxcConf": null,
    "NetworkMode": "",
    "PortBindings": {
        "80/tcp": [
            {
                "HostIp": "",
                "HostPort": "8887"
            }
        ]
    },
    "Privileged": false,
    "PublishAllPorts": false,
    "RestartPolicy": {
        "MaximumRetryCount": 0,
        "Name": ""
    },
    "SecurityOpt": null,
    "VolumesFrom": [
        "bestwebappever.dev.requirements-data"
    ]
},
"HostnamePath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hostname",
"HostsPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/hosts",
"Id": "44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4",
"Image": "b84804fac17b61fe8f344359285186f1a63cd8c0017930897a078cd09d61bb60",
"MountLabel": "",
"Name": "/bestwebappever.dev.app",
"NetworkSettings": {
    "Bridge": "docker0",
    "Gateway": "172.17.42.1",
    "IPAddress": "172.17.0.179",
    "IPPrefixLen": 16,
    "MacAddress": "02:42:ac:11:00:b3",
    "PortMapping": null,
    "Ports": {
        "80/tcp": [
            {
                "HostIp": "0.0.0.0",
                "HostPort": "8887"
            }
        ]
    }
},
"Path": "/sbin/my_init",
"ProcessLabel": "",
"ResolvConfPath": "/mnt/docker/containers/44a87fdaf870281e86160e9e844b8987cfefd771448887675fed99460de491c4/resolv.conf",
"State": {
    "Error": "",
    "ExitCode": 0,
    "FinishedAt": "0001-01-01T00:00:00Z",
    "OOMKilled": false,
    "Paused": false,
    "Pid": 21796,
    "Restarting": false,
    "Running": true,
    "StartedAt": "2015-01-03T23:56:47.537259546Z"
},
"Volumes": {
    "/opt/bestwebappever": "/home/german/bestwebappever",
    "requirements_data": "/mnt/docker/vfs/dir/bc14bec26ca311d5ed9f2a83eebef872a879c9e2f1d932470e0fd853fe8be336"
},
"VolumesRW": {
    "/opt/bestwebappever": true,
    "requirements_data": true
}
}
]

卷基本上与镜像相同,不过现在还指定了一些公开给宿主的端口,也声明了卷位于宿主的位置,容器状态是从现在直到结束,等等。与前面一样,如果它看起来让人生畏,不要担心,你不会直接处理这些json。

8.3 用例(USE CASES)

容器之旅从运行业务的现有应用程序开始。通过使它们更快,更安全和更便宜地运行,Docker可以腾出时间和资源来为您的客户构建新的应用程序。无需重新设计,重新编码或重新教育现有应用程序,策略或员工,即可转换IT。一旦集装箱化并在安全的供应链上,Docker就可以独特地满足整个IT组织的需求 - 从将遗产转移到未来的发展。加快您的上市时间,实现价值并提高投资回报率(ROI)。 

8.3.1 现代化传统应用[MTA]

Docker的第一步是使现有的应用程序组合现代化。将现有应用程序打包到容器中可立即提高安全性,降低成本并获得云端可移植性。此转换将现代属性应用于遗留应用程序 - 所有这些都无需更改单行代码。 

DOWNLOAD THE MTA KIT

8.3.2 混合云(HYBRID CLOUD)

云迁移,多云或混合云基础架构需要应用程序的无摩擦可移植性。 Docker将应用程序及其依赖项打包到一个隔离的容器中,使它们可以移植到任何基础架构。一劳永逸地消除“在我的机器上工作”的问题。 Docker认证的基础架构可确保容器化应用程序的一致性。 

LEARN ABOUT CERTIFIED INFRASTRUCTURE

8.3.3 持续集成和部署[DEVOPS]

通过Docker和DevOps的集成,集成现代方法并自动化开发流程。容器的孤立特性通过消除应用程序冲突和提高开发人员的工作效率,使其有助于快速变化的环境。 Docker实现了对关注点的真正分离,从而加速了DevOps流程的采用。 

WHITE PAPER - DOCKER AND DEVOPS

8.3.4 微服务(MICROSERVICES)

Docker容器设计轻巧,是实现微服务应用程序开发的理想选择。加速数十个或数百个作为单个应用程序组成的容器的开发,部署和回滚。无论是构建新的微服务还是将单块转换为更小的服务,简单易用的工具都可以轻松地组合,部署和维护复杂的应用程序。 

8.4 docker 安装

8.4.1 环境要求

要安装Docker CE,您需要CentOS 7的维护版本。不支持或测试存档版本。 必须启用centos-extras存储库。默认情况下,此存储库已启用,但如果已将其禁用,则需要重新启用它。 建议使用overlay2存储驱动程序。

Docker CE 支持 64 位版本 CentOS 7,并且要求内核版本不低于 3.10。 CentOS 7 满足最低内核的要求,但由于内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。

8.4.2 从仓库安装 Docker CE

Docker CE 有几种不同安装方式:

  • 大多数用户 set up Docker’s repositories 并从中进行安装,以便于安装和升级任务。这是推荐的方法。
  • 有些用户下载RPM软件包并 install it manually 并完全手动管理升级。这在诸如在没有访问互联网的air-gapped 统上安装Docker的情况下非常有用。
  • 在测试和开发环境中,一些用户选择使用自动 convenience scripts 来安装Docker。

我们这里选择使用 Docker 仓库进行安装。在新主机上首次安装Docker CE之前,需要设置Docker存储库。之后,您可以从存储库安装和更新Docker。

8.4.2.1 设置docker仓库

1、安装所需的包。 yum-utils提供yum-config-manager实用程序,devicemapper存储驱动程序需要device-mapper-persistent-data和lvm2。

$ sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

2、使用以下命令设置稳定(Stable)存储库。即使您还想从边缘(Edge)或测试(Test)存储库安装构建,您始终需要稳定(Stable)的存储库。

$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

3、可选:启用边缘和测试存储库。这些存储库包含在上面的docker.repo文件中,但默认情况下处于禁用状态。您可以将它们与稳定存储库一起启用。

$ sudo yum-config-manager --enable docker-ce-edge
$ sudo yum-config-manager --enable docker-ce-test

您可以通过使用–disable标志运行yum-config-manager命令来禁用边缘或测试存储库。要重新启用它,请使用–enable标志。以下命令禁用边缘存储库。

$ sudo yum-config-manager --disable docker-ce-edge

**注意:**从Docker 17.06开始,稳定版本也会被推送到边缘并测试存储库。

了解稳定stable) 和 边缘(edge) 构建。.

8.4.2.2 安装 Docker CE

1、安装最新版本的Docker CE,或转到下一步安装特定版本:

$ sudo yum install docker-ce

如果提示接受GPG密钥,请确认指纹符合060A 61C5 1B55 8A7F 742B 77AA C52F EB6B 621E 9F35,如果符合,请接受。

有多个Docker存储库? 如果您启用了多个Docker存储库,则在未指定yum install或yum update命令中的版本的情况下安装或更新将始终安装尽可能高的版本,这可能不适合您的稳定性需求。

Docker已安装但尚未启动。已创建docker组,但未向该组添加任何用户。

2、要安装特定版本的Docker CE,请列出repo中的可用版本,然后选择并安装:

a、列出并对您的仓库中可用的版本进行排序。此示例按版本号对结果进行排序,从最高到最低,并被截断:

$ yum list docker-ce --showduplicates | sort -r

docker-ce.x86_64            18.03.0.ce-1.el7.centos             docker-ce-stable

返回的列表取决于启用的存储库,并且特定于您的CentOS版本(在此示例中以.el7后缀表示)。

b、通过其完全限定的包名称安装特定版本,包名称(docker-ce)加上版本字符串(第2列)直到第一个连字符,用连字符( -)分隔,例如,docker-ce- 18.03.0.ce.

$ sudo yum install docker-ce-<VERSION STRING>

Docker已安装但尚未启动。已创建docker组,但未向该组添加任何用户。

#验证验证
$ docker -v
Docker version 18.03.0, build 4d60db4

3、启动Docker。

$ sudo systemctl start docker

4、通过运行hello-world镜像验证是否正确安装了docker

sudo docker run hello-world

此命令下载测试镜像并在容器中运行它。当容器运行时,它会打印一条信息性消息并退出。

Docker CE已安装并正在运行。您需要使用sudo来运行Docker命令。继续Linux postinstall 以允许非特权用户运行Docker命令和其他可选配置步骤。

8.4.2.3 升级DOCKER CE

要升级Docker CE,请按照安装说明( installation instructions )选择要安装的新版本。

8.4.4 从包安装Docker

如果您无法使用Docker的存储库来安装Docker,则可以下载适用于您的发行版的.rpm文件并手动安装。每次要升级Docker时都需要下载新文件。

这种方式安装具体请参考官方文档。

官方文档地址:https://docs.docker.com/install/linux/docker-ce/centos/#upgrade-docker-ce

8.4.5 脚本自动安装

在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS系统上可以使用这套脚本安装:

[root@localhost ~]# curl -fsSL get.docker.com -o get-docker.sh
[root@localhost ~]# sudo sh get-docker.sh --mirror Aliyun

执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker CE 的 Edge 版本安装在系统中。

8.4.6 卸载 Docker CE

8.4.6.1 卸载Docker包
$ sudo yum remove docker-ce
8.2.6.2 删除卸载数据

卸载Docker主机上的镜像,容器,卷或自定义配置文件不会自动删除。要删除所有镜像,容器和卷:

$ sudo rm -rf /var/lib/docker
8.2.6.3 卸载旧版本

较旧版本的Docker被称为docker或docker-engine。如果已安装这些,请卸载它们以及相关的依赖项。

$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-selinux \
                  docker-engine-selinux \
                  docker-engine

如果yum报告没有安装这些软件包,那就没关系。

保留/var/lib/docker/的内容,包括镜像,容器,卷和网络。 Docker CE包现在称为docker-ce。

8.4.7 查看docker信息

# 查看docker版本
$docker version

# 显示docker系统的信息
$docker info

8.4.8 登录registry server

# 登陆registry server; -e, --email="" Email; -p, --password="" Password; -u, --username="" Username

$docker login

8.4.9 Docker基本组件

DockerImage:Docker镜像是一个运行容器的只读模板。

DockerContainer:Docker容器是一个运行应用的标准化单元。

DockerRegistry:Docker注册服务器用来存放镜像。

DockerEngine:Docker引擎用来在主机上创建,运行和管理容器。

8.5 docker 配置

配置Linux主机以使用Docker更好地工作 。

8.5.1 以非root用户身份管理Docker

docker守护进程绑定到 Unix socket 而不是TCP端口。默认情况下,Unix套接字由用户root拥有,而其他用户只能使用sudo访问它。 docker守护进程始终以root用户身份运行。 

如果您在使用docker命令时不想使用sudo,请创建一个名为docker的Unix组并向其添加用户。当docker守护程序启动时,它会使docker组对Unix socket 的所有权进行read/write。 

警告:docker组授予与root用户等效的权限。有关如何影响系统安全性的详细信息,请参阅Docker Daemon Attack Surface

要创建docker组并添加您的用户:

1、创建docker组。

[root@localhost ~]# sudo groupadd docker

2、将您的用户添加到docker组。

[dockerUser@localhost ~]# sudo usermod -aG docker $USER

3、注销并重新登录,以便重新评估您的组成员身份。

如果在虚拟机上进行测试,则可能需要重新启动虚拟机才能使更改生效。

在桌面Linux环境(如X Windows)上,完全注销会话,然后重新登录。

4、验证您是否可以在没有sudo的情况下运行docker命令。

$ docker run hello-world

此命令下载测试映像并在容器中运行它。当容器运行时,它会打印一条信息性消息并退出。

如果在将用户添加到docker组之前最初使用sudo运行Docker CLI命令,则可能会看到以下错误,这表示由于sudo命令而创建的〜/ .docker /目录的权限不正确。

WARNING: Error loading config file: /home/user/.docker/config.json -
stat /home/user/.docker/config.json: permission denied

要解决此问题,请删除〜/ .docker /目录(它会自动重新创建,但任何自定义设置都将丢失),或使用以下命令更改其所有权和权限:

$ sudo chown "$USER":"$USER" /home/"$USER"/.docker -R
$ sudo chmod g+rwx "/home/$USER/.docker" -R

8.5.2 设置开机自启动

大多数当前的Linux发行版(RHEL,CentOS,Fedora,Ubuntu 16.04及更高版本)使用systemd来管理系统启动时的启动服务。

systemd
$ sudo systemctl enable docker

要禁用此行为,请改用disable。

$ sudo systemctl disable docker

如果需要添加HTTP代理,为Docker运行时文件设置不同的目录或分区,或进行其他自定义,请参阅自定义systemd Docker守护程序选项(customize your systemd Docker daemon options)。

upstart

Docker自动配置为使用upstart启动时启动。要禁用此行为,请使用以下命令:

$ echo manual | sudo tee /etc/init/docker.override
chkconfig
$ sudo chkconfig docker on

8.5.3 使用其他存储引擎

有关不同存储引擎的信息,请参阅存储驱动程序(Storage drivers)。默认存储引擎和支持的存储引擎列表取决于主机的Linux发行版和可用的内核驱动程序。

8.5.4 配置Docker守护程序侦听连接的位置

默认情况下,Docker守护程序侦听UNIX Socket上的连接以接受来自本地客户端的请求。通过将Docker配置为侦听IP地址和端口以及UNIX Socket,可以允许Docker接受来自远程主机的请求。有关此配置选项的更多详细信息,请参阅Docker CLI参考( Docker CLI Reference )文章中的“将Docker绑定到另一个主机/端口或unix Socket ”部分。

在配置Docker以接受来自远程主机的连接之前,了解打开docker到网络的安全隐患至关重要。如果不采取措施来保护连接,则远程非root用户可以在主机上获得root访问权限。有关如何使用TLS证书保护此连接的更多信息,请查看有关如何保护Docker守护程序套接字的文章(how to protect the Docker daemon socket)。

配置Docker以接受远程连接可以使用systemd的Linux发行版的docker.service systemd unit file 来完成,例如RedHat,CentOS,Ubuntu和SLES的最新版本,或者推荐用于Linux发行版的daemon.json文件。不要使用systemd。

systemd vs daemon.json

使用systemd单元文件和daemon.json文件配置docker以侦听连接会导致阻止Docker启动的冲突。

8.5.4.1 使用 systemd unit file 配置远程访问

1、使用命令sudo systemctl edit docker.service在文本编辑器中打开docker.service的覆盖文件。

$ sudo vi /usr/lib/systemd/system/docker.service 

2、添加或修改以下行,替换您自己的值。

[Service]
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// -H tcp://127.0.0.1:2375

3、保存文件。

4、重新加载systemctl配置

$ firewall-cmd --zone=public --add-port=2375/tcp --permanent && \
systemctl reload firewalld && \
systemctl daemon-reload && \
systemctl start docker || \
systemctl restart docker

5、重启Docker。

$ sudo systemctl restart docker.service

6、通过查看netstat的输出以确认dockerd正在侦听已配置的端口,检查更改是否得到遵守。

$ sudo netstat -lntp | grep dockerd
tcp        0      0 127.0.0.1:2375          0.0.0.0:*               LISTEN      3758/dockerd
8.5.4.2 使用daemon.json配置远程访问

1、在/etc/docker/daemon.json中设置hosts数组以连接到UNIX Socket 和IP地址,如下所示:

$ sudo vi /etc/docker/daemon.json
{
"hosts": ["unix:///var/run/docker.sock", "tcp://127.0.0.1:2375"]
}

2、重启Docker。

3、通过查看netstat的输出以确认dockerd正在侦听已配置的端口,检查更改是否得到遵守。

$ sudo netstat -lntp | grep dockerd
tcp        0      0 127.0.0.1:2375          0.0.0.0:*               LISTEN      3758/dockerd

8.5.5 在Docker守护程序上启用IPv6

要在Docker守护程序上启用IPv6,请参阅 Enable IPv6 support.

8.5.6 故障排除(Troubleshooting)

8.5.6.1 内核兼容性(Kernel compatibility)

如果您的内核早于3.10版本或者缺少某些模块,则Docker无法正常运行。要检查内核兼容性,可以下载并运行check-config.sh脚本。

$ curl https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh > check-config.sh

$ bash ./check-config.sh

该脚本仅适用于Linux,而不适用于macOS

Cannot connect to the Docker daemon

如果您看到如下所示的错误,则可能将Docker客户端配置为连接到其他主机上的Docker守护程序,并且该主机可能无法访问。

Cannot connect to the Docker daemon. Is 'docker daemon' running on this host?

要查看客户端配置连接到哪个主机,请检查环境中DOCKER_HOST变量的值。

$ env | grep DOCKER_HOST

如果此命令返回值,则Docker客户端将设置为连接到在该主机上运行的Docker守护程序。如果未设置,则Docker客户端将设置为连接到本地主机上运行的Docker守护程序。如果设置错误,请使用以下命令取消设置:

$ unset DOCKER_HOST

您可能需要在〜/.bashrc或〜/.profile等文件中编辑环境,以防止错误地设置DOCKER_HOST变量。

如果DOCKER_HOST设置为预期,请验证Docker守护程序是否在远程主机上运行,并且防火墙或网络中断不会阻止您进行连接。

8.5.6.2 IP 转发问题(IP forwarding problems)

如果使用systemd-network使用systemd版本219或更高版本手动配置网络,则Docker容器可能无法访问您的网络。从systemd版本220开始, 给定网络的转发设置(net.ipv4.conf。<interface>.forwarding)默认为off。 此设置可防止IP转发。它还与Docker在容器中启用net.ipv4.conf.all.forwarding设置的行为相冲突。

要在RHEL,CentOS或Fedora上解决此问题,请编辑Docker主机上/usr/lib/systemd/network/中的<interface>.network文件(例如:/usr/lib/systemd/network/80-container-host0.network)并在[Network]部分中添加以下块。

[Network]
...
IPForward=kernel
# OR
IPForward=true
...

此配置允许按预期从容器进行IP转发。

DNS resolver found in resolv.conf and containers can't use it

使用GUI的Linux系统通常运行网络管理器,该网络管理器使用在环回地址(如127.0.0.1127.0.1.1)上运行的dnsmasq实例来缓存DNS请求,并将此条目添加到/etc/resolv.confdnsmasq服务可加速DNS查找并提供DHCP服务。此配置在具有自己的网络命名空间的Docker容器中不起作用,因为Docker容器将诸如127.0.0.1之类的环回地址解析为自身,并且它不太可能在其自己的环回地址上运行DNS服务器。

如果Docker检测到/etc/resolv.conf中没有引用的DNS服务器是功能完备的DNS服务器,则会出现以下警告,并且Docker使用Google提供的公共DNS服务器(8.8.8.88.8.4.4)进行DNS解析。

WARNING: Local (127.0.0.1) DNS resolver found in resolv.conf and containers
can't use it. Using default external servers : [8.8.8.8 8.8.4.4]

如果您看到此警告,请首先检查您是否使用dnsmasq

$ ps aux |grep dnsmasq

如果您的容器需要解析网络内部的主机,则公共名称服务器不够用。你有两个选择:

  • 您可以为Docker指定要使用的DNS服务器,或
  • 您可以在NetworkManager中禁用dnsmasq。如果这样做,NetworkManager会将您的真实DNS名称服务器添加到/etc/resolv.conf,但是您将失去dnsmasq的可能优势。

您只需要使用这些方法之一。

8.5.6.3 为Docker指定DNS服务器

配置文件的默认位置是/etc/docker/daemon.json。您可以使用--config-file守护程序标志更改配置文件的位置。以下文档假定配置文件位于/etc/docker/daemon.json

1、创建或编辑Docker守护程序配置文件,该文件默认为/etc/docker/daemon.json文件,该文件控制Docker守护程序配置。

$ sudo nano /etc/docker/daemon.json

2、添加一个带有一个或多个IP地址的dns密钥作为值。如果文件包含现有内容,则只需添加或编辑dns行。

{
	"dns": ["8.8.8.8", "8.8.4.4"]
}

如果您的内部DNS服务器无法解析公共IP地址,请至少包含一个DNS服务器,以便您可以连接到Docker Hub,以便您的容器可以解析Internet域名。

保存并关闭文件。

3、重新启动Docker守护程序。

$ sudo service docker restart

4、验证Docker是否可以通过尝试拉取图像来解析外部IP地址:

$ docker pull hello-world

5、如有必要,请验证Docker容器是否可以通过ping它来解析内部主机名。

$ docker run --rm -it alpine ping -c4 <my_internal_host>

PING google.com (192.168.1.2): 56 data bytes
64 bytes from 192.168.1.2: seq=0 ttl=41 time=7.597 ms
64 bytes from 192.168.1.2: seq=1 ttl=41 time=7.635 ms
64 bytes from 192.168.1.2: seq=2 ttl=41 time=7.660 ms
64 bytes from 192.168.1.2: seq=3 ttl=41 time=7.677 ms
DISABLE DNSMASQ

要在RHEL,CentOS或Fedora上禁用dnsmasq(ubuntu的请参考官方文档处理):

1、禁用dnsmasq服务

$ sudo service dnsmasq stop

$ sudo systemctl disable dnsmasq

2、使用Red Hat文档(Red Hat documentation)手动配置DNS服务器。

8.5.6.4 允许通过防火墙访问远程API

如果在运行Docker的同一主机上运行防火墙,并且要从其他主机访问Docker Remote API并启用远程访问,则需要配置防火墙以允许Docker端口上的传入连接,默认为2376如果启用了TLS加密传输,否则为2375

两个常见的防火墙守护程序是UFW(简单防火墙 UFW (Uncomplicated Firewall) )(通常用于Ubuntu系统)和firewalld(通常用于基于RPM的系统)。请参阅操作系统和防火墙的文档,但以下信息可能有助于您入门。这些选项相当宽松,您可能希望使用不同的配置来更好地锁定系统。

  • UFW:在配置中设置DEFAULT_FORWARD_POLICY =“ACCEPT”
  • firewalld:在策略中添加与以下类似的规则(一个用于传入请求,另一个用于传出请求)。确保接口名称和链名称正确。
<direct>
  [ <rule ipv="ipv6" table="filter" chain="FORWARD_direct" priority="0"> -i zt0 -j ACCEPT </rule> ]
  [ <rule ipv="ipv6" table="filter" chain="FORWARD_direct" priority="0"> -o zt0 -j ACCEPT </rule> ]
</direct>
Your kernel does not support cgroup swap limit capabilities

在Ubuntu或Debian主机上,使用图像时,您可能会看到类似于以下内容的消息。

WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.

在基于RPM的系统上不会发生此警告,这些系统默认启用这些功能。

如果您不需要这些功能,则可以忽略该警告。您可以按照这些说明在Ubuntu或Debian上启用这些功能。即使Docker未运行,内存和交换计费也会占总可用内存的1%左右,总体性能降低10%。

1、以具有sudo权限的用户身份登录Ubuntu或Debian主机。

2、编辑/etc/default/grub文件。添加或编辑GRUB_CMDLINE_LINUX行以添加以下两个键值对:

GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

保存并关闭文件。

3、更新GRUB。

$ sudo update-grub

如果GRUB配置文件的语法不正确,则会发生错误。在这种情况下,请重复步骤3和4。

重新启动系统后,更改将生效。

8.5.7 添加内核参数

默认配置下,如果在 CentOS 使用 Docker CE 看到下面的这些警告信息:

WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

请添加内核配置参数以启用这些功能。

[root@localhost ~]# sudo tee -a /etc/sysctl.conf <<-EOF
> net.bridge.bridge-nf-call-ip6tables = 1
> net.bridge.bridge-nf-call-iptables = 1
> EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

然后重新加载 sysctl.conf 即可

[root@localhost ~]# sudo sysctl -p
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1

8.6 dockerfile

Dockfile是一种被Docker程序解释的脚本,Dockerfile由一条一条的指令组成,每条指令对应Linux下面的一条命令。Docker程序将这些Dockerfile指令翻译真正的Linux命令。

Dockerfile有自己书写格式和支持的命令,Docker程序解决这些命令间的依赖关系,类似于Makefile。Docker程序将读取Dockerfile,根据指令生成定制的image。

相比image这种黑盒子,Dockerfile这种显而易见的脚本更容易被使用者接受,它明确的表明image是怎么产生的。

有了Dockerfile,当我们需要定制自己额外的需求时,只需在Dockerfile上添加或者修改指令,重新生成image即可,省去了敲命令的麻烦。

8.6.1每一行都是 INSTRUCTION statement

FROM docker/whalesay:latest

RUN apt-get -y update && apt-get install -y fortunes  
# run指令会在前一条命令创建出的镜像的基础上创建一个容器,并在容器中运行命令,结束后又提交容器为新镜像,新镜像被dockerfile的下一条指令使用
RUN ["/bin/bash","-c","echo hello"]

CMD /usr/games/fortune -a | cowsay   
# cmd指令提供容器运行时的默认值,这些默认值可以是一条指令,也可以是一些参数。dockerfile中可以有多条cmd指令,但只有最后一条有效。cmd指令在构建镜像时并不执行任何命令,而是在容器启动时默认将cmd指令作为第一条执行的命令。如果使用 docker run 命令时指定了命令参数,则会覆盖CMD指令中的命令。

ENTRYPOINT  
# 和cmd指令类似,都可以让容器在每次启动时执行相同的命令,同样也是最后一条ENTRYPOINT指令有效。但区别是,docker run 命令提供的运行命令参数不能覆盖ENTRYPOINT. CMD(或docker run命令中指定的命令) 和 ENTRYPOINT 都存在时,CMD 的指令作为 ENTRYPOINT 的参数。

8.6.2 CMD和ENTRYPOINT和RUN都有两种写法

一种是直接写命令,
还有一种是[“cmdOrParam1”,“cmdOrParam2”]这样的写法,
如果直接写命令就会被解释为["/bin/sh","-c",“oriCmd”]

EXPOSE <port> [<port> ...]

ENV <key> <value>
ENV <key>=<value> ...
RUN <key>=<value> <command>  
#只在该命令行上有效的环境变量

WORKDIR /path  
#相当于cd

VOLUME ["/data"]  
# 创建一个可以从本地主机或其他容器挂载的挂载点,
# 需要注意
# 注意1 不能指定本地文件夹 
# 注意2 在Dockerfile中对/data进行修改的RUN命令会全部不生效,因为VOLUME指令是在容器运行时才去挂载,而RUN指令是在构造镜像的时候就会执行。

ONBUILD [INSTRUCTION] 
#当所创建的镜像作为其他新创建镜像的基础镜像时,所执行的操作指令

ADD/COPY <src> ... <dest>  
# 将文件或文件夹或远程URLs表示的文件拷贝到指定路径,使用COPY,不要使用ADD

HEALTHCHECK [OPTIONS] CMD command (check container health by running a command inside the container)
HEALTHCHECK NONE (disable any healthcheck inherited from the base image)
选项有:

  • –interval=DURATION (default: 30s)
  • –timeout=DURATION (default: 30s)
  • –retries=N (default: 3)

命令的exit状态表明健康状态,可能的值有0表示成功,1表示不健康,2还没有使用。 例子:

HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1

命令写到stdout或stderr的任何的输出文本(UTF-8编码)都会被存储到health status,并且可以被 docker inspect 检索到。这些输出只能保存4096 bytes。

当container的健康状态发送变化时,一个事件 health_status就被生成了。

8.6.4 dockerfile build

8.6.4.1 使用 Dockerfile 构建出一个容器
#build
--no-cache=false Do not use cache when building the image
-q, --quiet=false Suppress the verbose output generated by the containers
--rm=true Remove intermediate containers after a successful build
-t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success

$docker build -t image_name Dockerfile_path
8.6.4.2 使用Dockerfile创建ssh服务自启动容器镜像
  1. 首先创建一个Dockerfile文件,文件内容如下
# 选择一个已有的os镜像作为基础
FROM centos:centos6

# 镜像的作者
MAINTAINER Fanbin Kong "kongxx@hotmail.com"

# 安装openssh-server和sudo软件包,并且将sshd的UsePAM参数设置成no
RUN yum install -y openssh-server sudo
RUN sed -i 's/UsePAM yes/UsePAM no/g' /etc/ssh/sshd_config

# 添加测试用户admin,密码admin,并且将此用户添加到sudoers里
RUN useradd admin
RUN echo "admin:admin" | chpasswd
RUN echo "admin   ALL=(ALL)       ALL" >> /etc/sudoers

# 下面这两句比较特殊,在centos6上必须要有,否则创建出来的容器sshd不能登录
RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key

# 启动sshd服务并且暴露22端口
RUN mkdir /var/run/sshd
EXPOSE 22

CMD ["/usr/sbin/sshd", "-D"]

Dockerfile文件有自己的语法和命令,具体可以参考Docker的官方文档。

  1. 有了Dockerfile文件以后,就可以根据Dockerfile来创建image文件了,在Dockerfile所在的目录下,运行下面的命令
$ sudo docker build -t centos6-ssh .

命令成功后,就会创建一个名字为centos6-ssh的image,可以使用“sudo docker images”来查看。

  1. 此时就可以根据上面创建出来的image文件来创建自己的容器了,下面的命令会创建一个名字为“mytest”的容器。
$ sudo docker run -d -P --name=mytest centos6-ssh

有了容器,就可以测试我们的ssh服务了。

4.1 运行“sudo docker inspect mytest”,查看当前启动容器IP地址,然后运行下面的命令来测试

$ ssh admin@<容器IP>

4.2 另外,也可以通过docker的端口映射来访问, 使用“sudo docker port mytest 22”查看当前容器的22端口对应的宿主机器的端口,然后通过下面的命令来访问

$ ssh admin@<宿主机器IP> -p <宿主机器端口>

8.7 docker image

search、pull、images、rmi、history操作

# 检索image
$docker search image_name

# 下载image
$docker pull image_name

# 列出镜像列表; -a, --all=false Show all images; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
$docker images

# 删除一个或者多个镜像; -f, --force=false Force; --no-prune=false Do not delete untagged parents
$docker rmi image_name


# 显示一个镜像的历史; --no-trunc=false Don't truncate output; -q, --quiet=false Only show numeric IDs
$docker history image_name

发布image(push)

# 发布docker镜像
$docker push new_image_name

保存和加载镜像(save、load)

当需要把一台机器上的镜像迁移到另一台机器的时候,需要保存镜像与加载镜像。

# 保存镜像到一个tar包; -o, --output="" Write to an file
$docker save image_name -o file_path

# 加载一个tar包格式的镜像; -i, --input="" Read from a tar archive file
$docker load -i file_path

# 机器a
$docker save image_name > /home/save.tar

# 使用scp将save.tar拷到机器b上,然后:
$docker load < /home/save.tar

8.8 docker container

docker容器可以理解为在沙盒中运行的进程。这个沙盒包含了该进程运行所必须的资源,包括文件系统、系统类库、shell 环境等等。但这个沙盒默认是不会运行任何程序的。你需要在沙盒中运行一个进程来启动某一个容器。这个进程是该容器的唯一进程,所以当该进程结束的时候,容器也会完全的停止。

8.8.1 常用操作命令

8.8.1.1 run container
# 在容器中运行"echo"命令,输出"hello word"
$docker run image_name echo "hello word"

# 交互式进入容器中
$docker run -i -t image_name /bin/bash

# 在容器中安装新的程序
$docker run image_name apt-get install -y app_name

Note: 在执行apt-get 命令的时候,要带上-y参数。如果不指定-y参数的话,apt-get命令会进入交互模式,需要用户输入命令来进行确认,但在docker环境中是无法响应这种交互的。apt-get 命令执行完毕之后,容器就会停止,但对容器的改动不会丢失。

8.8.1.2 查看容器(ps)
# 列出当前所有正在运行的container
$docker ps

# 列出所有的container
$docker ps -a

# 列出最近一次启动的container
$docker ps -l
8.8.1.3 保存容器的修改(commit)

当你对某一个容器做了修改之后(通过在容器中运行某一个命令),可以把对容器的修改保存下来,这样下次可以从保存后的最新状态运行该容器。

# 保存对容器的修改; -a, --author="" Author; -m, --message="" Commit message
$ docker commit ID new_image_name

Note: image相当于类,container相当于实例,不过可以动态给实例安装新软件,然后把这个container用commit命令固化成一个image。

8.8.1.4 删除容器
# 删除所有容器
$docker rm `docker ps -a -q`

# 删除单个容器; -f, --force=false; -l, --link=false Remove the specified link and not the underlying container; -v, --volumes=false Remove the volumes associated to the container
$docker rm Name/ID
8.8.1.5 启动、停止、重启、附加和杀死容器
# 启动一个容器
$docker start Name/ID

# 停止一个容器
$docker stop Name/ID

# 重启一个正在运行的容器; -t, --time=10 Number of seconds to try to stop for before killing the container, Default=10
$docker restart Name/ID

# 附加到一个运行的容器上面; --no-stdin=false Do not attach stdin; --sig-proxy=true Proxify all received signal to the process
$docker attach ID

# 杀死一个容器
$docker kill Name/ID
8.8.1.6 查看容器日志
# 从一个容器中取日志; -f, --follow=false Follow log output; -t, --timestamps=false Show timestamps
$docker logs Name/ID
8.8.1.7 列出容器差异
# 列出一个容器里面被改变的文件或者目录,list列表会显示出三种事件,A 增加的,D 删除的,C 被改变的
$ docker diff Name/ID
8.8.1.8 查看容器进程信息
# 显示一个运行的容器里面的进程信息
$docker top Name/ID
8.8.1.9 容器拷贝文件/目录到本地
# 从容器里面拷贝文件/目录到本地一个路径
$docker cp Name:/container_path to_path
$docker cp ID:/container_path to_path

Note: attach命令允许你查看或者影响一个运行的容器。你可以在同一时间attach同一个容器。你也可以从一个容器中脱离出来,是从CTRL-C。

8.8.2 批量删除 Exited 容器

方法一:

#显示所有的容器,过滤出Exited状态的容器,取出这些容器的ID,

$ sudo docker ps -a|grep Exited|awk '{print $1}'

#查询所有的容器,过滤出Exited状态的容器,列出容器ID,删除这些容器

$ sudo docker rm docker ps -a|grep Exited|awk '{print $1}'

方法二:

#删除所有未运行的容器(已经运行的删除不了,未运行的就一起被删除了)

$ sudo docker rm $(sudo docker ps -a -q)

方法三:

#根据容器的状态,删除Exited状态的容器

$ sudo docker rm $(sudo docker ps -qf status=exited)

查询所有的容器,过滤出状态为Exited的容器

方法四:

#Docker 1.13版本以后,可以使用 docker containers prune 命令,删除孤立的容器。

$ sudo docker container prune

低于1.13版本的Docker,可以根据容器的状态来进行删除

Docker 1.13版本以后,开始支持prune命令,快速删除已退出的容器

删除所有镜像

删除所有的容器,所有未运行的容器都被删除,正在运行的无法删除,达到删除不用容器的目的。

$ sudo docker rmi $(docker images -q)

8.8.3 容器中文乱码

解决 docker 容器中文乱码问题,修改docker容器编码格式。

前台上传文件到服务器后,服务器返回给前台的文件列表中出现中文乱码,所有的中文文件名全部变成?,英文文件名则正常显示。

问题经过定位,发现后台代码的multipartfile类在执行transterto的方法时就发生了此异常,然而配置文件集中的multipartResolver以及encodingFilter均已经设置成了UTF-8,排除代码异常。kubectl exec进入到docker容器中进行查看时发现,文件在容器中也是中文文件名显示异常。查看docker容器编码格式:执行locale命令;可以看到当前编码格式为POSIX,而这种编码格式不支持中文

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EdX0wlFU-1599523586927)(…/%E6%8A%80%E6%9C%AF%E5%AE%9E%E8%B7%B5%E6%96%87%E7%AB%A0/assets/902576-20161209101405116-4019287.png)]

解决办法:locale -a查看容器所有语言环境

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kYsuB00m-1599523586929)(…/%E6%8A%80%E6%9C%AF%E5%AE%9E%E8%B7%B5%E6%96%87%E7%AB%A0/assets/902576-20161209101723007-416325100.png)]

C.UTF-8可以支持中文,只需要把容器编码设置为C.UTF-8即可

1.临时修改:

   locale

   locale -a

   LANG=C.UTF-8  (有的是zh_CN.UTF-8,不过我在本地没发现这种编码)

   source /etc/profile

2.永久修改:修改Dockerfile

  在Dockerfile中添加一行

  ENV LANG C.UTF-8

  重新制作docker镜像,docker run -ti [镜像] 进入容器后执行locale发现编码格式已经被修改为C.UTF-8,之前出现的中文文件名乱码问题也没有了。

8.9 docker network

  • (1)bridge模式:默认的模式。使用一个 linux bridge,默认为 docker0,使用 veth 对,一头在容器的网络 namespace 中,一头在 docker0 上。
  • (2)Host模式:该模式下的 Docker 容器会和 host 宿主机共享同一个网络 namespace,故 Docker Container可以和宿主机一样,使用宿主机的eth0,实现和外界的通信。换言之,Docker Container的 IP 地址即为宿主机 eth0 的 IP 地址。 这种模式下的容器没有隔离的 network namespace。
  • (3)container模式: Container 网络模式是 Docker 中一种较为特别的网络的模式。处于这个模式下的 Docker 容器会共享其他容器的网络环境,因此,至少这两个容器之间不存在网络隔离,而这两个容器又与宿主机以及除此之外其他的容器存在网络隔离。
$ docker run -d --network container:${anotherContainer} training/webapp python app.py
  • (4)none模式:只能使用loopback网络设备,不会再有其他的网络资源。

网络操作

#显示所有的network,默认使用的network是bridge
docker network ls 

#在输出中的Containers键中可以很容易地找出container的IP地址
docker network inspect bridge 

#将container从网络中移除
docker network disconnect bridge ${container} 

 #-d表示新的driver使用bridge driver,你可以省略这个参数,因为bridge就是默认的driver。
docker network create -d bridge my-bridge-network

#你会发现没什么内容,containers和options都是空的。
docker network inspect my-bridge-network 

#再启动一个container name为 db,指定加入哪一个network
docker run -d --network=my-bridge-network --name db training/postgres 

#如果你检查你的my-bridge-network,你会看到它已经有一个container附属于它了。
docker network inspect my-bridge-network 

#你还可以检查你的container来看看container连接到哪里了:
docker inspect --format='{{json .NetworkSettings.Networks}}'  db  

#docker networking允许你将一个container(即使
docker network connect my-bridge-network networktest 

8.9.1 port

在实际情况中我们有时为了做好端口规划,避免端口冲突,调整为自己合适的端口,这里以修改 jenkins端口为例,说明修改端口的两种方式。

8.9.1 使用已有容器重建镜像重建容器端口

已有 container 容器并在使用过程中产生过数据,为了保存容器数据同时也要做端口修改,建议重新生成一个新镜像 image。步骤如下:

1、将目标 container 容器停止。

# 此命令不会引起容器数据丢失,systemctl stop docker 会造成数据丢失
$ sudo docker stop jenkins

如果未经修改,要修改端口,可以直接使用原始镜像 image 重建 container 容器。

2、基于目标 container 容器创建一个新的镜像 image。

$ sudo commit jenkins new_jenkins

3、使用新创建镜像 image 重新建立 container 并指定所需的端口 port。

$ sudo docker run -d -p 8082:8080 new_jenkins

8082:为虚拟主机映射端口,8080:为容器 jenkins 端口

8.9.2 基于已有容器修改端口

1、停止目标 container 容器。

# 此命令不会引起容器数据丢失,systemctl stop docker 会造成数据丢失
$ sudo docker stop jenkins

2、修改 container 容器的 hostconfig.json

  • Linux 文件位置

/var/lib/docker/containers/{container_id}/hostconfig.json

  • Windows 文件位置:

C:\ProgramData\Docker\containers\{container_id}\hostconfig.json

  • 修改对应的端口
# 打开文件,文件内容已压缩过,很难看,可以借助json格式化工具,再修改
$ sudo vi hostconfig.json

## 修改文件,找到 "PortBindings" 位置在下面修改,增加端口配置
  "PortBindings": {
    "22/tcp": [
      {
        "HostIp": "",
        "HostPort": ""
      }
    ],
    "8080/tcp": [
      {
        "HostIp": "",
        "HostPort": "8082"
      }
    ]
  },

修改完,建议再将 json 数据压缩保存回去。

3、重启 docker engine。

$ sudo systemctl restart docker

4、重新启动 container 容器

$ sudo docker start jenkins

5、查看端口修改后的 container 效果

$ sudo docker ps -a

6、查看文件是否被修改

#文件文字看上面注释
$ sudo vi hostconfig.json

# 这个文件 docker 自己修改过来了
$ sudo vi config.v2.json

修改完,在外面宿主机就可以使用新设置的8082端口访问 jenkins了。

8.10.3 小结

基于 container 容器重新创建 image 镜像,依据性镜像重新生成容器,比较符合 container 的设计及使用。如果容器已有需要保存的修改,就该 commit 创建新的 image,然后使用新 image 来建立 container 同时重新设置port 映射才是正规做法。

只是操作思維上還是停留在非 container 時代,一時間想法沒辦法轉過來,怕影響資料或是設定之類的,才會打算修改現有 container 的對應。但定神細想,資料或是設定本來就不該放在 container 中,或者修改後本來就該 commit 避免資料遺失

雖然是簡單的操作,卻隱含著背景思維模式的衝擊呀

Dockerfile命令详解

前言

之前,制作镜像的伪姿势搭建已经见过了,今天介绍一下制作Docker镜像的正确姿势。

制作Dockerfile为Docker入门学习的第一步。Dockerfile 是一个文本格式的配置文件,用户可以使用 Dockerfile 快速创建自定义的镜像。我们会先介绍 Dockerfile 的基本结构及其支持的众多指令,并具体讲解通过执行指令来编写定制镜像的 Dockerfile。

以下为正文,下面进入学习姿势吧!

Dockerfile详解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBm3YUSm-1599523586933)(assets/501875919-1534044651936.jpg)]

Dockerfile.jpg

FROM

功能为指定基础镜像,并且必须是第一条指令。

如果不以任何镜像为基础,那么写法为:FROM scratch。

同时意味着接下来所写的指令将作为镜像的第一层开始

语法:

FROM
FROM :
FROM :
三种写法,其中和 是可选项,如果没有选择,那么默认值为latest

RUN

功能为运行指定的命令

RUN命令有两种格式

  1. RUN

  2. RUN [“executable”, “param1”, “param2”]

    第一种后边直接跟shell命令

在linux操作系统上默认 /bin/sh -c

在windows操作系统上默认 cmd /S /C

第二种是类似于函数调用。

可将executable理解成为可执行文件,后面就是两个参数。

两种写法比对:

RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME
RUN ["/bin/bash", “-c”, “echo hello”]
注意:多行命令不要写多个RUN,原因是Dockerfile中每一个指令都会建立一层.

多少个RUN就构建了多少层镜像,会造成镜像的臃肿、多层,不仅仅增加了构件部署的时间,还容易出错。

RUN书写时的换行符是\

CMD

功能为容器启动时要运行的命令

语法有三种写法

  1. CMD [“executable”,“param1”,“param2”]
  2. CMD [“param1”,“param2”]
  3. CMD command param1 param2
    第三种比较好理解了,就时shell这种执行方式和写法

第一种和第二种其实都是可执行文件加上参数的形式

举例说明两种写法:

CMD [ “sh”, “-c”, “echo H O M E " C M D [ " e c h o " , " HOME" CMD [ "echo", " HOME"CMD["echo","HOME” ]
补充细节:这里边包括参数的一定要用双引号,就是",不能是单引号。千万不能写成单引号。

原因是参数传递后,docker解析的是一个JSON array

RUN & CMD

不要把RUN和CMD搞混了。

RUN是构件容器时就运行的命令以及提交运行结果

CMD是容器启动时执行的命令,在构件时并不运行,构件时紧紧指定了这个命令到底是个什么样子

LABEL

功能是为镜像指定标签

语法:

LABEL = = = …
一个Dockerfile种可以有多个LABEL,如下:

LABEL “com.example.vendor”=“ACME Incorporated”
LABEL com.example.label-with-value=“foo”
LABEL version=“1.0”
LABEL description=“This text illustrates
that label-values can span multiple lines.”
但是并不建议这样写,最好就写成一行,如太长需要换行的话则使用符号

如下:

LABEL multi.label1=“value1”
multi.label2=“value2”
other=“value3”

说明:LABEL会继承基础镜像种的LABEL,如遇到key相同,则值覆盖

MAINTAINER

指定作者

语法:

MAINTAINER

EXPOSE

功能为暴漏容器运行时的监听端口给外部

但是EXPOSE并不会使容器访问主机的端口

如果想使得容器与主机的端口有映射关系,必须在容器启动的时候加上 -P参数

ENV

功能为设置环境变量

语法有两种

  • ENV
  • ENV = …

两者的区别就是第一种是一次设置一个,第二种是一次设置多个

ADD

一个复制命令,把文件复制到镜像中。

如果把虚拟机与容器想象成两台linux服务器的话,那么这个命令就类似于scp,只是scp需要加用户名和密码的权限验证,而ADD不用。

语法如下:

  • ADD …
  • ADD ["",… “”]

路径的填写可以是容器内的绝对路径,也可以是相对于工作目录的相对路径

可以是一个本地文件或者是一个本地压缩文件,还可以是一个url

如果把写成一个url,那么ADD就类似于wget命令

如以下写法都是可以的:

ADD test relativeDir/
ADD test /relativeDir
ADD http://example.com/foobar /
尽量不要把写成一个文件夹,如果是一个文件夹了,复制整个目录的内容,包括文件系统元数据

COPY

看这个名字就知道,又是一个复制命令

语法如下:

  • COPY …
  • COPY ["",… “”]

与ADD的区别

COPY的只能是本地文件,其他用法一致

ENTRYPOINT

功能是启动时的默认命令

语法如下:

  • ENTRYPOINT [“executable”, “param1”, “param2”]
  • ENTRYPOINT command param1 param2

如果从上到下看到这里的话,那么你应该对这两种语法很熟悉啦。

第二种就是写shell

第一种就是可执行文件加参数

与CMD比较说明(这俩命令太像了,而且还可以配合使用):

相同点:

  • 只能写一条,如果写了多条,那么只有最后一条生效
  • 容器启动时才运行,运行时机相同

不同点:

  • ENTRYPOINT不会被运行的command覆盖,而CMD则会被覆盖

如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD指令不是一个完整的可执行命令,那么CMD指定的内容将会作为ENTRYPOINT的参数

如下:

FROM ubuntu
ENTRYPOINT [“top”, “-b”]
CMD ["-c"]
如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效

如下:

FROM ubuntu
ENTRYPOINT [“top”, “-b”]
CMD ls -al
那么将执行ls -al ,top -b不会执行。

VOLUME

可实现挂载功能,可以将内地文件夹或者其他容器种得文件夹挂在到这个容器种

语法为:

VOLUME ["/data"]

说明:

["/data"]可以是一个JsonArray ,也可以是多个值。所以如下几种写法都是正确的

VOLUME ["/var/log/"]
VOLUME /var/log
VOLUME /var/log /var/db
一般的使用场景为需要持久化存储数据时

容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。

所以当数据需要持久化时用这个命令。

USER

设置启动容器的用户,可以是用户名或UID,所以,只有下面的两种写法是正确的

USER daemo
USER UID
注意:如果设置了容器以daemon用户去运行,那么RUN, CMD 和 ENTRYPOINT 都会以这个用户去运行

WORKDIR

语法:

WORKDIR /path/to/workdir

设置工作目录,对RUN,CMD,ENTRYPOINT,COPY,ADD生效。如果不存在则会创建,也可以设置多次。

如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
pwd执行的结果是/a/b/c

WORKDIR也可以解析环境变量

如:

ENV DIRPATH /path
WORKDIR D I R P A T H / DIRPATH/ DIRPATH/DIRNAME
RUN pwd
pwd的执行结果是/path/$DIRNAME

ARG

语法:

ARG [=]
设置变量命令,ARG命令定义了一个变量,在docker build创建镜像的时候,使用 --build-arg =来指定参数

如果用户在build镜像时指定了一个参数没有定义在Dockerfile种,那么将有一个Warning

提示如下:

[Warning] One or more build-args [foo] were not consumed.

我们可以定义一个或多个参数,如下:

FROM busybox
ARG user1
ARG buildno

也可以给参数一个默认值:

FROM busybox
ARG user1=someuser
ARG buildno=1

如果我们给了ARG定义的参数默认值,那么当build镜像时没有指定参数值,将会使用这个默认值

ONBUILD

语法:

ONBUILD [INSTRUCTION]
这个命令只对当前镜像的子镜像生效。

比如当前镜像为A,在Dockerfile种添加:

ONBUILD RUN ls -al
这个 ls -al 命令不会在A镜像构建或启动的时候执行

此时有一个镜像B是基于A镜像构建的,那么这个ls -al 命令会在B镜像构建的时候被执行。

STOPSIGNAL

语法:

STOPSIGNAL signal
STOPSIGNAL命令是的作用是当容器推出时给系统发送什么样的指令

HEALTHCHECK

容器健康状况检查命令

语法有两种:

  • HEALTHCHECK [OPTIONS] CMD command
  • HEALTHCHECK NONE

第一个的功能是在容器内部运行一个命令来检查容器的健康状况

第二个的功能是在基础镜像中取消健康检查命令

[OPTIONS]的选项支持以下三中选项:

  • interval=DURATION 两次检查默认的时间间隔为30秒
  • timeout=DURATION 健康检查命令运行超时时长,默认30秒
  • retries=N 当连续失败指定次数后,则容器被认为是不健康的,状态为unhealthy,默认次数是3

注意:

HEALTHCHECK命令只能出现一次,如果出现了多次,只有最后一个生效。

CMD后边的命令的返回值决定了本次健康检查是否成功,具体的返回值如下:

  • 0: success - 表示容器是健康的
  • 1: unhealthy - 表示容器已经不能工作了
  • 2: reserved - 保留值

例子:

HEALTHCHECK --interval=5m --timeout=3s
CMD curl -f http://localhost/ || exit 1

健康检查命令是:curl -f http://localhost/ || exit 1

两次检查的间隔时间是5秒

命令超时时间为3秒

Dockerfile案例

部署JavaWeb项目

新建并编辑 Dockerfile:

# 指定基于的容器镜像
FROM tomcat
# 维护者信息
MAINTAINER "itstyle <345849402@qq.com>"
# 复制项目到Tomcat指定目录
ADD test.war /usr/local/tomcat/webapps/
# 容器启动时执行指令
CMD ["catalina.sh", "run"]

构建镜像:

$ docker build -t itstyle/tomcat .
  • -t:表示为当前镜像命名。
  • .(最后的点):表示当前目录

运行镜像:

$ docker run -d -p 8888:8080  --name app itstyle/tomcat
  • -d:表示指定容器后台运行
  • -p:表示宿主机的8080端口对外映射暴露为8888端口
  • -name:表示执行容器名称

题外话:

实际生产中,我们一般不会这么去做,项目运行过程中可能会上传一些图片或者音频,这些文件可不能跟Docker容器共存亡。一般我们会这么做:

新建并编辑 Dockerfile:

# 指定基于的容器镜像
FROM tomcat
# 维护者信息
MAINTAINER "itstyle <345849402@qq.com>"
# 容器启动时执行指令
CMD ["catalina.sh", "run"]

构建镜像:

docker build -t itstyle/tomcat .

运行镜像:

$ docker run -d -p 8888:8080 -v /home/docker/web:/usr/local/tomcat/webapps --name app itstyle/tomcat
  • /home/docker/web目录下存放项目War
部署SpringBoot微服务
# 基础镜像:仓库是java
FROM java:7-jre
# 当前镜像的维护者和联系方式
MAINTAINER itstyle 345849402@qq.com
# 挂载卷
VOLUME /tmp
# 将打包好的springBoot程序拷贝到容器中的指定位置
ADD itstyle_stats.jar /opt/app.jar
# 容器对外暴露端口
EXPOSE 8080
# 容器启动后需要执行的命令
CMD java -Djava.security.egd=file:/dev/./urandom -jar /opt/app.jar

# ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","-Denv=DEV","/opt/app.jar"]
  • Djava.security.egd=file:/dev/./urandom ,与JVM上的随机数与熵池策略有关,加快启动服务

建立 docker 用户组

默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

建立 docker 组 我本机安装完后已经自动创建:

将当前用户加入 docker 组:

 $ sudo usermod -aG docker USER

退出当前终端并重新登录,进行如下测试。

root账户将用户加入docker用户组

[root@node6 ~]# usermod -aG docker ringyin
[root@node6 ~]# su ringyin
[ringyin@node6 root]$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                                                                                        NAMES
a747f4b2d711        mongo:4.0.3                 "docker-entrypoint.s…"   2 weeks ago         Up 5 days           0.0.0.0:27017->27017/tcp                           

没有加入docker用户组,会提示权限不足,需要加sudo

[ringyin@node6 ~]$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.38/containers/json: dial unix /var/run/docker.sock: connect: permission denied

参考

最后的点):表示当前目录

运行镜像:

$ docker run -d -p 8888:8080  --name app itstyle/tomcat
  • -d:表示指定容器后台运行
  • -p:表示宿主机的8080端口对外映射暴露为8888端口
  • -name:表示执行容器名称

题外话:

实际生产中,我们一般不会这么去做,项目运行过程中可能会上传一些图片或者音频,这些文件可不能跟Docker容器共存亡。一般我们会这么做:

新建并编辑 Dockerfile:

# 指定基于的容器镜像
FROM tomcat
# 维护者信息
MAINTAINER "itstyle <345849402@qq.com>"
# 容器启动时执行指令
CMD ["catalina.sh", "run"]

构建镜像:

docker build -t itstyle/tomcat .

运行镜像:

$ docker run -d -p 8888:8080 -v /home/docker/web:/usr/local/tomcat/webapps --name app itstyle/tomcat
  • /home/docker/web目录下存放项目War
部署SpringBoot微服务
# 基础镜像:仓库是java
FROM java:7-jre
# 当前镜像的维护者和联系方式
MAINTAINER itstyle 345849402@qq.com
# 挂载卷
VOLUME /tmp
# 将打包好的springBoot程序拷贝到容器中的指定位置
ADD itstyle_stats.jar /opt/app.jar
# 容器对外暴露端口
EXPOSE 8080
# 容器启动后需要执行的命令
CMD java -Djava.security.egd=file:/dev/./urandom -jar /opt/app.jar

# ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","-Denv=DEV","/opt/app.jar"]
  • Djava.security.egd=file:/dev/./urandom ,与JVM上的随机数与熵池策略有关,加快启动服务

建立 docker 用户组

默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

建立 docker 组 我本机安装完后已经自动创建:

将当前用户加入 docker 组:

 $ sudo usermod -aG docker USER

退出当前终端并重新登录,进行如下测试。

root账户将用户加入docker用户组

[root@node6 ~]# usermod -aG docker ringyin
[root@node6 ~]# su ringyin
[ringyin@node6 root]$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                                                                                        NAMES
a747f4b2d711        mongo:4.0.3                 "docker-entrypoint.s…"   2 weeks ago         Up 5 days           0.0.0.0:27017->27017/tcp                           

没有加入docker用户组,会提示权限不足,需要加sudo

[ringyin@node6 ~]$ docker ps
Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.38/containers/json: dial unix /var/run/docker.sock: connect: permission denied

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值