Docker容器的原理与实践 (下)

欢迎访问网易云社区,了解更多网易技术产品运营经验。html


Docker原理分析

Docker架构



镜像原理

镜像是一个只读的容器模板,含有启动docker容器所需的文件系统结构及内容
Docker以镜像和在镜像基础上构建的容器为基础,以容器开发、测试、发布的单元将应用相关的全部组件和环境进行封装,避免了应用在不一样平台间迁移所带来的依赖问题,确保了应用在生产环境的各阶段达到高度一致的实际效果。linux

主要特色

  • 分层
    镜像采用分层构建,每一个镜像由一系列的镜像层组成, 当须要修改容器内的某个文件时,只对处于最上方的读写层进行变更,不覆盖下面已有文件系统的内容。 当提交这个修改过的容器文件系统为一个新的镜像时,保存的内容仅为最上层读写文件系统中被更新过的文件。 docker


+ bootfs  主要包含bootloader和kernel, bootloader主要是引导加载kernel, 当容器启动成功后,kernel被加载到内存中后而引导文件系统则会被卸载unmount+ rootfs  是容器在启动时内部进程可见的文件系统,一般包含一个操做系统运行所需的文件系统
    + 传统linux在内核启动时首先会挂载一个只读的rootfs,检测器完整性以后再切换为读写模式
    + docker在挂载rootfs时也将其设为只读模式,挂载完毕后利用联合挂载技术在已有的只读rootfs上再挂载一个读写层。
    + 只有运行中文件系统发生变化,才会把变化的内容写到读写层,并隐藏只读层中的老版本文件
    + rootfs包含的就是典型Linux系统中的 /dev,/proc,/bin, /etc等标准目录和文件。复制代码
  • 写时复制json

    • 能够在多个容器之间共享镜像,每一个容器启动时不须要单独复制一份镜像文件api

    • 将全部镜像层以只读方式挂载到一个挂载点,在上面覆盖一个可读写的容器层。安全

    • 写时复制配合分层机制减小了镜像对磁盘空间的占用和容器启动时间bash

  • 内容寻址网络

    • 根据内容来索引镜像和镜像层架构

    • 是对镜像层的内容计算检验和,生成一个内容哈希值做为镜像层的惟一标识app

    • 对于来自不一样构建的镜像层,只要拥有相同的内容哈希,也能被不一样镜像共享

  • 联合挂载
    能够在一个挂载点挂载多个文件系统,将挂载点的原目录与被挂在内容进行整合,最终可见的文件系统将包含整合后各层的文件和目录

    • 读写层处于容器文件系统的最顶层,其下可能联合挂载多个只读层。


存储管理

为了适应不一样平台不一样场景的存储需求,Docker提供了各类基于不一样文件系统实现的存储驱动来管理实际的镜像文件


元数据管理

镜像在设计上将元数据和文件存储彻底隔离。Docker管理元数据采用的也正是从上至下repository、image、layer是3个层次。 因此repository与image两个元数据并没有物理上的镜像文件与之对应,layer则存在物理上的镜像文件与之对应。


  • 仓库元数据
    文件中存储了全部版本镜像的名字和tag以及对应的镜像ID(image/aufs)

  • 镜像元数据
    文件中存储了镜像架构、操做系统、默认配置、该镜像的容器ID和配置,构建镜像的历史信息以及rootfs组成(image/aufs/imagedb/content/sha256)

  • 分层元数据

    • 镜像层
      描述不可改变的镜像层(image/aufs/layerdb/sha256)

    • 容器层
      描述可读写的容器层(image/aufs/layerdb/mounts/),读写层的ID也对应容器的ID


存储驱动

为支持写时复制特性,根据不一样操做系统底层的支持提供不一样的存储驱动

  • aufs(advanced multi layered unification file system)
    是一种支持联合挂载的文件系统,支持不一样目录挂载到同一个目录,挂载对用户来讲是透明的。

    • 从最顶层的读写层开始向下寻找,本层没有则根据层与层之间的关系到下一层找

    • 若是文件不存在则在读写层新建一个,不然向上面同样从顶层开始查找,找到后复制到读写层进行修改

    • 若是文件仅仅在读写层则直接删除;不然须要删除读写层的备份,再在读写层中建立whiteout文件来标志这个文件不存在,而不会真正删除底层文件(会有.wh.开头的隐藏文件)

    • 若是这个文件在读写层存在对应的whiteout文件则先将whiteout文件删除再新建

    • 文件操做

  • btrfs

  • zfs

  • devicemapper

  • overlay
    overlayFS是一种新型联合文件系统,容许一个文件系统与另一个文件系统重叠,在上层文件系统中记录更改,而下层的文件系统保持不变。

  • vfs

    存储目录

