后端资源精选Dockerdocker

Docker文档整理与汇总

2016-09-01  本文已影响2152人  胡图仙人

写这篇文章主要是为了今后毕业论文素材上的整理,同时对docker进行巩固温习。
大纲:

                               一、docker简介

Docker是一个由GO语言写的程序运行的“容器”(Linux containers, LXCs); 目前云服务的基石是操作系统级别的隔离,在同一台物理服务器上虚拟出多个主机。Docker则实现了一种应用程序级别的隔离; 它改变我们基本的开发、操作单元,由直接操作虚拟主机(VM),转换到操作程序运行的“容器”上来。
Docker是2013年Docker 是 PaaS提供商 dotCloud 开源的一个基于 LXC 的高级容器引擎,源代码托管在 Github上, 基于go语言并遵从Apache2.0协议开源,是dotCloud公司的绝处逢生之作,目前该公司以[docker]为名。
从2013年3月20日,第一个版本的Docker正式发布到 2014年6月Docker 1.0 正式发布,经历了15个月。 虽然发展历程很短,但Docker正在有越来越流行的趋势。
其实Container技术并非Docker的创新,HeroKu, NodeJitsu 等云服务商都采用了类似这种轻量级的虚拟化技术,但Docker是第一个将这这种Container技术大规模开源并被社区广泛接受的。
截止2016年9月1日,docker版本已经发展到了1.12,支持容器管理功能。

docker版本
                       二、docker生态系统和应用场景

2.1 docker生态系统

docker生态系统如下图所示。


docker生态系统

Docker具备大量的合作用户如亚马逊、jekins等公司,社区活跃度也非常高。
Docker提供的平台主要有Docker Hub和Docker Engine(Docker Engine是一系列linux技术的集合体现,之后会有详细介绍)。
Docker生态系统需要了解的核心概念主要有三大组件。

2.1.1 Docker镜像

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

docker image construct
2.1.2 Docker仓库

Docker Hub是类似于Github的一种代码仓库,同样的,Docker 仓库也有公有和私有的概念。公有的 Docker 仓库名字是 Docker Hub。Docker Hub 提供了庞大的镜像集合供使用。这些镜像可以是自己创建,或者在别人的镜像基础上创建。Docker 仓库是 Docker 的分发部分。

docker registries
2.1.3 Docker 容器

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

docker container

2.2 Docker应用场景

Docker的生态系统如此庞大和受欢迎,那么它的应用场景在哪些方面呢?
我查阅资料,整理了一些场景如下。

以上场景应用摘自知乎问答https://www.zhihu.com/question/22969309

小结:个人认为在linux进行学习时,可以将学习内容进行docker化打包保存到docker hub或者私有仓库,这样就不怕在环境丢失时重新配置环境。当然,实际应用时,一个用户数不大的web应用放在docker上操作也简单,是可以作为一个备份保存下来的。

                            三、Docker原理

Docker核心解决的问题是利用LXC来实现类似VM的功能,从而利用更加节省的硬件资源提供给用户更多的计算资源。同VM的方式不同, LXC其并不是一套硬件虚拟化方法 - 无法归属到全虚拟化、部分虚拟化和半虚拟化中的任意一个,而是一个操作系统级虚拟化方法, 理解起来可能并不像VM那样直观。所以我们从虚拟化到docker要解决的问题出发,看看他是怎么满足用户虚拟化需求的。
用户需要考虑虚拟化方法,尤其是硬件虚拟化方法,需要借助其解决的主要是以下4个问题:

docker原理结构

3.1 Namespace

Namespace主要负责资源隔离,可以保障一个容器中运行一个进程而且不能看到和影响容器外的其它进程。

Namespace

有了以上5种namespace从进程、网络、IPC、文件系统、UTS和用户角度的隔离,一个container就可以对外展现出一个独立计算机的能力,并且不同container从OS层面实现了隔离。然而不同namespace之间资源还是相互竞争的,仍然需要类似ulimit来管理每个container所能使用的资源 - LXC 采用的是cgroup。

3.2 Control Groups(cgroup)

cgroups 实现了对资源的配额和度量。 cgroups 的使用非常简单,提供类似文件的接口,在 /cgroup目录下新建一个文件夹即可新建一个group,在此文件夹中新建task文件,并将pid写入该文件,即可实现对该进程的资源控制。具体的资源配置选项可以在该文件夹中新建子 subsystem ,{子系统前缀}.{资源项} 是典型的配置方法.
通过cgroup限制资源如cpu、内存、硬盘I/O的使用。

