Docker
文章目录
1.Docker的发展简史
Docker 最初是 dotCloud 公司创始人 Solomon Hykes 在法国期间发起的一个公司内部项目,它是基于 dotCloud 公司多年云服务技术的一次革新,并于 2013 年 3 月以 Apache 2.0 授权协议开源),主要项目代码在 GitHub 上进行维护。Docker 项目后来还加入了 Linux 基金会,并成立推动开放容器联盟。
Docker 自开源后受到广泛的关注和讨论,至今其 GitHub 项目已经超过 3 万 6 千个星标和一万多个 fork。甚至由于 Docker 项目的火爆,在 2013 年底,dotCloud 公司决定改名为 Docker。Docker 最初是在 Ubuntu 12.04 上开发实现的;Red Hat 则从 RHEL 6.5 开始对 Docker 进行支持;Google 也在其 PaaS 产品中广泛应用 Docker。
Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,对进程进行封装隔离,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其它的隔离的进程,因此也称其为容器。最初实现是基于 LXC,从 0.7 以后开始去除 LXC,转而使用自行开发的 libcontainer,从 1.11 开始,则进一步演进为使用 runC 和 containerd。
作为一种轻量级的虚拟化技术,容器使用方便、操作便捷,大大提高开发人员的工作效率,并得到业内的广泛使用。但与此同时,容器安全事故频发,包括不安全的镜像源、容器入侵事件、运行环境的安全问题等等。
1.不安全的镜像源
开发者通常会在Docker 官方的Docker Hub仓库下载镜像,这些镜像一部分来源于开发镜像内相应软件的官方组织,还有大量镜像来自第三方组织甚至个人。从这些镜像仓库中获取镜像的同时,也带来潜在的安全风险。例如,下载镜像内软件本身是否就包含漏洞,下载的镜像是否被恶意植入后门,镜像在传输过程中是否被篡改。
2.容器入侵事件
由docker本身的架构与机制可能产生的问题,这一攻击场景主要产生在黑客已经控制了宿主机上的一些容器(或者通过在公有云上建立容器的方式获得这个条件),然后对宿主机或其他容器发起攻击来产生影响。
3.运行环境的安全
除docker 本身存在的问题外,docker运行环境存在的问题同样给docker的使用带来风险。
由于容器是介于基础设施和平台之间的虚拟化技术,因此面向基础设施虚拟化的传统云安全解决方案无法完全解决前述安全问题。如以容器为支撑技术构建DevOps环境,就需要设计涵盖从容器镜像的创建到投产上线的整个生命周期的容器安全方案。
2.docker基础用法
什么是docker
docker中的容器:
lxc --> libcontainer --> runC
Docker 的优点
Docker 是一个用于开发,交付和运行应用程序的开放平台。Docker 使您能够将应用程序与基础架构分开,从而可以快速交付软件。借助 Docker,您可以与管理应用程序相同的方式来管理基础架构。通过利用 Docker 的方法来快速交付,测试和部署代码,您可以大大减少编写代码和在生产环境中运行代码之间的延迟。
1、快速,一致地交付您的应用程序
Docker 允许开发人员使用您提供的应用程序或服务的本地容器在标准化环境中工作,从而简化了开发的生命周期。
容器非常适合持续集成和持续交付(CI / CD)工作流程,请考虑以下示例方案:
- 您的开发人员在本地编写代码,并使用 Docker 容器与同事共享他们的工作。
- 他们使用 Docker 将其应用程序推送到测试环境中,并执行自动或手动测试。
- 当开发人员发现错误时,他们可以在开发环境中对其进行修复,然后将其重新部署到测试环境中,以进行测试和验证。
- 测试完成后,将修补程序推送给生产环境,就像将更新的镜像推送到生产环境一样简单。
2、响应式部署和扩展
Docker 是基于容器的平台,允许高度可移植的工作负载。Docker 容器可以在开发人员的本机上,数据中心的物理或虚拟机上,云服务上或混合环境中运行。
Docker 的可移植性和轻量级的特性,还可以使您轻松地完成动态管理的工作负担,并根据业务需求指示,实时扩展或拆除应用程序和服务。
3、在同一硬件上运行更多工作负载
Docker 轻巧快速。它为基于虚拟机管理程序的虚拟机提供了可行、经济、高效的替代方案,因此您可以利用更多的计算能力来实现业务目标。Docker 非常适合于高密度环境以及中小型部署,而您可以用更少的资源做更多的事情。
OCI&OCF
OCI
Open Container-initiative
- 由Linux基金会主导于2015年6月创立
- 旨在围绕容器格式和运行时制定一个开放的工业化标准
- contains two specifications
- the Runtime Specification(runtime-spec)
- the Image Specification(image-spec)
OCF
Open Container Format
runC 是一个 CLI 工具,用于根据 OCI 规范生成和运行容器
- 容器作为 runC 的子进程启动,可以嵌入到各种其他系统中,而无需运行守护程序
- runC 建立在 libcontainer 之上,同样的容器技术为数百万个 Docker 引擎安装提供支持
docker提供了一个专门容纳容器镜像的站点:https://hub.docker.com
docker架构
docker镜像与镜像仓库
为什么镜像仓库名字是Registry而不是repository?在docker中仓库的名字是以应用的名称取名的。
镜像是静态的,而容器是动态的,容器有其生命周期,镜像与容器的关系类似于程序与进程的关系。镜像类似于文件系统中的程序文件,而容器则类似于将一个程序运行起来的状态,也即进程。所以容器是可以删除的,容器被删除后其镜像是不会被删除的。
docker对象
使用 Docker 时,您正在创建和使境像、容器、网络、卷、插件和其他对象。
-
境像
-
境像是一个只读模板,其中包含有关创建 docker 容器的说明。
-
通常,一个镜像基于另一个镜像,并具有一些额外的自定义。
-
您可以创建自己的镜像,也可以仅使用其他人创建并在注册表中发布的镜像。
器皿 -
容器是镜像的可运行实例。
-
您可以使用 Docker API 或 CLI 创建、运行、停止、移动或删除容器。
-
可以将容器连接到一个或多个网络,将存储附加到该容器,甚至可以基于其当前状态创建新镜像。
-
安装docker
[root@localhost ~]# cd /etc/yum.repos.d/
[root@localhost yum.repos.d]# wget https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo
[root@localhost yum.repos.d]# sed -i 's@https://download.docker.com@https://mirrors.tuna.tsinghua.edu.cn/docker-ce@g' docker-ce.repo
[root@localhost yum.repos.d]# dnf -y install docker-ce
配置镜像加速
docker-ce的配置文件是/etc/docker/daemon.json,此文件默认不存在,需要我们手动创建并进行配置,而docker的加速就是通过配置此文件来实现的。
docker的加速有多种方式:
- docker cn
- 中国科技大学加速器
- 阿里云加速器(需要通过阿里云开发者平台注册帐号,免费使用个人私有的加速器)
[root@localhost ~]# sudo mkdir -p /etc/docker
[root@localhost ~]# sudo tee /etc/docker/daemon.json <<-'EOF'
> {
> "registry-mirrors": ["https://6wh26kc0.mirror.aliyuncs.com"]
> }
> EOF
{
"registry-mirrors": ["https://6wh26kc0.mirror.aliyuncs.com"]
}
[root@localhost ~]# sudo systemctl daemon-reload
[root@localhost ~]# sudo systemctl restart docker
[root@localhost ~]# systemctl enable docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service → /usr/lib/systemd/system/docker.service.
[root@localhost ~]# docker version
Client: Docker Engine - Community
Version: 20.10.17
API version: 1.41
Go version: go1.17.11
Git commit: 100c701
Built: Mon Jun 6 23:03:11 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.17
API version: 1.41 (minimum version 1.12)
Go version: go1.17.11
Git commit: a89b842
Built: Mon Jun 6 23:01:29 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.6
GitCommit: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
runc:
Version: 1.1.2
GitCommit: v1.1.2-0-ga916309
docker-init:
Version: 0.19.0
GitCommit: de40ad0
[root@localhost ~]# docker info
Client:
Context: default
Debug Mode: false
Plugins:
app: Docker App (Docker Inc., v0.9.1-beta3)
buildx: Docker Buildx (Docker Inc., v0.8.2-docker)
scan: Docker Scan (Docker Inc., v0.17.0)
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 20.10.17
Storage Driver: overlay2
Backing Filesystem: xfs
Supports d_type: true
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: runc io.containerd.runc.v2 io.containerd.runtime.v1.linux
Default Runtime: runc
Init Binary: docker-init
containerd version: 10c12954828e7c7c9b6e0ea9b0c02b01407d3ae1
runc version: v1.1.2-0-ga916309
init version: de40ad0
Security Options:
seccomp
Profile: default
Kernel Version: 4.18.0-394.el8.x86_64
Operating System: CentOS Stream 8
OSType: linux
Architecture: x86_64
CPUs: 4
Total Memory: 7.555GiB
Name: localhost.localdomain
ID: VFSB:7VDZ:EGML:YKDT:RFOQ:QVZC:52X7:YCEY:5Q7T:CC2M:DNZR:QGPQ
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Labels:
Experimental: false
Insecure Registries:
127.0.0.0/8
Registry Mirrors:
https://6wh26kc0.mirror.aliyuncs.com/
Live Restore Enabled: false
docker常用操作
命令 | 功能 |
---|---|
docker search | 在Docker Hub上查找镜像 |
docker pull | 从镜像仓库中拉取或者更新指定镜像 |
docker images | 列出本地镜像 |
docker create | 创建一个新的容器但不启动它 |
docker start | 启动容器 |
docker run | 创建一个新的容器并运行一个命令 |
docker attach | 连接到运行的容器 |
docker ps | 列出容器 |
docker logs | 查看容器日志 |
docker restart | 重启容器 |
docker stop | 停止容器 |
docker kill | 干掉一个或多个运行中的容器 |
docker rm | 删除一个或多个容器 |
docker exec | 在运行的容器中运行命令 |
docker info | 显示整个系统的信息 |
docker inspect | 返回Docker对象的低级信息 |
//在Docker Hub上查找镜像
[root@localhost ~]# docker search httpd
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
httpd The Apache HTTP Server Project 4109 [OK]
centos/httpd-24-centos7 Platform for running Apache httpd 2.4 or bui… 44
centos/httpd 35 [OK]
clearlinux/httpd httpd HyperText Transfer Protocol (HTTP) ser… 2
hypoport/httpd-cgi httpd-cgi 2 [OK]
......
//从镜像仓库中拉取或者更新指定镜像
[root@localhost ~]# docker pull httpd
Using default tag: latest
latest: Pulling from library/httpd
a2abf6c4d29d: Pull complete
dcc4698797c8: Pull complete
41c22baa66ec: Pull complete
67283bbdd4a0: Pull complete
d982c879c57e: Pull complete
Digest: sha256:0954cc1af252d824860b2c5dc0a10720af2b7a3d3435581ca788dff8480c7b32
Status: Downloaded newer image for httpd:latest
docker.io/library/httpd:latest
//列出本地镜像
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
httpd latest dabbfbe0c57b 7 months ago 144MB
//创建一个新的容器但不启动它
[root@localhost ~]# docker create --name web -p 80:80 httpd
3c9333c7bd7dc7cab889354ae543c586fe9d87fde1bc34ba497ad17fee41e16c
//列出容器
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c9333c7bd7d httpd "httpd-foreground" 39 seconds ago Created web
//启动容器
[root@localhost ~]# docker start web //可以用ID或者名字
web
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9fdaf3c409da httpd "httpd-foreground" 2 minutes ago Up 9 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web
//关闭容器
[root@localhost ~]# docker stop web
web
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
973d98dc41fd httpd "httpd-foreground" About a minute ago Created Sia
3c9333c7bd7d httpd "httpd-foreground" 3 minutes ago Exited (0) 8 seconds ago web
//重启容器
[root@localhost ~]# docker restart web
web
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c9333c7bd7d httpd "httpd-foreground" 4 minutes ago Up 8 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp web
//干掉运行中的容器 //stop正常退出 kill强制关闭
[root@localhost ~]# docker kill web
web
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
973d98dc41fd httpd "httpd-foreground" 3 minutes ago Created Sia
3c9333c7bd7d httpd "httpd-foreground" 5 minutes ago Exited (137) 7 seconds ago web
//查看容器日志
[root@localhost ~]# docker logs web
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Mon Aug 08 01:29:51.367971 2022] [mpm_event:notice] [pid 1:tid 139661043739968] AH00489: Apache/2.4.52 (Unix) configured -- resuming normal operations
[Mon Aug 08 01:29:51.368077 2022] [core:notice] [pid 1:tid 139661043739968] AH00094: Command line: 'httpd -D FOREGROUND'
[Mon Aug 08 01:31:47.485451 2022] [mpm_event:notice] [pid 1:tid 139661043739968] AH00492: caught SIGWINCH, shutting down gracefully
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Mon Aug 08 01:33:37.541693 2022] [mpm_event:notice] [pid 1:tid 140006721383744] AH00489: Apache/2.4.52 (Unix) configured -- resuming normal operations
[Mon Aug 08 01:33:37.541804 2022] [core:notice] [pid 1:tid 140006721383744] AH00094: Command line: 'httpd -D FOREGROUND'
//删除一个或多个容器 运行时不可以删除
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
973d98dc41fd httpd "httpd-foreground" 5 minutes ago Created Sia
3c9333c7bd7d httpd "httpd-foreground" 6 minutes ago Exited (137) About a minute ago web
[root@localhost ~]# docker stop web //先停止
web
[root@localhost ~]# docker rm web //在进出删除
web
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
973d98dc41fd httpd "httpd-foreground" 5 minutes ago Created Sia
//-f 在运行时可以强制删除
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
973d98dc41fd httpd "httpd-foreground" 6 minutes ago Created Sia
662e0fdfb4ca httpd "/bin/sh" 25 seconds ago Exited (0) 3 seconds ago test
[root@localhost ~]# docker rm -f test
test
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
973d98dc41fd httpd "httpd-foreground" 6 minutes ago Created Sia
//创建一个新的容器并运行一个命令
[root@localhost ~]# docker run -it --name test httpd /bin/sh
#
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
011663c8941a httpd "/bin/sh" 50 seconds ago Exited (0) 25 seconds ago Avril
e743b434ae92 httpd "/bin/sh" About a minute ago Exited (127) About a minute ago test
973d98dc41fd httpd "httpd-foreground" 10 minutes ago Created Sia
//连接到正在运行中的容器
[root@localhost ~]# docker start test
test
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
011663c8941a httpd "/bin/sh" 6 minutes ago Exited (0) 6 minutes ago Avril
e743b434ae92 httpd "/bin/sh" 7 minutes ago Up 58 seconds 80/tcp test
973d98dc41fd httpd "httpd-foreground" 16 minutes ago Created Sia
//在运行的容器中执行命令
[root@localhost ~]# docker start test
test
[root@localhost ~]# docker exec -it test /bin/sh
# exit
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
011663c8941a httpd "/bin/sh" 7 minutes ago Exited (0) 7 minutes ago Avril
e743b434ae92 httpd "/bin/sh" 8 minutes ago Up About a minute 80/tcp test
973d98dc41fd httpd "httpd-foreground" 17 minutes ago Created Sia
//返回Docker对象的低级信息
[root@localhost ~]# docker inspect test
[
{
"Id": "e743b434ae92dbb706482cf02e8e953d14852905a8b34ea481a997dc04d83921",
"Created": "2022-08-08T01:39:26.184390963Z",
"Path": "/bin/sh",
"Args": [],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 131459,
"ExitCode": 0,
"Error": "",
"StartedAt": "2022-08-08T01:46:20.117142564Z",
"FinishedAt": "2022-08-08T01:40:02.973594886Z"
},
......略
"PATH=/usr/local/apache2/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HTTPD_PREFIX=/usr/local/apache2",
"HTTPD_VERSION=2.4.52",
"HTTPD_SHA256=0127f7dc497e9983e9c51474bed75e45607f2f870a7675a86dc90af6d572f5c9",
"HTTPD_PATCHES="
],
"Cmd": [
"/bin/sh"
],
"Image": "httpd",
"Volumes": null,
"WorkingDir": "/usr/local/apache2",
"Entrypoint": null,
"OnBuild": null,
"Labels": {},
"StopSignal": "SIGWINCH"
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "1b6c8e72c1d11dc11dc7f8d7a596b34fb60c08e8a35f5fc8a63844e00bfc8583",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {
"80/tcp": null
},
"SandboxKey": "/var/run/docker/netns/1b6c8e72c1d1",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "8729dfd638fb5ef426c9742eaf3ef31dfc815f6b4dc4d3ce27398f05ffb81216",
"Gateway": "172.17.0.1",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"MacAddress": "02:42:ac:11:00:02",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "e6fe31174426a9d263901de6bf50e8a3f88609e3ac2a578fe74e54163dd519cb",
"EndpointID": "8729dfd638fb5ef426c9742eaf3ef31dfc815f6b4dc4d3ce27398f05ffb81216",
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.2",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:02",
"DriverOpts": null
}
}
}
}
]
3.docker存储驱动
1、AUFS
AUFS(AnotherUnionFS)是一种Union FS,是文件级的存储驱动。AUFS能透明覆盖一或多个现有文件系统的层状文件系统,把多层合并成文件系统的单层表示。简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。这种文件系统可以一层一层地叠加修改文件。无论底下有多少层都是只读的,只有最上层的文件系统是可写的。当需要修改一个文件时,AUFS创建该文件的一个副本,使用CoW将文件从只读层复制到可写层进行修改,结果也保存在可写层。在Docker中,底下的只读层就是image,可写层就是Container。结构如下图所示:
2、Overlay
Overlay是Linux内核3.18后支持的,也是一种Union FS,和AUFS的多层不同的是Overlay只有两层:一个upper文件系统和一个lower文件系统,分别代表Docker的镜像层和容器层。当需要修改一个文件时,使用CoW将文件从只读的lower复制到可写的upper进行修改,结果也保存在upper层。在Docker中,底下的只读层就是image,可写层就是Container。结构如下图所示:
3、Device mapper
Device mapper是Linux内核2.6.9后支持的,提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略。前面讲的AUFS和OverlayFS都是文件级存储,而Device mapper是块级存储,所有的操作都是直接对块进行操作,而不是文件。Device mapper驱动会先在块设备上创建一个资源池,然后在资源池上创建一个带有文件系统的基本设备,所有镜像都是这个基本设备的快照,而容器则是镜像的快照。所以在容器里看到文件系统是资源池上基本设备的文件系统的快照,并不有为容器分配空间。当要写入一个新文件时,在容器的镜像内为其分配新的块并写入数据,这个叫用时分配。当要修改已有文件时,再使用CoW为容器快照分配块空间,将要修改的数据复制到在容器快照中新的块里再进行修改。Device mapper 驱动默认会创建一个100G的文件包含镜像和容器。每一个容器被限制在10G大小的卷内,可以自己配置调整。结构如下图所示:
4、Btrfs
Btrfs被称为下一代写时复制文件系统,并入Linux内核,也是文件级存储驱动,但可以像Device mapper一直接操作底层设备。Btrfs把文件系统的一部分配置为一个完整的子文件系统,称之为subvolume 。采用 subvolume,一个大的文件系统可以被划分为多个子文件系统,这些子文件系统共享底层的设备空间,在需要磁盘空间时便从底层设备中分配,类似应用程序调用 malloc()分配内存一样。为了灵活利用设备空间,Btrfs 将磁盘空间划分为多个chunk 。每个chunk可以使用不同的磁盘空间分配策略。比如某些chunk只存放metadata,某些chunk只存放数据。这种模型有很多优点,比如Btrfs支持动态添加设备。用户在系统中增加新的磁盘之后,可以使用Btrfs的命令将该设备添加到文件系统中。Btrfs把一个大的文件系统当成一个资源池,配置成多个完整的子文件系统,还可以往资源池里加新的子文件系统,而基础镜像则是子文件系统的快照,每个子镜像和容器都有自己的快照,这些快照则都是subvolume的快照。
当写入一个新文件时,在容器的快照里为其分配一个新的数据块,文件写在这个空间里,这个叫用时分配。而当要修改已有文件时,使用CoW复制分配一个新的原始数据和快照,在这个新分配的空间变更数据,变结束再更新相关的数据结构指向新子文件系统和快照,原来的原始数据和快照没有指针指向,被覆盖。
5、ZFS
ZFS 文件系统是一个革命性的全新的文件系统,它从根本上改变了文件系统的管理方式,ZFS 完全抛弃了“卷管理”,不再创建虚拟的卷,而是把所有设备集中到一个存储池中来进行管理,用“存储池”的概念来管理物理存储空间。过去,文件系统都是构建在物理设备之上的。为了管理这些物理设备,并为数据提供冗余,“卷管理”的概念提供了一个单设备的映像。而ZFS创建在虚拟的,被称为“zpools”的存储池之上。每个存储池由若干虚拟设备(virtual devices,vdevs)组成。这些虚拟设备可以是原始磁盘,也可能是一个RAID1镜像设备,或是非标准RAID等级的多磁盘组。于是zpool上的文件系统可以使用这些虚拟设备的总存储容量。
下面看一下在Docker里ZFS的使用。首先从zpool里分配一个ZFS文件系统给镜像的基础层,而其他镜像层则是这个ZFS文件系统快照的克隆,快照是只读的,而克隆是可写的,当容器启动时则在镜像的最顶层生成一个可写层。如下图所示:
当要写一个新文件时,使用按需分配,一个新的数据快从zpool里生成,新的数据写入这个块,而这个新空间存于容器(ZFS的克隆)里。当要修改一个已存在的文件时,使用写时复制,分配一个新空间并把原始数据复制到新空间完成修改。
三、存储驱动对比及适应场景
存储驱动 | 特点 | 优点 | 缺点 | 使用场景 |
---|---|---|---|---|
AUFS | 联合文件系统、未并入内核主线、文件级存储 | 作为docker的第一个存储驱动,已经有很长的历史,比较稳定,且在大量的生产中实践过,有较强的社区支持 | 有多层,在做写时复制操作时,如果文件比较大且存在比较低的层,可能会慢一些 | 大并发但少IO的场景 |
overlayFS | 联合文件系统、并入内核主线、文件级存储 | 只有两层 | 不管修改的内容大小都会复制整个文件,对大文件进行修改显示要比小文件消耗更多的时间 | 大并发但少IO的场景 |
Devicemapper | 并入内核主线、块级存储 | 块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件 | 不支持共享存储,当有多个容器读同一个文件时,需要生成多个复本,在很多容器启停的情况下可能会导致磁盘溢出 | 适合io密集的场景 |
Btrfs | 并入linux内核、文件级存储 | 可以像devicemapper一样直接操作底层设备,支持动态添加设备 | 不支持共享存储,当有多个容器读同一个文件时,需要生成多个复本 | 不适合在高密度容器的paas平台上使用 |
ZFS | 把所有设备集中到一个存储池中来进行管理 | 支持多个容器共享一个缓存块,适合内存大的环境 | COW使用碎片化问题更加严重,文件在硬盘上的物理地址会变的不再连续,顺序读会变的性能比较差 | 适合paas和高密度的场景 |
区别
1、AUFS VS Overlay
AUFS和Overlay都是联合文件系统,但AUFS有多层,而Overlay只有两层,所以在做写时复制操作时,如果文件比较大且存在比较低的层,则AUSF可能会慢一些。而且Overlay并入了linux kernel mainline,AUFS没有,所以可能会比AUFS快。但Overlay还太年轻,要谨慎在生产使用。而AUFS做为docker的第一个存储驱动,已经有很长的历史,比较的稳定,且在大量的生产中实践过,有较强的社区支持。目前开源的DC/OS指定使用Overlay。
2、Overlay VS Device mapper
Overlay是文件级存储,Device mapper是块级存储,当文件特别大而修改的内容很小,Overlay不管修改的内容大小都会复制整个文件,对大文件进行修改显示要比小文件要消耗更多的时间,而块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件,在这种场景下,显然device mapper要快一些。因为块级的是直接访问逻辑盘,适合IO密集的场景。而对于程序内部复杂,大并发但少IO的场景,Overlay的性能相对要强一些。
3、Device mapper VS Btrfs Driver VS ZFS
Device mapper和Btrfs都是直接对块操作,都不支持共享存储,表示当有多个容器读同一个文件时,需要生活多个复本,所以这种存储驱动不适合在高密度容器的PaaS平台上使用。而且在很多容器启停的情况下可能会导致磁盘溢出,造成主机不能工作。Device mapper不建议在生产使用,Btrfs在docker build可以很高效。
ZFS最初是为拥有大量内存的Salaris服务器设计的,所以在使用时对内存会有影响,适合内存大的环境。ZFS的COW使碎片化问题更加严重,对于顺序写生成的大文件,如果以后随机的对其中的一部分进行了更改,那么这个文件在硬盘上的物理地址就变得不再连续,未来的顺序读会变得性能比较差。ZFS支持多个容器共享一个缓存块,适合PaaS和高密度的用户场景。