Docker镜像是由文件系统叠加而成。最底端是一个文件引导系统,即bootfs。Docker用户不会与引导文件系统有直接的交互。Docker镜像的第二层是root文件系统rootfs,通常是一种或多种操作系统,例如ubuntu等。在Docker中,文件系统永远都是只读的,在每次修改时,都是进行拷贝叠加从而形成最终的文件系统。Docker称这样的文件为镜像。一个镜像可以迭代在另一个镜像的顶部。位于下方的镜像称之为父镜像,最底层的镜像称之为基础镜像。最后,当从一个镜像启动容器时,Docker会在最顶层加载一个读写文件系统作为容器。
Docker的这种机制我们称之为写时复制。
当删除容器时,这个容器自有的“可写”层会一起被删除。
大致介绍了Docker镜像是什么,我们来看看Docker镜像中有哪些内容?
Docker 镜像存储在镜像仓库服务(Image Registry)当中
Docker 客户端的镜像仓库服务是可配置的,默认使用 Docker Hub
镜像仓库服务包含多个镜像仓库(Image Repository)。同样,一个镜像仓库中可以包含多个镜像
下图展示了包含 3 个镜像仓库的镜像仓库服务,其中每个镜像仓库都包含一个或多个镜像
docker提供了多种存储驱动来实现不同的方式存储镜像,下面是常用的几种存储驱动
AUFS(AnotherUnionFS)是一种Union FS,是文件级的存储驱动。AUFS是一个能透明覆盖一个或多个现有文件系统的层状文件系统,把多层合并成文件系统的单层表示。简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。这种文件系统可以一层一层地叠加修改文件。无论底下有多少层都是只读的,只有最上层的文件系统是可写的。当需要修改一个文件时,AUFS创建该文件的一个副本,使用COW(copy-on-write式,写时复制)将文件从只读层复制到可写层进行修改,结果也保存在可写层。在Docker中,底下的只读层就是image,可写层就是Container。ubuntu上的docker默认使用的就是AUFS。
Overlay是Linux内核3.18后支持的,也是一种Union FS,和AUFS的多层不同的是Overlay只有两层:一个upper文件系统和一个lower文件系统,分别代表Docker的镜像层和容器层。当需要修改一个文件时,使用COW将文件从只读的lower复制到可写的upper进行修改,结果也保存在upper层。在Docker中,底下的只读层就是image,可写层就是Container。目前最新的OverlayFS为Overlay2。
AUFS和Overlay都是联合文件系统,但AUFS有多层,而Overlay只有两层,所以在做写时复制操作时,如果文件比较大且存在比较低的层,则AUSF会慢一些。而且Overlay并入了linux kernel mainline,AUFS没有。目前AUFS已基本被淘汰。
Device mapper是Linux内核2.6.9后支持的,提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略。AUFS和OverlayFS都是文件级存储,而Device mapper是块级存储,所有的操作都是直接对块进行操作,而不是文件。Device mapper驱动会先在块设备上创建一个资源池,然后在资源池上创建一个带有文件系统的基本设备,所有镜像都是这个基本设备的快照,而容器则是镜像的快照。所以在容器里看到文件系统是资源池上基本设备的文件系统的快照,并没有为容器分配空间。当要写入一个新文件时,在容器的镜像内为其分配新的块并写入数据,这个叫用时分配。当要修改已有文件时,再使用CoW为容器快照分配块空间,将要修改的数据复制到在容器快照中新的块里再进行修改。
OverlayFS是文件级存储,Device mapper是块级存储,当文件特别大而修改的内容很小,Overlay不管修改的内容大小都会复制整个文件,对大文件进行修改显然要比小文件要消耗更多的时间,而块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件,在这种场景下,显然device mapper要快一些。因为块级的是直接访问逻辑盘,适合IO密集的场景。而对于程序内部复杂,大并发但少IO的场景,Overlay的性能相对要强一些。
BTRFS(通常念成Butter FS),由Oracle于2007年宣布并进行中的COW(copy-on-write式)文件系统。目标是取代Linux的ext3文件系统,改善ext3的限制,特别是单一文件大小的限制,总文件系统大小限制以及加入文件校验和特性。加入ext3/4未支持的一些功能,例如可写的磁盘快照(snapshots),以及支持递归的快照(snapshots of snapshots),内建磁盘阵列(RAID)支持,支持子卷(Subvolumes)的概念,允许在线调整文件系统大小。
VFS(virtual File System)的作用就是采用标准的Unix系统调用读写位于不同物理介质上的不同文件系统,即为各类文件系统提供了一个统一的操作界面和应用编程接口。VFS是一个可以让open()、read()、write()等系统调用不用关心底层的存储介质和文件系统类型就可以工作的粘合层。
启动容器时,docker daemon会试图从本地获取相关的镜像,本地镜像不存在时,其将从Registry中下载该镜像并保存到本地。
Registry用于保存docker镜像,包括镜像的层次结构和元数据。用户可以自建Registry,亦可使用官方的Docker Hub。
docker registry的分类
docker registry的组成
Repository
Index
Docker Hub是一个基于云的注册服务,它允许你链接到代码库,构建和测试你的镜像,存储手动推送的镜像,以及Docker Cloud的链接,这样你就可以把镜像部署到你的主机上。
它为容器镜像发现、分发和变更管理、用户和团队协作以及整个开发管道中的工作流自动化提供了集中的资源。
Docker Hub提供以下主要特性
多数情况下,我们做镜像是基于别人已存在的某个基础镜像来实现的,我们把它称为base image。比如一个纯净版的最小化的centos、ubuntu或debian
镜像的生成途径
根据容器的更改创建一个新映像
语法:docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
Options | Default | Description |
---|---|---|
—author, -a | 指定作者 | |
-c, --change list | 对创建的镜像应用Dockerfile指令 | |
-m, --message string | 提交消息 | |
-p, --pause | true | 提交期间暂停容器 |
要从远程Registry(如您自己的Docker Registry)获取Docker镜像并将其添加到您的本地系统,需要使用docker pull命令。我这里使用从Docker hub上拉取镜像
语法:docker pull <registry>[:<port>]/[<namespace>/]<name>:<tag> [root@Docker ~]# docker pull centos Using default tag: latest latest: Pulling from library/centos a1d0c7532777: Pull complete Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177 Status: Downloaded newer image for centos:latest docker.io/library/centos:latest
//运行容器,--name指定容器的名字,指定tty的类型,默认是sh [root@Docker ~]# docker run --name nginx -itd 5d0da3dc9764 /bin/bash 95f278ee0a241d28280f967c3878ee45d40b56f50d2a7bab3d2f25e511ed8b39
首先使用docker cp将nginx软件包传到容器中 [root@Docker ~]# docker cp /usr/src/nginx-1.20.2.tar.gz 95f278ee0a24:/usr/src/ [root@Docker ~]# docker exec -it 95f278ee0a24 /bin/bash [root@95f278ee0a24 /]# ls /usr/src/ debug kernels nginx-1.20.2.tar.gz //创建系统用户nginx [root@95f278ee0a24 /]# useradd -r -m -s /sbin/nologin nginx [root@95f278ee0a24 /]# id nginx uid=998(nginx) gid=996(nginx) groups=996(nginx) //安装依赖包和编译工具 [root@95f278ee0a24 /]# yum -y install pcre-devel openssl openssl-devel gd-devel gcc gcc-c++ make [root@95f278ee0a24 /]# yum -y groups mark install 'Development Tools' //创建日志存放目录 [root@95f278ee0a24 /]# mkdir -p /var/log/nginx [root@95f278ee0a24 /]# chown -R nginx.nginx /var/log/nginx/ //编译安装nginx [root@95f278ee0a24 ~]# cd /usr/src/ [root@95f278ee0a24 src]# ls debug kernels nginx-1.20.2.tar.gz [root@95f278ee0a24 src]# tar xf nginx-1.20.2.tar.gz [root@95f278ee0a24 src]# cd nginx-1.20.2 [root@95f278ee0a24 nginx-1.20.2]# ./configure \ --prefix=/usr/local/nginx \ --user=nginx \ --group=nginx \ --with-debug \ --with-http_ssl_module \ --with-http_realip_module \ --with-http_image_filter_module \ --with-http_gunzip_module \ --with-http_gzip_static_module \ --with-http_stub_status_module \ --http-log-path=ar/loginx/access.log \ --error-log-path=ar/loginx/error.log [root@95f278ee0a24 nginx-1.20.2]# make && make install [root@95f278ee0a24 ~]# ls /usr/local/ bin etc games include lib lib64 libexec nginx sbin share src //配置环境变量 [root@95f278ee0a24 ~]# echo 'export PATH=/usr/local/nginx/sbin:$PATH' > /etc/profile.d/nginx.sh [root@95f278ee0a24 ~]# source /etc/profile.d/nginx.sh //启动nginx [root@95f278ee0a24 ~]# nginx [root@95f278ee0a24 ~]# ss -antl State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 128 0.0.0.0:80 0.0.0.0:* //创建web测试内容 [root@6476d875f2b4 ~]# echo Nginx to work > /usr/local/nginx/html/index.html [root@6476d875f2b4 ~]# cat /usr/local/nginx/html/index.html Nginx to work //在主机上访问 [root@Docker ~]# curl 172.17.0.2 Nginx to work //此时因为docker容器中的端口没有映射所有在浏览器上访问不了
在创建镜像时,我们不能关闭容器,必须使其处于运行状态,所以我们必须要另起一个终端,然后执行
默认情况下是启动的是sh进程,但我们是要启动一个你滚下,所以我们要在创建镜像时将容器默认启动的进程设为nginx,这样一来我们就可以通过新生成的镜像来快速构建一个简单的nginx服务
[root@Docker ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9408c7af4b18 286644a4d832 "/bin/bash" 13 seconds ago Up 12 seconds nginx [root@Docker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos latest 5d0da3dc9764 2 months ago 231MB [root@Docker ~]# docker commit -a '172.168.25.147@qq.com' -c 'CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"]' -p 9408c7af4b18 zhaojie10/nginx:v1.20.2 sha256:6f5b02e61ad49adeae10eabab8aa49082c8721eeb731288d0d24d7aa96a5d823 [root@Docker ~]# [root@Docker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE zhaojie10/nginx v1.20.2 6f5b02e61ad4 6 seconds ago 549MB centos latest 5d0da3dc9764 2 months ago 231MB
[root@Docker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE zhaojie10/nginx v1.20.2 6f5b02e61ad4 About a minute ago 549MB centos latest 5d0da3dc9764 2 months ago 231MB [root@Docker ~]# docker run -dit --name nginx -p 80:80 6f5b02e61ad4 d9526158bdc16fb347d6c5a86a10f119ca138aa65c72798c77ac8d0984f36057 [root@Docker ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d9526158bdc1 6f5b02e61ad4 "/usr/local/nginx/sb…" 4 seconds ago Up 2 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp nginx [root@Docker ~]# docker exec -it d9526158bdc1 /bin/bash [root@d9526158bdc1 /]# ss -antl State Recv-Q Send-Q Local Address:Port Peer Address:Port Process LISTEN 0 128 0.0.0.0:80 0.0.0.0:* //此时容器启动nginx服务也会启动
此时可以到浏览器上访问
此时要注意的是,我们的镜像名为zhaojie10/nginx,所以我们要在Docker Hub上创建一个名为zhaojie10/nginx的仓库,然后再将我们做好的镜像push上去
//登录docker hub上传镜像 [root@Docker ~]# docker login Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username: zhaojie10 Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded [root@Docker ~]# docker push zhaojie10/nginx:v2.0 The push refers to repository [docker.io/zhaojie10/nginx] 54bfe1169208: Pushed 27c919eb23ea: Pushed 74ddd0ec08fa: Mounted from library/centos v2.0: digest: sha256:c62e7b10c8c6fcefa9d7248d466b278abc70cc3be9dc9a1a730400d2702f06b6 size: 950
//把刚刚上传的容器拉取到本地,验证刚刚的操作是否成功 [root@Docker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE zhaojie10/nginx v1.0 81a4a6c1034d About an hour ago 549MB centos latest 5d0da3dc9764 2 months ago 231MB
我们可以在主机1上push镜像到镜像仓库中,然后在主机2上pull把镜像拉下来使用,这种方式就显得比较麻烦,假如我只是测试用的,在一台主机上做好镜像后在另一台主机上跑一下就行了,没必要推到仓库上然后又把它拉到本地来。
此时我们可以在已有镜像的基础上把镜像打包成一个压缩文件,然后拷贝到另一台主机上将其导入,这就是镜像的导入和导出功能。
docker
中我们使用docker save
进行导出,使用docker load
进行导入
在已生成镜像的主机上执行docker save导出镜像
[root@Docker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE zhaojie10/nginx v1.20.2 6f5b02e61ad4 7 minutes ago 549MB centos latest 5d0da3dc9764 2 months ago 231MB [root@Docker ~]# docker save -o nginximages.gz zhaojie10/nginx:v1.20.2 [root@Docker ~]# ls anaconda-ks.cfg nginximages.gz
在另一台没有镜像的主机上执行docker load导入镜像
[root@Docker ~]# ls anaconda-ks.cfg nginximages.gz [root@Docker ~]# docker load -i nginximages.gz Loaded image: zhaojie10/nginx:v1.20.2 [root@Docker ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE zhaojie10/nginx v1.20.2 6f5b02e61ad4 11 minutes ago 549MB