Docker 容器技术使用指南

0    105    1

Tags:

👉 本文共约36374个字,系统预计阅读时间或需137分钟。

本页目录 隐藏

[toc]

第一部分 Docker 容器技术基础及其应用场景介绍

1.1 Docker 的基本概念

Docker 容器是资源分割和调度的基本单位,封装整个服务的运行时环境,用于构建、发布和运行分布式应用的一个框架。它是一个跨平台、可移植并且简单易用的容器解决方案。Docker 的源代码托管在 GitHub 上,基于 Go 语言开发并遵从 Apache 2.0 协议。

Docker 容器可以快速自动化地部署应用,并通过操作系统内核技术(namespaces、 cgroups等)为容器提供资源隔离与安全保障。Docker 作为轻量级的虚拟化方式,实现了 PaaS 平台的高效部署、运行和维护。

图片

1.2 为什么使用 Docker

Docker 容器技术使用指南

( 1 ) 、持续部署与测试

Docker 消除了线上线下的环境差异,保证了应用生命周期的环境一致性和标准化。开发人员使用镜像实现标准开发环境的构建,开发完成后通过封装着完整环境和应用的镜像进行迁移,由此,测试和运维人员可以直接部署软件镜像来进行测试和发布,大大简化了持续集成、测试和发布的过程。

Docker 是革命性的,它重新定义了软件开发、测试、交付和部署的流程。我们交付的东西不再只是代码、配置文件、数据库定义等,而是整个应用服务及其运行环境。

( 2 ) 、优异的跨平台性

Docker 在原有 Linux 容器的基础上进行大胆革新,为容器设定了一 整套标准化的配置方法,将应用及其依赖的运行环境打包成镜像。Docker 是可移植(或者说跨平台)的,可以在各种主流操作系统上使用。Java 可以做到“一次编译,到处运行”,而 Docker 可以 “构建一次,在各平台上运行”(Build once,run anywhere)。越来越多的云平台都支持 Docker,用户再也无需担心受到云平台的捆绑,同时也让应用多平台混合部署成为可能。

( 3 ) 、高资源利用率与隔离

Docker 容器没有管理程序的额外开销,与底层共享操作系统,性能更加优良,系统负载更低,在同等条件下可以运行更多的应用实例,可以更充分地利 用系统资源。同时,Docker 拥有不错的资源隔离与限制能力,可以精确地对应用分配 CPU、 内存等资源,保证了应用间不会相互影响。Docker 是轻量级虚拟化技术。与传统的 VM 相比,它更轻量,启动速度更快,单台硬件上可以同时跑成百上千个容器,所以非常适合在业务高峰期通过启动大量容器进行横向扩展。Docker 容器技术的直接虚拟化不仅在技术方面使 CPU 利用率得到显著提升,还因 80:20 法则可在业务上更大程度发挥 CPU 利用率,真正体现了虚拟化精髓。

( 4 ) 、环境标准化和版本控制

可以使用 Git 等工具对 Docker 镜像进行版本控制,相比基于代码的版本控制来说,能够对整个应用运行环境实现版本控制,一旦出现故障可以快速回滚。相比以前的虚拟机镜像,Docker 压缩和备份速度更快,镜像启动也像启动一个普通进程一样快速。

( 5 ) 、应用镜像仓库

Docker 官方构建了一个镜像仓库,组织和管理形式类似于 GitHub,其上 已累积了成千上万的镜像。因为 Docker 的跨平台适配性,相当于为用户提供了一个非常 有用的应用商店,所有人都可以自由地下载微服务组件,这为开发者提供了巨大便利。

1.3 Docker 体系结构简介

Docker 容器技术使用指南

Docker 是一个客户/服务器(CIient/Server,CS)架构(见上图) 。Docker 客户端是远程控制器,可通过 TCP REST 向 Docker Host 发送请求,包括创建容器、运行容器、保存容器、删除容器等请求。Docker 服务端的 Daemon 对客户端的请求进行相应的管理,随后通过 driver 转发至容器中的 libcontainer 执行环境。libcontainer 提供与不同 Linux 内核隔离的接口,类似命名空间及控制组。这种架构允许多个容器在共享同一个 Linux 内核的情况下完全隔离地运行。

Docker 容器技术使用指南

1.4 Docker 容器技术的应用场景

一般认为 Docker 技术有以下 8 个主要的应用场景,参见下图:

Docker 容器技术使用指南

( 1 ) 、应用场景 1:简化配置

这是 Docker 公司宣传的 Docker 的主要使用场景。Docker 能将运行环境和配置放在代码中然后部署,同一个 Docker 的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。

( 2 ) 、应用场景 2:代码流水线(Code Pipeline)管理

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

( 3 ) 、应用场景 3:提高开发效率

Docker 能提升开发者的开发效率。不同的开发环境中,Docker 都可以把两件事做好,一是可以在开发环境、生产环境之间直接迁移,二是可以让我们快速搭建开发环境。开发环境的机器通常内存比较小,之前使用虚拟的时候,我们经常需要为开发环境的机器加内存,而现在Docker 可以轻易的让几十个服务在 Docker 中跑起来。

( 4 ) 、应用场景 4:隔离应用

有很多种原因会让我们选择在一个机器上运行不同的应用,Docker 非常适合在较低的成本下实现多种应用的隔离。

( 5 ) 、应用场景 5:整合服务器

Docker 隔离应用的能力使得 Docker 可以整合多个服务器以降低成本。由于没有操作系统的内存占用,以及能在多个实例之间共享没有使用的内存,Docker 可以比虚拟机提供更好的服务器整合解决方案。通常数据中心的服务器资源利用率只有 30%,通过使用 Docker 并进行有效的资源分配可以大幅提高服务器资源的利用率。

( 6 ) 、应用场景 6:调试能力

Docker 提供了很多的工具,包括可以为容器设置检查点、设置版本和查看两个容器之间的差别,这些特性可以帮助调试 Bug。

( 7 ) 、应用场景 7:多租户环境

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

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

( 8 ) 、应用场景 8:快速部署

在虚拟机之前,购入部署新的硬件资源需要消耗几天的时间。虚拟化技术(Virtualization)将这个时间缩短到了分钟级别。而 Docker 通过为进程仅仅创建一个容器而无需启动一个操作系统,再次将这个过程缩短到了秒级。这正是 Google 和 Facebook 都看重的特性。我们可以创 建销毁 Docker 容器而无需担心重新启动带来的开销。

第二部分 核心概念与安装配置

本部分首先介绍 Docker 的三大核心概念。

 镜像(Image)

 容器(Container)

 仓库(Repository)

只有理解了这三个核心概念,才能顺利地理解 Docker 容器的整个生命周期。随后将介绍如何在常见的操作系统平台上安装 Docker,包括 Redhat Linux、Windows、Centos 等主流操作系统平台。

2.1 核心概念

Docker 的大部分操作都围绕着它的三大核心概念——镜像、容器和仓库而展开。因此,准确把握这三大核心概念对于掌握 Docker 技术尤为重要。

1 . Docker 镜像

Docker 镜像类似于虚拟机镜像,可以将它理解为一个只读的模板。例如,一个镜像可以包含一个基本的操作系统环境,里面仅安装了 Apache 应用程序(或用户需要的其他软件)。可以把它称为一个Apache 镜像。

镜像是创建 Docker 容器的基础。通过版本管理和增量的文件系统,Docker 提供了一套十分简单的机制来创建和更新现有的镜像,用户甚至可以从网上下载一个已经做好的应用镜像, 并直接使用 。

2 . Docker 容器

Docker 容器类似于一个轻量级的沙箱,Docker 利用容器来运行和隔离应用。容器是从镜像创建的应用运行实例。可以将其启动、开始、停止、删除,而这些容器都是彼此相互隔离的、互不可见的。

