Docker-秘籍(全)

Docker 秘籍(全)

原文:zh.annas-archive.org/md5/3BDF7E02FD45D3E3DF6846ABA9F12FB8

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

使用 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 部门管理,并且给开发人员提供了更少的灵活性。在这种情况下,硬件资源经常被低效利用。

以下图表描述了这样的设置:

Introduction

传统应用程序部署(rhsummit.files.wordpress.com/2014/04/rhsummit2014-application-centric_packaging_with_docker_and_linux_containers-20140412riek7.pdf

为了克服传统部署设置的限制,虚拟化被发明了。使用诸如 KVM、XEN、ESX、Hyper-V 等的 hypervisor,我们模拟了虚拟机(VM)的硬件,并在每个虚拟机上部署了一个客户操作系统。VM 可以具有与其主机不同的操作系统;这意味着我们负责管理该 VM 的补丁、安全性和性能。通过虚拟化,应用程序在 VM 级别上被隔离,并由 VM 的生命周期定义。这在投资回报和灵活性方面提供了更好的回报,但增加了复杂性和冗余成本。以下图表描述了典型的虚拟化环境:

Introduction

在虚拟化环境中的应用程序部署(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。与虚拟机相比,容器也被认为不太安全,因为在容器中,一切都在主机操作系统上运行。如果容器受到损害,那么可能会完全访问主机操作系统。设置、管理和自动化可能会变得有点复杂。这些是我们在过去几年中没有看到容器大规模采用的一些原因,尽管我们已经有了这项技术。

Introduction

使用容器进行应用部署(rhsummit.files.wordpress.com/2014/04/rhsummit2014-application-centric_packaging_with_docker_and_linux_containers-20140412riek7.pdf

有了 Docker,容器突然成为了一等公民。所有大公司,如 Google,Microsoft,Red Hat,IBM 等,现在都在努力使容器成为主流。

Docker 是由 Solomon Hykes 在 dotCloud 内部项目启动的,他目前是 Docker,Inc.的首席技术官。它于 2013 年 3 月以 Apache 2.0 许可证的形式开源发布。通过 dotCloud 的平台即服务经验,Docker 的创始人和工程师们意识到了运行容器的挑战。因此,他们开发了一种管理容器的标准方式。

Docker 使用了 Linux 的底层内核功能来实现容器化。以下图表描述了 Docker 使用的执行驱动程序和内核功能。我们稍后会讨论执行驱动程序。让我们先看一些 Docker 使用的主要内核功能:

Introduction

Docker 使用的执行驱动程序和内核功能(blog.docker.com/wp-content/uploads/2014/03/docker-execdriver-diagram.png

命名空间

命名空间是容器的构建模块。有不同类型的命名空间,每个命名空间都将应用程序相互隔离。它们是使用克隆系统调用创建的。也可以附加到现有的命名空间。Docker 使用的一些命名空间在以下部分进行了解释。

pid 命名空间

pid命名空间允许每个容器拥有自己的进程编号。每个pid形成自己的进程层次结构。父命名空间可以看到子命名空间并影响它们,但子命名空间既不能看到父命名空间也不能影响它。

如果有两个层次结构,那么在顶层,我们将看到在子命名空间中运行的进程具有不同的 PID。因此,在子命名空间中运行的进程将具有两个 PID:一个在子命名空间中,另一个在父命名空间中。例如,如果我们在容器上运行一个程序(container.sh),那么我们也可以在主机上看到相应的程序。

在容器内:

pid 命名空间

在主机上:

pid 命名空间

net 命名空间

有了pid命名空间,我们可以在不同的隔离环境中多次运行相同的程序;例如,我们可以在不同的容器上运行 Apache 的不同实例。但是没有net命名空间,我们将无法在每个容器上监听端口 80。net命名空间允许我们在每个容器上拥有不同的网络接口,从而解决了我之前提到的问题。回环接口在每个容器中也会有所不同。

要在容器中启用网络,我们可以在两个不同的net命名空间中创建一对特殊接口,并允许它们彼此通信。特殊接口的一端位于容器内,另一端位于主机系统中。通常,容器内的接口被命名为eth0,在主机系统中,它被赋予一个随机名称,如vethcf1a。然后,通过主机上的桥接器(docker0)将这些特殊接口连接起来,以实现容器之间的通信和数据包路由。

在容器内,你会看到类似以下的东西:

net 命名空间

在主机上,它看起来像是这样:

net 命名空间

此外,每个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

Cgroups

虽然我们还没有看实际的命令,但让我们假设我们正在运行一些容器,并且想要获取容器的 cgroup 条目。要获取这些条目,我们首先需要获取容器 ID,然后使用lscgroup命令获取容器的 cgroup 条目,可以从以下命令中获取:

Cgroups

注意

有关更多详细信息,请访问docs.docker.com/articles/runmetrics/

联合文件系统

联合文件系统允许透明地叠加分开的文件系统(称为层)的文件和目录,以创建一个新的虚拟文件系统。在启动容器时,Docker 叠加附加到图像的所有层,并创建一个只读文件系统。在此基础上,Docker 创建一个读/写层,容器的运行时环境使用它。有关更多详细信息,请参阅本章的拉取图像并运行容器部分。Docker 可以使用多种联合文件系统变体,包括 AUFS、Btrfs、vfs 和 DeviceMapper。

Docker 可以与不同的执行驱动程序一起工作,例如libcontainerlxclibvirt来管理容器。默认的执行驱动程序是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 的系统。

如何做…

执行以下步骤:

  1. Docker 不支持 32 位架构。要检查系统架构,请运行以下命令:
$ uname -i
x86_64

  1. Docker 支持内核 3.8 或更高版本。它已经被后移至一些内核 2.6,如 RHEL 6.5 及以上版本。要检查内核版本,请运行以下命令:
$ uname -r
3.18.7-200.fc21.x86_64

  1. 运行的内核应支持适当的存储后端。其中一些是 VFS、DeviceMapper、AUFS、Btrfs 和 OverlayFS。

大多数情况下,默认的存储后端或驱动程序是 devicemapper,它使用设备映射器薄配置模块来实现层。它应该默认安装在大多数 Linux 平台上。要检查设备映射器,您可以运行以下命令:

$ grep device-mapper /proc/devices
253 device-mapper

在大多数发行版中,AUFS 需要一个修改过的内核。

  1. 对于 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

由于有许多发行版支持 Docker,我们将在这篇文章中只看一下 Fedora 21 上的安装步骤。对于其他发行版,您可以参考本文的另请参阅部分中提到的安装说明。使用 Docker Machine,我们可以在本地系统、云提供商和其他环境上轻松设置 Docker 主机。我们将在另一篇文章中介绍这个。

准备工作

检查前面一篇文章中提到的先决条件。

如何做…

  1. 使用 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 架构及其组件。

准备工作

获取安装了 Docker 的系统访问权限。

如何做到...

  1. 要拉取一个镜像,请运行以下命令:
$ docker pull fedora

  1. 使用以下命令列出现有的镜像:
$ docker images

如何做到...

  1. 使用拉取的镜像创建一个容器,并列出容器为:如何做到...

它是如何工作的...

Docker 具有客户端-服务器架构。其二进制文件包括 Docker 客户端和服务器守护程序,并且可以驻留在同一台主机上。客户端可以通过套接字或 RESTful API 与本地或远程 Docker 守护程序通信。Docker 守护程序构建、运行和分发容器。如下图所示,Docker 客户端将命令发送到运行在主机上的 Docker 守护程序。Docker 守护程序还连接到公共或本地索引,以获取客户端请求的镜像:

它是如何工作的...

Docker 客户端-服务器架构 (docs.docker.com/introduction/understanding-docker/)

因此,在我们的情况下,Docker 客户端向在本地系统上运行的守护程序发送请求,然后守护程序连接到公共 Docker 索引并下载镜像。一旦下载完成,我们就可以运行它。

还有更多...

让我们探索一些我们在这个配方中遇到的关键词:

  • 图像:Docker 图像是只读模板,在运行时它们为我们提供容器。有一个基本图像和在其上的层的概念。例如,我们可以有一个基本图像的 Fedora 或 Ubuntu,然后我们可以安装软件包或对基本图像进行修改以创建一个新的层。基本图像和新层可以被视为一个新的图像。例如,在下图中,Debian是基本图像,emacsApache是添加在其上的两个层。它们非常易于移植,并且可以轻松共享:更多信息...

Docker 图像层(docs.docker.com/terms/images/docker-filesystems-multilayer.png)

层被透明地放在基本图像的顶部,以创建一个统一的文件系统。

  • 注册表:注册表保存 Docker 图像。它可以是公共的或私有的,您可以从中下载或上传图像。公共 Docker 注册表称为Docker Hub,我们稍后会介绍。

  • 索引:索引管理用户帐户、权限、搜索、标记以及 Docker 注册表公共 Web 界面中的所有好东西。

  • 容器:容器是由基本图像和在其上的层组合创建的运行图像。它们包含运行应用程序所需的一切。如前图所示,在启动容器时还会添加一个临时层,如果在停止和删除容器后未提交,则会被丢弃。如果提交,则会创建另一个层。

  • 仓库:一个图像的不同版本可以通过多个标签进行管理,这些标签保存在不同的 GUID 中。仓库是由 GUID 跟踪的图像集合。

另请参阅

添加非 root 用户以管理 Docker

为了方便使用,我们可以允许非 root 用户通过将其添加到 Docker 组来管理 Docker。

做好准备

  1. 如果还没有,创建 Docker 组:
$ sudo group add docker

  1. 创建要授予管理 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 EngineGCE)上没有帐户,那么您可以注册免费试用(cloud.google.com/compute/docs/signup)来尝试这个配方。我假设您在 GCE 上有一个项目,并且在下载 Docker Machine 二进制文件的系统上安装了 Google Cloud SDK。如果没有,那么您可以按照以下步骤操作:

  1. 在本地系统上设置 Google Cloud SDK:
$ curl https://sdk.cloud.google.com | bash

  1. 在 GCE 上创建一个项目(console.developers.google.com/project)并获取其项目 ID。请注意,项目名称和其 ID 是不同的。

  2. 转到项目主页,在API 和身份验证部分下,选择API,并启用 Google Compute Engine API

如何操作...

  1. 将我们收集到的项目 ID 分配给变量GCE_PROJECT
$ export  GCE_PROJECT="<Your Project ID>"

  1. 运行以下命令并输入弹出的网页浏览器上提供的代码:
$ 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)"

  1. 列出 Docker Machine 管理的现有主机:
$ ./docker-machine_linux-amd64 ls

如何操作...

您可以使用 Docker Machine 管理多个主机。*表示活动主机。

  1. 显示设置 Docker 客户端环境的命令:
$  ./docker-machine_linux-amd64 env cookbook

如何操作...

因此,如果使用前面的环境变量指向 Docker 客户端,我们将连接到在 GCE 上运行的 Docker 守护程序。

  1. 并且要指定 Docker 客户端使用我们新创建的机器,请运行以下命令:
$ eval "$(./docker-machine_linux-amd64 env  cookbook)"

从现在开始,所有 Docker 命令都将在我们在 GCE 上预配的机器上运行,直到设置前面的环境变量。

工作原理...

Docker Machine 连接到云提供商并设置带有 Docker Engine 的 Linux VM。它在当前用户的主目录下创建一个.docker/machine/目录以保存配置。

还有更多...

Docker Machine 提供管理命令,如createstartstoprestartkillremovessh和其他命令来管理机器。有关详细选项,请查找 Docker Machine 的帮助选项:

$ docker-machine  -h

您可以使用--driver/-d选项来选择部署的许多端点之一。例如,要使用 VirtualBox 设置环境,请运行以下命令:

$ docker-machine create --driver virtualbox dev

还有更多...

在这里,dev是机器名称。默认情况下,最新部署的机器将成为主机。

另请参阅

使用 Docker 命令行查找帮助

Docker 命令有很好的文档,可以在需要时进行参考。在线文档也有很多,但可能与您正在运行的 Docker 版本的文档不同。

准备工作

在您的系统上安装 Docker。

如何操作...

  1. 在基于 Linux 的系统上,您可以使用man命令查找帮助,如下所示:
$ man docker

  1. 还可以使用以下任何命令找到特定子命令的帮助:
$ man docker ps
$ man docker-ps

工作原理…

man命令使用 Docker 软件包安装的man页面显示帮助信息。

另请参阅

第二章:使用 Docker 容器

在本章中,我们将涵盖以下配方:

  • 列出/搜索镜像

  • 拉取镜像

  • 列出镜像

  • 启动容器

  • 列出容器

  • 停止容器

  • 查看容器的日志

  • 删除容器

  • 设置容器的重启策略

  • 在容器内获取特权访问

  • 在启动容器时暴露端口

  • 在容器内访问主机设备

  • 向正在运行的容器注入新进程

  • 返回有关容器的低级信息

  • 对容器进行标记和过滤

介绍

在上一章中,安装 Docker 后,我们拉取了一个镜像,并从中创建了一个容器。Docker 的主要目标是运行容器。在本章中,我们将看到我们可以对容器进行不同的操作,如启动、停止、列出、删除等。这将帮助我们将 Docker 用于不同的用例,如测试、CI/CD、设置 PaaS 等,我们将在后面的章节中进行介绍。在开始之前,让我们通过运行以下命令来验证 Docker 安装:

$ docker version

介绍

这将提供 Docker 客户端和服务器版本,以及其他详细信息。

我正在使用 Fedora 20/21 作为运行配方的主要环境。它们也应该适用于其他环境。

列出/搜索镜像

我们需要一个镜像来启动容器。让我们看看如何在 Docker 注册表上搜索镜像。正如我们在第一章中所看到的,介绍和安装,注册表保存 Docker 镜像,它可以是公共的也可以是私有的。默认情况下,搜索将在默认的公共注册表 Docker Hub 上进行,它位于 hub.docker.com/

准备就绪

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。

如何做…

  1. 要在 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 搜索的帮助,请运行以下命令:
$ docker search --help

拉取图像

搜索图像后,我们可以通过运行 Docker 守护程序将其拉取到系统中。让我们看看我们可以如何做到这一点。

准备工作

确保 Docker 守护程序在主机上运行,并且可以通过 Docker 客户端进行连接。

如何做...

  1. 要在 Docker 注册表上拉取图像,请运行以下命令:
docker pull NAME[:TAG]

以下是拉取 Fedora 图像的示例:

$ docker pull fedora

如何做...

它是如何工作的...

pull命令从 Docker 注册表下载所有层,这些层是在本地创建该图像所需的。我们将在下一章中看到有关层的详细信息。

还有更多...

  • 图像标签将相同类型的图像分组。例如,CentOS 可以具有标签如centos5centos6等的图像。例如,要拉取具有特定标签的图像,请运行以下命令:
$ 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 pullhelp选项:
$ docker pull --help

列出图像

我们可以列出运行 Docker 守护程序的系统上可用的图像。这些图像可能已经从注册表中拉取,通过docker命令导入,或者通过 Docker 文件创建。

准备工作

确保 Docker 守护程序在主机上运行,并且可以通过 Docker 客户端进行连接。

如何做...

  1. 运行以下命令列出图像:
$ docker images

如何做...

它是如何工作的...

Docker 客户端与 Docker 服务器通信,并获取服务器端的图像列表。

还有更多...

  • 所有具有相同名称但不同标签的图像都会被下载。这里值得注意的有趣之处是它们具有相同的名称但不同的标签。此外,对于相同的IMAGE ID,有两个不同的标签,即2d24f826cb16

  • 您可能会看到与最新的 Docker 软件包不同的REPOSITORY输出,如下面的屏幕截图所示。更多内容…

这是因为镜像列表打印了 Docker 注册表主机名。如前面的屏幕截图所示,docker.io是注册表主机名。

另请参阅

  • 查看docker imageshelp选项:
$ docker images --help

启动容器

一旦我们有了镜像,就可以使用它们来启动容器。在这个示例中,我们将使用fedora:latest镜像启动一个容器,并查看幕后发生的所有事情。

准备就绪

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。

如何做…

  1. 启动容器的语法如下:
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 将运行searchpull命令。

工作原理…

在幕后,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 runhelp选项:
$ docker run --help

列出容器

我们可以列出正在运行和停止的容器。

准备就绪

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一些正在运行和/或已停止的容器。

如何做…

  1. 要列出容器,请运行以下命令:
docker ps [ OPTIONS ]

如何做…

它是如何工作的…

Docker 守护程序可以查看与容器关联的元数据并将其列出。默认情况下,该命令返回:

  • 容器 ID

  • 创建它的镜像

  • 在启动容器后运行的命令

  • 有关创建时间的详细信息

  • 当前状态

  • 从容器中公开的端口

  • 容器的名称

还有更多…

  • 要列出运行和停止的容器,请使用-a选项,如下所示:还有更多…

  • 要仅返回所有容器的容器 ID,请使用-aq选项,如下所示:还有更多…

  • 要显示最后创建的容器,包括非运行容器,请运行以下命令:

$ docker ps -l

  • 使用--filter/-f选项对ps进行标记,我们可以列出具有特定标签的容器。有关更多详细信息,请参阅本章中的标记和过滤容器示例。

另请参阅

查看docker psman页面以查看更多选项:

  • 查看docker pshelp选项:
$ docker ps --help

查看容器的日志

如果容器在STDOUT/STDERR上发出日志或输出,则可以在不登录到容器的情况下获取它们。

准备就绪

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一个正在运行的容器,该容器会在STDOUT上发出日志/输出。

如何做…

  1. 要从容器中获取日志,请运行以下命令:
docker logs [-f|--follow[=false]][-t|--timestamps[=false]] CONTAINER

  1. 让我们以前面部分的示例为例,运行一个守护式容器并查看日志:
$ 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 logshelp选项:
$ docker logs --help

停止一个容器

我们可以一次停止一个或多个容器。在这个示例中,我们将首先启动一个容器,然后停止它。

准备就绪

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一个或多个正在运行的容器。

如何做…

  1. 要停止容器,请运行以下命令:
docker stop [-t|--time[=10]] CONTAINER [CONTAINER...]

  1. 如果您已经有一个正在运行的容器,那么您可以继续停止它;如果没有,我们可以创建一个然后停止它,如下所示:
$ ID='docker run -d -i fedora /bin/bash'
$ docker stop $ID

它是如何工作的…

这将保存容器的状态并停止它。如果需要,可以重新启动。

还有更多…

  • 要在等待一段时间后停止容器,请使用--time/-t选项。

  • 要停止所有正在运行的容器,请运行以下命令:

$ docker stop 'docker ps -q'

另请参阅

  • 查看docker stophelp选项:
$ docker stop --help

删除容器

我们可以永久删除一个容器,但在此之前我们必须停止容器或使用强制选项。在这个示例中,我们将启动、停止和删除一个容器。

准备工作

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一些处于停止或运行状态的容器来删除它们。

如何做…

  1. 使用以下命令:
$ docker rm [ OPTIONS ] CONTAINER [ CONTAINER ]

  1. 让我们首先启动一个容器,然后停止它,然后使用以下命令删除它:
$ 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 rmhelp选项
$ docker rm --help

在容器上设置重启策略

在 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 runhelp选项:
$ docker run --help

在容器内获取特权访问

Linux 将传统上与超级用户关联的特权分为不同的单元,称为功能(在基于 Linux 的系统上运行man capabilities),可以独立启用和禁用。例如,net_bind_service功能允许非用户进程绑定到 1,024 以下的端口。默认情况下,Docker 以有限的功能启动容器。通过在容器内获取特权访问,我们可以赋予更多的功能来执行通常由 root 完成的操作。例如,让我们尝试在挂载磁盘映像时创建一个回环设备。

在容器内获取特权访问

准备工作

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。

操作步骤…

  1. 要使用privileged模式,请使用以下命令:
$ docker run --privileged [ OPTIONS ]  IMAGE[:TAG]  [COMMAND]  [ARG...]

  1. 现在让我们尝试使用特权访问的前面的示例:
$ 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 runhelp选项:
$ docker run --help

在启动容器时暴露端口

有多种方法可以暴露容器上的端口。其中一种是通过run命令,我们将在本章中介绍。其他方法是通过 Docker 文件和--link命令。我们将在其他章节中探讨它们。

准备就绪

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。

如何做…

  1. 暴露端口的语法如下:
$ docker run --expose=PORT [ OPTIONS ]  IMAGE[:TAG]  [COMMAND]  [ARG...]

例如,要在启动容器时暴露端口 22,请运行以下命令:

$ docker run --expose=22 -i -t fedora /bin/bash

还有更多…

有多种方法可以为容器暴露端口。现在,我们将看到如何在启动容器时暴露端口。我们将在后续章节中探讨其他暴露端口的选项。

另请参阅

  • 查看docker runhelp选项:
$ docker run --help

在容器内访问主机设备

从 Docker 1.2 开始,我们可以使用--device选项将主机设备的访问权限提供给容器的run命令。以前,必须使用-v选项进行绑定挂载,并且必须使用--privileged选项进行操作。

准备就绪

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您还需要一个设备传递给容器。

如何做…

  1. 您可以使用以下语法将主机设备的访问权限提供给容器:
$ 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 runhelp选项:
 $ docker run --help

向正在运行的容器注入新进程

在开发和调试过程中,我们可能想要查看已经运行的容器内部。有一些实用程序,比如nsenter(github.com/jpetazzo/nsenter),允许我们进入容器的命名空间进行检查。使用在 Docker 1.3 中添加的exec选项,我们可以在运行的容器内注入新进程。

准备工作

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。您可能还需要一个正在运行的容器来注入进程。

如何做…

  1. 您可以使用以下命令在运行的容器中注入进程:
 $ docker exec [-d|--detach[=false]] [--help] [-i|--interactive[=false]] [-t|--tty[=false]] CONTAINER COMMAND [ARG...]

  1. 让我们启动一个nginx容器,然后注入bash进去:
$ ID='docker run -d nginx'
$ docker run -it $ID bash

如何做…

工作原理…

exec命令进入容器的命名空间并启动新进程。

另请参阅

  • 查看 Docker inspect 的help选项:
 $ docker exec --help

返回有关容器的低级信息

在进行调试、自动化等操作时,我们将需要容器配置详细信息。Docker 提供了inspect命令来轻松获取这些信息。

准备工作

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。

如何做…

  1. 要检查容器/镜像,请运行以下命令:
$ docker inspect [-f|--format="" CONTAINER|IMAGE [CONTAINER|IMAGE...]

  1. 我们将启动一个容器,然后对其进行检查:
$ 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 inspecthelp选项:
 $ docker inspect --help

标记和过滤容器

使用 Docker 1.6,已添加了一个功能来标记容器和镜像,通过这个功能,我们可以向它们附加任意的键值元数据。您可以将它们视为环境变量,这些变量对于容器内运行的应用程序不可用,但对于管理镜像和容器的程序(Docker CLI)是可用的。附加到镜像的标签也会应用到通过它们启动的容器。我们还可以在启动容器时附加标签。

Docker 还为容器、镜像和事件提供了过滤器(docs.docker.com/reference/commandline/cli/#filtering),我们可以与标签一起使用,以缩小搜索范围。

对于这个示例,让我们假设我们有一个带有标签 distro=fedora21 的镜像。在下一章中,我们将看到如何为镜像分配标签。

标记和过滤容器

从上面的截图中可以看到,如果我们在 docker images 命令中使用过滤器,我们只会得到一个在镜像元数据中找到相应标签的镜像。

准备工作

确保主机上运行着 Docker 守护程序 1.6 及以上版本,并且您可以通过 Docker 客户端进行连接。

操作步骤如下…

  1. 要使用 --label/-l 选项启动容器,请运行以下命令:
$ docker run --label environment=dev f21 date

  1. 让我们启动一个没有标签的容器,并使用相同的标签启动另外两个:操作步骤如下…

如果我们列出所有没有标签的容器,我们将看到所有的容器,但如果我们使用标签,那么我们只会得到与标签匹配的容器。

操作步骤如下…

工作原理…

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 内核通信。这是通过执行驱动程序完成的。libconatinernative是其中之一。其他的有libvirtlxc等,我们在第一章中看到了,介绍和安装

  • 主机操作系统上的内核版本。

  • 在下一节提到的注册表上注册的用户帐户以拉取/推送镜像。

注意

我正在使用 Fedora 20/21 作为运行配方的主要环境。它们也应该适用于其他环境。

在 Docker Hub 上创建一个帐户

Docker Hub 就像图像的 GitHub。它是一个公共注册表,您可以在其中托管图像,包括公共和私有图像,并与他人合作。它与 GitHub、Bitbucket 集成,并可以触发自动构建。

目前,在 Docker Hub 上创建帐户是免费的。一个仓库可以容纳图像的不同版本。您可以为您的图像创建任意数量的公共仓库。默认情况下,您将拥有一个私有仓库,该仓库对公众不可见。您可以购买更多的私有仓库。您可以通过 Web 浏览器或命令行创建帐户。

准备工作

要从命令行注册,您需要在系统上安装 Docker。

如何做...

  1. 要通过 Docker Hub 的 Web 浏览器创建帐户,请访问hub.docker.com/account/signup/并创建一个帐户:如何做...

  2. 要使用命令行创建一个帐户,请运行以下命令并提交所需的详细信息:

$ docker login

它是如何工作的...

上述步骤将为您创建一个 Docker Hub 帐户。帐户创建后,您将收到一封确认邮件,通过该邮件您需要确认您的身份。

另请参阅

从容器创建镜像

有几种创建镜像的方法,一种是手动提交层,另一种是通过 Dockerfile。在这个教程中,我们将看到前者,稍后在本章中再看 Dockerfile。

当我们启动一个新的容器时,会附加一个读/写层。如果我们不保存这个层,它将被销毁。在这个教程中,我们将看到如何保存这个层,并使用docker commit命令从正在运行或停止的容器中创建一个新的镜像。

准备工作

要获取 Docker 镜像,请使用它启动一个容器。

如何做...

  1. 要进行提交,请运行以下命令:
docker commit -a|--author[=""] -m|--message[=""] CONTAINER [REPOSITORY[:TAG]]

  1. 让我们启动一个容器并使用install httpd包创建/修改一些文件:如何做...

  2. 然后,打开一个新的终端并通过提交创建一个新的镜像:

$ 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 commithelp选项:
$ docker commit --help

将镜像发布到注册表

假设您已经创建了一个适合组织开发环境的镜像。您可以使用 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 pushhelp选项:
$ docker push --help

查看图像的历史记录

了解我们正在使用的图像是如何创建的很方便。docker history命令帮助我们找到所有中间层。

准备工作

拉取或导入任何 Docker 图像。

如何操作...

  1. 要查看图像的历史记录,请考虑以下语法:
$ docker history [ OPTIONS ] IMAGE

以下是使用上述语法的示例:

$ docker history nkhare/fedora:httpd

如何操作...

它是如何工作的...

通过图像的元数据,Docker 可以知道图像是如何创建的。使用history命令,它将递归查看元数据以找到原始来源。

更多内容...

查看已提交层的提交消息:

$ docker inspect --format='{{.Comment}}' nkhare/fedora:httpd
Fedora with HTTPD package

目前,没有直接的方法可以使用一个命令查看每个层的提交消息,但是我们可以使用inspect命令,我们之前看到的,对每个层进行查看。

另请参阅

  • 查看docker historyhelp选项:
$ docker history --help

删除图像

要从主机中删除图像,我们可以使用docker rmi命令。但是,这不会从注册表中删除图像。

准备工作

确保一个或多个 Docker 图像在本地可用。

如何做…

  1. 要删除图像,请考虑以下语法:
$ 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 rmihelp选项:
$ docker rmi --help

导出图像

假设您有一个客户,其非常严格的政策不允许他们使用来自公共领域的图像。在这种情况下,您可以通过 tar 文件共享图像,稍后可以在另一个系统上导入。在本示例中,我们将看到如何使用docker save命令来做到这一点。

准备工作

在 Docker 主机上拉取或导入一个或多个 Docker 图像。

如何做…

  1. 使用以下语法将图像保存在 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 savedocker exporthelp选项:
$ docker save -help
$ docker export --help

导入图像

要获得图像的本地副本,我们需要从可访问的注册表中拉取它,或者从已导出的图像中导入它,就像我们在之前的示例中看到的那样。使用docker import命令,我们导入一个已导出的图像。

准备工作

您需要一个可访问的导出的 Docker 镜像。

如何做…

  1. 要导入图像,我们可以使用以下语法:
$ docker import URL|- [REPOSITORY[:TAG]]

以下是使用前述语法的示例:

$ cat fedora-latest.tar | docker import - fedora:latest

或者,您可以考虑以下示例:

$ docker import http://example.com/example.tar example/image

前面的示例将首先创建一个空的文件系统,然后导入内容。

参见

  • 查看docker importhelp选项:
$ docker import --help

使用 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

操作方法...

  1. 在创建 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是一个中间镜像,c5d4dd2b3db9ffb9303ab124是中间容器。执行最后一个指令后,将创建最终镜像。在这种情况下,最终镜像是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

  • CMDCMD指令在启动容器时提供默认可执行文件。如果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:这为随后的RUNCMDENTRYPOINT指令设置工作目录。它可以在同一个 Dockerfile 中有多个条目。可以给出相对路径,它将相对于之前的WORKDIR指令,使用以下语法:
WORKDIR <PATH>

  • ONBUILD:这将向图像添加触发指令,稍后将在将此图像用作另一个图像的基本图像时执行。此触发器将作为下游 Dockerfile 中的FROM指令的一部分运行,使用以下语法:
ONBUILD [INSTRUCTION]

另请参阅

  • 查看docker buildhelp选项:
$ docker build -help

构建 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 buildhelp选项:
$ docker build --help

从容器访问 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 buildhelp选项:
$ docker build --help

构建 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 的脚本。

  • LICENSELICENSE.txtUNLICENSE.txt:这些文件包含许可信息。

  • README.md:这是一个 README 文件。

  • supervisord.conf:这是一个结果容器,必须同时运行SSHDMySQLHTTPD。在这种特殊情况下,使用 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

它是如何工作的…

与其他示例一样,我们从基本镜像开始,安装所需的软件包,并复制支持文件。然后设置sudodownloaduntar 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 buildhelp选项:
$ docker build --help

设置私有索引/注册表

正如我们之前看到的,公共 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 客户端进行连接。

如何做…

  1. 要在容器上运行注册表,请运行以下命令:
$ docker run -p 5000:5000 registry

  1. 要测试新创建的注册表,请执行以下步骤:

  2. 使用以下命令启动容器及其 ID:

 $ ID='docker run -d -i fedora /bin/bash'

  1. 如果需要,附加到新创建的容器并进行一些更改。然后,将这些更改提交到本地存储库:
 $ docker commit $ID fedora-20

  1. 要将镜像推送到本地注册表,我们需要使用注册表主机的主机名或 IP 地址对镜像进行标记。假设我们的注册表主机是 registry-host;然后,要对其进行标记,请使用以下命令:
$ docker tag fedora-20 registry-host:5000/nkhare/f20

  1. 由于我们在启动注册表时没有正确配置 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

  1. 要推送镜像,请使用以下命令:
$ docker push registry-host:5000/nkhare/f20

  1. 要从本地注册表中拉取镜像,请运行以下命令:
$ 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 和 Bitbucket 进行自动构建

我们之前已经看到如何将 Docker 镜像推送到 Docker Hub。Docker Hub 允许我们使用其构建集群从 GitHub/Bitbucket 存储库创建自动化镜像。GitHub/Bitbucket 存储库应包含 Dockerfile 和所需的内容以复制/添加到镜像中。让我们在接下来的部分中看一个 GitHub 的例子。

准备工作

您将需要在 Docker Hub 和 GitHub 上拥有帐户。您还需要一个具有相应 Dockerfile 的 GitHub 存储库,位于顶层。

如何做…

  1. 登录到 Docker Hub(hub.docker.com/)并单击绿色加号。在右上角添加存储库图标,然后单击自动化构建。选择 GitHub 作为自动化构建的源。然后,选择公共和私有(推荐)选项以连接到 GitHub。在提示时提供 GitHub 用户名/密码。选择要执行自动化构建的 GitHub 存储库。如何做…

  2. 选择 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

如何做...

  1. 使用prepare模式在目录中安装bashcoreutils和相关依赖项。
$ supermin --prepare -o OUTPUTDIR PACKAGE [PACKAGE ...]

以下是使用前述语法的示例:

$ supermin --prepare bash coreutils -o f21_base

  1. 现在,使用build模式为基础镜像创建一个 chroot 环境:
$ supermin --build -o OUTPUTDIR -f chroot|ext2 INPUT [INPUT ...]

以下是使用前述语法的示例:

$ supermin --build --format chroot f21_base -o f21_image

  1. 如果我们在输出目录上执行ls,我们将看到一个类似于任何 Linux 根文件系统的目录树:
$ ls f21_image/
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

  1. 现在我们可以使用以下命令将目录导出为 Docker 镜像:
$ tar -C f21_image/ -c . | docker import - nkhare/f21_base
d6db8b798dee30ad9c84480ef7497222f063936a398ecf639e60599eed7f6560

  1. 现在,查看docker images输出。您应该有一个名为nkhare/f21_base的新镜像。

它是如何工作的...

Supermin 有两种模式,preparebuild。使用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)的基本镜像,则执行以下操作:

  1. 在要安装操作系统的目录上创建一个目录。Debootstrap 还创建了 chroot 环境以安装软件包,就像我们之前在 supermin 中看到的那样。
$ mkdir trusty_chroot

  1. 现在,使用debootstrap在我们之前创建的目录中安装 Trusty Tahr:
$ debootstrap trusty ./trusty_chroot http://in.archive.ubuntu.com/ubuntu/

  1. 您将看到类似于任何 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

  1. 现在,我们可以使用以下命令将目录导出为 Docker 镜像:
$ tar -C trusty_chroot/ -c . |  docker import - nkhare/trusty_base

  1. 现在,查看docker images输出。您应该有一个名为nkhare/trusty_base的新镜像。

另请参阅

可视化层之间的依赖关系

随着镜像数量的增加,找到它们之间的关系变得困难。有一些实用程序可以找到镜像之间的关系。

准备工作

在运行 Docker 守护程序的主机上有一个或多个 Docker 镜像。

如何做…

  1. 运行以下命令以获取图像的树状视图:
$ docker images -t

工作原理…

层之间的依赖关系将从 Docker 镜像的元数据中获取。

还有更多…

--vizdocker images,我们可以以图形方式看到依赖关系;要做到这一点,您需要安装graphviz软件包:

$ docker images --viz | dot -Tpng -o /tmp/docker.png
$ display /tmp/docker.png

正如在运行上述命令时出现的警告中所述,-t--viz选项可能很快就会被弃用。

另请参阅

第四章:容器的网络和数据管理

在本章中,我们将涵盖以下内容:

  • 从外部访问容器

  • 管理容器中的数据

  • 连接两个或多个容器

  • 通过链接容器开发 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 客户端进行连接。

如何做…

  1. 让我们使用-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 注册表并推送了一些镜像,那么一旦注册表容器被删除,所有这些镜像都会丢失,如果您没有提交它们。即使您提交了,也不是最佳做法。我们应该尽量保持容器的轻量化。以下是两种主要的管理 Docker 数据的方法:

  • 数据卷:来自 Docker 文档(docs.docker.com/userguide/dockervolumes/),数据卷是一个专门指定的目录,位于一个或多个容器中,绕过联合文件系统,提供了几个有用的特性,用于持久或共享数据:

  • 在创建容器时初始化卷。如果容器的基本镜像包含指定挂载点的数据,则该数据将被复制到新卷中。

  • 数据卷可以在容器之间共享和重复使用。

  • 对数据卷的更改是直接进行的。

  • 对数据卷的更改不会在更新镜像时包含在内。

  • 数据卷会持久存在,直到没有容器在使用它们。

  • 数据卷容器:由于卷会持久存在,直到没有容器在使用它,我们可以使用卷在容器之间共享持久数据。因此,我们可以创建一个命名的数据卷容器,并将数据挂载到另一个容器中。

准备工作

确保 Docker 守护程序在主机上运行,并且可以通过 Docker 客户端进行连接。

如何做...

  1. 添加数据卷。使用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 

如何做...

如果容器中不存在目标目录,它将被创建。

  1. 接下来,我们将主机目录挂载为数据卷。我们还可以使用-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 安全性,以获取更多详细信息。

  1. 现在,创建一个数据卷容器。通过卷共享主机目录到容器时,我们将容器绑定到给定的主机,这是不好的。此外,在这种情况下,存储不受 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 

另请参阅

链接两个或更多个容器

通过容器化,我们希望通过在不同的容器上运行服务,然后将它们链接在一起来创建我们的堆栈。在上一章中,我们通过将 Web 服务器和数据库放在同一个容器中来创建了一个 WordPress 容器。但是,我们也可以将它们放在不同的容器中并将它们链接在一起。容器链接在它们之间创建了一个父子关系,其中父容器可以看到其子容器的选定信息。链接依赖于容器的命名。

准备工作

确保 Docker 守护程序在主机上运行,并且您可以通过 Docker 客户端进行连接。

如何操作…

  1. 创建一个名为centos_server的命名容器:
$ docker run  -d -i -t --name centos_server centos /bin/bash 

如何操作…

  1. 现在,让我们使用--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_*)。

提示

由于链接取决于容器的名称,如果要重用名称,必须删除旧容器。

另请参阅

通过链接容器开发 LAMP 应用程序

让我们通过链接容器来扩展先前的食谱,创建一个 LAMP 应用程序(WordPress)。

准备工作

从 Docker 注册表中拉取 MySQL 和 WordPress 镜像:

如何做...

  1. 首先,启动一个mysql容器:
$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=mysecretpassword -d mysql 

  1. 然后,启动wordpress容器并将其与mysql容器链接起来:
$ docker run -d --name wordpress --link mysql:mysql -p 8080:80 wordpress 

如何做...

我们将 Docker 主机的8080端口映射到容器的80端口,因此我们可以通过访问 Docker 主机上的8080端口和http://<DockerHost>:8080 URL 来连接 WordPress。

它是如何工作的...

wordpressmysql容器之间创建了一个链接。每当wordpress容器收到一个数据库请求时,它将其传递给mysql容器并获取结果。查看前面的食谱以获取更多详细信息。

使用 Flannel 进行多主机容器的网络连接

在这个教程中,我们将使用 Flannel (github.com/coreos/flannel) 来设置多主机容器网络。Flannel 是一个通用的覆盖网络,可以作为软件定义网络SDN)的替代方案。它是一个基于 IP 的解决方案,使用虚拟可扩展局域网VXLAN),为运行该容器的主机分配唯一的 IP 地址。因此,在这种解决方案中,每个主机在集群中使用覆盖网络内的不同子网进行通信。Flannel 使用etcd服务 (github.com/coreos/etcd) 作为键值存储。