3.3 Linux 容器 (LXC)

借助于namespace的隔离机制和cgroup限额功能,LXC提供了一套统一的API和工具来建立和管理container。
LXC 旨在提供一个共享kernel的 OS 级虚拟化方法,在执行时不用重复加载Kernel, 且container的kernel与host共享,因此可以大大加快container的 启动过程,并显著减少内存消耗。在实际测试中,基于LXC的虚拟化方法的IO和CPU性能几乎接近 baremetal 的性能, 大多数数据有相比 Xen具有优势。当然对于KVM这种也是通过Kernel进行隔离的方式, 性能优势或许不是那么明显, 主要还是内存消耗和启动时间上的差异。

3.4 AUFS

Docker对container的使用基本是建立在LXC基础之上的,然而LXC存在的问题是难以移动 - 难以通过标准化的模板制作、重建、复制和移动 container。
在以VM为基础的虚拟化手段中,有image和snapshot可以用于VM的复制、重建以及移动的功能。想要通过container来实现快速的大规模部署和更新, 这些功能不可或缺。
Docker 正是利用AUFS来实现对container的快速更新 - 在docker0.7中引入了storage driver, 支持AUFS, VFS, device mapper, 也为BTRFS以及ZFS引入提供了可能。 但除了AUFS都未经过dotcloud的线上使用,因此我们还是从AUFS的角度介绍。
AUFS (AnotherUnionFS) 是一种 Union FS, 简单来说就是支持将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)的文件系统, 更进一步地, AUFS支持为每一个成员目录(AKA branch)设定'readonly', 'readwrite' 和 'whiteout-able' 权限, 同时AUFS里有一个类似
分层的概念, 对 readonly 权限的branch可以逻辑上进行修改(增量地, 不影响readonly部分的)。通常 Union FS有两个用途, 一方面可以实现不借助 LVM, RAID 将多个disk和挂在到一个目录下, 另一个更常用的就是将一个readonly的branch和一个writeable的branch联合在一起,Live CD正是基于此可以允许在 OS image 不变的基础上允许用户在其上进行一些写操作。Docker在AUFS上构建的container image也正是如此。

AUFS image

如上图所示,AUFS技术利用了写时拷贝技术,只读部分是Image,可写部分是container.允许read-only和read-write目录并存;可以实现把多个不同目录的内容合并在一起。

3.5 GRSEC

grsec是linux kernel安全相关的patch, 用于保护host防止非法入侵。由于其并不是docker的一部分,我们只进行简单的介绍。
grsec可以主要从4个方面保护进程不被非法入侵:

3.6 Docker运行原理

Docker本身是基于C/S架构的,有自己的服务器和客户端,Docker实际上由三大组件构成,如下图所示。

docker组件

其中,docker client是用户界面,docker daemon处理服务请求,docker containers负责应用程序的运行。

                              四、Docker与vm性能对比

4.1 Docker性能测试测试环境:

4.1.1 Docker与虚拟机计算效率比较

在测试中是通过运算Linpack程序来获得计算能力数据的。结果如下图所示:

计算效率对比

图中从左往右分别是物理机、docker和虚拟机的计算能力数据。可见docker相对于物理机其计算能力几乎没有损耗,而虚拟机对比物理机则有着非常明显的损耗。虚拟机的计算能力损耗在50%左右。 为什么会有这么大的性能损耗呢?一方面是因为虚拟机增加了一层虚拟硬件层,运行在虚拟机上的应用程序在进行数值计算时是运行在Hypervisor虚拟的CPU上的;另外一方面是由于计算程序本身的特性导致的差异。虚拟机虚拟的cpu架构不同于实际cpu架构,数值计算程序一般针对特定的cpu架构有一定的优化措施,虚拟化使这些措施作废,甚至起到反效果。比如对于本次实验的平台,实际的CPU架构是2块物理CPU,每块CPU拥有16个核,共32个核,采用的是NUMA架构;而虚拟机则将CPU虚拟化成一块拥有32个核的CPU。这就导致了计算程序在进行计算时无法根据实际的CPU架构进行优化,大大减低了计算效率。

