目录

  • 1.虚拟化技术
  • 2.Linux 容器
  • 2.1 cgroups控制组
  • 2.2 Namespace命名空间
  • 3.docker
  • 3.1 镜像、容器、仓库
  • 镜像
  • 容器
  • 仓库
  • 3.2 docker工作方式
  • 3.3 docker容器编排
  • 3.4 docker架构
  • 3.4.1 docker各模块
  • docker client
  • docker daemon
  • docker registry
  • graph
  • Driver
  • libcontainer
  • docker container
  • 3.4.2 docker工作机制
  • 3.5 OCI与OCF
  • OCI
  • OCF

1.虚拟化技术

虚拟化分为下面两类:

  1. 主机级虚拟化(全虚拟化,半虚拟化)
  2. 容器级虚拟化(Linux Containers,缩写为 LXC)

Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离.

上图比较了 Docker 和传统虚拟化方式的不同之处。传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。

2.Linux 容器

  • Linux容器功能是基于 cgroups 和 Namespace 来实现的.

LXC是最早一批真正把完整的容器技术用一组简易使用的工具和模板来极大的简化了容器技术使用的一个方案,它是一种操作系统层虚拟化技术。所以从一定程度上来说,docker就是LXC的增强版。

2.1 cgroups控制组

cgroups 是将任意进程进行分组化管理的Linux内核功能.通过cgroups可以有效的隔离各类进程, 同时还可以控制进程的资源占用(CPU, 内存等等)情况.

  • CGroups能够限制的资源有:
blkio:块设备IO
cpu:CPU
cpuacct:CPU资源使用报告
cpuset:多处理器平台上的CPU集合
devices:设备访问
freezer:挂起或恢复任务
memory:内存用量及报告
perf_event:对cgroup中的任务进行统一性能测试
net_cls:cgroup中的任务创建的数据报文的类别标识符
  • CGroups提供如下具体功能
1.资源限制(Resource Limitting)组可以设置为不超过设定的内存限制。比如:内存子系统可以为进行组设定一个内存使用上限,一旦进程组使用的内存达到限额再申请内存,就会发出Out of Memory警告
2.优先级(Prioritization)通过优先级让一些组优先得到更多的CPU等资源
3.资源审计(Accounting)用来统计系统实际上把多少资源用到合适的目的上,可以使用cpuacct子系统记录某个进程组使用的CPU时间
4.隔离(Isolation)为组隔离命名空间,这样一个组不会看到另一个组的进程、网络连接和文件系统
5.控制(Control)挂起、恢复和重启等操作
  • 对于docker,可以在/sys/fs/cgroup/memory/docker/目录下看到对Docker组应用的各种限制项

2.2 Namespace命名空间

使用Namespace, 可以让每个进程组有独立的PID, IPC和网络空间.
命名空间是Linux内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。

namespaces

系统调用参数

隔离内容

内核版本

UTS

CLONE_NEWUTS

主机名和域名

2.6.19

IPC

CLONE_NEWIPC

信号量、消息队列和共享内存

2.6.19

PID

CLONE_NEWPID

进程编号

2.6.24

Network

CLONE_NEWNET

网络设备、网络栈、端口等

2.6.29

Mount

CLONE_NEWNS

挂载点(文件系统)

2.4.19

User

CLONE_NEWUSER

用户和用户组

3.8

  • PID

不同用户的进程就是通过pid命名空间隔离开的,且不同命名空间中可以有相同 pid。

  • NETWORK

有了pid命名空间,每个命名空间中的pid能够相互隔离,但是网络端口还是共享 host 的端口。网络隔离是通过 net 命名空间实现的, 每个 net 命名空间有独立的网络设备,IP 地址,路由表,/proc/net 目录。这样每个容器的网络就能隔离开来。

  • IPC

进程间交互方法(interprocess communication-IPC),包括信号量、消息队列和共享内存等。

与VM不同的是,容器的进程间交互实际上还是 host 上具有相同 pid 命名空间中的进程间交互,因此需要在 IPC 资源申请时加入命名空间信息,每个 IPC 资源有一个唯一的 32 位 id。

  • MOUNT

类似 chroot,将一个进程放到一个特定的目录执行。mnt 命名空间允许不同命名空间的进程看到的文件结构不同,这样每个命名空间 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个命名空间中的容器在 /proc/mounts 的信息只包含所在命名空间的挂载点。

  • UTS

