https://segmentfault.com/a/11... 容器,隔离,云的概述。这篇对其中用途广泛的docker,k8s做详细介绍,并给出云搭建的生态环境体系。

docker

1.与其他VM等对比

容器发展,详见上面提到的文章

chroot 1979
Linux Vserver 2001
process container 2006
LXC 2008
Docker 2013
windows container 2017

典型图:VM与container对比,差异在于OS

clipboard.png

VM container
隔离 OS kernel namespace
可配额,可度量 硬件页映射 cgroups
移动性 snapshot,image AUFS
安全 gresc patch

缺点

隔离性相比KVM之类的虚拟化方案还是有些欠缺,所有container公用一部分的运行库
网络管理相对简单,主要是基于namespace隔离
cgroup的cpu和cpuset提供的cpu功能相比KVM的等虚拟化方案相比难以度量(所以dotcloud主要是安内存收费)
docker对disk的管理比较有限(disk quota)
container随着用户进程的停止而销毁,container中的log等用户数据不便收集   

优点:

轻量级的特点,其启动快,而且docker能够只加载每个container变化的部分,这样资源占用小
docker不只是容器,高级容器引擎,应用部署平台,应用镜像商店

2.docker生态圈————不只是容器

clipboard.png

  • Image 一个包含运行环境的只读 模板
  • Container 镜像的运行态,docker利 用容器运行应用
  • Registry 存储容器镜像的仓库
  • Dockerfile 包含构建镜像一系列命令 和参数的脚本

3.执行过程

clipboard.png

  • Docker Daemon 可以认为是通过 Docker Server 模块接受 Docker Client 的请求,并在 Engine 中处理请求,然后根据请求类型,创建出指定的 Job 并运行。 Docker Daemon 运行在 Docker host 上,负责创建、运行、监控容器,构建、存储镜像。
  • job运行过程的作用有以下几种可能:
    向 Docker Registry 获取镜像
    通过 graphdriver 执行容器镜像的本地化操作
    启动容器
  • graph
    Graph在Docker架构中扮演已下载容器镜像的保管者,以及已下载容器镜像之间关系的记录者。
    一方面,Graph存储着本地具有版本信息的文件系统镜像,另一方面也通过GraphDB记录着所有文件系统镜像彼此之间的关系。
    GraphDB是一个构建在SQLite之上的小型图数据库,实现了节点的命名以及节点之间关联关系的记录。它仅仅实现了大多数图数据库所拥有的一个小的子集,但是提供了简单的接口表示节点之间的关系。
    Graph的本地目录中,关于每一个的容器镜像,具体存储的信息有:该容器镜像的元数据,容器镜像的大小信息,以及该容器镜像所代表的具体rootfs。
  • networkdriver 执行容器网络环境的配置
    networkdriver的用途是完成Docker容器网络环境的配置,其中包括Docker启动时为Docker环境创建网桥;Docker容器创建时为其创建专属虚拟网卡设备;以及为Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等。
  • execdriver 执行容器内部运行的执行工作
    execdriver作为Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。在execdriver的实现过程中,原先可以使用LXC驱动调用LXC的接口,来操纵容器的配置以及生命周期,而现在execdriver默认使用native驱动,不依赖于LXC。具体体现在Daemon启动过程中加载的ExecDriverflag参数,该参数在配置文件已经被设为"native"。这可以认为是Docker在1.2版本上一个很大的改变,或者说Docker实现跨平台的一个先兆
  • libcontainer
    Docker架构中一个使用Go语言设计实现的库,设计初衷是希望该库可以不依靠任何依赖,直接访问内核中与容器相关的API。
    正是由于libcontainer的存在,Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。这一系列操作的完成都不需要依赖LXC或者其他包。
    libcontainer提供了一整套标准的接口来满足上层对容器管理的需求。

    docker run过程
    (1) Docker Client接受docker run命令,解析完请求以及收集完请求参数之后,发送一个HTTP请求给Docker Server,HTTP请求方法为POST,请求URL为/containers/create? +xxx;
    (2) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;
    (3) mux.Router将请求路由分发至相应的handler,具体为PostContainersCreate;
    (4) 在PostImageCreate这个handler之中,一个名为"create"的job被创建,并开始让该job运行;
    (5) 名为"create"的job在运行过程中,执行Container.Create操作,该操作需要获取容器镜像来为Docker容器创建rootfs,即调用graphdriver;
    (6) graphdriver从Graph中获取创建Docker容器rootfs所需要的所有的镜像;
    (7) graphdriver将rootfs所有镜像,加载安装至Docker容器指定的文件目录下;
    (8) 若以上操作全部正常执行,没有返回错误或异常,则Docker Client收到Docker Server返回状态之后,发起第二次HTTP请求。请求方法为"POST",请求URL为"/containers/"+container_ID+"/start";
    (9) Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;
    (10) mux.Router将请求路由分发至相应的handler,具体为PostContainersStart;
    (11) 在PostContainersStart这个handler之中,名为"start"的job被创建,并开始执行;
    (12) 名为"start"的job执行完初步的配置工作后,开始配置与创建网络环境,调用networkdriver;
    (13) networkdriver需要为指定的Docker容器创建网络接口设备,并为其分配IP,port,以及设置防火墙规则,相应的操作转交至libcontainer中的netlink包来完成;
    (14) netlink完成Docker容器的网络环境配置与创建;
    (15) 返回至名为"start"的job,执行完一些辅助性操作后,job开始执行用户指令,调用execdriver;
    (16) execdriver被调用,初始化Docker容器内部的运行环境,如命名空间,资源控制与隔离,以及用户命令的执行,相应的操作转交至libcontainer来完成;
    (17) libcontainer被调用,完成Docker容器内部的运行环境初始化,并最终执行用户要求启动的命令。
    