4.1.2 docker与虚拟机内存访问效率比较

内存访问效率的比较相对比较复杂一点,主要是内存访问有多种场景:

docker与虚拟机内存访问模型

可见在应用程序内存访问上,虚拟机的应用程序要进行2次的虚拟内存到物理内存的映射,读写内存的代价比docker的应用程序高。

4.1.3 docker与虚拟机启动时间及资源耗费比较

上面两个小节主要从运行在docker里的程序和运行在虚拟机里的程序进行性能比较。事实上,docker之所以如此受到开发者关注的另外一个重要原因是启动docker的系统代价比启动一台虚拟机的代价要低得多:无论从启动时间还是从启动资源耗费角度来说。docker直接利用宿主机的系统内核,避免了虚拟机启动时所需的系统引导时间和操作系统运行的资源消耗。利用docker能在几秒钟之内启动大量的容器,这是虚拟机无法办到的。快速启动、低系统资源消耗的优点使docker在弹性云平台和自动运维系统方面有着很好的应用前景。

4.2 docker优缺点总结

4.2.1优点
4.2.2缺点

5.1 Docker安装

windows、mac、linux版本安装均可在官网https://docs.docker.com/

5.2 常用docker命令

其它语法可通过docker命令查询学习,后面的案例会介绍一些实际命令的使用。

5.3 dockerfile文件的编写

5.4 Docker应用案例

5.4.1 javeee应用
5.4.2 mysql服务
5.4.3 wordpress博客服务
5.4.4 tomcat的web应用,以现有应用为例
                           六、容器管理工具kubernetes

6.1 kubernetes简介

Kubernetes是Google开源的容器集群管理系统,其提供应用部署、维护、 扩展机制等功能,利用Kubernetes能方便地管理跨机器运行容器化的应用,其主要功能如下:

  1. 使用Docker对应用程序包装(package)、实例化(instantiate)、运行(run)。

  2. 以集群的方式运行、管理跨机器的容器。

  3. 解决Docker跨机器容器之间的通讯问题。

  4. Kubernetes的自我修复机制使得容器集群总是运行在用户期望的状态。
      它透明地为用户提供原生态系统,如“需要5个 WildFly服务器和1个 MySQL服务器运行". Kubernetes具有自我修复机制,如重新启动 重新启动定时计划 复制容器以确保恢复状态,用户只需要定义状态,那么 Kubernetes就会确保状态总是在集群中。
      Docker定义了运行代码时的容器,有命令用来启动 停止 重启 链接容器,Kubernetes使用Docker打包以及实例化应用程序。
      一个典型的应用程序必须跨多个主机。 例如,您的web层(Apache )可能运行在一个容器。 同样地,应用程序层将会运行在另外一组不同的容器中。 web层需要将请求委托给应用程序层。 当然,在某些情况下,你可能将web服务器和应用服务器打包在一起放在相同的容器。 但是数据库层通常运行在一个单独层中。 这些容器之间需要相互交互。 使用上面的任何解决方案都需要编制脚本启动容器,以及监控容器,因防止出现问题。 而Kubernetes在应用程序状态被定义后将为用户实现所有这些工作。

6.2 kubernetes原理与架构

6.2.1 kubernetes概念
kubernetes架构
6.2.1.1 Pods

Pod是Kubernetes的基本操作单元,把相关的一个或多个容器构成一个Pod,通常Pod里的容器运行相同的应用。Pod包含的容器运行在同一个Minion(Host)上,看作一个统一管理单元,共享相同的volumes和network namespace/IP和Port空间。

6.2.1.2 Services

Services也是Kubernetes的基本操作单元,是真实应用服务的抽象,每一个服务后面都有很多对应的容器来支持,通过Proxy的port和服务selector决定服务请求传递给后端提供服务的容器,对外表现为一个单一访问接口,外部不需要了解后端如何运行,这给扩展或维护后端带来很大的好处。

6.2.1.3 Replication Controllers

