关于容器化的碎碎念
很有幸,公司云原生领域的大牛百忙之抽出时间给我团队做了扫盲,趁还没全忘了做个学习笔记,毕竟,每次都问,容易挨打…
容器化与虚拟化的恩怨情仇
先说结论: 虚拟化分为容器级虚拟化与主机级虚拟化(传统虚拟化),容器无需独立虚拟操作系统,故性能、资源消耗、及运维难度都优于主机级虚拟技术,当然有利有弊,容器的封闭性和安全性低于主机虚拟,这个太高深了,就不深入讨论了。
看图说话,先上图:
- 主机虚拟:
- 原理:通过Hpyervisor虚拟硬件环境,并据此安装虚拟操作系统。应用及应用运行所需依赖运行于虚拟操作系统上。
- 感受:从用户角度看,真实主机操作基本一致
- 容器虚拟(这里主要是Docker):
- 原理:Docker Engine基于操作系统内核功能(主要是LXC ,包括使用NameSpaces以隔离硬件资源、使用Cgroups以实现资源的限制及约束,当然docker for Windows出现后,已经不限于Linux内核技术)分配及隔离出一组硬件资源,并在其内运行应用。
- 感受:常用的场景是“运行无界面的后台服务”或者“运行Web服务”,运行图形界面这个事情,有人在尝试,但是不是主流,就不深入讨论了。如果有兴趣的话,感觉这篇文章写得还是很到位的:在Docker中运行桌面应用 - DockOne.io (dockerone.com)
Docker 架构初解
还是老规矩,先上图(网图侵删):
- Docker Client:客户用来与Docker Daemon建立通信的次抛型客户端(每次都需要通过Docker文件创建),,操作采用命令行模式(命令就不记录了,速查地址:Docker 命令大全 | 菜鸟教程 (runoob.com))
- Docker Daemon:Docker的守护进程,也是Docker最为核心的后台进程,主要包括:
- Server:负责接受Docker Client发送的请求;接受请求后,Server通过路由与分发调度,找到相应的Handler来执行请求;
- Engine:扮演Docker container存储仓库的角色,并且通过执行job的方式来操纵管理这些容器。
- Job:Engine内部最基本的工作执行单元。Docker可以做的每一项工作,都可以抽象为一个job,Job有一个名称,有参数,有环境变量,有标准的输入输出,有错误处理,有返回状态等。
- Docker Registry:镜像仓库,可搜索镜像(Search)、下载镜像(Pull)及上传镜像(Push),镜像仓库可为公有云(Docker Hub),也可以自建私有化仓库。
- 镜像:Docker 镜像可以看作是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变(State Less)。
- Graph:本地保存已下载镜像及其之间关系
- Driver:驱动模块,以实现对Docker容器执行环境的定制
- graphdriver:完成容器镜像的管理,包括存储与获取,当用户需要下载指定的容器镜像时,graphdriver将容器镜像存储在本地的指定目录,同时当用户需要使用指定的容器镜像来创建容器的rootfs时,graphdriver从本地镜像存储目录中获取指定的容器镜像。
- networkdriver:完成Docker容器网络环境的配置,其中包括Docker启动时为Docker环境创建网桥;Docker容器创建时为其创建专属虚拟网卡设备;以及为Docker容器分配IP、端口并与宿主机做端口映射,设置容器防火墙策略等
- execdriver:Docker容器的执行驱动,负责创建容器运行命名空间,负责容器资源使用的统计与限制,负责容器内部进程的真正运行等。
- libcontainer:Docker可以直接调用libcontainer,而最终操纵容器的namespace、cgroups、apparmor、网络设备以及防火墙规则等。这一系列操作的完成都不需要依赖LXC或者其他包
- Container:Docker架构中服务交付的最终体现形式。Docker按照用户的需求与指令,订制相应的Docker容器:
- 用户通过指定容器镜像,使得Docker容器可以自定义rootfs等文件系统;
- 用户通过指定计算资源的配额,使得Docker容器使用指定的计算资源;
- 用户通过配置网络及其安全策略,使得Docker容器拥有独立且安全的网络环境;
- 用户通过指定运行的命令,使得Docker容器执行指定的工作。
- rootfs:Linux根文件系统,特定的文件夹,文件夹之间的关系,即组织架构,以及特定的各种文件
一些不太冷的冷知识
1、你用的Docker其实不叫Docker:2017年开源Docker项目命名为Moby
2、Docker遵循的标准:为了避免Docker和rocket打架,共同成立了OCI,一个旨在管理容器标准的轻量级的、敏捷型的委员会
Dock执行分析
- Docker Client接受docker run命令,解析完请求以及收集完请求参数之后,发送一个HTTP请求给Docker Server,HTTP请求方法为POST,请求URL为”/containers/create? “+”xxx”;
- Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;
- mux.Router将请求路由分发至相应的handler,具体为PostContainersCreate;
- 在PostImageCreate这个handler之中,一个名为”create”的job被创建,并开始让该job运行;
- 名为”create”的job在运行过程中,执行Container.Create操作,该操作需要获取容器镜像来为Docker容器创建rootfs,即调用graphdriver;
- graphdriver从Graph中获取创建Docker容器rootfs所需要的所有的镜像;
- graphdriver将rootfs所有镜像,加载安装至Docker容器指定的文件目录下;
- 若以上操作全部正常执行,没有返回错误或异常,则Docker Client收到Docker Server返回状态之后,发起第二次HTTP请求。请求方法为”POST”,请求URL为”/containers/”+container_ID+”/start”;
- Docker Server接受以上HTTP请求,并交给mux.Router,mux.Router通过URL以及请求方法来确定执行该请求的具体handler;
- mux.Router将请求路由分发至相应的handler,具体为PostContainersStart;
- 在PostContainersStart这个handler之中,名为”start”的job被创建,并开始执行;
- 名为”start”的job执行完初步的配置工作后,开始配置与创建网络环境,调用networkdriver;
- networkdriver需要为指定的Docker容器创建网络接口设备,并为其分配IP,port,以及设置防火墙规则,相应的操作转交至libcontainer中的netlink包来完成;
- netlink完成Docker容器的网络环境配置与创建;
- 返回至名为”start”的job,执行完一些辅助性操作后,job开始执行用户指令,调用execdriver;
- execdriver被调用,初始化Docker容器内部的运行环境,如命名空间,资源控制与隔离,以及用户命令的执行,相应的操作转交至libcontainer来完成;
- libcontainer被调用,完成Docker容器内部的运行环境初始化,并最终执行用户要求启动的命令。