可以把容器看做是一个简易版的 Linux 系统环境(包括 root 用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子。

3 . Docker 仓库

Docker 仓库类似于代码仓库,它是 Docker 集中存放镜像文件的场所。

有时候会看到有资料将 Docker 仓库和仓库注册服务器(Registry)混为一谈,并不严格区分。实际上,仓库注册服务器是存放仓库的地方,其上往往存放着多个仓库。每个仓库集中存放某一类镜像,往往包括多个镜像文件,通过不同的标签(tag)来进行区分。例如存放 ubuntu操作系统镜像的仓库称为 ubuntu 仓库,其中可能包括 14.04、12.04 等不同版本的镜像。

根据所存储的镜像公开分享与否,Docker 仓库可以分为公开仓库(Public)和私有仓库(Private)两种形式。目前,最大的公开仓库是官方提供的 Docker Hub,其中存放了数量庞大的镜像供用户下载。国内不少云服务提供商(如时速云、阿里云等)也提供了仓库的本地源,可以提供稳定的国内访问。

当然,用户如果不希望公开分享自己的镜像文件,Docker 也支持用户在本地网络内创建一个只能自己访问的私有仓库。当用户创建了自己的镜像之后就可以使用 push 命令将它上传到指定的公有或者私有仓库。这样用户下次在另外一台机器上使用该镜像时,只需要将其从仓库上 pull 下来就可以了。

可以看出,Docker 利用仓库管理镜像的设计理念与 Git 非常相似,实际上在理念设计上借鉴了 Git 的很多优秀思想。

2.2 安装Docker

Docker 在主流的操作系统和云平台上都可以使用,包括 Linux 操作系统(如 ubuntu、Debian、CentOS、Redhat 等)、MacOS 操作系统和 Windows 操作系统,以及 AWS 等云平台。

用户可以访问 Docker 官网的 Get Docker(https://www.docker.com/products/overview)页面,查看获取 Docker 的方式,以及 Docker 支持的平台类型,如图 2-2 所示。

在 Get Docker 页面,我们可以看到目前 Docker 支持 Docker Platform、Docker Hub、Docker Cloud 和 Docker DataCenter。

 Docker Platform:支持在桌面系统或云平台安装 Docker;

 DockerHub:官方提供的云托管服务,可以提供公有或私有的镜像仓库;

 DockerCloud:官方提供的容器云服务,可以完成容器的部署与管理,可以完整地支持容器化项目,还有 CI、CD 功能;

 Docker DataCenter:提供企业级的简单安全弹性的容器集群编排和管理。

推荐尽量使用 Linux 操作系统来运行 Docker,因为目前 Linux 操作系统对 Docker的支持是原生的,使用体验最好。

2.2.1 在 Red Hat Enterprise Linux 上安装 Docker

以下是支持 Docker 的 RHEL 版本:

Red Hat Enterprise Linux 7 (64-bit)

Red Hat Enterprise Linux 6.5 (64-bit) 或更高版本

如果你的 RHEL 运行的是发行版内核。那就仅支持通过 extras 渠道或者 EPEL 包来安装Docker。如果我们打算在非发行版本的内核上运行 Docker ,内核的改动可能会导致出错

1.Red Hat Enterprise Linux 7 安装 Docker

Red Hat Enterprise Linux 7 (64 位) 自带 Docker。我们可以在发行日志中找到概述和指南。

Docker 包含在 extras 镜像源中,使用下面的方法可以安装 Docker:

启用 extras 镜像源:

$ sudo subscription-manager repos --enable=rhel-7-server-extras-rpms

安装 Docker :

$ sudo yum install docker

2.Red Hat Enterprise Linux 6.5 安装 Docker

需要在 64 位 的 RHEL 6.5 或更高的版本上来安装 Docker,Docker 工作需要特定的内核补丁, 因此 RHEL 的内核版本应为 2.6.32-431 或者更高。

Docker 已经包含在 RHEL 的 EPEL 源中。该源是 Extra Packages for Enterprise Linux (EPEL) 的一个额外包,社区中正在努力创建和维护相关镜像。

首先,你需要安装 EPEL 镜像源,在 EPEL 中已经提供了 docker-io 包。

下一步,我们将要在我们的主机中安装 Docker,也就是 docker-io 包:

$ sudo yum -y install docker-io

更新 docker-io 包:

$ sudo yum -y update docker-io

现在 Docker 已经安装好了,我们来启动 docker 进程:

$ sudo service docker start

设置开机启动:

$ sudo chkconfig docker on

现在,让我们确认 Docker 是否正常工作:

$ sudo docker run -i -t fedora /bin/bash

现在 Docker 已经安装好了,让我们来启动 Docker 进程

$ sudo service docker start

如果我们想要开机启动 Docker ,我们需要执行如下的命令:

$ sudo chkconfig docker on

现在测试一下是否正常工作:

$ sudo docker run -i -t fedora /bin/bash

注意: 如果运行的时候提示一个 Cannot start container 的错误,错误中提到了SELINUX 或者 权限不足。我们需要更新 SELINUX 规则。可以使用 sudo yum upgrade selinux-policy 然后重启。

2.2.2 在 Windows 上安装 Docker

  1. 安装

1 ) 、下载最新版本的 Docker for Windows Installer

2 ) 、运行安装文件,它将会安装 virtualbox、 MSYS-git boot2docker Linux 镜像和 Boot2Docker的管理工具。

图片

3)、从桌面上或者 Program Files 中找到 Boot2Docker for Windows,运行 Boot2Docker Start 脚本。这个脚本会要求你输入 ssh 密钥密码 - 可以简单点(但是起码看起来比较安全) ,然后只需要按[Enter]按钮即可。

4)、Boot2Docker Start 将启动一个 Unix shell 来配置和管理运行在虚拟主机中的 Docker,运行 docker version 来查看它是否正常工作。

图片

  1. 运行 Docker

注意:如果使用的是一个远程的 Docker 进程,像 Boot2docker ,就不需要像前边的文档实例中那样在输入 Docker 命令之前输入 sudo。

Boot2docker start 将会自动启动一个 shell 命令框并配置好环境变量,以便我们可以马上使用 Docker :

让我们尝试运行 hello-world 例子。运行:

$ docker run hello-world

