云计算核心技术Docker教程:Docker存储写入时复制(CoW)策略
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了云计算核心技术Docker教程:Docker存储写入时复制(CoW)策略相关的知识,希望对你有一定的参考价值。
参考技术A【点击右上角加\'关注\',全国产经信息不错过】
写时复制是一种共享和复制文件的策略,可最大程度地提高效率。如果文件或目录位于映像的较低层中,而另一层(包括可写层)需要对其进行读取访问,则它仅使用现有文件。另一层第一次需要修改文件时(在构建映像或运行容器时),将文件复制到该层并进行修改。这样可以将I / O和每个后续层的大小最小化。这些优点将在下面更深入地说明。
共享可以提升较小的图像
当您用于docker pull从存储库中下拉映像时,或者当您从本地尚不存在的映像中创建容器时,每个层都会被分别下拉,并存储在Docker的本地存储区域中,该区域通常/var/lib/docker/在Linux主机上。在此示例中,您可以看到这些层被拉出:
$ docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
f476d66f5408: Pull complete
8882c27f669e: Pull complete
d9af21273955: Pull complete
f5029279ec12: Pull complete
Digest: sha256:ab6cb8de3ad7bb33e2534677f865008535427390b117d7939193f8d1a6613e34
Status: Downloaded newer image for ubuntu:18.04
这些层中的每一层都存储在Docker主机的本地存储区域内的自己的目录中。要检查文件系统上的各层,请列出的内容/var/lib/docker/。本示例使用overlay2 存储驱动程序:
$ ls /var/lib/docker/overlay2
16802227a96c24dcbeab5b37821e2b67a9f921749cd9a2e386d5a6d5bc6fc6d3
377d73dbb466e0bc7c9ee23166771b35ebdbe02ef17753d79fd3571d4ce659d7
3f02d96212b03e3383160d31d7c6aeca750d2d8a1879965b89fe8146594c453d
ec1ec45792908e90484f7e629330666e7eee599f08729c93890a7205a6ba35f5
l
目录名称与层ID不对应(自Docker 1.10开始就是如此)。
现在,假设您有两个不同的Dockerfile。您使用第一个创建名为的图像acme/my-base-image:1.0。
# syntax=docker/dockerfile:1
FROM ubuntu:18.04
COPY . /app
第二acme/my-base-image:1.0层基于,但具有一些附加层:
# syntax=docker/dockerfile:1
FROM acme/my-base-image:1.0
CMD /app/hello.sh
第二个图像包含第一个图像的所有层,再加上带有CMD指令的新层,以及一个可读写容器层。Docker已经具有第一个映像中的所有层,因此不需要再次将其拉出。这两个图像共享它们共有的任何图层。
如果从两个Dockerfile构建映像,则可以使用docker image ls和 docker history命令来验证共享层的密码ID是否相同。
1.创建一个新目录cow-test/并更改到该目录中。
2.在中cow-test/,创建一个hello.sh具有以下内容的新文件:
#!/bin/sh
echo "Hello world"
保存文件,并使其可执行:
chmod +x hello.sh
3.将上面第一个Dockerfile的内容复制到一个名为的新文件中 Dockerfile.base。
4.将上面第二个Dockerfile的内容复制到一个名为的新文件中 Dockerfile。
5.在cow-test/目录中,构建第一个映像。不要忘记.在命令中包含final 。设置了PATH,它告诉Docker在哪里寻找需要添加到映像中的任何文件。
$docker build -t acme/my-base-image:1.0 -f Dockerfile.base .
6.建立第二张镜像。
$docker build -t acme/my-final-image:1.0 -f Dockerfile .
7.检查镜像的大小:
$docker image ls
8.检出构成每个镜像的图层:
$docker history bd09118bcef6
请注意,除了第二个图像的顶层以外,所有层都是相同的。所有其他层在两个图像之间共享,并且仅在中存储一次/var/lib/docker/。实际上,新层根本不占用任何空间,因为它不更改任何文件,而仅运行命令。
全国产经平台联系电话:010-65367702,邮箱:hz@people-energy.com.cn,地址:北京市朝阳区金台西路2号人民日报社
云原生 • Docker用故事给老板讲Docker核心原理
白话Docker核心原理
Docker是什么?
Docker使用Google公司推出的Go语言进行开发实现,基于操作系统内核中Cgroup(资源控制)、Namespace(资源隔离)与OverlayFS(数据存储)等技术,实现了基于操作系统层面的虚拟化技术。
理解的早就理解了这句话核心本质,不理解的给他这么一解释还是云里雾里。那我们先不急于搞懂Docker
是什么,说到Docker
容器,就不得不说下虚拟机(Virtual Machine
),Docker
容器和虚拟机又有什么区别呢?
Docker vs 虚拟机
虚拟机对于我们开发者是个再熟悉不过的概念,比如我们经常使用VMware Workstation
搭建虚拟操作系统部署应用,使用JVM
虚拟机运行Java
应用等,如下图,通常使用虚拟机管理器作为中间转换层,可以屏蔽底层操作系统或硬件设备差异,比如上层虚拟机操作系统(Guest OS
)执行程序或Java
程序运行等,这个中间件转换层就像翻译家一样,将上层执行的指令解释翻译成下层操作系统对应的指令进行执行。
正如Java世界中吹嘘的"一次编译,到处运行",虚拟机本质上通过中间件转换层屏蔽了底层差异,模拟出一个新环境,实现与平台无关,达到与外界隔离的目的,这就是虚拟机实现虚拟化的核心思想。
从虚拟机架构实现上可以看出,其存在一个很大问题:所有的指令都必须经过虚拟机管理器这个中间转换层翻译解释才能在真实操作系统上运行,这就意味着虚拟机会存在性能损耗。另外,为了模拟一个Linux
环境上运行的应用,需要使用VMware运行部署一个宿主机(Guest OS
),再在宿主机上运行应用,宿主机本身占用好几个G的存储空间、400-500MB+内存空间,现在微服务架构动不动就是10+、100+个应用组件需要部署,那这些组件都需要做隔离部署使用虚拟机方式无疑是致命的。
上述说的虚拟机存在性能问题和资源浪费造成了虚拟机对细粒度的环境隔离有点力不从心,而这又与当前流行的微服务架构场景下,系统被拆分成几十、上百个微服务应用组件需要独立部署存在冲突。Docker
推崇的是一种轻量级容器的结构,即一个应用一个容器。所以,Docker一出来就被推向巅峰,那它又是如何搞定虚拟机隔离存在的问题的呢?
Docker容器核心技术
Docker
容器中进程是直接运行在底层操作系统上,没有中间转换层,所以也就不存在性能损耗的问题。关键那它是如何做到隔离的呢?
这里就引出了支撑Docker
容器的两大内核技术:Namespace和Cgroups(Control Groups)。Namespace主要是用来进行资源隔离,对于那些计算型资源,比如CPU
、内存、磁盘IO等不能进行隔离的资源,这时就需要采用Cgroups
进行资源限制,防止有些资源消耗较大的容器,将整个物理机器的硬件资源(CPU
, Memory
、磁盘IO等) 占满,进而影响其它进程性能。
Namespace
和Cgroups
这两个技术都是Linux
内核本身支持的功能,Docker
如果只使用这两大技术也不可能造就出道即巅峰的火热程度,Docker
创新点恰恰是引入镜像概念,并使用联合文件系统(UnionFS
)技术很好的实现了镜像分层,这样就可以将应用部署介质、依赖环境配置文件以及操作系统二进制文件进行分层叠加构建出应用运行时文件系统环境。
镜像包含一个基础镜像(Base Image
),这个一般包含操作系统介质,比如centos
、debian
,但是它只包括使用的操作系统二进制文件,并没有包括内核相关,所以,它的体积远远小于部署整个操作系统占用的空间,比如一个centos
基础镜像大概只有70-80MB
。另外,镜像分层设计进一步减少存储占用,比如现在100+应用组件都是基于centos
基础镜像部署,实际部署时只需要拉取一份centos基础镜像,就像搭积木一样,将每一层使用的文件进行组合叠加,最终构建出程序运行时完整的目录结构。
白话核心技术关系
Docker
容器技术火热的背后,其实是Namespace
、Cgroups
和UnionFS
三大技术创新的结合,造就出了Docker
这种现象级产品。下面用个比较形象的比喻来帮助你理解三大技术关系:
1、正常程序启动时直接运行在操作系统上,使用Docker
启动程序时,也是直接运行在操作系统上,但是Docker
引擎在启动程序时会给程序套一个立方体壳(见下图);
2、这个立方体壳前后左右四个面使用Namespace
资源隔离技术打造,这样就给Docker
容器中进程和其它进程隔离开来,给容器中进程造成一种运行在一个独立环境中的假象(见下图);
3、这个立方体壳的上面这个面使用Cgroups
资源限制技术打造,避免程序壮大生长出来抢占其它进程的资源,进而影响其它进程性能,这样就给盖盖上加上了一个紧箍咒,再牛逼的程序也会把你死死的限制住(见下图);
4、最后再来看下这个立方体壳剩下的最下面这个面,其采用UnionFS
技术打造,构建出容器中进程运行时文件系统根基。将操作系统二进制指令、依赖配置文件、程序介质等通过镜像分层叠加构建出程序运行时看到的整个文件系统环境;比如宿主机是Debian系统
,但是基础镜像是CentOS环境
,容器中进程看到的是CentOS系统
,而不是Debian系统
,同时将yum install
安装的依赖介质也通过镜像打包进来,容器中进程就不需要关注宿主机上到底有没有安装该依赖介质等等,这样容器中进程看到是一个拥有程序运行时完整介质,并与宿主机操作系统隔离开的独立操作系统(见下图);
5、所以,程序运行在三大核心技术创造的立方体壳壳中,被蒙蔽双眼傻乎乎的以为运行在一个独立计算机环境中,看不到外界程序运行情况,也影响不到外界程序的运行。
如何查看Docker进程在宿主机上的PID?
Docker
容器中的进程是直接运行在宿主机上,可以通过docker inspect container
查看到Docker
容器中进程在宿主机上对应的PID信息(见下图):
宿主机上ps -ef
查看下容器进程信息:
因为,这里运行的是一个nginx
容器,所以宿主机上看到对应的是nginx
主进程,同时该进程创建了两个nginx worker
子进程。
Docker容器缺陷
高性能、轻便是容器相较于虚拟机最大的优势,容器本质上是一种特殊的进程。
不过,有利就有弊,基于Namespace
的资源隔离和Cgroups
的资源限制都不是那么彻底,因为容器之间底层还是共享使用宿主机的Linux
内核,尽管你可以在容器里使用不同版本的操作系统文件,比如CentOS
或者Ubuntu
,但这并不能改变共享宿主机内核的事实。这意味着,如果你要在Windows
宿主机上运行Linux
容器,或者在低版本的Linux
宿主机上运行高版本的Linux
容器,都是行不通的。
其次,在Linux
内核中,有很多资源和对象是不能被Namespace
化的,最典型的例子就是:时间。这就意味着,如果你的容器中的程序修改了时间,整个宿主机的时间都会被随之修改,这显然不符合用户的预期。
另外,跟Namespace
的情况类似,Cgroups
对资源的限制能力也有很多不完善的地方,这里最常见的是/proc
文件系统的问题。Linux
下的/proc
目录存储的是记录当前内核运行状态的一系列特殊文件,用户可以通过访问这些文件,查看系统以及当前正在运行的进程的信息,比如CPU
使用情况、内存占用率等,这些文件也是top
指令查看系统信息的主要数据来源。但是,你如果在容器里执行top
指令,就会发现,它显示的信息居然是宿主机的CPU
和内存数据,而不是当前容器的数据。造成这个问题的原因就是,Docker引擎在启动进程时直接将宿主机/proc
下很多文件挂载到Docker
容器上。
以上是关于云计算核心技术Docker教程:Docker存储写入时复制(CoW)策略的主要内容,如果未能解决你的问题,请参考以下文章
Docker+ Kubernetes已成为云计算的主流(二十六)