Docker 容器镜像
概念
- Docker 镜像是只读的容器模板,是Docker容器基础
- 为Docker容器提供了静态文件系统运行环境(rootfs)
- 区分: 容器是镜像的运行状态,镜像是容器的静止状态.
联合文件系统(union filesystem)
概念
是实现联合挂载技术的文件系统.可以实现在一个挂载点同时挂在多个文件系统,将挂载点的原目录与被挂载内容进行整合,使得最终可见的文件系统包含整合后的各层文件与目录.
图示
Docker Overlay2
容器文件系统中有多种存储驱动实现方式:aufs,devicemapper,overlay,overlay2等.docker目前默认使用的是overlay2.
概念
- registry: repository集合.
- repository: repository 是镜像的集合.
- image: image 是存储镜像相关的元数据,包括镜像的架构,镜像默认配置信息,镜像的容器配置信息等等。它是“逻辑”上的概念,并无物理上的镜像文件与之对应。
- layer: layer(镜像层) 组成了镜像,单个 layer 可以被多个镜像共享。
overlay2是由merged,lowerdir,upperdir,workdir组成.
- lowerdir: 镜像只读层,镜像层.
- upperdir: 容器读写层, 反应容器的读写操作.
- workdir: overlayfs的内部层, 用于实现从只读到读写层的copy_up操作.
- merge: 容器内作为同一视图联合挂载点的目录.
docker 查看存储驱动
docker info | grep overlay
Storage Driver: overlay2
Network: bridge host macvlan null overlay
WARNING: You're not using the default seccomp profile
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
docker 镜像存储位置
入口: /var/lib/docker/image/overlay2/
ls /var/lib/docker/image/overlay2/
distribution imagedb layerdb repositories.json
- repositories.json 记录了repo与镜像id的映射关系
- imagedb 记录了镜像架构,操作系统,构建镜像的容器 ID 和配置以及 rootfs 等信息
- layerdb 记录了每层镜像层的元数据。
docker 镜像分层文件查找方法
以redis为例.
通过下载镜像可以得知该镜像有六层.
# docker pull redis Using default tag: latest Trying to pull repository docker.io/library/redis ... latest: Pulling from docker.io/library/redis 3f4ca61aafcd: Already exists c3775af77098: Pull complete fa7c5c7e501c: Pull complete 4059b4d2a3ce: Pull complete 6bc523bfb16a: Pull complete 20bf15ad3c24: Pull complete Digest: sha256:8184cfe57f205ab34c62bd0e9552dffeb885d2a7f82ce4295c0df344cb6f0007 Status: Downloaded newer image for docker.io/redis:latest
通过docker images 来查看redis的短的image id
docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker.io/redis latest 0256c63af7db 2 weeks ago 117 MB
进入docker 镜像存储路径,通过短id查找完整的长id.再通过长id可以在imagedb中找到镜像的元数据.
cd /var/lib/docker/image/overlay2 more repositories.json { "Repositories": { "docker.io/redis": { "docker.io/redis:latest": "sha256:0256c63af7dbecdb8783029521d1bcaf02d194d1451ad8cddc193352b6caedd0", "docker.io/redis@sha256:8184cfe57f205ab34c62bd0e9552dffeb885d2a7f82ce4295c0df344cb6f0007": "sha256:0256c63af7dbecdb8783029521d1bcaf02d194d1451ad8cddc193352b6caedd0" } }
通过结构可以看到redis:latest的长id是
0256c63af7dbecdb8783029521d1bcaf02d194d1451ad8cddc193352b6caedd0
.在imagedb/content/sha256/文件夹下找到同名文件.查看信息.# more /var/lib/docker/image/overlay2/imagedb/content/sha256/0256c63af7dbecdb8783029521d1bcaf02d194d1451ad8cddc193352b6caedd0 ... "rootfs": { "type": "layers", "diff_ids": [ "sha256:8a70d251b65364698f195f5a0b424e0d67de81307b79afbe662abd797068a069", "sha256:f425a4ae4b4e5325d4462e5d617a58784c445405e7435c5dc9eef7f3fc177075", "sha256:25573b19d1564d20fb6eafe2cc68eaebea262b2add86bafe0aace6a8d9273ccb", "sha256:a2e52a99a99fef7d419c5ce7a3e23a8e60b5b51a03dc3f1417347e12bd21c051", "sha256:807cec72b26c8db57733dd867031b00d113d7b834f4b0601ee41ec4fa63b894b", "sha256:2ffda52105d36ec4cdbd24bd17e698e26d19bcba072238ec519a0381795108f5" ] } ...
在json文件最下方有一块
rootfs
目录.其中的diff_ids就记录着分层的数据的id号.这六层分别对应镜像的六层.自上而下,引射着容器的底层到顶层.该id的作用是找到镜像层的镜像.根据chainId(底层diff_id即是chainId)可以在layer/sha256/(/var/lib/docker/image/overlay2/layerdb/sha256)目录下同名文件夹.里面存放了文件路径指向.
# pwd /var/lib/docker/image/overlay2/layerdb/sha256/8a70d251b65364698f195f5a0b424e0d67de81307b79afbe662abd797068a069 # ls cache-id diff size tar-split.json.gz # more cache-id 7a335dd840b2cd775af90da49047a068eae5613484aec5e50b0c0c0018882a30 # more diff sha256:8a70d251b65364698f195f5a0b424e0d67de81307b79afbe662abd797068a069
文件夹里有个cache-id的文件,里面存放了cache-id,我们可以通过cache-id:
7a335dd840b2cd775af90da49047a068eae5613484aec5e50b0c0c0018882a30
去/var/lib/docker/overlay2/路径下查找具体文件内容.根据查到的cache-id去/var/lib/docker/overlay2/查看文件夹信息
# cd /var/lib/docker/overlay2/7a335dd840b2cd775af90da49047a068eae5613484aec5e50b0c0c0018882a30/ # ls committed diff link # more link KUNIQVGBWFZHEASENNFRPT46MN # ls diff bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
- link: 存放镜像层对应的短id
- diff: 存放镜像层的原始文件
- lower: 上一层镜像层的短id.
找到镜像底层后,需要通过公式来计算出下一层的chainId,然后循环执行4.5.6步骤.依次逐层查找文件信息.
# echo -n "sha256:8a70d251b65364698f195f5a0b424e0d67de81307b79afbe662abd797068a069 sha256:f425a4ae4b4e5325d4462e5d617a58784c445405e7435c5dc9eef7f3fc177075" | sha256sum - 555a4acbdb957b13ce0e5320a106dba38014f72d84dcab6ca989ce7ab505308f - # ls /var/lib/docker/image/overlay2/layerdb/sha256/555a4acbdb957b13ce0e5320a106dba38014f72d84dcab6ca989ce7ab505308f cache-id diff parent size tar-split.json.gz # more parent sha256:8a70d251b65364698f195f5a0b424e0d67de81307b79afbe662abd797068a069
- cache-id: 当前容器层的缓存id.
- diff: 当前容器层的diffid.
- parent: 上一层的cache-id.
docker 引入了内容寻址机制,该机制会根据文件内容来索引镜像和镜像层。docker 利用 rootfs 中的 diff_id 计算出内容寻址的 chainID,通过 chainID 获取 layer 相关信息,最终索引到镜像层文件内容。
对于最底层镜像层其 diff_id 即是 chainID。因此我们可以查找到它的文件内容。除最底层外,chainID 需通过公式 chainID(n) = SHA256(chain(n-1) diffID(n)) 计算得到.
eg:
"sha256:8a70d251b65364698f195f5a0b424e0d67de81307b79afbe662abd797068a069 sha256:f425a4ae4b4e5325d4462e5d617a58784c445405e7435c5dc9eef7f3fc177075" | sha256sum -
镜像与容器
启动一个docker 容器后,docker会mount一个overlay 的联合文件系统到容器内。这个文件系统由三层组成:
lowerdir,upperdir,workdir. 对外统一用merge目录展示.
# docker run -d --privileged=true -p 6379:6379 --name redis redis redis-server --appendonly yes
f0689a0be678fa161374cad4da978369cf2eabb30761964cb9419349c882e62f
# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f0689a0be678 redis "docker-entrypoint..." 3 seconds ago Up 2 seconds 0.0.0.0:6379->6379/tcp redis
# mount |grep overlay
/dev/vda1 on /var/lib/docker/overlay2 type ext4 (rw,relatime,data=ordered)
overlay on /var/lib/docker/overlay2/41142d0283bc4c18abf7229830e7bd6ee37aa438321d0ca910341d2c4951ee42/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/WYS5YBW3HPSLD3LOZ7ENZAQH5K:
/var/lib/docker/overlay2/l/AUC35C4AV2R55HFCPAII4ULPJB:
/var/lib/docker/overlay2/l/3FWL7ZCAH2K47UI6SUERCDFXUO:
/var/lib/docker/overlay2/l/W3LTS35MU3HRPPO6D22VKQ4MMR:
/var/lib/docker/overlay2/l/YMGPW6T3736D2VPYWA5LCM2X3E:
/var/lib/docker/overlay2/l/GUIPEFGV4TLNAML2MR42VREWZD:
/var/lib/docker/overlay2/l/KUNIQVGBWFZHEASENNFRPT46MN,upperdir=/var/lib/docker/overlay2/41142d0283bc4c18abf7229830e7bd6ee37aa438321d0ca910341d2c4951ee42/diff,workdir=/var/lib/docker/overlay2/41142d0283bc4c18abf7229830e7bd6ee37aa438321d0ca910341d2c4951ee42/work)
细心点可以发现容器的lowerdir镜像层有七层目录.而我们的镜像缺只有六层目录.
/var/lib/docker/overlay2/l/AUC35C4AV2R55HFCPAII4ULPJB: /var/lib/docker/overlay2/l/3FWL7ZCAH2K47UI6SUERCDFXUO: /var/lib/docker/overlay2/l/W3LTS35MU3HRPPO6D22VKQ4MMR: /var/lib/docker/overlay2/l/YMGPW6T3736D2VPYWA5LCM2X3E: /var/lib/docker/overlay2/l/GUIPEFGV4TLNAML2MR42VREWZD: /var/lib/docker/overlay2/l/KUNIQVGBWFZHEASENNFRPT46MN:
这六层目录从下到上分别对应镜像的6层镜像层文件内容,它们分别映射到镜像层的 diff 目录。
/var/lib/docker/overlay2/l/WYS5YBW3HPSLD3LOZ7ENZAQH5K
映射的是容器的初始化层 init,该层内容是和容器配置相关的文件内容,它是只读的,从系统的软连接来看比较明显,其软连接的文件路径上的chainId带有init字眼.
# ll -l /var/lib/docker/overlay2/l/
total 32
lrwxrwxrwx 1 root root 72 Jan 8 10:40 3FWL7ZCAH2K47UI6SUERCDFXUO -> ../2784b462764e1047c9812f8179d8dc716484d3fc29fc56254c2c4c28464dd210/diff
lrwxrwxrwx 1 root root 72 Jan 8 13:16 6NZOOWLAX6OVSSWWC2XVPPWNZM -> ../41142d0283bc4c18abf7229830e7bd6ee37aa438321d0ca910341d2c4951ee42/diff
lrwxrwxrwx 1 root root 72 Jan 8 10:40 AUC35C4AV2R55HFCPAII4ULPJB -> ../23f995bf67090d39d2244fba02a92c09238b397f6ed03e922b851c524ec2415d/diff
lrwxrwxrwx 1 root root 72 Jan 8 10:40 GUIPEFGV4TLNAML2MR42VREWZD -> ../1db6ab3ea318bc2ded84f7b0ebee89be34f56b08973e48cfef0e2a0004fb99a3/diff
lrwxrwxrwx 1 root root 72 Dec 30 10:11 KUNIQVGBWFZHEASENNFRPT46MN -> ../7a335dd840b2cd775af90da49047a068eae5613484aec5e50b0c0c0018882a30/diff
lrwxrwxrwx 1 root root 72 Jan 8 10:40 W3LTS35MU3HRPPO6D22VKQ4MMR -> ../36834a7e9756d599a9389d683d2a08912598905b50ad62ac9b5eb8cdfde8a0c0/diff
lrwxrwxrwx 1 root root 77 Jan 8 13:16 WYS5YBW3HPSLD3LOZ7ENZAQH5K -> ../41142d0283bc4c18abf7229830e7bd6ee37aa438321d0ca910341d2c4951ee42-init/diff
lrwxrwxrwx 1 root root 72 Jan 8 10:40 YMGPW6T3736D2VPYWA5LCM2X3E -> ../dfa70e9006936534510da1d5380f9828a5fad83614d3f8d1ba2fded3d5b482a4/diff
容器镜像的操作命令
docker commit
docker 通过commit与build操作实现镜像的构建.
两者的区别:
- docker commit: 将容器提交为一个镜像 . eg:
docker commit {容器id}
- docker build: 在一个镜像的基础上构建镜像.多与dockerfile配合使用. eg :
docker build Dockerfile
docker save
导出容器或镜像,方便分享,可以将多个容器或镜像同时保存在一个tar包里.
当然,虽然可以导出容器,但本质是导出容器所依赖的镜像.
使用场景: 使用了docker-compose 编排的多个镜像组合,且部署的客户服务器并不能连外网.这时需要通过将镜像save成文件,拷贝到目标机器上后使用load载入.
格式: docker save -o {输出文件路径} {name:tag ...}
-o 输出到文件.
eg: docker save -o redis.tar redis:v1
补充: 压缩导出
将导出的镜像再通过gzip压缩.使之更小更容易传输.压缩后的文件大小为原来的三分之一左右.
格式:docker save {name:tag} | gzip > {输出文件路径}
eg: docker save redis:v1 | gzip > redis2.tar
docker load
把他人分享的容器镜像导入到本地镜像中,这通常是容器镜像分发方式之一, 与save配套使用.
格式: docker load -i {镜像文件路径}
–input, -i : 指定文件输入
-quiet, -q : 精简输出信息.
docker export
把正在运行的容器导出.应用场景主要是用来制作基础镜像.
使用场景: 制作符合自生环境的基础镜像.比如从一个centos镜像启动一个容器后,安装了一些软件与设置后,使用export保存成一个基础镜像,供以后使用.
格式: docker export -o {输出文件路径} {容器名称/容器id}
eg: docker export -o redis.tar redis
docker import
导入使用docker export导入的容器做为本地容器镜像。
格式: docker import {镜像文件路径} {生成的镜像名称/生成的镜像tag}
eg: docker import redis.tar redis:v1
区别
- docker save 保存的是镜像, docker export 可以保存容器.
- docker load 载入镜像包, docker import 载入容器包,但都将载入成镜像.
- docker load 不能对载入的镜像重命名, docker import 可以为镜像重命名.
- docker load 不能载入export出来的容器包.docker import 可以载入load出来的镜像包,但启动失败.
容器镜像仓库的使用命令
docker tag
用于给镜像打标签.生成不通版本的镜像,为上传做支撑.
格式: docker tag {目标镜像}:[tag] {生成镜像}:[tag]
eg: docker tag redis:latest redis:v1
docker push
上传容器镜像至远程仓库.
格式: docker push {镜像名称}:[tag]
eg: docker push redis:v1
docker pull
下载容器镜像.
使用场景: 在专门的打包服务器/或本地将镜像更新上传后,在其生产环境上下载并启动,多实例的场景就无需多次上传.且方便回滚.
格式: docker pull {镜像名称}:[tag]
eg: docker pull redis:v1
docker login
登入
docker logout
登出