这将会下载一个非常小的 hello-world 镜像,并且打印出 Hello from Docker 的信息。

  1. 使用 Windows 的命令行( ( cmd.exe ) 来管理运行 Docker

启动一个 Windows 命令行(cmd.exe) ,运行 Boot2docker 命令,这需要 Windows PATH 环境变量中包含了 ssh.exe。因此我们需要将安装的 Git 的 bin 目录 (其中包含了 ssh.exe)配置到我们的 %PATH% 环境变量中,运行如下命令:

set PATH=%PATH%;"c:\Program Files (x86)\Git\bin"

现在,我们可以运行 boot2docker start 命令来启动 Boot2docker 虚拟机。(如果有虚拟主机不存在的错误提示,需要运行 boot2docker init 命令) 。复制上边的指令到 cmd.exe 来设置 windows 控制台的环境变量,然后就可以运行 docker 命令了,譬如 docker ps :

图片

  1. PowerShell 中使用 Docker

启动 PowerShell,需要将 ssh.exe 添加到 PATH 中。

$Env:Path = "${Env:Path};c:\Program Files (x86)\Git\bin"

之后,运行 boot2docker start 命令行,它会打印出 PowerShell 命令,这些命令是用来设置环境变量来连接运行在虚拟机中 Docker 的。运行这些命令,然后就可以运行 docker 命令了,譬如 docker ps :

图片

提示:可以使用 boot2docker shellinit | Invoke-Expression 来设置环境变量来代替复制粘贴 Powershell 命令。

2.2.3 在 CentOS 环境下安装 Docker

Docker 支持以下的 CentOS 版本:

CentOS 7 (64-bit)

CentOS 6.5 (64-bit)或更高的版本

前提条件

Docker 运行在 CentOS 7 上,要求系统为 64 位、系统内核版本为 3.10 以上。

Docker 运行在 CentOS-6.5 或更高的版本的 CentOS 上,要求系统为 64 位、系统内核版本为2.6.32-431 或者更高版本。

使用 yum 安装(CentOS 7 下)

Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的 CentOS 版本是否支持 Docker 。

通过 uname - - r 命令查看你当前的内核版本

uname -r 3.10.0-327.el7.x86_64

图片

安装 Docker

Docker 软件包和依赖包已经包含在默认的 CentOS-Extras 软件源里,安装命令如下:

yum -y install docker

图片

安装完成。

图片

启动 Docker 后台服务

service docker start

图片

测 试 运 行 hello-world

docker run hello-world

图片

由于本地没有 hello-world 这个镜像,所以会下载一个 hello-world 的镜像,并在容器内运行。

使用脚本安装 Docker

1、使用 sudo 或 root 权限登录 Centos。

2、确保 yum 包更新到最新。

$ sudo yum update

3、执行 Docker 安装脚本。

执行这个脚本会添加 docker.repo 源并安装 Docker。

4、启动 Docker 进程。

$ sudo service docker start

5、验证 docker 是否安装成功并在容器中执行一个测试的镜像。

$ sudo docker run hello-world

到此,docker 在 CentOS 系统的安装完成。

第三部分 使用 Docker 镜像

镜像(image)是 Docker 三大核心概念中最为重要的,自 Docker 诞生之日起“镜像”就是相关社区最为热门的关键词。

Docker 运行容器前需要本地存在对应的镜像,如果镜像没保存在本地,Docker 会尝试先从默认镜像仓库下载(默认使用:Docker Hub 公共注册服务器中的仓库),用户也可以通过配置,使用自定义的镜像仓库。

本部分将介绍围绕镜像这一核心概念的具体操作,包括如何使用 pull 命令从 Docker Hub 仓库中下载镜像到本地,如何查看本地已有的镜像信息和管理镜像标签,如何在远端仓库使用 search 命令进行搜索和过滤,如何删除镜像标签和镜像文件,如何创建用户定制的镜像并且保存为外部文件。最后,还介绍如何往 Docker Hub 仓库中推送自己的镜像。

3.1 获取镜像

镜像是运行容器的前提,官方的 Docker Hub 网站已经提供了数十万个镜像供大家开放下载。

可以使用 docker pull 命令直接从 Docker Hub 镜像源来下载镜像。该命令的格式为docker pull NAME[:TAG]。其中,NAME 是镜像仓库的名称(用来区分镜像),TAG 是镜像的标签(往往用来表示版本信息)。通常情况下,描述一个镜像需要包括‘名称+标签’信息。

例如,获取一个 Ubuntu 14.04 系统的基础镜像可以使用如下的命令:

图片

对于 Docker 镜像来说,如果不显式指定 TAG,则默认会选择 latest 标签,这会下载仓库中最新版本的镜像。

下面的例子将从 Docker Hub 的 Ubuntu 仓库下载一个最新的 Ubuntu 操作系统的镜像。

图片

该命令实际上下载的就是 ubuntu: latest 镜像。

下载过程中可以看出,镜像文件一般由若干层(layer)组成,6c953ac5d795 这样的串是层的唯一 id(实际上完整的 id 包括 256 比特,由 64 个十六进制字符组成)。使用 docker pull 命令下载时会获取并输出镜像的各层信息。当不同的镜像包括相同的层时,本地仅存储层的一份内容,减小了需要的存储空问。

我们可能会想到,在使用不同的镜像仓库服务器的情况下,可能会出现按镜像重名的情况。

严格地讲,镜像的仓库名称中还应该添加仓库地址(即 registry,注册服务器)作为前缀,只是我们默认使用的是 Docker Hub 服务,该前缀可以忽略。

例如,docker pull Ubuntu:14.04 命令相当于 docker pull registry.hub.docker.com/Ubuntu:14.04 命令,即从默认的注册服务器 Docker Hub Registry 中的 ubuntu 仓库来下载标记为 14.04 的镜像。

如果从非官方的仓库下载,则需要在仓库名称前指定完整的仓库地址。例如从网易蜂巢的镜像源来下载 ubuntu:14.04 镜像,可以使用如下命令,此时下载的镜像名称为

hub.c.163.com/public/Ubuntu:14.04:

$ docker pull hub.c.163.com/public/Ubuntu:14.04

pull 子命令支持的选项主要包括:

-a,--all- tags=true| false:是否获取仓库中的所有镜像,默认为否。

下载镜像到本地后,即可随时使用该镜像了,例如利用该镜像创建一个容器,在其中运行 bash 应用,执行 ping localhost 命令:

图片

图片

3.2 查看镜像信息

  1. 使用 images 命令列出镜像

使用 docker images 命令可以列出本地主机上已有镜像的基本信息。例

如,下面的命令列出了上一小节中下载的镜像信息:

图片

在列出的信息中,可以看到以下几个字段信息。

 来自于哪个仓库,比如 ubuntu 仓库用来保存 ubuntu 系列的基础镜像;

 镜像的标签信息,比如 14.04、latest 用来标注不同的版本信息。标签只是标记,并不能标识镜像内容;

 镜像的 ID(唯一标识镜像),如 ubuntu:latest 和 ubuntu:16.04 镜像的 ID 都是 2fa927b5cdd3,说明它们目前实际上指向同一个镜像;

 创建时间,说明镜像最后的更新时间;

 镜像大小,优秀的镜像往往体积都较小。

其中镜像的 ID 信息十分重要,它唯一标识了镜像。在使用镜像 ID 的时候,一般可以使用该 ID 的前若干个字符组成的可区分串来替代完整的 ID。

TAG 信息用来标记来自同一个仓库的不同镜像。例如 ubunm 仓库中有多个镜像,通过TAG 信息来区分发行版本,包括 10.04、12.04、12.10、13.04、14.04、16.04 等标签:

镜像大小信息只是表示该镜像的逻辑体积大小,实际上由于相同的镜像层本地只会存储一份,物理上占用的存储空间会小于各镜像的逻辑体积之和。

images 子命令主要支持如下选项,用户可以自行进行尝试。

 -a,--all=true | false:列出所有的镜像文件(包括临时文件),默认为否;

 --digests=true I false:列出镜像的数字摘要值,默认为否;

 -f,---filter=[ ]:过滤列出的镜像,如 dangling=true 只显示没有被使用的镜像;

也可指定带有特定标注的镜像等;

 --format="TEMPLATE":控制输出格式,如.ID 代表 ID 信息,Repository 代表仓库信息等;

 --no-trunc=true l false:对输出结果中太长的部分是否进行截断,如镜像的 ID信息,默认为是;

 -q,--quiet=true l false:仅输出 ID 信息,默认为否。

其中,对输出结果进行控制的选项如-f,--filter=[ ]、--no-trunc=true | false、-q,--quiet=true I false 等,大部分子命令都支持。

更多子命令选项还可以通过 man docker-images 来查看。

2 .使用 tag 命令添加镜像标签

为了方便在后续工作中使用特定镜像,还可以使用 docker tag 命令来为本地镜像任意添加新的标签。例如添加一个新的 myubuntu:latest 镜像标签:

$ docker tag ubuntu:latest myubuntu:latest

再次使用 docker images 列出本地主机上镜像信息,可以看到多了一个拥有 myubuntu:latest 标签的镜像。之后,用户就可以直接使用 myubuntu:latest 来表示这个镜像了。

我们可能注意到,这些 myubuntu:latest 镜像的 ID 跟 ubuntu:latest 完全一致。它们实际上指向同一个镜像文件,只是别名不同而已。docker tag 命令添加的标签实际上起到了类似链接的作用。

  1. 使用 inspect 命令查看详细信息

使用 docker inspect 命令可以获取该镜像的详细信息,包括制作者、适应架构、各层的数字摘要等,可输入以下命令:

$docker inspect ubuntu:14.04

返回的是一个 JSON 格式的消息,如果我们只要其中一项内容时,可以使用参数-f 来指定,例如,获取镜像的 Architecture:

$docker inspect -f {{".Architecture"}}

amd64

  1. 使用 history 命令查看镜像历史

既然镜像文件由多个层组成,那么怎么知道各个层的内容具体是什么呢?这时候可以使用 history 子命令,该命令将列出各层的创建信息。

例如,查看 ubuntu:14.04 镜像的创建过程,可以使用如下命令:

$docker history ubuntu:14.04

3.3 搜寻镜像

使用 docker search 命令可以搜索远端仓库中共享的镜像,默认搜索官方仓库中的镜像。用法为 docker search TERM,支持的参数主要包括:

 --automated=true I false:仅显示自动创建的镜像,默认为否;

 --no-trunc=true | false:输出信息不截断显示,默认为否;

 -s,--stars=X:指定仅显示评价为指定星级以上的镜像,默认为 0,即输出所有镜像。

例如,搜索所有自动创建的评价为 3+的带 nginx 关键字的镜像,如下所示:

$ docker search----automated -s 3 nginx

可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、星级(表示该镜像的受欢迎程度)、是否官方创建、是否自动创建等。

默认的输出结果将按照星级评价进行排序。

3.4 删除镜像

  1. 使用标签删除镜像

使用 docker rmi 命令可以删除镜像,命令格式为 docker rmi IMAGE [IMAGE…],其中IMAGE 可以为标签或 ID。

例如,要删除掉 myubuntu:latest 镜像,可以使用如下命令:

$ docker rmi myubuntu:latest

Untagged:myubuntu:latest

我们可能会担心,本地的 ubuntu:latest 镜像是否会受此命令的影响。无需担心,当同一个镜像拥有多个标签的时候,docker rmi 命令只是删除该镜像多个标签中的指定标签而已,并不影响镜像文件。因此上述操作相当于只是删除了镜像 2fa927b5cdd3 的一个标签而已。

为保险起见,再次查看本地的镜像,发现 ubuntu:latest 镜像(准确地说是2fa927b5cdd3 镜像)仍然存在。但当镜像只剩下一个标签的时候就要小心了,此时再使用 docker rmi 命令会彻底删除镜像。

例如删除标签为 ubuntu:14.04 的镜像,由于该镜像没有额外的标签指向它,执行 docker rmi 命令,它会删除这个镜像文件的所有层:

$ docker rmi ubuntu:14.04

  1. 使用镜像 ID 删除镜像

当使用 docker rmi 命令,并且后面跟上镜像的 ID(也可以是能进行区分的部分 ID 串前缀)时,会先尝试删除所有指向该镜像的标签,然后删除该镜像文件本身。

注意,当有该镜像创建的容器存在时,镜像文件默认是无法被删除的,例如,先利用 ubuntu:14.04 镜像创建一个简单的容器来输出一段话:

$ docker run Ubuntu:14.04 echo 'hello! I am here!'

hello!I am here!

使用 docker ps -a 命令可以看到本机上存在的所有容器:

$ docker ps -a

可以看到,后台存在一个退出状态的容器,是刚基于 ubuntu:14.04 镜像创建的。

试图删除该镜像,Docker 会提示有容器正在运行,无法删除:

$ docker rmi Ubuntu:14.04

Error response from daemon:conflict:unable to remove repository reference

"Ubuntu: 14.04" (must force)一 container a21c0840213e is using it's referenced

image 8f1bd21bd25c

如果要想强行删除镜像,可以使用 -f 参数。

$ docker rmi -f ubuntu:14.04

注意,通常并不推荐使用-f 参数来强制删除一个存在容器依赖的镜像。正确的做法是,先删除依赖该镜像的所有容器,再来删除镜像。首先删除容器 a21c0840213e:

$docker rm a21c0840213e

再使用 ID 来删除镜像,此时会正常打印出删除的各层信息:

$docker rmi 8f1bd21bd25c

3.5 创建镜像

创建镜像的方法主要有三种:基于已有镜像的容器创建、基于本地模板导入、基于Dockerfile 创建。

1 .基于已有镜像的容器创建

该方法主要是使用 docker commit 命令。命令格式为 docker commit [OPTIONS]

CONTAINER [REPOSITORY[:TAG]],主要选项包括:

 -a,--author="":作者信息;

 -c,--change=[ ]:提交的时候执行 Dockerfile 指令,包括 CMD | ENTRYPOINT |

ENV l EXPOSE | LABEL | ONBUILD | USER | VOLUME | WORKDIR 等;

 -m,--message="":提交消息;-

 -p,--pause=true:提交时暂停容器运行。

下面将演示如何使用该命令创建一个新镜像。首先,启动一个镜像,并在其中进行修改操作,例如创建一个 test 文件,之后退出:

$ docker run -it Ubuntu:14.04/bin/bash

root@a925cb40b3f0:/# touch test

root@a925Cb40b3f0:/# exit

记住容器的 ID 为 a925cb40b3f0。

此时该容器跟原 ubuntu:14.04 镜像相比,已经发生了改变,可以使用 docker commit 命令来提交为一个新的镜像。提交时可以使用 ID 或名称来指定容器:

$ docker commit -m "Added a new file" -a "Docker Newbee" a925cb40b3f0 test:0.1

9e9C814023bcffc3e67e892a235afe61b02f66a947d2747f724bd317dda02f27

顺利的话,会返回新创建的镜像的 ID 信息,例如

9e9C814023bcffc3e67e892a235afe61b02f66a947d2747f724bd317dda02f27。此时查看本地镜像列表,会发现新创建的镜像已经存在了。

$docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE

test 0.1 9e9c814023bc 4 seconds ago 188 MB

2 .基于本地模板导入

用户也可以直接从一个操作系统模板文件导入一个镜像,主要使用 docker import 命令。命令格式为 docker import [OPTIONS] file |URL|-[REPOSITORY [:TAG]]。

要直接导入一个镜像,可以使用 OpenVZ 提供的模板来创建,或者用其他已导出的镜像模板来创建。OPENVZ 模板的下载地址为 http:

//openvz.org/Download/templates/precreated。

例如,下载了 ubuntu -14.04 的模板压缩包,之后使用以下命令导入:

$cat Ubuntu -14.04 -x86_64 -minimal.tar.gz | docker import -ubuntu:14.04

然后查看新导入的镜像,会发现它已经在本地存在了:

$docker images

REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE

ubuntu 14.04 05ac7cob9383 17 seconds ago 215.5 MB

3.6 存出和载入镜像

用户可以使用 docker save 和 docker load 命令来存出和载入镜像。

1 .存出镜像

如果要导出镜像到本地文件,可以使用 docker save 命令。例如,导出本地的ubuntu:14.04 镜像为文件 ubuntu_14.04.tar,如下所示:

图片

之后,用户就可以通过复制 ubuntu_14.04.tar 文件将该镜像分享给他人。

2 .载入镜像

可以使用 docker load 将导出的 tar 文件再导入到本地镜像库,例如从文件ubuntu_14.04.tar 导入镜像到本地镜像列表,如下所示:

$docker load --input ubuntu_14.04.tar

或:

$docker load < ubuntu_14.04.tar

这将导入镜像及其相关的元数据信息(包括标签等)。导入成功后,可以使用 dockerimages 命令进行查看。

3.7 上传镜像

可以使用 docker push 命令上传镜像到仓库,默认上传到 Docker Hub 官方仓库(需要登录)。命令格式为:

docker push NAME [:TAG] l [REGISTRY_HOST [:REGISTRY_PORT]/ ] NAME [:TAG]

用户在 Docker Hub 网站注册后可以上传自制的镜像。例如用户 user 上传本地的 test:

latest 镜像.可以先添加新的标签 user/test:latest,然后用 docker push 命令上传镜像:

$docker tag test:latest user/test:latest

$docker push user/test:latest

第一次上传时,会提示输入登录信息或进行注册。

第四部分 操作 Docker 容器

容器是 Docker 的另一个核心概念。简单来说,容器是镜像的一个运行实例。所不同的是,镜像是静态的只读文件,而容器带有运行时需要的可写文件层。如果认为虚拟机是模拟运行的一整套操作系统(包括内核、应用运行态环境和其他系统环境)和跑在上面的应用, 那么Docker 容器就是独立运行的一个(或一组)应用,以及它们必需的运行环境。容器是直接提供应用服务的组件,也是 Docker 实现快速启停和高效服务性能的基础。

在生产环境中,因为容器自身的轻量级特性,我们推荐使用容器时在一组容器前引入HA(High Availability,高可靠性)机制。例如使用 HAProxy 工具来代理容器访问,这样在容器出现故障时,可以快速切换到功能正常的容器。此外,建议通过指定合适的容器重启策略,来自动重启退出的容器。

本部分具体介绍围绕容器的重要操作,包括创建一个容器、启动容器、终止一个容器、进入容器内执行操作、删除容器和通过导入导出容器来实现容器迁移等。

4.1 创建容器

从现在开始,可以忘掉虚拟机。对容器进行操作就跟直接操作应用一样简单、快速。

Docker 容器实在太轻量级了,用户可以随时创建或删除容器。

  1. 新建容器

可以使用 docker create 命令新建一个容器,例如:

图片

使用 docker create 命令新建的容器处于停止状态,可以使用 docker start 命令来启动它。

create 命令和后续的 run 命令支持的选项都十分复杂,与容器运行模式相关、与容器和环境配置相关、与容器资源限制和安全保护相关。

  1. 启动容器

使用 docker start 命令来启动一个已经创建的容器,例如启动刚创建的 ubuntu 容器:

$ docker start af

af

此时,通过 docker ps 命令可以查看一个运行中的容器:

$docker ps

图片

  1. 新建并启动容器

除了创建容器后通过 start 命令来启动,也可以直接新建并启动容器。所需要的命令主要为 docker run,等价于先执行 docker create 命令,再执行 docker start 命令。

例如,下面的命令输出一个 "Hello World",之后容器自动终止:

$ docker run ubuntu /bin/echo 'Hello world'

Hello world

这跟在本地直接执行/bin/echo 'Hello world' 几乎感觉不出任何区别。当利用 docker run 来创建并启动容器时,Docker 在后台运行的标准操作包括:

 检查本地是否存在指定的镜像,不存在就从公有仓库下载;

 利用镜像创建一个容器,并启动该容器;

 分配一个文件系统给容器,并在只读的镜像层外面挂载一层可读写层;

 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中;

 从网桥的地址池配置一个 IP 地址给容器;

 执行用户指定的应用程序;

 执行完毕后容器被自动终止。

下面的命令启动一个 bash 终端,允许用户进行交互:

$ docker run -it Ubuntu:14.04/bin/bash

root@af8bae53bdd3:/#

其中,-t 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,-i则让容器的标准输入保持打开。更多的命令选项可以通过 man docker -run 命令来查看。

在交互模式下,用户可以通过所创建的终端来输入命令,例如:

图片

在容器内用 ps 命令查看进程,可以看到,只运行了 bash 应用,并没有运行其他无关的进程。用户可以按 Ctrl+d 或输入 exit 命令来退出容器:

root@af8bae53bdd3:/# exit

exit

对于所创建的 bash 容器,当使用 exit 命令退出之后,容器就自动处于退出(Exited)状态了。这是因为对 Docker 容器来说,当运行的应用退出后,容器也就没有继续运行的必要了。

某些时候,执行 docker run 会出错,因为命令无法正常执行容器会直接退出,此时可以查看退出的错误代码。

默认情况下,常见错误代码包括:

 125:Dockerdaemon 执行出错,例如指定了不支持的 Docker 命令参数;

 126:所指定命令无法执行,例如权限出错;

 127:容器内命令无法找到。

命令执行后出错,会默认返回错误码。

  1. 守护态运行

更多的时候,需要让 Docker 容器在后台以守护态(Daemonized)形式运行。此时,可以通过添加 -d 参数来实现。

例如下面的命令会在后台运行容器:

图片

容器启动后会返回一个唯一的 id,也可以通过 docker ps 命令来查看容器信息:

图片

此时,要获取容器的输出信息,可以如下使用 docker logs 命令:

$docker logs ce5

helio world

helio world

hello world

4.2 终止容器

可以使用 docker stop 来终止一个运行中的容器。该命令的格式为 docker stop [-t|--time[=10]] [CONTAINER…]。

首先向容器发送 SIGTERM 信号,等待一段超时时间(默认为 10 秒)后,再发送 SIGKILL 信号来终止容器:

$docker stop ce5

ce5

此外,当 Docker 容器中指定的应用终结时,容器也会自动终止。例如对于上一节中只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止,处于 stopped 状态。可以用 docker ps -qa 命令看到所有容器的 ID。例如:

$docker ps -qa

ce554267d7a4

d58050081fe3

e812 617b41f6

处于终止状态的容器,可以通过 docker start 命令来重新启动:

$ docker start ce5

此外,docker restart 命令会将一个运行态的容器先终止,然后再重新启动它:

$docker restart ce5

4.3 进入容器

Docker 从 1.3.0 版本起提供了一个方便的 exec 命令,可以在容器内直接执行任意命令。该命令的基本格式为:

docker exec [-d|--detach] [--detach-keys[=[ ]]] [-i|--interactive] [--privileged] [-t |--tty] [-u |--user[=USER]] CONTAINER COMMAND [ARG…] 。

比较重要的参数有:

 -i,--interactive=true | false:打开标准输入接受用户输入命令,默认为

false;

 --privileged=true | false:是否给执行命令以高权限,默认为 false;

 -t,--tty= true | false:分配伪终端,默认为 false;

 -u,--user=" ":执行命令的用户名或 ID。

例如进入到刚创建的容器中,并启动一个 bash:

$docker exec -it 243c32535da7 /bin/bash

root@243c32535da7:/#

可以看到,一个 bash 终端打开了,在不影响容器内其他应用的前提下,用户可以很容易与容器进行交互。

4.4 删除容器

可以使用 docker rm 命令来删除处于终止或退出状态的容器,命令格式为 docker rm [-f |--force] [-l |--link] [-v |--volumes] CONTAINER [CONTAINER…] 。

主要支持的选项包括:

 -f,--force=false:是否强行终止并删除一个运行中的容器;

 -l,--link=false:删除容器的连接,但保留容器;

 -v,--volumes=false:删除容器挂载的数据卷。

例如,查看处于终止状态的容器,并删除:

$docker rm ce554267d7a4

Ce554267d7a4

默认情况下,docker rm 命令只能删除处于终止或退出状态的容器,并不能删除还处于运行状态的容器。

如果要直接删除一个运行中的容器,可以添加 -f 参数。Docker 会先发送 SIGKILL 信号给容器,终止其中的应用,之后强行删除,如下所示:

$ docker rm -f 2ae

2ae

4.5 导入和导出容器

某些时候,需要将容器从一个系统迁移到另外一个系统,此时可以使用 Docker 的导入和导出功能。这也是 Docker 自身提供的一个重要特性。

1 .导出容器

导出容器是指导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态,可以使用 docker export 命令,该命令的格式为 docker export [-o] --output[=" "]]

