Docker技术基础

这篇总结下Docker的技术基础,然后在此基础上解释下Docker为什么快,为什么轻量级,为什么比传统的虚拟机技术更火。

首先,Docker是Github上的一个项目,出现在2013年,发展到现在不过7年的时间,最初的名字叫做dotCloud, 后来才改为Docker。

然后,Docker是一个软件容器平台。容器技术是虚拟化技术的一种。说Docker火,实际上是在说,容器技术很火。

虚拟化

先说虚拟化。虚拟化技术是一种资源优化技术,是指将计算机的各种物理资源(比如CPU、内存以及磁盘空间、网络适配器等 I/O 设备)抽象出来,再分割组合成多个虚拟的计算机的配置环境。平时我们说的虚拟化,一般指平台的虚拟化,而像Java虚拟机这种属于应用层面的虚拟化。-->这里<--有云计算与虚拟化的发展编年史,非常详细。

在容器技术出现之前,平台虚拟化技术主要是Hypervisor或者叫VMM(Vitual Machine Monitor),实际上是硬件的虚拟化,包括Xen(2005), KVM(Kenel-based VM,2007)等。嗯,他们都是VPS提供商常用的技术,个人觉得KVM能好一点。

容器虚拟化

容器的官方定义:

容器就是将软件打包成标准化单元,以用于开发、交付和部署

与其他平台虚拟化技术不同,容器虚拟化的是操作系统而不是硬件,容器之间是共享同一套操作系统资源的。虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统。因此容器的隔离级别会稍低一些。下面有个Docker容器的图可以看看:

虚拟化vs容器

可以看到,容器中使用Docker Engine代替了Guest OS和Hypervisor,它成为一个平台,在其上运行多个容器,然后容器之间互相隔离。

没有Guest OS的启动过程,也没有Hypervisor对硬件的抽象虚拟化,所以Docker的容器更加轻量级,启动速度远远超过虚拟机

Docker

Docker的英文意思,是码头的装卸工。Docker这个装卸工要装卸的内容,就是装载着软件的容器(container)。所以才说,Docker是一个软件容器平台。

Docker是在Linux Contaner 技术(LXC)的基础上发展起来的,是容器化技术的具体技术实现之一,是容器的管理引擎。Docker的开发语言是Go,它的三大技术基础分别是 Namespace,CGroups, 以及Uinon FS,这几个都是Linux内核的核心技术。Docker使用这三个技术对进程进行隔离,使得进程独立于Host以及其他的进程,这个运行的进程被称之为容器。容器实际完成了开发运行环境的搭建和配置,这样开发人员能够专注于构建软件,写代码,而不需要再去配置和搭建环境。开发人员,运维等人员可以方便地创建和使用容器,把自己的应用放入容器,而且通过Dokcerfile等,还可以以代码的形式管理容器的版本,可以按需修改交付,算是CaaS(此处是Code as a Service)的一种实现方式,也应该是软件定义运维的一种实现方式

