Docker中的镜像是其重要组成部分,下面就来说说Docker镜像的工作原理。
Docker镜像的分层结构
每当使用docker pull的时候,我们都会看到docker会依次下载不同的层(layer),比如:
xxxxxxxxxx: Pulling fs layer
UnionFS联合文件系统
UnionFS是Docker镜像的基础,它是一种轻量级的分层文件系统。通过UnionFS,可以将对文件系统的修改作为一次提交(Commit)来一层层的叠加。UnionFS会将不同目录挂载到同一个虚拟文件系统中。
分层的镜像系统就有点像OO中的继承,比如在Java中,所有类的父类是Objcet,其余类都是在其基础上不断继承得到的。
之所以叫做分层文件系统,是因为Docker会同时加载多个文件系统。但从外部看来,只能看到一个多层文件叠加之后的结果,也就是同一个文件系统。
这个分层文件系统包括:
- bootfs:这是Linux系统的起点,通过这个bootfs,可以加载Linux内核,不同Linux发行版本可以共用bootfs
- rootfs:它包含了Linux中最基本的目录和文件,比如:/dev, /bin, /etc等
在使用某个具体镜像时,可以根据需要选择不同的镜像进行组合,达到最大程度的重用。
如果多个镜像都需要使用一个基本镜像,Docker只需要存储一份就可以了,这样能够最大程度的节省磁盘空间。
Docker中的镜像都是只读的,只有容器才是可写的。
可以使用以下命令查看docker镜像的不同层:
docker history redis
使用docker commit增强容器功能并生成新的镜像
在默认的Redis镜像中是不包含curl命令的,下面演示如何创建一个新的docker镜像:
首先启动一个redis容器:
docker run redis
以交互模式进入容器:
docker exec -it CONTAINER_ID /bin/bash
安装curl:
apt-get update
apt-get -y install curl
在host上运行如下命令提交容器副本以生成新镜像:
docker commit -m "add curl" -a=lcoding cbca7ef86c63 lcoding/redis:7.0.4
再查看docker镜像,就能看到新创建的镜像了:
docker images
其运行结果:
REPOSITORY TAG IMAGE ID CREATED SIZE
lcoding/redis 7.0.4 0c6b694c84ec 3 seconds ago 141MB
redis latest dc7b40a0b05d 2 weeks ago 117MB
hello-world latest feb5d9fea6a5 11 months ago 13.3kB
这个时候如果使用新创建的镜像启动容器就能看到curl已经能够直接使用了:
docker run -it lcoding/redis:7.0.4 bash
生成自己Docker镜像的两种方式
- 通过前面提到的docker commit在现有docker镜像的基础上创建新镜像
- 通过Dockerfile创建新镜像
搭建自己的私有Docker镜像仓库
拉取并启动registry容器:
docker pull registry
docker run -d -p 5000:5000 -v /home/lcoding/myregistry:/tmp/registry --privileged=true registry
回到host进行测试:
curl -XGET http://127.0.0.1:5000/v2/_catalog
添加新tag:
docker tag lcoding/redis:7.0.4 127.0.0.1:5000/lcoding/redis:7.0.4
由于在默认方式下,docker不支持http方式推送到仓库,因此需要添加/更新文件:/etc/docker/daemon.json:
{
"insecure-registries": ["127.0.0.1:5000"]
}
将自己的镜像推送到私有镜像仓库:
docker push 127.0.0.1:5000/lcoding/redis:7.0.4
再次查看私有镜像仓库:
curl -XGET http://127.0.0.1:5000/v2/_catalog
其运行结果为:
{"repositories":["lcoding/redis"]}
删除本地的镜像后,重新从私有镜像服务器拉取:
docker pull 127.0.0.1:5000/lcoding/redis:7.0.4