CONTAINER。其中,可以通过-o 选项来指定导出的 tar 文件名,也可以直接通过重定向来实现。

示例:分别导出 ce554267d7a4 容器和 e812617b41f6 容器到文件 test_for_run.tar

文件和 test_for_stop.tar 文件:

$ docker export -o test_for_run.tar ce5

$ls

test_for_run.tar

$ docker export e81 > test_for_stop.tar

$ls

test_for_run.tar test_for_stop.tar

之后,可将导出的 tar 文件传输到其他机器上,然后再通过导人命令导人到系统中,从而实现容器的迁移。

2 .导入容器

导出的文件又可以使用 docker import 命令导入变成镜像,该命令格式为:

docker import [-c I --change[=[ ]]] [-m|--message [=MESSAGE]] file | URL | -[REPOSITORY [:TAG]]

用户可以通过-c,--change=[ ]选项在导入的同时执行对容器进行修改的 Dockerfile 指令。

下面将导出的 test_for_run.tar 文件导入到系统中:

$ docker import test_for_run.tar - test/Ubuntu:v1.0

之前镜像章节中我们曾介绍过使用 docker load 命令来导入一个镜像文件,与 dockerimport 命令十分类似。

实际上,既可以使用 docker load 命令来导入镜像存储文件到本地镜像库,也可以使用docker import 命令来导人一个容器快照到本地镜像库。

