Docker底层原理(一)
1 Docker介绍
1.1 Docker是什么
Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2.0协议开源。
Docker 可以让开发者打包他们的应用及依赖包到一个轻量级、可移植的镜像中,然后发布到任何流行的 Linux 机器上。
1.2 Docker能解决什么问题
1.2.1高效有序利用计算资源
- 机器资源有限;
- 单台机器得部署多个应用;
- 应用之间互相隔离;
- 应用之间不能发生资源抢占,每个应用只能使用事先注册申请的资源。
1.2.2一次打包,到处运行
- 类似于java代码,应用及依赖的环境构建一次,可以到处运行。
2 Docker底层原理介绍
Linux的namespace和cgroup简单理解
namespace:类似于JAVA的命名空间
controll groups: controll (system resource) (for) (process)groups
2.1 Linux中的namespace
在Linux系统中,可以同时存在多用户多进程,那么对他们的运行协调管理,通过进程调度和进度管理可以解决,但是,整体资源是有限的,怎么把有限的资源(进程号、网络资源等等)合理分配给各个用户所在的进程?
Linux Namespaces 机制提供一种资源隔离方案。PID、IPC、Network等系统资源不再是全局性的,而是属于某个特定的Namespace。每个namespace下的资源对于其他namespace下的进程都是透明,不可见的。因此在操作系统层面上看,就会出现多个相同pid的进程。系统中可以同时存在两个进程号为0、1、2的进程,由于属于不同的namespace,所以它们之间并不冲突。而在用户层面上只能看到属于用户自己namespace下的资源,例如使用ps命令只能列出自己namespace下的进程。这样每个namespace看上去就像一个单独的Linux系统。
命名空间建立系统的不同视图,对于每一个命名空间,从用户看起来,应该像一台单独的Linux计算机一样,有自己的init进程(PID为0),其他进程的PID依次递增,A和B空间都有PID为0的init进程,子容器的进程映射到父容器的进程上,父容器可以知道每一个子容器的运行状态,而子容器与子容器之间是隔离的。
各类namespace的引入的内核版本、被隔离的全局系统资源、在容器语境下的隔离效果;
- Mount namespaces:Linux 2.4.19文件系统挂接点
将一个文件系统的顶层目录挂到另一个文件系统的子目录上,使它们成为一个整体,称为挂载。把该子目录称为挂载点。
Mount namespace用来隔离文件系统的挂载点, 使得不同的mount namespace拥有自己独立的挂载点信息,不同的namespace之间不会相互影响,这对于构建用户或者容器自己的文件系统目录非常有用。
- UTS namespaces:
Linux 2.6.19 nodename 和 domainname
UTS,UNIX Time-sharing System namespace提供了主机名和域名的隔离。能够使得子进程有独立的主机名和域名(hostname),这一特性在Docker容器技术中被用到,使得docker容器在网络上被视作一个独立的节点,而不仅仅是宿主机上的一个进程。
- IPC namespaces:
Linux 2.6.19特定的进程间通信资源,包括System V IPC 和 POSIX message queues
IPC全称 Inter-Process Communication,是Unix/Linux下进程间通信的一种方式,IPC有共享内存、信号量、消息队列等方法。所以,为了隔离,我们也需要把IPC给隔离开来,这样,只有在同一个Namespace下的进程才能相互通信。如果你熟悉IPC的原理的话,你会知道,IPC需要有一个全局的ID,即然是全局的,那么就意味着我们的Namespace需要对这个ID隔离,不能让别的Namespace的进程看到。
- PID namespaces:
Linux 2.6.24进程 ID 数字空间 (process ID number space)
PID namespaces用来隔离进程的ID空间,使得不同pid namespace里的进程ID可以重复且相互之间不影响。
PID namespace可以嵌套,也就是说有父子关系,在当前namespace里面创建的所有新的namespace都是当前namespace的子namespace。父namespace里面可以看到所有子孙后代namespace里的进程信息,而子namespace里看不到祖先或者兄弟namespace里的进程信息。
- Network namespaces:
始于Linux 2.6.24 完成于 Linux 2.6.29网络相关的系统资源每个容器用有其独立的网络设备,IP 地址,IP 路由表,/proc/net 目录,端口号等等。这也使得一个 host 上多个容器内的同一个应用都绑定到各自容器的 80 端口上。
- User namespaces:始于 Linux 2.6.23 完成于 Linux 3.8)用户和组 ID 空间
User namespace 用来隔离user权限相关的Linux资源,包括user IDs and group IDs。
这是目前实现的namespace中最复杂的一个,因为user和权限息息相关,而权限又事关容器的安全,所以稍有不慎,就会出安全问题。
在不同的user namespace中,同样一个用户的user ID 和group ID可以不一样,换句话说,一个用户可以在父user namespace中是普通用户,在子user namespace中是超级用户
2.2 linux cgroup介绍
- 有了namespace为什么还要cgroup:
Docker 容器使用 linux namespace 来隔离其运行环境,使得容器中的进程看起来就像一个独立环境中运行一样。但是,光有运行环境隔离还不够,因为这些进程还是可以不受限制地使用系统资源,比如网络、磁盘、CPU以及内存 等。关于其目的,一方面,是为了防止它占用了太多的资源而影响到其它进程;另一方面,在系统资源耗尽的时候,linux 内核会触发 OOM,这会让一些被杀掉的进程成了无辜的替死鬼。因此,为了让容器中的进程更加可控,Docker 使用 Linux cgroups 来限制容器中的进程允许使用的系统资源。
- 原理
Linux Cgroup 可为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。可以监控管理员配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置 cgroup。所以,可以将 controll groups 理解为 controller (system resource) (for) (process)groups,也就是是说它以一组进程为目标进行系统资源分配和控制。它主要提供了如下功能:
Resource limitation: 限制资源使用,比如内存使用上限以及文件系统的缓存限制。
Prioritization: 优先级控制,比如:CPU利用和磁盘IO吞吐。
Accounting: 一些审计或一些统计,主要目的是为了计费。
Controll: 挂起进程,恢复执行进程。
使用 cgroup,系统管理员可更具体地控制对系统资源的分配、优先顺序、拒绝、管理和监控。可更好地根据任务和用户分配硬件资源,提高总体效率。
在实践中,系统管理员一般会利用CGroup做下面这些事:
隔离一个进程集合(比如:nginx的所有进程),并限制他们所消费的资源,比如绑定CPU的核。
为这组进程分配其足够使用的内存
为这组进程分配相应的网络带宽和磁盘存储限制
限制访问某些设备(通过设置设备的白名单)
3 Docker与虚拟机的区别
3.1 虚拟机结构介绍
- 基础设施(Infrastructure)。它可以是你的个人电脑,数据中心的服务器,或者是云主机。
- 主操作系统(Host Operating System)。你的个人电脑之上,运行的可能是MacOS,Windows或者某个Linux发行版。
- 虚拟机管理系统(Hypervisor)。利用Hypervisor,可以在主操作系统之上运行多个不同的从操作系统。类型1的Hypervisor有支持MacOS的HyperKit,支持Windows的Hyper-V以及支持Linux的KVM。类型2的Hypervisor有VirtualBox和VMWare。
- 从操作系统(Guest Operating System)。假设你需要运行3个相互隔离的应用,则需要使用Hypervisor启动3个从操作系统,也就是3个虚拟机。这些虚拟机都非常大,也许有700MB,这就意味着它们将占用2.1GB的磁盘空间。更糟糕的是,它们还会消耗很多CPU和内存。
- 各种依赖。每一个从操作系统都需要安装许多依赖。如果你的的应用需要连接PostgreSQL的话,则需要安装libpq-dev;如果你使用Ruby的话,应该需要安装gems;如果使用其他编程语言,比如Python或者Node.js,都会需要安装对应的依赖库。
- 应用。安装依赖之后,就可以在各个从操作系统分别运行应用了,这样各个应用就是相互隔离的。
3.2 Docker结构介绍
- 基础设施(Infrastructure)。
- 主操作系统(Host Operating System)。所有主流的Linux发行版都可以运行Docker。对于MacOS和Windows,也有一些办法”运行”Docker。
- Docker守护进程(Docker Daemon)。Docker守护进程取代了Hypervisor,它是运行在操作系统之上的后台进程,负责管理Docker容器。
- 各种依赖。对于Docker,应用的所有依赖都打包在Docker镜像中,Docker容器是基于Docker镜像创建的。
- 应用的源代码与它的依赖都打包在Docker镜像中,不同的应用需要不同的Docker镜像。不同的应用运行在不同的Docker容器中,它们是相互隔离的。
Docker守护进程可以直接与主操作系统进行通信,为各个Docker容器分配资源;它还可以将容器与主操作系统隔离,并将各个容器互相隔离。
虚拟机启动需要数分钟,而Docker容器可以在数毫秒内启动。
由于没有臃肿的从操作系统,Docker可以节省大量的磁盘空间以及其他系统资源;
虚拟机更擅长于资源的完全隔离。