简单地理解,Docker镜像就是一个Linux的文件系统(Root FileSystem),这个文件系统里面包含可以运行在Linux内核的程序以及相应的数据。
谈到这里,我们可能需要先补充一点与Linux操作系统相关的知识:
一般而言, Linux分为两个部分:Linux内核(Linux Kernel)与用户空间,而真正的Linux操作系统,是指Linux内核,我们常用的Ubuntu,Centos等操作系统其实是不同厂商在Linux内核基础上添加自己的软件与工具集(tools)形成的发布版本(Linux Distribution)。
因此,我们也可以把镜像看成是上面所说的用户空间,当Docker通过镜像创建一个容器时,就是将镜像定义好的用户空间作为独立隔离的进程运行在宿主机的Linux内核之上。
这里要强调一下镜像的两个特征:
镜像是分层(Layer)的:即一个镜像可以多个中间层组成,多个镜像可以共享同一中间层,我们也可以通过在镜像添加多一层来生成一个新的镜像。
镜像是只读的(read-only):镜像在构建完成之后,便不可以再修改,而上面我们所说的添加一层构建新的镜像,这中间实际是通过创建一个临时的容器,在容器上增加或删除文件,从而形成新的镜像,因为容器是可以动态改变的。
通过下面的示意图,我可以更好地理解Docker镜像与Linux的关系:
操作镜像的命令
Docker中与镜像操作相关的命令都在docker image这条子命令下,通过docker image --help这条命令,可以看到docker image子命令的详细文档,如下:
Usage: docker image COMMAND
Manage images
Commands:
build Build an image from a Dockerfile(构建镜像的命令)
history Show the history of an image(显示镜像构建历史过程)
import Import the contents from a tarball to create a filesystem image(导入一个由容器导出的镜像)
inspect Display detailed information on one or more images(显示一个镜像的详细信息)
load Load an image from a tar archive or STDIN(从一个文件或标准输入流中导入镜像)
ls List images(查看镜像列表)
prune Remove unused images(删除虚悬镜像)
pull Pull an image or a repository from a registry(从仓库拉取镜像)
push Push an image or a repository to a registry(推送镜像到仓库)
rm Remove one or more images(删除镜像)
save Save one or more images to a tar archive (streamed to STDOUT by default)(保存镜像到文件)
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE(给镜像打标签)
获取镜像
在安装了Docker之后,我们本地并没有任何镜像,当然我们可以自己构建,不过更方便还是从Docker官方提供的仓库服务Docker Hub上拉取官方或第三方已经构建好的镜像。
拉取镜像可以使用docker image pull,其格式如下:
docker image pull [OPTIONS] NAME[:TAG|@DIGEST]
当然,docker image pull有更简洁的用法:如:
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
要拉取镜像,需要指定Docker Registry的URL和端口号,默认是Docker Hub,另外还需要指定仓库名和标签,仓库名和标签唯一确定一个镜像,而标签是可能省略,如果省略,则默认使用latest作为标签名,而仓库名则由作者名和软件名组成。
所以,在省略了那么参数后,比如我们想拉取centos镜像,可以使用下面简单的命令从Docker Hub上拉到:
$ docker pull centos
查看本地镜像
通过上面的方法我们将镜像拉取到了本地,那要如何查看本地有哪些镜像呢?通过下面的命令我们可以查看本地的全部镜像:
$ docker image ls
当然Docker提供了更简洁的写法,如下:
$ docker images
虚悬镜像
我们知道Docker镜像名由仓库名和标签组成,但有时候我们会看到仓库名和标签皆为的镜像,我们称为这种镜像为虚悬镜像,如下图所示:
虚悬镜像一般是当我们使用docker pull拉取最新镜像时,生成的新的镜像,所以仓库名和标签给了新的镜像,旧的镜像仓库和标签则被取消,成为虚悬镜像。
我们可以使用下面的语句打印所有的虚悬镜像:
$ docker image ls -f dangling=true
一般的虚悬镜像已经没有什么作用了,所以可以清理掉的,下面的命令可以清除所有的虚悬镜像:
$ docker image prune
不过,如果我们想保留一些有用的虚拟镜像时,可以使用docker tag命令重新给镜像起个仓库名和标签:
$ docker tag 621d57f27e93 "test:1.0"
镜像导出与导入
如果想与别人共享某个镜像,除了从镜像服务仓库中pull镜像和把镜像push到仓库上去之外,其实我们还可以将本地构建好的镜像直接导出并保存为文件发送给别人,如下:
$ docker image save -o /tmp/test_image.tar.gz centos:latest
而当你拿到别人导出的镜像文件,你可以使用docker load命令把镜像加载到本地的Docker镜像列表中,如下:
$ docker load < /tmp/test_image.tar.gz
删除本地镜像
要删除一个或多个本地的镜像,可以使用下面的命令:
docker image rm [option] IMAGE1,IMAGE2,...IMAGEn
也可以使用更简洁的方式,如:
docker rmi [option] IMAGE1,IMAGE2,...IMAGEn
可以使用镜像的长id、镜像短id、镜像摘要以及镜像名称来删除镜像,如下
$ docker rmi f7302e4ab3a8
一般更常用镜像的短id,如:
$ docker rmi f7302
使用镜像的摘要也可以删除镜像,镜像的摘要可以使用下面的命令查询:
$ docker image ls --digests
当然我们想要清除本地全部镜像时,可以使用下面的命令,不过一般不建议使用。
$ docker rmi $(docker images -qa)
另外,一般如果镜像已经被使用来创建容器,使用上面的命令删除会报下面的错误,告诉我们该镜像已经被使用,不允许删除。
Error response from daemon: conflict: unable to remove repository reference "mysql:5.7" (must force) - container ccd406c07a78 is using its referenced image e1e1680ac726
对于已经被用于创建容器的镜像,删除方法有两种,一种是先把容器删除,再删除镜像,另一种则只需要在删除镜像的命令中跟一个-f参数便可,如:
$ docker rim -f f7302
使用docker commit构建镜像
上面的例子都是直接使用官方提供的镜像,其实,除了从官方仓库或其他镜像仓库拉取别人构建好的镜像外,我们也可以构建自己的镜像,一般有以下两种构建方式。
使用docker commit命令,我们可以将修改过的容器重新提交为一个镜像,如:
$ docker commit conntaner_id my-hello:1.0
使用这种方式构建的镜像,我们称为黑箱镜像,就是一个黑箱子一样,别人并不知道我们对容器做了哪些修改和操作,所以会对其安全性有所质疑。
所以不推荐使用这种方式构建镜像,下面我们介绍一种更加通用且方便的方式。
使用Dockerfile构建镜像
一般推荐编写Dockerfile来构建一种镜像,Docker Hub上的镜像都是采用这种方式构建的,采用这种方式的好处就是,我们不用把镜像分发给别人,而只是把Dockerfile和相应需要写入镜像的资料发给别人,别人也能自己构建镜像,安全透明。
编写一个简单的Got程序
package main
import "fmt"
func main(){
fmt.Println("Hello Go")
}
将Go程序编译为可执行程序,如:
$ go build hello.go
编写Dockerfile文件
下面我们编写一个简单的Dockerfile文件,构建自己的第一个镜像,如下:
# 从一个空白的镜像开始
FROM stratch
ADD hello /
# 执行
CMD /hello
开始构建镜像
编写好Dockerfile文件后,需要使用docker build命令进行构建,docker build命令的格式如下:
$ docker build [OPTIONS] PATH | URL | -# 注意最后的点(.)表示当前目录,即Dockerfile所在的目录
$ docker build -t "hello-go:1.0" .
上面只是简单演示了使用Dockerfile文件如何构建镜像,关于Dockerfile,还有许多更加深入地用法,我们之后有机再谈。