这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也更大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

4.6 实现容器的网络端口映射

下面我们来实现 Docker 容器的网络端口映射。我们先创建了一个 python 应用的容器。

$ docker run -d -P training/webapp python app.py

fce072cc88cee71b1cdceb57c2821d054a4a59f67da6b416fceb5593f059fc6d

另外,我们可以指定容器绑定的网络地址,比如绑定 127.0.0.1。

我们使用 -P 参数创建一个容器,使用 docker ps 来看到端口 5000 绑定主机端口 32768。

$ docker ps

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTSNAMES

fce072cc88ce training/webapp "python app.py" 4 minutes ago Up 4 minutes 0.0.0.0:32768-

>5000/tcp grave_hopper

我们也可以使用 -p 标识来指定容器端口绑定到主机端口。

两种方式的区别是:

-P :是容器内部端口随机映射到主机的高端口。

-p : 是容器内部端口绑定到指定的主机端口。

图片

另外,我们可以指定容器绑定的网络地址,比如绑定 127.0.0.1。

图片

这样我们就可以通过访问 127.0.0.1:5001 来访问容器的 5002 端口。

上面的例子中,默认都是绑定 tcp 端口,如果要绑定 UPD 端口,可以在端口后面加上/udp。

图片

docker port 命令可以让我们快捷地查看端口的绑定情况。

$ docker port adoring_stonebraker 5002

127.0.0.1:5001

第五部分 Docker 容器实现 Web 服务与应用

5.1 Docker 容器实现 Apache 服务

方法一、通过 Dockerfile 构建

创建 Dockerfile

首先,创建目录 apache,用于存放后面的相关东西。

$ mkdir -p ~/apache/www ~/apache/logs ~/apache/conf

www 目录将映射为 apache 容器配置的应用程序目录,logs 目录将映射为 apache 容器的日志目录,conf 目录里的配置文件将映射为 apache 容器的配置文件,进入创建的 apache 目录,创建 Dockerfile,内容如下:

图片

图片

图片

图片

图片

本人提供Oracle、MySQL、PG等数据库的培训和考证业务,私聊QQ646634621或微信db_bao,谢谢!

图片

图片

Dockerfile 文件中 COPY httpd-foreground /usr/local/bin/ 是将当前目录下的 httpd-foreground 拷贝到镜像里,作为 httpd 服务的启动脚本,所以我们要在本地创建一个脚本文件 httpd-foreground

#!/bin/bash

set -e

# Apache gets grumpy about PID files pre-existing

rm -f /usr/local/apache2/logs/httpd.pid

exec httpd -DFOREGROUND

赋予 httpd-foreground 文件可执行权限

$ chmod +x httpd-foreground

通过 Dockerfile 创建一个镜像,替换成你自己的名字

$ docker build -t httpd .

创建完成后,我们可以在本地的镜像列表里查找到刚刚创建的镜像

$ docker images httpd

REPOSITORY TAG IMAGE ID CREATED SIZE

httpd latest da1536b4ef14 23 seconds ago .... 195.1 MB

方法二、 docker pull httpd

查找 Docker Hub 上的 httpd 镜像

图片

这里我们拉取官方的镜像

$ docker pull httpd

等待下载完成后,我们就可以在本地镜像列表里查到 REPOSITORY 为 httpd 的镜像。

使用 apache 镜像

运行容器

图片

命令说明:

-p 80:80 :将容器的 80 端口映射到主机的 80 端口

-v $PWD/www/:/usr/local/apache2/htdocs/ :将主机中当前目录下的 www 目录挂载到容器的 /usr/local/apache2/htdocs/

-v $PWD/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf :将主机中当前目录下的 conf/httpd.conf 文件挂载到容器的/usr/local/apache2/conf/httpd.conf

-v $PWD/logs/:/usr/local/apache2/logs/ :将主机中当前目录下的 logs 目录挂载到容器的 /usr/local/apache2/logs/

查看容器启动情况

$ docker ps

CONTAINER ID IMAGE COMMAND ............... PORTS NAMES

79a97f2aac37 httpd "httpd-foreground" ... 0.0.0.0:80->80/tcp sharp_swanson

通过浏览器访问验证 Docker 中的 Apache 服务是否可访问。

5.2 Docker 容器实现 Nginx 服务

方法一、通过 Dockerfile 构建

创建 Dockerfile

首先,创建目录 nginx,用于存放后面的相关东西。

$ mkdir p ~/nginx/www ~/nginx/logs ~/nginx/conf

www 目录将映射为 nginx 容器配置的虚拟目录,logs 目录将映射为 nginx 容器的日志目录,conf 目录里的配置文件将映射为 nginx 容器的配置文件,进入创建的 nginx 目录,创建Dockerfile。

图片

图片

图片

通过 Dockerfile 创建一个镜像,替换成你自己的名字。

docker build -t nginx .

创建完成后,我们可以在本地的镜像列表里查找到刚刚创建的镜像

图片

方法二、 docker pull nginx

查找 Docker Hub 上的 nginx 镜像。

图片

图片

图片

这里我们拉取官方的镜像

$ docker pull nginx

等待下载完成后,我们就可以在本地镜像列表里查到 REPOSITORY 为 nginx 的镜像。

使用 nginx 镜像,运行容器

图片

命令说明:

-p 80:80:将容器的 80 端口映射到主机的 80 端口

--name mynginx:将容器命名为 mynginx

-v $PWD/www:/www:将主机中当前目录下的 www 挂载到容器的/www

-v $PWD/conf/nginx.conf:/etc/nginx/nginx.conf:将主机中当前目录下的

nginx.conf 挂载到容器的/etc/nginx/nginx.conf

-v $PWD/logs:/wwwlogs:将主机中当前目录下的 logs 挂载到容器的/wwwlogs

查看容器启动情况

图片

通过浏览器访问

图片

5.3 Docker 容器实现 Python 应用

方法一、通过 Dockerfile 构建

创建 Dockerfile

首先,创建目录 python,用于存放后面的相关东西。

$ mdkir -p ~/python ~/python/myapp

myapp 目录将映射为 python 容器配置的应用目录,进入创建的 python 目录,创建 Dockerfile。

图片

图片

图片

图片

图片

通过 Dockerfile 创建一个镜像,替换成你自己的名字

$ docker build -t python:3.5 .

创建完成后,我们可以在本地的镜像列表里查找到刚刚创建的镜像

图片

方法二、 docker pull python

查找 Docker Hub 上的 python 镜像

图片

图片

图片

这里我们拉取官方的镜像,标签为 3.5

$ docker pull python:3.5

等待下载完成后,我们就可以在本地镜像列表里查到 REPOSITORY 为 python,标签为 3.5 的镜像。

在~/python/myapp 目录下创建一个 helloworld.py 文件,代码如下:

#!/usr/bin/python

print("Hello, World!");

运行容器

$ docker run -v $PWD/myapp:/usr/src/myapp -w /usr/src/myapp python:3.5 python

helloworld.py

命令说明:

-v $PWD/myapp:/usr/src/myapp :将主机中当前目录下的 myapp 挂载到容器的/usr/src/myapp

-w /usr/src/myapp :指定容器的/usr/src/myapp 目录为工作目录

python helloworld.py :使用容器的 python 命令来执行工作目录中的 helloworld.py 文件

输出结果:

Hello, World!

5.4 Docker 容器实现 MySQL 服务

方法一、通过 Dockerfile 构建

创建 Dockerfile

首先,创建目录 mysql,用于存放后面的相关东西。

$ mkdir -p ~/mysql/data ~/mysql/logs ~/mysql/conf

data 目录将映射为 mysql 容器配置的数据文件存放路径,logs 目录将映射为 mysql 容器的日志目录,conf 目录里的配置文件将映射为 mysql 容器的配置文件。

进入创建的 mysql 目录,创建 Dockerfile

FROM debian:jessie

图片

图片

图片

图片

图片

图片

图片

通过 Dockerfile 创建一个镜像,替换成你自己的名字。

$ docker build -t mysql .

创建完成后,我们可以在本地的镜像列表里查找到刚刚创建的镜像

$ docker images |grep mysql

mysql .............. 5.6 2c0964ec182a 3 weeks ago 329 MB

方法二、 docker pull mysql

查找 Docker Hub 上的 mysql 镜像。

图片

图片

图片