UTS("UNIX Time-sharing System") 命名空间允许每个容器拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 主机上的一个进程。

  • USER

每个容器可以有不同的用户和组 id, 也就是说可以在容器内用容器内部的用户执行程序而非主机上的用户。

3.docker

docker是容器技术的一个前端工具,容器是内核的一项技术,docker只是把这一项技术的使用得以简化,使之普及而已。

docker早期的版本其核心就是一个LXC,docker对其进行了二次封装,功能的实现是通过LXC做容器管理引擎,但是在创建容器时,不再是像LXC一样用模板去现场安装,而是事先通过一种类似镜像技术,就像在KVM中一样,将一个操作系统打包成一个镜像,然后将这个镜像拷贝到目标主机上直接部署启动。

自docker 0.9版本起,docker除了继续支持LXC外,还开始引入自家的libcontainer,试图打造更通用的底层容器虚拟化库。如今的docker基本上都已经是使用libcontainer而非LXC了。

从操作系统功能上看,docker底层依赖的核心技术主要包括Linux操作系统的命名空间、控制组、联合文件系统和Linux虚拟网络支持。

3.1 镜像、容器、仓库

镜像

操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

  • 分层存储

Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现是由一组文件系统组成,或者说,由多层文件系统联合组成.

容器

镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,容器进程运行于属于自己的独立的命名空间。

  • 容器存储层

镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为容器存储层。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。

容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 数据卷(Volume)、或者 绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。

仓库

  • Docker Registry

如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry.

一个 Docker Registry中可以包含多个仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。

通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

  • 公开服务

Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。

最常使用的 Registry 公开服务是官方的 Docker Hub,这也是默认的 Registry,并拥有大量的高质量的官方镜像。除此以外,还有 Red Hat 的 Quay.io;Google 的 Google Container Registry,Kubernetes 的镜像使用的就是这个服务。国内的一些云服务商提供了针对 Docker Hub 的镜像服务(Registry Mirror),这些镜像服务被称为 加速器。常见的有 阿里云加速器、DaoCloud 加速器 等。

  • 私有服务

用户还可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。

开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。

除了官方的 Docker Registry 外,还有第三方软件实现了 Docker Registry API,甚至提供了用户界面以及一些高级功能。比如,Harbor 和 Sonatype Nexus。

3.2 docker工作方式

为了使容器的使用更加易于管理,docker采取一个用户空间只跑一个业务进程的方式,在一个容器内只运行一个进程,比如我们要在一台主机上安装一个nginx和一个tomcat,那么nginx就运行在nginx的容器中,tomcat运行在tomcat的容器中,二者用容器间的通信逻辑来进行通信。

3.3 docker容器编排

在搭建一个有依赖关系,次序关系的架构时,docker本身并不会提供处理这些依赖或者次序的功能.

我们需要一个在docker的基础上,能够把这种应用程序之间的依赖关系、从属关系、隶属关系等等反映在启动、关闭时的次序和管理逻辑中,这种功能被称为容器编排。

  • 常见的编排工具
1.machine+swarm(把N个docker主机当一个主机来管理)+compose(单机编排)
2.mesos(实现统一资源调度和分配)+marathon
3.kubernetes --> k8s

3.4 docker架构

3.4.1 docker各模块

docker client

  1. 以docker+参数的命令形式来实现一个完整的请求。
  2. Docker Client 可以通过以下三种方式和Docker Daemon建立通信 tcp://host:port、unix://path_to_socket 和 fd://socketfd

docker daemon

  • docker server
  1. 功能为接受并调度分发 Docker Client 发送的请求。接受请求后,Docker Server 通过路由与分发调度,找到相应的 Handler 来执行请求。
  2. gorilla/mux 是一个强大的 URL 路由器以及调度分发器。Docker 的启动过程中,通过包 gorilla/mux 创建了一个 mux.Router 来提供请求的路由功能。mux.Router 中添加了众多的路由项,每一个路由项由 HTTP 请求方法(PUT、POST、GET 或DELETE)、URL、Handler 三部分组成。
  • docker engine
  1. Docker Engine 是 Docker 架构中的运行引擎,它通过执行Job的方式来操纵管理这些容器。
  2. Docker Engine 在实现过程中,有一个Handler对象。该Handler对象存储的都是关于众多特定 Job 的 Handler 处理访问。(Docker Engine 的Handler 对象中有一项为:{“create”: daemon.ContainerCreate,},则说明当名为"create" 的 Job 在运行时,执行的是 daemon.ContainerCreate 的 Handler。)
  3. Job可以认为是 Docker 架构中 Docker Engine 内部最基本的工作执行单元,类似于进程。(在容器内部运行一个进程,这是一个 Job;创建一个新的容器,这是一个 Job。 Docker Server 的运行过程也是一个 Job,名为 ServeApi。)