4.docker技术

clipboard.png

隔离 命名空间

1、进程命名空间:每个命名空间独立维护自己的进程号,父子关系结构,子空间的进程对父空间是可见的,新fork出的进程 在父命名空间和子命名空间都会生成一个进程号
 2、网络命名空间:实现网络隔离 完全独立的网络协议视图,包括网络设备接口、IPV4 IPV6协议栈,路由表,防火墙规则,sockers等,采用虚拟网络设备,将容器中的虚拟网卡绑定在本地主机的docker0的网桥上
 3、ipc命名空间 进程间交互,信号量、消息队列,和共享内存,同一个IPC空间的可以交互,不同的不可以
 4、挂载命名空间 类似chroot, 将一个进程放到一个特定的目录执行。允许不同命名空间的进程看到的文件结构不同,文件目录被隔离 chroot(PATH)这个function必须具有 root的身份才能执行,执行后会将根目录切换到PATH 所指定的地方。
5、 UTS命名空间 UNIX Time-sharing System 允许每个容器拥有独立的主机名和域名,默认主机名为容器ID
 6、用户命名空间 每个容器可以有不同的用户和组id,可以在容器内使用特定的内部用户执行程序。每个容器都有root账号

可配额,可度量

1.cpu,io,mem等等:cgroups
2.网卡
docker创建容器的过程:
     1、创建一对虚拟接口 veth pair ,分别放在本地主机和新容器的命名空间中
     2、本地主机一端的虚拟接口 连接到默认的docker0网桥上,或指定网桥,并具有一个veth开头的唯一名字
     3、容器一端的虚拟接口,放到新的容器中,修改名字为eth0,只在容器中可见
     4、从网桥可用地址中 分配一个空闲的给 eth0,(172.17.0.2/16) 并设置默认路由网卡为 docker0的ip (172.17.42.1/16)
clipboard.png
Veth pair 是一对虚拟网卡,从一张veth网卡发出的数据包可以直接到达它的peer veth,两者之间存在着虚拟链路。不同网络命名空间之间交互
Veth 网卡和常规的以太网区别仅在于xmit接口:将数据发送到其peer,触发peer的Rx 过程。
3.disk/network quota
虽然cgroup提供IOPS之类的限制机制,但是从限制用户能使用的磁盘大小和网络带宽上还是非常有限的。
Disk/network的quota现在有两种思路:
1).通过docker run -v命令将外部存储mount到container的目录下,quota从Host方向限制,在device mapper driver中更采用实际的device因此更好控制。
2).通过使用disk quota来限制AUFS的可操作文件大小。维护一个UID池,每次创建container都从中取一个user name, 在container里和Host上用这个username创建用户,在Host上用set quota限制该username的UID的disk. 网络上由于docker采用veth的方式,可以采用tc来控制host上的veth的设备。

移植 portable