Replication Controller确保任何时候Kubernetes集群中有指定数量的pod副本(replicas)在运行, 如果少于指定数量的pod副本(replicas),Replication Controller会启动新的Container,反之会杀死多余的以保证数量不变。Replication Controller使用预先定义的pod模板创建pods,一旦创建成功,pod 模板和创建的pods没有任何关联,可以修改pod 模板而不会对已创建pods有任何影响,也可以直接更新通过Replication Controller创建的pods。对于利用pod 模板创建的pods,Replication Controller根据label selector来关联,通过修改pods的label可以删除对应的pods。Replication Controller主要有如下用法:

  1. Rescheduling
    如上所述,Replication Controller会确保Kubernetes集群中指定的pod副本(replicas)在运行, 即使在节点出错时。
  2. Scaling
    通过修改Replication Controller的副本(replicas)数量来水平扩展或者缩小运行的pods。
  3. Rolling updates
    Replication Controller的设计原则使得可以一个一个地替换pods来rolling updates服务。
  4. Multiple release tracks
    如果需要在系统中运行multiple release的服务,Replication Controller使用labels来区分multiple release tracks。
6.2.1.4 Labels

Labels是用于区分Pod、Service、Replication Controller的key/value键值对,Pod、Service、 Replication Controller可以有多个label,但是每个label的key只能对应一个value。Labels是Service和Replication Controller运行的基础,为了将访问Service的请求转发给后端提供服务的多个容器,正是通过标识容器的labels来选择正确的容器。同样,Replication Controller也使用labels来管理通过pod 模板创建的一组容器,这样Replication Controller可以更加容易,方便地管理多个容器,无论有多少容器。

6.2.2 kubernetes架构

Kubenetes整体框架如下图,主要包括kubecfg、Master API Server、Kubelet、Minion(Host)以及Proxy。


Kubernetes High Level构件

具体每部分功能可参考书籍《kubernetes权威指南》。

6.3 kubernetes中算法介绍

6.3.1算法概览

最新一版的kubernetes release中我们看到了一个令人欣喜的特性:Autoscaling。它实现了replicationcontroller中pod的横向自动扩容。以下是摘自官方文档的相关内容:
自动扩容将通过一个新的resource(就像之前的pod,service等等)实现。目前只支持针对cpu使用度进行动态扩容。未来的版本中,将会实现基于另一个resource:metrics(这是说未来监控数据将会有一个更统一的展示?)
主要结构
1.Scale subresourceScale subresource是一个虚拟的resource,用来记录扩容进度。其主要结构如下:
// represents a scaling request for a resource.
type Scale struct {
unversioned.TypeMeta
api.ObjectMeta

// defines the behavior of the scale.
Spec ScaleSpec

// current status of the scale.
Status ScaleStatus
}

// describes the attributes of a scale subresource
type ScaleSpec struct {
// desired number of instances for the scaled object.
Replicas int json:"replicas,omitempty"
}
// represents the current status of a scale subresource.
type ScaleStatus struct {
// actual number of observed instances of the scaled object.
Replicas int json:"replicas"
// label query over pods that should match the replicas count. Selector map[string]string json:"selector,omitempty"
}

其中ScaleSpec.Replicas表示我们预定的集群实例数目标。ScaleStatus.Replicas表示当前实例数,ScaleStatus.Selector是一个选择器,选择对应的pods。
2.HorizontalPodAutoscaler 这个就是真正控制扩容的resource,其结构如下:
// configuration of a horizontal pod autoscaler.
type HorizontalPodAutoscaler struct {
unversioned.TypeMeta
api.ObjectMeta
// behavior of autoscaler.
Spec HorizontalPodAutoscalerSpec
// current information about the autoscaler.
Status HorizontalPodAutoscalerStatus
}
// specification of a horizontal pod autoscaler.
type HorizontalPodAutoscalerSpec struct {
// reference to Scale subresource; horizontal pod autoscaler will learn the current resource
// consumption from its status,and will set the desired number of pods by modifying its spec.
ScaleRef SubresourceReference
// lower limit for the number of pods that can be set by the autoscaler, default 1.
MinReplicas *int
// upper limit for the number of pods that can be set by the autoscaler.
// It cannot be smaller than MinReplicas.
MaxReplicas int
// target average CPU utilization (represented as a percentage of requested CPU) over all the pods;
// if not specified it defaults to the target CPU utilization at 80% of the requested resources.
CPUUtilization *CPUTargetUtilization
}
type CPUTargetUtilization struct {
// fraction of the requested CPU that should be utilized/used, // e.g. 70 means that 70% of the requested CPU should be in use.
TargetPercentage int
}
// current status of a horizontal pod autoscaler
type HorizontalPodAutoscalerStatus struct {
// most recent generation observed by this autoscaler. ObservedGeneration *int64
// last time the HorizontalPodAutoscaler scaled the number of pods;
// used by the autoscaler to control how often the number of pods is changed.
LastScaleTime *unversioned.Time
// current number of replicas of pods managed by this autoscaler.
CurrentReplicas int
// desired number of replicas of pods managed by this autoscaler. DesiredReplicas int
// current average CPU utilization over all pods, represented as a percentage of requested CPU,
// e.g. 70 means that an average pod is using now 70% of its requested CPU.
CurrentCPUUtilizationPercentage *int
}

