Docker并非全能的,设计之初也不是KVM之类虚拟化手段的替代品,简单总结几点:
-
Docker是基于Linux 64bit的,没法在32bit的linux/Windows/unix环境下使用
-
LXC是基于cgroup等linux kernel功能的,所以container的guest系统只能是linux base的
-
隔离性相比KVM之类的虚拟化方案仍是有些欠缺,全部container公用一部分的运行库
-
网络管理相对简单,主要是基于namespace隔离
-
cgroup的cpu和cpuset提供的cpu功能相比KVM的等虚拟化方案相比难以度量(因此dotcloud主要是按内存收费)
-
Docker对disk的管理比较有限
-
container随着用户进程的中止而销毁,container中的log等用户数据不便收集
针对1-2,有windows base应用的需求的基本能够pass了; 3-5主要是看用户的需求,究竟是须要一个container仍是一个VM, 同时也决定了docker做为 IaaS 不太可行。
针对6,7虽然是docker自己不支持的功能,可是能够经过其余手段解决(disk quota, mount --bind)。总之,选用container仍是vm, 就是在隔离性和资源复用性上作权衡。
另外即使docker 0.7可以支持非AUFS的文件系统,可是因为其功能还不稳定,商业应用或许会存在问题,而AUFS的稳定版须要kernel 3.8, 因此若是想复制dotcloud的成功案例,可能须要考虑升级kernel或者换用ubuntu的server版本(后者提供deb更新)。这也是为何开源界更倾向于支持ubuntu的缘由(kernel版本)
Docker并不是适合全部应用场景,Docker只能虚拟基于Linux的服务。Windows Azure 服务可以运行Docker实例,但到目前为止Windows服务还不能被虚拟化。
可能最大的障碍在于管理实例之间的交互。因为全部应用组件被拆分到不一样的容器中,全部的服务器须要以一致的方式彼此通讯。这意味着任何人若是选择复杂的基础设施,那么必须掌握应用编程接口管理以及集群工具,好比Swarm、Mesos或者Kubernets以确保机器按照预期运转并支持故障切换。
Docker在本质上是一个附加系统。使用文件系统的不一样层构建一个应用是有可能的。每一个组件被添加到以前已经建立的组件之上,能够比做为一个文件系统更明智。分层架构带来另外一方面的效率提高,当你重建存在变化的Docker镜像时,不须要重建整个Docker镜像,只须要重建变化的部分。
可能更为重要的是,Docker旨在用于弹性计算。每一个Docker实例的运营生命周期有限,实例数量根据需求增减。在一个管理适度的系统中,这些实例生而平等,再也不须要时便各自消亡了。
针对Docker环境存在的不足,意味着在开始部署Docker前须要考虑以下几个问题。首先,Docker实例是无状态的。这意味着它们不该该承载任何交易数据,全部数据应该保存在数据库服务器中。
其次,开发Docker实例并不像建立一台虚拟机、添加应用而后克隆那样简单。为成功建立并使用Docker基础设施,管理员须要对系统管理的各个方面有一个全面的理解,包括Linux管理、编排及配置工具好比Puppet、Chef以及Salt。这些工具生来就基于命令行以及脚本。
LXC所实现的隔离性主要是来自kernel的namespace, 其中pid, net, ipc, mnt, uts 等namespace将container的进程, 网络, 消息, 文件系统和hostname 隔离开。
pid namespace
以前提到用户的进程是lxc-start进程的子进程, 不一样用户的进程就是经过pidnamespace隔离开的,且不一样 namespace 中能够有相同PID。具备如下特征:
-
每一个namespace中的pid是有本身的pid=1的进程(相似/sbin/init进程)
-
每一个namespace中的进程只能影响本身的同一个namespace或子namespace中的进程
-
由于/proc包含正在运行的进程,所以在container中的pseudo-filesystem的/proc目录只能看到本身namespace中的进程
-
由于namespace容许嵌套,父namespace能够影响子namespace的进程,因此子namespace的进程能够在父namespace中看到,可是具备不一样的pid
正是由于以上的特征,全部的LXC进程在docker中的父进程为docker进程,每一个lxc进程具备不一样的namespace。同时因为容许嵌套,所以能够很方便的实现 LXC in LXC
net namespace
有了 pid namespace, 每一个namespace中的pid可以相互隔离,可是网络端口仍是共享host的端口。网络隔离是经过netnamespace实现的,
每一个net namespace有独立的 network devices, IP addresses, IP routing tables, /proc/net 目录。这样每一个container的网络就能隔离开来。
LXC在此基础上有5种网络类型,docker默认采用veth的方式将container中的虚拟网卡同host上的一个docker bridge链接在一块儿。
ipc namespace
container中进程交互仍是采用linux常见的进程间交互方法(interprocess communication - IPC), 包括常见的信号量、消息队列和共享内存。然而同VM不一样,container 的进程间交互实际上仍是host上具备相同pid namespace中的进程间交互,所以须要在IPC资源申请时加入namespace信息 - 每一个IPC资源有一个惟一的 32bit ID。
mnt namespace
相似chroot,将一个进程放到一个特定的目录执行。mnt namespace容许不一样namespace的进程看到的文件结构不一样,这样每一个 namespace 中的进程所看到的文件目录就被隔离开了。同chroot不一样,每一个namespace中的container在/proc/mounts的信息只包含所在namespace的mount point。
uts namespace
UTS(“UNIX Time-sharing System”) namespace容许每一个container拥有独立的hostname和domain name,
使其在网络上能够被视做一个独立的节点而非Host上的一个进程。
user namespace
每一个container能够有不一样的 user 和 group id, 也就是说能够以container内部的用户在container内部执行程序而非Host上的用户。
有了以上6种namespace从进程、网络、IPC、文件系统、UTS和用户角度的隔离,一个container就能够对外展示出一个独立计算机的能力,而且不一样container从OS层面实现了隔离。
然而不一样namespace之间资源仍是相互竞争的,仍然须要相似ulimit来管理每一个container所能使用的资源 - LXC 采用的是cgroup。
Control Groups
cgroups 实现了对资源的配额和度量。 cgroups 的使用很是简单,提供相似文件的接口,在 /cgroup目录下新建一个文件夹便可新建一个group,在此文件夹中新建task文件,并将pid写入该文件,便可实现对该进程的资源控制。具体的资源配置选项能够在该文件夹中新建子 subsystem ,{子系统前缀}.{资源项} 是典型的配置方法,
如memory.usage_in_bytes 就定义了该group 在subsystem memory中的一个内存限制选项。
另外,cgroups中的 subsystem能够随意组合,一个subsystem能够在不一样的group中,也能够一个group包含多个subsystem - 也就是说一个 subsystem。
关于术语定义
A *cgroup* associates a set of tasks with a set of parameters for one
or more subsystems.
A *subsystem* is a module that makes use of the task grouping
facilities provided by cgroups to treat groups of tasks in
particular ways. A subsystem is typically a "resource controller" that
schedules a resource or applies per-cgroup limits, but it may be
anything that wants to act on a group of processes, e.g. a
virtualization subsystem.
咱们主要关心cgroups能够限制哪些资源,即有哪些subsystem是咱们关心。
cpu : 在cgroup中,并不能像硬件虚拟化方案同样可以定义CPU能力,可是可以定义CPU轮转的优先级,所以具备较高CPU优先级的进程会更可能获得CPU运算。
经过将参数写入cpu.shares,便可定义改cgroup的CPU优先级 - 这里是一个相对权重,而非绝对值。固然在cpu这个subsystem中还有其余可配置项,手册中有详细说明。
cpusets : cpusets 定义了有几个CPU能够被这个group使用,或者哪几个CPU能够供这个group使用。在某些场景下,单CPU绑定能够防止多核间缓存切换,从而提升效率
memory : 内存相关的限制
blkio : block IO相关的统计和限制,byte/operation统计和限制(IOPS等),读写速度限制等,可是这里主要统计的都是同步IO
net_cls, cpuacct , devices , freezer 等其余可管理项。
借助于namespace的隔离机制和cgroup限额功能,
LXC提供了一套统一的API和工具来创建和管理container, LXC利用了以下 kernel 的features:
-
Kernel namespaces (ipc, uts, mount, pid, network and user)
-
Apparmor and SELinux profiles
-
Seccomp policies
-
Chroots (using pivot_root)
-
Kernel capabilities
-
Control groups (cgroups)
LXC 向用户屏蔽了以上 kernel 接口的细节, 提供了以下的组件大大简化了用户的开发和使用工做:
LXC 旨在提供一个共享kernel的 OS 级虚拟化方法,在执行时不用重复加载Kernel, 且container的kernel与host共享,所以能够大大加快container的 启动过程,并显著减小内存消耗。在实际测试中,基于LXC的虚拟化方法的IO和CPU性能几乎接近 baremetal 的性能
[9] , 大多数数据有相比 Xen具备优点。固然对于KVM这种也是经过Kernel进行隔离的方式, 性能优点或许不是那么明显, 主要仍是内存消耗和启动时间上的差别。在参考文献 [10] 中提到了利用iozone进行 Disk IO吞吐量测试KVM反而比LXC要快,并且笔者在device mapping driver下重现一样case的实验中也确实能获得如此结论。参考文献从网络虚拟化中虚拟路由的场景(网络IO和CPU角度)比较了KVM和LXC, 获得结论是KVM在性能和隔离性的平衡上比LXC更优秀 - KVM在吞吐量上略差于LXC, 但CPU的隔离可管理项比LXC更明确。
关于CPU, DiskIO, network IO 和 memory 在KVM和LXC中的比较仍是须要更多的实验才能得出可信服的结论。
Docker推出的一个名为
Docker Content Trust(DCT)的新功能,
它可帮助IT专业人士确保Docker的安全性。DCT使用了
一个公共密钥基础设施(PKI)的方法,它提供了
两个不一样的密钥:一个离线(root)密钥和一个标记(每次入库)密钥,当第一次发布者推出镜像时它可建立和存储客户端。
此举有助于弥补正在使用恶意容器这一最大的漏洞。DCT还生成了一个时间戳密钥,它可保护系统免受重放攻击,即运行过时的标记内容。这解决了上面说起容器具备不一样安全补丁等级的问题。
为了解决针对容器安全性的问题,包括Docker在内的众多公司都为Docker发布了安全基准。这套标准为确保Docker容器的安全性提供了指导。全篇118页的文档囊括了部署Docker容器的84个最佳实践以及一个涉及全部内容的检查清单。
那么,若是你决定自行负责确保Docker容器的安全性,但又不知道从何入手,咱们在这里为你提供了一些建议:
阅读上面说起的Docker安全基准文件。重点关注与如何部署基于容器的应用程序相关的建议和最佳实践。这真的是有助于缓解你的财务压力,认真考虑大部分因糟糕设计而致使的Docker安全性问题。
考虑你的特定安全性需求。这将促使你选择正确的工具和方法。不少使用容器技术的企业对于他们基于容器的应用程序要么安全措施不足,要么安全措施过足。
尽量多地进行测试。容器技术是新技术,所以咱们须要搞清楚哪些是可以发挥做用,哪些是无用的,而要作到这一点的惟一方法就是进行安全性方面的测试,例如渗透测试。
容器安全性的发展趋势可能会与虚拟化安全性同样。虽然安全性从第一台虚拟机部署开始就是一个问题,可是多年以来积累下来的良好安全性实践、架构和工具都证实了其有效性。咱们相信,Docker容器安全性的问题也一样可以获得较好解决。