1:镜像的分层结构
docker 镜像共享宿主机的kernel,base镜像提供的是最小的Linux发行版,同一docker主机支持运行多种Linux发行版。
比如我们在宿主机上拉取一个busybox镜像,以交互式运行一个容器,执行uname -r
命令,可以发现在容器中的操作系统和内核版本与宿主机相同。
一个docker镜像由多个只读的镜像层组成,然后运行的容器会在这个docker的镜像上面多加一层可写的容器层,docker从上往下依次查找文件,容器层以下所有镜像层都是只读的,任何对容器的更改都只存在此容器层,并不会对镜像本身进行修改。同时,采用分层结构的最大好处就是:共享资源。
一个镜像最多127层。
比如 ,我们拉取一个busybox镜像,执行docker history busybox
查看这个镜像的创建历史,可以看到一共有两层。
我们以交互式运行一个busybox的容器,在test容器中创建几个新的文件后,使用docker commit -m "v1" test demo:v1
命令,从容器创建一个新的镜像demo:v1。
对比demo:v1和busybody的创建历史,demo:v1在前两层的基础上新建了一层镜像。
使用docker rmi demo:v1
命令删除demo:v1镜像,发现只删除了最上面的一层,因为busybox还在使用最基础的两层镜像。
2:镜像的构建
根据以上实验,我们可以看到使用docker commit
创建新镜像,分为以下三步:
- 运行容器
- 修改容器
- 将容器保存为新的镜像
这样做的缺点是:
- 效率低、可重复性弱、容易出错
- 使用者无法对镜像进行审计,存在安全隐患。
因为,我们接下来可以通过创建dockerfile来构造镜像。
3:dockerfile的创建
Dockerfile 是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。每条指令都必须为大写字母,切后面要跟随一个参数。Dockerfile中的指令会按照顺序从上到下执行,所以应该根据需要合理安排指令的顺序。每条指令都会创建一个新的镜像层并对镜像进行提交。Docker大体上按照如下流程执行Dockerfile中的指令。
Dockerfile构建构过程:
- 从基础镜像运行一个容器
- 执行一条指令,对容器做出修改
- 执行类似docker commit 的操作,提交一个新的镜像层
- 再基于刚提交的镜像运行一个新容器
- 执行Dockerfile中的下一条指令,直至所有指令执行完毕
下面我们通过构造一个dockerfile的实例来了解它的工作原理。
创建一个目录,在dockerfile目录下新建一个dockerfile文件,然后编写一个完整的dockerfile,内容如下;
FROM busybox ##指定一个基础镜像busybox,本地没有的话,会从网络上pull
RUN echo "hello world">testfile ##构建镜像时执行 echo "hello world">testfile 这个命令
使用dockerfile创建一个新的镜像demo:v3 (docker build 创建镜像的命令, -f 指定dockerfile文件,镜像的名字及标签 ,后面跟的是路径," . "表示当前路径);
查看demo:v3的创建历史,我们可以看到镜像内的所有操作,便于审计;
同时,dockerfile也是可以缓存,我们在demo:v3的dockerfile上增加一条指令;
FROM busybox
RUN echo "hello world">testfile
RUN rm -fr testfile
使用新的dockerfile构建demo:v4镜像,我们可以看到前面的指令是使用了缓存的,更高效的利用系统资源。
4: 镜像加速
国内从 DockerHub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
- 科大镜像:https://docker.mirrors.ustc.edu.cn/
- 网易:https://hub-mirror.c.163.com/
- 阿里云:https://<你的ID>.mirror.aliyuncs.com
- 七牛云加速器:https://reg-mirror.qiniu.com
当配置某一个加速器地址之后,若发现拉取不到镜像,请切换到另一个加速器地址。国内各大云服务商均提供了 Docker 镜像加速服务,建议根据运行 Docker 的云平台选择对应的镜像加速服务。
阿里云镜像获取地址:登陆后,左侧菜单选中镜像加速器就可以看到你的专属地址了:
对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件):
{"registry-mirrors":["https://xxxx.aliyuncs.com/"]}
之后重新启动服务:
systemctl daemon-reload
systemctl restart docker