REDHAT实现AFUS的driver(RHEL DEVIDE MAPPER)

  • 功能 采用AUFS作为docker的container的文件系统,能够提供如下好处:
    1)节省存储空间 - 多个container可以共享base image存储
    2)快速部署 - 如果要部署多个container,base image可以避免多次拷贝
    3)内存更省 - 因为多个container共享base image, 以及OS的disk缓存机制,多个container中的进程命中缓存内容的几率大大增加
    4)升级更方便 - 相比于 copy-on-write 类型的FS,base-image也是可以挂载为可writeable的,可以通过更新base image而一次性更新其之上的container
    5)允许在不更改base-image的同时修改其目录中的文件 - 所有写操作都发生在最上层的writeable层中,这样可以大大增加base image能共享的文件内容。
    以上5条 1-3 条可以通过 copy-on-write 的FS实现, 4可以利用其他的union mount方式实现, 5只有AUFS实现的很好。这也是为什么Docker一开始就建立在AUFS之上。
  • AUFS工作过程:
    1.启动:bootfs+rootfs
    bootfs (boot file system) 主要包含 bootloader 和 kernel, bootloader主要是引导加载kernel, 当boot成功后 kernel 被加载到内存中后 bootfs就被umount了。
    rootfs (root file system) 包含的就是典型 Linux 系统中的 /dev, /proc,/bin, /etc 等标准目录和文件。
    2.典型的Linux在启动后,首先将 rootfs 设置为 readonly, 进行一系列检查, 然后将其切换为 "readwrite" 供用户使用。在Docker中,初始化时也是将 rootfs 以readonly方式加载并检查,然而接下来利用 union mount 的方式将一个 readwrite 文件系统挂载在 readonly 的rootfs之上,并且允许再次将下层的 FS(file system) 设定为readonly 并且向上叠加, 这样一组readonly和一个writeable的结构构成一个container的运行时态, 每一个FS被称作一个FS层。
    3.得益于AUFS的特性, 每一个对readonly层文件/目录的修改都只会存在于上层的writeable层中。这样由于不存在竞争, 多个container可以共享readonly的layer。 所以docker将readonly的层称作 "image" - 对于container而言整个rootfs都是read-write的,但事实上所有的修改都写入最上层的writeable层中, image不保存用户状态,可以用于模板、重建和复制。

5.部署平台

用户每次向Git服务器的push提交都会通知给Jenkins(基于Java开发的一种持续集成工具),Jenkins触发build。Maven(是一个采用Java编写的开源项目管理工具,我们最常用的就是该工具的构建功能)构建所有的相关代码,包括Docker镜像。
Maven会把完成的镜像推送到私有的Registry保存
最后Jenkins会触发Docker Registry pull下载镜像到宿主机本地,并自动启动应用容器。

k8s

k8s是一个容器集群管理系统,其提供应用部署、维护、 扩展机制等功能,利用 Kubernetes 能方便地管理跨机器运行容器化的应用。其架构图如下:
clipboard.png

  • Labels 是用于区分 Pod、Service、Replication Controller 的 key/value 键值对
  • master 包含Replication Controller,API Server
  • Replication Controller:
    会确保 Kubernetes 集群中指定的 pod 副本 (replicas) 在运行, 即使在节点出错时。通过修改 Replication Controller 的副本 (replicas) 数量来水平扩展或者缩小运行的pods,一个一个地替换pods来 rolling updates 服务,labels维度的Multiple release tracks
  • API Server 的主要声明,包括 Pod Registry、Controller Registry、Service Registry、Endpoint Registry、Minion Registry、Binding Registry、RESTStorage 以及 Client

    clipboard.png

    Minion Registry 负责跟踪 Kubernetes 集群中有多少 Minion(Host):可以对 Minion Registry Create、Get、List、Delete
    Pod Registry 负责跟踪 Kubernetes 集群中有多少 Pod 在运行,以及这些 Pod 跟 Minion 是如何的映射关系,对 Pod 进行 Create、Get、List、Update、Delete 操作。
    ……
    Endpoints Registry 负责收集 Service 的 endpoint,比如 Name:"mysql",Endpoints: ["10.10.1.1:1909","10.10.2.2:8834"],Create、Get、List、Update、Delete 以及 watch
    Binding Registry 绑定的 Pod 被绑定到一个 host
    Scheduler 收集和分析当前 Kubernetes 集群中所有 Minion 节点的资源 (内存、CPU) 负载情况,然后依此分发新建的 Pod 到 Kubernetes 集群中可用的节点

  • Kubelet
    是 Kubernetes 集群中每个 Minion 和 Master API Server 的连接点,Kubelet 运行在每个 Minion 上,是 Master API Server 和 Minion 之间的桥梁,接收 Master API Server 分配给它的 commands 和 work,与持久性键值存储 etcd、file、server 和 http 进行交互,读取配置信息。Kubelet 的主要工作是管理 Pod 和容器的生命周期,其包括 Docker Client、Root Directory、Pod Workers、Etcd Client、Cadvisor Client 以及 Health Checker 组件
  • Proxy
    是为了解决外部网络能够访问跨机器集群中容器提供的应用服务而设计的,从上图 3-3 可知 Proxy 服务也运行在每个 Minion 上。Proxy 提供 TCP/UDP sockets 的 proxy,每创建一种 Service,Proxy 主要从 etcd 获取 Services 和 Endpoints 的配置信息,或者也可以从 file 获取,然后根据配置信息在 Minion 上启动一个 Proxy 的进程并监听相应的服务端口,当外部请求发生时,Proxy 会根据 Load Balancer 将请求分发到后端正确的容器处理。