/var/lib/docker  
               /aufs                                aufs驱动工做的目录
                    /diff                           mount-id容器层,文件系统全部层的存储目录(下载的镜像内容就保存在这里)和容器层文件的差别变化会在这里出现。
                         /mount-id-init             最后一个只读层,用于挂载并从新生成dev/etc所列文件,这些文件与容器内的环境息息相关,但不适合被打包做为镜像的文件内容,
                                                    又不该该直接修改在宿主机文件上,因此设计了mountID-init层单独处理这些文件。这一层只在容器启动时添加。
                    /layers                         mount-id存储上述全部aufs层之间的关系等元数据,记录该层所依赖的全部其它层
                    /mnt                            aufs文件系统的挂载点,graphdriver会将diff中属于容器镜像的全部层目录以只读方式挂到mnt 
               /container                           容器配置文件目录
               /image 
                     /aufs                          存储镜像与镜像层元数据信息,真正的镜像层内容保存在aufs/diff
                          /imagedb/content          存储全部镜像的元数据
                          /layerdb                  存储全部镜像层和容器层的元数据
                                  /mounts           存储容器层元数据
                                         /init-id   init层id,这个layer存放的位置
                                         /mount-id  mount层id,这个layer存放的位置
                                         /parent    父layer的chain-id
                                  /share256         存储镜像层元数据
                                         /cache-id  该层数据存放的位置,在对应驱动目录下
                                         /diff      标识每个layer
                                         /parent    标识父layer的chain-id
                                         /size      存放layer的数据大小
                                         /tar-split.json.gz 存放layer层的json信息
                          /repositories.json        记录镜像仓库中全部镜像的repository和tag名
               /volumes                             volumes的工做目录,存放全部volume数据和元数据

读写层、volumes、init-layer、只读层这几部分结构共同组成了一个容器所需的文件系统。
diff-id:经过docker pull下载镜像时,镜像的json文件中每个layer都有一个惟一的diff-idchain-id:chain-id是根据parent的chain-id和自身的diff-id生成的,假如没有parent,则chain-id等于diff-id,假若有parent,则chain-id等于sha256sum( “parent-chain-id diff-id”)
cache-id:随机生成的64个16进制数。cache-id标识了这个layer的数据具体存放位置复制代码

数据卷

volume是存在于一个或多个容器中的特定文件或文件夹,这个目录以独立联合文件系统的形式存在于宿主机中


  • 特色

    • 容器建立时就会初始化,在容器运行时就可使用其中的文件

    • 能在不一样的容器中共享和重用

    • 对volume中的数据操做不会影响到镜像自己

    • 生命周期独立,即便删除容器volume依然存在

Namespace

同一个namespace下的进程能够感知彼此的变化,而对外界进程一无所知


  • UTS 隔离主机名与域名

  • IPC 隔离信号量、消息队列和共享内存

  • PID 隔离进程编号

    • 不一样的PID namespaces会造成一个层级体系

    • 每一个pid namespace的第一个进程 pid 1会像传统linux的init进程号同样拥有特权

  • Net 隔离网络设备、网络栈、端口

  • Mount 隔离挂载点(文件系统)

  • User 隔离用户和用户组


例子


mkdir newroot
cd newroot
cp -r /bin/ binchroot newrootexit复制代码


Cgroup

根据需求把一系列的系统任务和子任务整合到按资源划分等级的不一样组内,从而为系统资源管理提供一个统计的框架


主要做用

  • 资源限制

  • 优先级分配

  • 资源统计

  • 任务控制


主要特色

  • cgroup的api是以一个为文件系统的方式实现,用户态的程序能够经过文件操做实现cgroup的组织管理

  • 组织管理操做单元能够细粒度到线程级别,能够建立和销毁cgroup从而实现资源再分配和管理

  • 全部资源管理的功能都以子系统的方式实现,接口统一

  • 子任务建立之初与父任务处于同一个cgroups控制组


相关术语

task

表示系统的一个进程或线程


cgroup

按某种资源控制标准划分而成的任务组,包含一个或多个子系统

  • 实现形式表现为一个文件系统mount -t cgroup

  • docker实现

    • 会在单独挂载了每个子系统的控制组目录下建立一个名为docker的控制组

    • 在docker控制组里面再为每一个容器建立一个容器id为名称的容器控制组

    • 容器里的全部进程号都会写到该控制组tasks中,并在控制组文件cpu.cfs_quota_us中写入预设的限制参数值

  • 工做原理

    • 本质上来讲,cgroups是内核附加在程序上的一系列钩子,经过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的

    • 进程所需内存超过它所属cgroup最大限制之后,若是设置了oom control,进程会收到oom信号并结束,不然进程会挂起,进入睡眠状态,直到cgroup中的其余进程释放了足够的内存资源为止


subsystem