这里我们拉取官方的镜像,标签为 5.6。

$ docker pull mysql:5.6

等待下载完成后,我们就可以在本地镜像列表里查到 REPOSITORY 为 mysql,标签为 5.6 的镜像。

运行容器:

图片

命令说明:

-p 3306:3306:将容器的 3306 端口映射到主机的 3306 端口

-v $PWD/conf/my.cnf:/etc/mysql/my.cnf:将主机当前目录下的 conf/my.cnf 挂载到容

器的/etc/mysql/my.cnf

-v $PWD/logs:/logs:将主机当前目录下的 logs 目录挂载到容器的/logs

-v $PWD/data:/mysql_data:将主机当前目录下的 data 目录挂载到容器的/mysql_data

-e MYSQL_ROOT_PASSWORD=123456:初始化 root 用户的密码

查看容器启动情况:

图片

第六部分 Docker 的运行监控

在运维体系中, 监控是非常重要的组成部分。通过监控可以实时掌握系统运行的状态,对故障的提前预警,历史状态的回放等,还可以通过监控数据为系统的容量规划提供辅助决策,为系统性能优化提供真实的用户行为和体验。

6.1 容器的监控方案

传统的监控系统大多是针对物理机或虚拟机设计的,物理机和虚拟机的特点是静态的,生命周期长,一个环境安装配置好后可能几年都不会去变动,那么对监控系统来说,监控对像是静态的,对监控对象做的监控配置也是静态的,系统上线部署好监控后基本就不再需要管理。

虽然物理机、虚拟机、容器对于应用进程来说都是 host 环境,容器也是一个轻量级的虚拟机, 但容器是动态的, 生命周期短,特别是在微服务的分布式架构下,容器的个数,IP 地址随时可能变化。如果还采用原来传统监控的方案,则会增加监控的复杂度。比如对于一个物理机或虚拟机,我们只要安装一个监控工具的 agent 就可以了,但如果在一个物理机上运行了

无数个容器,也采用安装 agent 的方式,就会增加 agent 对资源的占用,但因为容器是与宿主机是共享资源,所以在容器内采集的性能数据会是宿主机的数据,那就失去在容器内采集数据的意义。

而且往往容器的数量比较多,采集到的数量也会非常多,容器可能启动几分钟就停止了,那么原来采集的数据就没有价值了,则会产生大量这样没有价值的监控数据,维护起来也会非常的复杂。那么应该如何对容器进行监控呢?答案是在容器外,宿主机上进行监控。这样不仅可以监控到每个容器的资源使用情况,还可以监控到容器的状态,数量等数据。

6.2 单台主机上容器的监控

单台主机上容器的监控实现最简单的方法就是使用命令 Docker stats,就可以显示所有容器的资源使用情况,如下输出:

Docker 容器技术使用指南

虽然可以很直观地看到每个容器的资源使用情况,但是显示的只是一个当前值,并不能看到变化趋势。而谷歌提供的图形化工具不仅可以看到每个容器的资源使用情况,还可以看到主机的资源使用情况,并且可以设置显示一段时间内的趋势。以下是 cAdvisor 的面板:

Docker 容器技术使用指南

图片

而且 cAdivsor 的安装非常简单,下载一个 cAdvisor 的容器启动后,就可以使用主机 IP 加默认端口 8080 进行访问了。

6.3 跨多台主机上容器的监控

cAdivsor 虽然能采集到监控数据,也有很好的界面展示,但是并不能显示跨主机的监控数据,当主机多的情况,需要有一种集中式的管理方法将数据进行汇总展示,最经典的方案就是cAdvisor+ Influxdb+grafana,可以在每台主机上运行一个 cAdvisor 容器负责数据采集,再将采集后的数据都存到时序型数据库 influxdb 中,再通过图形展示工具 grafana 定制展示面板。结构如下:

Docker 容器技术使用指南

这三个工具的安装也非常简单,可以直接启动三个容器快速安装。如下所示:

图片

在上面的安装步骤中,先是启动 influxdb 容器,然后进行到容器内部配置一个数据库给cadvisor 专用,然后再启动 cadvisor 容器,容器启动的时候指定把数据存储到 influxdb 中,最后启动 grafana 容器,在展示页面里配置 grafana 的数据源为 influxdb,再定制要展示的数据,一个简单的跨多主机的监控系统就构建成功了。下图为 Grafana 的界面:

Docker 容器技术使用指南

6.4 Kubernetes 上容器的监控

在 Kubernetes 的新版本中已经集成了 cAdvisor,所以在 Kubernetes 架构下,不需要单独再去安装 cAdvisor,可以直接使用节点的 IP 加默认端口 4194 就可以直接访问 cAdvisor 的监控面板。而 Kubernetes 还提供一个叫 heapster 的组件用于聚合每个 node 上 cAdvisor 采集的数据,再通过 Kubedash 进行展示,结构如下:

Docker 容器技术使用指南

在 Kubernetes 的框架里,master 复杂调度后有的 node,所以在 heapster 启动时,当heapster 配合 k8s 运行时,需要指定 kubernetes_master 的地址,heapster 通过 k8s 得到所有 node 节点地址,然后通过访问对应的 node ip 和端口号(10250)来调用目标节点 Kubelet的 HTTP 接口,再由 Kubelet 调用 cAdvisor 服务获取该节点上所有容器的性能数据,并依次返回到 heapster 进行数据聚合。再通过 kubedash 进行展示,界面如下:

Docker 容器技术使用指南

6.5 Mesos 的监控方案

而 Mesos 提供一个 mesos-exporter 工具,用于导出 mesos 集群的监控数据 prometheus,而prometheus 是个集 db、graph、statistic、alert 于一体的监控工具,安装也非常简单,下载包后做些参数的配置,比如监控的对象就可以运行了,默认通过 9090 端口访问。而mesos-exporter 工具只需要在每个 slave 节点上启动一个进程,再 mesos-exporter 监控配置到 prometheus server 的监控目标中就可以获取到相关的数据。架构如下:

Docker 容器技术使用指南

在 Prometheus 的面板上我们可以看到 Prometheus 的监控对象可以为 mesos-export,也可以为 cAdvisor。

Docker 容器技术使用指南

下面为 Prometheus 的展示界面:

Docker 容器技术使用指南

6.6 性能采集工具的对比

cAdvisor 可以采集本机以及容器的资源监控数据,如 CPU、 memory、filesystem and network usage statistics) 。还可以展示 Docker 的信息及主机上已下载的镜像情况。因为cAdvisor 默认是将数据缓存在内存中,在显示界面上只能显示 1 分钟左右的趋势,所以历史的数据还是不能看到,但它也提供不同的持久化存储后端,比如 influxdb 等。

Heapster 的前提是使用 cAdvisor 采集每个 node 上主机和容器资源的使用情况,再将所有 node 上的数据进行聚合,这样不仅可以看到整个 Kubernetes 集群的资源情况,还可以分别查看每个 node/namespace 及每个 node/namespace 下 pod 的资源情况。这样就可以从cluster,node,pod 的各个层面提供详细的资源使用情况。默认也是存储在内存中,也提供不同的持久化存储后端,比如 influxdb 等。

mesos-exporter 的特点是可以采集 task 的监控数据。mesos 在资源调度时是在每个slave 上启动 task executor,这些 task executor 可以是容器,也可以不是容器。而mesos-exporter 则可以从 task 的角度来了解资源的使用情况,而不是一个一个没有关联关系的容器。

以上从几个典型的架构上介绍了一些 Docker 的运行监控,需要根据生产环境的特点结合每个监控产品的优势来达到监控的目的。比如 Grafana 的图表展示能力强,但是没有告警的功能,那么可以结合 Prometheus 在数据处理能力改善数据分析的展示。

Docker(十九)-Docker监控容器资源的占用情况

启动一个容器并限制资源

启动一个centos容器,限制其内存为1G ,可用cpu数为2

启动容器后,可以使用docker 的监控指令查看容器的运行状态

  • docker top 容器名: 查看容器的进程,不加容器名即查看所有
  • docker stats 容器名:查看容器的CPU,内存,IO 等使用信息

安装stress

在容器中安装docker容器压测工具 stress

stress压测

在容器使用stress指令进行负载压测

使用docker指令查看容器运行状态,可以os1容器的内存和cpu都得到了限制,即使给压测时超出了最大内存,也不会额外占用资源

Stress参数说明

Stress使用实例

  • 产生13个cpu进程4个io进程1分钟后停止运行

  • 产生3个cpu进程、3个io进程、2个10M的malloc()/free()进程,并且vm进程中malloc的字节不释放

  • 测试硬盘,通过mkstemp()生成800K大小的文件写入硬盘,对CPU、内存的使用要求很低

  • 产生13个进程,每个进程都反复不停的计算由rand ()产生随机数的平方根

  • 产生1024个进程,仅显示出错信息

  • 产生4个进程,每个进程反复调用sync(),sync()用于将内存上的内容写到硬盘上

  • 向磁盘中写入固定大小的文件,这个文件通过调用mkstemp()产生并保存在当前目录下,默认是文件产生后就被执行unlink(清除)操作,但是可以使用--hdd-bytes选项将产生的文件全部保存在当前目录下,这会将你的磁盘空间逐步耗尽

Docker容器CPU、memory资源限制

https://www.cnblogs.com/zhuochong/p/9728383.html

背景

在使用 docker 运行容器时,默认的情况下,docker没有对容器进行硬件资源的限制,当一台主机上运行几百个容器,这些容器虽然互相隔离,但是底层却使用着相同的 CPU、内存和磁盘资源。如果不对容器使用的资源进行限制,那么容器之间会互相影响,小的来说会导致容器资源使用不公平;大的来说,可能会导致主机和集群资源耗尽,服务完全不可用。