准备工作

对于这个教程,我们将需要安装了 Fedora 21 的三个虚拟机或物理机。

如何做…

  1. 让我们称一台机器/虚拟机为master,另外两台为minion1minion2。根据您系统的 IP 地址,更新/etc/hosts文件如下:如何做…

  2. 在我们设置的所有系统上安装etcdFlannelDocker

$ yum install -y etcd flannel docker 

  1. /etc/etcd/etcd.conf文件中将ETCD_LISTEN_CLIENT_URLS的值修改为http://master.example.com:4001如下:
ETCD_LISTEN_CLIENT_URLS="http://master.example.com:4001"
  1. 在 master 中,启动etcd服务并检查其状态:
$ systemctl start etcd 
$ systemctl enable etcd 
$ systemctl status etcd 

  1. 在 master 中,创建一个名为flannel-config.json的文件,内容如下:
{
"Network": "10.0.0.0/16",
"SubnetLen": 24,
"Backend": {
"Type": "vxlan",
"VNI": 1
   }
}
  1. 使用config作为键将上述配置文件上传到etcd
$ curl -L http://master.example.com:4001/v2/keys/coreos.com/network/config -XPUT --data-urlencode value@flannel-config.json 

如何做…

  1. 在 master 中,更新/etc/sysconfig/flanneld文件中的FLANNEL_OPTIONS以反映系统的接口。同时,更新FLANNEL_ETCD以使用主机名而不是 127.0.0.1:4001 地址。如何做…

  2. 在 master 中,启用和启动flanneld服务:

