「Docker」- 镜像与仓库

更新日期:2021年03月22日

该笔记整理介绍什么是镜像,以及镜像的管理,镜像的创建,存储,修改,共享等方面的内容。

镜像(Image)

镜像是什么?

镜像(image)由多个文件系统叠加而成:

	bootfs,引导文件系统,位于最底端,像 Linux/Unix 的引导文件系统。用户不会和该引导文件系统有什么交互。当容器启动时,容器会被移动到内存中,而 bootfs 会被卸载,以释放由 initrd 磁盘镜像使用的内存。
	rootfs,第二层文件系统,位于 bootfs 之上。rootfs 可以是多种操作系统,例如 Debain、Ubuntu 等等。rootfs 永远是只读状态,不同于传统的 Linux 引导过程(在传统的 Linux 引导过程中,rootfs 先以只读方式加载,引导结束并完成完整性检查后,会进入读写模式)。
	只读文件系统,位于rootfs之上。通过“联合挂载”(union mount)技术,在 rootfs 上挂载更多的只读文件系统。什么是联合挂载呢?联合挂载同时加载多个文件系统,但是整体上看只呈现一个文件系统。联合挂载会将各层文件系统叠加到一起,这样最终的文件系统会包含所有底层文件系统的文件和目录。

在 Docker 中,将这些文件系统称为镜像。

一个镜像可以放到另一个镜像的顶部。位于下面的镜像成为父镜像,最底层的镜像称之为基础镜像。当从镜像中启动容器时,Docker 会在镜像的最顶层加载一个读写文件系统,我们运行的程序都是在这个读写文件系统中执行的。

在 Docker 中,使用到了一种写时复制机制。在初始化时,读写层是空的。当文件系统发生修改时,所有的变化都存在与该读写层中。比如修改一个文件,该文件会被从只读层复制到读写层,在读写层进行修改。但是该文件的只读版本依旧存在,但是它被读写层中的该文件隐藏,正是因为使用了联合挂载技术。上面的这些层共同构成了镜像。

#02 镜像 - 列出

使用docker images命令来查看镜像。

当执行docker run时,如果镜像不存在,则会从仓库中自动拉去。镜像保存在/var/lib/docker/中,每个镜像都保存在Docker所采用的存储驱动目录下面,如aufs或devicemapper。而/var/lib/docker/containers/中保存了所有的容器。

IMAGE -> REPOSITORY -> REGISTRY

REGISTRY,开源,可以运行自己私有的Registry,Docker公司提供了商业版的Docker Hub(Docker Trusted Registry),可以运行在防火墙内部的产品,之前称为Docker Enterprise Hub。

镜像保存在仓库中,可以类比成Git仓库,包含了镜像,层,镜像的元数据。

使用docker pull命令拉取镜像:docker pull ubuntu:12.04

docker images的输出中,镜像是一个TAG为单位列出的,即“有一个TAG,就有一条记录”。每个TAG对组成特定镜像的一些镜像层进行标记,这样一个仓库里就可以由多个镜像。使用“冒号加TAG”的形式来指定某个仓库里的指定镜像。

一个镜像可以创建多个TAG,镜像之间使用镜像ID区分的,就是说“可以存在TAG不同,但是镜像ID相同的多个镜像,但实际上他们都是同一个镜像”。使用TAG是一个好习惯。

在Docker Hub中,有两类仓库:用户仓库;顶层仓库。用户仓库,由用户创建。顶层仓库,由Docker内部人管理。

用户仓库命名分为两部分:用户名和仓库名。如jamtur01/puppet。这些仓库由用户自己创建,未经Docker公司确认,使用时需要承担相应的风险。

顶层仓库则只有仓库名部分,这些镜像由优质的厂商提供,用户可以基于这些镜像构建自己的镜像。

在Docker 1.8后,增加了对镜像签名的功能。用于对镜像内容的安全管理。

#03 镜像 - 拉取

使用docker pull命令拉取镜像。

