Docker-秘籍(全)
Docker 秘籍(全)
原文:
zh.annas-archive.org/md5/3BDF7E02FD45D3E3DF6846ABA9F12FB8
译者:飞龙
前言
使用 Docker^(TM),容器正在成为主流,企业已准备好在生产中使用它们。这本书专门设计帮助您快速掌握最新的 Docker 版本,并让您有信心在生产中使用它。本书还涵盖了 Docker 的用例、编排、集群、托管平台、安全性和性能,这将帮助您了解生产部署的不同方面。
Docker 及其生态系统正在以非常快的速度发展,因此了解基础知识并逐步采用新概念和工具非常重要。通过逐步的实用和适用的操作指南,“Docker Cookbook”不仅将帮助您使用当前版本的 Docker(1.6),而且通过附带的文本,将为您提供应对新版本 Docker 中的微小变化的概念信息。要了解更多关于本书的信息,请访问dockercookbook.github.io/
。
Docker^(TM)是 Docker,Inc.的注册商标。
本书涵盖的内容
第一章,“介绍和安装”,将容器与裸机和虚拟机进行比较。它可以帮助您了解启用容器化的 Linux 内核功能;最后,我们将看一下安装操作。
第二章,“使用 Docker 容器”,涵盖了大部分与容器相关的操作,如启动、停止和删除容器。它还可以帮助您获取有关容器的低级信息。
第三章,“使用 Docker 镜像”,解释了与镜像相关的操作,如拉取、推送、导出、导入、基础镜像创建和使用 Dockerfile 创建镜像。我们还建立了一个私有注册表。
第四章,“容器的网络和数据管理”,涵盖了连接容器与另一个容器在外部世界的操作。它还涵盖了如何共享来自其他容器和主机系统的外部存储。
第五章,“Docker 的用例”,解释了大部分 Docker 的用例,如将 Docker 用于测试、CI/CD、设置 PaaS 以及将其用作计算引擎。
第六章,“Docker API 和语言绑定”,涵盖了 Docker 远程 API 和 Python 语言绑定作为示例。
第七章,“Docker 性能”,解释了一个人可以遵循的性能方法,以比较容器与裸金属和虚拟机的性能。它还涵盖了监控工具。
第八章,“Docker 编排和托管平台”,介绍了 Docker compose 和 Swarm。我们将研究 CoreOS 和 Project Atomic 作为容器托管平台,然后介绍 Docker 编排的 Kubernetes。
第九章,“Docker 安全性”,解释了一般安全准则,用于强制访问控制的 SELinux,以及更改功能和共享命名空间等其他安全功能。
第十章,“获取帮助和技巧和窍门”,提供了有关 Docker 管理和开发相关的帮助、技巧和资源。
本书需要什么
这本食谱中的食谱肯定会在安装了 Fedora 21 的物理机器或虚拟机上运行,因为我将该配置作为主要环境。由于 Docker 可以在许多平台和发行版上运行,您应该能够毫无问题地运行大多数食谱。对于一些食谱,您还需要 Vagrant (www.vagrantup.com/
) 和 Oracle Virtual Box (www.virtualbox.org/
)。
本书适合谁
Docker Cookbook适用于希望在开发、QA 或生产环境中使用 Docker 的开发人员、系统管理员和 DevOps 工程师。
预计读者具有基本的 Linux/Unix 技能,如安装软件包,编辑文件,管理服务等。
任何关于虚拟化技术(如 KVM、XEN 和 VMware)的经验都将帮助读者更好地理解容器技术,但并非必需。
章节
在本书中,您会发现一些经常出现的标题(准备工作,如何做,它是如何工作的,还有更多,以及另请参阅)。
为了清晰地说明如何完成一个食谱,我们使用以下章节:
准备工作
本节告诉您在食谱中可以期待什么,并描述如何设置食谱所需的任何软件或任何初步设置。
如何做…
本节包含遵循食谱所需的步骤。
它是如何工作的…
本节通常包括对前一节发生的事情的详细解释。
还有更多…
本节包括有关食谱的额外信息,以使读者更加了解食谱。
另请参阅
本节提供有关食谱的其他有用信息的链接。
约定
在本书中,您将找到许多文本样式,用于区分不同类型的信息。以下是一些示例以及它们的含义解释。
文本中的代码词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄显示如下:“您可以使用--driver/-d
选项来选择部署所需的多个端点之一。”
代码块设置如下:
[Unit]
Description=MyApp
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill busybox1
ExecStartPre=-/usr/bin/docker rm busybox1
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
当我们希望引起您对代码块的特定部分的注意时,相关行或项目将以粗体显示:
[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/docker
EnvironmentFile=-/etc/sysconfig/docker-storage
ExecStart=/usr/bin/docker -d -H fd:// $OPTIONS $DOCKER_STORAGE_OPTIONS
LimitNOFILE=1048576
LimitNPROC=1048576
[Install]
WantedBy=multi-user.target
任何命令行输入或输出都以以下方式编写:
$ docker pull fedora
新术语和重要单词以粗体显示。例如,在屏幕上看到的单词,例如菜单或对话框中的单词,会以这种方式出现在文本中:“转到项目主页,在APIs & auth部分下,选择APIs,并启用 Google Compute Engine API。”
注意
警告或重要说明会出现在这样的框中。
提示
技巧和窍门会出现在这样的地方。
第一章:介绍和安装
在本章中,我们将涵盖以下内容:
-
验证 Docker 安装的要求
-
安装 Docker
-
拉取镜像并运行容器
-
向 Docker 添加非 root 用户进行管理
-
使用 Docker Machine 设置 Docker 主机
-
使用 Docker 命令行查找帮助
介绍
在 IT 革命的最初阶段,大多数应用程序是直接部署在物理硬件上,通过主机操作系统。由于单一用户空间,运行时在应用程序之间共享。部署是稳定的,以硬件为中心,并且具有长时间的维护周期。大多由 IT 部门管理,并且给开发人员提供了更少的灵活性。在这种情况下,硬件资源经常被低效利用。
以下图表描述了这样的设置:
为了克服传统部署设置的限制,虚拟化被发明了。使用诸如 KVM、XEN、ESX、Hyper-V 等的 hypervisor,我们模拟了虚拟机(VM)的硬件,并在每个虚拟机上部署了一个客户操作系统。VM 可以具有与其主机不同的操作系统;这意味着我们负责管理该 VM 的补丁、安全性和性能。通过虚拟化,应用程序在 VM 级别上被隔离,并由 VM 的生命周期定义。这在投资回报和灵活性方面提供了更好的回报,但增加了复杂性和冗余成本。以下图表描述了典型的虚拟化环境:
在虚拟化环境中的应用程序部署(rhsummit.files.wordpress.com/2014/04/rhsummit2014-application-centric_packaging_with_docker_and_linux_containers-20140412riek7.pdf
)
在虚拟化之后,我们现在正朝着更加应用中心化的 IT 发展。我们已经移除了虚拟机监视器层,以减少硬件仿真和复杂性。应用程序与其运行时环境一起打包,并使用容器进行部署。OpenVZ,Solaris Zones 和 LXC 是容器技术的一些例子。与虚拟机相比,容器的灵活性较低;例如,我们无法在 Linux 操作系统上运行 Microsoft Windows。与虚拟机相比,容器也被认为不太安全,因为在容器中,一切都在主机操作系统上运行。如果容器受到损害,那么可能会完全访问主机操作系统。设置、管理和自动化可能会变得有点复杂。这些是我们在过去几年中没有看到容器大规模采用的一些原因,尽管我们已经有了这项技术。
有了 Docker,容器突然成为了一等公民。所有大公司,如 Google,Microsoft,Red Hat,IBM 等,现在都在努力使容器成为主流。
Docker 是由 Solomon Hykes 在 dotCloud 内部项目启动的,他目前是 Docker,Inc.的首席技术官。它于 2013 年 3 月以 Apache 2.0 许可证的形式开源发布。通过 dotCloud 的平台即服务经验,Docker 的创始人和工程师们意识到了运行容器的挑战。因此,他们开发了一种管理容器的标准方式。
Docker 使用了 Linux 的底层内核功能来实现容器化。以下图表描述了 Docker 使用的执行驱动程序和内核功能。我们稍后会讨论执行驱动程序。让我们先看一些 Docker 使用的主要内核功能:
Docker 使用的执行驱动程序和内核功能(blog.docker.com/wp-content/uploads/2014/03/docker-execdriver-diagram.png
)
命名空间
命名空间是容器的构建模块。有不同类型的命名空间,每个命名空间都将应用程序相互隔离。它们是使用克隆系统调用创建的。也可以附加到现有的命名空间。Docker 使用的一些命名空间在以下部分进行了解释。
pid 命名空间
pid
命名空间允许每个容器拥有自己的进程编号。每个pid
形成自己的进程层次结构。父命名空间可以看到子命名空间并影响它们,但子命名空间既不能看到父命名空间也不能影响它。
如果有两个层次结构,那么在顶层,我们将看到在子命名空间中运行的进程具有不同的 PID。因此,在子命名空间中运行的进程将具有两个 PID:一个在子命名空间中,另一个在父命名空间中。例如,如果我们在容器上运行一个程序(container.sh
),那么我们也可以在主机上看到相应的程序。
在容器内:
在主机上:
net 命名空间
有了pid
命名空间,我们可以在不同的隔离环境中多次运行相同的程序;例如,我们可以在不同的容器上运行 Apache 的不同实例。但是没有net
命名空间,我们将无法在每个容器上监听端口 80。net
命名空间允许我们在每个容器上拥有不同的网络接口,从而解决了我之前提到的问题。回环接口在每个容器中也会有所不同。
要在容器中启用网络,我们可以在两个不同的net
命名空间中创建一对特殊接口,并允许它们彼此通信。特殊接口的一端位于容器内,另一端位于主机系统中。通常,容器内的接口被命名为eth0
,在主机系统中,它被赋予一个随机名称,如vethcf1a
。然后,通过主机上的桥接器(docker0
)将这些特殊接口连接起来,以实现容器之间的通信和数据包路由。
在容器内,你会看到类似以下的东西:
在主机上,它看起来像是这样:
此外,每个net
命名空间都有自己的路由表和防火墙规则。
ipc 命名空间
进程间通信(ipc)提供信号量、消息队列和共享内存段。这些天它并不被广泛使用,但一些程序仍然依赖它。
如果一个容器创建的ipc
资源被另一个容器消耗,那么运行在第一个容器上的应用程序可能会失败。有了ipc
命名空间,运行在一个命名空间中的进程无法访问另一个命名空间的资源。
mnt 命名空间
只需一个 chroot,就可以检查来自 chroot 目录/命名空间的系统的相对路径。mnt
命名空间将 chroot 的概念提升到了下一个级别。有了mnt
命名空间,容器可以拥有自己的一组挂载的文件系统和根目录。一个mnt
命名空间中的进程无法看到另一个mnt
命名空间的挂载文件系统。
uts 命名空间
有了uts
命名空间,我们可以为每个容器设置不同的主机名。
用户命名空间
有了user
命名空间支持,我们可以在主机上拥有非零 ID 的用户,但在容器内可以拥有零 ID。这是因为user
命名空间允许用户和组 ID 的每个命名空间映射。
有多种方法可以在主机和容器之间以及容器和容器之间共享命名空间。我们将在后续章节中看到如何做到这一点。
Cgroups
控制组(cgroups)为容器提供资源限制和计量。来自 Linux 内核文档:
控制组提供了一种聚合/分区任务集的机制,并将所有未来的子任务分成具有特定行为的分层组。
简单来说,它们可以与ulimit
shell 命令或setrlimit
系统调用进行比较。cgroups 允许将资源限制设置为一组进程,而不是单个进程。
控制组分为不同的子系统,如 CPU、CPU 集、内存块 I/O 等。每个子系统可以独立使用,也可以与其他子系统分组。cgroups 提供的功能包括:
-
资源限制:例如,一个 cgroup 可以绑定到特定的 CPU,因此该组中的所有进程只能在给定的 CPU 上运行
-
优先级:一些组可能会获得更多的 CPU 份额
-
计量:您可以测量不同子系统的资源使用情况以进行计费
-
控制:冻结和重新启动组
一些可以由 cgroups 管理的子系统如下:
-
blkio:它设置对块设备(如磁盘、SSD 等)的 I/O 访问
-
Cpu:它限制对 CPU 的访问
-
Cpuacct:它生成 CPU 资源利用率
-
Cpuset:它将多核系统上的 CPU 分配给 cgroup 中的任务
-
Devices:它为 cgroup 中的一组任务提供访问
-
Freezer:它暂停或恢复 cgroup 中的任务
-
Memory:它设置 cgroup 中任务的内存使用限制
有多种方法可以控制 cgroups 的工作。最流行的两种方法是手动访问 cgroup 虚拟文件系统和使用libcgroup
库访问它。要在 fedora 中使用libcgroup
,运行以下命令安装所需的软件包:
$ sudo yum install libcgroup libcgroup-tools
安装后,您可以使用以下命令在伪文件系统中获取子系统及其挂载点的列表:
$ lssubsys -M
虽然我们还没有看实际的命令,但让我们假设我们正在运行一些容器,并且想要获取容器的 cgroup 条目。要获取这些条目,我们首先需要获取容器 ID,然后使用lscgroup
命令获取容器的 cgroup 条目,可以从以下命令中获取:
注意
有关更多详细信息,请访问docs.docker.com/articles/runmetrics/
。
联合文件系统
联合文件系统允许透明地叠加分开的文件系统(称为层)的文件和目录,以创建一个新的虚拟文件系统。在启动容器时,Docker 叠加附加到图像的所有层,并创建一个只读文件系统。在此基础上,Docker 创建一个读/写层,容器的运行时环境使用它。有关更多详细信息,请参阅本章的拉取图像并运行容器部分。Docker 可以使用多种联合文件系统变体,包括 AUFS、Btrfs、vfs 和 DeviceMapper。
Docker 可以与不同的执行驱动程序一起工作,例如libcontainer
、lxc
和libvirt
来管理容器。默认的执行驱动程序是libcontainer
,它是 Docker 的默认驱动程序。它可以为 Docker 操作命名空间、控制组、能力等。
验证 Docker 安装的要求
Docker 支持许多 Linux 平台,如 RHEL、Ubuntu、Fedora、CentOS、Debian、Arch Linux 等。它也支持许多云平台,如 Amazon EC2、Rackspace Cloud 和 Google Compute Engine。借助虚拟环境 Boot2Docker,它也可以在 OS X 和 Microsoft Windows 上运行。不久前,微软宣布将在其下一个 Microsoft Windows 版本中添加对 Docker 的本机支持。
在这篇文章中,让我们验证 Docker 安装的要求。我们将在安装了 Fedora 21 的系统上进行检查,尽管相同的步骤也适用于 Ubuntu。
准备工作
以 root 用户登录安装了 Fedora 21 的系统。
如何做…
执行以下步骤:
- Docker 不支持 32 位架构。要检查系统架构,请运行以下命令:
$ uname -i
x86_64
- Docker 支持内核 3.8 或更高版本。它已经被后移至一些内核 2.6,如 RHEL 6.5 及以上版本。要检查内核版本,请运行以下命令:
$ uname -r
3.18.7-200.fc21.x86_64
- 运行的内核应支持适当的存储后端。其中一些是 VFS、DeviceMapper、AUFS、Btrfs 和 OverlayFS。
大多数情况下,默认的存储后端或驱动程序是 devicemapper,它使用设备映射器薄配置模块来实现层。它应该默认安装在大多数 Linux 平台上。要检查设备映射器,您可以运行以下命令:
$ grep device-mapper /proc/devices
253 device-mapper
在大多数发行版中,AUFS 需要一个修改过的内核。
- 对于 cgroups 和命名空间的支持已经在内核中有一段时间了,并且应该默认启用。要检查它们的存在,您可以查看正在运行的内核的相应配置文件。例如,在 Fedora 上,我可以做类似以下的事情:
$ grep -i namespaces /boot/config-3.18.7-200.fc21.x86_64
CONFIG_NAMESPACES=y
$ grep -i cgroups /boot/config-3.18.7-200.fc21.x86_64
CONFIG_CGROUPS=y
工作原理…
通过前面的命令,我们验证了 Docker 安装的要求。
另请参阅
- 在 Docker 网站的安装文档中
docs.docker.com/installation/
安装 Docker
由于有许多发行版支持 Docker,我们将在这篇文章中只看一下 Fedora 21 上的安装步骤。对于其他发行版,您可以参考本文的另请参阅部分中提到的安装说明。使用 Docker Machine,我们可以在本地系统、云提供商和其他环境上轻松设置 Docker 主机。我们将在另一篇文章中介绍这个。
准备工作
检查前面一篇文章中提到的先决条件。
如何做…
- 使用 yum 安装 Docker:
$ yum -y install docker
它是如何工作的...
上述命令将安装 Docker 及其所需的所有软件包。
还有更多...
默认的 Docker 守护程序配置文件位于/etc/sysconfig/docker
,在启动守护程序时使用。以下是一些基本操作:
- 启动服务:
$ systemctl start docker
- 验证安装:
$ docker info
- 更新软件包:
$ yum -y update docker
- 启用开机启动服务:
$ systemctl enable docker
- 停止服务:
$ systemctl stop docker
另请参阅
- 安装文档位于 Docker 网站上的
docs.docker.com/installation/
拉取镜像并运行容器
我从下一章借用了这个配方来介绍一些概念。如果您在这个配方中找不到所有的解释,不要担心。我们将在本章节或接下来的几章中详细讨论所有的主题。现在,让我们拉取一个镜像并运行它。在这个配方中,我们还将熟悉 Docker 架构及其组件。
准备工作
获取安装了 Docker 的系统访问权限。
如何做到...
- 要拉取一个镜像,请运行以下命令:
$ docker pull fedora
- 使用以下命令列出现有的镜像:
$ docker images
- 使用拉取的镜像创建一个容器,并列出容器为:
它是如何工作的...
Docker 具有客户端-服务器架构。其二进制文件包括 Docker 客户端和服务器守护程序,并且可以驻留在同一台主机上。客户端可以通过套接字或 RESTful API 与本地或远程 Docker 守护程序通信。Docker 守护程序构建、运行和分发容器。如下图所示,Docker 客户端将命令发送到运行在主机上的 Docker 守护程序。Docker 守护程序还连接到公共或本地索引,以获取客户端请求的镜像:
Docker 客户端-服务器架构 (docs.docker.com/introduction/understanding-docker/
)
因此,在我们的情况下,Docker 客户端向在本地系统上运行的守护程序发送请求,然后守护程序连接到公共 Docker 索引并下载镜像。一旦下载完成,我们就可以运行它。
还有更多...
让我们探索一些我们在这个配方中遇到的关键词:
- 图像:Docker 图像是只读模板,在运行时它们为我们提供容器。有一个基本图像和在其上的层的概念。例如,我们可以有一个基本图像的 Fedora 或 Ubuntu,然后我们可以安装软件包或对基本图像进行修改以创建一个新的层。基本图像和新层可以被视为一个新的图像。例如,在下图中,Debian是基本图像,emacs和Apache是添加在其上的两个层。它们非常易于移植,并且可以轻松共享:
Docker 图像层(docs.docker.com/terms/images/docker-filesystems-multilayer.png
)
层被透明地放在基本图像的顶部,以创建一个统一的文件系统。
-
注册表:注册表保存 Docker 图像。它可以是公共的或私有的,您可以从中下载或上传图像。公共 Docker 注册表称为Docker Hub,我们稍后会介绍。
-
索引:索引管理用户帐户、权限、搜索、标记以及 Docker 注册表公共 Web 界面中的所有好东西。
-
容器:容器是由基本图像和在其上的层组合创建的运行图像。它们包含运行应用程序所需的一切。如前图所示,在启动容器时还会添加一个临时层,如果在停止和删除容器后未提交,则会被丢弃。如果提交,则会创建另一个层。
-
仓库:一个图像的不同版本可以通过多个标签进行管理,这些标签保存在不同的 GUID 中。仓库是由 GUID 跟踪的图像集合。
另请参阅
-
Docker 网站上的文档
docs.docker.com/introduction/understanding-docker/
-
使用 Docker 1.6,Docker 社区和微软 Windows 发布了 Windows 的 Docker 本机客户端
azure.microsoft.com/blog/2015/04/16/docker-client-for-windows-is-now-available
添加非 root 用户以管理 Docker
为了方便使用,我们可以允许非 root 用户通过将其添加到 Docker 组来管理 Docker。
做好准备
- 如果还没有,创建 Docker 组:
$ sudo group add docker
- 创建要授予管理 Docker 权限的用户:
$ useradd dockertest
如何做…
运行以下命令以允许新创建的用户管理 Docker:
$ sudo gpasswd -a dockertest docker
它是如何工作的…
上述命令将向 Docker 组添加一个用户。添加的用户因此可以执行所有 Docker 操作。这可能存在安全风险。请访问第九章,Docker 安全了解更多详情。
使用 Docker Machine 设置 Docker 主机
今年早些时候,Docker 发布了编排工具(blog.docker.com/2015/02/orchestrating-docker-with-machine-swarm-and-compose/
)和 Machine、Swarm 和 Compose 可以无缝部署容器。在这个配方中,我们将介绍 Docker Machine,并在以后的章节中查看其他内容。使用 Docker Machine 工具(github.com/docker/machine/
),您可以使用一个命令在本地云上设置 Docker 主机。它目前处于测试模式,不建议用于生产。它支持诸如 VirtualBox、OpenStack、Google、Digital Ocean 等环境。有关完整列表,您可以访问github.com/docker/machine/tree/master/drivers
。让我们使用这个工具在 Google Cloud 中设置一个主机。
注意
我们将仅在本配方中使用 Docker Machine。本章或其他章节中提到的配方可能在 Docker Machine 设置的主机上工作或不工作。
准备工作
Docker Machine 不会出现在默认安装中。您需要从其 GitHub 发布链接(github.com/docker/machine/releases
)下载它。请在下载之前检查最新版本和分发。作为 root 用户,下载二进制文件并使其可执行:
$ curl -L https://github.com/docker/machine/releases/download/v0.2.0/docker-machine_linux-amd64 > /usr/local/bin/docker-machine
$ chmod a+x /usr/local/bin/docker-machine
如果您在Google Compute Engine(GCE)上没有帐户,那么您可以注册免费试用(cloud.google.com/compute/docs/signup
)来尝试这个配方。我假设您在 GCE 上有一个项目,并且在下载 Docker Machine 二进制文件的系统上安装了 Google Cloud SDK。如果没有,那么您可以按照以下步骤操作:
- 在本地系统上设置 Google Cloud SDK:
$ curl https://sdk.cloud.google.com | bash
-
在 GCE 上创建一个项目(
console.developers.google.com/project
)并获取其项目 ID。请注意,项目名称和其 ID 是不同的。 -
转到项目主页,在API 和身份验证部分下,选择API,并启用 Google Compute Engine API。
如何操作...
- 将我们收集到的项目 ID 分配给变量
GCE_PROJECT
:
$ export GCE_PROJECT="<Your Project ID>"
- 运行以下命令并输入弹出的网页浏览器上提供的代码:
$ docker-machine create -d google --google-project=$GCE_PROJECT --google-machine-type=n1-standard-2 --google-disk-size=50 cookbook
INFO[0000] Opening auth URL in browser.
.......
......
INFO[0015] Saving token in /home/nkhare/.docker/machine/machines/cookbook/gce_token
INFO[0015] Creating host...
INFO[0015] Generating SSH Key
INFO[0015] Creating instance.
INFO[0016] Creating firewall rule.
INFO[0020] Waiting for Instance...
INFO[0066] Waiting for SSH...
INFO[0066] Uploading SSH Key
INFO[0067] Waiting for SSH Key
INFO[0224] "cookbook" has been created and is now the active machine.
INFO[0224] To point your Docker client at it, run this in your shell: eval "$(docker-machine_linux-amd64 env cookbook)"
- 列出 Docker Machine 管理的现有主机:
$ ./docker-machine_linux-amd64 ls
您可以使用 Docker Machine 管理多个主机。*
表示活动主机。
- 显示设置 Docker 客户端环境的命令:
$ ./docker-machine_linux-amd64 env cookbook
因此,如果使用前面的环境变量指向 Docker 客户端,我们将连接到在 GCE 上运行的 Docker 守护程序。
- 并且要指定 Docker 客户端使用我们新创建的机器,请运行以下命令:
$ eval "$(./docker-machine_linux-amd64 env cookbook)"
从现在开始,所有 Docker 命令都将在我们在 GCE 上预配的机器上运行,直到设置前面的环境变量。
工作原理...
Docker Machine 连接到云提供商并设置带有 Docker Engine 的 Linux VM。它在当前用户的主目录下创建一个.docker/machine/
目录以保存配置。
还有更多...
Docker Machine 提供管理命令,如create
、start
、stop
、restart
、kill
、remove
、ssh
和其他命令来管理机器。有关详细选项,请查找 Docker Machine 的帮助选项:
$ docker-machine -h
您可以使用--driver/-d
选项来选择部署的许多端点之一。例如,要使用 VirtualBox 设置环境,请运行以下命令:
$ docker-machine create --driver virtualbox dev
在这里,dev
是机器名称。默认情况下,最新部署的机器将成为主机。
另请参阅
-
Docker 网站上的文档
docs.docker.com/machine/
-
在
docs.docker.com/installation/google/
上设置 Docker 在 Google Compute Engine 上的指南
使用 Docker 命令行查找帮助
Docker 命令有很好的文档,可以在需要时进行参考。在线文档也有很多,但可能与您正在运行的 Docker 版本的文档不同。
准备工作
在您的系统上安装 Docker。
如何操作...
- 在基于 Linux 的系统上,您可以使用
man
命令查找帮助,如下所示:
$ man docker
- 还可以使用以下任何命令找到特定子命令的帮助:
$ man docker ps
$ man docker-ps
工作原理…
man
命令使用 Docker 软件包安装的man
页面显示帮助信息。
另请参阅
- Docker 网站上的文档位于
docs.docker.com/reference/commandline/cli/
第二章:使用 Docker 容器
在本章中,我们将涵盖以下配方:
-
列出/搜索镜像
-
拉取镜像
-
列出镜像
-
启动容器
-
列出容器
-
停止容器
-
查看容器的日志
-
删除容器
-
设置容器的重启策略
-
在容器内获取特权访问
-
在启动容器时暴露端口
-
在容器内访问主机设备
-
向正在运行的容器注入新进程
-
返回有关容器的低级信息
-
对容器进行标记和过滤
介绍
在上一章中,安装 Docker 后,我们拉取了一个镜像,并从中创建了一个容器。Docker 的主要目标是运行容器。在本章中,我们将看到我们可以对容器进行不同的操作,如启动、停止、列出、删除等。这将帮助我们将 Docker 用于不同的用例,如测试、CI/CD、设置 PaaS 等,我们将在后面的章节中进行介绍。在开始之前,让我们通过运行以下命令来验证 Docker 安装:
$ docker version
这将提供 Docker 客户端和服务器版本,以及其他详细信息。
我正在使用 Fedora 20/21 作为运行配方的主要环境。它们也应该适用于其他环境。
列出/搜索镜像
我们需要一个镜像来启动容器。让我们看看如何在 Docker 注册表上搜索镜像。正如我们在第一章中所看到的,介绍和安装,注册表保存 Docker 镜像,它可以是公共的也可以是私有的。默认情况下,搜索将在默认的公共注册表 Docker Hub 上进行,它位于 hub.docker.com/
。
准备就绪
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何做…
- 要在 Docker 注册表上搜索镜像,请运行以下命令:
docker search TERM
以下是搜索 Fedora 镜像的示例:
$ docker search fedora | head -n5
前面的屏幕截图列出了图像的名称,描述和获得的星星数量。它还指出图像是否是官方和自动化的。STARS
表示有多少人喜欢给定的图像。OFFICIAL
列帮助我们确定图像是否是从可信任的来源构建的。AUTOMATED
列是一种告诉我们图像是否是在 GitHub 或 Bitbucket 存储库中自动构建的方法。有关AUTOMATED
的更多详细信息可以在下一章中找到。
提示
图像名称的约定是<user>/<name>
,但它可以是任何东西。
它是如何工作的...
Docker 在 Docker 公共注册表上搜索镜像,该注册表在registry.hub.docker.com/
上有一个镜像仓库。
我们也可以配置我们的私有索引,它可以进行搜索。
还有更多...
- 要列出获得超过 20 颗星并且是自动化的图像,请运行以下命令:
$ docker search -s 20 --automated fedora
在第三章中,使用 Docker 镜像,我们将看到如何设置自动构建。
-
从 Docker 1.3 开始,提供了
--insecure-registry
选项给 Docker 守护程序,允许我们从不安全的注册表中搜索/拉取/提交图像。有关更多详细信息,请查看docs.docker.com/reference/commandline/cli/#insecure-registries
。 -
RHEL 7 和 Fedora 上的 Docker 软件包提供了
--add-registry
和--block-registry
选项,分别用于添加和阻止注册表,以更好地控制图像搜索路径。有关更多详细信息,请查看以下链接:
另请参阅
- 要获取 Docker 搜索的帮助,请运行以下命令:
$ docker search --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#search
拉取图像
搜索图像后,我们可以通过运行 Docker 守护程序将其拉取到系统中。让我们看看我们可以如何做到这一点。
准备工作
确保 Docker 守护程序在主机上运行,并且可以通过 Docker 客户端进行连接。
如何做...
- 要在 Docker 注册表上拉取图像,请运行以下命令:
docker pull NAME[:TAG]
以下是拉取 Fedora 图像的示例:
$ docker pull fedora
它是如何工作的...
pull
命令从 Docker 注册表下载所有层,这些层是在本地创建该图像所需的。我们将在下一章中看到有关层的详细信息。
还有更多...
- 图像标签将相同类型的图像分组。例如,CentOS 可以具有标签如
centos5
,centos6
等的图像。例如,要拉取具有特定标签的图像,请运行以下命令:
$ docker pull centos:centos7
- 默认情况下,将拉取具有最新标签的图像。要拉取所有对应于所有标签的图像,请使用以下命令:
$ docker pull --all-tags centos
- 使用 Docker 1.6(
blog.docker.com/2015/04/docker-release-1-6/
),我们可以通过称为“摘要”的新内容可寻址标识符构建和引用图像。当我们想要使用特定图像而不是标签时,这是一个非常有用的功能。要拉取具有特定摘要的图像,可以考虑以下语法:
$ docker pull <image>@sha256:<digest>
以下是一个命令的示例:
$ docker pull debian@sha256:cbbf2f9a99b47fc460d422812b6a5adff7dfee951d8fa2e4a98caa0382cfbdbf
仅支持 Docker 注册表 v2 的摘要。
- 一旦图像被拉取,它将驻留在本地缓存(存储)中,因此后续的拉取将非常快。这个功能在构建 Docker 分层图像中扮演着非常重要的角色。
另请参阅
- 查看 Docker
pull
的help
选项:
$ docker pull --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#pull
列出图像
我们可以列出运行 Docker 守护程序的系统上可用的图像。这些图像可能已经从注册表中拉取,通过docker
命令导入,或者通过 Docker 文件创建。
准备工作
确保 Docker 守护程序在主机上运行,并且可以通过 Docker 客户端进行连接。
如何做...
- 运行以下命令列出图像:
$ docker images
它是如何工作的...
Docker 客户端与 Docker 服务器通信,并获取服务器端的图像列表。
还有更多...
-
所有具有相同名称但不同标签的图像都会被下载。这里值得注意的有趣之处是它们具有相同的名称但不同的标签。此外,对于相同的
IMAGE ID
,有两个不同的标签,即2d24f826cb16
。 -
您可能会看到与最新的 Docker 软件包不同的
REPOSITORY
输出,如下面的屏幕截图所示。
这是因为镜像列表打印了 Docker 注册表主机名。如前面的屏幕截图所示,docker.io
是注册表主机名。
另请参阅
- 查看
docker images
的help
选项:
$ docker images --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#images
启动容器
一旦我们有了镜像,就可以使用它们来启动容器。在这个示例中,我们将使用fedora:latest
镜像启动一个容器,并查看幕后发生的所有事情。
准备就绪
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何做…
- 启动容器的语法如下:
docker run [ OPTIONS ] IMAGE[:TAG] [COMMAND] [ARG...]
以下是一个命令的示例:
$ docker run -i -t --name=f21 fedora /bin/bash
默认情况下,Docker 会选择带有最新标签的镜像:
-
-i
选项以交互模式启动容器 -
使用
-t
选项分配一个伪终端
并将其附加到标准输入
因此,通过上述命令,我们从fedora:latest
镜像启动一个容器,附加伪终端
,将其命名为f21
,并运行/bin/bash
命令。如果未指定名称,则将分配一个随机字符串作为名称。
此外,如果镜像在本地不可用,则会首先从注册表中下载,然后运行。在运行run
命令之前,Docker 将运行search
和pull
命令。
工作原理…
在幕后,Docker:
-
将使用 UnionFS 合并构成该镜像的所有层。
-
为容器分配一个唯一的 ID,称为容器 ID。
-
为容器分配一个文件系统并挂载一个读/写层。对该层的任何更改都将是临时的,如果它们没有被提交,就会被丢弃。
-
分配一个网络/桥接口。
-
为容器分配一个 IP 地址。
-
执行用户指定的进程。
此外,默认情况下,Docker 会在/var/lib/docker/containers
目录中创建一个包含容器 ID 的目录,其中包含容器的特定信息,如主机名、配置详细信息、日志和/etc/hosts
。
更多内容…
-
要退出容器,请按Ctrl + D或输入
exit
。这类似于从 shell 中退出,但这将停止容器。 -
run
命令创建并启动容器。使用 Docker 1.3 或更高版本,可以使用create
命令只创建容器,然后使用start
命令稍后运行它,如下例所示:
$ ID=$(docker create -t -i fedora bash)
$ docker start -a -i $ID
- 容器可以在后台启动,然后我们可以在需要时附加到它。我们需要使用
-d
选项在后台启动容器:
$ docker run -d -i -t fedora /bin/bash
0df95cc49e258b74be713c31d5a28b9d590906ed9d6e1a2dc756 72aa48f28c4f
前面的命令返回容器的容器 ID,稍后我们可以附加到该容器,如下所示:
$ ID='docker run -d -t -i fedora /bin/bash'
$ docker attach $ID
在前面的情况下,我们选择了/bin/bash
在容器内运行。如果我们附加到容器,我们将获得一个交互式 shell。我们可以运行一个非交互式进程,并将其在后台运行,以创建一个守护进程容器,如下所示:
$ docker run -d fedora /bin/bash -c "while [ 1 ]; do echo hello docker ; sleep 1; done"
- 要在退出后删除容器,请使用
--rm
选项启动容器,如下所示:
$ docker run --rm fedora date
一旦date
命令退出,容器将被删除。
run
命令的--read-only
选项将以只读
模式挂载根文件系统:
$ docker run --read-only -d -i -t fedora /bin/bash
请记住,此选项只是确保我们不能修改根文件系统上的任何内容,但我们正在写入卷,这将在本书的后面部分进行介绍。当我们不希望用户意外地在容器内写入内容时,此选项非常有用,如果容器没有提交或复制到非临时存储(如卷)上,这些内容将会丢失。
- 您还可以为容器设置自定义标签,这些标签可以用于根据标签对容器进行分组。有关更多详细信息,请参阅本章中的标记和过滤容器配方。
提示
容器可以通过三种方式引用:按名称,按容器 ID(0df95cc49e258b74be713c31d5a28b9d590906ed9d6e1a2dc75672 aa48f28c4f)和按短容器 ID(0df95cc49e25)
另请参阅
- 查看
docker run
的help
选项:
$ docker run --help
-
Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#run
-
Docker 1.3 发布公告
blog.docker.com/2014/10/docker-1-3-signed-images-process-injection-security-options-mac-shared-directories/
列出容器
我们可以列出正在运行和停止的容器。
准备就绪
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一些正在运行和/或已停止的容器。
如何做…
- 要列出容器,请运行以下命令:
docker ps [ OPTIONS ]
它是如何工作的…
Docker 守护程序可以查看与容器关联的元数据并将其列出。默认情况下,该命令返回:
-
容器 ID
-
创建它的镜像
-
在启动容器后运行的命令
-
有关创建时间的详细信息
-
当前状态
-
从容器中公开的端口
-
容器的名称
还有更多…
-
要列出运行和停止的容器,请使用
-a
选项,如下所示: -
要仅返回所有容器的容器 ID,请使用
-aq
选项,如下所示: -
要显示最后创建的容器,包括非运行容器,请运行以下命令:
$ docker ps -l
- 使用
--filter/-f
选项对ps
进行标记,我们可以列出具有特定标签的容器。有关更多详细信息,请参阅本章中的标记和过滤容器示例。
另请参阅
查看docker ps
的man
页面以查看更多选项:
- 查看
docker ps
的help
选项:
$ docker ps --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#ps
查看容器的日志
如果容器在STDOUT
/STDERR
上发出日志或输出,则可以在不登录到容器的情况下获取它们。
准备就绪
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一个正在运行的容器,该容器会在STDOUT
上发出日志/输出。
如何做…
- 要从容器中获取日志,请运行以下命令:
docker logs [-f|--follow[=false]][-t|--timestamps[=false]] CONTAINER
- 让我们以前面部分的示例为例,运行一个守护式容器并查看日志:
$ docker run -d fedora /bin/bash -c "while [ 1 ]; do echo hello docker ; sleep 1; done"
它是如何工作的…
Docker 将查看来自/var/lib/docker/containers/<Container ID>
的容器特定日志文件并显示结果。
还有更多…
使用-t
选项,我们可以在每个日志行中获取时间戳,并使用-f
可以获得类似 tailf 的行为。
另请参阅
- 查看
docker logs
的help
选项:
$ docker logs --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#logs
停止一个容器
我们可以一次停止一个或多个容器。在这个示例中,我们将首先启动一个容器,然后停止它。
准备就绪
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一个或多个正在运行的容器。
如何做…
- 要停止容器,请运行以下命令:
docker stop [-t|--time[=10]] CONTAINER [CONTAINER...]
- 如果您已经有一个正在运行的容器,那么您可以继续停止它;如果没有,我们可以创建一个然后停止它,如下所示:
$ ID='docker run -d -i fedora /bin/bash'
$ docker stop $ID
它是如何工作的…
这将保存容器的状态并停止它。如果需要,可以重新启动。
还有更多…
-
要在等待一段时间后停止容器,请使用
--time/-t
选项。 -
要停止所有正在运行的容器,请运行以下命令:
$ docker stop 'docker ps -q'
另请参阅
- 查看
docker stop
的help
选项:
$ docker stop --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#stop
删除容器
我们可以永久删除一个容器,但在此之前我们必须停止容器或使用强制选项。在这个示例中,我们将启动、停止和删除一个容器。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一些处于停止或运行状态的容器来删除它们。
如何做…
- 使用以下命令:
$ docker rm [ OPTIONS ] CONTAINER [ CONTAINER ]
- 让我们首先启动一个容器,然后停止它,然后使用以下命令删除它:
$ ID='docker run -d -i fedora /bin/bash '
$ docker stop $ID
$ docker rm $ID
正如我们从前面的屏幕截图中可以看到的,容器没有显示出来,这是在停止后输入docker ps
命令后。我们必须提供-a
选项来列出它。容器停止后,我们可以删除它。
还有更多…
-
强制删除容器而不进行中间停止,请使用
-f
选项。 -
要删除所有容器,我们首先需要停止所有正在运行的容器,然后再删除它们。在运行命令之前要小心,因为这些命令将删除正在运行和停止的容器:
$ docker stop 'docker ps -q'
$ docker rm 'docker ps -aq'
- 有选项可以删除与容器相关的指定链接和卷,我们将在后面探讨。
它是如何工作的…
Docker 守护程序将删除在启动容器时创建的读/写层。
另请参阅
- 查看
docker rm
的help
选项
$ docker rm --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#rm
在容器上设置重启策略
在 Docker 1.2 之前,曾经有一个重新启动容器的选项。随着 Docker 1.2 的发布,它已经添加到了run
命令中,并使用标志来指定重新启动策略。通过这个策略,我们可以配置容器在启动时启动。当容器意外死掉时,这个选项也非常有用。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
操作步骤…
您可以使用以下语法设置重新启动策略:
$ docker run --restart=POLICY [ OPTIONS ] IMAGE[:TAG] [COMMAND] [ARG...]
以下是一个命令的示例:
$ docker run --restart=always -d -i -t fedora /bin/bash
有三种重新启动策略可供选择:
-
no
: 如果容器死掉,它不会重新启动 -
on-failure
: 如果容器以非零退出代码失败,则重新启动容器 -
always
: 这总是重新启动容器,不用担心返回代码
还有更多…
您还可以使用on-failure
策略给出可选的重新启动计数,如下所示:
$ docker run --restart=on-failure:3 -d -i -t fedora /bin/bash
前面的命令只会在发生故障时重新启动容器三次。
另请参阅
- 查看
docker run
的help
选项:
$ docker run --help
-
Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#run
。 -
如果重新启动不符合您的要求,那么可以使用
systemd
(freedesktop.org/wiki/Software/systemd/
) 来解决容器在失败时自动重新启动的问题。有关更多信息,请访问docs.docker.com/articles/host_integration/
。
在容器内获取特权访问
Linux 将传统上与超级用户关联的特权分为不同的单元,称为功能(在基于 Linux 的系统上运行man capabilities
),可以独立启用和禁用。例如,net_bind_service
功能允许非用户进程绑定到 1,024 以下的端口。默认情况下,Docker 以有限的功能启动容器。通过在容器内获取特权访问,我们可以赋予更多的功能来执行通常由 root 完成的操作。例如,让我们尝试在挂载磁盘映像时创建一个回环设备。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
操作步骤…
- 要使用
privileged
模式,请使用以下命令:
$ docker run --privileged [ OPTIONS ] IMAGE[:TAG] [COMMAND] [ARG...]
- 现在让我们尝试使用特权访问的前面的示例:
$ docker run --privileged -i -t fedora /bin/bash
它是如何工作的…
通过在容器内提供几乎所有功能。
还有更多…
这种模式会带来安全风险,因为容器可以在 Docker 主机上获得根级访问权限。使用 Docker 1.2 或更高版本,添加了两个新标志--cap-add
和--cap-del
,以在容器内提供细粒度的控制。例如,要防止容器内的任何chown
,请使用以下命令:
$ docker run --cap-drop=CHOWN [ OPTIONS ] IMAGE[:TAG] [COMMAND] [ARG...]
查看第九章,“Docker 安全性”,了解更多详情。
另请参阅
- 查看
docker run
的help
选项:
$ docker run --help
-
Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#run
-
Docker 1.2 发布公告
blog.docker.com/2014/08/announcing-docker-1-2-0/
在启动容器时暴露端口
有多种方法可以暴露容器上的端口。其中一种是通过run
命令,我们将在本章中介绍。其他方法是通过 Docker 文件和--link
命令。我们将在其他章节中探讨它们。
准备就绪
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何做…
- 暴露端口的语法如下:
$ docker run --expose=PORT [ OPTIONS ] IMAGE[:TAG] [COMMAND] [ARG...]
例如,要在启动容器时暴露端口 22,请运行以下命令:
$ docker run --expose=22 -i -t fedora /bin/bash
还有更多…
有多种方法可以为容器暴露端口。现在,我们将看到如何在启动容器时暴露端口。我们将在后续章节中探讨其他暴露端口的选项。
另请参阅
- 查看
docker run
的help
选项:
$ docker run --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#run
在容器内访问主机设备
从 Docker 1.2 开始,我们可以使用--device
选项将主机设备的访问权限提供给容器的run
命令。以前,必须使用-v
选项进行绑定挂载,并且必须使用--privileged
选项进行操作。
准备就绪
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一个设备传递给容器。
如何做…
- 您可以使用以下语法将主机设备的访问权限提供给容器:
$ docker run --device=<Host Device>:<Container Device Mapping>:<Permissions> [ OPTIONS ] IMAGE[:TAG] [COMMAND] [ARG...]
这是一个命令的例子:
$ docker run --device=/dev/sdc:/dev/xvdc -i -t fedora /bin/bash
它是如何工作的…
上述命令将访问容器内的/dev/sdc
。
另请参阅
- 查看
docker run
的help
选项:
$ docker run --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#run
向正在运行的容器注入新进程
在开发和调试过程中,我们可能想要查看已经运行的容器内部。有一些实用程序,比如nsenter
(github.com/jpetazzo/nsenter
),允许我们进入容器的命名空间进行检查。使用在 Docker 1.3 中添加的exec
选项,我们可以在运行的容器内注入新进程。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您可能还需要一个正在运行的容器来注入进程。
如何做…
- 您可以使用以下命令在运行的容器中注入进程:
$ docker exec [-d|--detach[=false]] [--help] [-i|--interactive[=false]] [-t|--tty[=false]] CONTAINER COMMAND [ARG...]
- 让我们启动一个
nginx
容器,然后注入bash
进去:
$ ID='docker run -d nginx'
$ docker run -it $ID bash
工作原理…
exec
命令进入容器的命名空间并启动新进程。
另请参阅
- 查看 Docker inspect 的
help
选项:
$ docker exec --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#exec
返回有关容器的低级信息
在进行调试、自动化等操作时,我们将需要容器配置详细信息。Docker 提供了inspect
命令来轻松获取这些信息。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何做…
- 要检查容器/镜像,请运行以下命令:
$ docker inspect [-f|--format="" CONTAINER|IMAGE [CONTAINER|IMAGE...]
- 我们将启动一个容器,然后对其进行检查:
$ ID='docker run -d -i fedora /bin/bash'
$ docker inspect $ID
[{
"Args": [],
"Config": {
"AttachStderr": false,
"AttachStdin": false,
"AttachStdout": false,
"Cmd": [
"/bin/bash"
],
.........
.........
}]
工作原理…
Docker 将查看给定镜像或容器的元数据和配置,并呈现出来。
还有更多…
使用-f | --format
选项,我们可以使用 Go(编程语言)模板来获取特定信息。以下命令将给出容器的 IP 地址:
$ docker inspect --format='{{.NetworkSettings.IPAddress}}' $ID
172.17.0.2
另请参阅
- 查看
docker inspect
的help
选项:
$ docker inspect --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#inspect
标记和过滤容器
使用 Docker 1.6,已添加了一个功能来标记容器和镜像,通过这个功能,我们可以向它们附加任意的键值元数据。您可以将它们视为环境变量,这些变量对于容器内运行的应用程序不可用,但对于管理镜像和容器的程序(Docker CLI)是可用的。附加到镜像的标签也会应用到通过它们启动的容器。我们还可以在启动容器时附加标签。
Docker 还为容器、镜像和事件提供了过滤器(docs.docker.com/reference/commandline/cli/#filtering
),我们可以与标签一起使用,以缩小搜索范围。
对于这个示例,让我们假设我们有一个带有标签 distro=fedora21
的镜像。在下一章中,我们将看到如何为镜像分配标签。
从上面的截图中可以看到,如果我们在 docker images
命令中使用过滤器,我们只会得到一个在镜像元数据中找到相应标签的镜像。
准备工作
确保主机上运行着 Docker 守护程序 1.6 及以上版本,并且您可以通过 Docker 客户端进行连接。
操作步骤如下…
- 要使用
--label/-l
选项启动容器,请运行以下命令:
$ docker run --label environment=dev f21 date
- 让我们启动一个没有标签的容器,并使用相同的标签启动另外两个:
如果我们列出所有没有标签的容器,我们将看到所有的容器,但如果我们使用标签,那么我们只会得到与标签匹配的容器。
工作原理…
Docker 在启动容器时附加标签元数据,并在列出它们或其他相关操作时匹配标签。
更多信息…
-
我们可以通过
inspect
命令列出附加到容器的所有标签,这是我们在之前的示例中看到的。正如我们所看到的,inspect
命令返回了镜像和容器的标签。 -
您可以从文件(使用
--from-file
选项)中应用标签,该文件包含以新的 EOL 分隔的标签列表。 -
这些标签与 Kubernetes 标签不同,我们将在第八章中看到,Docker Orchestration and Hosting Platforms。
另请参阅
第三章:使用 Docker 镜像
在本章中,我们将涵盖以下配方:
-
在 Docker Hub 上创建一个帐户
-
从容器创建一个镜像
-
将镜像发布到注册表
-
查看镜像的历史
-
删除镜像
-
导出镜像
-
导入镜像
-
使用 Dockerfile 构建镜像
-
构建 Apache 镜像 - 一个 Dockerfile 示例
-
从容器中访问 Firefox - 一个 Dockerfile 示例
-
构建 WordPress 镜像 - 一个 Dockerfile 示例
-
设置私有索引/注册表
-
自动化构建 - 使用 GitHub 和 Bitbucket
-
创建基础镜像 - 使用 supermin
-
创建基础镜像 - 使用 Debootstrap
-
可视化层之间的依赖关系
介绍
在本章中,我们将专注于与镜像相关的操作。正如我们所知,运行容器需要镜像。您可以使用现有的镜像或创建新的自定义镜像。您需要创建自定义镜像以适应您的开发和部署环境。创建镜像后,您可以通过公共或私有注册表共享它。在我们更多地探索 Docker 镜像之前,让我们看一下docker info
命令的输出:
前面的命令给出了当前系统范围的信息如下:
-
它有 21 个容器和 21 个镜像。
-
当前的存储驱动程序,
devicemapper
,以及与之相关的信息,如 thin pool 名称,数据,元数据文件等。其他类型的存储驱动程序包括 aufs,btrfs,overlayfs,vfs 等。Devicemapper,btrfs 和 overlayfs 在 Linux 内核中有原生支持。AUFS 支持需要一个经过修补的内核。我们在第一章中讨论了 Union 文件系统,介绍和安装。 -
为了利用启用容器化的内核特性,Docker 守护程序必须与 Linux 内核通信。这是通过执行驱动程序完成的。
libconatiner
或native
是其中之一。其他的有libvirt
,lxc
等,我们在第一章中看到了,介绍和安装。 -
主机操作系统上的内核版本。
-
在下一节提到的注册表上注册的用户帐户以拉取/推送镜像。
注意
我正在使用 Fedora 20/21 作为运行配方的主要环境。它们也应该适用于其他环境。
在 Docker Hub 上创建一个帐户
Docker Hub 就像图像的 GitHub。它是一个公共注册表,您可以在其中托管图像,包括公共和私有图像,并与他人合作。它与 GitHub、Bitbucket 集成,并可以触发自动构建。
目前,在 Docker Hub 上创建帐户是免费的。一个仓库可以容纳图像的不同版本。您可以为您的图像创建任意数量的公共仓库。默认情况下,您将拥有一个私有仓库,该仓库对公众不可见。您可以购买更多的私有仓库。您可以通过 Web 浏览器或命令行创建帐户。
准备工作
要从命令行注册,您需要在系统上安装 Docker。
如何做...
-
要通过 Docker Hub 的 Web 浏览器创建帐户,请访问
hub.docker.com/account/signup/
并创建一个帐户: -
要使用命令行创建一个帐户,请运行以下命令并提交所需的详细信息:
$ docker login
它是如何工作的...
上述步骤将为您创建一个 Docker Hub 帐户。帐户创建后,您将收到一封确认邮件,通过该邮件您需要确认您的身份。
另请参阅
-
Docker 网站上的文档:
从容器创建镜像
有几种创建镜像的方法,一种是手动提交层,另一种是通过 Dockerfile。在这个教程中,我们将看到前者,稍后在本章中再看 Dockerfile。
当我们启动一个新的容器时,会附加一个读/写层。如果我们不保存这个层,它将被销毁。在这个教程中,我们将看到如何保存这个层,并使用docker commit
命令从正在运行或停止的容器中创建一个新的镜像。
准备工作
要获取 Docker 镜像,请使用它启动一个容器。
如何做...
- 要进行提交,请运行以下命令:
docker commit -a|--author[=""] -m|--message[=""] CONTAINER [REPOSITORY[:TAG]]
-
让我们启动一个容器并使用
install httpd
包创建/修改一些文件: -
然后,打开一个新的终端并通过提交创建一个新的镜像:
$ docker commit -a "Neependra Khare" -m "Fedora with HTTPD package" 0a15686588ef nkhare/fedora:httpd
如您所见,新的镜像现在正在使用nkhare/fedora
作为名称和httpd
作为标签提交到本地仓库。
它是如何工作的...
在第一章介绍和安装中,我们看到在启动容器时,将在容器启动的现有镜像层之上创建一个读/写文件系统层,并且通过安装软件包,一些文件将被添加/修改到该层中。所有这些更改目前都在临时的读/写文件系统层中,该层分配给容器。如果我们停止并删除容器,那么所有先前提到的修改将丢失。
使用 commit,我们创建一个新的层,其中包含自容器启动以来发生的更改,这些更改保存在后端存储驱动程序中。
还有更多…
- 查找自容器启动以来已更改的文件:
$ docker diff CONTAINER
在我们的情况下,我们将看到类似以下代码的内容:
$ docker diff 0a15686588ef
.....
C /var/log
A /var/log/httpd
C /var/log/lastlog
.....
我们可以在输出的每个条目之前看到一个前缀。以下是这些前缀的列表:
-
A
: 当文件/目录被添加时 -
C
: 当文件/目录被修改时 -
D
: 当文件/目录被删除时 -
默认情况下,在执行提交时容器会被暂停。您可以通过传递
--pause=false
来更改其行为。
另请参阅
- 查看
docker commit
的help
选项:
$ docker commit --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#commit
将镜像发布到注册表
假设您已经创建了一个适合组织开发环境的镜像。您可以使用 tar ball 进行共享,我们将在本章后面看到,或者将其放在用户可以拉取的中央位置。这个中央位置可以是公共的或私有的注册表。在本教程中,我们将看到如何使用docker push
命令将镜像推送到注册表。在本章后面,我们将介绍如何设置私有注册表。
准备工作
您需要在 Docker Hub 上拥有有效的帐户才能推送镜像/仓库。
如果您要推送本地镜像/仓库,必须设置本地注册表。
如何做…
$ docker push NAME[:TAG]
默认情况下,前面的命令将使用docker info
命令中显示的用户名和注册表来推送镜像。如前面的屏幕截图所示,该命令将使用nkhare
作为用户名,https://index.docker.io/v1/
作为注册表。
要推送在上一节中创建的图像,请运行以下命令:
$ docker push nkhare/fedora:httpd
假设您想要将图像推送到本地注册表,该注册表托管在名为local-registry
的主机上。为此,您首先需要使用注册表主机的名称或 IP 地址以及注册表正在运行的端口号对图像进行标记,然后推送图像。
$ docker tag [-f|--force[=false] IMAGE [REGISTRYHOST/][USERNAME/]NAME[:TAG]
$ docker push [REGISTRYHOST/][USERNAME/]NAME[:TAG]
例如,假设我们的注册表配置在shadowfax.example.com
上,然后使用以下命令标记图像:
$ docker tag nkhare/fedora:httpd shadowfax.example.com:5000/nkhare/fedora:httpd
然后,要推送图像,请使用以下命令:
$ docker push shadowfax.example.com:5000/nkhare/fedora:httpd
它是如何工作的...
它将首先列出制作特定图像所需的所有中间层。然后,它将检查这些层中有多少已经存在于注册表中。最后,它将复制所有不在注册表中的层,并附上构建图像所需的元数据。
更多内容...
当我们将图像推送到公共注册表时,我们可以登录 Docker Hub 并查找图像:
另请参阅
- 查看
docker push
的help
选项:
$ docker push --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#push
查看图像的历史记录
了解我们正在使用的图像是如何创建的很方便。docker history
命令帮助我们找到所有中间层。
准备工作
拉取或导入任何 Docker 图像。
如何操作...
- 要查看图像的历史记录,请考虑以下语法:
$ docker history [ OPTIONS ] IMAGE
以下是使用上述语法的示例:
$ docker history nkhare/fedora:httpd
它是如何工作的...
通过图像的元数据,Docker 可以知道图像是如何创建的。使用history
命令,它将递归查看元数据以找到原始来源。
更多内容...
查看已提交层的提交消息:
$ docker inspect --format='{{.Comment}}' nkhare/fedora:httpd
Fedora with HTTPD package
目前,没有直接的方法可以使用一个命令查看每个层的提交消息,但是我们可以使用inspect
命令,我们之前看到的,对每个层进行查看。
另请参阅
- 查看
docker history
的help
选项:
$ docker history --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#history
删除图像
要从主机中删除图像,我们可以使用docker rmi
命令。但是,这不会从注册表中删除图像。
准备工作
确保一个或多个 Docker 图像在本地可用。
如何做…
- 要删除图像,请考虑以下语法:
$ docker rmi [ OPTIONS ] IMAGE [IMAGE...]
在我们的情况下,以下是使用前述语法的示例:
$ docker rmi nkhare/fedora:httpd
还有更多…
如果要删除所有容器和镜像,请执行以下操作;但是,请确保自己知道自己在做什么,因为这是非常具有破坏性的:
- 要停止所有容器,请使用以下命令:
$ docker stop 'docker ps -q'
- 要删除所有容器,请使用以下命令:
$ docker rm 'docker ps -a -q'
- 要删除所有图像,请使用以下命令:
$ docker rmi 'docker images -q'
另请参阅
- 查看
docker rmi
的help
选项:
$ docker rmi --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#rmi
导出图像
假设您有一个客户,其非常严格的政策不允许他们使用来自公共领域的图像。在这种情况下,您可以通过 tar 文件共享图像,稍后可以在另一个系统上导入。在本示例中,我们将看到如何使用docker save
命令来做到这一点。
准备工作
在 Docker 主机上拉取或导入一个或多个 Docker 图像。
如何做…
- 使用以下语法将图像保存在 tar 文件中:
$ docker save [-o|--output=""] IMAGE [:TAG]
例如,要为 Fedora 创建一个 tar 归档,请运行以下命令:
$ docker save --output=fedora.tar fedora
如果指定了标签名称与我们要导出的图像名称,例如fedora:latest
,那么只有与该标签相关的层将被导出。
还有更多…
如果没有使用--output
或-o
,输出将被流式传输到STDOUT
:
$ docker save fedora:latest > fedora-latest.tar
类似地,可以使用以下命令导出容器文件系统的内容:
$ docker export CONTAINER > containerXYZ.tar
另请参阅
- 查看
docker save
和docker export
的help
选项:
$ docker save -help
$ docker export --help
-
Docker 网站上的文档:
导入图像
要获得图像的本地副本,我们需要从可访问的注册表中拉取它,或者从已导出的图像中导入它,就像我们在之前的示例中看到的那样。使用docker import
命令,我们导入一个已导出的图像。
准备工作
您需要一个可访问的导出的 Docker 镜像。
如何做…
- 要导入图像,我们可以使用以下语法:
$ docker import URL|- [REPOSITORY[:TAG]]
以下是使用前述语法的示例:
$ cat fedora-latest.tar | docker import - fedora:latest
或者,您可以考虑以下示例:
$ docker import http://example.com/example.tar example/image
前面的示例将首先创建一个空的文件系统,然后导入内容。
参见
- 查看
docker import
的help
选项:
$ docker import --help
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#import
使用 Dockerfile 构建映像
Dockerfile 帮助我们自动化映像创建,并在我们每次需要时获得完全相同的映像。Docker 构建器从文本文件(Dockerfile)中读取指令,并按顺序依次执行。它可以与 Vagrant 文件进行比较,Vagrant 文件允许您以可预测的方式配置虚拟机。
准备工作
具有构建指令的 Dockerfile。
- 创建一个空目录:
$ mkdir sample_image
$ cd sample_image
- 创建一个名为
Dockerfile
的文件,内容如下:
$ cat Dockerfile
# Pick up the base image
FROM fedora
# Add author name
MAINTAINER Neependra Khare
# Add the command to run at the start of container
CMD date
操作方法...
- 在创建 Dockerfile 的目录中运行以下命令来构建映像:
$ docker build .
在构建映像时,我们没有指定任何存储库或标签名称。我们可以使用-t
选项来指定:
$ docker build -t fedora/test .
前面的输出与我们之前所做的不同。然而,在这里,我们在每个指令之后都使用缓存。Docker 尝试保存中间映像,就像我们之前看到的那样,并尝试在后续构建中使用它们来加速构建过程。如果你不想缓存中间映像,那么在构建时添加--no-cache
选项。现在让我们来看一下可用的映像:
它是如何工作的...
上下文定义了用于构建 Docker 映像的文件。在前面的命令中,我们将上下文定义为构建。构建由 Docker 守护程序完成,并且整个上下文被传输到守护程序。这就是为什么我们看到Sending build context to Docker daemon 2.048 kB
消息。如果当前工作目录中有一个名为.dockerignore
的文件,其中包含文件和目录的列表(以换行符分隔),那么这些文件和目录将被构建上下文忽略。有关.dockerignore
的更多详细信息,请参阅docs.docker.com/reference/builder/#the-dockerignore-file
。
执行每个指令后,Docker 会提交中间镜像并为下一个指令运行一个容器。在下一个指令运行后,Docker 将再次提交容器以创建中间镜像,并删除在上一步中创建的中间容器。
例如,在上面的屏幕截图中,eb9f10384509
是一个中间镜像,c5d4dd2b3db9
和ffb9303ab124
是中间容器。执行最后一个指令后,将创建最终镜像。在这种情况下,最终镜像是4778dd1f1a7a
:
可以在docker images
命令中使用-a
选项来查找中间层:
$ docker images -a
还有更多…
Dockerfile 的格式如下:
INSTRUCTION arguments
通常,指令以大写形式给出,但它们不区分大小写。它们按顺序进行评估。以#
开头的内容被视为注释。
让我们来看看不同类型的指令:
FROM
:这必须是任何 Dockerfile 的第一个指令,它为后续指令设置了基础镜像。默认情况下,假定为最新标签:
FROM <image>
或者,考虑以下标签:
FROM <images>:<tag>
一个 Dockerfile 中可以有多个FROM
指令,以创建多个镜像。
如果只提供镜像名称,例如 Fedora 和 Ubuntu,则将从默认的 Docker 注册表(Docker Hub)下载镜像。如果要使用私有或第三方镜像,则必须按以下方式提及:
[registry_hostname[:port]/]user_name/
以下是使用上述语法的示例:
FROM registry-host:5000/nkhare/f20:httpd
-
MAINTAINER
:这为生成的镜像设置了作者,MAINTAINER <name>
。 -
RUN
:我们可以以两种方式执行RUN
指令——首先,在 shell 中运行(sh -c
):
RUN <command> <param1> ... <pamamN>
其次,直接运行可执行文件:
RUN ["executable", "param1",...,"paramN" ]
正如我们所知,使用 Docker,我们创建一个覆盖层——在另一个层之上的一层——以创建最终的镜像。通过每个RUN
指令,我们创建并提交一个层,放在之前提交的层之上。可以从任何已提交的层启动容器。
默认情况下,Docker 会尝试缓存不同RUN
指令提交的层,以便在后续构建中使用。但是,可以在构建镜像时使用--no-cache
标志来关闭此行为。
-
LABEL
:Docker 1.6 添加了一个新功能,可以将任意键值对附加到 Docker 镜像和容器上。我们在第二章的标记和过滤容器中介绍了部分内容,使用 Docker 容器。要为图像添加标签,我们在 Dockerfile 中使用LABEL
指令,如LABEL distro=fedora21
。 -
CMD
:CMD
指令在启动容器时提供默认可执行文件。如果CMD
指令没有可执行文件(参数 2),那么它将为ENTRYPOINT
提供参数。
CMD ["executable", "param1",...,"paramN" ]
CMD ["param1", ... , "paramN"]
CMD <command> <param1> ... <pamamN>
Dockerfile 中只允许一个CMD
指令。如果指定了多个指令,则只有最后一个会被采纳。
ENTRYPOINT
:这有助于我们将容器配置为可执行文件。与CMD
类似,ENTRYPOINT
最多只能有一条指令;如果指定了多条指令,则只有最后一条会被采纳:
ENTRYPOINT ["executable", "param1",...,"paramN" ]
ENTRYPOINT <command> <param1> ... <pamamN>
一旦使用ENTRYPOINT
指令定义了参数,它们就不能在运行时被覆盖。但是,如果我们想要对ENTRYPOINT
使用不同的参数,可以将ENTRYPOINT
用作CMD
。
EXPOSE
:这将在容器上暴露网络端口,容器将在其中运行时监听:
EXPOSE <port> [<port> ... ]
我们还可以在启动容器时暴露端口。我们在第二章的在启动容器时暴露端口中介绍了这一点,使用 Docker 容器。
ENV
:这将将环境变量<key>
设置为<value>
。它将传递所有未来的指令,并在从生成的镜像运行容器时持久存在:
ENV <key> <value>
ADD
:这将文件从源复制到目的地:
ADD <src> <dest>
以下是包含空格的路径:
ADD ["<src>"... "<dest>"]
-
<src>
:这必须是构建目录中的文件或目录,我们正在从中构建图像,也称为构建的上下文。源也可以是远程 URL。 -
<dest>
:这必须是容器内的绝对路径,源中的文件/目录将被复制到其中。 -
COPY
:这类似于ADD.COPY <src> <dest>
:
COPY ["<src>"... "<dest>"]
VOLUME
:此指令将使用以下语法创建具有给定名称的挂载点,并将其标记为使用外部卷进行挂载:
VOLUME ["/data"]
或者,您可以使用以下代码:
VOLUME /data
USER
:这将使用以下语法为任何后续的运行指令设置用户名:
USER <username>/<UID>
WORKDIR
:这为随后的RUN
、CMD
和ENTRYPOINT
指令设置工作目录。它可以在同一个 Dockerfile 中有多个条目。可以给出相对路径,它将相对于之前的WORKDIR
指令,使用以下语法:
WORKDIR <PATH>
ONBUILD
:这将向图像添加触发指令,稍后将在将此图像用作另一个图像的基本图像时执行。此触发器将作为下游 Dockerfile 中的FROM
指令的一部分运行,使用以下语法:
ONBUILD [INSTRUCTION]
另请参阅
- 查看
docker build
的help
选项:
$ docker build -help
- Docker 网站上的文档
docs.docker.com/reference/builder/
构建 Apache 镜像 - 一个 Dockerfile 示例
我将在从 Fedora-Dockerfiles GitHub 存储库(github.com/fedora-cloud/Fedora-Dockerfiles
)中引用 Dockerfiles,之后对其进行分叉。如果您使用的是 Fedora,那么您也可以安装fedora-dockerfiles
软件包,以获取/usr/share/fedora-dockerfiles
中的示例 Dockerfiles。在每个子目录中,您将放置一个 Dockerfile、支持文件和一个 README 文件。
Fedora-Dockerfiles GitHub 存储库将具有最新的示例,我强烈建议您尝试最新的内容。
准备工作
使用以下命令克隆 Fedora-Dockerfiles Git 存储库:
$ git clone https://github.com/nkhare/Fedora-Dockerfiles.git
现在,转到apache
子目录:
$ cd Fedora-Dockerfiles/apache/
$ cat Dockerfile
FROM fedora:20
MAINTAINER "Scott Collier" <scollier@redhat.com>
RUN yum -y update && yum clean all
RUN yum -y install httpd && yum clean all
RUN echo "Apache" >> /var/www/html/index.html
EXPOSE 80
# Simple startup script to avoid some issues observed with container restart
ADD run-apache.sh /run-apache.sh
RUN chmod -v +x /run-apache.sh
CMD ["/run-apache.sh"]
其他支持文件包括:
-
README.md
:这是 README 文件 -
run-apache.sh
:这是在前台运行HTTPD
的脚本 -
LICENSE
:这是 GPL 许可证
如何做...
使用以下build
命令,我们可以构建一个新的镜像:
$ docker build -t fedora/apache .
Sending build context to Docker daemon 23.55 kB
Sending build context to Docker daemon
Step 0 : FROM fedora:20
---> 6cece30db4f9
Step 1 : MAINTAINER "Scott Collier" <scollier@redhat.com>
---> Running in 2048200e6338
---> ae8e3c258061
Removing intermediate container 2048200e6338
Step 2 : RUN yum -y update && yum clean all
---> Running in df8bc8ee3117
.... Installing/Update packages ...
Cleaning up everything
---> 5a6d449e59f6
Removing intermediate container df8bc8ee3117
Step 3 : RUN yum -y install httpd && yum clean all
---> Running in 24449e520f18
.... Installing HTTPD ...
Cleaning up everything
---> ae1625544ef6
Removing intermediate container 24449e520f18
Step 4 : RUN echo "Apache" >> /var/www/html/index.html
---> Running in a35cbcd8d97a
---> 251eea31b3ce
Removing intermediate container a35cbcd8d97a
Step 5 : EXPOSE 80
---> Running in 734e54f4bf58
---> 19503ae2a8cf
Removing intermediate container 734e54f4bf58
Step 6 : ADD run-apache.sh /run-apache.sh
---> de35d746f43b
Removing intermediate container 3eec9a46da64
Step 7 : RUN chmod -v +x /run-apache.sh
---> Running in 3664efba393f
mode of '/run-apache.sh' changed from 0644 (rw-r--r--) to 0755 (rwxr-xr-x)
---> 1cb729521c3f
Removing intermediate container 3664efba393f
Step 8 : CMD /run-apache.sh
---> Running in cd5e7534e815
---> 5f8041b6002c
Removing intermediate container cd5e7534e815
Successfully built 5f8041b6002c
它是如何工作的...
构建过程需要一个基本镜像,安装所需的HTTPD
软件包并创建一个 HTML 页面。然后,它公开端口80
以提供网页,并设置指令在容器启动时启动 Apache。
更多内容...
让我们从创建的镜像中运行容器,获取其 IP 地址,并从中访问网页:
另请参阅
- 查看
docker build
的help
选项:
$ docker build --help
- Docker 网站上的文档
docs.docker.com/reference/builder/
从容器访问 Firefox - 一个 Dockerfile 示例
我们可以通过 Dockerfile 做一些更有趣的事情,比如创建一个只运行 Firefox 的容器。这种用例可以帮助在同一台机器上运行不同版本的多个浏览器,这在进行多浏览器测试时非常有帮助。
准备工作
使用以下命令克隆 Fedora-Dockerfiles Git 存储库:
$ git clone https://github.com/nkhare/Fedora-Dockerfiles.git
然后,转到firefox
子目录。
$ cd Fedora-Dockerfiles/firefox
$ cat Dockerfile
FROM fedora
MAINTAINER scollier <emailscottcollier@gmail.com>
# Install the appropriate software
RUN yum -y update && yum clean all
RUN yum -y install x11vnc \
firefox xorg-x11-server-Xvfb \
xorg-x11-twm tigervnc-server \
xterm xorg-x11-font \
xulrunner-26.0-2.fc20.x86_64 \
dejavu-sans-fonts \
dejavu-serif-fonts \
xdotool && yum clean all
# Add the xstartup file into the image
ADD ./xstartup /
RUN mkdir /.vnc
RUN x11vnc -storepasswd 123456 /.vnc/passwd
RUN \cp -f ./xstartup /.vnc/.
RUN chmod -v +x /.vnc/xstartup
RUN sed -i '/\/etc\/X11\/xinit\/xinitrc-common/a [ -x /usr/bin/firefox ] && /usr/bin/firefox &' /etc/X11/xinit/xinitrc
EXPOSE 5901
CMD ["vncserver", "-fg" ]
# ENTRYPOINT ["vncserver", "-fg" ]
支持文件:
-
README.md
:这是一个 README 文件 -
LICENSE
:这是 GPL 许可证 -
xstartup
:这是设置 X11 环境的脚本
如何做...
运行以下命令构建镜像:
$ docker build -t fedora/firefox .
Sending build context to Docker daemon 24.58 kB
Sending build context to Docker daemon
Step 0 : FROM fedora
---> 834629358fe2
Step 1 : MAINTAINER scollier <emailscottcollier@gmail.com>
---> Running in ae0fd3c2cb2e
---> 7ffc6c9af827
Removing intermediate container ae0fd3c2cb2e
Step 2 : RUN yum -y update && yum clean all
---> Running in 1c67b8772718
..... Installing/Update packages ...
---> 075d6ceef3d0
Removing intermediate container 1c67b8772718
Step 3 : RUN yum -y install x11vnc firefox xorg-x11-server-Xvfb xorg-x11-twm tigervnc-server xterm xorg-x11-font xulrunner-26.0-2.fc20.x86_64 dejavu-sans-fonts dejavu-serif-fonts xdotool && yum clean all
..... Installing required packages packages ...
Cleaning up everything
---> 986be48760a6
Removing intermediate container c338a1ad6caf
Step 4 : ADD ./xstartup /
---> 24fa081dcea5
Removing intermediate container fe98d86ba67f
Step 5 : RUN mkdir /.vnc
---> Running in fdb8fe7e697a
---> 18f266ace765
Removing intermediate container fdb8fe7e697a
Step 6 : RUN x11vnc -storepasswd 123456 /.vnc/passwd
---> Running in c5b7cdba157f
stored passwd in file: /.vnc/passwd
---> e4fcf9b17aa9
Removing intermediate container c5b7cdba157f
Step 7 : RUN \cp -f ./xstartup /.vnc/.
---> Running in 21d0dc4edb4e
---> 4c53914323cb
Removing intermediate container 21d0dc4edb4e
Step 8 : RUN chmod -v +x /.vnc/xstartup
---> Running in 38f18f07c996
mode of '/.vnc/xstartup' changed from 0644 (rw-r--r--) to 0755 (rwxr-xr-x)
---> caa278024354
Removing intermediate container 38f18f07c996
Step 9 : RUN sed -i '/\/etc\/X11\/xinit\/xinitrc-common/a [ -x /usr/bin/firefox ] && /usr/bin/firefox &' /etc/X11/xinit/xinitrc
---> Running in 233e99cab02c
---> 421e944ac8b7
Removing intermediate container 233e99cab02c
Step 10 : EXPOSE 5901
---> Running in 530cd361cb3c
---> 5de01995c156
Removing intermediate container 530cd361cb3c
Step 11 : CMD vncserver -fg
---> Running in db89498ae8ce
---> 899be39b7feb
Removing intermediate container db89498ae8ce
Successfully built 899be39b7feb
它是如何工作的...
我们从基本的 Fedora 镜像开始,安装 X Windows System,Firefox,VNC 服务器和其他软件包。然后设置 VNC 服务器启动 X Windows System,然后启动 Firefox。
还有更多...
- 要启动容器,请运行以下命令:
$ docker run -it -p 5901:5901 fedora/firefox
并输入123456
作为密码。
- 在运行容器时,我们将主机的
5901
端口映射到容器的5901
端口。为了连接容器内的 VNC 服务器,只需从另一个终端运行以下命令:
$ vncviewer localhost:1
或者,从网络中的另一台机器上,用 Docker 主机的 IP 地址或 FQDN 替换localhost
。
另请参阅
- 查看
docker build
的help
选项:
$ docker build --help
- Docker 网站上的文档
docs.docker.com/reference/builder/
构建 WordPress 镜像-一个 Dockerfile 示例
到目前为止,我们已经看到了在容器中运行一个服务的示例。如果我们想要运行一个需要同时运行一个或多个服务的应用程序,那么我们要么需要在同一个容器上运行它们,要么在不同的容器上运行它们并将它们链接在一起。WordPress 就是一个这样的例子,它需要数据库和 web 服务。
Docker 只喜欢每个容器中运行的前台一个进程。因此,为了让 Docker 满意,我们有一个控制进程来管理数据库和 web 服务。在这种情况下,控制进程是 supervisord(supervisord.org/
)。这是我们用来让 Docker 满意的一个技巧。
同样,我们将使用 Fedora-Dockerfiles 存储库中的 Dockerfile。
准备工作
使用以下命令克隆 Fedora-Dockerfiles Git 存储库:
$ git clone https://github.com/nkhare/Fedora-Dockerfiles.git
然后,转到wordpress_single_container
子目录:
$ cd Fedora-Dockerfiles/systemd/wordpress_single_container
$ cat Dockerfile
FROM fedora
MAINTAINER scollier <scollier@redhat.com>
RUN yum -y update && yum clean all
RUN yum -y install httpd php php-mysql php-gd pwgen supervisor bash-completion openssh-server psmisc tar && yum clean all
ADD ./start.sh /start.sh
ADD ./foreground.sh /etc/apache2/foreground.sh
ADD ./supervisord.conf /etc/supervisord.conf
RUN echo %sudo ALL=NOPASSWD: ALL >> /etc/sudoers
ADD http://wordpress.org/latest.tar.gz /wordpress.tar.gz
RUN tar xvzf /wordpress.tar.gz
RUN mv /wordpress/* /var/www/html/.
RUN chown -R apache:apache /var/www/
RUN chmod 755 /start.sh
RUN chmod 755 /etc/apache2/foreground.sh
RUN mkdir /var/run/sshd
EXPOSE 80
EXPOSE 22
CMD ["/bin/bash", "/start.sh"]
前面代码中使用的支持文件解释如下:
-
foreground.sh
:这是一个在前台运行 HTTPS 的脚本。 -
LICENSE
、LICENSE.txt
和UNLICENSE.txt
:这些文件包含许可信息。 -
README.md
:这是一个 README 文件。 -
supervisord.conf
:这是一个结果容器,必须同时运行SSHD
、MySQL
和HTTPD
。在这种特殊情况下,使用 supervisor 来管理它们。这是 supervisor 的配置文件。有关此的更多信息,请访问supervisord.org/
。 -
start.sh
:这是一个设置 MySQL、HTTPD 并启动 supervisor 守护进程的脚本。
如何做…
$ docker build -t fedora/wordpress .
Sending build context to Docker daemon 41.98 kB
Sending build context to Docker daemon
Step 0 : FROM fedora
---> 834629358fe2
Step 1 : MAINTAINER scollier <scollier@redhat.com>
---> Using cache
---> f21eaf47c9fc
Step 2 : RUN yum -y update && yum clean all
---> Using cache
---> a8f497a6e57c
Step 3 : RUN yum -y install httpd php php-mysql php-gd pwgen supervisor bash-completion openssh-server psmisc tar && yum clean all
---> Running in 303234ebf1e1
.... updating/installing packages ....
Cleaning up everything
---> cc19a5f5c4aa
Removing intermediate container 303234ebf1e1
Step 4 : ADD ./start.sh /start.sh
---> 3f911077da44
Removing intermediate container c2bd643236ef
Step 5 : ADD ./foreground.sh /etc/apache2/foreground.sh
---> 3799902a60c5
Removing intermediate container c99b8e910009
Step 6 : ADD ./supervisord.conf /etc/supervisord.conf
---> f232433b8925
Removing intermediate container 0584b945f6f7
Step 7 : RUN echo %sudo ALL=NOPASSWD: ALL >> /etc/sudoers
---> Running in 581db01d7350
---> ec686e945dfd
Removing intermediate container 581db01d7350
Step 8 : ADD http://wordpress.org/latest.tar.gz /wordpress.tar.gz
Downloading [==================================================>] 6.186 MB/6.186 MB
---> e4e902c389a4
Removing intermediate container 6bfecfbe798d
Step 9 : RUN tar xvzf /wordpress.tar.gz
---> Running in cd772500a776
.......... untarring wordpress .........
---> d2c5176228e5
Removing intermediate container cd772500a776
Step 10 : RUN mv /wordpress/* /var/www/html/.
---> Running in 7b19abeb509c
---> 09400817c55f
Removing intermediate container 7b19abeb509c
Step 11 : RUN chown -R apache:apache /var/www/
---> Running in f6b9b6d83b5c
---> b35a901735d9
Removing intermediate container f6b9b6d83b5c
Step 12 : RUN chmod 755 /start.sh
---> Running in 81718f8d52fa
---> 87470a002e12
Removing intermediate container 81718f8d52fa
Step 13 : RUN chmod 755 /etc/apache2/foreground.sh
---> Running in 040c09148e1c
---> 1c76f1511685
Removing intermediate container 040c09148e1c
Step 14 : RUN mkdir /var/run/sshd
---> Running in 77177a33aee0
---> f339dd1f3e6b
Removing intermediate container 77177a33aee0
Step 15 : EXPOSE 80
---> Running in f27c0b96d17f
---> 6078f0d7b70b
Removing intermediate container f27c0b96d17f
Step 16 : EXPOSE 22
---> Running in eb7c7d90b860
---> 38f36e5c7cab
Removing intermediate container eb7c7d90b860
Step 17 : CMD /bin/bash /start.sh
---> Running in 5635fe4783da
---> c1a327532355
Removing intermediate container 5635fe4783da
Successfully built c1a327532355
它是如何工作的…
与其他示例一样,我们从基本镜像开始,安装所需的软件包,并复制支持文件。然后设置sudo
、download
和untar
WordPress 在 HTTPD 文档根目录内。之后,我们暴露端口并运行 start.sh 脚本,该脚本设置 MySQL、WordPress、HTTPS 权限并将控制权交给 supervisord。在supervisord.conf
中,您将看到 supervisord 管理的以下服务条目:
[program:mysqld]
command=/usr/bin/mysqld_safe
[program:httpd]
command=/etc/apache2/foreground.sh
stopsignal=6
[program:sshd]
command=/usr/sbin/sshd -D
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stderr_logfile=/var/log/supervisor/%(program_name)s.log
autorestart=true
还有更多…
-
启动容器,获取其 IP 地址并通过 Web 浏览器打开。在进行语言选择后,您应该看到欢迎屏幕,如下面的屏幕截图所示:
-
现在可以在容器内运行 systemd,这是一种更受欢迎的方式。Systemd 可以管理多个服务。您可以在
github.com/fedora-cloud/Fedora-Dockerfiles/tree/master/systemd
中查看 systemd 的示例。
另请参阅
- 查看
docker build
的help
选项:
$ docker build --help
- Docker 网站上的文档
docs.docker.com/reference/builder/
设置私有索引/注册表
正如我们之前看到的,公共 Docker 注册表是可用的 Docker Hub(registry.hub.docker.com/
),用户可以通过它推送/拉取镜像。我们还可以在本地环境或云上托管私有注册表。有几种设置本地注册表的方法:
-
使用 Docker Hub 的 Docker 注册表
-
从 Dockerfile 构建镜像并运行注册表容器:
github.com/fedora-cloud/Fedora-Dockerfiles/tree/master/registry
- 配置特定于发行版的软件包,例如提供了 docker-registry 软件包的 Fedora。
设置它的最简单方法是通过注册表容器本身。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何做…
- 要在容器上运行注册表,请运行以下命令:
$ docker run -p 5000:5000 registry
-
要测试新创建的注册表,请执行以下步骤:
-
使用以下命令启动容器及其 ID:
$ ID='docker run -d -i fedora /bin/bash'
- 如果需要,附加到新创建的容器并进行一些更改。然后,将这些更改提交到本地存储库:
$ docker commit $ID fedora-20
- 要将镜像推送到本地注册表,我们需要使用注册表主机的主机名或 IP 地址对镜像进行标记。假设我们的注册表主机是
registry-host
;然后,要对其进行标记,请使用以下命令:
$ docker tag fedora-20 registry-host:5000/nkhare/f20
- 由于我们在启动注册表时没有正确配置 HTTPS,因此我们将收到错误,例如
ping attempt failed with error: Get https://dockerhost:5000/v1/_ping
,这是预期的。为了使我们的示例工作,我们需要向守护程序添加--insecure-registry registry-host:5000
选项。如果您手动启动了 Docker 守护程序,那么我们必须按照以下方式运行命令以允许不安全的注册表:
$ docker -d --insecure-registry registry-host:5000
- 要推送镜像,请使用以下命令:
$ docker push registry-host:5000/nkhare/f20
- 要从本地注册表中拉取镜像,请运行以下命令:
$ docker pull registry-host:5000/nkhare/f20
工作原理…
从 Docker Hub 下载官方注册表镜像并在端口 5000
上运行它的上述命令。-p
选项将容器端口发布到主机系统的端口。我们将在下一章中详细了解端口发布的细节。
也可以使用 docker-registry 应用程序在任何现有服务器上配置注册表。执行此操作的步骤可在 docker-registry GitHub 页面上找到:
github.com/docker/docker-registry
还有更多…
让我们看看 docker-registry 的 Dockerfile,以了解注册表镜像是如何创建的,以及如何设置不同的配置选项:
# VERSION 0.1
# DOCKER-VERSION 0.7.3
# AUTHOR: Sam Alba <sam@docker.com>
# DESCRIPTION: Image with docker-registry project and dependencies
# TO_BUILD: docker build -rm -t registry .
# TO_RUN: docker run -p 5000:5000 registry
# Latest Ubuntu LTS
FROM ubuntu:14.04
# Update
RUN apt-get update \
# Install pip
&& apt-get install -y \
swig \
python-pip \
# Install deps for backports.lzma (python2 requires it)
python-dev \
python-mysqldb \
python-rsa \
libssl-dev \
liblzma-dev \
libevent1-dev \
&& rm -rf /var/lib/apt/lists/*
COPY . /docker-registry
COPY ./config/boto.cfg /etc/boto.cfg
# Install core
RUN pip install /docker-registry/depends/docker-registry-core
# Install registry
RUN pip install file:///docker-registry#egg=docker-registry[bugsnag,newrelic,cors]
RUN patch \
$(python -c 'import boto; import os; print os.path.dirname(boto.__file__)')/connection.py \
< /docker-registry/contrib/boto_header_patch.diff
ENV DOCKER_REGISTRY_CONFIG /docker-registry/config/config_sample.yml
ENV SETTINGS_FLAVOR dev
EXPOSE 5000
CMD ["docker-registry"]
使用上述 Dockerfile,我们将:
-
使用 Ubuntu 的基本镜像安装/更新软件包
-
将 docker-registry 源代码复制到镜像中
-
使用
pip install
docker-registry -
设置在运行注册表时使用的配置文件的环境变量
-
使用环境变量设置运行注册表时要使用的 flavor
-
暴露端口
5000
-
运行注册表可执行文件
配置文件(/docker-registry/config/config_sample.yml
)中的风格提供了配置注册表的不同方式。使用上述 Dockerfile,我们将使用环境变量设置dev
风格。不同类型的风格包括:
-
common
: 这是所有其他风格的基本设置 -
local
: 这将数据存储在本地文件系统中 -
s3
: 这将数据存储在 AWS S3 存储桶中 -
dev
: 这是使用本地风格的基本配置 -
test
: 这是单元测试使用的配置 -
prod
: 这是生产配置(基本上是 S3 风格的同义词) -
gcs
: 这将数据存储在 Google 云存储中 -
swift
: 这将数据存储在 OpenStack Swift 中 -
glance
: 这将数据存储在 OpenStack Glance 中,备用为本地存储 -
glance-swift
: 这将数据存储在 OpenStack Glance 中,备用为 Swift -
elliptics
: 这将数据存储在椭圆键值存储中
对于上述每种风格,都有不同的配置选项,例如日志级别、身份验证等。所有选项的文档都可以在我之前提到的 docker-registry 的 GitHub 页面上找到。
另请参阅
- GitHub 上的文档
github.com/docker/docker-registry
使用 GitHub 和 Bitbucket 进行自动构建
我们之前已经看到如何将 Docker 镜像推送到 Docker Hub。Docker Hub 允许我们使用其构建集群从 GitHub/Bitbucket 存储库创建自动化镜像。GitHub/Bitbucket 存储库应包含 Dockerfile 和所需的内容以复制/添加到镜像中。让我们在接下来的部分中看一个 GitHub 的例子。
准备工作
您将需要在 Docker Hub 和 GitHub 上拥有帐户。您还需要一个具有相应 Dockerfile 的 GitHub 存储库,位于顶层。
如何做…
-
登录到 Docker Hub(
hub.docker.com/
)并单击绿色加号。在右上角添加存储库图标,然后单击自动化构建。选择 GitHub 作为自动化构建的源。然后,选择公共和私有(推荐)选项以连接到 GitHub。在提示时提供 GitHub 用户名/密码。选择要执行自动化构建的 GitHub 存储库。 -
选择 GitHub 存储库后,它会要求您选择要用于自动构建的分支。它还会要求您提供一个标签名称,以在自动构建的镜像之后使用。默认情况下,将使用最新的标签名称。然后,单击保存并触发构建按钮开始自动构建过程。就是这样!您的构建现在已提交。您可以单击构建状态来检查构建的状态。
它是如何工作的...
当我们选择 GitHub 存储库进行自动构建时,GitHub 会为该存储库启用 Docker 服务。您可以查看 GitHub 存储库的设置部分以进行更多配置。每当我们对这个 GitHub 存储库进行任何更改,比如提交,都会使用存储在 GitHub 存储库中的 Dockerfile 触发自动构建。
还有更多...
您可以通过转到您的存储库部分来获取诸如 Dockerfile、构建详细信息标签和其他信息。它还包含了如何拉取您的镜像的详细信息:
使用自动构建过程创建的镜像无法通过docker push
命令推送。
您可以在 GitHub 存储库的Webhooks & Services部分更改设置,以注销 Docker 服务。这将停止自动构建。
另请参阅
-
使用 Bitbucket 设置自动构建的步骤几乎相同。自动构建的挂钩在 Bitbucket 存储库的设置部分的Hooks部分下进行配置。
-
Docker 网站上的文档
docs.docker.com/docker-hub/builds/
创建基础镜像-使用 supermin
在本章的前面,我们使用了FROM
指令来选择要开始的基础镜像。我们创建的镜像可以成为另一个应用程序容器化的基础镜像,依此类推。从一开始到这个链条,我们将有一个来自我们想要使用的基础 Linux 发行版的基础镜像,比如 Fedora、Ubuntu、CentOS 等。
要构建这样的基础镜像,我们需要在目录中安装特定于发行版的基本系统,然后将其导入为 Docker 镜像。使用 chroot 实用程序,我们可以将一个目录伪装成根文件系统,然后在导入为 Docker 镜像之前将所有必要的文件放入其中。Supermin 和 Debootstrap 是可以帮助我们使前述过程更容易的工具。
Supermin 是构建 supermin 应用程序的工具。这些是微型应用程序,可以在飞行中完全实例化。早期这个程序被称为 febootstrap。
准备就绪
在要构建基础镜像的系统上安装 supermin。您可以使用以下命令在 Fedora 上安装 supermin:
$ yum install supermin
如何做...
- 使用
prepare
模式在目录中安装bash
,coreutils
和相关依赖项。
$ supermin --prepare -o OUTPUTDIR PACKAGE [PACKAGE ...]
以下是使用前述语法的示例:
$ supermin --prepare bash coreutils -o f21_base
- 现在,使用
build
模式为基础镜像创建一个 chroot 环境:
$ supermin --build -o OUTPUTDIR -f chroot|ext2 INPUT [INPUT ...]
以下是使用前述语法的示例:
$ supermin --build --format chroot f21_base -o f21_image
- 如果我们在输出目录上执行
ls
,我们将看到一个类似于任何 Linux 根文件系统的目录树:
$ ls f21_image/
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
- 现在我们可以使用以下命令将目录导出为 Docker 镜像:
$ tar -C f21_image/ -c . | docker import - nkhare/f21_base
d6db8b798dee30ad9c84480ef7497222f063936a398ecf639e60599eed7f6560
- 现在,查看
docker images
输出。您应该有一个名为nkhare/f21_base
的新镜像。
它是如何工作的...
Supermin 有两种模式,prepare
和build
。使用prepare
模式,它只是将所有请求的软件包及其依赖项放在一个目录中,而不复制主机操作系统特定的文件。
使用build
模式,先前通过prepare
模式创建的 supermin 应用程序将被转换为具有所有必要文件的完整可引导应用程序。此步骤将从主机复制所需的文件/二进制文件到应用程序目录,因此必须在要在应用程序中使用的主机机器上安装软件包。
build
模式有两种输出格式,chroot 和 ext2。使用 chroot 格式,目录树被写入目录中,而使用 ext2 格式,则创建磁盘映像。我们通过 chroot 格式导出创建的目录来创建 Docker 镜像。
还有更多...
Supermin 不特定于 Fedora,应该适用于任何 Linux 发行版。
另请参阅
- 使用以下命令查看 supermin 的
man
页面以获取更多信息:
$ man supermin
创建基本镜像-使用 Debootstrap
Debootstrap 是一种工具,用于将基于 Debian 的系统安装到已安装系统的目录中。
准备工作
在基于 Debian 的系统上使用以下命令安装debootstrap
:
$ apt-get install debootstrap
如何做…
以下命令可用于使用 Debootstrap 创建基本镜像:
$ debootstrap [OPTION...] SUITE TARGET [MIRROR [SCRIPT]]
SUITE
指的是发布代码名称,MIRROR
是相应的存储库。如果您想创建 Ubuntu 14.04.1 LTS(Trusty Tahr)的基本镜像,则执行以下操作:
- 在要安装操作系统的目录上创建一个目录。Debootstrap 还创建了 chroot 环境以安装软件包,就像我们之前在 supermin 中看到的那样。
$ mkdir trusty_chroot
- 现在,使用
debootstrap
在我们之前创建的目录中安装 Trusty Tahr:
$ debootstrap trusty ./trusty_chroot http://in.archive.ubuntu.com/ubuntu/
- 您将看到类似于任何 Linux 根文件系统的目录树,位于 Trusty Tahr 安装的目录内。
$ ls ./trusty_chroot
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
- 现在,我们可以使用以下命令将目录导出为 Docker 镜像:
$ tar -C trusty_chroot/ -c . | docker import - nkhare/trusty_base
- 现在,查看
docker images
输出。您应该有一个名为nkhare/trusty_base
的新镜像。
另请参阅
-
Debootstrap 维基页面
wiki.debian.org/Debootstrap
。 -
还有其他几种创建基本镜像的方法。您可以在
docs.docker.com/articles/baseimages/
找到链接。
可视化层之间的依赖关系
随着镜像数量的增加,找到它们之间的关系变得困难。有一些实用程序可以找到镜像之间的关系。
准备工作
在运行 Docker 守护程序的主机上有一个或多个 Docker 镜像。
如何做…
- 运行以下命令以获取图像的树状视图:
$ docker images -t
工作原理…
层之间的依赖关系将从 Docker 镜像的元数据中获取。
还有更多…
从--viz
到docker
images
,我们可以以图形方式看到依赖关系;要做到这一点,您需要安装graphviz
软件包:
$ docker images --viz | dot -Tpng -o /tmp/docker.png
$ display /tmp/docker.png
正如在运行上述命令时出现的警告中所述,-t
和--viz
选项可能很快就会被弃用。
另请参阅
- 以下项目尝试通过使用来自 Docker 的原始 JSON 输出来可视化 Docker 数据
github.com/justone/dockviz
第四章:容器的网络和数据管理
在本章中,我们将涵盖以下内容:
-
从外部访问容器
-
管理容器中的数据
-
连接两个或多个容器
-
通过链接容器开发 LAMP 应用程序
-
使用 Flannel 进行多主机容器的网络
-
为容器分配 IPv6 地址
介绍
到目前为止,我们已经使用单个容器并在本地访问它。但是随着我们转向更多的真实用例,我们将需要从外部世界访问容器,在容器内共享外部存储,与在其他主机上运行的容器通信等。在本章中,我们将看到如何满足其中一些要求。让我们首先了解 Docker 的默认网络设置,然后转向高级用例。
当 Docker 守护程序启动时,它会创建一个名为docker0
的虚拟以太网桥。例如,我们将在运行 Docker 守护程序的系统上使用ip addr
命令看到以下内容:
正如我们所看到的,docker0
的 IP 地址为 172.17.42.1/16。Docker 随机选择一个地址和子网,来自 RFC 1918(tools.ietf.org/html/rfc1918
)中定义的私有范围。使用这个桥接接口,容器可以彼此通信,也可以与主机系统通信。
默认情况下,每次 Docker 启动一个容器,它都会创建一对虚拟接口,其中一端连接到主机系统,另一端连接到创建的容器。让我们启动一个容器,看看会发生什么:
连接到容器的eth0
接口的一端获得了 172.17.0.1/16 的 IP 地址。我们还在主机系统上的接口的另一端看到了以下条目:
现在,让我们创建更多的容器,并使用管理以太网桥的brctl
命令查看docker0
桥接:
每个 veth*都绑定到docker0
桥接,这样就在主机和每个 Docker 容器之间创建了一个虚拟子网。除了设置docker0
桥接之外,Docker 还创建了 IPtables NAT 规则,以便所有容器默认可以与外部世界通信,但反之则不行。让我们看看 Docker 主机上的 NAT 规则:
如果我们尝试从容器连接到外部世界,我们将不得不通过默认创建的 Docker 桥:
在本章的后面,我们将看到外部世界如何连接到容器。
在启动容器时,我们有几种模式可以选择其网络:
--net=bridge
:这是我们刚刚看到的默认模式。因此,我们用来启动容器的前面的命令可以写成如下形式:
$ docker run -i -t --net=bridge centos /bin/bash
--net=host
:使用此选项,Docker 不会为容器创建网络命名空间;相反,容器将与主机进行网络堆栈。因此,我们可以使用以下选项启动容器:
$ docker run -i -t --net=host centos bash
然后我们可以在容器内运行ip addr
命令,如下所示:
我们可以看到所有连接到主机的网络设备。使用这种配置的一个例子是在容器中运行nginx
反向代理,以提供在主机上运行的 Web 应用程序。
--net=container:NAME_or_ID
:使用此选项,Docker 在启动容器时不会创建新的网络命名空间,而是从另一个容器中共享它。让我们启动第一个容器并查找其 IP 地址:
$ docker run -i -t --name=centos centos bash
现在开始另一个如下:
$ docker run -i -t --net=container:centos ubuntu bash
正如我们所看到的,两个容器包含相同的 IP 地址。
Kubernetes(kubernetes.io/
)Pod 中的容器使用此技巧来相互连接。我们将在第八章中重新讨论这个问题,Docker 编排和托管平台。
--net=none
:使用此选项,Docker 在容器内创建网络命名空间,但不配置网络。
注意
有关我们在前面部分讨论的不同网络的更多信息,请访问docs.docker.com/articles/networking/#how-docker-networks-a-container
。
从 Docker 1.2 版本开始,还可以在运行的容器上更改/etc/host
、/etc/hostname
和/etc/resolv.conf
。但是,请注意,这些只是用于运行容器。如果重新启动,我们将不得不再次进行更改。
到目前为止,我们已经在单个主机上查看了网络,但在现实世界中,我们希望连接多个主机,并且让一个主机上的容器与另一个主机上的容器进行通信。Flannel (github.com/coreos/flannel
),Weave (github.com/weaveworks/weave
),Calio (www.projectcalico.org/getting-started/docker/
)和 Socketplane (socketplane.io/
)是一些提供此功能的解决方案。在本章的后面,我们将看到如何配置 Flannel 进行多主机网络。Socketplane 于'15 年 3 月加入了 Docker Inc。
社区和 Docker 正在构建一个容器网络模型(CNM)与 libnetwork (github.com/docker/libnetwork
),它提供了一个原生的 Go 实现来连接容器。有关此开发的更多信息,请访问blog.docker.com/2015/04/docker-networking-takes-a-step-in-the-right-direction-2/
。
从外部访问容器
一旦容器启动,我们希望从外部访问它。如果您使用--net=host
选项启动容器,则可以通过 Docker 主机 IP 访问它。使用--net=none
,您可以通过公共端口或其他复杂的设置附加网络接口。让我们看看默认情况下会发生什么——从主机网络接口转发数据包到容器。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何做…
- 让我们使用
-P
选项启动一个容器:
$ docker run --expose 80 -i -d -P --name f20 fedora /bin/bash
这会自动将容器的任何网络端口映射到 Docker 主机的 49000 到 49900 之间的随机高端口。
在PORTS
部分,我们看到0.0.0.0:49159->80/tcp
,格式如下:
<Host Interface>:<Host Port> -> <Container Interface>/<protocol>
因此,如果来自 Docker 主机上任何接口的端口49159
的任何请求,请求将被转发到centos1
容器的端口80
。
我们还可以使用-p
选项将容器的特定端口映射到主机的特定端口:
$ docker run -i -d -p 5000:22 --name centos2 centos /bin/bash
在这种情况下,来自 Docker 主机上任何接口的端口5000
的所有请求都将被转发到centos2
容器的端口22
。
工作原理…
使用默认配置,Docker 设置防火墙规则,将连接从主机转发到容器,并在 Docker 主机上启用 IP 转发:
如前面的例子所示,已经设置了一个DNAT
规则,将主机上端口5000
的所有流量转发到容器的端口22
。
还有更多…
默认情况下,使用-p
选项,Docker 将所有请求转发到主机的任何接口。要绑定到特定接口,可以指定如下内容:
$ docker run -i -d -p 192.168.1.10:5000:22 --name f20 fedora /bin/bash
在这种情况下,只有来自 Docker 主机上 IP 为192.168.1.10
的接口的端口5000
的所有请求都将被转发到f20
容器的端口22
。要将容器的端口22
映射到主机的动态端口,可以运行以下命令:
$ docker run -i -d -p 192.168.1.10::22 --name f20 fedora /bin/bash
我们可以将容器上的多个端口绑定到主机上的端口,如下所示:
$ docker run -d -i -p 5000:22 -p 8080:80 --name f20 fedora /bin/bash
我们可以查找映射到容器端口的公共端口,如下所示:
$ docker port f20 80
0.0.0.0:8080
要查看容器的所有网络设置,可以运行以下命令:
$ docker inspect -f "{{ .NetworkSettings }}" f20
另请参阅
- Docker 网站上的网络文档:
docs.docker.com/articles/networking/
。
在容器中管理数据
当容器被删除时,任何未提交的数据或容器中的更改都会丢失。例如,如果您在容器中配置了 Docker 注册表并推送了一些镜像,那么一旦注册表容器被删除,所有这些镜像都会丢失,如果您没有提交它们。即使您提交了,也不是最佳做法。我们应该尽量保持容器的轻量化。以下是两种主要的管理 Docker 数据的方法:
-
数据卷:来自 Docker 文档(
docs.docker.com/userguide/dockervolumes/
),数据卷是一个专门指定的目录,位于一个或多个容器中,绕过联合文件系统,提供了几个有用的特性,用于持久或共享数据: -
在创建容器时初始化卷。如果容器的基本镜像包含指定挂载点的数据,则该数据将被复制到新卷中。
-
数据卷可以在容器之间共享和重复使用。
-
对数据卷的更改是直接进行的。
-
对数据卷的更改不会在更新镜像时包含在内。
-
数据卷会持久存在,直到没有容器在使用它们。
-
数据卷容器:由于卷会持久存在,直到没有容器在使用它,我们可以使用卷在容器之间共享持久数据。因此,我们可以创建一个命名的数据卷容器,并将数据挂载到另一个容器中。
准备工作
确保 Docker 守护程序在主机上运行,并且可以通过 Docker 客户端进行连接。
如何做...
- 添加数据卷。使用
docker run
命令的-v
选项,我们向容器添加数据卷:
$ docker run -t -d -P -v /data --name f20 fedora /bin/bash
我们可以在容器中拥有多个数据卷,可以通过多次添加-v
来创建:
$ docker run -t -d -P -v /data -v /logs --name f20 fedora /bin/bash
提示
VOLUME
指令可以在 Dockerfile 中使用,通过添加类似于VOLUME ["/data"]
的内容来添加数据卷。
我们可以使用inspect
命令查看容器的数据卷详细信息:
$ docker inspect -f "{{ .Config.Volumes }}" f20
$ docker inspect -f "{{ .Volumes }}" f20
如果容器中不存在目标目录,它将被创建。
- 接下来,我们将主机目录挂载为数据卷。我们还可以使用
-v
选项将主机目录映射到数据卷:
$ docker run -i -t -v /source_on_host:/destination_on_container fedora /bin/bash
考虑以下示例:
$ docker run -i -t -v /srv:/mnt/code fedora /bin/bash
在不同环境中测试代码、在中央位置收集日志等情况下,这可能非常有用。我们还可以按照以下方式将主机目录映射为只读模式:
$ docker run -i -t -v /srv:/mnt/code:ro fedora /bin/bash
我们还可以使用以下命令将主机的整个根文件系统挂载到容器中:
$ docker run -i -t -v /:/host:ro fedora /bin/bash
如果主机上的目录(/srv
)不存在,则将创建它,前提是您有权限创建。此外,在启用 SELinux 的 Docker 主机上,如果 Docker 守护程序配置为使用 SELinux(docker -d --selinux-enabled
),则在尝试访问挂载卷上的文件之前,如果您尝试访问挂载卷上的文件,您将看到permission denied
错误。要重新标记它们,请使用以下命令之一:
$ docker run -i -t -v /srv:/mnt/code:z fedora /bin/bash
$ docker run -i -t -v /srv:/mnt/code:Z fedora /bin/bash
请访问第九章,Docker 安全性,以获取更多详细信息。
- 现在,创建一个数据卷容器。通过卷共享主机目录到容器时,我们将容器绑定到给定的主机,这是不好的。此外,在这种情况下,存储不受 Docker 控制。因此,在我们希望数据持久化即使更新容器时,我们可以从数据卷容器获得帮助。数据卷容器用于创建卷,仅此而已;它们甚至不运行。由于创建的卷附加到容器(未运行),因此无法删除。例如,这是一个命名的数据容器:
$ docker run -d -v /data --name data fedora echo "data volume container"
这将只创建一个将映射到 Docker 管理的目录的卷。现在,其他容器可以使用--volumes-from
选项从数据容器中挂载卷,如下所示:
$ docker run -d -i -t --volumes-from data --name client1 fedora /bin/bash
我们可以从数据卷容器挂载卷到多个容器:
$ docker run -d -i -t --volumes-from data --name client2 fedora /bin/bash
我们还可以多次使用--volumes-from
来从多个容器获取数据卷。我们还可以通过从某个其他容器挂载卷的容器来创建链。
它是如何工作的...
在数据卷的情况下,当主机目录未共享时,Docker 在/var/lib/docker/
中创建一个目录,然后与其他容器共享。
还有更多...
-
使用
-v
标志删除卷以docker rm
,只有当没有其他容器在使用它时。如果其他容器正在使用卷,则容器将被删除(使用docker rm
),但卷将不会被删除。 -
在上一章中,我们看到了如何配置 Docker 注册表,默认情况下以
dev
flavor 启动。在此注册表中,上传的图像保存在我们启动的容器中的/tmp/registry
文件夹中。我们可以在注册表容器中挂载主机上的/tmp/registry
目录,因此每当我们上传图像时,它将保存在运行 Docker 注册表的主机上。因此,要启动容器,我们运行以下命令:
$ docker run -v /srv:/tmp/registry -p 5000:5000 registry
要推送图像,我们运行以下命令:
$ docker push registry-host:5000/nkhare/f20
成功推送图像后,我们可以查看我们在 Docker 注册表中挂载的目录的内容。在我们的情况下,我们应该看到以下目录结构:
/srv/
├── images
│ ├── 3f2fed40e4b0941403cd928b6b94e0fd236dfc54656c00e456747093d10157ac
│ │ ├── ancestry
│ │ ├── _checksum
│ │ ├── json
│ │ └── layer
│ ├── 511136ea3c5a64f264b78b5433614aec563103b4d4702f3ba7d4d2698e22c158
│ │ ├── ancestry
│ │ ├── _checksum
│ │ ├── json
│ │ └── layer
│ ├── 53263a18c28e1e54a8d7666cb835e9fa6a4b7b17385d46a7afe55bc5a7c1994c
│ │ ├── ancestry
│ │ ├── _checksum
│ │ ├── json
│ │ └── layer
│ └── fd241224e9cf32f33a7332346a4f2ea39c4d5087b76392c1ac5490bf2ec55b68
│ ├── ancestry
│ ├── _checksum
│ ├── json
│ └── layer
├── repositories
│ └── nkhare
│ └── f20
│ ├── _index_images
│ ├── json
│ ├── tag_latest
│ └── taglatest_json
另请参阅
-
Docker 网站上的文档位于
docs.docker.com/userguide/dockervolumes/
-
container42.com/2013/12/16/persistent-volumes-with-docker-container-as-volume-pattern/
链接两个或更多个容器
通过容器化,我们希望通过在不同的容器上运行服务,然后将它们链接在一起来创建我们的堆栈。在上一章中,我们通过将 Web 服务器和数据库放在同一个容器中来创建了一个 WordPress 容器。但是,我们也可以将它们放在不同的容器中并将它们链接在一起。容器链接在它们之间创建了一个父子关系,其中父容器可以看到其子容器的选定信息。链接依赖于容器的命名。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何操作…
- 创建一个名为
centos_server
的命名容器:
$ docker run -d -i -t --name centos_server centos /bin/bash
如何操作…
- 现在,让我们使用
--link
选项启动另一个名为 client 的容器,并将其与centos_server
容器进行链接,该选项接受name:alias
参数。然后查看/etc/hosts
文件:
$ docker run -i -t --link centos_server:server --name client fedora /bin/bash
如何操作…
工作原理…
在上面的例子中,我们使用别名 server 将centos_server
容器链接到客户端容器。通过链接这两个容器,第一个容器(在本例中为centos_server
)的条目将被添加到客户端容器的/etc/hosts
文件中。此外,在客户端中设置了一个名为SERVER_NAME
的环境变量来引用服务器。
工作原理…
还有更多…
现在,让我们创建一个mysql
容器:
$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
然后,让我们从客户端链接它并检查环境变量:
$ docker run -i -t --link mysql:mysql-server --name client fedora /bin/bash
还有更多…
还让我们看一下docker ps
的输出:
还有更多…
如果仔细观察,我们在启动client
容器时没有指定-P
或-p
选项来映射两个容器之间的端口。根据容器暴露的端口,Docker 在链接到它的容器之间创建了一个内部安全隧道。为此,Docker 在链接器容器内设置环境变量。在前面的情况下,mysql
是链接的容器,client 是链接器容器。由于mysql
容器暴露端口3306
,我们在客户端容器内看到相应的环境变量(MYSQL_SERVER_*
)。
提示
由于链接取决于容器的名称,如果要重用名称,必须删除旧容器。
另请参阅
- 在 Docker 网站上的文档,请访问
docs.docker.com/userguide/dockerlinks/
通过链接容器开发 LAMP 应用程序
让我们通过链接容器来扩展先前的食谱,创建一个 LAMP 应用程序(WordPress)。
准备工作
从 Docker 注册表中拉取 MySQL 和 WordPress 镜像:
-
对于 MySQL:
-
有关镜像,请访问
registry.hub.docker.com/_/mysql/
-
对于 Dockerfile,请访问
github.com/docker-library/docker-mysql
-
对于 WordPress:
-
对于 Dockerfile,请访问
github.com/docker-library/wordpress
如何做...
- 首先,启动一个
mysql
容器:
$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql
- 然后,启动
wordpress
容器并将其与mysql
容器链接起来:
$ docker run -d --name wordpress --link mysql:mysql -p 8080:80 wordpress
我们将 Docker 主机的8080
端口映射到容器的80
端口,因此我们可以通过访问 Docker 主机上的8080
端口和http://<DockerHost>:8080
URL 来连接 WordPress。
它是如何工作的...
在wordpress
和mysql
容器之间创建了一个链接。每当wordpress
容器收到一个数据库请求时,它将其传递给mysql
容器并获取结果。查看前面的食谱以获取更多详细信息。
使用 Flannel 进行多主机容器的网络连接
在这个教程中,我们将使用 Flannel (github.com/coreos/flannel
) 来设置多主机容器网络。Flannel 是一个通用的覆盖网络,可以作为软件定义网络(SDN)的替代方案。它是一个基于 IP 的解决方案,使用虚拟可扩展局域网(VXLAN),为运行该容器的主机分配唯一的 IP 地址。因此,在这种解决方案中,每个主机在集群中使用覆盖网络内的不同子网进行通信。Flannel 使用etcd
服务 (github.com/coreos/etcd
) 作为键值存储。
准备工作
对于这个教程,我们将需要安装了 Fedora 21 的三个虚拟机或物理机。
如何做…
-
让我们称一台机器/虚拟机为
master
,另外两台为minion1
和minion2
。根据您系统的 IP 地址,更新/etc/hosts
文件如下: -
在我们设置的所有系统上安装
etcd
、Flannel
和Docker
:
$ yum install -y etcd flannel docker
- 在
/etc/etcd/etcd.conf
文件中将ETCD_LISTEN_CLIENT_URLS
的值修改为http://master.example.com:4001
如下:
ETCD_LISTEN_CLIENT_URLS="http://master.example.com:4001"
- 在 master 中,启动
etcd
服务并检查其状态:
$ systemctl start etcd
$ systemctl enable etcd
$ systemctl status etcd
- 在 master 中,创建一个名为
flannel-config.json
的文件,内容如下:
{
"Network": "10.0.0.0/16",
"SubnetLen": 24,
"Backend": {
"Type": "vxlan",
"VNI": 1
}
}
- 使用
config
作为键将上述配置文件上传到etcd
:
$ curl -L http://master.example.com:4001/v2/keys/coreos.com/network/config -XPUT --data-urlencode value@flannel-config.json
-
在 master 中,更新
/etc/sysconfig/flanneld
文件中的FLANNEL_OPTIONS
以反映系统的接口。同时,更新FLANNEL_ETCD
以使用主机名而不是 127.0.0.1:4001 地址。 -
在 master 中,启用和启动
flanneld
服务:
$ systemctl enable flanneld
$ systemctl start flanneld
$ systemctl status flanneld
- 从 minion 系统中,检查对
etcd
的与 master 的连接:
[root@minion1 ~]# curl -L http://master.example.com:4001/v2/keys/coreos.com/network/config
-
更新两个 minion 中的
/etc/sysconfig/flanneld
文件,指向 master 中运行的etcd
服务器,并更新FLANNEL_OPTIONS
以反映 minion 主机的接口: -
在两个 minion 中启用和启动
flanneld
服务:
$ systemctl enable flanneld
$ systemctl start flanneld
$ systemctl status flanneld
- 在集群中的任何一台主机上运行以下命令:
$ curl -L http://master.example.com:4001/v2/keys/coreos.com/network/subnets | python -mjson.tool
这告诉我们网络中主机的数量以及与它们关联的子网(查看每个节点的密钥)。我们可以将子网与主机上的 MAC 地址关联起来。在每个主机上,/run/flannel/docker
和/run/flannel/subnet.env
文件中填充了子网信息。例如,在minion2
中,你会看到类似以下内容:
- 要在所有主机中重新启动 Docker 守护程序:
$ systemctl restart docker
然后,查看docker0
和flannel.1
接口的 IP 地址。在minion2
中,看起来像下面这样:
我们可以看到docker0
接口从与flannel.1
接口相同的子网中获取了 IP,该子网用于路由所有流量。
-
我们已经准备好在任何主机中生成两个容器,并且它们应该能够进行通信。让我们在
minion1
中创建一个容器并获取其 IP 地址: -
现在在
minion2
中创建另一个容器,并像下面这样 pingminion1
中运行的容器:
工作原理…
使用 Flannel,我们首先使用10.0.0.0/16
网络配置覆盖。然后,每个主机选择一个随机的/24
网络;例如,在我们的情况下,minion2
获取10.0.62.0/24
子网等等。配置完成后,主机中的容器将从所选的子网中获取 IP 地址。Flannel 封装数据包并使用 UDP 将其发送到远程主机。
此外,在安装过程中,Flannel 会在/usr/lib/systemd/system/docker.service.d/
中复制一个配置文件(flannel.conf
),Docker 使用该文件进行配置。
另请参阅
-
Flannel GitHub 上的图表可帮助您了解操作理论,网址为
github.com/coreos/flannel/blob/master/packet-01.png
-
CoreOS 网站上的文档位于
coreos.com/blog/introducing-rudder/
-
Scott Collier 在 Fedora 上设置 Flannel 的博客文章位于
www.colliernotes.com/2015/01/flannel-and-docker-on-fedora-getting.html
为容器分配 IPv6 地址
默认情况下,Docker 为容器分配 IPv4 地址。Docker 1.5 添加了一个功能来支持 IPv6 地址。
准备工作
确保 Docker 守护进程(1.5 版本及以上)正在主机上运行,并且您可以通过 Docker 客户端进行连接。
如何操作...
- 使用
--ipv6
选项启动 Docker 守护进程,我们可以在守护进程的配置文件(在 Fedora 上为/etc/sysconfig/docker
)中添加以下选项:
OPTIONS='--selinux-enabled --ipv6'
或者,如果我们以守护进程模式启动 Docker,那么可以按以下方式启动:
$ docker -d --ipv6
通过运行这些命令之一,Docker 将使用 IPv6 本地链路地址fe80::1
设置docker0
桥。
- 让我们启动容器并查找分配给它的 IP 地址:
正如我们所看到的,容器可以获得 IPv4 和本地链路 IPv6 地址。要从主机机器上 ping 容器的 IPv6 地址,请运行以下命令:
$ ping6 -I docker0 fe80::42:acff:fe11:3
要从容器中 pingdocker0
桥,请运行以下命令:
[root@c7562c38bd0f /]# ping6 -I eth0 fe80::1
工作原理...
Docker 配置docker0
桥以向容器分配 IPv6 地址,这使我们能够使用容器的 IPv6 地址。
更多信息...
默认情况下,容器将获得链路本地地址。要为它们分配全局可路由地址,可以通过--fixed-cidr-v6
选项传递 IPv6 子网选择地址,如下所示:
$ docker -d --ipv6 --fixed-cidr-v6="2001:db8:1::/64"
从这里,我们可以看到全局可路由地址(GlobalIPv6Address)现在已经设置。
另请参阅
-
有关 Docker 1.5 版本的发布说明,请访问
blog.docker.com/2015/02/docker-1-5-ipv6-support-read-only-containers-stats-named-dockerfiles-and-more/
。 -
有关 Docker 网站上的文档,请访问
docs.docker.com/v1.5/articles/networking/#ipv6
。 -
在设置 IPv6 选项之前,您可能需要删除主机上现有的
docker0
桥接。要了解如何操作,请访问docs.docker.com/v1.5/articles/networking/#customizing-docker0
。
第五章:Docker 使用案例
在本章中,我们将涵盖以下配方:
-
使用 Docker 进行测试
-
使用 Shippable 和 Red Hat OpenShift 进行 CI/CD
-
使用 Drone 进行 CI/CD
-
使用 OpenShift Origin 设置 PaaS
-
在 OpenShift v3 上构建和部署应用程序的源代码
-
将 Docker 配置为 Openstack 的 hypervisor 驱动程序
介绍
现在我们知道如何使用容器和镜像。在上一章中,我们还看到了如何链接容器并在主机和其他容器之间共享数据。我们还看到了来自一个主机的容器如何与其他主机的容器进行通信。
现在让我们看看 Docker 的不同用例。这里列举了其中的一些:
-
快速原型设计:这是我最喜欢的用例之一。一旦我们有了一个想法,使用 Docker 很容易进行原型设计。我们所需要做的就是设置容器来提供我们需要的所有后端服务,并将它们连接在一起。例如,要设置一个 LAMP 应用程序,获取 Web 和 DB 服务器并将它们链接在一起,就像我们在上一章中看到的那样。
-
协作和分发:GitHub 是协作和分发代码的最佳示例之一。同样,Docker 提供了 Dockerfile、注册表和导入/导出等功能,以与他人共享和协作。我们在之前的章节中已经涵盖了所有这些内容。
-
持续集成(CI):Martin Fowler 网站上的以下定义(
www.martinfowler.com/articles/continuousIntegration.html
)涵盖了所有内容:
"持续集成是一个软件开发实践,团队成员经常集成他们的工作,通常每个人至少每天集成一次 - 导致每天多次集成。每次集成都由自动构建(包括测试)进行验证,以尽快检测集成错误。许多团队发现这种方法大大减少了集成问题,并允许团队更快地开发一致的软件。本文是持续集成的快速概述,总结了该技术及其当前用法。"
使用其他章节的示例,我们可以使用 Docker 构建一个 CI 环境。您可以创建自己的 CI 环境,也可以从 Shippable 和 Drone 等公司获取服务。我们将在本章后面看到如何使用 Shippable 和 Drone 进行 CI 工作。Shippable 不是托管解决方案,但 Drone 是,它可以给您更好的控制。我认为在这里谈论它们两个会很有帮助:
-
持续交付(CD):CI 之后的下一步是持续交付,通过这一步,我们可以将我们的代码快速可靠地部署到我们的客户、云和其他环境中,而无需任何手动工作。在本章中,我们将看到如何通过 Shippable CI 自动部署 Red Hat OpenShift 上的应用程序。
-
平台即服务(PaaS):Docker 可以用于构建您自己的 PaaS。它可以使用 OpenShift、CoreOS、Atomic、Tsuru 等工具/平台进行部署。在本章后面,我们将看到如何使用 OpenShift Origin(
www.openshift.com/products/origin
)设置 PaaS。
使用 Docker 进行测试
在开发或 QA 期间,如果我们可以针对不同的环境检查我们的代码,将会很有帮助。例如,我们可能希望在不同版本的 Python 或不同发行版(如 Fedora、Ubuntu、CentOS 等)之间检查我们的 Python 代码。对于这个配方,我们将从 Flask 的 GitHub 存储库中挑选示例代码,这是一个用于 Python 的微框架(flask.pocoo.org/
)。我选择这个是为了保持简单,并且它也更容易用于其他配方。
对于这个配方,我们将创建图像,其中一个容器带有 Python 2.7,另一个带有 Python 3.3。然后,我们将使用一个示例 Python 测试代码来针对每个容器运行。
准备就绪
- 由于我们将使用 Flask 的 GitHub 存储库中的示例代码,让我们克隆它:
$ git clone https://github.com/mitsuhiko/flask
- 创建一个名为
Dockerfile_2.7
的文件,然后从中构建一个图像:
$ cat /tmp/ Dockerfile_2.7
FROM python:2.7
RUN pip install flask
RUN pip install pytest
WORKDIR /test
CMD ["/usr/local/bin/py.test"]
- 要构建
python2.7test
图像,请运行以下命令:
$ docker build -t python2.7test - < /tmp/Dockerfile_2.7
- 类似地,创建一个以
python:3.3
为基础图像的 Dockerfile,并构建python3.3test
图像:
$ cat /tmp/Dockerfile_3.3
FROM python:3.3
RUN pip install flask
RUN pip install pytest
WORKDIR /test
CMD ["/usr/local/bin/py.test"]
- 要构建图像,请运行以下命令:
$ docker build -t python3.3test - < /tmp/Dockerfile_3.3
确保两个图像都已创建。
如何做…
现在,使用 Docker 的卷功能,我们将挂载包含源代码和测试用例的外部目录。要使用 Python 2.7 进行测试,请执行以下操作:
- 转到包含 Flask 示例的目录:
$ cd /tmp/flask/examples/
- 启动一个带有
python2.7
测试镜像并在/test
下挂载blueprintexample
的容器:
$ docker run -d -v `pwd`/blueprintexample:/test python2.7test
- 类似地,要使用 Python 3.3 进行测试,请运行以下命令:
$ docker run -d -v `pwd`/blueprintexample:/test python3.3test
- 在启用 SELinux 的 Fedora/RHEL/CentOS 上运行上述测试时,您将收到“权限被拒绝”的错误。要解决此问题,请在容器内挂载主机目录时重新标记主机目录,如下所示:
$ docker run -d -v `pwd`/blueprintexample:/test:z python2.7test
注意
有关 SELinux 的更多详细信息,请参阅第九章,“Docker 安全性”。
工作原理...
从 Dockerfile 中可以看出,在运行py.test
二进制文件的 CMD 之前,我们将工作目录更改为/test
。在启动容器时,我们将源代码挂载到/test
。因此,一旦容器启动,它将运行py.test
二进制文件并运行测试。
还有更多...
-
在这个示例中,我们已经看到了如何使用不同版本的 Python 来测试我们的代码。同样,您可以从 Fedora、CentOS、Ubuntu 中选择不同的基本镜像,并在不同的 Linux 发行版上进行测试。
-
如果您在环境中使用 Jenkins,则可以使用其 Docker 插件动态提供从机器,运行构建并在 Docker 主机上关闭它。有关此的更多详细信息,请访问
wiki.jenkins-ci.org/display/JENKINS/Docker+Plugin
。
使用 Shippable 和 Red Hat OpenShift 进行 CI/CD
在前面的示例中,我们看到了 Docker 如何在本地开发和 QA 环境中用于测试的示例。让我们看一个端到端的示例,看看 Docker 现在如何在 CI/CD 环境中使用。在这个示例中,我们将看到如何使用 Shippable (www.shippable.com/
) 进行 CI/CD 并将其部署在 Red Hat 的 OpenShift 环境 (openshift.redhat.com
)。
Shippable 是一个 SaaS 平台,可以让您轻松地将持续集成/部署添加到您的 GitHub 和 Bitbucket(Git)存储库中,它完全建立在 Docker 上。Shippable 使用构建 minions,这些是基于 Docker 的容器,用于运行工作负载。Shippable 支持许多语言,如 Ruby、Python、Node.js、Java、Scala、PHP、Go 和 Clojure。默认的构建 minions 是 Ubuntu 12.04 LTS 和 Ubuntu 14.04。他们还添加了支持使用来自 Docker Hub 的自定义镜像作为 minions。Shippable CI 需要关于项目和构建指令的信息,这些信息存储在名为shippable.yml
的yml
文件中,您必须在源代码存储库中提供。yml
文件包含以下指令:
-
build_image
:这是用于构建的 Docker 镜像 -
language
:这将显示编程语言 -
versions
:您可以指定不同版本的语言以在单个构建指令中进行测试。 -
before_install
:这些是在运行构建之前的指令 -
script
:这是一个用于运行测试的二进制/脚本 -
after_success
:这些是构建成功后的指令;这用于在 PaaS 上执行部署,如 Heroku、Amazon Elastic Beanstalk、AWS OpsWorks、Google App Engine、Red Hat OpenShift 等。
Red Hat 的 OpenShift 是一个用于托管应用程序的 PaaS 平台。目前,它使用非基于 Docker 的容器技术来托管应用程序,但下一个版本的 OpenShift(github.com/openshift/origin
)正在基于 Kubernetes 和 Docker 构建。这告诉我们 Docker 在企业世界中被采用的速度。我们将在本章后面看到如何设置 OpenShift v3。
对于这个食谱,我们将使用在上一个食谱中使用的相同示例代码,首先在 Shippable 上进行测试,然后在 OpenShift 上部署它。
准备工作
-
在 Shippable 上创建一个帐户(
www.shippable.com/
)。 -
从
github.com/openshift/flask-example
派生 flask 示例。 -
在 OpenShift 上为派生存储库创建一个应用程序,具体步骤如下:
-
在 OpenShift 上创建一个帐户(
www.openshift.com/app/account/new
)并登录。 -
为应用程序选择Python 2.7 Cartridge。
-
更新您想要的Public URL部分。在Source Code部分,提供我们分叉存储库的 URL。对于本示例,我分别放下了
blueprint
和https://github.com/nkhare/flask-example
: -
单击Create Application创建新应用程序。创建后,您应该能够访问我们在上一步中提到的公共 URL。
创建应用程序后,OpenShift 提供了一种在进行代码更改
部分管理/更新此应用程序的源代码的方法。由于我们希望使用 Shippable 部署应用程序,因此无需遵循这些说明。
- 在本地系统上克隆分叉存储库:
$ git clone git@github.com:nkhare/flask-example.git
-
让我们使用之前使用过的相同蓝图示例。要这样做,请按照以下说明进行操作:
-
克隆 flask 存储库:
$ git clone https://github.com/mitsuhiko/flask.git
- 复制蓝图示例:
$ cp -Rv flask/examples/blueprintexample/* flask-example/wsgi/
- 更新
flask-example/wsgi/application
文件,从blueprintexample
模块导入app
模块。因此,flask-example/wsgi/application
文件中的最后一行看起来像下面这样:
from blueprintexample import app as application
- 在 flask-example 存储库的顶层添加带有以下内容的
requirements.txt
文件:
flask
pytest
- 添加带有以下内容的
shippable.yml
文件:
language: python
python:
- 2.6
- 2.7
install:
- pip install -r requirements.txt
# Make folders for the reports
before_script:
- mkdir -p shippable/testresults
- mkdir -p shippable/codecoverage
script:
- py.test
archive: true
- 提交代码并将其推送到您的分叉存储库中。
如何操作...
-
登录到 Shippable。
-
登录后,单击SYNC ACCOUNT以获取列出您的分叉存储库,如果尚未列出。查找并启用要构建和运行测试的存储库。在本例中,我选择了我的 GitHub 存储库中的
flask-example
。启用后,您应该看到类似以下内容: -
单击播放按钮并选择要构建的分支。对于本示例,我选择了 master:
如果构建成功,您将看到成功的图标。
下次在存储库中提交代码时,Shippable 将触发构建并测试代码。现在,要在 OpenShift 上执行持续部署,请按照 Shippable 网站提供的说明进行操作(docs.shippable.com/deployment/openshift/
):
-
从 Shippable 仪表板获取部署密钥(位于右侧,Repos下方):
-
将其复制到 OpenShift 的(
openshift.redhat.com/app/console/settings
) Settings | Public Keys部分如下所示: -
从 OpenShift 应用程序页面获取源代码存储库链接,它将在下一步中用作
OPNESHIFT_REPO
: -
安装部署密钥后,更新
shippable.yml
文件如下:
env:
global:
- **OPENSHIFT_REPO**=ssh://545ea4964382ec337f000009@blueprint-neependra.rhcloud.com/~/git/blueprint.git
language: python
python:
- 2.6
- 2.7
install:
- pip install -r requirements.txt
# Make folders for the reports
before_script:
- mkdir -p shippable/testresults
- mkdir -p shippable/codecoverage
- git remote -v | grep ^openshift || git remote add openshift $OPENSHIFT_REPO
- cd wsgi
script:
- py.test
after_success:
- git push -f openshift $BRANCH:master
archive: true
OPENSHIFT_REPO
应该反映您使用 OpenShift 部署的应用程序。它将与此示例中显示的内容不同。
-
现在提交这些更改并将其推送到 GitHub。您将看到 Shippable 触发的构建以及在 OpenShift 上部署的新应用程序。
-
访问您的应用主页,您应该看到其更新的内容。
工作原理…
在每个构建指令中,Shippable 会根据shippable.yml
文件中指定的镜像和语言类型,启动新的容器并运行构建以执行测试。在我们的情况下,Shippable 将启动两个容器,一个用于 Python 2.6,另一个用于 Python 2.7。当您在 GitHub 上注册时,Shippable 会向您的存储库添加一个 webhook,如下所示:
因此,每次对 GitHub 进行提交更改时,Shippable 上的构建都会被触发,并在成功后部署到 OpenShift 上。
另请参阅
- Shippable 网站上提供了详细的文档,网址为
docs.shippable.com/
。
使用 Drone 进行 CI/CD
如 Drone 网站(drone.io/
)上所述,Drone 是一个托管的持续集成服务。它使您能够方便地设置项目,以便在对代码进行更改时自动构建、测试和部署。他们提供了他们平台的开源版本,您可以在您的环境或云上进行托管。截至目前,他们支持诸如 C/C++、Dart、Go、Haskell、Groovy、Java、Node.js、PHP、Python、Ruby 和 Scala 等语言。使用 Drone,您可以将应用程序部署到 Heroku、Dotcloud、Google App Engine 和 S3 等平台。您还可以通过 SSH(rsync)将代码同步到远程服务器进行部署。
对于这个示例,让我们使用之前示例中使用的相同示例。
准备工作
-
登录到 Drone(
drone.io/
)。 -
单击新项目并设置存储库。在我们的情况下,我们将选择在之前的示例中使用的 GitHub 上的相同存储库(
github.com/nkhare/flask-example
): -
一旦选择了,它会要求您为所选的存储库选择编程语言。在这种情况下,我选择了 Python。
-
然后它会提示您设置构建脚本。对于这个教程,我们将输入以下内容并保存:
pip install -r requirements.txt --use-mirrors
cd wsgi
py.test
操作步骤如下…
- 通过单击立即构建来触发手动构建,如下面的屏幕截图所示:
它是如何工作的…
构建过程启动一个新的容器,克隆源代码存储库,并在其中运行我们在命令部分中指定的命令(运行测试用例)。
还有更多…
-
构建完成后,您可以查看控制台输出。
-
Drone 还在 GitHub 中添加了一个 Webhook;因此,下次您提交存储库中的更改时,将触发构建。
-
Drone 还支持将应用程序持续部署到不同的云环境,就像我们在之前的教程中看到的那样。要设置这个,转到设置选项卡,选择部署,然后选择添加新的部署。选择您的云提供商并设置它:
另请参阅
-
Drone 的文档网址为
docs.drone.io/
-
配置自托管的 Drone 环境的步骤,目前处于 alpha 阶段,网址为
github.com/drone/drone
使用 OpenShift Origin 设置 PaaS
平台即服务是一种云服务类型,其中消费者控制应用程序(主要是 Web 应用程序)的软件部署和配置设置,提供者提供服务器、网络和其他服务来管理这些部署。提供者可以是外部的(公共提供者)或内部的(组织中的 IT 部门)。有许多 PaaS 提供者,例如 Amazon (aws.amazon.com/
)、Heroku (www.heroku.com/
)、OpenShift (www.openshift.com/
)等。最近,容器似乎已成为应用程序部署的自然选择。
在本章的前面,我们看了如何使用 Shippable 和 OpenShift 构建 CI/CD 解决方案,我们将我们的应用程序部署到 OpenShift PaaS。我们在 Openshift Online 上部署了我们的应用程序,这是公共云服务。在撰写本书时,OpenShift 公共云服务使用非 Docker 容器技术将应用程序部署到公共云服务。OpenShift 团队一直在开发 OpenShift v3 (github.com/openshift/origin
),这是一个利用 Docker 和 Kubernetes (kubernetes.io
)等技术的 PaaS,为您的云启用应用程序提供了一个完整的生态系统。他们计划在今年晚些时候将其移至公共云服务。正如我们在第八章,“Docker 编排和托管平台”中所讨论的,强烈建议在继续本教程之前先阅读该章节。我将借用该章节中的一些概念。
blog.openshift.com/openshift-v3-deep-dive-docker-kubernetes/
Kubernetes 提供了容器集群管理的功能,如调度 pod 和服务发现,但它没有完整应用程序的概念,也没有从源代码构建和部署 Docker 镜像的能力。OpenShift v3 扩展了基本的 Kubernetes 模型,并填补了这些空白。如果我们快进并查看第八章,“Docker 编排和托管平台”,对于 Kubernetes 部分,您会注意到要部署一个应用程序,我们需要定义 Pods、Services 和 Replication-Controllers。OpenShift v3 试图将所有这些信息抽象出来,并让您定义一个配置文件,该文件负责处理所有内部连接。此外,OpenShift v3 还提供其他功能,如通过源代码推送进行自动部署,集中管理和管理应用程序,身份验证,团队和项目隔离,以及资源跟踪和限制,所有这些都是企业部署所需的。
在这个示例中,我们将在 VM 上设置全功能的 OpenShift v3 Origin 并启动一个 pod。在下一个示例中,我们将看到如何使用源到镜像(STI)构建功能通过源代码构建和部署应用程序。由于 OpenShift v3 Origin 正在进行积极的开发,我从源代码中选择了一个标签,并在这个示例和下一个示例中使用了该代码库。在更新的版本中,命令行选项可能会发生变化。有了这些信息,您应该能够适应最新的发布版本。最新的示例可以在github.com/openshift/origin/tree/master/examples/hello-openshift
找到。
准备就绪
设置 Vagrant (www.vagrantup.com/
)并安装 VirtualBox 提供程序(www.virtualbox.org/
)。如何设置这些内容的说明超出了本书的范围。
- 克隆 OpenShift Origin 存储库:
$ git clone https://github.com/openshift/origin.git
- 检出
v0.4.3
标签:
$ cd origin
$ git checkout tags/v0.4.3
- 启动虚拟机:
$ vagrant up --provider=virtualbox
- 登录到容器:
$ vagrant ssh
如何做...
- 构建 OpenShift 二进制文件:
$ cd /data/src/github.com/openshift/origin
$ make clean build
- 转到
hello-openshift
示例:
$ cd /data/src/github.com/openshift/origin/examples/hello-openshift
- 在一个守护进程中启动所有 OpenShift 服务:
$ mkdir logs
$ sudo /data/src/github.com/openshift/origin/_output/local/go/bin/openshift start --public-master=localhost &> logs/openshift.log &
- OpenShift 服务由 TLS 保护。我们的客户端需要接受服务器证书并呈现自己的客户端证书。这些证书是作为 Openshift 启动的一部分在当前工作目录中生成的。
$ export OPENSHIFTCONFIG=`pwd`/openshift.local.certificates/admin/.kubeconfig
$ export CURL_CA_BUNDLE=`pwd`/openshift.local.certificates/ca/cert.crt
$ sudo chmod a+rwX "$OPENSHIFTCONFIG"
- 根据
hello-pod.json
定义创建 pod:
$ osc create -f hello-pod.json
- 连接到 pod:
$ curl localhost:6061
它是如何工作的...
当 OpenShift 启动时,所有 Kubernetes 服务也会启动。然后,我们通过 CLI 连接到 OpenShift 主服务器,并请求它启动一个 pod。该请求然后转发到 Kubernetes,Kubernetes 启动了 pod。在 pod 配置文件中,我们提到将主机机器的端口6061
映射到 pod 的端口8080
。因此,当我们在端口6061
上查询主机时,我们从 pod 得到了回复。
还有更多...
如果运行docker ps
命令,将看到相应的容器正在运行。
另请参阅
-
在
github.com/openshift/origin
上的了解更多部分 -
在
blog.openshift.com/openshift-3-beta-3-training-commons-briefing-12/
上查看 OpenShift 3 beta 3 视频教程 -
最新的 OpenShift 培训在
github.com/openshift/training
-
OpenShift v3 文档位于
docs.openshift.org/latest/welcome/index.html
从源代码构建和部署应用程序到 OpenShift v3
OpenShift v3 提供了从源代码构建镜像的构建过程。以下是可以遵循的构建策略:
-
Docker 构建:在这种情况下,用户将提供 Docker 上下文(Dockerfiles 和支持文件),用于构建镜像。OpenShift 只需触发
docker build
命令来创建镜像。 -
源到镜像(STI)构建:在这种情况下,开发人员定义源代码仓库和构建器镜像,后者定义了用于创建应用程序的环境。然后 STI 使用给定的源代码和构建器镜像为应用程序创建一个新的镜像。有关 STI 的更多详细信息,请参见
github.com/openshift/source-to-image
。 -
自定义构建:这类似于 Docker 构建策略,但用户可以自定义将用于构建执行的构建器镜像。
在这个教程中,我们将看一下 STI 构建过程。我们将查看 OpenShift v3 Origin 仓库中的 sample-app(github.com/openshift/origin/tree/v0.4.3/examples/sample-app
)。相应的 STI 构建文件位于github.com/openshift/origin/blob/v0.4.3/examples/sample-app/application-template-stibuild.json
。
在BuildConfig
部分,我们可以看到源指向 GitHub 仓库(git://github.com/openshift/ruby-hello-world.git
),而strategy
部分下的镜像指向openshift/ruby-20-centos7
镜像。因此,我们将使用openshift/ruby-20-centos7
镜像,并使用来自 GitHub 仓库的源代码构建一个新的镜像。构建后的新镜像将根据设置被推送到本地或第三方 Docker 注册表。BuildConfig
部分还定义了何时触发新构建的触发器,例如,当构建镜像发生变化时。
在同一个 STI 构建文件(application-template-stibuild.json
)中,您会找到多个DeploymentConfig
部分,每个 pod 一个。DeploymentConfig
部分包含诸如导出端口、副本、pod 的环境变量和其他信息等信息。简单来说,您可以将DeploymentConfig
视为 Kubernetes 的扩展复制控制器。它还有触发器来触发新的部署。每次创建新的部署时,DeploymentConfig
的latestVersion
字段会递增。还会向DeploymentConfig
添加deploymentCause
,描述导致最新部署的更改。
ImageRepository
,最近更名为ImageStream
,是一组相关图像。BuildConfig
和DeploymentConfig
监视ImageStream
以查找图像更改并根据各自的触发器做出相应反应。
在 STI 构建文件中,您还会找到用于 pod 的服务(数据库和前端)、用于前端服务的路由,通过该路由可以访问应用程序,以及一个模板。模板描述了一组预期一起使用的资源,可以进行自定义和处理以生成配置。每个模板可以定义一组参数,这些参数可以被容器修改后使用。
与 STI 构建类似,在同一个 sample-app 示例文件夹中有 Docker 和自定义构建的示例。我假设您已经有了之前的配方,所以我们将从那里继续。
准备工作
您应该已经完成了之前的配方,使用 OpenShift Origin 设置 PaaS。
您当前的工作目录应该是 Vagrant 启动的 VM 内的/data/src/github.com/openshift/origin /examples/hello-openshift
。
如何操作...
- 部署一个私有的 Docker 注册表来托管 STI 构建过程中创建的镜像:
$ sudo openshift ex registry --create --credentials=./openshift.local.certificates/openshift-registry/.kubeconfig
- 确认注册表已启动(这可能需要几分钟):
$ osc describe service docker-registry
- 在 OpenShift 中创建一个新项目。这将创建一个命名空间
test
来包含构建和稍后我们将生成的应用程序:
$ openshift ex new-project test --display-name="OpenShift 3 Sample" --description="This is an example project to demonstrate OpenShift v3" --admin=test-admin
- 使用
test-admin
用户登录并切换到test
项目,从现在开始每个命令都将使用该项目:
$ osc login -u test-admin -p pass
$ osc project test
- 提交应用程序模板进行处理(生成模板中请求的共享参数),然后请求创建处理后的模板:
$ osc process -f application-template-stibuild.json | osc create -f -
- 这不会触发构建。要启动应用程序的构建,请运行以下命令:
$ osc start-build ruby-sample-build
- 监视构建并等待状态变为
complete
(这可能需要几分钟):
$ osc get builds
- 获取服务列表:
$ osc get services
它是如何工作的...
在BuildConfig
(ruby-sample-build
)部分,我们将源指定为ruby-hello-world
Git 存储库(git://github.com/openshift/ruby-hello-world.git
),我们的镜像为openshift/ruby-20-centos7
。因此,构建过程将使用该镜像,并使用 STI 构建器,在openshift/ruby-20-centos7
上构建我们的源后创建一个名为origin-ruby-sample
的新镜像。然后将新镜像推送到我们之前创建的 Docker 注册表中。
使用DeploymentConfig
,前端和后端 pod 也被部署并链接到相应的服务。
还有更多...
- 前面的前端服务可以通过服务 IP 和相应的端口访问,但无法从外部访问。为了使其可访问,我们给我们的应用程序一个 FQDN;例如,在以下示例中,它被定义为
www.example.com
:
OpenShift v3 提供了一个 HAProxy 路由器,可以将 FQDN 映射到相应的 pod。有关更多信息,请访问docs.openshift.org/latest/architecture/core_objects/routing.html
。您还需要在外部 DNS 中添加一个条目来解析此处提供的 FQDN。
- OpenShift v3 Origin 也是一个管理 GUI。要在 GUI 上查看我们部署的应用程序,请将用户名
test-admin
绑定到默认命名空间中的查看角色,以便您可以在 Web 控制台中观察进展:
$ openshift ex policy add-role-to-user view test-admin
然后,通过浏览器,连接到https://<host>:8443/console
,并通过test-admin
用户登录,输入任何密码。由于 Vagrant 将主机机器上端口8443
的流量转发到 VM,您应该能够通过运行 VM 的主机连接。然后选择OpenShift 3 Sample作为项目并进行探索。
- 在多节点设置中,您的 pod 可以安排在不同的系统上。OpenShift v3 通过覆盖网络 pod 连接 pod,运行在一个节点上的 pod 可以访问另一个节点上的 pod。它被称为
openshift-sdn
。有关更多详细信息,请访问github.com/openshift/openshift-sdn
。
另请参阅
-
在
github.com/openshift/origin
的了解更多部分 -
在
blog.openshift.com/openshift-3-beta-3-training-commons-briefing-12/
上有 OpenShift 3 beta 3 视频教程 -
最新的 OpenShift 培训在
github.com/openshift/training
-
在
docs.openshift.org/latest/welcome/index.html
上有 OpenShift v3 文档
将 Docker 配置为 OpenStack 的虚拟化程序驱动程序
我假设读者对 OpenStack 有一定了解,因为本书不涵盖这方面的内容。有关 OpenStack 及其组件的更多信息,请访问www.openstack.org/software/
。
在 OpenStack 中,Nova 支持不同的计算虚拟化程序,如 KVM、XEN、VMware、HyperV 等。我们可以使用这些驱动程序来创建虚拟机。使用 Ironic(wiki.openstack.org/wiki/Ironic
),您也可以创建裸金属服务器。Nova 在 Havana(www.openstack.org/software/havana/
)版本中添加了对 Docker 的容器创建支持,但目前它不在主线中,以加快开发周期。未来计划将其合并到主线中。在底层,它看起来像这样:
wiki.openstack.org/wiki/File:Docker-under-the-hood.png
DevStack(docs.openstack.org/developer/devstack/overview.html
)是一组脚本,用于快速创建 OpenStack 开发环境。它不是通用安装程序,但是非常容易开始使用 OpenStack 的方法。在本教程中,我们将在 Fedora21 上将 DevStack 的环境配置为使用 Docker 作为 Nova 驱动程序。
准备工作
-
在系统上安装 Docker。
-
克隆
nova-docker
和devstack
:
$ git clone https://git.openstack.org/stackforge/nova-docker /opt/stack/nova-docker
$ git clone https://git.openstack.org/openstack-dev/devstack /opt/stack/devstack
- 在我们可以使用
configure_nova_hypervisor_rootwrap
之前需要以下步骤:
$ git clone https://git.openstack.org/openstack/nova /opt/stack/nova
- 准备安装 Devstack:
$ cd /opt/stack/nova-docker
$ ./contrib/devstack/prepare_devstack.sh
- 创建 stack 用户并将其添加到
sudo
:
$ /opt/stack/devstack/tools/create-stack-user.sh
- 使用 Python 安装
docker-py
以与 docker 进行通信:
$ yum install python-pip
$ pip install docker-py
如何做…
- 完成先决条件步骤后,运行以下命令安装 Devstack:
$ cd /opt/stack/devstack
$ ./stack.sh
它是如何工作的...
prepare_devstack.sh
驱动程序在localrc
文件中进行以下条目的设置,以设置 Nova 驱动程序的正确环境:
export VIRT_DRIVER=docker
export DEFAULT_IMAGE_NAME=cirros
export NON_STANDARD_REQS=1
export IMAGE_URLS=" "
-
运行
stackrc
文件后,我们可以看到关于 Nova 和 Glance 的以下更改: -
/etc/nova/nova.conf
文件更改了计算驱动程序:
[DEFAULT]
compute_driver = novadocker.virt.docker.DockerDriver
/etc/nova/rootwrap.d/docker.filters
文件更新为以下内容:
[Filters]
# nova/virt/docker/driver.py: 'ln', '-sf', '/var/run/netns/.*'
ln: CommandFilter, /bin/ln, root
- 在
/etc/glance/glance-api.conf
中,在容器/镜像格式中添加docker
:
[DEFAULT]
container_formats = ami,ari,aki,bare,ovf,docker
还有更多...
- 在
localrc
中,我们将cirros
作为默认镜像,因此一旦设置完成,我们可以看到已下载cirros
的 Docker 镜像:
这将自动导入到 Glance 中。
从前面的截图中,我们可以看到容器格式是 Docker。
-
现在您可以使用 Horizon 创建一个使用
cirros
镜像的实例,或者从命令行创建一个实例,并查看使用 Docker 命令行启动的容器。 -
要将任何镜像导入 Glance,可以执行以下操作:
-
从 Docker Hub 拉取所需的镜像:
$ docker pull fedora
- 导入镜像(目前只有管理员可以导入镜像):
$ source openrc
$ export OS_USERNAME=admin
$ sudo docker save fedora | glance image-create --is-public=True --container-format=docker --disk-format=raw --name fedora
-
Cinder 和 Neutron 的集成不足,但情况正在迅速改善。
-
在安装过程中,如果出现
AttributeError: 'module' object has no attribute 'PY2'
错误,则运行以下命令进行修复:
$ pip uninstall six
$ pip install --upgrade six
另请参阅
-
在 OpenStack 网站上的文档
wiki.openstack.org/wiki/Docker
。 -
Docker 也是 OpenStack Heat 的资源类型之一。在
docs.openstack.org/developer/heat/template_guide/contrib.html#dockerinc-resource
了解更多信息。 -
OpenStack 中有一个有趣的项目叫做 Kolla,它专注于通过 Docker 容器部署 OpenStack 服务。在
github.com/stackforge/kolla/
了解更多信息。
第六章:Docker API 和语言绑定
在本章中,我们将涵盖以下内容:
-
配置 Docker 守护程序远程 API
-
使用远程 API 执行图像操作
-
使用远程 API 执行容器操作
-
探索 Docker 远程 API 客户端库
-
保护 Docker 守护程序远程 API
介绍
在之前的章节中,我们学习了不同的命令来管理图像、容器等。尽管我们通过命令行运行所有命令,但 Docker 客户端(CLI)与 Docker 守护程序之间的通信是通过 API 进行的,这被称为 Docker 守护程序远程 API。
Docker 还提供了用于与 Docker Hub 和 Docker 注册表通信的 API,Docker 客户端也使用这些 API。除了这些 API 之外,我们还有不同编程语言的 Docker 绑定。因此,如果您想为 Docker 图像、容器管理等构建一个漂亮的 GUI,了解前面提到的 API 将是一个很好的起点。
在本章中,我们将研究 Docker 守护程序远程 API,并使用curl
命令(curl.haxx.se/docs/manpage.html
)与不同 API 的端点进行通信,这将类似于以下命令:
$ curl -X <REQUEST> -H <HEADER> <OPTION> <ENDPOINT>
前面的请求将返回一个返回代码和与我们选择的端点和请求相对应的输出。GET
、PUT
和DELETE
是不同类型的请求,如果没有指定,默认请求是 GET。每个 API 端点对于返回代码都有自己的解释。
配置 Docker 守护程序远程 API
正如我们所知,Docker 具有客户端-服务器架构。当我们安装 Docker 时,用户空间程序和守护程序从同一个二进制文件启动。守护程序默认绑定到同一主机上的unix://var/run/docker.sock
。这将不允许我们远程访问守护程序。为了允许远程访问,我们需要以允许远程访问的方式启动 Docker,这可以通过适当地更改-H
标志来实现。
准备工作
根据您正在运行的 Linux 发行版,找出需要更改的 Docker 守护程序配置文件。对于 Fedora、Red Hat 发行版,它可能是/etc/sysconfig/docker
,对于 Ubuntu/Debian 发行版,它可能是/etc/default/docker
。
操作步骤…
- 在 Fedora 20 系统上,在配置文件(/etc/sysconfig/docker)中添加
-H tcp://0.0.0.0:2375
选项,如下所示:
OPTIONS=--selinux-enabled -H tcp://0.0.0.0:2375
- 重新启动 Docker 服务。在 Fedora 上,运行以下命令:
$ sudo systemctl restart docker
- 从远程客户端连接到 Docker 主机:
$ docker -H <Docker Host>:2375 info
确保防火墙允许在安装了 Docker 守护程序的系统上访问端口2375
。
它是如何工作的…
通过前述命令,我们允许 Docker 守护程序通过 TCP 在所有网络接口上监听端口2375
。
还有更多…
-
在前面提到的客户端和 Docker 之间的通信中,主机是不安全的。在本章的后面,我们将看到如何在它们之间启用 TLS。
-
Docker CLI 查找环境变量;如果被设置了,那么 CLI 将使用该端点进行连接,例如,如果我们设置如下:
$ export DOCKER_HOST=tcp://dockerhost.example.com:2375
然后,在该会话中,未来的 docker 命令默认连接到远程 Docker 主机并运行此命令:
$ docker info
另请参阅
- Docker 网站上的文档
docs.docker.com/reference/api/docker_remote_api/
使用远程 API 执行图像操作
在启用了 Docker 守护程序远程 API 之后,我们可以通过客户端进行所有与图像相关的操作。为了更好地理解 API,让我们使用curl
连接到远程守护程序并进行一些与图像相关的操作。
准备工作
配置 Docker 守护程序并允许远程访问,如前面的配方中所解释的。
如何做…
在这个配方中,我们将看一下一些图像操作,如下所示:
- 要列出图像,请使用以下 API:
GET /images/json
以下是前述语法的一个例子:
$ curl http://dockerhost.example.com:2375/images/json
- 要创建图像,请使用以下 API:
POST /images/create
以下是一些示例:
- 从 Docker Hub 获取 Fedora 图像:
$ curl -X POST
http://dockerhost.example.com:2375/images/create?fromImage=fedora
- 获取带有
latest
标签的 WordPress 图像:
$ curl -X POST
http://dockerhost.example.com:2375/images/create?fromImage=wordpress&tag=latest
- 从可访问的 Web 服务器上的
tar
文件创建图像:
$ curl -X POST
http://dockerhost.example.com:2375/images/create?fromSrc=http://localhost/image.tar
- 要构建图像,请使用以下 API:
POST /commit
以下是一些示例:
- 从容器(
container id = 704a7c71f77d
)构建图像
$ curl -X POST
http://dockerhost.example.com:2375/commit?container=704a7c71f77d
- 从 Docker 文件构建图像:
$ curl -X POST -H "Content-type:application/tar" --data-binary '@/tmp/Dockerfile.tar.gz'
http://dockerhost.example.com:2375/build?t=apache
由于 API 期望内容为tar
文件,我们需要将 Docker 文件放入 tar 中并调用 API。
- 要删除图像,请使用以下 API:
DELETE /images/<name>
以下是前述语法的一个例子:
$ curl -X DELETE
http://dockerhost.example.com:2375/images/wordpress:3.9.1
它是如何工作的…
在前面提到的所有情况下,API 将连接到 Docker 守护程序并执行请求的操作。
还有更多…
我们还没有涵盖之前讨论的 API 的所有选项,Docker 为其他与镜像相关的操作提供了 API。访问 API 文档以获取更多详细信息。
另请参阅
- 每个 API 端点可以有不同的输入来控制操作。更多详细信息,请访问 Docker 网站上的文档
docs.docker.com/reference/api/docker_remote_api_v1.18/#22-images
。
使用远程 API 执行容器操作
与我们使用 API 执行镜像操作类似,我们也可以使用 API 执行所有与容器相关的操作。
准备工作
配置 Docker 守护程序并允许远程访问,如前面的示例所述。
如何做…
在这个示例中,我们将看一些容器操作:
- 要列出容器,请使用以下 API:
GET /containers/json
以下是一些示例:
- 获取所有正在运行的容器:
$ curl -X GET http://shadowfax.example.com:2375/containers/json
- 获取所有正在运行的容器,包括已停止的容器
$ curl -X GET http://shadowfax.example.com:2375/containers/json?all=True
- 要创建一个新的容器,请使用以下 API:
POST /containers/create
以下是一些示例
- 从
fedora
镜像创建一个容器:
$ curl -X POST -H "Content-type:application/json" -d '{"Image": "fedora", "Cmd": ["ls"] }' http://dockerhost.example.com:2375/containers/create
- 从
fedora
镜像创建一个名为f21
的容器:
$ curl -X POST -H "Content-type:application/json" -d '{"Image": "fedora", "Cmd": ["ls"] }' http://dockerhost.example.com:2375/containers/create?name=f21
- 要启动一个容器,请使用以下 API:
POST /containers/<id>/start
例如,启动 ID 为591ab8ac2650
的容器:
$ curl -X POST -H "Content-type:application/json" -d '{"Dns": ["4.2.2.1"] }' http://dockerhost.example.com:2375/containers/591ab8ac2650/start
请注意,当启动已停止的容器时,我们还传递了 DNS 选项,这将改变容器的 DNS 配置。
- 要检查一个容器,请使用以下 API:
GET /containers/<id>/json
例如,检查 ID 为591ab8ac2650
的容器:
$ curl -X GET http://dockerhost.example.com:2375/containers/591ab8ac2650/json
- 要获取正在容器内运行的进程列表,请使用以下 API:
GET /containers/<id>/top
例如,获取 ID 为591ab8ac2650
的容器中正在运行的进程:
$ curl -X GET http://dockerhost.example.com:2375/containers/591ab8ac2650/top
- 要停止一个容器,请使用以下 API:
POST /containers/<id>/stop
例如,停止 ID 为591ab8ac2650
的容器:
$ curl -X POST http://dockerhost.example.com:2375/containers/591ab8ac2650/stop
它是如何工作的…
我们还没有涵盖之前讨论的 API 的所有选项,Docker 为其他与容器相关的操作提供了 API。访问 API 文档以获取更多详细信息。
另请参阅
探索 Docker 远程 API 客户端库
在最近的几个示例中,我们探索了 Docker 提供的 API,以连接并对远程 Docker 守护程序执行操作。Docker 社区已经为不同的编程语言添加了绑定,以访问这些 API。其中一些列在docs.docker.com/reference/api/remote_api_client_libraries/
上。
请注意,Docker 维护人员不维护这些库。让我们通过一些示例来探索 Python 绑定,并看看它如何使用 Docker 远程 API。
准备就绪
- 在 Fedora 上安装
docker-py
:
$ sudo yum install python-docker-py
或者,使用pip
来安装该软件包:
$ sudo pip install docker-py
- 导入模块:
$ python
>>> import docker
如何做…
-
创建客户端,使用以下步骤:
-
通过 Unix 套接字连接:
>>> client = docker.Client(base_url='unix://var/run/docker.sock', version='1.18', timeout=10)
- 通过 HTTP 连接:
>>> client = docker.Client(base_url='http://dockerhost.example.com:2375', version='1.18', timeout=10)
在这里,base_url
是要连接的端点,version
是客户端将使用的 API 版本,timeout
是以秒为单位的超时值。
- 使用以下代码搜索图像:
>>> client.search ("fedora")
- 使用以下代码拉取图像:
>>> client.pull("fedora", tag="latest")
- 使用以下代码启动容器:
>>> client.create_container("fedora", command="ls", hostname=None, user=None, detach=False, stdin_open=False, tty=False, mem_limit=0, ports=None, environment=None, dns=None, volumes=None, volumes_from=None,network_disabled=False, name=None, entrypoint=None, cpu_shares=None, working_dir=None,memswap_limit=0)
它是如何工作的…
在所有前面的情况下,Docker Python 模块将使用 Docker 提供的 API 向端点发送 RESTful 请求。查看docker-py
中提供的search
、pull
和start
等方法的以下代码,该代码位于github.com/docker/docker-py/blob/master/docker/client.py
。
还有更多…
您可以探索为 Docker 编写的不同用户界面。其中一些如下所示:
-
Shipyard(
shipyard-project.com/
)—使用 Python 编写 -
DockerUI(
github.com/crosbymichael/dockerui
)—使用 AngularJS 编写的 JavaScript
保护 Docker 守护程序远程 API
在本章的前面,我们看到了如何配置 Docker 守护程序以接受远程连接。但是,使用我们遵循的方法,任何人都可以连接到我们的 Docker 守护程序。我们可以使用传输层安全性(en.wikipedia.org/wiki/Transport_Layer_Security
)来保护我们的连接。
我们可以通过使用现有的证书颁发机构(CA)或创建我们自己来配置 TLS。为简单起见,我们将创建自己的证书颁发机构,这在生产中不推荐。在本例中,我们假设运行 Docker 守护程序的主机是dockerhost.example.com
。
准备就绪
确保您已安装openssl
库。
操作步骤...
- 在您的主机上创建一个目录,放置我们的 CA 和其他相关文件:
$ mkdirc-p /etc/docker
$ cd /etc/docker
- 创建 CA 私钥和公钥:
$ openssl genrsa -aes256 -out ca-key.pem 2048
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
- 现在,让我们创建服务器密钥和证书签名请求。确保
通用名称
与 Docker 守护程序系统的主机名匹配。在我们的情况下,它是dockerhost.example.com
。
$ openssl genrsa -out server-key.pem 2048
$ openssl req -subj "/CN=dockerhost.example.com" -new -key server-key.pem -out server.csr
- 为了允许来自 127.0.0.1 和特定主机(例如 10.70.1.67)的连接,创建一个扩展配置文件并使用我们的 CA 签署公钥:
$ echo subjectAltName = IP:10.70.1.67,IP:127.0.0.1 > extfile.cnf
$ openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf
- 对于客户端认证,创建一个客户端密钥和证书签名请求:
$ openssl genrsa -out key.pem 2048
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr
- 为了使密钥适用于客户端认证,创建一个扩展配置文件并签署公钥:
$ echo extendedKeyUsage = clientAuth > extfile_client.cnf
$ openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out cert.pem -extfile_client.cnf
- 生成
cert.pem
和server-cert.pem
后,我们可以安全地删除证书签名请求:
$ rm -rf client.csr server.csr
- 为了加强安全性并保护密钥免受意外损坏,让我们更改权限:
$ chmod -v 0600 ca-key.pem key.pem server-key.pem ca.pem server-cert.pem cert.pem
- 如果守护程序正在
dockerhost.example.com
上运行,请停止它。然后,从/etc/docker
手动启动 Docker 守护程序:
$ pwd
/etc/docker
$ docker -d --tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H=0.0.0.0:2376
- 从另一个终端,转到
/etc/docker
。运行以下命令连接到 Docker 守护程序:
$ cd /etc/docker
$ docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem -H=127.0.0.1:2376 version
您将看到建立了 TLS 连接,并且可以在其上运行命令。您还可以将 CA 公钥和客户端的 TLS 证书和密钥放在用户的主目录中的.docker
文件夹中,并使用DOCKER_HOST
和DOCKER_TLS_VERIFY
环境变量来默认进行安全连接。
- 要从我们在签署服务器密钥时提到的远程主机连接,我们需要将 CA 公钥和客户端的 TLS 证书和密钥复制到远程机器,然后按照前面的截图连接到 Docker 主机。
工作原理…
我们为 Docker 守护程序和客户端建立了 TLS 连接,以进行安全通信。
还有更多…
- 要设置 Docker 守护程序默认启动 TLS 配置,我们需要更新 Docker 配置文件。例如,在 Fedora 上,您可以在
/etc/sysconfig/docker
中更新OPTIONS
参数如下:
OPTIONS='--selinux-enabled -H tcp://0.0.0.0:2376 --tlsverify --tlscacert=/etc/docker/ca.pem --tlscert=/etc/docker/server-cert.pem --tlskey=/etc/docker/server-key.pem'
- 如果你还记得,在第一章中,介绍和安装,我们看到了如何使用 Docker Machine(
docs.docker.com/machine/
)来设置 Docker 主机,并且在这个设置过程中,TLS 设置发生在运行 Docker 守护程序的主机和客户端之间。在使用 Docker Machine 配置 Docker 主机后,检查客户端系统上的.docker/machine
用户。
第七章:Docker 性能
在本章中,我们将涵盖以下配方:
-
基准测试 CPU 性能
-
基准测试磁盘性能
-
基准测试网络性能
-
使用统计功能获取容器资源使用情况
-
设置性能监控
介绍
在第三章中,使用 Docker 镜像,我们看到了 Dockerfile 如何用于创建由不同服务/软件组成的镜像,稍后在第四章中,容器的网络和数据管理,我们看到了一个 Docker 容器如何与外部世界进行数据和网络交流。在第五章中,Docker 使用案例,我们研究了 Docker 的不同使用案例,在第六章中,Docker API 和语言绑定,我们看到了如何使用远程 API 连接到远程 Docker 主机。
易用性都很好,但在投入生产之前,性能是考虑的关键因素之一。在本章中,我们将看到 Docker 的性能影响特性以及我们可以遵循的基准测试不同子系统的方法。在进行性能评估时,我们需要将 Docker 性能与以下进行比较:
-
裸金属
-
虚拟机
-
Docker 在虚拟机内运行
在本章中,我们将探讨进行性能评估的方法,而不是从运行中收集的性能数据进行比较。但是,我会指出不同公司进行的性能比较,供您参考。
让我们首先看一些影响 Docker 性能的特性:
-
卷:在放置任何企业级工作负载时,您希望相应地调整底层存储。您不应该使用容器使用的主/根文件系统来存储数据。Docker 提供了通过卷附加/挂载外部存储的功能。正如我们在第四章中所看到的,容器的网络和数据管理,有两种类型的卷,如下所示:
-
通过
--volume
选项通过主机机器挂载的卷 -
通过
--volumes-from
选项通过另一个容器挂载的卷 -
存储驱动程序:我们在第一章中查看了不同的存储驱动程序,即 vfs、aufs、btrfs、devicemapper 和 overlayFS。最近还合并了对 ZFS 的支持。您可以在
github.com/docker/docker/blob/master/daemon/graphdriver/driver.go
上检查当前支持的存储驱动程序及其选择优先级,如果没有选择 Docker 启动时间。
如果您正在运行 Fedora、CentOS 或 RHEL,则设备映射器将是默认的存储驱动程序。您可以在github.com/docker/docker/tree/master/daemon/graphdriver/devmapper
找到一些特定于设备映射器的调整。
您可以使用-s
选项更改 Docker 守护程序的默认存储驱动程序。您可以更新特定于发行版的配置/系统文件,以在服务重新启动时进行更改。对于 Fedora/RHEL/CentOS,您需要在/etc/sysconfig/docker
中更新OPTIONS
字段。类似以下内容可用于使用btrfs
后端:
OPTIONS=-s btrfs
以下图表显示了使用不同存储驱动程序配置启动和停止 1,000 个容器所需的时间:
developerblog.redhat.com/2014/09/30/overview-storage-scalability-docker/
正如您所看到的,overlayFS 的性能优于其他存储驱动程序。
- --net=host:我们知道,默认情况下,Docker 会创建一个桥接,并将 IP 分配给容器。使用
--net=host
将主机网络堆栈暴露给容器,跳过为容器创建网络命名空间。由此可见,与桥接方式相比,此选项始终提供更好的性能。
这有一些限制,比如不能让两个容器或主机应用程序监听相同的端口。
-
Cgroups:Docker 的默认执行驱动程序
libcontainer
公开了不同的 Cgroups 旋钮,可用于微调容器性能。其中一些如下: -
CPU 份额:通过这个,我们可以为容器分配比例权重,并相应地共享资源。考虑以下示例:
$ docker run -it -c 100 fedora bash
- CPUsets:这允许您创建 CPU 掩码,使用它可以控制容器内线程在主机 CPU 上的执行。例如,以下代码将在容器内的第 0 和第 3 个核心上运行线程:
$ docker run -it --cpuset=0,3 fedora bash
- 内存限制:我们可以为容器设置内存限制。例如,以下命令将限制容器的内存使用量为 512 MB:
$ docker run -it -m 512M fedora bash
- Sysctl 和 ulimit 设置:在某些情况下,您可能需要根据用例更改一些
sysclt
值以获得最佳性能,例如更改打开文件的数量。使用 Docker 1.6(docs.docker.com/v1.6/release-notes/
)及以上版本,我们可以使用以下命令更改ulimit
设置:
$ docker run -it --ulimit data=8192 fedora bash
前面的命令将仅更改给定容器的设置,这是一个每个容器的调整变量。我们还可以通过 Docker 守护程序的 systemd 配置文件设置其中一些设置,默认情况下将适用于所有容器。例如,在 Fedora 上查看 Docker 的 systemd 配置文件,您将在服务部分看到类似以下内容:
LimitNOFILE=1048576 # Open file descriptor setting
LimitNPROC=1048576 # Number of processes settings
LimitCORE=infinity # Core size settings
您可以根据需要进行更新。
您可以通过研究他人的工作来了解 Docker 的性能。在过去一年中,一些公司已经发表了一些与 Docker 性能相关的研究:
-
来自 Red Hat:
-
在 Red Hat Enterprise Linux 上对 Docker 的性能分析:
developerblog.redhat.com/2014/08/19/performance-analysis-docker-red-hat-enterprise-linux-7/
github.com/jeremyeder/docker-performance
- Docker 中存储可扩展性的综合概述:
developerblog.redhat.com/2014/09/30/overview-storage-scalability-docker/
- 超越微基准-以特斯拉效率突破容器性能:
- 使用 Red Hat Enterprise Linux 容器化数据库:
rhelblog.redhat.com/2014/10/29/containerizing-databases-with-red-hat-enterprise-linux/
-
来自 IBM
-
虚拟机和 Linux 容器的性能比较的更新版本:
github.com/thewmf/kvm-docker-comparison
-
来自 VMware
-
VMware vSphere 中的 Docker 容器性能
blogs.vmware.com/performance/2014/10/docker-containers-performance-vmware-vsphere.html
为了进行基准测试,我们需要在不同的环境(裸机/虚拟机/Docker)上运行类似的工作负载,然后借助不同的性能统计数据收集结果。为了简化事情,我们可以编写通用的基准测试脚本,这些脚本可以用于不同的环境。我们还可以创建 Dockerfiles 来生成带有工作负载生成脚本的容器。例如,在Red Hat 企业 Linux 上 Docker 性能分析文章中,作者使用了一个 Dockerfile 来创建一个 CentOS 镜像,并使用container
环境变量来选择 Docker 和非 Docker 环境的基准测试脚本run-sysbench.sh
。
同样,IBM 发布了用于其研究的 Dockerfiles 和相关脚本,可在github.com/thewmf/kvm-docker-comparison
上找到。
我们将在本章的示例中使用一些之前提到的 Docker 文件和脚本。
基准测试 CPU 性能
我们可以使用诸如 Linpack(www.netlib.org/linpack/
)和 sysbench(github.com/nuodb/sysbench
)之类的基准测试来测试 CPU 性能。对于这个示例,我们将使用 sysbench。我们将看到如何在裸机和容器内运行基准测试。如前所述,类似的步骤可以在其他环境中执行。
准备工作
我们将使用 CentOS 7 容器在容器内运行基准测试。理想情况下,我们应该有一个安装了 CentOS 7 的系统,以便在裸机上获得基准测试结果。对于容器测试,让我们从之前提到的 GitHub 存储库构建镜像:
$ git clone https://github.com/jeremyeder/docker-performance.git
$ cd docker-performance/Dockerfiles/
$ docker build -t c7perf --rm=true - < Dockerfile
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
c7perf latest 59a10df39a82 About a minute ago 678.3 MB
如何做…
在同一个 GitHub 存储库中,我们有一个用于运行 sysbench 的脚本,docker-performance/bench/sysbench/run-sysbench.sh
。它有一些配置,您可以根据需要进行修改。
- 作为 root 用户,在主机上创建
/results
目录:
$ mkdir -p /results
现在,在将容器环境变量设置为与 Docker 不同的值后运行基准测试,我们在主机上使用该值构建c7perf
镜像,运行以下命令:
$ cd docker-performance/bench/sysbench
$ export container=no
$ sh ./run-sysbench.sh cpu test1
默认情况下,结果会收集在/results
中。确保您对其具有写访问权限,或者在基准测试脚本中更改OUTDIR
参数。
- 要在容器内运行基准测试,我们需要先启动容器,然后运行基准测试脚本:
$ mkdir /results_container
$ docker run -it -v /results_container:/results c7perf bash
$ docker-performance/bench/sysbench/run-sysbench.sh cpu test1
由于我们挂载了主机目录/results_container
到容器内的/results
,因此结果将在主机上收集。
- 在 Fedora/RHEL/CentOS 上运行上述测试时,如果启用了 SELinux,您将收到
Permission denied
错误。要解决此问题,请在将其挂载到容器内时重新标记主机目录,如下所示:
$ docker run -it -v /results_container:/results:z c7perf bash
或者,暂时将 SELinux 设置为宽松模式:
$ setenforce 0
然后,在测试之后,将其恢复为宽松模式:
$ setenforce 1
注意
有关 SELinux 的更多详细信息,请参阅第九章,“Docker 安全性”。
它是如何工作的…
基准测试脚本在内部调用 sysbench 的 CPU 基准测试,用于给定输入。CPU 是通过使用 Euklid 算法进行 64 位整数操作来进行基准测试,用于计算素数。每次运行的结果都会收集在相应的结果目录中,可用于比较。
还有更多…
裸机和 Docker CPU 性能报告几乎没有差异。
另请参阅
- 查看 IBM 和 VMware 在本章前面引用的链接中使用 Linpack 发布的 CPU 基准测试结果。
基准测试磁盘性能
有一些工具可用于基准测试磁盘性能,例如 Iozone (www.iozone.org/
),smallfile (github.com/bengland2/smallfile
)和 Flexible IO (github.com/axboe/fio
)。对于本教程,我们将使用 FIO。为此,我们需要编写一个作业文件,模拟您想要运行的工作负载。使用此作业文件,我们可以在目标上模拟工作负载。对于本教程,让我们使用 IBM 发布的基准测试结果中的 FIO 示例(github.com/thewmf/kvm-docker-comparison/tree/master/fio
)。
准备就绪
在裸机/虚拟机/Docker 容器上,安装 FIO 并挂载包含文件系统的磁盘以进行每个测试,挂载在/ferrari
下或在 FIO 作业文件中提到的任何位置。在裸机上,您可以进行本地挂载,在虚拟机上,可以使用虚拟磁盘驱动程序进行挂载,或者可以进行设备透传。在 Docker 上,我们可以使用 Docker 卷从主机机器附加文件系统。
准备工作负载文件。我们可以选择github.com/thewmf/kvm-docker-comparison/blob/master/fio/mixed.fio
:
[global]
ioengine=libaio
direct=1
size=16g
group_reporting
thread
filename=/ferrari/fio-test-file
[mixed-random-rw-32x8]
stonewall
rw=randrw
rwmixread=70
bs=4K
iodepth=32
numjobs=8
runtime=60
使用上述作业文件,我们可以在/ferrari/fio-test-file
上进行 4K 块大小的随机直接 I/O,使用 16GB 文件上的libaio
驱动程序。I/O 深度为 32,并行作业数为 8。这是一个混合工作负载,其中 70%为读取,30%为写入。
如何做...
- 对于裸机和虚拟机测试,您只需运行 FIO 作业文件并收集结果:
$ fio mixed.fio
- 对于 Docker 测试,您可以按以下方式准备 Docker 文件:
FROM ubuntu
MAINTAINER nkhare@example.com
RUN apt-get update
RUN apt-get -qq install -y fio
ADD mixed.fio /
VOLUME ["/ferrari"]
ENTRYPOINT ["fio"]
- 现在,使用以下命令创建一个镜像:
$ docker build -t docker_fio_perf .
- 按照以下方式启动容器以运行基准测试并收集结果:
$ docker run --rm -v /ferrari:/ferrari docker_fio_perf mixed.fio
- 在 Fedora/RHEL/CentOS 上运行上述测试时,启用 SELinux,您将收到“权限被拒绝”的错误。要解决此问题,请在容器内部挂载主机目录时重新标记主机目录,如下所示:
$ docker run --rm -v /ferrari:/ferrari:z docker_fio_perf mixed.fio
它是如何工作的...
FIO 将运行作业文件中给定的工作负载并输出结果。
还有更多...
收集结果后,您可以进行结果比较。您甚至可以尝试使用作业文件进行不同类型的 I/O 模式,并获得所需的结果。
另请参阅
- 查看 IBM 和 VMware 使用 FIO 在本章前面引用的链接中发布的磁盘基准测试结果
基准测试网络性能
网络是在容器环境中部署应用程序时需要考虑的关键因素之一。为了与裸机、虚拟机和容器进行性能比较,我们必须考虑以下不同的场景:
-
裸机到裸机
-
虚拟机到虚拟机
-
使用默认网络模式(桥接)的 Docker 容器到容器
-
使用主机网络(
--net=host
)的 Docker 容器到容器 -
Docker 容器在虚拟机中运行,与外部世界连接
在前述任何情况下,我们可以选择两个端点进行基准测试。我们可以使用工具如nuttcp
(www.nuttcp.net/
)和netperf
(netperf.org/netperf/
)来分别测量网络带宽和请求/响应。
准备就绪
确保两个端点可以相互到达并安装了必要的软件包/软件。在 Fedora 21 上,您可以使用以下命令安装nuttcp
:
$ yum install -y nuttcp
然后,从其网站获取netperf
。
如何做…
使用nuttcp
测量网络带宽,执行以下步骤:
- 在一个端点上启动
nuttcp
服务器:
$ nuttcp -S
- 使用以下命令从客户端测量传输吞吐量(客户端到服务器):
$ nuttcp -t <SERVER_IP>
- 使用以下命令在客户端上测量接收吞吐量(服务器到客户端):
$ nuttcp -r <SERVER_IP>
-
使用
netperf
运行请求/响应基准测试,执行以下步骤: -
在一个端点上启动
netserver
:
$ netserver
- 从另一个端点连接到服务器并运行请求/响应测试:
- 对于 TCP:
$ netperf -H 172.17.0.6 -t TCP_RR
- 对于 UDP:
$ netperf -H 172.17.0.6 -t UDP_RR
它是如何工作的…
在前面提到的两种情况中,一个端点成为客户端,向另一个端点上的服务器发送请求。
还有更多…
我们可以收集不同场景的基准测试结果并进行比较。netperf
也可以用于吞吐量测试。
另请参阅
- 查看 IBM 和 VMware 在本章前面引用的链接中发布的网络基准测试结果。
使用统计功能获取容器资源使用情况
随着 1.5 版本的发布,Docker 增加了一个功能,可以从内置命令中获取容器资源使用情况。
准备就绪
安装了 1.5 或更高版本的 Docker 主机,可以通过 Docker 客户端访问。同时,启动一些容器以获取统计信息。
如何做…
- 运行以下命令从一个或多个容器获取统计信息:
$ docker stats [CONTAINERS]
例如,如果我们有两个名为some-mysql
和backstabbing_turing
的容器,然后运行以下命令以获取统计信息:
$ docker stats some-mysql backstabbing_turing
它是如何工作的…
Docker 守护程序从 Cgroups 获取资源信息,并通过 API 提供它。
参见
- 参考 Docker 1.5 的发布说明
docs.docker.com/v1.5/release-notes/
设置性能监控
我们有诸如 SNMP、Nagios 等工具来监视裸机和虚拟机的性能。同样,有一些可用于监视容器性能的工具/插件,如 cAdvisor(github.com/google/cadvisor
)和 sFlow(blog.sflow.com/2014/06/docker-performance-monitoring.html
)。在本教程中,让我们看看如何配置 cAdvisor。
准备就绪
设置 cAdvisor。
- 运行 cAdvisor 的最简单方法是运行其 Docker 容器,可以使用以下命令完成:
sudo docker run \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:rw \
--volume=/sys:/sys:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--publish=8080:8080 \
--detach=true \
--name=cadvisor \
google/cadvisor:latest
- 如果您想在 Docker 之外运行 cAdvisor,请按照 cAdvisor 主页上给出的说明进行操作
github.com/google/cadvisor/blob/master/docs/running.md#standalone
操作方法…
容器启动后,将浏览器指向http://localhost:8080
。您将首先获得有关主机机器的 CPU、内存使用情况和其他信息的图表。然后,通过单击 Docker 容器链接,您将在子容器部分下获得运行在机器上的容器的 URL。如果单击其中任何一个,您将看到相应容器的资源使用信息。
以下是一个这样的容器的屏幕截图:
它是如何工作的…
使用docker run
命令,我们已经以只读模式挂载了一些卷,cAdvisor 将从中读取相关信息,比如容器的 Cgroup 详细信息,并以图形方式显示它们。
还有更多…
cAdvisor 支持将性能矩阵导出到 influxdb(influxdb.com/
)。Heapster(github.com/GoogleCloudPlatform/heapster
)是 Google 的另一个项目,它允许使用 cAdvisor 进行集群范围(Kubernetes)监控。
参见
- 您可以在 Docker 网站的文档中查看 cAdvisor 从 Cgroups 中使用的矩阵[https://docs.docker.com/articles/runmetrics/]。
第八章:Docker 编排和托管平台
在本章中,我们将涵盖以下配方:
-
使用 Docker Compose 运行应用程序
-
使用 Docker Swarm 设置集群
-
为 Docker 编排设置 CoreOS
-
设置 Project Atomic 主机
-
使用 Project Atomic 进行原子更新/回滚
-
为 Project Atomic 中的 Docker 添加更多存储
-
为 Project Atomic 设置 Cockpit
-
设置 Kubernetes 集群
-
在 Kubernetes 集群中进行扩展和缩减
-
使用 Kubernetes 集群设置 WordPress
介绍
在单个主机上运行 Docker 可能对开发环境有好处,但当我们跨多个主机时才能发挥真正的价值。然而,这并不是一件容易的事情。您必须编排这些容器。因此,在本章中,我们将介绍一些编排工具和托管平台。
Docker Inc.宣布了两种工具:
使用 Docker Compose(docs.docker.com/compose
)创建由多个容器组成的应用程序,使用 Docker Swarm(docs.docker.com/swarm/
)来集群多个 Docker 主机。Docker Compose 以前被称为 Fig(www.fig.sh/
)。
CoreOS(coreos.com/
)创建了 etcd(github.com/coreos/etcd
)用于一致性和服务发现,fleet(coreos.com/using-coreos/clustering
)用于在集群中部署容器,flannel(github.com/coreos/flannel
)用于覆盖网络。
谷歌启动了 Kubernetes(kubernetes.io/
)用于 Docker 编排。Kubernetes 提供了应用部署、调度、更新、维护和扩展的机制。
红帽推出了一个专门针对容器的操作系统,名为 Project Atomic(www.projectatomic.io/
),可以利用 Kubernetes 的编排能力。
甚至微软也宣布了专门为 Docker 设计的操作系统(azure.microsoft.com/blog/2015/04/08/microsoft-unveils-new-container-technologies-for-the-next-generation-cloud/
)。
Apache Mesos(mesos.apache.org/
)提供了整个数据中心和云环境的资源管理和调度,还增加了对 Docker(mesos.apache.org/documentation/latest/docker-containerizer/
)的支持。
VMware 还推出了专门针对容器的宿主机 VMware Photon(vmware.github.io/photon/
)。
这绝对是一个非常有趣的领域,但许多编排引擎的策略管理工具并没有让开发人员和运维人员的生活变得轻松。当他们从一个平台转移到另一个平台时,他们必须学习不同的工具和格式。如果我们能够有一种标准的方式来构建和启动复合的多容器应用程序,那将是很棒的。Project Atomic 社区似乎正在致力于一种名为 Nulecule 的平台中立规范(github.com/projectatomic/nulecule/
)。关于 Nulecule 的一个很好的描述可以在www.projectatomic.io/blog/2015/05/announcing-the-nulecule-specification-for-composite-applications/
找到。
“Nulecule 定义了打包复杂的多容器应用程序的模式和模型,引用了它们的所有依赖关系,包括单个容器映像中的编排元数据,用于构建、部署、监视和主动管理。只需创建一个带有 Nulecule 文件的容器,应用程序就会‘自动运行’。在 Nulecule 规范中,您可以在图形中定义编排提供者、容器位置和配置参数,Atomic App 实现将在提供者的帮助下将它们组合在一起。Nulecule 规范支持多个复合应用程序的聚合,它也是容器和编排引擎不可知的,可以使用任何容器和编排引擎。”
AtomicApp 是 Nulecule 规范的一个参考实现(github.com/projectatomic/atomicapp/
)。它可以用来引导容器应用程序的安装和运行。AtomicApp 目前支持有限数量的提供者(Docker、Kubernetes、OpenShift),但很快将添加对其他提供者的支持。
相关的是,CentOS 社区正在构建一个 CI 环境,将利用 Nulecule 和 AtomicApp。欲了解更多信息,请访问wiki.centos.org/ContainerPipeline
。
所有前述的工具和平台都需要单独的章节来介绍。在本章中,我们将简要探讨 Compose、Swarm、CoreOS、Project Atomic 和 Kubernetes。
使用 Docker Compose 运行应用程序
Docker Compose(docs.docker.com/compose/
)是运行组成应用程序的相互依赖容器的本地 Docker 工具。我们在一个文件中定义一个多容器应用程序,并将其提供给 Docker Compose,它会设置应用程序。在撰写本文时,Compose 仍未达到生产就绪状态。在本教程中,我们将再次使用 WordPress 作为示例应用程序来运行。
准备工作
确保系统上安装了 Docker 版本 1.3 或更高版本。要安装 Docker Compose,请运行以下命令:
$ sudo pip install docker-compose
如何做…
-
为应用程序创建一个目录,并在其中创建
docker-compose.yml
来定义应用程序: -
我们从 Docker Hub 的官方 WordPress Docker 存储库(
registry.hub.docker.com/_/wordpress/
)中获取了上述示例。 -
在应用程序目录中,运行以下命令构建应用程序:
$ docker-compose up
- 构建完成后,从
http://localhost:8080
或http://<host-ip>:8080
访问 WordPress 安装页面。
它是如何工作的…
Docker Compose 会从官方 Docker 注册表下载mariadb
和wordpress
镜像(如果本地不存在)。首先,它会从mariadb
镜像启动db
容器;然后启动wordpress
容器。接下来,它会与db
容器进行链接,并将端口导出到主机。
更多内容…
我们甚至可以在 Compose 期间从 Dockerfile 构建镜像,然后将其用于应用程序。例如,要构建wordpress
镜像,我们可以从应用程序的 Compose 目录中获取相应的 Dockerfile 和其他支持文件,并以类似的方式更新docker-compose.yml
文件:
我们可以启动、停止、重建和获取应用程序的状态。请访问 Docker 网站上的文档。
另请参阅
-
Docker Compose YAML 文件参考位于
docs.docker.com/compose/yml/
-
Docker Compose 命令行参考位于
docs.docker.com/compose/cli/
-
Docker Compose GitHub 存储库位于
github.com/docker/compose
使用 Docker Swarm 设置集群
Docker Swarm (docs.docker.com/swarm/
)是 Docker 的本机集群。它将多个 Docker 主机分组到一个池中,可以在其中启动容器。在本教程中,我们将使用 Docker Machine (docs.docker.com/machine/
)来设置 Swarm 集群。在撰写本文时,Swarm 仍未达到生产就绪状态。如果您还记得,我们在第一章中使用 Docker Machine 在 Google Compute Engine 上设置了 Docker 主机,介绍和安装。为了保持简单,我们将在这里使用 VirtualBox 作为 Docker Machine 的后端来配置主机。
准备工作
-
在您的系统上安装 VirtualBox (
www.virtualbox.org/
)。配置 VirtualBox 的说明不在本书的范围之内。 -
下载并设置 Docker Machine。在 Fedora x86_64 上,运行以下命令:
$ wget https://github.com/docker/machine/releases/download/v0.2.0/docker-machine_linux-amd64
$ sudo mv docker-machine_linux-amd64 /usr/local/bin/docker-machine
$ chmod a+x /usr/local/bin/docker-machine
操作方法…
- 使用 Swarm 发现服务,我们首先需要创建一个 Swarm 令牌来唯一标识我们的集群。除了默认的托管发现服务外,Swarm 还支持不同类型的发现服务,如 etcd、consul 和 zookeeper。有关更多详细信息,请访问
docs.docker.com/swarm/discovery/
。要使用默认的托管发现服务创建令牌,我们将首先在 VM 上使用 Docker Machine 设置 Docker 主机,然后获取令牌:
$ docker-machine create -d virtualbox local
- 要从本地 Docker 客户端访问我们刚创建的 Docker,请运行以下命令:
$ eval "$(docker-machine env local)"
- 要获取令牌,请运行以下命令:
$ docker run swarm create
7c3a21b42708cde81d99884116d68fa1
- 使用前一步骤中创建的令牌,设置 Swarm 主节点:
$ docker-machine create -d virtualbox --swarm --swarm-master --swarm-discovery token://7c3a21b42708cde81d99884116d68fa1 swarm-master
- 同样,让我们创建两个 Swarm 节点:
$ docker-machine create -d virtualbox --swarm --swarm-discovery token://7c3a21b42708cde81d99884116d68fa1 swarm-node-1
$ docker-machine create -d virtualbox --swarm --swarm-discovery token://7c3a21b42708cde81d99884116d68fa1 swarm-node-2
- 现在,从本地 Docker 客户端连接到 Docker Swarm:
$ eval "$(docker-machine env swarm-master)"
- Swarm API 与 Docker 客户端 API 兼容。让我们运行
docker info
命令来查看 Swarm 的当前配置/设置:
$ docker info
如您所见,我们的集群中有三个节点:一个主节点和两个节点。
工作原理…
使用我们从托管发现服务获得的唯一令牌,我们在集群中注册了主节点和节点。
还有更多...
-
在前面的
docker info
输出中,我们还安排了策略和过滤器。有关这些的更多信息可以在docs.docker.com/swarm/scheduler/strategy/
和docs.docker.com/swarm/scheduler/filter/
找到。这些定义了容器将在哪里运行。 -
正在积极开发以集成 Docker Swarm 和 Docker Compose,以便我们将应用指向 Swarm 集群并进行组合。然后应用将在集群上启动。访问
github.com/docker/compose/blob/master/SWARM.md
另请参阅
-
Docker 网站上的 Swarm 文档位于
docs.docker.com/swarm/
-
Swarm 的 GitHub 存储库位于
github.com/docker/swarm
为 Docker 编排设置 CoreOS
CoreOS(coreos.com/
)是一种经过重新架构以提供运行现代基础架构堆栈所需功能的 Linux 发行版。它是 Apache 2.0 许可的。它有一个名为 CoreOS Managed Linux(coreos.com/products/managed-linux/
)的产品,CoreOS 团队为其提供商业支持。
基本上,CoreOS 提供了托管完整应用程序堆栈的平台。我们可以在不同的云提供商、裸机和虚拟机环境上设置 CoreOS。让我们来看看 CoreOS 的构建模块:
-
etcd
-
容器运行时
-
Systemd
-
Fleet
让我们详细讨论每个:
-
etcd:来自 etcd 的 GitHub 页面(
github.com/coreos/etcd/#etcd
)。etcd
是一个用于共享配置和服务发现的高可用性键值存储。它受到 Apache ZooKeeper 和 doozer 的启发,专注于以下方面: -
简单:可通过 Curl 访问的用户界面 API(HTTP 加 JSON)
-
安全:可选的 SSL 客户端证书认证
-
快速:每个实例的数千次写入的基准测试
-
可靠:使用 Raft 进行适当的分发
它是用 Go 编写的,并使用 Raft 一致性算法(raftconsensus.github.io/
)来管理高可用性的复制日志。etcd 可以独立于 CoreOS 使用。我们可以:
-
建立单节点或多节点集群。有关此信息,请访问
github.com/coreos/etcd/blob/master/Documentation/clustering.md
。 -
使用 CURL 和不同的库进行访问,可在
github.com/coreos/etcd/blob/master/Documentation/libraries-and-tools.md
找到。
在 CoreOS 中,etcd
用于协调集群。它提供了一种以一致的方式存储配置和关于服务的信息的机制。
-
容器运行时:CoreOS 支持 Docker 作为容器运行时环境。在 2014 年 12 月,CoreOS 宣布推出了一个新的容器运行时 Rocket (
coreos.com/blog/rocket/
)。让我们将讨论限制在目前安装在所有 CoreOS 机器上的 Docker 上。 -
systemd:
systemd
是用于启动、停止和管理进程的初始化系统。在 CoreOS 中,它用于: -
启动 Docker 容器
-
将由容器启动的服务注册到 etcd
Systemd 管理单元文件。示例单元文件如下:
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.com
After=network.target docker.socket
Requires=docker.socket
[Service]
Type=notify
EnvironmentFile=-/etc/sysconfig/docker
EnvironmentFile=-/etc/sysconfig/docker-storage
ExecStart=/usr/bin/docker -d -H fd:// $OPTIONS $DOCKER_STORAGE_OPTIONS
LimitNOFILE=1048576
LimitNPROC=1048576
[Install]
WantedBy=multi-user.target
此单元文件在 Fedora 21 上使用ExecStart
中提到的命令启动 Docker 守护程序。Docker 守护程序将在network target
和docker socket
服务之后启动。docker socket
是 Docker 守护程序启动的先决条件。Systemd 目标是将进程分组以便它们可以同时启动的方式。multi-user
是前面单元文件注册的目标之一。有关更多详细信息,您可以查看 Systemd 的上游文档www.freedesktop.org/wiki/Software/systemd/
。
- Fleet:Fleet (
coreos.com/using-coreos/clustering/
)是控制集群级别的systemd
的集群管理器。systemd 单元文件与一些特定于 Fleet 的属性结合起来实现目标。来自 Fleet 文档(github.com/coreos/fleet/blob/master/Documentation/architecture.md
):
“Fleet 集群中的每个系统都运行一个
fleetd
守护程序。每个守护程序封装了两个角色:引擎和代理。引擎主要做出调度决策,而代理执行单元。引擎和代理都使用协调模型,定期生成'当前状态'和'期望状态'的快照,并进行必要的工作将前者变异为后者。”
etcd
是fleet
集群中唯一的数据存储。所有持久和临时数据都存储在etcd
中;单元文件、集群存在、单元状态等。etcd
也用于 fleet 引擎和代理之间的所有内部通信。
现在我们知道了 CoreOS 的所有构建模块。让我们在本地系统/笔记本上尝试 CoreOS。为了保持简单,我们将使用 Vagrant 来设置环境。
准备就绪
-
在系统上安装 VirtualBox(
www.virtualbox.org/
)和 Vagrant(www.vagrantup.com/
)。配置这两个东西的说明超出了本书的范围。 -
克隆
coreos-vagrant
存储库:
$ git clone https://github.com/coreos/coreos-vagrant.git
$ cd coreos-vagrant
- 将示例文件
user-data.sample
复制到user-data
并设置引导集群的令牌:
$ cp user-data.sample user-data
-
当我们使用多个节点配置 CoreOS 集群时,我们需要一个令牌来引导集群以选择初始的 etcd 领导者。这项服务由 CoreOS 团队免费提供。我们只需要在浏览器中打开
https://discovery.etcd.io/new
来获取令牌,并在user-data
文件中更新如下: -
将
config.rb.sample
复制到config.rb
并更改以下行:
$num_instances=1
现在应该是这样的:
$num_instances=3
这将要求 Vagrant 设置三个节点集群。默认情况下,Vagrant 配置为从 alpha 版本获取 VM 映像。我们可以通过在 Vagrantfile 中更新$update_channel
参数将其更改为 beta 或 stable。对于这个示例,我选择了 stable。
操作步骤如下…
- 运行以下命令设置集群:
$ vagrant up
现在,使用以下截图中显示的命令检查状态:
- 使用 SSH 登录到其中一个 VM,查看服务状态,并列出集群中的机器:
$ vagrant ssh core-01
$ systemctl status etcd fleet
$ fleetctl list-machines
- 创建一个名为
myapp.service
的服务单元文件,内容如下:
[Unit]
Description=MyApp
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill busybox1
ExecStartPre=-/usr/bin/docker rm busybox1
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --name busybox1 busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop=/usr/bin/docker stop busybox1
- 现在让我们提交服务进行调度并启动服务:
$ fleetctl submit myapp.service
$ fleetctl start myapp.service
$ fleetctl list-units
正如我们所看到的,我们的服务已经在集群中的一个节点上启动。
工作原理...
Vagrant 使用云配置文件(user-data
)来引导 VM。由于它们具有相同的令牌来引导集群,它们选择领导者并开始操作。然后,使用fleetctl
,这是 fleet 集群管理工具,我们提交单元文件进行调度,该文件在一个节点上启动。
还有更多...
-
使用此配方中的云配置文件,我们可以在所有 VM 上启动
etcd
和fleet
。我们可以选择仅在选定的节点上运行etcd
,然后配置运行fleet
的工作节点以连接到 etcd 服务器。可以通过相应地设置云配置文件来完成此操作。有关更多信息,请访问coreos.com/docs/cluster-management/setup/cluster-architectures/
。 -
使用
fleet
,我们可以为高可用性配置服务。有关更多信息,请查看coreos.com/docs/launching-containers/launching/fleet-unit-files/
。 -
尽管您的服务正在主机上运行,但您将无法从外部访问它。您需要添加某种路由器和通配符 DNS 配置,以便从外部世界访问您的服务。
另请参阅
-
更多详细信息,请参阅 CoreOS 文档
coreos.com/docs/
-
在
thesecretlivesofdata.com/raft
上可视化 RAFT 一致性算法 -
如何配置云配置文件,请访问
coreos.com/docs/cluster-management/setup/cloudinit-cloud-config/
和coreos.com/validate/
-
有关 systemd 的文档,请访问
coreos.com/docs/launching-containers/launching/getting-started-with-systemd/
-
如何使用 fleet 启动容器,请访问
coreos.com/docs/launching-containers/launching/launching-containers-fleet/
设置 Project Atomic 主机
Project Atomic 通过提供端到端的解决方案来促进以应用为中心的 IT 架构,快速可靠地部署容器化应用程序,并为应用程序和主机提供原子更新和回滚。
这是通过在 Project Atomic 主机上在容器中运行应用程序来实现的,这是一种专门设计用于运行容器的轻量级操作系统。主机可以基于 Fedora、CentOS 或 Red Hat Enterprise Linux。
接下来,我们将详细介绍 Project Atomic 主机的构建模块。
- OSTree 和 rpm-OSTree:OSTree (
wiki.gnome.org/action/show/Projects/OSTree
)是一种管理可引导、不可变和版本化文件系统树的工具。使用这个工具,我们可以构建客户端-服务器架构,其中服务器托管一个 OSTree 存储库,订阅它的客户端可以逐步复制内容。
rpm-OSTree 是一种在服务器端将 RPM 解压缩为客户端可以订阅并执行更新的 OSTree 存储库的系统。每次更新都会创建一个新的根,用于下一次重启。在更新期间,/etc
被重新设置,/var
则不变。
-
容器运行时:截至目前,Project Atomic 只支持 Docker 作为容器运行时。
-
systemd:正如我们在之前的配方中看到的,systemd 是一个新的 init 系统。它还帮助为完整的多租户安全性设置 SELinux 策略,并控制 Cgroups 策略,我们在第一章中看到了介绍和安装。
Project Atomic 使用 Kubernetes (kubernetes.io/
)在容器主机集群上部署应用程序。Project Atomic 可以安装在裸机、云提供商、虚拟机等上。在这个配方中,让我们看看如何在 Fedora 上使用 virt-manager 在虚拟机上安装它。
做好准备
- 下载图像:
$ wget http://download.fedoraproject.org/pub/fedora/linux/releases/test/22_Beta/Cloud/x86_64/Images/Fedora-Cloud-Atomic-22_Beta-20150415.x86_64.raw.xz
我已经下载了 Fedora 22 云图像用于容器的测试版图像。您应该在getfedora.org/en/cloud/download/
上寻找最新的云图像用于容器。
- 使用以下命令解压此图像:
$ xz -d Fedora-Cloud-Atomic-22_Beta-20150415.x86_64.raw.xz
如何做到这一点…
- 我们下载了一个没有为默认用户
fedora
设置任何密码的云镜像。在启动虚拟机时,我们必须通过一个云配置文件来自定义虚拟机。为此,我们需要创建两个文件,meta-data
和user-data
,如下所示:
$ cat meta-data
instance-id: iid-local01
local-hostname: atomichost
$ cat user-data
#cloud-config
password: atomic
ssh_pwauth: True
chpasswd: { expire: False }
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc.........
在上面的代码中,我们需要提供完整的 SSH 公钥。然后,我们需要创建一个包含这些文件的 ISO 镜像,我们将使用它来引导虚拟机。由于我们使用的是云镜像,我们的设置将在引导过程中应用于虚拟机。这意味着主机名将设置为atomichost
,密码将设置为atomic
,依此类推。要创建 ISO,请运行以下命令:
$ genisoimage -output init.iso -volid cidata -joliet -rock user-data meta-data
-
启动 virt-manager。
-
选择新建虚拟机,然后导入现有的磁盘映像。输入我们之前下载的 Project Atomic 映像的路径。选择操作系统类型为Linux,版本为Fedora 20/Fedora 21(或更高版本),然后点击下一步。接下来,分配 CPU 和内存,然后点击下一步。然后,在安装之前为虚拟机命名并选择自定义配置。最后,点击完成并查看详细信息。
-
接下来,点击添加硬件,在选择存储后,将我们创建的 ISO(
init.iso
)文件附加到虚拟机,并选择开始安装:
启动后,您可以看到其主机名已正确设置,并且您将能够使用云初始化文件中给定的密码登录。默认用户是fedora
,密码是atomic
,如在user-data
文件中设置的那样。
工作原理…
在这个示例中,我们使用virt-manager
提供云初始化文件,使用 Project Atomic Fedora 云镜像引导了虚拟机。
更多内容…
-
登录后,如果在
/
目录下列出文件,你会看到大多数传统目录都链接到/var
,因为它在升级过程中会被保留。 -
登录后,您可以像往常一样运行 Docker 命令:
$sudo docker run -it fedora bash
另请参阅
-
有关虚拟管理器的文档,请访问
virt-manager.org/documentation/
-
有关软件包系统、镜像系统和 RPM-OSTree 的更多信息,请访问
github.com/projectatomic/rpm-ostree/blob/master/doc/background.md
-
Project Atomic 网站上的快速入门指南,请访问
www.projectatomic.io/docs/quickstart/
-
有关云镜像的资源,请访问
www.technovelty.org//linux/running-cloud-images-locally.html
和cloudinit.readthedocs.org/en/latest/
-
如何在 Atomic 主机上设置 Kubernetes,网址为
www.projectatomic.io/blog/2014/11/testing-kubernetes-with-an-atomic-host/
和github.com/cgwalters/vagrant-atomic-cluster
使用 Project Atomic 进行原子更新/回滚
要升级到最新版本或回滚到 Project Atomic 的旧版本,我们使用atomic host
命令,该命令内部调用 rpm-ostree。
准备工作
启动并登录到 Atomic 主机。
如何做…
- 启动后,运行以下命令:
$ atomic host status
您将看到有关当前正在使用的部署的详细信息。
升级,请运行以下命令:
- 这将更改和/或添加新的软件包。升级后,我们需要重新启动系统以使用新的更新。让我们重新启动并查看结果:
正如我们所看到的,系统现在已经使用新的更新启动。位于第一行开头的*
表示活动构建。
- 要回滚,请运行以下命令:
$ sudo atomic host rollback
如果我们想使用旧的位,我们将不得不再次重启。
工作原理…
对于更新,Atomic 主机连接到托管较新构建的远程存储库,该构建将在下一次重启后下载并使用,直到用户升级或回滚。在回滚的情况下,系统上可用的旧构建将在重启后使用。
另请参阅
- 可以在 Project Atomic 网站的文档中找到,网址为
www.projectatomic.io/docs/os-updates/
在 Project Atomic 中为 Docker 添加更多存储
Atomic 主机是一个最小的发行版,因此以 6GB 的镜像分发,以保持占用空间小。这是非常少的存储空间来构建和存储大量的 Docker 镜像,因此建议为这些操作附加外部存储。
默认情况下,Docker 使用/var/lib/docker
作为存储所有与 Docker 相关的文件(包括镜像)的默认目录。在 Project Atomic 中,我们使用直接的 LVM 卷通过 devicemapper 后端将 Docker 镜像和元数据存储在/dev/atomicos/docker-data
和/dev/atomicos/docker-meta
中。
因此,为了添加更多存储空间,Project Atomic 提供了一个名为docker-storage-helper
的辅助脚本,将外部磁盘添加到现有的 LVM thin pool 中。让我们使用docker info
命令查看当前可用于 Docker 的存储空间:
正如我们所看到的,总数据空间为 2.96 GB,总元数据空间为 8.38 MB。
做好准备
-
停止 VM,如果它正在运行。
-
向 Project Atomic VM 添加所需大小的额外磁盘。我已经添加了 8 GB。
-
启动 VM。
-
检查新添加的磁盘是否对 VM 可见。
如何操作...
- 检查附加磁盘是否可用于 Atomic 主机 VM:
正如我们所看到的,新创建的 8 GB 磁盘对 VM 可用。
- 由于新添加的磁盘是
/dev/sdb
,因此创建一个名为/etc/sysconfig/docker-storage-setup
的文件,并包含以下内容:
DEVS="/dev/sdb"
[fedora@atomichost ~]$ cat /etc/sysconfig/docker-storage-setup
DEVS="/dev/sdb"
- 运行
docker-storage-setup
命令将/dev/sdb
添加到现有卷中:
$ sudo docker-storage-setup
- 现在,让我们再次使用
docker info
命令查看当前可用于 Docker 的存储空间:
正如我们所看到的,总数据空间和元数据空间都增加了。
它是如何工作的...
该过程与扩展任何其他 LVM 卷的过程相同。我们在添加的磁盘上创建一个物理卷,将该物理卷添加到卷组中,然后扩展 LVM 卷。由于我们直接访问 Docker 中的 thin pool,因此我们不需要创建或扩展文件系统或挂载 LVM 卷。
还有更多...
-
除了
DEVS
选项之外,您还可以在/etc/sysconfig/docker-storage-setup
文件中添加VG
选项以使用不同的卷组。 -
您可以使用
DEVS
选项添加多个磁盘。 -
如果已经在卷组中的磁盘已经在
DEVS
选项中被提及,那么docker-storage-setup
脚本将退出,因为现有设备已经创建了分区和物理卷。 -
docker-storage-setup
脚本为meta-data
保留了 0.1%的大小。这就是为什么我们也看到了 Metadata Space 的增加。
另请参阅
-
在 Project Atomic 网站上的文档
www.projectatomic.io/docs/docker-storage-recommendation/
-
在 Project Atomic 上支持的文件系统
www.projectatomic.io/docs/filesystems/
为 Project Atomic 设置 Cockpit
Cockpit (cockpit-project.org/
)是一个服务器管理器,可以通过 Web 浏览器轻松管理 GNU/Linux 服务器。它也可以用来管理 Project Atomic 主机。一个 Cockpit 实例可以管理多个主机。Cockpit 不会默认随最新的 Project Atomic 一起提供,您需要将其作为超级特权容器(SPC)启动。SPC 是专门构建的容器,以关闭安全性运行(--privileged
);它关闭一个或多个命名空间或将主机 OS 的“卷挂载到”容器中的部分。有关 SPC 的更多详细信息,请参阅developerblog.redhat.com/2014/11/06/introducing-a-super-privileged-container-concept/
和www.youtube.com/watch?v=eJIeGnHtIYg
。
因为 Cockpit 作为 SPC 运行,所以可以访问容器内管理原子主机所需的资源。
准备工作
设置 Project Atomic 主机并登录。
如何操作…
- 运行以下命令启动 Cockpit 容器:
[fedora@atomichost ~]$ sudo atomic run stefwalter/cockpit-ws
- 打开浏览器(
http://<VM IP>:9090
)并使用默认用户/密码fedora/atomic
登录。登录后,您可以选择当前主机进行管理。您将看到如下所示的屏幕:
工作原理…
在这里,我们使用atomic
命令而不是docker
命令来启动容器。让我们看看 Cockpit Dockerfile(github.com/fedora-cloud/Fedora-Dockerfiles/blob/master/cockpit-ws/Dockerfile
),看看为什么我们这样做。在 Dockerfile 中,您将看到一些指令:
LABEL INSTALL /usr/bin/docker run -ti --rm --privileged -v /:/host IMAGE /container/atomic-install
LABEL UNINSTALL /usr/bin/docker run -ti --rm --privileged -v /:/host IMAGE /cockpit/atomic-uninstall
LABEL RUN /usr/bin/docker run -d --privileged --pid=host -v /:/host IMAGE /container/atomic-run --local-ssh
如果您回忆起第二章中的使用 Docker 容器和第三章中的使用 Docker 镜像,我们可以使用标签为镜像和容器分配元数据。这里的标签是INSTALL
、UNINSTALL
和RUN
。atomic
命令是 Project Atomic 特有的命令,它读取这些标签并执行操作。由于容器作为 SPC 运行,因此不需要从主机到容器的端口转发。有关atomic
命令的更多详细信息,请访问developerblog.redhat.com/2015/04/21/introducing-the-atomic-command/
。
还有更多...
您可以通过 GUI 执行几乎所有管理员任务。您可以通过这个管理 Docker 镜像/容器。您可以执行以下操作:
-
拉取镜像
-
启动/停止容器
您还可以将其他机器添加到同一个 Cockpit 实例中,以便从一个中央位置管理它们。
另请参阅
- Cockpit 文档位于
files.cockpit-project.org/guide/
设置 Kubernetes 集群
Kubernetes 是一个开源的容器编排工具,可以跨集群的多个节点进行操作。目前,它只支持 Docker。它是由 Google 发起的,现在其他公司的开发人员也在为其做出贡献。它提供了应用部署、调度、更新、维护和扩展的机制。Kubernetes 的自动放置、自动重启、自动复制功能确保了应用程序的期望状态得以维持,这是由用户定义的。用户通过 YAML 或 JSON 文件定义应用程序,我们稍后会看到。这些 YAML 和 JSON 文件还包含 API 版本(apiVersion
字段)来识别模式。以下是 Kubernetes 的架构图:
raw.githubusercontent.com/GoogleCloudPlatform/kubernetes/master/docs/architecture.png
让我们来看看 Kubernetes 的一些关键组件和概念。
-
Pods:Pod 由一个或多个容器组成,是 Kubernetes 的部署单元。Pod 中的每个容器与同一 Pod 中的其他容器共享不同的命名空间。例如,Pod 中的每个容器共享相同的网络命名空间,这意味着它们可以通过 localhost 进行通信。
-
节点/从属节点:节点,以前被称为从属节点,是 Kubernetes 集群中的工作节点,并通过主节点进行管理。Pod 被部署在具有运行它们所需服务的节点上。
-
docker,用于运行容器
-
kubelet,用于与主节点交互
-
代理(kube-proxy),将服务连接到相应的 Pod
-
主节点:主节点托管集群级别的控制服务,例如以下内容:
-
API 服务器:具有用于与主节点和节点交互的 RESTful API。这是唯一与 etcd 实例通信的组件。
-
调度器:在集群中调度作业,例如在节点上创建 Pod。
-
复制控制器:确保用户指定数量的 Pod 副本在任何给定时间都在运行。要使用复制控制器管理副本,我们必须定义一个配置文件,其中包含 Pod 的副本计数。
主节点还与 etcd 通信,etcd 是一个分布式键值对。etcd 用于存储配置信息,主节点和节点都使用这些信息。etcd 的 watch 功能用于通知集群中的更改。etcd 可以托管在主节点上或不同的一组系统上。
-
服务:在 Kubernetes 中,每个 Pod 都有自己的 IP 地址,并且根据复制控制器的配置,Pod 会不时地被创建和销毁。因此,我们不能依赖于 Pod 的 IP 地址来为应用程序提供服务。为了解决这个问题,Kubernetes 定义了一个抽象,定义了一组逻辑 Pod 和访问它们的策略。这个抽象被称为服务。标签用于定义服务管理的逻辑集合。
-
标签:标签是可以附加到对象的键值对,使用它们可以选择对象的子集。例如,服务可以选择具有标签
mysql
的所有 Pod。 -
卷: 卷是一个对 pod 中的容器可访问的目录。它类似于 Docker 卷,但不完全相同。Kubernetes 支持不同类型的卷,其中一些是 EmptyDir(临时)、HostDir、GCEPersistentDisk 和 NFS。正在积极开发以支持更多类型的卷。更多细节可以在
github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/volumes.md
找到。
Kubernetes 可以安装在虚拟机、物理机和云上。要查看完整的矩阵,请查看github.com/GoogleCloudPlatform/kubernetes/tree/master/docs/getting-started-guides
。在这个示例中,我们将看到如何在虚拟机上使用 VirtualBox 提供程序安装它。这个示例和接下来关于 Kubernetes 的示例是在 Kubernetes 的 v0.17.0 上尝试的。
准备工作
-
从
www.vagrantup.com/downloads.html
安装最新的 Vagrant >= 1.6.2。 -
从
www.virtualbox.org/wiki/Downloads
安装最新的 VirtualBox。如何设置这个的详细说明超出了本书的范围。
如何做...
- 运行以下命令在 Vagrant 虚拟机上设置 Kubernetes:
$ export KUBERNETES_PROVIDER=vagrant
$ export VAGRANT_DEFAULT_PROVIDER=virtualbox
$ curl -sS https://get.k8s.io | bash
它是如何工作的...
从curl
命令下载的 bash 脚本首先下载最新的 Kubernetes 版本,然后运行./kubernetes/cluster/kube-up.sh
bash 脚本来设置 Kubernetes 环境。由于我们已经指定 Vagrant 为KUBERNETES_PROVIDER
,脚本首先下载 Vagrant 镜像,然后使用 Salt (saltstack.com/
) 配置一个主节点和一个节点(minion)虚拟机。初始设置需要几分钟来运行。
Vagrant 在~/.kubernetes_vagrant_auth
中创建一个凭据文件进行身份验证。
还有更多...
类似于./cluster/kube-up.sh
,还有其他辅助脚本可以在主机上执行不同的操作。确保你在kubernetes
目录中,在运行以下命令时已经安装了之前的安装:
- 获取节点列表:
$ ./cluster/kubectl.sh get nodes
- 获取 pod 的列表:
$ ./cluster/kubectl.sh get pods
- 获取服务列表:
$ ./cluster/kubectl.sh get services
- 获取复制控制器的列表:
$ ./cluster/kubectl.sh get replicationControllers
- 销毁 vagrant 集群:
$ ./cluster/kube-down.sh
- 然后恢复 vagrant 集群:
$ ./cluster/kube-up.sh
您将看到一些列出的pods
,services
和replicationControllers
,因为 Kubernetes 为内部使用创建它们。
另请参阅
-
在
github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/getting-started-guides/vagrant.md
设置 Vagrant 环境 -
github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/user-guide.md
的 Kubernetes 用户指南 -
在
github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/api-conventions.md
了解 Kubernetes API 约定
在 Kubernetes 集群中进行扩展和缩减
在前一节中,我们提到复制控制器确保用户指定数量的 pod 副本在任何给定时间都在运行。要使用复制控制器管理副本,我们必须定义一个具有 pod 副本计数的配置文件。此配置可以在运行时更改。
准备就绪
确保 Kubernetes 设置正在按照前面的配方运行,并且您在kubernetes
目录中,该目录是使用前面的安装创建的。
如何做…
- 启动带有 3 个副本计数的
nginx
容器:
$ ./cluster/kubectl.sh run-container my-nginx --image=nginx --replicas=3 --port=80
这将启动nginx
容器的三个副本。列出 pod 以获取状态:
$ ./cluster/kubectl.sh get pods
- 获取复制控制器配置:
$ ./cluster/kubectl.sh get replicationControllers
如您所见,我们有一个my-nginx
控制器,其副本计数为 3。还有一个kube-dns
的复制控制器,我们将在下一个配方中探索。
- 请求复制控制器服务将副本缩减为 1 并更新复制控制器:
$ ./cluster/kubectl.sh resize rc my-nginx –replicas=1
$ ./cluster/kubectl.sh get rc
- 获取 pod 列表以进行验证;您应该只看到一个
nginx
的 pod:
$ ./cluster/kubectl.sh get pods
工作原理…
我们请求在主节点上运行的复制控制器服务更新 pod 的副本,这将更新配置并要求节点/从节点相应地进行调整以遵守调整大小。
还有更多…
获取服务:
$ ./cluster/kubectl.sh get services
正如你所看到的,我们之前启动的nginx
容器没有定义任何服务。这意味着虽然我们有一个正在运行的容器,但我们无法从外部访问它们,因为相应的服务没有定义。
另请参阅
-
在
github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/getting-started-guides/vagrant.md
设置 Vagrant 环境 -
github.com/GoogleCloudPlatform/kubernetes/blob/master/docs/user-guide.md
中的 Kubernetes 用户指南
在 Kubernetes 集群中设置 WordPress
在这个教程中,我们将使用 Kubernetes GitHub 中提供的 WordPress 示例(github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/mysql-wordpress-pd
)。给定的示例需要一些更改,因为我们将在 Vagrant 环境中运行它,而不是默认的 Google Compute 引擎。此外,我们将登录到 master 并使用kubectl
二进制文件,而不是使用辅助函数(例如,<kubernetes>/cluster/kubectl.sh
)。
准备工作
-
确保 Kubernetes 集群已按照上一个教程中描述的那样设置。
-
在安装过程中下载的
kubernetes
目录中,您将找到一个包含许多示例的 examples 目录。让我们转到mysql-wordpress-pd
目录:
$ cd kubernetes/examples/mysql-wordpress-pd
$ ls *.yaml
mysql-service.yaml mysql.yaml wordpress-service.yaml wordpress.yaml
-
这些
.yaml
文件分别描述了mysql
和wordpress
的 pod 和服务。 -
在 pod 文件(
mysql.yaml
和wordpress.yaml
)中,您将找到关于卷和相应volumeMount
文件的部分。原始示例假定您可以访问 Google Compute Engine 并且已经设置了相应的存储。为简单起见,我们将不设置它,而是使用EmptyDir
卷选项的临时存储。供参考,我们的mysql.yaml
将如下所示: -
对
wordpress.yaml
进行类似的更改。
操作步骤…
- 通过 SSH 登录到 master 节点并查看正在运行的 pod:
$ vagrant ssh master
$ kubectl get pods
kube-dns-7eqp5
pod 包含三个容器:etcd
、kube2sky
和skydns
,用于配置内部 DNS 服务器以进行服务名到 IP 的解析。我们稍后会在这个示例中看到它的运行。
在这个示例中使用的 Vagrantfile 是这样创建的,我们之前创建的kubernetes
目录在 VM 下被共享为/vagrant
,这意味着我们对主机系统所做的更改也会在这里可见。
- 从主节点创建
mysql
pod 并检查运行中的 pod:
$ kubectl create -f /vagrant/examples/mysql-wordpress-pd/mysql.yaml
$ kubectl get pods
我们可以看到,一个名为mysql
的新 pod 已经被创建,并且正在运行在主机10.245.1.3
上,这是我们的节点(minion)。
- 现在让我们为
mysql
创建服务并查看所有服务:
$ kubectl create -f /vagrant/examples/mysql-wordpress-pd/mysql-service.yaml
$ kubectl get services
我们可以看到,一个名为mysql
的服务已经被创建。每个服务都有一个虚拟 IP。除了kubernetes
服务,我们还看到一个名为kube-dns
的服务,它被用作我们之前看到的kube-dns
pod 的服务名。
- 类似于
mysql
,让我们为wordpress
创建一个 pod:
$ kubectl create -f /vagrant/examples/mysql-wordpress-pd/wordpress.yaml
使用这个命令,后台会发生一些事情:
-
wordpress
镜像从官方 Docker 注册表中下载并运行容器。 -
默认情况下,每当一个 pod 启动时,所有现有服务的信息都会导出为环境变量。例如,如果我们登录到
wordpress
pod 并查找MYSQL
特定的环境变量,我们会看到类似以下的内容: -
当 WordPress 容器启动时,它运行
/entrypoint.sh
脚本,该脚本查找之前提到的环境变量来启动服务。github.com/docker-library/wordpress/blob/master/docker-entrypoint.sh
。 -
通过
kube-dns
服务,wordpress
的 PHP 脚本能够进行反向查找以继续向前进行。
- 启动 pod 后,这里的最后一步是设置
wordpress
服务。在默认示例中,你会在服务文件(/vagrant/examples/mysql-wordpress-pd/mysql-service.yaml)
中看到类似以下的条目:
createExternalLoadBalancer: true
这篇文章是为了记住这个示例将在 Google Compute Engine 上运行。所以这里不适用。我们需要做的是像下面这样做一个条目:
publicIPs:
- 10.245.1.3
我们用节点的公共 IP 替换了负载均衡器的条目,这在我们的情况下就是节点(minion)的 IP 地址。因此,wordpress
文件看起来会像下面这样:
- 要启动
wordpress
服务,请从主节点上运行以下命令:
$ kubectl create -f /vagrant/examples/mysql-wordpress-pd/wordpress-service.yaml
我们可以看到我们的 service 也可以通过节点(minion)IP 访问。
- 要验证一切是否正常工作,我们可以在主节点上安装 links 软件包,通过它我们可以通过命令行浏览 URL 并连接到我们提到的公共 IP:
$ sudo yum install links -y
$ links 10.245.1.3
有了这些,你应该能看到wordpress
安装页面。
工作原理...
在这个示例中,我们首先创建了一个mysql
的 pod 和 service。之后,我们将它连接到了一个wordpress
的 pod,并且为了访问它,我们创建了一个wordpress
的 service。每个 YAML 文件都有一个kind
键,用来定义它是什么类型的对象。例如,在 pod 文件中,kind
被设置为 pod,在 service 文件中,被设置为 service。
还有更多...
- 在这个示例设置中,我们只有一个节点(minion)。如果你登录进去,你会看到所有正在运行的容器:
$ vagrant ssh minion-1
$ sudo docker ps
- 在这个示例中,我们没有配置复制控制器。我们可以通过创建它们来扩展这个示例。
另请参阅
-
在kube-dns上的文档
第九章:Docker 安全
在本章中,我们将介绍以下配方:
-
使用 SELinux 设置强制访问控制(MAC)
-
允许在启用 SELinux 的情况下对从主机挂载的卷进行写入
-
删除能力以降低容器内部 root 用户的权限
-
在主机和容器之间共享命名空间
介绍
Docker 容器实际上并不是沙箱应用程序,这意味着不建议在系统上以 root 身份运行随机应用程序。您应该始终将运行服务/进程的容器视为在主机系统上运行的服务/进程,并在容器内部放置在主机系统上放置的所有安全措施。
我们在第一章中看到,介绍和安装,Docker 如何使用命名空间进行隔离。Docker 使用的六个命名空间是进程、网络、挂载、主机名、共享内存和用户。在 Linux 中,并非所有内容都有命名空间,例如 SELinux、Cgroups、设备(/dev/mem
、/dev/sd*
)和内核模块。文件系统下的/sys
、/proc/sys
、/proc/sysrq-trigger
、/proc/irq
、/proc/bus
也没有命名空间,但它们默认以只读方式挂载,使用 libcontainer 执行驱动程序。
为了使 Docker 成为一个安全的环境,最近已经做了很多工作,还有更多的工作正在进行中。
- 由于 Docker 镜像是基本构建块,因此非常重要的是我们选择正确的基础镜像开始。Docker 有官方镜像的概念,这些镜像由 Docker、供应商或其他人维护。如果您还记得第二章中的内容,使用 Docker 容器,我们可以使用以下语法在 Docker Hub 上搜索镜像:
$ docker search <image name>
例如,考虑以下命令:
$ docker search fedora
我们将看到一个OFFICIAL
列,如果镜像是官方的,你将在该列中看到对应的[OK]
。在 Docker 1.3 中添加了一个实验性功能(blog.docker.com/2014/10/docker-1-3-signed-images-process-injection-security-options-mac-shared-directories/
),它在拉取镜像后对官方镜像进行数字信号验证。如果镜像被篡改,用户将收到通知,但不会阻止用户运行它。目前,此功能仅适用于官方镜像。有关官方镜像的更多详细信息,请访问github.com/docker-library/official-images
。镜像签名和验证功能尚未准备就绪,因此目前不要完全依赖它。
-
在第六章中,Docker API 和语言绑定,我们看到了当 Docker 守护程序访问通过 TCP 配置时,我们如何可以保护 Docker 远程 API。
-
我们还可以考虑在 Docker 主机上使用
--icc=false
关闭默认的容器间网络通信。尽管容器仍然可以通过链接进行通信,覆盖 iptables 的默认 DROP 策略,但它们会使用--icc=false
选项进行设置。 -
我们还可以通过 Cgroups 资源限制来防止拒绝服务(DoS)攻击通过系统资源约束。
-
Docker 利用了特殊设备 Cgroups,允许我们指定容器内可以使用哪些设备节点。它阻止进程创建和使用可能用于攻击主机的设备节点。
-
在镜像上预先创建的任何设备节点都不能用于与内核通信,因为镜像是以
nodev
选项挂载的。
以下是一些指南(可能不完整),可以遵循以确保安全的 Docker 环境:
-
以非 root 用户身份运行服务,并将容器内外的 root 视为 root。
-
使用来自可信方的镜像来运行容器;避免使用
-insecure-registry=[]
选项。 -
不要从 Docker 注册表或其他任何地方运行随机容器。Red Hat 携带了补丁,以添加和阻止注册表,以便企业有更多控制权(
rhelblog.redhat.com/2015/04/15/understanding-the-changes-to-docker-search-and-docker-pull-in-red-hat-enterprise-linux-7-1/
)。 -
确保您的主机内核是最新的。
-
尽量避免使用
--privileged
,并尽快放弃容器特权。 -
通过 SELinux 或 AppArmor 配置强制访问控制(MAC)。
-
收集日志进行审计。
-
定期进行审计。
-
在专门设计仅运行容器的主机上运行容器。考虑使用 Project Atomic、CoreOS 或类似的解决方案。
-
使用
--device
选项挂载设备,而不是使用--privileged
选项在容器内使用设备。 -
禁止容器内的 SUID 和 SGID。
最近,Docker 和互联网安全中心(www.cisecurity.org/
)发布了 Docker 安全最佳实践指南,涵盖了大部分前述指南以及更多指南,网址为blog.docker.com/2015/05/understanding-docker-security-and-best-practices/
。
为了为本章中的一些示例设置上下文,让我们尝试在安装了 Docker 的 Fedora 21 上进行默认安装的实验。
- 使用以下命令禁用 SELinux:
$ sudo setenforce 0
- 创建一个用户并将其添加到默认的 Docker 组中,以便用户可以在不使用
sudo
的情况下运行 Docker 命令:
$ sudo useradd dockertest
$ sudo passwd dockertest
$ sudo groupadd docker
$ sudo gpasswd -a dockertest docker
- 使用我们之前创建的用户登录,启动容器如下:
$ su - dockertest
$ docker run -it -v /:/host fedora bash
- 从容器 chroot 到
/host
并运行shutdown
命令:
$ chroot /host
$ shutdown
正如我们所看到的,Docker 组中的用户可以关闭主机系统。Docker 目前没有授权控制,因此如果您可以与 Docker 套接字通信,就可以运行任何 Docker 命令。这类似于/etc/sudoers
。
USERNAME ALL=(ALL) NOPASSWD: ALL
这真的不好。让我们看看在本章的其余部分中如何防范这种情况以及更多内容。
使用 SELinux 设置强制访问控制(MAC)
建议在 Docker 主机上设置某种形式的 MAC,可以是通过 SELinux 或 AppArmor,具体取决于 Linux 发行版。在本教程中,我们将看到如何在安装了 Fedora/RHEL/CentOS 的系统上设置 SELinux。让我们首先看看 SELinux 是什么:
-
SELinux 是一个标签系统
-
每个进程都有一个标签
-
每个文件、目录和系统对象都有一个标签
-
策略规则控制标记进程和标记对象之间的访问
-
内核执行规则
在 Docker 容器中,我们使用两种类型的 SELinux 强制:
-
类型强制:用于保护主机系统免受容器进程的影响。每个容器进程都被标记为
svirt_lxc_net_t
,每个容器文件都被标记为svirt_sandbox_file_t
。svirt_lxc_net_t
类型被允许管理任何标记为svirt_sandbox_file_t
的内容。容器进程只能访问/写入容器文件。 -
多类别安全强制:通过设置类型强制,所有容器进程将以
svirt_lxc_net_t
标签运行,所有内容都将被标记为svirt_sandbox_file_t
。然而,只有这些设置,我们无法保护一个容器免受另一个容器的影响,因为它们的标签是相同的。
我们使用多类别安全(MCS)强制来保护一个容器免受另一个容器的影响,这是基于多级安全(MLS)的。当启动一个容器时,Docker 守护程序会选择一个随机的 MCS 标签,例如s0:c41,c717
,并将其保存在容器元数据中。当任何容器进程启动时,Docker 守护程序告诉内核应用正确的 MCS 标签。由于 MCS 标签保存在元数据中,如果容器重新启动,它将获得相同的 MCS 标签。
准备就绪
在安装了最新版本的 Docker 的 Fedora/RHEL/CentOS 主机上,可以通过 Docker 客户端访问。
如何做到这一点...
Fedora/RHEL/CentOS 默认安装 SELinux 并设置为强制模式,并且 Docker 守护程序设置为与 SELinux 一起启动。要检查是否满足这些条件,请执行以下步骤。
- 运行以下命令以确保 SELinux 已启用:
$ getenforce
如果前面的命令返回enforcing
,那就很好,否则我们需要通过更新 SELinux 配置文件(/etc/selinux/config
)并重新启动系统来进行更改。
- Docker 应该使用
--selinux-enabled
选项运行。您可以在 Docker 守护程序配置(/etc/sysconfig/docker
)文件的OPTIONS
部分中检查。还要交叉检查 Docker 服务是否已启动并使用 SELinux 选项:
$ systemctl status docker
上述命令假定您没有手动以守护程序模式启动 Docker。
让我们在挂载主机目录作为卷后启动容器(不使用特权选项),并尝试在其中创建一个文件:
如预期的那样,我们看到Permission denied
,因为具有svirt_lxc_net_t
标签的容器进程无法在主机文件系统上创建文件。如果我们查看主机上的 SELinux 日志(/var/log/audit.log
),我们将看到类似以下的消息:
s0:c157,c350
标签是容器上的 MCS 标签。
它是如何工作的...
当为 SELinux 和 Docker 设置正确的选项时,SELinux 设置了类型和多类别安全强制执行。Linux 内核执行这些强制执行。
还有更多...
-
如果 SELinux 处于强制执行模式,并且 Docker 守护程序配置为使用 SELinux,则我们将无法像在本章前面那样从容器关闭主机:
-
我们知道,默认情况下,所有容器都将以
svirt_lxc_net_t
标签运行,但我们也可以根据自定义要求调整 SELinux 标签。访问opensource.com/business/15/3/docker-security-tuning
的调整 SELinux 标签部分。 -
使用 Docker 容器设置 MLS 也是可能的。访问
opensource.com/business/15/3/docker-security-tuning
的多级安全模式部分。
另请参阅
允许从启用 SELinux 的主机挂载的卷进行写入
正如我们在前面的示例中看到的,当 SELinux 配置时,非特权容器无法访问从主机系统挂载目录创建的卷上的文件。但是,有时需要允许容器从主机文件访问。在本示例中,我们将看到如何在这种情况下允许访问。
准备工作
安装了最新版本的 Docker 的 Fedora/RHEL/CentOS 主机,可以通过 Docker 客户端访问。此外,SELinux 设置为强制模式,并且 Docker 守护程序配置为使用 SELinux。
如何做...
- 使用以下方式使用
z
或Z
选项挂载卷:
$ docker run -it -v /tmp/:/tmp/host:z docker.io/fedora bash
$ docker run -it -v /tmp/:/tmp/host:Z docker.io/fedora bash
它是如何工作的...
在挂载卷时,Docker 将重新标记卷以允许访问。来自 Docker 运行的 man 页面。
z
选项告诉 Docker 卷内容将在容器之间共享。Docker 将使用共享内容标签标记内容。共享卷标签允许所有容器读/写内容。Z
选项告诉 Docker 使用私有未共享标签标记内容。私有卷只能被当前容器使用。
另请参阅
在容器内取消功能以分解根用户的权限
简而言之,通过功能,我们可以分解根用户的权限。来自功能的 man 页面:
为了进行权限检查,传统的 UNIX 实现区分两类进程:特权进程(有效用户 ID 为 0,称为超级用户或根用户)和非特权进程(有效 UID 为非零)。特权进程绕过所有内核权限检查,而非特权进程则根据进程的凭据(通常为:有效 UID、有效 GID 和附加组列表)进行完整的权限检查。
从 2.2 内核开始,Linux 将传统上与超级用户关联的特权分为不同的单元,称为功能,可以独立启用和禁用。功能是每个线程的属性。
一些示例功能包括:
-
CAP_SYSLOG
:这修改了内核的打印行为 -
CAP_NET_ADMIN
:这配置网络 -
CAP_SYS_ADMIN
:这有助于捕获所有功能
内核中只有 32 个功能插槽可用。有一个功能CAP_SYS_ADMIN
,它捕获所有功能;每当有疑问时使用。
在 1.2 版本中,Docker 添加了一些功能,用于为容器添加或删除功能。它默认使用chown
、dac_override
、fowner
、kill
、setgid
、setuid
、setpcap
、net_bind_service
、net_raw
、sys_chroot
、mknod
、setfcap
和audit_write
功能,并默认删除容器的以下功能。
-
CAP_SETPCAP
: 这修改进程功能 -
CAP_SYS_MODULE
: 这插入/删除内核模块 -
CAP_SYS_RAWIO
: 这修改内核内存 -
CAP_SYS_PACCT
: 这配置进程记账 -
CAP_SYS_NICE
: 这修改进程的优先级 -
CAP_SYS_RESOURCE
: 这覆盖资源限制 -
CAP_SYS_TIME
: 这修改系统时钟 -
CAP_SYS_TTY_CONFIG
: 这配置tty
设备 -
CAP_AUDIT_WRITE
: 这写入审计日志 -
CAP_AUDIT_CONTROL
: 这配置审计子系统 -
CAP_MAC_OVERRIDE
: 这忽略内核 MAC 策略 -
CAP_MAC_ADMIN
: 这配置 MAC 配置 -
CAP_SYSLOG
: 这修改内核 printk 行为 -
CAP_NET_ADMIN
: 这配置网络 -
CAP_SYS_ADMIN
: 这有助于捕获所有容器
我们需要非常小心地删除功能,因为如果应用程序没有足够的功能来运行,可能会出现问题。要为容器添加和删除功能,可以分别使用--cap-add
和--cap-drop
选项。
准备工作
安装了最新版本的 Docker 的主机,可以通过 Docker 客户端访问。
如何做…
- 要删除功能,运行类似以下命令:
$ docker run --cap-drop <CAPABILITY> <image> <command>
要从容器中删除setuid
和setgid
功能,以便它无法运行具有这些位设置的二进制文件,运行以下命令:
$ docker run -it --cap-drop setuid --cap-drop setgid fedora bash
- 同样,要添加功能,运行类似以下命令:
$ docker run --cap-add <CAPABILITY> <image> <command>
要添加所有功能并仅删除sys-admin
,运行以下命令:
$ docker run -it --cap-add all --cap-drop sys-admin fedora bash
它是如何工作的…
在启动容器之前,Docker 为容器内的根用户设置功能,这会影响容器进程的命令执行。
还有更多...
让我们重新访问我们在本章开头看到的例子,通过它我们看到主机系统通过容器关闭。让 SELinux 在主机系统上禁用;但是,在启动容器时,删除sys_choot
功能:
$ docker run -it --cap-drop sys_chroot -v /:/host fedora bash
$ shutdown
另请参阅
-
Dan Walsh 在
opensource.com/business/14/9/security-for-docker
上的文章。 -
blog.docker.com/2014/08/announcing-docker-1-2-0/
上的 Docker 1.2 发布说明。 -
有努力在进行中,以有选择地禁用容器进程的系统调用,以提供更严格的安全性。访问
opensource.com/business/15/3/docker-security-future
的Seccomp部分。 -
与版本 1.6 中的自定义命名空间和功能类似,Docker 支持
--cgroup-parent
标志,以传递特定的 Cgroup 来运行容器。docs.docker.com/v1.6/release-notes/
。
在主机和容器之间共享命名空间
我们知道,在启动容器时,默认情况下,Docker 为容器创建六个不同的命名空间——进程、网络、挂载、主机名、共享内存和用户。在某些情况下,我们可能希望在两个或更多的容器之间共享命名空间。例如,在 Kubernetes 中,一个 pod 中的所有容器共享相同的网络命名空间。
在某些情况下,我们希望与容器共享主机系统的命名空间。例如,我们在主机和容器之间共享相同的网络命名空间,以在容器内获得接近线速。在这个教程中,我们将看到如何在主机和容器之间共享命名空间。
准备工作
安装了最新版本的 Docker 的主机,可以通过 Docker 客户端访问。
如何做…
- 要与容器共享主机网络命名空间,请运行以下命令:
$ docker run -it --net=host fedora bash
如果要在容器内查看网络详细信息,请运行以下命令:
$ ip a
您将看到与主机相同的结果。
- 要与容器共享主机网络、PID 和 IPC 命名空间,请运行以下命令:
$ docker run -it --net=host --pid=host --ipc=host fedora bash
它是如何工作的…
当传递这些参数给容器时,Docker 不会为容器创建单独的命名空间。
还有更多...
对于专门用于运行容器的主机,比如 Project Atomic (www.projectatomic.io/
),我们在第八章中看到的Docker 编排和托管平台,在主机系统上没有像tcpdump
和sysstat
这样的调试工具。因此,我们创建了带有这些工具并可以访问主机资源的容器。在这种情况下,在主机和容器之间共享命名空间变得很方便。您可以在以下链接中了解更多信息:
-
developerblog.redhat.com/2014/11/06/introducing-a-super-privileged-container-concept/
-
developerblog.redhat.com/2015/03/11/introducing-the-rhel-container-for-rhel-atomic-host/
另请参阅
- 丹·沃尔什在
opensource.com/business/15/3/docker-security-tuning
上的 Docker 安全文档
第十章:获取帮助和技巧
在本章中,我们将看到以下配方:
-
以调试模式启动 Docker
-
从源代码构建 Docker 二进制文件
-
构建图像而不使用缓存层
-
为容器通信构建自己的桥接
-
更改 Docker 的默认执行驱动程序
-
为容器选择日志记录驱动程序
-
获取容器的实时 Docker 事件
介绍
随着我们对 Docker 的了解越来越多,我们会变得更加好奇。邮件列表和 IRC 频道是获取帮助、学习和分享关于 Docker 知识的最佳场所。Docker 在免费节点上有一些 IRC 频道,如#docker
和#docker-dev
,分别用于讨论 Docker 和与开发相关的内容。同样,Docker 有两个邮件列表:
-
Docker 用户列表可在
groups.google.com/forum/#!forum/docker-user
找到 -
Docker 开发人员列表可在
groups.google.com/forum/#!forum/docker-dev
找到
在使用 Docker 时,如果发现任何错误,可以在 GitHub 上报告它们,网址为github.com/docker/docker/issues
。
同样,如果您修复了一个错误,可以发送拉取请求,该请求将得到审查,然后合并到代码库中。
Docker 还有一个论坛和一个 YouTube 频道,它们是很好的学习资源,分别可以在forums.docker.com/
和www.youtube.com/user/dockerrun
找到。
在世界各地有许多 Docker 见面小组,您可以在www.docker.com/community/meetups/
上与志同道合的人见面,并通过分享经验来学习。
在本章中,我还将提供一些技巧和窍门,这将帮助您更好地使用 Docker。
以调试模式启动 Docker
我们可以以调试模式启动 Docker 来调试日志。
准备就绪
在系统上安装 Docker。
如何做…
- 使用调试选项
-D
启动 Docker 守护进程。要从命令行启动,可以运行以下命令:
$ docker -d -D
- 您还可以在 Docker 配置文件中添加
--debug/-D
选项以以调试模式启动。
它是如何工作的…
上述命令将以守护程序模式启动 Docker。当您启动守护程序时,您将看到许多有用的消息,例如加载现有图像,防火墙设置(iptables)等。如果启动容器,您将看到以下消息:
[info] POST /v1.15/containers/create
[99430521] +job create()
......
......
从源代码构建 Docker 二进制文件
有时需要从源代码构建 Docker 二进制文件以测试补丁。从源代码构建 Docker 二进制文件非常容易。
准备工作
- 使用
git
下载 Docker 源代码:
$ git clone https://github.com/docker/docker.git
- 在 Fedora 上安装
make
:
$ yum install -y make
- 确保 Docker 在构建代码的主机上运行,并且您可以通过 Docker 客户端访问它,因为我们讨论的构建发生在容器内。
如何做…
- 进入克隆的目录:
$ cd docker
- 运行
make
命令:
$ sudo make
工作原理…
这将创建一个容器,并在其中从主分支编译代码。完成后,它将在bundles/<version>/binary
中输出二进制文件。
还有更多…
- 与源代码类似,您也可以构建文档:
$ sudo make docs
- 您还可以使用以下命令运行测试:
$ sudo make test
另请参阅
- 查看 Docker 网站上的文档
docs.docker.com/contributing/devenvironment/
构建图像而不使用缓存层
默认情况下,当我们构建图像时,Docker 将尝试使用缓存的层,以便构建时间更短。但是,有时需要从头开始构建。例如,您需要强制进行系统更新,例如yum -y update
。让我们看看如何在这个示例中做到这一点。
准备工作
获取一个 Dockerfile 来构建镜像。
如何做…
- 构建镜像时,通过以下方式传递
--no-cache
选项:
$ docker build -t test --no-cache - < Dockerfile
工作原理…
--no-cache
选项将丢弃任何缓存的层,并根据指令构建一个 Dockerfile。
还有更多…
有时,我们还想在仅执行几条指令后丢弃缓存。在这种情况下,我们可以添加任何不影响图像的任意命令,例如创建或设置环境变量。
为容器通信构建自己的桥接
我们知道,当 Docker 守护程序启动时,它会创建一个名为docker0
的桥接,并且所有容器都将从中获取 IP。有时我们可能想要自定义这些设置。让我们看看如何在这个示例中做到这一点。
准备工作
我假设您已经设置好了 Docker。在 Docker 主机上,停止 Docker 守护程序。在 Fedora 上,使用以下命令:
$ systemctl stop docker
如何做…
- 要删除默认的
docker0
桥接,请使用以下命令:
$ sudo ip link set dev docker0 down
$ sudo brctl delbr docker0
- 要创建自定义桥接,请使用以下命令:
$ sudo brctl addbr br0
$ sudo ip addr add 192.168.2.1/24 dev br0
$ sudo ip link set dev bridge0 up
- 更新 Docker 配置文件以使用我们之前创建的桥接。在 Fedora 上,您可以按以下方式更新配置文件:
$ sed -i '/^OPTIONS/ s/$/ --bridge br0/' /etc/sysconfig/docker
- 要启动 Docker 守护程序,请使用以下命令:
$ systemctl start docker
工作原理…
上述步骤将创建一个新的桥接,并将从 192.168.2.0 子网中为容器分配 IP。
还有更多…
您甚至可以向桥接添加接口。
另请参阅
- Docker 网站上的文档
docs.docker.com/articles/networking/
更改 Docker 的默认执行驱动程序
正如我们所知,libcontainer 是默认的执行驱动程序。对于 LXC 用户空间工具(linuxcontainers.org/
)有传统支持。请记住,LXC 不是主要的开发环境。
准备工作
在系统上安装 Docker。
如何做…
- 以以下方式启动 Docker 守护程序,使用
-e lxc
选项:
$ docker -d -e lxc
您还可以根据发行版在 Docker 的配置文件中添加此选项。
工作原理…
Docker 使用 LXC 工具访问内核功能,如命名空间和 Cgroups 来运行容器。
另请参阅
为容器选择日志驱动程序
随着 Docker 1.6 的发布,新增了一个功能,可以在启动 Docker 守护程序时选择日志驱动程序。目前支持三种类型的日志驱动程序:
-
none
-
json-file(默认)
-
syslog
准备工作
在系统上安装 Docker 1.6 或更高版本。
如何做…
- 以以下方式启动 Docker 守护程序,使用所需的日志驱动程序:
$ docker -d --log-driver=none
$ docker -d --log-driver=syslog
您还可以根据发行版在 Docker 的配置文件中添加此选项。
docker logs
命令将仅支持默认的日志驱动程序 JSON 文件。
工作原理…
根据日志驱动程序配置,Docker 守护程序选择相应的日志驱动程序。
还有更多…
正在进行工作,将journald
添加为日志驱动程序之一。它将在 Docker 1.7 中可用www.projectatomic.io/blog/2015/04/logging-docker-container-output-to-journald/
。
另请参阅
获取容器的实时 Docker 事件
由于我们将在生产中运行许多容器,如果我们可以观察实时容器事件以进行监视和调试,将会很有帮助。Docker 容器可以报告事件,例如创建、销毁、死亡、导出、杀死、oom、暂停、重启、启动、停止和取消暂停。在这个教程中,我们将看到如何启用事件日志记录,然后使用过滤器选择特定的事件类型、镜像或容器。
准备工作
确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端连接。
如何做…
- 使用以下命令启动 Docker 事件日志记录:
$ docker events
- 从另一个终端执行一些与容器/镜像相关的操作,您将在第一个终端上看到类似以下截图的结果:
在事件收集开始后,我创建了一个容器来打印一些东西。如前面的截图所示,一个容器被创建、启动和死亡。
工作原理…
使用 Docker 事件,Docker 开始列出不同的事件。
还有更多…
- 您可以使用
--since
或--until
选项与 Docker 事件,以缩小所选时间戳的结果:
--since="" Show all events created since timestamp
--until="" Stream events until this timestamp
考虑以下示例:
$ docker events --since '2015-01-01'
-
使用过滤器,我们可以根据事件、容器和镜像进一步缩小事件日志,如下所示:
-
要仅列出启动事件,请使用以下命令:
$ docker events --filter 'event=start'
- 要仅列出来自 CentOS 镜像的事件,请使用以下命令:
$ docker events --filter 'image=docker.io/centos:centos7'
- 要列出特定容器的事件,请使用以下命令:
docker events --filter 'container=b3619441cb444b87b4d79a8c30616ca70da4b5aa8fdc5d8a48d23a2082052174'
另请参阅
- Docker 网站上的文档
docs.docker.com/reference/commandline/cli/#events
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2023-05-06 ChatGPT 调教日记(一):Markdown 解析器