交互:
clipboard.png
http://tiewei.github.io/cloud...

公司云架构图:
clipboard.png

容器

  • 静态容器
    固定可访问IP,不能动态漂移,容器中无代码,扩容较快,轻量虚拟机,支持共享卷/块设备,日志收集与物理机一样。
  • 有状态容器
    k8s的statefulstes模型,可以较好弹性伸缩,可以动态漂移,有代码,扩容很快,支持共享卷/块设备(带云盘),固定ip。适用于对程序数据有持久化需求的服务。
  • 无状态容器
    k8s的deployment模型,开箱即用,可以动态漂移,急速扩容,弹性伸缩极好,只支持共享卷(ceph这种网络分布式磁盘),日志远程收集,容器名和ip不固定,适用于无状态微服务。只支持云平台变更(直接的上线系统不行),不挂载云盘

数据

小的放ceph,大的物理机映射=>网络抖动全换成本地磁盘(无论日志还是数据,数据需要单独迁移,两种方案都可以)

网络

docker的虚拟网络很多种实现,原生或者插件,一般原生的overlay用的较多
sdn overlay网络 稳定性差(ip不受限制,因此漂移也不受限制) =》物理网络(ip受限于tor[交换机]的网络分配,ip只能tor切换,扩容后资源不均衡)
流量到LB后,若采取sdn,会到sdn中心节点,虚拟ip和物理ip的映射,找到物理ip的宿主机器,宿主机器通过overlay与docker通信
若物理网络,直接到docker,docker的ip就是分配的物理ip。漂移受tor的影响指的是在ip不变的情况下漂移。ip变化会导致日志等问题

  • 物理网络
    容器ip2直接到物理机(ip1,ip2=》mac1。物理机再转发给ip
  • docker虚拟网络原理
    1.同host:bridge的ip模式为例
    创建虚拟网卡。同一网卡之间都可以互相通信
    不同网卡之间即使配了ip路由也会隔离,用的iptable的drop
    与外网:先默认连docker0,如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理(iptable NAT)。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)
    外网反向:ip:port(不同虚拟ip绑定不同端口),docker-proxy监听所有port,换ip1发给docker0,与ip1通信

    clipboard.png
    2.跨主机overlay
    Docerk overlay 网络需要一个 key-value 数据库用于保存网络状态信息,包括 Network、Endpoint、IP 等。Consul、Etcd 和 ZooKeeper 都是 Docker 支持的 key-vlaue 软件,我们这里使用 Consul。
    不同host:基于VxLAN。VxLAN 可将二层数据封装到 UDP 进行传输,VxLAN 提供与 VLAN 相同的以太网二层服务,但是拥有更强的扩展性和灵活性。
    clipboard.png
    外网:为每个创建了eth1可以连接外网。其过程和bridge的docker0访问外网一样
    host内部 br0还是直接endpoint连接着,另外加了一个vxlan设备,与其他host连接

  • VXLAN原理
    vxlan在二层包上封装vxlan header放在UDP中发到VTEP设备上解封(header中包含24位的ID,同一ID的可以互相通信)
    clipboard.png
    Linux vxlan 创建一个 UDP Socket,默认在 8472 端口监听。
    接收到 vxlan 包后,解包,然后根据其中的 vxlan ID 将它转给某个 vxlan interface,然后再通过它所连接的 linux bridge 转给虚机。
    Linux vxlan 在收到虚机发来的数据包后,将其封装为多播 UDP 包,从网卡发出。

梦想家
107 声望76 粉丝