执行docker run命令时,如果镜像不存在,则会先拉取TAG为latest镜像。可以先使用docker pull命令拉取镜像,以减少镜像启动的时间。

#04 镜像 - 查找

使用docker search命令可以在Docker Hub上搜索镜像。

#!/bin/sh

docker search puppet

返回的信息有:仓库名、镜像描述、用户评价、是否官方、是否自动构建(是否由Docker Hub的自动构建流程创建)。

然后,可以使用docker pull命令来拉取镜像:docker pull puppet/puppetserver

最后,就可以运行这个容器了:docker run -i -t puppet/puppetserver /bin/bash

#05 镜像 - 构建

#01 创建 Docker Hub 帐号

去Docker Hub注册帐号,使用docker login命令登录,使用docker logout退出登录。

个人认证信息保存在$HOME/.dockercfg中,从Docker 1.7.0开始,保存在$HOME/.docker/config.json中。

#06 镜像 - 推送到Docker Hub

使用docker push命令将镜像推送的远程的Docker Hub,供别人使用。也可以创建私有镜像,但是这是一个付费功能。

推送到远程仓库时,需要指定用户名:docker push your_user/your_image

访问Docker Hub Quickstart | Docker Documentation查看文档。

!!!Docker Hub支持自动构建,这需要与GitHub或者BitBuket仓库关联,他会自动读取Dockerfile文件,然后进行构建,查看构建日志及输出。这里不再展开,不过不知道开源的Docker Registry是否也支持这个功能。:-)

相关杂记

# 关于父镜像与父镜像层
Is docker inspect -f '{{.Parent}}' a safe way to get the base image ID?
命令docker image inspect b7b28af77ffe --format '{{.Parent}}'输出确实是父镜像,但是这个“父镜像”是“父镜像层”,而不是Dockerfile中FROM引用的镜像层(除非你的Dockerfile中只有一个FROM指令)。

构建上下文(Build Context)

什么是构建上下文?

Docker 为 Client/Server 架构,构建命令 docker build 实际由 Docker Server 负责执行,而 Docker Clinet 只负责发送请求。

因此参与构建的文件需要发送到 Docker Server 中,这样 Docker Server 才能进行构建,这些文件就是构建上下文(Build Context)。

需要在命令行中指定 Build Context 信息,即要参与构建的文件。然后 Docker Client 会将这些文件打包并发送到 Docker Server 以进行构建。可以直接指定目录、归档文件、Git 仓库。如果使用 Git 仓库,Docker Client 会先拉取代码,然后打包上传。

潜在问题

#1 构建时间过长、内存网络资源占用较多
如果在 Build Context 中的内容占用较大空间,比如具有很多构建产出临时文件,那么全部上传到 Docker Server 时会增加网络传输时间以及消耗带宽内存资源

#2 镜像大小增加,影响启动速度
有些镜像在构建时,直接复制 Build Context 的内容(虽然应该由 Dockerfile 负责,但是通过 .dockerignore 可以避免),这将当之镜像大小增加,占用更多磁盘空间,并且还会影响启动速度。

#3 暴露敏感信息
某些内容属于敏感信息,比如在编译或加密前的明文信息,应避免直接复制到镜像中。应该始终将这些目录排除在外。

#4 构建缓存失效(潜在问题)
如果某些非必要文件经常变更,比如构建临时输出,当复制到 Docker Server 中时,那么在构建时将无法利用上次构建缓存。

解决办法

这些问题,通过控制发送到 Docker Server 中的内容,可以得到解决。这可以通过 .dockerignore 文件解决。

相关连接

Dockerfile reference

参考文献

Do not ignore .dockerignore (it’s expensive and potentially dangerous)


ToC

镜像(Image)

镜像是什么?

#02 镜像 - 列出

#03 镜像 - 拉取

#04 镜像 - 查找

#05 镜像 - 构建

#01 创建 Docker Hub 帐号

#06 镜像 - 推送到Docker Hub

相关杂记

构建上下文(Build Context)

什么是构建上下文?

潜在问题

解决办法

相关连接

参考文献