资源调度器,cpu子系统能够控制cpu分配时间(/sys/fs/cgroup/cpu/docker/)


  • blkio 能够为块设备设定输入、输出限制,好比物理驱动设备(磁盘、固态硬盘、USB)

  • cpu 使用调度程序控制任务对CPU的使用

  • cpuacct 自动生成cgroup中任务对cpu资源使用状况的报告

  • cpuset 为cgroup中的任务分配独立的cpu和内存

  • devices 能够开启或关闭cgroup中任务对设备的访问

  • freezer 能够挂起或恢复cgroup中的任务

  • memory 能够设定cgroup中任务对内存使用量的限定

  • perf_event 使用后使cgroup中的任务能够进行统一的性能测试

  • net_cls 经过使用等级识别符标记网络数据包,从而容许linux流量控制程序(traffic controller)识别从具体cgroup中生成的数据包


hierachy

层级是由一系列cgroup以一个树状结构排列而成,每一个层级经过绑定对应的子系统进行资源控制

依赖其余的内核能力

  • seccomp(secure computing mode)
    安全计算模式,这个模式能够设置容器在对系统进行调用时进行一些筛选,也就是所谓的白名单。 是一种简洁的sandboxing机制。能使一个进程进入到一种“安全”运行模式,该模式下的进程只能调用4种系统调用(system calls),即read(), write(), exit()和sigreturn(),不然进程便会被终止。

  • SELinux
    安全加强式Linux(SELinux, Security-Enhanced Linux)是一种强制访问控制(mandatory access control)的实现

  • Netlink
    用来让不一样的容器之间进行通讯,可用于进程间通讯,Linux内核与用户空间的进程间、用户进程间的通信

  • Netfilter
    Linux内核中的一个软件框架,用于管理网络数据包。
    不只具备网络地址转换(NAT)的功能,也具有数据包内容修改、以及数据包过滤等防火墙功能。

  • AppArmor
    相似于selinux,主要的做用是设置某个可执行程序的访问控制权限,能够限制程序 读/写某个目录/文件,打开/读/写网络端口等等

  • capability
    Linux把原来和超级用户相关的高级权限划分红为不一样的单元,称为Capability,能够单独启用或者关闭, 它打破了UNIX/LINUX操做系统中超级用户/普通用户的概念,由普通用户也能够作只有超级用户能够完成的工做


网络原理

一些概念

  • net namespace
    隔离网络栈,有本身的接口、路由、防火墙规则

  • bridge
    至关于交换机,为链接在其上的设备转发数据帧

  • veth
    至关于交换机上的端口,是一对虚拟网卡,用于不一样网络空间进行通讯的方式,从一张veth网卡发出的数据包能够直接到达它的peer veth

  • gateway
    就是一个网络链接到另外一个网络的“关口”,与本地网络链接的机器会把向外的流量传递到此地址中从而使那个地址成为本地子网之外的IP地址的"网关".

  • iptables
    linux内核的包过滤系统。在三、4层提供规则链对包进行标记、假装、转发等


两个例子

经过虚拟网卡实现2个namespace通讯

一、建立虚拟网络环境
ip netns add net0
ip netns add net十二、在虚拟的net0环境中执行
ip netns exec net0 ifconfig -aip netns exec net0 ping localhost
ip netns exec net0 ip link set lo up
ip netns exec net0 ping localhost三、建立一对虚拟网卡
ip link add type veth四、把veth0移动到net0环境里面,把veth1移动到net1环境里面
ip link set veth0 netns net0
ip link set veth1 netns net1四、配置虚拟网卡
ip netns exec net0 ip link set veth0 up
ip netns exec net0 ip address add 10.0.1.1/24 dev veth0
ip netns exec net1 ip link set veth1 up
ip netns exec net1 ip address add 10.0.1.2/24 dev veth1五、测试
ip netns exec net0 ping -c 3 10.0.1.2ip netns exec net0 ping -c 3 10.0.1.1ip netns exec net1 ping -c 3 10.0.1.1复制代码

经过网桥实现多个namespace通讯及外网访问原理

一、建立虚拟网络环境并链接网线
ip netns add net0
ip netns add net1
ip netns add bridge

ip link add type veth
ip link set dev veth0 name net0-bridge netns net0
ip link set dev veth1 name bridge-net0 netns bridge

ip link add type veth
ip link set dev veth0 name net1-bridge netns net1
ip link set dev veth1 name bridge-net1 netns bridge二、在bridge中建立虚拟网桥
ip netns exec bridge brctl addbr br
ip netns exec bridge ip link set dev br up
ip netns exec bridge ip link set dev bridge-net0 up
ip netns exec bridge ip link set dev bridge-net1 up
ip netns exec bridge brctl addif br bridge-net0
ip netns exec bridge brctl addif br bridge-net1
ip netns exec bridge brctl show三、配置虚拟环境网卡
ip netns exec net0 ip link set dev net0-bridge up
ip netns exec net0 ip address add 10.0.1.1/24 dev net0-bridge