$ systemctl enable flanneld 
$ systemctl start flanneld 
$ systemctl status flanneld 

如何做…

  1. 从 minion 系统中,检查对etcd的与 master 的连接:
[root@minion1 ~]#  curl -L http://master.example.com:4001/v2/keys/coreos.com/network/config 

  1. 更新两个 minion 中的/etc/sysconfig/flanneld文件,指向 master 中运行的etcd服务器,并更新FLANNEL_OPTIONS以反映 minion 主机的接口:如何做…

  2. 在两个 minion 中启用和启动flanneld服务:

$ systemctl enable flanneld 
$ systemctl start flanneld 
$ systemctl status flanneld 

  1. 在集群中的任何一台主机上运行以下命令:
$ 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中,你会看到类似以下内容:

如何做…

  1. 要在所有主机中重新启动 Docker 守护程序:
$ systemctl restart docker 

然后,查看docker0flannel.1接口的 IP 地址。在minion2中,看起来像下面这样:

如何做…

我们可以看到docker0接口从与flannel.1接口相同的子网中获取了 IP,该子网用于路由所有流量。

  1. 我们已经准备好在任何主机中生成两个容器,并且它们应该能够进行通信。让我们在minion1中创建一个容器并获取其 IP 地址:如何做…

  2. 现在在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 使用该文件进行配置。