docker registry

  1. Docker Registry 是一个存储容器镜像的仓库,以[repository]:[tag]来精确定义一个具体的 Image。
  2. Docker Daemon 与 Docker Registry 通信,实现search,push,pull镜像的job。

graph

  • graph为docker内部数据库
  • Repository

存储下载的镜像和通过 Dockerfile 构建的镜像。镜像的存储类型有 Aufs、Devicemapper、Btrfs、Vfs等。其中 CentOS 系统 7.x 以下版本使用 Devicemapper 的存储类型。

  • graphDB:

构建在 SQLite 之上的小型数据库,记录已下载容器镜像之间的关系

Driver

  • Graph 负责镜像的存储,Driver负责容器的执行。
  • Graphdriver

主要用于完成容器镜像的管理,包括存储与获取。

  • Networkdriver:

用途是完成Docker容器网络环境的配置(创建网卡,分配IP与端口及与宿主机的端口映射等)

  • Execdriver

作为Docker容器的执行驱动,负责创建容器运行命名空间、容器资源使用的统计与限制、容器内部进程的真正运行

libcontainer

  1. Docker可以直接调用Libcontainer库来操纵容器的Namespace、Cgroups、Apparmor、网络设备以及防火墙规则等
  2. Libcontainer 提供了一整套标准的接口来满足上层对容器管理的需求。可以不依靠任何依赖,直接访问内核中与容器相关的 API。

docker container

Docker架构中服务交付的最终体现形式,即容器。Docker可以按照用户的需求与指令,订制相应的 Docker 容器。

3.4.2 docker工作机制

  • Docker 是一个 C/S 模式的架构
1.用户是使用 Docker Client 与 Docker Daemon 建立通信,并发送请求给后者。
2.Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server的功能使其可以接受 Docker Client 的请求。
3.Docker Engine 执行 Docker 内部的一系列工作,每一项工作都是以一个 Job 的形式的存在。
4.Job 的运行过程中,当需要容器镜像时,则从Docker Registry中下载镜像,并通过镜像管理驱动 Graphdriver 将下载镜像以 Graph 的形式存储。
5.当需要为 Docker 创建网络环境时,通过网络管理驱动Networkdriver创建并配置Docker容器网络环境。
6.当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Execdriver 来完成。
7.Libcontainer 是一项独立的容器管理包,Networkdriver 以及 Execdriver 都是通过 Libcontainer 来实现具体对容器进行的操作。

3.5 OCI与OCF

  • docker容器演变:lxc --> libcontainer --> runC
  • runC的前身是docker的libcontainer项目,在libcontainer的基础上做了封装,

OCI

  • Open Container-initiative 组织

由Linux基金会主导于2015年6月创立,旨在围绕容器格式和运行时制定一个开放的工业化标准OCF.
目前主要有两个标准:容器运行时标准(runtime-spec)和容器镜像标准(image-spec)

  • 容器镜像标准(image-spec)
  1. 文件系统:以层级形式保存的文件系统,每层保存了和上层之间变化的部分。
  2. config文件: 保存了文件系统的层级信息(每层的hash值,以及历史信息)以及容器运行时需要的一些信息(比如环境变量、工作目录、命令参数、mount 列表)。
  3. mainfest文件:镜像的config文件索引,有哪些层,额外的注释信息,manifest文件中保存了很多和当前平台有关的信息
  4. index文件: 可选的文件,指向不同平台的manifest文件,这个文件能保证一个镜像可以跨平台使用,每个平台拥有不同的manifest文件,使用index作为索引
  • 容器运行时标准(runtime-spec)
  1. OCI版本
  2. id: 容器ID
  3. status: 容器运行时状态(creating,created,running,stopped)
  4. pid: 容器进程在宿主机的进程ID
  5. bundle: 容器文件目录,存放容器rootfs及相应配置(JSON格式)的目录
  6. annotations: 与容器相关的注释

OCF

  • Open Container Format
  • runC
  1. Runc是一个CLI工具,用于根据OCF规范生成和运行容器.runc是libcontainer的封装,而libcontainer是对操作系统关于容器方面的一些接口。