docker 作为容器的管理者,自然提供了控制容器资源的功能。正如使用内核的 namespace 来做容器之间的隔离,docker 也是通过内核的 cgroups 来做容器的资源限制;包括CPU、内存、磁盘三大方面,基本覆盖了常见的资源配额和使用量控制。

Docker内存控制OOME在linxu系统上,如果内核探测到当前宿主机已经没有可用内存使用,那么会抛出一个OOME(Out Of Memory Exception:内存异常 ),并且会开启killing去杀掉一些进程。

一旦发生OOME,任何进程都有可能被杀死,包括docker daemon在内,为此,docker特地调整了docker daemon的OOM_Odj优先级,以免他被杀掉,但容器的优先级并未被调整。经过系统内部复制的计算后,每个系统进程都会有一个OOM_Score得分,OOM_Odj越高,得分越高,(在docker run的时候可以调整OOM_Odj)得分最高的优先被kill掉,当然,也可以指定一些特定的重要的容器禁止被OMM杀掉,在启动容器时使用 –oom-kill-disable=true指定。

参考:Docker监控容器资源的占用情况

cgroup简介

cgroup是Control Groups的缩写,是Linux 内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(如 cpu、memory、磁盘IO等等) 的机制,被LXC、docker等很多项目用于实现进程资源控制。cgroup将任意进程进行分组化管理的 Linux 内核功能。cgroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O 或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为cgroup子系统,有以下几大子系统实现:

  1. blkio:设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
  2. cpu:使用调度程序为cgroup任务提供cpu的访问。
  3. cpuacct:产生cgroup任务的cpu资源报告。
  4. cpuset:如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
  5. devices:允许或拒绝cgroup任务对设备的访问。
  6. freezer:暂停和恢复cgroup任务。
  7. memory:设置每个cgroup的内存限制以及产生内存资源报告。
  8. net_cls:标记每个网络包以供cgroup方便使用。
  9. ns:命名空间子系统。
  10. perf_event:增加了对每group的监测跟踪的能力,即可以监测属于某个特定的group的所有线程以及运行在特定CPU上的线程。

目前docker只是用了其中一部分子系统,实现对资源配额和使用的控制。

可以使用stress工具来测试CPU和内存。使用下面的Dockerfile来创建一个基于Ubuntu的stress工具镜像。

FROM ubuntu:14.04
RUN apt-get update &&apt-get install stress

资源监控的关键目录:cat读出
已使用内存:
/sys/fs/cgroup/memory/docker/应用ID/memory.usage_in_bytes

分配的总内存:
/sys/fs/cgroup/memory/docker/应用ID/memory.limit_in_bytes

已使用的cpu:单位纳秒
/sys/fs/cgroup/cpuacct/docker/应用ID/cpuacct.usage

系统当前cpu:

例子

cpu使用率:
(已使用2-已使用1)/(系统当前2-系统当前1)*100%

内存限制

Docker 提供的内存限制功能有以下几点:

  • 容器能使用的内存和交换分区大小。
  • 容器的核心内存大小。
  • 容器虚拟内存的交换行为。
  • 容器内存的软性限制。
  • 是否杀死占用过多内存的容器。
  • 容器被杀死的优先级

一般情况下,达到内存限制的容器过段时间后就会被系统杀死。

内存限制相关的参数

执行docker run命令时能使用的和内存限制相关的所有选项如下。

选项描述
-m,--memory内存限制,格式是数字加单位,单位可以为 b,k,m,g。最小为 4M
--memory-swap内存+交换分区大小总限制。格式同上。必须必-m设置的大
--memory-reservation内存的软性限制。格式同上
--oom-kill-disable是否阻止 OOM killer 杀死容器,默认没设置
--oom-score-adj容器被 OOM killer 杀死的优先级,范围是[-1000, 1000],默认为 0
--memory-swappiness用于设置容器的虚拟内存控制行为。值为 0~100 之间的整数
--kernel-memory核心内存限制。格式同上,最小为 4M

用户内存限制

用户内存限制就是对容器能使用的内存和交换分区的大小作出限制。使用时要遵循两条直观的规则:-m,--memory选项的参数最小为 4 M。--memory-swap不是交换分区,而是内存加交换分区的总大小,所以--memory-swap必须比-m,--memory大。在这两条规则下,一般有四种设置方式。

你可能在进行内存限制的实验时发现docker run命令报错:WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.

这是因为宿主机内核的相关功能没有打开。按照下面的设置就行。

step 1:编辑/etc/default/grub文件,将GRUB_CMDLINE_LINUX一行改为GRUB_CMDLINE_LINUX="cgroup_enable=memory swapaccount=1"

step 2:更新 GRUB,即执行$ sudo update-grub

step 3: 重启系统。

1. 不设置

如果不设置-m,--memory--memory-swap,容器默认可以用完宿舍机的所有内存和 swap 分区。不过注意,如果容器占用宿主机的所有内存和 swap 分区超过一段时间后,会被宿主机系统杀死(如果没有设置--00m-kill-disable=true的话)。

2. 设置-m,--memory,不设置--memory-swap

-m--memory设置一个不小于 4M 的值,假设为 a,不设置--memory-swap,或将--memory-swap设置为 0。这种情况下,容器能使用的内存大小为 a,能使用的交换分区大小也为 a。因为 Docker 默认容器交换分区的大小和内存相同。

如果在容器中运行一个一直不停申请内存的程序,你会观察到该程序最终能占用的内存大小为 2a。

比如$ docker run -m 1G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小也为 1G。容器内的进程能申请到的总内存大小为 2G。

3. 设置-m,--memory=a,--memory-swap=b,且b > a

-m设置一个参数 a,给--memory-swap设置一个参数 b。a 时容器能使用的内存大小,b是容器能使用的 内存大小 + swap 分区大小。所以 b 必须大于 a。b -a 即为容器能使用的 swap 分区大小。

比如$ docker run -m 1G --memory-swap 3G ubuntu:16.04,该容器能使用的内存大小为 1G,能使用的 swap 分区大小为 2G。容器内的进程能申请到的总内存大小为 3G。

4. 设置-m,--memory=a,--memory-swap=-1

-m参数设置一个正常值,而给--memory-swap设置成 -1。这种情况表示限制容器能使用的内存大小为 a,而不限制容器能使用的 swap 分区大小。

这时候,容器内进程能申请到的内存大小为 a + 宿主机的 swap 大小。

Memory reservation

这种 memory reservation 机制不知道怎么翻译比较形象。Memory reservation 是一种软性限制,用于节制容器内存使用。给--memory-reservation设置一个比-m小的值后,虽然容器最多可以使用-m使用的内存大小,但在宿主机内存资源紧张时,在系统的下次内存回收时,系统会回收容器的部分内存页,强迫容器的内存占用回到--memory-reservation设置的值大小。

没有设置时(默认情况下)--memory-reservation的值和-m的限定的值相同。将它设置为 0 会设置的比-m的参数大 等同于没有设置。

Memory reservation 是一种软性机制,它不保证任何时刻容器使用的内存不会超过--memory-reservation限定的值,它只是确保容器不会长时间占用超过--memory-reservation限制的内存大小。

例如:

如果容器使用了大于 200M 但小于 500M 内存时,下次系统的内存回收会尝试将容器的内存锁紧到 200M 以下。

例如:

容器可以使用尽可能多的内存。--memory-reservation确保容器不会长时间占用太多内存。

OOM killer

默认情况下,在出现 out-of-memory(OOM) 错误时,系统会杀死容器内的进程来获取更多空闲内存。这个杀死进程来节省内存的进程,我们姑且叫它 OOM killer。我们可以通过设置--oom-kill-disable选项来禁止 OOM killer 杀死容器内进程。但请确保只有在使用了-m/--memory选项时才使用--oom-kill-disable禁用 OOM killer。如果没有设置-m选项,却禁用了 OOM-killer,可能会造成出现 out-of-memory 错误时,系统通过杀死宿主机进程或获取更改内存。

下面的例子限制了容器的内存为 100M 并禁止了 OOM killer:

是正确的使用方法。

而下面这个容器没设置内存限制,却禁用了 OOM killer 是非常危险的:

容器没用内存限制,可能或导致系统无内存可用,并尝试时杀死系统进程来获取更多可用内存。

一般一个容器只有一个进程,这个唯一进程被杀死,容器也就被杀死了。我们可以通过--oom-score-adj选项来设置在系统内存不够时,容器被杀死的优先级。负值更教不可能被杀死,而正值更有可能被杀死。

核心内存

核心内存和用户内存不同的地方在于核心内存不能被交换出。不能交换出去的特性使得容器可以通过消耗太多内存来堵塞一些系统服务。核心内存包括:

  • stack pages(栈页面)
  • slab pages
  • socket memory pressure
  • tcp memory pressure

可以通过设置核心内存限制来约束这些内存。例如,每个进程都要消耗一些栈页面,通过限制核心内存,可以在核心内存使用过多时阻止新进程被创建。

核心内存和用户内存并不是独立的,必须在用户内存限制的上下文中限制核心内存。

假设用户内存的限制值为 U,核心内存的限制值为 K。有三种可能地限制核心内存的方式:

  1. U != 0,不限制核心内存。这是默认的标准设置方式
  2. K < U,核心内存时用户内存的子集。这种设置在部署时,每个 cgroup 的内存总量被过度使用。过度使用核心内存限制是绝不推荐的,因为系统还是会用完不能回收的内存。在这种情况下,你可以设置 K,这样 groups 的总数就不会超过总内存了。然后,根据系统服务的质量自有地设置 U。
  3. K > U,因为核心内存的变化也会导致用户计数器的变化,容器核心内存和用户内存都会触发回收行为。这种配置可以让管理员以一种统一的视图看待内存。对想跟踪核心内存使用情况的用户也是有用的。