三大技术基础及其作用

  1. Namespace

    Namespace是Linux内核提供的一种资源隔离方案。下面的表格是最新的8种namespace以及隔离的对象。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
           Namespace Flag            Page                  Isolates
           Cgroup    CLONE_NEWCGROUP cgroup_namespaces(7)  Cgroup root directory
           IPC       CLONE_NEWIPC    ipc_namespaces(7)     System V IPC,
                                                           POSIX message queues
           Network   CLONE_NEWNET    network_namespaces(7) Network devices,
                                                           stacks, ports, etc.
           Mount     CLONE_NEWNS     mount_namespaces(7)   Mount points
           PID       CLONE_NEWPID    pid_namespaces(7)     Process IDs
           Time      CLONE_NEWTIME   time_namespaces(7)    Boot and monotonic
                                                           clocks
           User      CLONE_NEWUSER   user_namespaces(7)    User and group IDs
           UTS       CLONE_NEWUTS    uts_namespaces(7)     Hostname and NIS
                                                           domain name
    

    使用Namespace可以使一个进程拥有这些系统全局资源的一个隔离的实例,对这个实例的修改对这个进程有效,其他进程对修改不可见。通过这种方式,可以使得不同Namsace的容器彼此互不影响,透明但是互不干扰。最常见的就是PID=1的进程,每个容器内部都会有一个,是使用PID命名空间隔离过后的一个实例,在主机内部是PID肯定是不为1的id,这个进程id在隔离之后对主机没有任何影响,容器内部都把这个隔离出的进程或者虚拟化出来的进程当作init进程,而容器和容器之间也是没有影响的。在主机使用ps命令是可以查看到所有运行的进程的,包括容器内的进程在内。还有个例子就是容器内的文件系统,属于Mount命名空间隔离的结果,实际上也是Docker image在下载之后的一个隔离,仅对当前容器有效,其他的容器是看不到的。总之,Namespace控制的是资源对容器的可见性

  2. CGroups

    Control Groups, 对进程使用的当前计算机物理资源进行分组隔离和限制的一种技术,分组的资源包括内存,CPU,磁盘以及网络IO等等。CGroups控制的是容器可以使用的主机资源的数量。一般在Linux主机在/sys/fs/cgroup目录下,可以看到CGroups所包含的不同的子系统,看下面的栗子:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    $ ll /sys/fs/cgroup/
    drwxr-xr-x 6 root root  0 Jun 15 09:56 blkio
    lrwxrwxrwx 1 root root 11 Jun 15 09:56 cpu -> cpu,cpuacct
    lrwxrwxrwx 1 root root 11 Jun 15 09:56 cpuacct -> cpu,cpuacct
    drwxr-xr-x 6 root root  0 Jun 15 09:56 cpu,cpuacct
    drwxr-xr-x 5 root root  0 Jun 15 09:56 cpuset
    drwxr-xr-x 8 root root  0 Jun 15 09:56 devices
    drwxr-xr-x 5 root root  0 Jun 15 09:56 freezer
    drwxr-xr-x 5 root root  0 Jun 15 09:56 hugetlb
    drwxr-xr-x 6 root root  0 Jun 15 09:56 memory
    lrwxrwxrwx 1 root root 16 Jun 15 09:56 net_cls -> net_cls,net_prio
    drwxr-xr-x 5 root root  0 Jun 15 09:56 net_cls,net_prio
    lrwxrwxrwx 1 root root 16 Jun 15 09:56 net_prio -> net_cls,net_prio
    drwxr-xr-x 5 root root  0 Jun 15 09:56 perf_event
    drwxr-xr-x 8 root root  0 Jun 15 09:56 pids
    

    对Docker来讲,在各个目录下都有docker的container id目录, 然后这个目录下的每个文件都是用来做资源限制的,可以查看里面文件的具体内容。默认情况下,应该是没有任何限制的,文件的大小都是0。相比较虚拟机来讲,这种控制方式更加精细化,占用较少的系统资源就可以启动一个容器了。这也是Docke容器轻量级的一个例证。

    -->这里<--有个对CGroups的详细解释说明。东西太多了真心记不住...

    要注意的是,K8s对资源的限制是最低是在Pod上,因为Pod是K8s调度的最小单元,更高层次的资源限制也可以在K8s的namespace上,可以使用ResourceQuota和LimitRange。这块儿用的极少,有空了试试。

  3. Union FS

    这是用来实现Docker镜像的技术,它是一种轻量级的高性能分层文件系统,支持将文件系统中的修改进行提交和层层叠加,这个特性使得镜像可以通过分层实现和继承,同时支持将不同目录挂载到同一个虚拟文件系统下。

    在Docker中,镜像分为基础镜像和父镜像,没有父镜像的镜像被称为基础镜像。基础镜像提供的是最小安装的 Linux 发行版。基础镜像的Dockerfile当中,from语句后对应的是scratch

    用户基于基础镜像来制作各种不同的应用镜像。这些应用镜像共享同一个基础镜像层,提高了存储效率。在改变Docker镜像时,一个新的镜像层会被创建。因此,用户不用替换整个原镜像或者完全重新建立新镜像,只需要添加新层即可。在用户分发镜像的时,也只需要分发被改动的新层内容(增量部分)。这让Docker的镜像管理变得十分轻量级和快速。

    Dockerfile中几乎每个指令都会生成一个文件系统种的分层,而为了减少分层结构,在写Dockerfile的时候,应该尽量将可以合并的命令写在一行之内。

    容器镜像在很大的情况下依然可以保持快速的启动,依赖于这个文件系统的Copy-on-Write技术,中文称为修改时复制。Docker镜像下载完成时,所有的分层目录就已经在Host上准备好了。当我们启动一个容器时候,并不需要将整个镜像中的文件copy一份,容器直接引用分层目录中的文件,任何的读操作都直接直接从镜像层中读即可,当发生写操作时,才需要将镜像中的相应文件copy到容器的可写层,在可写层进行写入。

    当容器需要删除文件的时候

    从上往下层寻找文件,找到后在容器中记录删除。即,并不会真正的删除文件,而是软删除。这将导致镜像体积只会增加,不会减少

    Build镜像时,每次对镜像的独立写入都是在新的镜像层,经过测试,在安装yum包或者Python包之后,哪怕是马上删除临时文件,只要安装的内容不变,最终的镜像大小都是一样的,因为对文件的删除,只是在文件系统内部做一个删除标记,并没有发生实际的删除。就是这样。有点像我们的数据库了,啊哈哈。

所以,Docker 容器没有GuestOS的启动过程,没有硬件的虚拟化,启动时做了资源的隔离和控制,镜像的大小对启动速度没有影响。所以启动更快,更轻便易用。

在这个基础上,研发人员build自己的应用,做到一次构建,多处运行;每次的应用更新也更加方便,包括配置改变也都在Dockerfile中方便管理,而且现在又有了K8s这样一个编排的事实标准,不火也怪了。

2006年AWS开始以 Web 服务的形式向企业提供 IT 基础设施服务,现在通常叫做云计算。这是云计算时代的开始。

而2015年7月21日,K8s V1.0的正式发布,则标志着云原生时代的到来。

嗯嗯,现在还不晚,抓紧上车才是~


这篇的速度算是正常,下一篇总结下K8s的Pod吧。

updatedupdated2020-08-052020-08-05