另请参阅

为容器分配 IPv6 地址

默认情况下,Docker 为容器分配 IPv4 地址。Docker 1.5 添加了一个功能来支持 IPv6 地址。

准备工作

确保 Docker 守护进程(1.5 版本及以上)正在主机上运行,并且您可以通过 Docker 客户端进行连接。

如何操作...

  1. 使用--ipv6选项启动 Docker 守护进程,我们可以在守护进程的配置文件(在 Fedora 上为/etc/sysconfig/docker)中添加以下选项:
OPTIONS='--selinux-enabled --ipv6' 

或者,如果我们以守护进程模式启动 Docker,那么可以按以下方式启动:

$ docker -d --ipv6 

通过运行这些命令之一,Docker 将使用 IPv6 本地链路地址fe80::1设置docker0桥。

如何操作...

  1. 让我们启动容器并查找分配给它的 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 使用案例

在本章中,我们将涵盖以下配方:

  • 使用 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 进行测试,请执行以下操作:

  1. 转到包含 Flask 示例的目录:
$ cd /tmp/flask/examples/

  1. 启动一个带有python2.7测试镜像并在/test下挂载blueprintexample的容器:
$ docker run -d -v `pwd`/blueprintexample:/test python2.7test

操作方法...

  1. 类似地,要使用 Python 3.3 进行测试,请运行以下命令:
 $ docker run -d -v `pwd`/blueprintexample:/test python3.3test

  1. 在启用 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.ymlyml文件中,您必须在源代码存储库中提供。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 上部署它。

准备工作

  1. 在 Shippable 上创建一个帐户(www.shippable.com/)。

  2. github.com/openshift/flask-example派生 flask 示例。

  3. 在 OpenShift 上为派生存储库创建一个应用程序,具体步骤如下:

  4. 在 OpenShift 上创建一个帐户(www.openshift.com/app/account/new)并登录。

  5. 为应用程序选择Python 2.7 Cartridge

  6. 更新您想要的Public URL部分。在Source Code部分,提供我们分叉存储库的 URL。对于本示例,我分别放下了blueprinthttps://github.com/nkhare/flask-example准备就绪

  7. 单击Create Application创建新应用程序。创建后,您应该能够访问我们在上一步中提到的公共 URL。

创建应用程序后,OpenShift 提供了一种在进行代码更改部分管理/更新此应用程序的源代码的方法。由于我们希望使用 Shippable 部署应用程序,因此无需遵循这些说明。

  1. 在本地系统上克隆分叉存储库:
$ git clone git@github.com:nkhare/flask-example.git

  1. 让我们使用之前使用过的相同蓝图示例。要这样做,请按照以下说明进行操作:

  2. 克隆 flask 存储库:

$ git clone https://github.com/mitsuhiko/flask.git

  1. 复制蓝图示例:
$ cp -Rv flask/examples/blueprintexample/* flask-example/wsgi/

  1. 更新flask-example/wsgi/application文件,从blueprintexample模块导入app模块。因此,flask-example/wsgi/application文件中的最后一行看起来像下面这样:
from blueprintexample import app as application
  1. 在 flask-example 存储库的顶层添加带有以下内容的requirements.txt文件:
flask 
pytest
  1. 添加带有以下内容的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 
  1. 提交代码并将其推送到您的分叉存储库中。

如何操作...

  1. 登录到 Shippable。

  2. 登录后,单击SYNC ACCOUNT以获取列出您的分叉存储库,如果尚未列出。查找并启用要构建和运行测试的存储库。在本例中,我选择了我的 GitHub 存储库中的flask-example。启用后,您应该看到类似以下内容:如何操作...

  3. 单击播放按钮并选择要构建的分支。对于本示例,我选择了 master:

如果构建成功,您将看到成功的图标。

下次在存储库中提交代码时,Shippable 将触发构建并测试代码。现在,要在 OpenShift 上执行持续部署,请按照 Shippable 网站提供的说明进行操作(docs.shippable.com/deployment/openshift/):

  1. 从 Shippable 仪表板获取部署密钥(位于右侧,Repos下方):如何操作...

  2. 将其复制到 OpenShift 的(openshift.redhat.com/app/console/settings) Settings | Public Keys部分如下所示:如何操作...

  3. 从 OpenShift 应用程序页面获取源代码存储库链接,它将在下一步中用作OPNESHIFT_REPO如何操作...

  4. 安装部署密钥后,更新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 部署的应用程序。它将与此示例中显示的内容不同。

  1. 现在提交这些更改并将其推送到 GitHub。您将看到 Shippable 触发的构建以及在 OpenShift 上部署的新应用程序。

  2. 访问您的应用主页,您应该看到其更新的内容。

工作原理…

在每个构建指令中,Shippable 会根据shippable.yml文件中指定的镜像和语言类型,启动新的容器并运行构建以执行测试。在我们的情况下,Shippable 将启动两个容器,一个用于 Python 2.6,另一个用于 Python 2.7。当您在 GitHub 上注册时,Shippable 会向您的存储库添加一个 webhook,如下所示:

工作原理…

因此,每次对 GitHub 进行提交更改时,Shippable 上的构建都会被触发,并在成功后部署到 OpenShift 上。

另请参阅

使用 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)将代码同步到远程服务器进行部署。

对于这个示例,让我们使用之前示例中使用的相同示例。

准备工作

  1. 登录到 Drone(drone.io/)。

  2. 单击新项目并设置存储库。在我们的情况下,我们将选择在之前的示例中使用的 GitHub 上的相同存储库(github.com/nkhare/flask-example):准备工作

  3. 一旦选择了,它会要求您为所选的存储库选择编程语言。在这种情况下,我选择了 Python。

  4. 然后它会提示您设置构建脚本。对于这个教程,我们将输入以下内容并保存:

pip install -r requirements.txt --use-mirrors
cd wsgi
py.test

操作步骤如下…

  1. 通过单击立即构建来触发手动构建,如下面的屏幕截图所示:操作步骤如下…

它是如何工作的…

构建过程启动一个新的容器,克隆源代码存储库,并在其中运行我们在命令部分中指定的命令(运行测试用例)。

还有更多…

  • 构建完成后,您可以查看控制台输出。

  • Drone 还在 GitHub 中添加了一个 Webhook;因此,下次您提交存储库中的更改时,将触发构建。

  • 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 编排和托管平台”中所讨论的,强烈建议在继续本教程之前先阅读该章节。我将借用该章节中的一些概念。

使用 OpenShift Origin 设置 PaaS

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/)。如何设置这些内容的说明超出了本书的范围。

  1. 克隆 OpenShift Origin 存储库:
$ git clone https://github.com/openshift/origin.git

  1. 检出v0.4.3标签:
$ cd origin
$ git checkout tags/v0.4.3

  1. 启动虚拟机:
$ vagrant up --provider=virtualbox

  1. 登录到容器:
$ vagrant ssh

如何做...

  1. 构建 OpenShift 二进制文件:
$ cd /data/src/github.com/openshift/origin
$ make clean build

  1. 转到hello-openshift示例:
$  cd /data/src/github.com/openshift/origin/examples/hello-openshift

  1. 在一个守护进程中启动所有 OpenShift 服务:
$ mkdir logs
$ sudo /data/src/github.com/openshift/origin/_output/local/go/bin/openshift start --public-master=localhost &> logs/openshift.log &

  1. 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"

  1. 根据hello-pod.json定义创建 pod:
$ osc create -f hello-pod.json

如何做...

  1. 连接到 pod:
$ curl localhost:6061

它是如何工作的...

当 OpenShift 启动时,所有 Kubernetes 服务也会启动。然后,我们通过 CLI 连接到 OpenShift 主服务器,并请求它启动一个 pod。该请求然后转发到 Kubernetes,Kubernetes 启动了 pod。在 pod 配置文件中,我们提到将主机机器的端口6061映射到 pod 的端口8080。因此,当我们在端口6061上查询主机时,我们从 pod 得到了回复。

还有更多...

如果运行docker ps命令,将看到相应的容器正在运行。

另请参阅

从源代码构建和部署应用程序到 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 的扩展复制控制器。它还有触发器来触发新的部署。每次创建新的部署时,DeploymentConfiglatestVersion字段会递增。还会向DeploymentConfig添加deploymentCause,描述导致最新部署的更改。

ImageRepository,最近更名为ImageStream,是一组相关图像。BuildConfigDeploymentConfig监视ImageStream以查找图像更改并根据各自的触发器做出相应反应。

在 STI 构建文件中,您还会找到用于 pod 的服务(数据库和前端)、用于前端服务的路由,通过该路由可以访问应用程序,以及一个模板。模板描述了一组预期一起使用的资源,可以进行自定义和处理以生成配置。每个模板可以定义一组参数,这些参数可以被容器修改后使用。

与 STI 构建类似,在同一个 sample-app 示例文件夹中有 Docker 和自定义构建的示例。我假设您已经有了之前的配方,所以我们将从那里继续。

准备工作

您应该已经完成了之前的配方,使用 OpenShift Origin 设置 PaaS

您当前的工作目录应该是 Vagrant 启动的 VM 内的/data/src/github.com/openshift/origin /examples/hello-openshift

如何操作...

  1. 部署一个私有的 Docker 注册表来托管 STI 构建过程中创建的镜像:
$ sudo openshift ex registry --create --credentials=./openshift.local.certificates/openshift-registry/.kubeconfig

  1. 确认注册表已启动(这可能需要几分钟):
$ osc describe service docker-registry

如何操作...

  1. 在 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

  1. 使用test-admin用户登录并切换到test项目,从现在开始每个命令都将使用该项目:
$ osc login -u test-admin -p pass
$ osc project test

  1. 提交应用程序模板进行处理(生成模板中请求的共享参数),然后请求创建处理后的模板:
$ osc process -f application-template-stibuild.json | osc create -f -

  1. 这不会触发构建。要启动应用程序的构建,请运行以下命令:
$ osc start-build ruby-sample-build

  1. 监视构建并等待状态变为complete(这可能需要几分钟):
$ osc get builds

  1. 获取服务列表:
$ osc get services

如何做...

它是如何工作的...

BuildConfigruby-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

另请参阅

将 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 的容器创建支持,但目前它不在主线中,以加快开发周期。未来计划将其合并到主线中。在底层,它看起来像这样:

为 OpenStack 配置 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 驱动程序。

准备工作

  1. 在系统上安装 Docker。

  2. 克隆nova-dockerdevstack

$ 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

  1. 在我们可以使用configure_nova_hypervisor_rootwrap之前需要以下步骤:
$ git clone https://git.openstack.org/openstack/nova /opt/stack/nova

  1. 准备安装 Devstack:
$ cd /opt/stack/nova-docker
$ ./contrib/devstack/prepare_devstack.sh

  1. 创建 stack 用户并将其添加到sudo
$ /opt/stack/devstack/tools/create-stack-user.sh

  1. 使用 Python 安装docker-py以与 docker 进行通信:
$ yum install python-pip
$ pip install docker-py

如何做…

  1. 完成先决条件步骤后,运行以下命令安装 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

另请参阅

第六章: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>

前面的请求将返回一个返回代码和与我们选择的端点和请求相对应的输出。GETPUTDELETE是不同类型的请求,如果没有指定,默认请求是 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

操作步骤…

  1. 在 Fedora 20 系统上,在配置文件(/etc/sysconfig/docker)中添加-H tcp://0.0.0.0:2375选项,如下所示:
OPTIONS=--selinux-enabled -H tcp://0.0.0.0:2375

  1. 重新启动 Docker 服务。在 Fedora 上,运行以下命令:
$ sudo systemctl restart docker

  1. 从远程客户端连接到 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

另请参阅

使用远程 API 执行图像操作

在启用了 Docker 守护程序远程 API 之后,我们可以通过客户端进行所有与图像相关的操作。为了更好地理解 API,让我们使用curl连接到远程守护程序并进行一些与图像相关的操作。

准备工作

配置 Docker 守护程序并允许远程访问,如前面的配方中所解释的。

如何做…

在这个配方中,我们将看一下一些图像操作,如下所示:

  1. 要列出图像,请使用以下 API:
GET /images/json

以下是前述语法的一个例子:

$ curl http://dockerhost.example.com:2375/images/json

如何做…

  1. 要创建图像,请使用以下 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

  1. 要构建图像,请使用以下 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。

  1. 要删除图像,请使用以下 API:
DELETE  /images/<name>

以下是前述语法的一个例子:

$ curl -X DELETE
http://dockerhost.example.com:2375/images/wordpress:3.9.1

它是如何工作的…

在前面提到的所有情况下,API 将连接到 Docker 守护程序并执行请求的操作。

还有更多…

我们还没有涵盖之前讨论的 API 的所有选项,Docker 为其他与镜像相关的操作提供了 API。访问 API 文档以获取更多详细信息。

另请参阅

使用远程 API 执行容器操作

与我们使用 API 执行镜像操作类似,我们也可以使用 API 执行所有与容器相关的操作。

准备工作

配置 Docker 守护程序并允许远程访问,如前面的示例所述。

如何做…

在这个示例中,我们将看一些容器操作:

  1. 要列出容器,请使用以下 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

  1. 要创建一个新的容器,请使用以下 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

  1. 要启动一个容器,请使用以下 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 配置。

  1. 要检查一个容器,请使用以下 API:
GET  /containers/<id>/json

例如,检查 ID 为591ab8ac2650的容器:

$ curl -X GET http://dockerhost.example.com:2375/containers/591ab8ac2650/json

  1. 要获取正在容器内运行的进程列表,请使用以下 API:
GET /containers/<id>/top

例如,获取 ID 为591ab8ac2650的容器中正在运行的进程:

$ curl -X GET http://dockerhost.example.com:2375/containers/591ab8ac2650/top

  1. 要停止一个容器,请使用以下 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

如何做…

  1. 创建客户端,使用以下步骤:

  2. 通过 Unix 套接字连接:

 >>> client = docker.Client(base_url='unix://var/run/docker.sock', version='1.18',  timeout=10)

  1. 通过 HTTP 连接:
 >>> client = docker.Client(base_url='http://dockerhost.example.com:2375', version='1.18',  timeout=10)

在这里,base_url是要连接的端点,version是客户端将使用的 API 版本,timeout是以秒为单位的超时值。

  1. 使用以下代码搜索图像:
>>> client.search ("fedora")

  1. 使用以下代码拉取图像:
>>> client.pull("fedora", tag="latest")

  1. 使用以下代码启动容器:
>>> 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中提供的searchpullstart等方法的以下代码,该代码位于github.com/docker/docker-py/blob/master/docker/client.py

还有更多…

您可以探索为 Docker 编写的不同用户界面。其中一些如下所示:

保护 Docker 守护程序远程 API

在本章的前面,我们看到了如何配置 Docker 守护程序以接受远程连接。但是,使用我们遵循的方法,任何人都可以连接到我们的 Docker 守护程序。我们可以使用传输层安全性(en.wikipedia.org/wiki/Transport_Layer_Security)来保护我们的连接。

我们可以通过使用现有的证书颁发机构CA)或创建我们自己来配置 TLS。为简单起见,我们将创建自己的证书颁发机构,这在生产中不推荐。在本例中,我们假设运行 Docker 守护程序的主机是dockerhost.example.com

准备就绪

确保您已安装openssl库。

操作步骤...

  1. 在您的主机上创建一个目录,放置我们的 CA 和其他相关文件:
$ mkdirc-p /etc/docker
$ cd  /etc/docker

  1. 创建 CA 私钥和公钥:
$ openssl genrsa -aes256 -out ca-key.pem 2048
$ openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem

操作步骤...

  1. 现在,让我们创建服务器密钥和证书签名请求。确保通用名称与 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

操作步骤...

  1. 为了允许来自 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

操作步骤...

  1. 对于客户端认证,创建一个客户端密钥和证书签名请求:
$ openssl genrsa -out key.pem 2048
$ openssl req -subj '/CN=client' -new -key key.pem -out client.csr

操作步骤...

  1. 为了使密钥适用于客户端认证,创建一个扩展配置文件并签署公钥:
$ 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

操作步骤...

  1. 生成cert.pemserver-cert.pem后,我们可以安全地删除证书签名请求:
$ rm -rf client.csr server.csr

  1. 为了加强安全性并保护密钥免受意外损坏,让我们更改权限:
$ chmod -v 0600 ca-key.pem key.pem server-key.pem ca.pem server-cert.pem cert.pem

  1. 如果守护程序正在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

  1. 从另一个终端,转到/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_HOSTDOCKER_TLS_VERIFY环境变量来默认进行安全连接。

操作步骤...

  1. 要从我们在签署服务器密钥时提到的远程主机连接,我们需要将 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 个容器所需的时间:

Introduction

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/

  • 超越微基准-以特斯拉效率突破容器性能:

developerblog.redhat.com/2014/10/21/beyond-microbenchmarks-breakthrough-container-performance-with-tesla-efficiency/

  • 使用 Red Hat Enterprise Linux 容器化数据库:

rhelblog.redhat.com/2014/10/29/containerizing-databases-with-red-hat-enterprise-linux/

  • 来自 IBM

  • 虚拟机和 Linux 容器的性能比较的更新版本:

domino.research.ibm.com/library/cyberdig.nsf/papers/0929052195DD819C85257D2300681E7B/$File/rc25482.pdf

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。它有一些配置,您可以根据需要进行修改。

  1. 作为 root 用户,在主机上创建/results目录:
$ mkdir -p /results

现在,在将容器环境变量设置为与 Docker 不同的值后运行基准测试,我们在主机上使用该值构建c7perf镜像,运行以下命令:

$ cd docker-performance/bench/sysbench
$ export container=no
$ sh ./run-sysbench.sh  cpu test1

默认情况下,结果会收集在/results中。确保您对其具有写访问权限,或者在基准测试脚本中更改OUTDIR参数。

  1. 要在容器内运行基准测试,我们需要先启动容器,然后运行基准测试脚本:
$ mkdir /results_container
$ docker run -it -v /results_container:/results c7perf bash
$ docker-performance/bench/sysbench/run-sysbench.sh cpu test1

由于我们挂载了主机目录/results_container到容器内的/results,因此结果将在主机上收集。

  1. 在 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%为写入。

如何做...

  1. 对于裸机和虚拟机测试,您只需运行 FIO 作业文件并收集结果:
$ fio mixed.fio

  1. 对于 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"]
  1. 现在,使用以下命令创建一个镜像:
$ docker build -t docker_fio_perf .

  1. 按照以下方式启动容器以运行基准测试并收集结果:
$ docker run --rm -v /ferrari:/ferrari docker_fio_perf mixed.fio

  1. 在 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测量网络带宽,执行以下步骤:

  1. 在一个端点上启动nuttcp服务器:
$ nuttcp -S

  1. 使用以下命令从客户端测量传输吞吐量(客户端到服务器):
$ nuttcp -t <SERVER_IP>

  1. 使用以下命令在客户端上测量接收吞吐量(服务器到客户端):
$ nuttcp -r <SERVER_IP>

  1. 使用netperf运行请求/响应基准测试,执行以下步骤:

  2. 在一个端点上启动netserver

$ netserver

  1. 从另一个端点连接到服务器并运行请求/响应测试:
  • 对于 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 客户端访问。同时,启动一些容器以获取统计信息。

如何做…

  1. 运行以下命令从一个或多个容器获取统计信息:
$ docker stats [CONTAINERS]

例如,如果我们有两个名为some-mysqlbackstabbing_turing的容器,然后运行以下命令以获取统计信息:

$ docker stats some-mysql backstabbing_turing

操作方法…

它是如何工作的…

Docker 守护程序从 Cgroups 获取资源信息,并通过 API 提供它。

参见

设置性能监控

我们有诸如 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

操作方法…

容器启动后,将浏览器指向http://localhost:8080。您将首先获得有关主机机器的 CPU、内存使用情况和其他信息的图表。然后,通过单击 Docker 容器链接,您将在子容器部分下获得运行在机器上的容器的 URL。如果单击其中任何一个,您将看到相应容器的资源使用信息。

以下是一个这样的容器的屏幕截图:

操作方法…

它是如何工作的…

使用docker run命令,我们已经以只读模式挂载了一些卷,cAdvisor 将从中读取相关信息,比如容器的 Cgroup 详细信息,并以图形方式显示它们。

还有更多…

cAdvisor 支持将性能矩阵导出到 influxdb(influxdb.com/)。Heapster(github.com/GoogleCloudPlatform/heapster)是 Google 的另一个项目,它允许使用 cAdvisor 进行集群范围(Kubernetes)监控。

参见

第八章: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

如何做…

  1. 为应用程序创建一个目录,并在其中创建docker-compose.yml来定义应用程序:如何做…

  2. 我们从 Docker Hub 的官方 WordPress Docker 存储库(registry.hub.docker.com/_/wordpress/)中获取了上述示例。

  3. 在应用程序目录中,运行以下命令构建应用程序:

$ docker-compose up

  1. 构建完成后,从http://localhost:8080http://<host-ip>:8080访问 WordPress 安装页面。

它是如何工作的…

Docker Compose 会从官方 Docker 注册表下载mariadbwordpress镜像(如果本地不存在)。首先,它会从mariadb镜像启动db容器;然后启动wordpress容器。接下来,它会与db容器进行链接,并将端口导出到主机。

更多内容…

我们甚至可以在 Compose 期间从 Dockerfile 构建镜像,然后将其用于应用程序。例如,要构建wordpress镜像,我们可以从应用程序的 Compose 目录中获取相应的 Dockerfile 和其他支持文件,并以类似的方式更新docker-compose.yml文件:

更多内容…

我们可以启动、停止、重建和获取应用程序的状态。请访问 Docker 网站上的文档。

另请参阅

使用 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 的后端来配置主机。

准备工作

  1. 在您的系统上安装 VirtualBox (www.virtualbox.org/)。配置 VirtualBox 的说明不在本书的范围之内。

  2. 下载并设置 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

操作方法…

  1. 使用 Swarm 发现服务,我们首先需要创建一个 Swarm 令牌来唯一标识我们的集群。除了默认的托管发现服务外,Swarm 还支持不同类型的发现服务,如 etcd、consul 和 zookeeper。有关更多详细信息,请访问docs.docker.com/swarm/discovery/。要使用默认的托管发现服务创建令牌,我们将首先在 VM 上使用 Docker Machine 设置 Docker 主机,然后获取令牌:
$ docker-machine create -d virtualbox local

  1. 要从本地 Docker 客户端访问我们刚创建的 Docker,请运行以下命令:
$ eval "$(docker-machine env local)"

  1. 要获取令牌,请运行以下命令:
$ docker run swarm create
7c3a21b42708cde81d99884116d68fa1

  1. 使用前一步骤中创建的令牌,设置 Swarm 主节点:
$ docker-machine create  -d virtualbox  --swarm  --swarm-master  --swarm-discovery token://7c3a21b42708cde81d99884116d68fa1  swarm-master

  1. 同样,让我们创建两个 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

  1. 现在,从本地 Docker 客户端连接到 Docker Swarm:
$ eval "$(docker-machine env swarm-master)"

  1. Swarm API 与 Docker 客户端 API 兼容。让我们运行docker info命令来查看 Swarm 的当前配置/设置:
$ docker info

操作方法…

如您所见,我们的集群中有三个节点:一个主节点和两个节点。

工作原理…

使用我们从托管发现服务获得的唯一令牌,我们在集群中注册了主节点和节点。

还有更多...

另请参阅

为 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 使用。我们可以:

在 CoreOS 中,etcd用于协调集群。它提供了一种以一致的方式存储配置和关于服务的信息的机制。

  • 容器运行时:CoreOS 支持 Docker 作为容器运行时环境。在 2014 年 12 月,CoreOS 宣布推出了一个新的容器运行时 Rocket (coreos.com/blog/rocket/)。让我们将讨论限制在目前安装在所有 CoreOS 机器上的 Docker 上。

  • systemdsystemd是用于启动、停止和管理进程的初始化系统。在 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 targetdocker socket服务之后启动。docker socket是 Docker 守护程序启动的先决条件。Systemd 目标是将进程分组以便它们可以同时启动的方式。multi-user是前面单元文件注册的目标之一。有关更多详细信息,您可以查看 Systemd 的上游文档www.freedesktop.org/wiki/Software/systemd/

“Fleet 集群中的每个系统都运行一个fleetd守护程序。每个守护程序封装了两个角色:引擎代理。引擎主要做出调度决策,而代理执行单元。引擎和代理都使用协调模型,定期生成'当前状态'和'期望状态'的快照,并进行必要的工作将前者变异为后者。”

etcdfleet集群中唯一的数据存储。所有持久和临时数据都存储在etcd中;单元文件、集群存在、单元状态等。etcd也用于 fleet 引擎和代理之间的所有内部通信。

现在我们知道了 CoreOS 的所有构建模块。让我们在本地系统/笔记本上尝试 CoreOS。为了保持简单,我们将使用 Vagrant 来设置环境。

准备就绪

  1. 在系统上安装 VirtualBox(www.virtualbox.org/)和 Vagrant(www.vagrantup.com/)。配置这两个东西的说明超出了本书的范围。

  2. 克隆coreos-vagrant存储库:

$ git clone https://github.com/coreos/coreos-vagrant.git
$ cd coreos-vagrant

  1. 将示例文件user-data.sample复制到user-data并设置引导集群的令牌:
$ cp user-data.sample user-data

  1. 当我们使用多个节点配置 CoreOS 集群时,我们需要一个令牌来引导集群以选择初始的 etcd 领导者。这项服务由 CoreOS 团队免费提供。我们只需要在浏览器中打开https://discovery.etcd.io/new来获取令牌,并在user-data文件中更新如下:准备就绪

  2. config.rb.sample复制到config.rb并更改以下行:

$num_instances=1

现在应该是这样的:

$num_instances=3

这将要求 Vagrant 设置三个节点集群。默认情况下,Vagrant 配置为从 alpha 版本获取 VM 映像。我们可以通过在 Vagrantfile 中更新$update_channel参数将其更改为 beta 或 stable。对于这个示例,我选择了 stable。

操作步骤如下…

  1. 运行以下命令设置集群:
$ vagrant up

现在,使用以下截图中显示的命令检查状态:

操作步骤如下…

  1. 使用 SSH 登录到其中一个 VM,查看服务状态,并列出集群中的机器:
$ vagrant ssh core-01
$ systemctl status etcd fleet
$ fleetctl list-machines

操作步骤如下…

  1. 创建一个名为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
  1. 现在让我们提交服务进行调度并启动服务:
$ fleetctl submit myapp.service
$ fleetctl start myapp.service
$ fleetctl list-units

操作步骤如下…

正如我们所看到的,我们的服务已经在集群中的一个节点上启动。

工作原理...

Vagrant 使用云配置文件(user-data)来引导 VM。由于它们具有相同的令牌来引导集群,它们选择领导者并开始操作。然后,使用fleetctl,这是 fleet 集群管理工具,我们提交单元文件进行调度,该文件在一个节点上启动。

还有更多...

  • 使用此配方中的云配置文件,我们可以在所有 VM 上启动etcdfleet。我们可以选择仅在选定的节点上运行etcd,然后配置运行fleet的工作节点以连接到 etcd 服务器。可以通过相应地设置云配置文件来完成此操作。有关更多信息,请访问coreos.com/docs/cluster-management/setup/cluster-architectures/

  • 使用fleet,我们可以为高可用性配置服务。有关更多信息,请查看coreos.com/docs/launching-containers/launching/fleet-unit-files/

  • 尽管您的服务正在主机上运行,但您将无法从外部访问它。您需要添加某种路由器和通配符 DNS 配置,以便从外部世界访问您的服务。

另请参阅

设置 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 在虚拟机上安装它。

做好准备

  1. 下载图像:
$ 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/上寻找最新的云图像用于容器

  1. 使用以下命令解压此图像:
$ xz -d Fedora-Cloud-Atomic-22_Beta-20150415.x86_64.raw.xz

如何做到这一点…

  1. 我们下载了一个没有为默认用户fedora设置任何密码的云镜像。在启动虚拟机时,我们必须通过一个云配置文件来自定义虚拟机。为此,我们需要创建两个文件,meta-datauser-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

  1. 启动 virt-manager。

  2. 选择新建虚拟机,然后导入现有的磁盘映像。输入我们之前下载的 Project Atomic 映像的路径。选择操作系统类型Linux版本Fedora 20/Fedora 21(或更高版本),然后点击下一步。接下来,分配 CPU 和内存,然后点击下一步。然后,在安装之前为虚拟机命名并选择自定义配置。最后,点击完成并查看详细信息。

  3. 接下来,点击添加硬件,在选择存储后,将我们创建的 ISO(init.iso)文件附加到虚拟机,并选择开始安装如何操作…

启动后,您可以看到其主机名已正确设置,并且您将能够使用云初始化文件中给定的密码登录。默认用户是fedora,密码是atomic,如在user-data文件中设置的那样。

工作原理…

在这个示例中,我们使用virt-manager提供云初始化文件,使用 Project Atomic Fedora 云镜像引导了虚拟机。

更多内容…

  • 登录后,如果在/目录下列出文件,你会看到大多数传统目录都链接到/var,因为它在升级过程中会被保留。更多内容…

  • 登录后,您可以像往常一样运行 Docker 命令:

$sudo docker run -it fedora bash

另请参阅

使用 Project Atomic 进行原子更新/回滚

要升级到最新版本或回滚到 Project Atomic 的旧版本,我们使用atomic host命令,该命令内部调用 rpm-ostree。

准备工作

启动并登录到 Atomic 主机。

如何做…

  1. 启动后,运行以下命令:
$ atomic host status

您将看到有关当前正在使用的部署的详细信息。

如何做…

升级,请运行以下命令:

如何做…

  1. 这将更改和/或添加新的软件包。升级后,我们需要重新启动系统以使用新的更新。让我们重新启动并查看结果:如何做…

正如我们所看到的,系统现在已经使用新的更新启动。位于第一行开头的*表示活动构建。

  1. 要回滚,请运行以下命令:
$ sudo atomic host rollback

如果我们想使用旧的位,我们将不得不再次重启。

工作原理…

对于更新,Atomic 主机连接到托管较新构建的远程存储库,该构建将在下一次重启后下载并使用,直到用户升级或回滚。在回滚的情况下,系统上可用的旧构建将在重启后使用。

另请参阅

在 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 的存储空间:

在 Project Atomic 中为 Docker 添加更多存储空间

正如我们所看到的,总数据空间为 2.96 GB,总元数据空间为 8.38 MB。

做好准备

  1. 停止 VM,如果它正在运行。

  2. 向 Project Atomic VM 添加所需大小的额外磁盘。我已经添加了 8 GB。

  3. 启动 VM。

  4. 检查新添加的磁盘是否对 VM 可见。

如何操作...

  1. 检查附加磁盘是否可用于 Atomic 主机 VM:如何操作...

正如我们所看到的,新创建的 8 GB 磁盘对 VM 可用。

  1. 由于新添加的磁盘是/dev/sdb,因此创建一个名为/etc/sysconfig/docker-storage-setup的文件,并包含以下内容:
DEVS="/dev/sdb"
[fedora@atomichost ~]$ cat /etc/sysconfig/docker-storage-setup
DEVS="/dev/sdb"

  1. 运行docker-storage-setup命令将/dev/sdb添加到现有卷中:
$ sudo docker-storage-setup

如何操作...

  1. 现在,让我们再次使用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 设置 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 主机并登录。

如何操作…

  1. 运行以下命令启动 Cockpit 容器:
[fedora@atomichost ~]$ sudo atomic run stefwalter/cockpit-ws

如何操作…

  1. 打开浏览器(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 镜像,我们可以使用标签为镜像和容器分配元数据。这里的标签是INSTALLUNINSTALLRUNatomic命令是 Project Atomic 特有的命令,它读取这些标签并执行操作。由于容器作为 SPC 运行,因此不需要从主机到容器的端口转发。有关atomic命令的更多详细信息,请访问developerblog.redhat.com/2015/04/21/introducing-the-atomic-command/

还有更多...

您可以通过 GUI 执行几乎所有管理员任务。您可以通过这个管理 Docker 镜像/容器。您可以执行以下操作:

  • 拉取镜像

  • 启动/停止容器

您还可以将其他机器添加到同一个 Cockpit 实例中,以便从一个中央位置管理它们。

另请参阅

设置 Kubernetes 集群

Kubernetes 是一个开源的容器编排工具,可以跨集群的多个节点进行操作。目前,它只支持 Docker。它是由 Google 发起的,现在其他公司的开发人员也在为其做出贡献。它提供了应用部署、调度、更新、维护和扩展的机制。Kubernetes 的自动放置、自动重启、自动复制功能确保了应用程序的期望状态得以维持,这是由用户定义的。用户通过 YAML 或 JSON 文件定义应用程序,我们稍后会看到。这些 YAML 和 JSON 文件还包含 API 版本(apiVersion字段)来识别模式。以下是 Kubernetes 的架构图:

设置 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 上尝试的。

准备工作

  1. www.vagrantup.com/downloads.html安装最新的 Vagrant >= 1.6.2。

  2. www.virtualbox.org/wiki/Downloads安装最新的 VirtualBox。如何设置这个的详细说明超出了本书的范围。

如何做...

  1. 运行以下命令在 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

您将看到一些列出的podsservicesreplicationControllers,因为 Kubernetes 为内部使用创建它们。

另请参阅

在 Kubernetes 集群中进行扩展和缩减

在前一节中,我们提到复制控制器确保用户指定数量的 pod 副本在任何给定时间都在运行。要使用复制控制器管理副本,我们必须定义一个具有 pod 副本计数的配置文件。此配置可以在运行时更改。

准备就绪

确保 Kubernetes 设置正在按照前面的配方运行,并且您在kubernetes目录中,该目录是使用前面的安装创建的。

如何做…

  1. 启动带有 3 个副本计数的nginx容器:
$ ./cluster/kubectl.sh run-container my-nginx --image=nginx --replicas=3 --port=80

如何做…

这将启动nginx容器的三个副本。列出 pod 以获取状态:

$  ./cluster/kubectl.sh get pods

  1. 获取复制控制器配置:
$ ./cluster/kubectl.sh get replicationControllers

如何做…

如您所见,我们有一个my-nginx控制器,其副本计数为 3。还有一个kube-dns的复制控制器,我们将在下一个配方中探索。

  1. 请求复制控制器服务将副本缩减为 1 并更新复制控制器:
$ ./cluster/kubectl.sh resize rc my-nginx –replicas=1
$ ./cluster/kubectl.sh get rc

如何做…

  1. 获取 pod 列表以进行验证;您应该只看到一个nginx的 pod:
$  ./cluster/kubectl.sh get pods

工作原理…

我们请求在主节点上运行的复制控制器服务更新 pod 的副本,这将更新配置并要求节点/从节点相应地进行调整以遵守调整大小。

还有更多…

获取服务:

$ ./cluster/kubectl.sh get services

还有更多…

正如你所看到的,我们之前启动的nginx容器没有定义任何服务。这意味着虽然我们有一个正在运行的容器,但我们无法从外部访问它们,因为相应的服务没有定义。

另请参阅

在 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文件分别描述了mysqlwordpress的 pod 和服务。

  • 在 pod 文件(mysql.yamlwordpress.yaml)中,您将找到关于卷和相应volumeMount文件的部分。原始示例假定您可以访问 Google Compute Engine 并且已经设置了相应的存储。为简单起见,我们将不设置它,而是使用EmptyDir卷选项的临时存储。供参考,我们的mysql.yaml将如下所示:准备工作

  • wordpress.yaml进行类似的更改。

操作步骤…

  1. 通过 SSH 登录到 master 节点并查看正在运行的 pod:
$ vagrant ssh master
$ kubectl get pods

操作步骤…

kube-dns-7eqp5 pod 包含三个容器:etcdkube2skyskydns,用于配置内部 DNS 服务器以进行服务名到 IP 的解析。我们稍后会在这个示例中看到它的运行。

在这个示例中使用的 Vagrantfile 是这样创建的,我们之前创建的kubernetes目录在 VM 下被共享为/vagrant,这意味着我们对主机系统所做的更改也会在这里可见。

  1. 从主节点创建mysql pod 并检查运行中的 pod:
$ kubectl create -f /vagrant/examples/mysql-wordpress-pd/mysql.yaml
$ kubectl get pods

如何做…

我们可以看到,一个名为mysql的新 pod 已经被创建,并且正在运行在主机10.245.1.3上,这是我们的节点(minion)。

  1. 现在让我们为mysql创建服务并查看所有服务:
$ kubectl create -f /vagrant/examples/mysql-wordpress-pd/mysql-service.yaml
$ kubectl get services

如何做…

我们可以看到,一个名为mysql的服务已经被创建。每个服务都有一个虚拟 IP。除了kubernetes服务,我们还看到一个名为kube-dns的服务,它被用作我们之前看到的kube-dns pod 的服务名。

  1. 类似于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 脚本能够进行反向查找以继续向前进行。

  1. 启动 pod 后,这里的最后一步是设置wordpress服务。在默认示例中,你会在服务文件(/vagrant/examples/mysql-wordpress-pd/mysql-service.yaml)中看到类似以下的条目:
createExternalLoadBalancer: true

这篇文章是为了记住这个示例将在 Google Compute Engine 上运行。所以这里不适用。我们需要做的是像下面这样做一个条目:

publicIPs: 
    - 10.245.1.3

我们用节点的公共 IP 替换了负载均衡器的条目,这在我们的情况下就是节点(minion)的 IP 地址。因此,wordpress文件看起来会像下面这样:

如何操作...

  1. 要启动wordpress服务,请从主节点上运行以下命令:
$ kubectl create -f /vagrant/examples/mysql-wordpress-pd/wordpress-service.yaml

如何操作...

我们可以看到我们的 service 也可以通过节点(minion)IP 访问。

  1. 要验证一切是否正常工作,我们可以在主节点上安装 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

  • 在这个示例中,我们没有配置复制控制器。我们可以通过创建它们来扩展这个示例。

另请参阅

第九章: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 上进行默认安装的实验。

  1. 使用以下命令禁用 SELinux:
$ sudo setenforce 0 

  1. 创建一个用户并将其添加到默认的 Docker 组中,以便用户可以在不使用sudo的情况下运行 Docker 命令:
$ sudo useradd dockertest 
$ sudo passwd dockertest 
$ sudo groupadd docker 
$ sudo gpasswd -a dockertest docker 

  1. 使用我们之前创建的用户登录,启动容器如下:
$ su - dockertest 
$ docker run -it -v /:/host fedora bash 

  1. 从容器 chroot 到/host并运行shutdown命令:
$ chroot /host 
$ shutdown 

Introduction

正如我们所看到的,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_tsvirt_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 一起启动。要检查是否满足这些条件,请执行以下步骤。

  1. 运行以下命令以确保 SELinux 已启用:
$ getenforce 

如果前面的命令返回enforcing,那就很好,否则我们需要通过更新 SELinux 配置文件(/etc/selinux/config)并重新启动系统来进行更改。

  1. 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。

如何做...

  1. 使用以下方式使用zZ选项挂载卷:
$ 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 添加了一些功能,用于为容器添加或删除功能。它默认使用chowndac_overridefownerkillsetgidsetuidsetpcapnet_bind_servicenet_rawsys_chrootmknodsetfcapaudit_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 客户端访问。

如何做…

  1. 要删除功能,运行类似以下命令:
$ docker run --cap-drop <CAPABILITY> <image> <command> 

要从容器中删除setuidsetgid功能,以便它无法运行具有这些位设置的二进制文件,运行以下命令:

$ docker run -it --cap-drop  setuid --cap-drop setgid fedora bash 

  1. 同样,要添加功能,运行类似以下命令:
$ 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 

还有更多...

另请参阅

在主机和容器之间共享命名空间

我们知道,在启动容器时,默认情况下,Docker 为容器创建六个不同的命名空间——进程、网络、挂载、主机名、共享内存和用户。在某些情况下,我们可能希望在两个或更多的容器之间共享命名空间。例如,在 Kubernetes 中,一个 pod 中的所有容器共享相同的网络命名空间。

在某些情况下,我们希望与容器共享主机系统的命名空间。例如,我们在主机和容器之间共享相同的网络命名空间,以在容器内获得接近线速。在这个教程中,我们将看到如何在主机和容器之间共享命名空间。

准备工作

安装了最新版本的 Docker 的主机,可以通过 Docker 客户端访问。

如何做…

  1. 要与容器共享主机网络命名空间,请运行以下命令:
$ docker run -it  --net=host fedora bash 

如果要在容器内查看网络详细信息,请运行以下命令:

$ ip a 

您将看到与主机相同的结果。

  1. 要与容器共享主机网络、PID 和 IPC 命名空间,请运行以下命令:
$ docker run -it --net=host --pid=host --ipc=host fedora bash 

它是如何工作的…

当传递这些参数给容器时,Docker 不会为容器创建单独的命名空间。

还有更多...

对于专门用于运行容器的主机,比如 Project Atomic (www.projectatomic.io/),我们在第八章中看到的Docker 编排和托管平台,在主机系统上没有像tcpdumpsysstat这样的调试工具。因此,我们创建了带有这些工具并可以访问主机资源的容器。在这种情况下,在主机和容器之间共享命名空间变得很方便。您可以在以下链接中了解更多信息:

另请参阅

第十章:获取帮助和技巧

在本章中,我们将看到以下配方:

  • 以调试模式启动 Docker

  • 从源代码构建 Docker 二进制文件

  • 构建图像而不使用缓存层

  • 为容器通信构建自己的桥接

  • 更改 Docker 的默认执行驱动程序

  • 为容器选择日志记录驱动程序

  • 获取容器的实时 Docker 事件

介绍

随着我们对 Docker 的了解越来越多,我们会变得更加好奇。邮件列表和 IRC 频道是获取帮助、学习和分享关于 Docker 知识的最佳场所。Docker 在免费节点上有一些 IRC 频道,如#docker#docker-dev,分别用于讨论 Docker 和与开发相关的内容。同样,Docker 有两个邮件列表:

在使用 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。

如何做…

  1. 使用调试选项-D启动 Docker 守护进程。要从命令行启动,可以运行以下命令:
$ docker -d -D

  1. 您还可以在 Docker 配置文件中添加--debug/-D选项以以调试模式启动。

它是如何工作的…

上述命令将以守护程序模式启动 Docker。当您启动守护程序时,您将看到许多有用的消息,例如加载现有图像,防火墙设置(iptables)等。如果启动容器,您将看到以下消息:

[info] POST /v1.15/containers/create
[99430521] +job create()
......
......

从源代码构建 Docker 二进制文件

有时需要从源代码构建 Docker 二进制文件以测试补丁。从源代码构建 Docker 二进制文件非常容易。

准备工作

  1. 使用git下载 Docker 源代码:
$ git clone https://github.com/docker/docker.git

  1. 在 Fedora 上安装make
$ yum install -y make

  1. 确保 Docker 在构建代码的主机上运行,并且您可以通过 Docker 客户端访问它,因为我们讨论的构建发生在容器内。

如何做…

  1. 进入克隆的目录:
$ cd docker

  1. 运行make命令:
$ sudo make

工作原理…

这将创建一个容器,并在其中从主分支编译代码。完成后,它将在bundles/<version>/binary中输出二进制文件。

还有更多…

  • 与源代码类似,您也可以构建文档:
$ sudo make docs

  • 您还可以使用以下命令运行测试:
 $ sudo make test

另请参阅

构建图像而不使用缓存层

默认情况下,当我们构建图像时,Docker 将尝试使用缓存的层,以便构建时间更短。但是,有时需要从头开始构建。例如,您需要强制进行系统更新,例如yum -y update。让我们看看如何在这个示例中做到这一点。

准备工作

获取一个 Dockerfile 来构建镜像。

如何做…

  1. 构建镜像时,通过以下方式传递--no-cache选项:
$ docker build -t test --no-cache - < Dockerfile

工作原理…

--no-cache选项将丢弃任何缓存的层,并根据指令构建一个 Dockerfile。

还有更多…

有时,我们还想在仅执行几条指令后丢弃缓存。在这种情况下,我们可以添加任何不影响图像的任意命令,例如创建或设置环境变量。

为容器通信构建自己的桥接

我们知道,当 Docker 守护程序启动时,它会创建一个名为docker0的桥接,并且所有容器都将从中获取 IP。有时我们可能想要自定义这些设置。让我们看看如何在这个示例中做到这一点。

准备工作

我假设您已经设置好了 Docker。在 Docker 主机上,停止 Docker 守护程序。在 Fedora 上,使用以下命令:

$ systemctl stop docker

如何做…

  1. 要删除默认的docker0桥接,请使用以下命令:
$ sudo ip link set dev docker0 down
$ sudo brctl delbr docker0

  1. 要创建自定义桥接,请使用以下命令:
$ sudo brctl addbr br0
$ sudo ip addr add 192.168.2.1/24 dev br0
$ sudo ip link set dev bridge0 up

  1. 更新 Docker 配置文件以使用我们之前创建的桥接。在 Fedora 上,您可以按以下方式更新配置文件:
$ sed -i '/^OPTIONS/ s/$/ --bridge br0/' /etc/sysconfig/docker

  1. 要启动 Docker 守护程序,请使用以下命令:
$ systemctl start docker

工作原理…

上述步骤将创建一个新的桥接,并将从 192.168.2.0 子网中为容器分配 IP。

还有更多…

您甚至可以向桥接添加接口。

另请参阅

更改 Docker 的默认执行驱动程序

正如我们所知,libcontainer 是默认的执行驱动程序。对于 LXC 用户空间工具(linuxcontainers.org/)有传统支持。请记住,LXC 不是主要的开发环境。

准备工作

在系统上安装 Docker。

如何做…

  1. 以以下方式启动 Docker 守护程序,使用-e lxc选项:
$ docker -d -e lxc

您还可以根据发行版在 Docker 的配置文件中添加此选项。

工作原理…

Docker 使用 LXC 工具访问内核功能,如命名空间和 Cgroups 来运行容器。

另请参阅

为容器选择日志驱动程序

随着 Docker 1.6 的发布,新增了一个功能,可以在启动 Docker 守护程序时选择日志驱动程序。目前支持三种类型的日志驱动程序:

  • none

  • json-file(默认)

  • syslog

准备工作

在系统上安装 Docker 1.6 或更高版本。

如何做…

  1. 以以下方式启动 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 客户端连接。

如何做…

  1. 使用以下命令启动 Docker 事件日志记录:
$ docker events

  1. 从另一个终端执行一些与容器/镜像相关的操作,您将在第一个终端上看到类似以下截图的结果:如何做…

在事件收集开始后,我创建了一个容器来打印一些东西。如前面的截图所示,一个容器被创建、启动和死亡。

工作原理…

使用 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'

另请参阅

posted @ 2024-05-06 18:33  绝不原创的飞龙  阅读(9)  评论(0编辑  收藏  举报