其中的ScaleRef是一个Scale subresource的引用,MinReplicas, MaxReplicas and CPUUtilization定义了自动扩容的配置(允许的最大实例数,最小实例数,以及cpu使用配额)。
3.HorizontalPodAutoscalerList用于记录一个namespace下的所有HorizontalPodAutoscaler。本质上是一个结构数组。

6.3.2自动扩容算法

官方文档给出的并不是算法, 而是实现步骤,整个自动扩容的流程是:1.通过podselector找到要扩容的集群2.收集集群最近的cpu使用情况(CPU utilization)3.对比在扩容条件里记录的cpu限额(CPUUtilization)4.调整实例数(必须要满足不超过最大/最小实例数)5.每隔30s做一次自动扩容的判断(这个日后应该会成为一个扩容条件的参数
CPU utilization的计算方法是用cpu usage(最近一分钟的平均值,通过heapster可以直接获取到)除以cpu请求(疑问:这个是是什么意思?)。未来k8s会开放一个api直接获取heapster收集到的监控数据。
真正的算法是:
A.TargetNumOfPods = ceil(sum(CurrentPodsCPUUtilization) / Target)ceil()表示取大于或等于某数的最近一个整数举个栗子:我们有一个集群实例数是3 pods。cpu限额,即Target是每个pod分配1.1核,当cpu的使用度CurrentPodsCPUUtilization为1.1,1.4,1.3时,要扩容成多少个呢?ceil((1.1+1.4+1.3)/1.1)= 4 所以扩容成四个实例。
B.由于启动实例时cpu的使用度会陡增,所以自动扩容会等待一段时间以收集准确的运行时监控数据。每次扩容/缩容后冷却三分钟才能再度进行扩容,而缩容则要等5分钟后。这是因为自动扩容使用保守的方法,尽可能满足pods业务的正常使用,所以扩容的优先级要大于缩容。
C.当满足:avg(CurrentPodsConsumption) / Target >1.1 或 <0.9时才会触发进行扩容/缩容。这也是为了避免出现频繁的扩容缩容。
扩容条件的相对与绝对度量
为了方便使用,建议采用相对(relative)的度量标准(如 90%的cpu资源)而不是绝对的标准(如0.6个cpu核心)来描述扩容条件。否则,当用户修改pods的请求资源时还需要去修改这些绝对值。比如:我们创建一个集群时,podtemplate中的resource里填入了cpu为1,即最多分配一个cpu核心给该pod,如果在扩容条件中采用绝对标准,我们必须填一个小于1的数,否则这个条件根本不会被触发。而当我们要修改分配的资源为0.8个核心时,又必须要修改扩容条件以确保其小于0.8。这就很麻烦了。
kubectl中的支持以及待支持
为了方便使用,在kubectl的cmd命令中加入了 creating/updating/deleting/listing 命令用来操作HorizontalPodAutoscaler
未来可能会加入像kubectl autoscale这样的命令,对一个已经在跑的集群实时进行动态扩容。

6.4 kubernetes环境搭建与工具介绍

本人已通过此教程搭建单机版kubernetes成功http://dockone.io/article/950
多机版由于条件限制还未成功实施。

                           七、Docker学习总结与展望

7.1 总结

容器技术究竟其成熟与否,也是互联网发展的一种趋势,它所提出的理念是值得每一位互联网工作者去学习和探讨的。

7.2 展望

docker官网在容器编排上的持续发力,今年7月份时docker公司将swarm整合进入docker engine层,其实我希望看到docker生态系统不要过多的碎片化,解决方案过多,容易导致迷失方向。

上一篇下一篇

猜你喜欢

热点阅读