Docker学习笔记
索引
Docker简介
基本概念
Docker包括三个基本概念,
- 镜像(
Image
) - 容器(
Container
) - 仓库(
Repository
)
理解了这三个概念,就理解了Docker的整个生命周期。镜像
操作系统分为内核和用户空间,对于Linux而言,内核启动后,会挂载root文件系统为其提供用户空间支持。而Docker镜像就相当于一个root文件系统。
Docker镜像是一个特殊的文件系统,除了提供容器运行时所欲要的程序、库、资源、配置等文件外,还包含了一些为运行时准备的配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。容器
镜像和容器的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的尸体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的root文件系统、自己的网络配置、自己的进程空间,甚至自己的用户ID空间。仓库
镜像构建完成后,可以很容易的在当前宿主机上运行。但是,如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务。Docker Registry就是这样的服务。
一个Docker Registry中可以包含多个Repository,每个仓库可以包含多个标签Tag,每个标签对应一个镜像。
Docker安装
本人在macOS下使用手动安装的方式,点击链接下载Stable或Edge版本的Docker for Mac。安装完成后,运行下面的命令,验证是否安装成功。1
2$ docker --version
Docker version 18.05.0-ce-rc1, build 33f00ce
由于众所周知的网络原因,建议配置国内镜像加速。在任务栏点击Docker的图标->Perferenes->Daemon->Registry mirrors,如图所示,填入网易的镜像。设置成功后,可输入如下命令验证配置是否生效。
1 | $ docker info |
使用Docker镜像
获取镜像
Docker Hub上有大量的高质量的镜像可用,我们来看下怎么获取这些镜像。
从Docker镜像仓库获取镜像的命令是docker pull
,其命令格式为:1
docker pull [选项][Docker Registry 地址[:端口号]/]仓库名[:标签]
具体的选项可以通多docker pull --help
命令看到,下面解释下镜像名称格式。
- Docker镜像仓库地址:地址的格式一般是
<域名/IP>[:端口号]
,默认地址是Docker Hub。 - 仓库名:仓库名是两段式名称,即
<用户名>/<软件名>
。对于Docker Hub,如果不给出用户名,则默认为library
,也就是官方镜像。
比如:1
2
3
4
5
6
7
8
9$ docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
297061f60c36: Pull complete
e9ccef17b516: Pull complete
dbc33716854d: Pull complete
8fe36b178d25: Pull complete
686596545a94: Pull complete
Digest: sha256:147cb164c3a23807bd8013e4bcc4553a34af49fdfd2478ba5b98f2bac9c495e5
Status: Downloaded newer image for ubuntu:16.04
上面的命令中没有给出Docker镜像仓库地址,因此将会从Docker Hub获取镜像。而镜像名称是ubuntu:16.04
,因此将会获取去官方进项‘library/ubuntu’仓库中标签为16.04
的镜像。
运行
有了镜像后,我们就能以这个镜像为基础启动并运行一个容器。以上面的ubuntu:16.04
为例,如果我们打算启动里面的bash并且运行交互式操作的话,可以执行下面的命令。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$ docker run -it --rm \
ubuntu:16.04 \
bash
root@25ca789d6341:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.4 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.4 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
docker run
就是运行容器的命令,具体格式我们会在容器一节进行详细讲解,我们这里简单说明一下上面用到的参数。
- it:这是两个参数,一个是
-i
交互式操作,一个是-t
终端 - rm:这个参数是说容器退出之后立即将其删除
- ubuntu:16.04:这是指用
ubuntu:16.04
镜像作为基础来启动镜像 - bash:放在镜像名后面的是命令,这里我们希望有个交互式的Shell,因此用的是
bash
进入容器后,我们可以在Shell下操作,执行任何所需的命令。这里我们执行cat /etc/os-release
,这是Linux查看当前系统版本的命令。最后我们通过exit
退出这个容器。
列出镜像
使用docker image ls
命令可以列出已经下载下来的镜像1
2
3
4
5
6
7
8docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis 3.2 e97b1f10d81a 3 weeks ago 99.7MB
nginx latest ae513a47849c 3 weeks ago 109MB
mysql latest 80bbf861367a 3 weeks ago 445MB
httpd latest fb2f3851a971 3 weeks ago 178MB
openjdk 8-jdk-alpine 224765a6bdbe 4 months ago 102MB
ubuntu 15.10 9b9cb95443b5 22 months ago 137MB
列表包含了仓库名
、标签
、镜像ID
、创建时间
、以及所占用的空间
。
可以通过docker system df
命令来查看镜像、容器、数据卷所占用的空间。1
2
3
4
5
6$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 10 4 1.499GB 972.7MB (64%)
Containers 5 0 300B 300B (100%)
Local Volumes 3 0 0B 0B
Build Cache
中间层镜像
为了加速镜像构建、重复利用资源,Docker会利用中间层镜像。docker image ls
命令只会列出顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加-a
参数。1
2
3
4
5
6
7
8
9
10
11
12$ docker image ls -a
REPOSITORY TAG IMAGE ID CREATED SIZE
springboot/system latest d4c6d5085319 6 days ago 124MB
<none> <none> c09c2bd1d8c1 6 days ago 124MB
<none> <none> 4a00a600ccf6 6 days ago 102MB
runoob/ubuntu v2 bc3d34e8203a 2 weeks ago 137MB
redis 3.2 e97b1f10d81a 3 weeks ago 99.7MB
nginx latest ae513a47849c 3 weeks ago 109MB
mysql latest 80bbf861367a 3 weeks ago 445MB
httpd latest fb2f3851a971 3 weeks ago 178MB
ubuntu 16.04 0b1edfbffd27 3 weeks ago 113MB
openjdk 8-jdk-alpine 224765a6bdbe 4 months ago 102MB
我们可以看到许多无标签的镜像,这些镜像很多都是中间层镜像,是其他镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。不过只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。
列出部分镜像
我们可以根据仓库名列出镜像,命令如下1
2
3
4$ docker image ls ubuntu
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 16.04 0b1edfbffd27 3 weeks ago 113MB
ubuntu 15.10 9b9cb95443b5 22 months ago 137MB
列出特定的某个镜像,也就是说置顶仓库名和标签1
2
3$ docker image ls ubuntu:16.04
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 16.04 0b1edfbffd27 3 weeks ago 113MB
除此之外,docker image ls
还支持强大的过滤器参数--filter
,或者简写-f
。比如我们希望看到mysql:latest
之后建立的镜像,可以用下面的命令1
2
3
4
5
6$ docker image ls -f since=mysql:latest
REPOSITORY TAG IMAGE ID CREATED SIZE
springboot/system latest d4c6d5085319 6 days ago 124MB
runoob/ubuntu v2 bc3d34e8203a 2 weeks ago 137MB
redis 3.2 e97b1f10d81a 3 weeks ago 99.7MB
nginx latest ae513a47849c 3 weeks ago 109MB
以特定格式显示
默认情况下,docker image ls
会输出一个完整的表格,但是我们并有所有时候都会需要这些内容。比如我们删除虚悬镜像的时候,我们利用docker image ls
把所有的虚悬镜像的ID列出来,然后才可以交给docker image rm
命令作为参数来删除制定的这些镜像,这个时候就用到了-q
参数。1
2
3
4
5
6
7
8
9
10
11$ docker image ls -q
d4c6d5085319
bc3d34e8203a
e97b1f10d81a
ae513a47849c
80bbf861367a
fb2f3851a971
0b1edfbffd27
224765a6bdbe
9b9cb95443b5
6fae60ef3446
-f
配个-q
产生出指定范围的ID列表,然后送给另一个docker
命令作为参数,从而针对这组实体成批的进行某种操作的做饭在docker命令行使用过程中非常常见,不仅仅是镜像,将来我们会在各个命令中看到这类搭配以完成强大的功能。
另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列或者不希望有标题,这样方便其他程序解析结果等,这就用到了Go的模板语法
比如下面的命令会直接列出镜像结果,并且只包含镜像ID和仓库名1
2
3
4
5
6
7
8
9
10$ docker image ls --format "{{.ID}}:{{.Repository}}"
d4c6d5085319:springboot/system
bc3d34e8203a:runoob/ubuntu
e97b1f10d81a:redis
ae513a47849c:nginx
80bbf861367a:mysql
fb2f3851a971:httpd
0b1edfbffd27:ubuntu
224765a6bdbe:openjdk
9b9cb95443b5:ubuntu
或者打算以表格等距显示,并且有标题行。和默认一样,不过自己定义列。1
2
3
4
5
6
7
8
9
10
11$ docker image ls --format "{{.ID}}:{{.Repository}}\t{{.Tag}}"
d4c6d5085319:springboot/system latest
bc3d34e8203a:runoob/ubuntu v2
e97b1f10d81a:redis 3.2
ae513a47849c:nginx latest
80bbf861367a:mysql latest
fb2f3851a971:httpd latest
0b1edfbffd27:ubuntu 16.04
224765a6bdbe:openjdk 8-jdk-alpine
9b9cb95443b5:ubuntu 15.10
6fae60ef3446:training/webapp latest
删除镜像
如果要删除本地的镜像,可以使用docker image rm
命令,其格式为:1
$ docker image rm [选项]<镜像1>[<镜像2>...]
用ID、镜像名、摘要删除镜像
其中<镜像>
可以是镜像短ID
、镜像长ID
、镜像名
或者镜像摘要
。
比如我们有如下一些镜像1
2
3
4
5
6
7
8
9
10
11
12$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
springboot/system latest d4c6d5085319 6 days ago 124MB
runoob/ubuntu v2 bc3d34e8203a 2 weeks ago 137MB
redis 3.2 e97b1f10d81a 3 weeks ago 99.7MB
nginx latest ae513a47849c 3 weeks ago 109MB
mysql latest 80bbf861367a 3 weeks ago 445MB
httpd latest fb2f3851a971 3 weeks ago 178MB
ubuntu 16.04 0b1edfbffd27 3 weeks ago 113MB
openjdk 8-jdk-alpine 224765a6bdbe 4 months ago 102MB
ubuntu 15.10 9b9cb95443b5 22 months ago 137MB
training/webapp latest 6fae60ef3446 3 years ago 349MB
如果我们要删除redis:3.2
镜像,可以用短ID
来删除镜像,一般取前三个字符以上,可以执行下面命令。1
2
3
4
5
6
7
8
9
10$ docker image rm e97b
U$ntagged: redis:3.2
Untagged: redis@sha256:33dccfc7c95b31c09d6f7de824716e79c1624e7587ee08dd402442fece7be214
Deleted: sha256:e97b1f10d81a1da230e6327f5d7a13b1e5b3716bb979cdd583c608ecae88adef
Deleted: sha256:2ba10eb7ce40ff2fb8240b89efdfcaca65e766e322dd931811a70c089ce2524f
Deleted: sha256:3ca88aa54c7634d16d8f6717aebdcef639cead694ced3c393b237117a5e1e30f
Deleted: sha256:f115d9da9e55bc62cf359227dd64edc01911f7b1ed52afbffd7b00a2e6d5d102
Deleted: sha256:ba4122a82d251fc6b432237079bbb0d645ef47049e800c1f9e56d61fc8e823cf
Deleted: sha256:2425077af8e28faee2557ddabb85a00938d81d13cf8c41897958d07aaeaa39d2
Deleted: sha256:ba291263b0854589e32a6fa7775c898d662ed835cd686ac9ac2d33dcefa91a39
我们也可以用<仓库名>:<标签>
来删除镜像1
2
3
4
5
6$ docker image rm springboot/system:latest
Untagged: springboot/system:latest
Deleted: sha256:d4c6d5085319876efefdc1fbe9990240a35d4e044b72367d4bc541e08025850d
Deleted: sha256:c09c2bd1d8c11ee1a449c2c2f9959149822ea17b98513bca6337d0f72d688a2e
Deleted: sha256:8a8d6542fde6017f06197c0edfb099391d2d3d36c82d3b8cf92656b03ba8d305
Deleted: sha256:4a00a600ccf68543699f0c4b6d7682702f8df9697973c7cbcb0980d10c936809
Untagged和Deleted
观察下上面这几行命令的运行输出的话,你会注意到删除行为分为两类。一类是Untagged
,另一类是Deleted
。之前我们介绍过,镜像的唯一标识是其ID和摘要,而一个镜像可以有多个标签的。因此当我们使用命令删除镜像的时候,实际上是在要求删除某个标签的镜像。首先需要做的事将满足我们要求的镜像标签都取消,这就是我们看到的Untagged
的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像。如果是这种情况,那么Delete
行为就不会发生。所以并非所有的docker rmi
都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。
当该镜像所以的标签都被取消了,该镜像很有可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础方向一次进行判断删除。
除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行。如果这些容器是不需要的,应该现将它们删除,然后再来删除镜像。
用docker image ls命令来配合
像其他可以承接多个实体的命令一样,可以使用docker image ls -q
来配合使用docker image rm
,这样可以成批的删除希望删除的镜像。比如我们需要删除所有仓库名为redis
的镜像1
$ docker image rm $(docker image ls -q redis)
或者删除所有在mysql:latest
之前的镜像1
$ docker image rm $(docker image ls -q -f before=mysql:latest)
利用commit理解镜像构成
注意:docker commit
命令除了学习之外,还有一些特殊的应用场合,比如发现被入侵后保存现场等。但是,不要是使用docker commit
定制镜像,定制镜像应该使用Dockerfile
来完成。后续会讲到定制镜像的内容。
镜像是容器的基础,每次执行docker run
的时候都会指定哪个镜像作为容器运行的基础。在之前的例子中,我们所使用的都是来自于Docker Hub的镜像。直接使用这些镜像可以满足一定的需求,但是当无法满需求时,我们就需要定制这些镜像,接下来我们将讲解如何定制镜像。
回顾之前的内容,镜像是多层存储,每一层都是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层。
现在让我们以定制一个Web服务器为例子,来讲解镜像是如何构建的。1
$ docker run --name webserver -d -p 80:80 nginx
这条命令会用nginx
镜像启动一个容器,命名为webserver
,并且映射了90端口,这样我们就可以用浏览器访问这个nginx
服务器。我们直接用浏览器访问http://localhost,可以看到默认的nginx欢迎页面。
现在假设我们不喜欢这个欢迎页面,我们希望改成Hello,Docker的文字,我们可以使用docker exec
命令进入这个容器,修改其内容。1
2
3
4
5docker exec -it webserver bash
root@6b6b8692220d:/# ls
root@6b6b8692220d:/# echo '<h1>Hello,Docker!</h1>' > /usr/share/nginx/html/index.html
root@6b6b8692220d:/# exit
exit
我们以交互式终端方式进入了webserver
容器,并且执行了bash
命令,也就是获得一个可以操作的Shell。然后我们用<h1>Hello,Docker!</h1>
覆盖了/usr/share/nginx/html/index.html
的内容。现在我们刷新浏览器,发现内容被改变了。
我们修改了容器的文件,也就是动了容器的存储层,我们可以通过docker diff
命令看到具体的改的。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15**docker** diff webserver
C /root
A /root/.bash_history
C /run
A /run/nginx.pid
C /usr/share/nginx
C /usr/share/nginx/html/index.html
A /usr/share/nginx/index
A /usr/share/nginx/index.html
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
我们现在定制好了变化,我们希望能将其保存下来形成镜像。当我们运行一个容器的时候,我们做的任何文件修改都会被记录于容器存储层里。而Docker提供了一个docker commit
命令,可以将容器的存储层保存下来成为镜像。换句话说,就是原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会有用原有容器最后的文件变化。
docker commit
的语法格式为:1
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
我们可以用下面的命令将容器保存为镜像:1
2
3
4
5
6$ docker commit \
--author "wyb<wyb496926024@gmail.com>" \
--message "修改了默认网页" \
webserver \
nginx:v2
sha256:39b0a01f559ac0970df2d35aba7e6bbc683630bcf458ff86a7d75750637243ee
其中--author
是指定修改的作者,而--message
则是记录本次修改的内容。这个点和git
版本控制相似,不过这里这些信息可以省略为空。
我们可以用docker image ls
命令查看这个新定制的镜像:1
2
3$ docker image ls nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 39b0a01f559a 3 minutes ago 109MB
我们还可以用docker history
具体查看镜像内的历史记录,如果比较nginx:v2
的历史记录,我们会发现新增了我们刚刚提交的这一层。1
2
3
4
5
6
7
8
9
10
11
12
13$ docker history nginx:v2
IMAGE CREATED CREATED BY SIZE COMMENT
39b0a01f559a 13 minutes ago nginx -g daemon off; 465B 修改了默认网页
ae513a47849c 3 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 3 weeks ago /bin/sh -c #(nop) STOPSIGNAL [SIGTERM] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) EXPOSE 80/tcp 0B
<missing> 3 weeks ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx… 22B
<missing> 3 weeks ago /bin/sh -c set -x && apt-get update && apt… 53.7MB
<missing> 3 weeks ago /bin/sh -c #(nop) ENV NJS_VERSION=1.13.12.0… 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.13.12… 0B
<missing> 3 weeks ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 3 weeks ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 3 weeks ago /bin/sh -c #(nop) ADD file:ec5be7eec56a74975… 55.3MB
新的镜像定制好后,我们可以来运行这个镜像。1
docker run --name web2 -d -p 81:80 nginx:v2
这里我们命名为新的服务为web2
,并且映射到81
端口。我们使用浏览器直接访问http://localhost:81/看到结果,其内容应该和之前修改后的webserver
一样。
至此,我们第一次完成了定制镜像,使用的是docker commit
命令,手动操作给的旧的镜像添加了新的一层,形成了新的镜像,对镜像多层存储应该有了更直观的感觉。
使用Dockerfile定制镜像
在docker commit
的学习中,我们了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以吧每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题都会解决,这个脚本就是Dockerfile。
Dockerfile是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
还以之前定制nginx
镜像为例,这次我们使用Dockerfile来定制。在一个空白目录中,建立一个文本文件,并命名为Dockerfile
:1
2$ mkdir docker
$ touch Dockerfile
其内容为:1
2FROM nginx
RUN echo '<h1>Hello,Docker!</h1>' > /usr/share/nginx/html/index.html
这个Dockerfile很简单,一共就两行,涉及到了FROM
和RUN
两条指令。
FORM指定基础镜像
所谓定制镜像,是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个修改过的nginx
镜像的容器一样,基础镜像必须是指定的。而FROM
就是指定基础镜像,因此一个Dockerfile
中FROM
是必备的指令,并且必须是第一条指令。
在Docker Store上有非常多的高质量的官方镜像,有的可以直接拿来使用的服务类镜像,如nginx、redis、mongo、mysql、httpd、php、tomcat等,也有一些方便开发、构建、运行各种语言应用的镜像,如node、openjdk、python、ruby、golang等。可以在其中选择最符合我们需求的镜像当做基础镜像进行定制。
如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像。如ubuntu、debian、centos、fedora、alpine等,这些操作系统的软件库为我们提供了更广阔的空间。
除了选择现有镜像为基础镜像外,Docker还存在一个特殊的镜像,名为scratch
。这个镜像是虚拟的概念,并没有实际存在,它表示一个空白的镜像。
RUN执行命令
RUN
指令是用来执行命令行命令的,由于命令行的强大能力,RUN
指令在定制镜像时是最常用的指令之一,其格式有两种:
shell格式:
RUN <命令>
,就像直接在命令行中输入的命令一样。刚才在写的Dockerfile中的RUN
指令就是这种格式。1
RUN echo '<h1>Hello,Docker!</h1>' > /usr/share/nginx/html/index.html
exec格式:
RUN ["可执行文件","参数1","参数2"]
,这更像是函数调用中的格式。
既然RUN
就像shell脚本一样可以执行命令,那我们是否可以向shell脚本一样把每个命令对应一个RUN呢?比如这样:1
2
3
4
5
6
7
8FROM debian:jessie
RUN apt-get update
RUN apt-get install -y gcc libc6-dev make
RUN wget -o redis.tat.gz "http://downlaod.redis.io/releases/redis-3.2.5.tar.gz"
RUN mkdir -p /usr/src/redis
RUN tar - xzf redis.tart.gz -C /usr/src/redis --strip-compinents=1
RUN make -C /usr/src/redis
RUN make -C /usr/src/redis install
之前说过,Dockerfile中每个指令都会简历一层,RUN
也不例外。每一个RUN
的行为,都和刚才我们手工建立镜像的过程一样。新建立一层,在其上执行这些命令,执行结束后,commit
这一层的修改,构成新的镜像。
而上面的这种写法,创建了7层镜像。这是没有必要的,而且很多运行时不需要的东西都被装进了镜像里,比如编译环境、更新的软件包等。结果就是产生非常臃肿、非常多层的镜像,这样不仅增加了构建部署的时间,也很容易出错。上面的Dockerfile
的正确写法应该是:1
2
3
4
5
6
7
8
9
10
11
12
13
14FROM debian:jessie
RUN buildDeps='gcc libc6-dev make' \
&& apt-get update \
&& apt-get install -y $buildDeps \
&& wget -o redis.tat.gz "http://downlaod.redis.io/releases/redis-3.2.5.tar.gz" \
&& mkdir -p /usr/src/redis \
&& tar - xzf redis.tart.gz -C /usr/src/redis --strip-compinents=1 \
&& make -C /usr/src/redis \
&& make -C /usr/src/redis install \
&& rm -rf /var/lib/apt/lists/* \
&& rm redis.tar.gz \
&& rm -r /usr/src/redis \
&& apt-get purge -y --auto-remove $buildDeps
首先,之前所有的命令只有一个目的,就是编译、安装redis可执行雯姐。因此没有必要建立很多层,这只是一层的事情。因此,这里只使用了一个RUN
命令,并且用&&
将各个所需的命令串联起来,将之前的7层简化成了1层。
其次,这里为了格式化还行了换行。Dockerfile支持Sheel类的行尾添加\
的命令换行方式,以及行首#
进行注释的格式。
此外,这组命令的最后添加了清理工作的命令,删除了为编译构建所需要的软件,清理了所有下载、展开的文件,并且还清理了apt
缓存文件。这很重要,因为镜像是多层存储,每一层的东西并不会在下一层被删除,会一直跟随着镜像。因此镜像构建时,一定要确保每一层只添加真正需要的东西,任何无关的东西都应该清理掉。
构建镜像
让我们在回到之前定制的nginx镜像的Dockerfile来,现在我们明白了这个Dockerfile的内容,让我们构建这个镜像吧。在Dockerfile
文件所在的目录执行:1
$ docker build -t nginx:v3