例如:

容器中的进程最多能使用 500M 内存,在这 500M 中,最多只有 50M 核心内存。

没用设置用户内存限制,所以容器中的进程可以使用尽可能多的内存,但是最多能使用 50M 核心内存。

Swappiness

默认情况下,容器的内核可以交换出一定比例的匿名页。--memory-swappiness就是用来设置这个比例的。--memory-swappiness可以设置为从 0 到 100。0 表示关闭匿名页面交换。100 表示所有的匿名页都可以交换。默认情况下,如果不使用--memory-swappiness,则该值从父进程继承而来。

例如:

--memory-swappiness设置为 0 可以保持容器的工作集,避免交换代理的性能损失。

CPU 限制

概述

Docker 的资源限制和隔离完全基于 Linux cgroups。对 CPU 资源的限制方式也和 cgroups 相同。Docker 提供的 CPU 资源限制选项可以在多核系统上限制容器能利用哪些 vCPU。而对容器最多能使用的 CPU 时间有两种限制方式:一是有多个 CPU 密集型的容器竞争 CPU 时,设置各个容器能使用的 CPU 时间相对比例。二是以绝对的方式设置容器在每个调度周期内最多能使用的 CPU 时间。

CPU 限制相关参数

docker run命令和 CPU 限制相关的所有选项如下:

选项描述
--cpuset-cpus=""允许使用的 CPU 集,值可以为 0-3,0,1
-c,--cpu-shares=0CPU 共享权值(相对权重)
cpu-period=0限制 CPU CFS 的周期,范围从 100ms~1s,即[1000, 1000000]
--cpu-quota=0限制 CPU CFS 配额,必须不小于1ms,即 >= 1000
--cpuset-mems=""允许在上执行的内存节点(MEMs),只对 NUMA 系统有效

其中--cpuset-cpus用于设置容器可以使用的 vCPU 核。-c,--cpu-shares用于设置多个容器竞争 CPU 时,各个容器相对能分配到的 CPU 时间比例。--cpu-period--cpu-quata用于绝对设置容器能使用 CPU 时间。

--cpuset-mems暂用不上,这里不谈。

CPU 集

我们可以设置容器可以在哪些 CPU 核上运行。

例如:

表示容器中的进程可以在 cpu 1 和 cpu 3 上执行。

表示容器中的进程可以在 cpu 0、cpu 1 及 cpu 3 上执行。

在 NUMA 系统上,我们可以设置容器可以使用的内存节点。

例如:

表示容器中的进程只能使用内存节点 1 和 3 上的内存。

表示容器中的进程只能使用内存节点 0、1、2 上的内存。

CPU 资源的相对限制

默认情况下,所有的容器得到同等比例的 CPU 周期。在有多个容器竞争 CPU 时我们可以设置每个容器能使用的 CPU 时间比例。这个比例叫作共享权值,通过-c--cpu-shares设置。Docker 默认每个容器的权值为 1024。不设置或将其设置为 0,都将使用这个默认值。系统会根据每个容器的共享权值和所有容器共享权值和比例来给容器分配 CPU 时间。

假设有三个正在运行的容器,这三个容器中的任务都是 CPU 密集型的。第一个容器的 cpu 共享权值是 1024,其它两个容器的 cpu 共享权值是 512。第一个容器将得到 50% 的 CPU 时间,而其它两个容器就只能各得到 25% 的 CPU 时间了。如果再添加第四个 cpu 共享值为 1024 的容器,每个容器得到的 CPU 时间将重新计算。第一个容器的CPU 时间变为 33%,其它容器分得的 CPU 时间分别为 16.5%、16.5%、33%。

必须注意的是,这个比例只有在 CPU 密集型的任务执行时才有用。在四核的系统上,假设有四个单进程的容器,它们都能各自使用一个核的 100% CPU 时间,不管它们的 cpu 共享权值是多少。

在多核系统上,CPU 时间权值是在所有 CPU 核上计算的。即使某个容器的 CPU 时间限制少于 100%,它也能使用各个 CPU 核的 100% 时间。

例如,假设有一个不止三核的系统。用-c=512的选项启动容器{C0},并且该容器只有一个进程,用-c=1024的启动选项为启动容器C2,并且该容器有两个进程。CPU 权值的分布可能是这样的:

表示容器中的进程CPU份额值为100。

CPU 资源的绝对限制

Linux 通过 CFS(Completely Fair Scheduler,完全公平调度器)来调度各个进程对 CPU 的使用。CFS 默认的调度周期是 100ms。

关于 CFS 的更多信息,参考CFS documentation on bandwidth limiting

我们可以设置每个容器进程的调度周期,以及在这个周期内各个容器最多能使用多少 CPU 时间。使用--cpu-period即可设置调度周期,使用--cpu-quota即可设置在每个周期内容器能使用的 CPU 时间。两者一般配合使用。

例如:

将 CFS 调度的周期设为 50000,将容器在每个周期内的 CPU 配额设置为 25000,表示该容器每 50ms 可以得到 50% 的 CPU 运行时间。

将容器的 CPU 配额设置为 CFS 周期的两倍,CPU 使用时间怎么会比周期大呢?其实很好解释,给容器分配两个 vCPU 就可以了。该配置表示容器可以在每个周期内使用两个 vCPU 的 100% 时间。

CFS 周期的有效范围是 1ms~1s,对应的--cpu-period的数值范围是 1000~1000000。而容器的 CPU 配额必须不小于 1ms,即--cpu-quota的值必须 >= 1000。可以看出这两个选项的单位都是 us。

正确的理解“绝对”

注意前面我们用--cpu-quota设置容器在一个调度周期内能使用的 CPU 时间时实际上设置的是一个上限。并不是说容器一定会使用这么长的 CPU 时间。比如,我们先启动一个容器,将其绑定到 cpu 1 上执行。给其--cpu-quota--cpu-period都设置为 50000。

调度周期为 50000,容器在每个周期内最多能使用 50000 cpu 时间。

再用docker stats test01可以观察到该容器对 CPU 的使用率在100%左右。然后,我们再以同样的参数启动另一个容器。

再用docker stats test01 test02可以观察到这两个容器,每个容器对 cpu 的使用率在 50% 左右。说明容器并没有在每个周期内使用 50000 的 cpu 时间。

使用docker stop test02命令结束第二个容器,再加一个参数-c 2048启动它:

再用docker stats test01命令可以观察到第一个容器的 CPU 使用率在 33% 左右,第二个容器的 CPU 使用率在 66% 左右。因为第二个容器的共享值是 2048,第一个容器的默认共享值是 1024,所以第二个容器在每个周期内能使用的 CPU 时间是第一个容器的两倍。

磁盘IO配额控制

相对于CPU和内存的配额控制,docker对磁盘IO的控制相对不成熟,大多数都必须在有宿主机设备的情况下使用。主要包括以下参数:

  • –device-read-bps:限制此设备上的读速度(bytes per second),单位可以是kb、mb或者gb。
  • –device-read-iops:通过每秒读IO次数来限制指定设备的读速度。
  • –device-write-bps :限制此设备上的写速度(bytes per second),单位可以是kb、mb或者gb。
  • –device-write-iops:通过每秒写IO次数来限制指定设备的写速度。
  • –blkio-weight:容器默认磁盘IO的加权值,有效值范围为10-100。
  • –blkio-weight-device: 针对特定设备的IO加权控制。其格式为DEVICE_NAME:WEIGHT

存储配额控制的相关参数,可以参考Red Hat文档中blkio这一章,了解它们的详细作用。

磁盘IO配额控制示例

blkio-weight

要使–blkio-weight生效,需要保证IO的调度算法为CFQ。可以使用下面的方式查看:

root@ubuntu:~# cat /sys/block/sda/queue/scheduler
noop [deadline] cfq

使用下面的命令创建两个–blkio-weight值不同的容器:

docker run -ti –rm –blkio-weight 100 ubuntu:stress
docker run -ti –rm –blkio-weight 1000 ubuntu:stress

在容器中同时执行下面的dd命令,进行测试:

time dd if=/dev/zero of=test.out bs=1M count=1024 oflag=direct

最终输出如下图所示:

quota08

在我的测试环境上没有达到理想的测试效果,通过docker官方的blkio-weight doesn’t take effect in docker Docker version 1.8.1 #16173,可以发现这个问题在一些环境上存在,但docker官方也没有给出解决办法。

device-write-bps

使用下面的命令创建容器,并执行命令验证写速度的限制。

docker run -tid –name disk1 –device-write-bps /dev/sda:1mb ubuntu:stress

通过dd来验证写速度,输出如下图示:

quota09

可以看到容器的写磁盘速度被成功地限制到了1MB/s。device-read-bps等其他磁盘IO限制参数可以使用类似的方式进行验证。

容器空间大小限制

在docker使用devicemapper作为存储驱动时,默认每个容器和镜像的最大大小为10G。如果需要调整,可以在daemon启动参数中,使用dm.basesize来指定,但需要注意的是,修改这个值,不仅仅需要重启docker daemon服务,还会导致宿主机上的所有本地镜像和容器都被清理掉。

使用aufs或者overlay等其他存储驱动时,没有这个限制。

标签:

头像

小麦苗

学习或考证,均可联系麦老师,请加微信db_bao或QQ646634621

您可能还喜欢...

发表回复

嘿,我是小麦,需要帮助随时找我哦
  • 18509239930
  • 个人微信

  • 麦老师QQ聊天
  • 个人邮箱
  • 点击加入QQ群
  • 个人微店

  • 回到顶部
返回顶部