Docker架构源码解析

docker架构源码解析

docker简介

Docker 借助操作系统层的虚拟化实现资源的隔离,因此Docker 容器在运行时与虚拟
机(VM) 的运行有很大的区别, Docker 容器与宿主机共享同一个操作系统,不会有额外的
操作系统开销。这样的优势很明显,因而大大提高了资源利用率,并且提升了1/0 等方面
的性能。

Docker Daemon 作为Docker 架构中的主体部分,首先具备服务端的功能,有能力接收
Docker Client 发起的请求;其次具备Docker Client 请求的处理能力。Docker Daemon 内部所
有的任务均由Engine 来完成,且每一项工作都以一个Job 的形式存在。

Docker 无疑是DevOps 大潮中最具实践价值的不二法宝。Docker 从Linux 内核的角
度出发,属于轻量级虚拟化技术,有能力秒级提供应用隔离环境,完成云计算时代分布式
应用的第一需求”隔离

Docker 通过DockerDaemon 的管理, libcontainer 的执行,最终创建Docker 容器

docker架构

  • docker daemon架构图

  • docker server架构图


在Docker Daemon 的启动过程中, DockerServer 第一个完成。Docker Server 通过包
gorilla/mux ,创建了一个mux.Router 路由器,提供请求的路由功能。在Go 语言中, gorilla/
mux 是一个强大的URL 路由器以及调度分发器。创建路由器之后, Docker Se凹er 为mux.
Router 中添加有效的路由项,每一个路由项由HTTP 请求方法(PUT , POST 、GET 或
DELETE)、URL 和Handler 三部分组成。

  • Graph

Graph 在Docker 架构中扮演的角色是容器镜像的保管者

  • Docker Container
    • 支持配置类型:
      • 通过指定容器镜像,使得Docker 容器可以自定义rootfs 等文件系统。
      • 通过指定物理资源的配额,如CPU、内存等,使得Docker 容器使用受限的物理资源。
      • 通过配置容器网络及其安全策略,使得Docker 容器拥有独立且安全、的网络环境。
      • 通过指定容器的运行命令,使得Docker 容器执行指定的任务。

  • docker pull
    docker pull命令的作用是: Docker Daemon 从Docker Registry 下载指定的容器镜像,并将镜像存储在本地的Graph 中,以备后续创建Docker 容器时使用

  • docker run

  • docker run流程:
    • Docker Client 处理用户发起的docker run 命令,解析完请求与参数之后,向Docker Server 发送一个HTTP 请求, HTTP 请求方法为POST ,请求URL 为”/containers/create?”+”xxx” ,实际意义为创建一个容器对象,即Docker Daemon 程序逻辑中的容器对象,并非实际运行的容器。
  • Docker Server 接收以上HTTP 请求,并交给mux.Router , mux.Router 通过URL 以及请求方法来确定执行该请求的具体handler。
  • mux.Router 将请求路由分发至相应的handler ,具体为PostContainersCreate 。
  • 在PostContainersCreate 这个handler 之中,创建并初始化一个名为”create” 的Job ,之后触发执行该Job 。
  • 名为”create” 的Job 在运行过程中执行Container.Create 操作,该操作需要获取容器镜像来为Docker 容器准备rootfs ,通过graphdriver 完成。
  • graphdriver 从Graph 中获取创建Docker 容器rootfs 所需要的所有镜像。
  • graphdriver 将rootfs 的所有镜像通过某种联合文件系统的方式加载至Docker 容器指定的文件目录下。
  • 若以上操作全部正常执行,没有返回错误或异常,则Docker Client 收到DockerServer 返回状态之后,发起第二次HTTP 请求。请求方法为”POST” ,请求URL 为”/containers/“+container ID+”/start” ,实际意义为启动时才创建完毕的容器对象,实现物理容器的真正运行。
  • Docker Server 接收以上HTTP 请求,并交给mux.Router , mux.Router 通过URL 以及请求方法来确定执行该请求的具体handler。
  • mux.Router 将请求路由分发至相应的handler ,具体为PostContainersStart 。
  • 在PostContainersStart 这个handler 之中,创建并初始化名为”start” 的Job ,之后触发执行该Job 。
  • 名为”start” 的Job 执行需要完成一系列与Docker 容器相关的配置工作,其中之一是为Docker 容器网络环境分配网络资源,如IP 资源等,通过调用networkdriver 完成。
  • networkdriver 为指定的Docker 容器分配网络资源,其中有IP 、po此等,另外为容器设置防火墙规则。
  • 返回名为”start” 的Job ,执行完一些辅助性操作后, Job 开始执行用户指令,调用execdriver。
  • execdriver 被调用,开始初始化Docker 容器内部的运行环境,如命名空间、资源控制与隔离,以及用户命令的执行,相应的操作转交至libcontainer 来完成。
  • libcontainer 被调用完成Docker 容器内部的运行环境初始化,并最终执行用户要求启动的命令。

libcontainer

cgroup进程隔离步骤

  • 父进程通过fork 创建子进程时,使用namespaces 技术,实现子进程与父进程以及其他进程之间命名空间的隔离。

  • 子进程创建完毕之后,使用cgroups 技术来处理进程,实现进程的资源限制。

  • namespaces 和cgroups 这两种技术都用上之后,进程所处的”隔离”环境才真正建立,此时”容器”真正诞生!

  • 现有进程,后有容器

  • 使用Docker 创建一个进程,为这个进程创建隔离的环境,这样的环境可以称为Docker 容器

  • 与namespaces 不同的是, cgroup 的使用并不是在创建容器内进程时完成,而是在创建容器内进程之后完成

  • cgroups 的运用必须要等到容器内第一个进程被真正创建出来之后才能实现

namespaces

docker网络

Docker 容器共有以下4 种网络模式: bridge 桥接模式、host模式、other container 模式和none 模式

docker网络 bridge

bridge 桥接模式的实现步骤如下

Virtual Ethernet Pair简称veth pair,是一个成对的端口,所有从这对端口一 端进入的数据包都将从另一端出来,反之也是一样.

  • 利用veth pair 技术,在宿主机上创建两个虚拟网络接口,假设为veth0 和veth1。而veth pair 技术的特性可以保证无论哪一个veth 接收到网络报文,都会将报文传输给另一方。
  • Docker Daemon 将veth0 附加到Docker Daemon 创建的docker0 网桥上。保证宿主机的网络报文有能力发往veth0
  • Docker Daemon 将veth1 添加到Docker 容器所属的网络命名空间( namespaces) 下,veth1在Docker 容器看来就是eth0。一方面,保证宿主机的网络报文若发往veth0 ,可以立即被veth1 收到,实现宿主机到Docker 容器之间网络的联通性;另一方面,保证Docker 容器单独使用veth1 ,实现容器之间以及容器与宿主机之间网络环境的隔离性。

  • bridge 桥接模式,从原理上实现了Docker 容器到宿主机乃至其他机器的网络联通性。然而,由于宿主机的IP 地址与veth pair 的IP 地址不属于同一个网段,故仅仅依靠veth pair和网络命名空间的技术,还不足以使宿主机以外的网络主动发现Docker 容器的存在。为使Docker 容器有能力让宿主机以外的世界感受到容器内部暴露的服务, Docker 采用NAT(Network Address Translation ,网络地址转换)的方式让宿主机以外的世界可以将网络报文发送至容器内部