ip netns exec net1 ip link set dev net1-bridge up
ip netns exec net1 ip address add 10.0.1.2/24 dev net1-bridge四、测试
ip netns exec net0 ping -c 3 10.0.1.2ip netns exec net1 ping -c 3 10.0.1.1ip netns exec bridge ping -c 3 10.0.1.2五、须要给当前网络环境配置一个网卡ip否则没有网络路由网络不可达
ip netns exec bridge ip address add 10.0.1.3/24 dev br六、想要ping宿主机网络怎么办?
ip netns exec bridge ping 192.168.99.100ip link add A type veth peer name B
ip link set B netns bridge
ip netns exec bridge ip link set dev B up

ip link set A up六、给AB网卡配置ip
ip netns exec bridge ip address add 172.22.0.11/24 dev B
ip address add 172.22.0.10/24 dev A七、设置bridge默认网关
ip netns exec bridge route add default gw 172.22.0.10ip netns exec bridge ping 192.168.99.100八、设置net0默认网关
ip netns exec net0 ping 192.168.99.100ip netns exec net0 route add default gw 10.0.1.3ip netns exec net0 ping 192.168.99.100 不通九、地址假装
ip netns exec bridge iptables -t nat -A POSTROUTING -o B -j MASQUERADE
ip netns exec net0 ping 192.168.99.100十、让虚拟网卡访问外网
ip netns exec net0 ping 8.8.8.8iptables -t filter -I FORWARD -o A -j ACCEPT
iptables -t filter -I FORWARD -i A -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -s 172.22.0.0/24 -j MASQUERADE十一、vbox访问mac地址
ip netns exec bridge ping 192.168.99.1iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
ip netns exec bridge ping 192.168.99.1复制代码

影响网络连通的几个要素


  • 内核是否开启IP转发支持
    cat /proc/sys/net/ipv4/ip_forward
    sysctl -w net.ipv4.ip_forward=1

  • 防火墙转发规则是否打开

  • 是否开启IP地址假装

  • 是否正确设置网关

CNM网络模型

  • 沙盒,一个沙盒包含了一个容器网络栈信息。沙盒能够对容器的接口、路由和DNS设置等进行管理。一个沙盒能够有多个端点和多个网络。

  • 端点,一个端点能够加入一个沙盒和一个网络。端点的实现可使veth pair、open vSwitch内部端口。

  • 网络,一个网络是一组能够互相联通的端点。网络的实现能够是linux bridge、VLAN等。

内置驱动




  • bridge
    默认设置,libnetwork将建立出来的容器链接到docker网桥。其与外界使用NAT.

    操做例子

    docker network create br3
      docker run -itd --name busybox-bridge --net=br3 busybox
    
      docker run -itd --name busybox-bridge-none busybox
      docker network connect br3 busybox-bridge-none
      查看容器网络和宿主机网桥及其上端口复制代码
  • host
    libnetwork不为docker容器建立网络协议栈及独立的network namespace。

    • 容器使用宿主机的网卡、IP、端口、路由、iptable规则

    • host驱动很好的解决了容器与外界通讯的地址转换问题,但也下降了容器与容器建、容器与宿主机之间的网络隔离性,引发网络资源竞争的冲突。

      例子

      docker run -itd --name busybox-host --net=host busybox复制代码
  • container

    • 指定新建立的容器和已存在的容器共享一个网络空间

    • 两个容器的进程能够经过lo网卡设备通讯

      例子

      docker run -itd --name busybox busyboxdocker run -itd --name busybox-container --net=container:busybox-bridge busybox复制代码
  • none
    容器拥有本身的network namespace,但不进行任何网络配置。

    例子


docker run -itd --name busybox-none --net=none busybox复制代码
  • overlay

    • 使用标准的VXLAN

    • 使用过程当中须要一个额外的配置存储服务如consul、etcd、Zookeeper

    • 须要在daemon启动的时候额外添加参数来指定配置存储服务地址

  • remote
    实现插件化,调用用户自行实现的网络驱动插件

DNAT来实现外部访问容器

docker run -itd -p 9901:991 --name busybox-dnat busybox复制代码


容器云平台


云平台厂家


若是你从头看到尾那真是太棒了,若是你也对docker技术感兴趣欢迎popo(hzchenzhiliang@corp.netease.com)联系交流。

相关阅读:Docker容器的原理与实践(上)

本文来自网易实践者社区,经做者陈志良受权发布。


相关文章:
【推荐】 KOL运营之——如何与网文做者高效地约稿?
【推荐】 ThreeJs 3D 全景项目开发总结
【推荐】 移动端爬虫工具与方法介绍

相关文章
相关标签/搜索