前面我们已经了解了Docker镜像的结构,实际上所有常用的应用程序都有对应的镜像,我们只需要下载这些镜像然后就可以使用了,而不需要自己去手动安装,顶多需要进行一些特别的配置。当然要是遇到某些冷门的应用,可能没有提供镜像,这时就要我们手动去安装,接着我们就来看看如何构建我们自己的Docker镜像。构建镜像有两种方式,一种是使用commit
命令来完成,还有一种是使用Dockerfile来完成,我们先来看第一种。
这里我们就做一个简单的例子,比如我们现在想要在Ubuntu的base镜像中安装Java环境,并将其打包为新的镜像(这个新的镜像就是一个包含Java环境的Ubuntu系统镜像)
咱们先启动Ubuntu镜像,然后使用yum
命令(跟apt比较类似)来安装Java环境,首先是run
命令:
docker pull ubuntu
接着启动:
直接使用apt命令来安装Java环境,在这之前先更新一下,因为是最小安装所以本地没有任何软件包:
接着输入:
apt install openjdk-8-jdk
等待安装完成:
这样,我们就完成了对Java环境的安装了,接着我们就可以退出这个镜像然后将其构建为新的镜像:
使用commit
命令可以将容器保存为新的镜像:
docker commit 容器名称/ID 新的镜像名称
可以看到安装了软件之后的镜像大小比我们原有的大小大得多,这样我们就可以通过这个镜像来直接启动一个带Java环境的Ubuntu操作系统容器了。不过这种方式虽然自定义度很高,但是Docker官方并不推荐,这样的话使用者并不知道镜像是如何构建出来的,是否里面带了后门都不知道,并且这样去构建效率太低了,如果要同时构建多种操作系统的镜像岂不是要一个一个去敲?我们作为普通用户实际上采用Dokcerfile的方式会更好一些。
我们来看看如何使用Dockerfile的形式创建一个带Java环境的Ubuntu系统镜像。首先直接新建一个名为Dockerfile
的文件:
touch Dockerfile
接着我们来进行编辑,Dockerfile
内部需要我们编写多种指令来告诉Docker我们的镜像的相关信息:
FROM <基础镜像>
首先我们需要使用FROM指令来选择当前镜像的基础镜像(必须以这个指令开始),这里我们直接使用ubuntu
作为基础镜像即可,当然如果不需要任何基础镜像的话,直接使用scratch
表示从零开始构建,这里就不演示了。
基础镜像设定完成之后,我们就需要在容器中运行命令来安装Java环境了,这里需要使用RUN
指令:
RUN apt update RUN apt install -y openjdk-8-jdk
每条指令执行之后,都会生成一个新的镜像层。
OK,现在我们的Dockerfile就编写完成了,只需要完成一次构建即可:
docker build -t <镜像名称> <构建目录>
执行后,Docker会在构建目录中寻找Dockerfile文件,然后开始依次执行Dockerfile中的指令:
构建过程的每一步都非常清晰地列出来了,一共三条指令对应三步依次进行,我们稍微等待一段时间进行安装,安装过程中所以的日志信息会直接打印到控制台(注意Docker镜像构建有缓存机制,就算你现在中途退出了,然后重新进行构建,也会直接将之前已经构建好的每一层镜像,直接拿来用,除非修改了Dockerfile文件重新构建,只要某一层发生变化其上层的构建缓存都会失效,当然包括pull
时也会有类似的机制)
最后成功安装,会出现在本地:
可以看到安装出来的大小跟我们之前的是一样的,因为做的事情是一模一样的。我们可以使用history
命令来查看构建历史:
可以看到最上面两层是我们通过使用apt命令生成的内容,就直接作为当前镜像中的两层镜像,每层镜像都有一个自己的ID,不同的镜像大小也不一样。而我们手动通过commit
命令来生成的镜像没有这个记录:
如果遇到镜像ID为missing的一般是从Docker Hub中下载的镜像会有这个问题,但是问题不大。用我们自己构建的镜像来创建容器就可以直接体验带Java环境的容器了:
有关Dockerfile的其他命令,我们还会在后续的学习中逐步认识。