在上一篇教程里,我们学会了docker的使用,也体验了docker的隔离性,在里面随意增删软件,不用的时候直接删除镜像容器,很方便。但是,还有问题,如果你的朋友也想试用你的软件,怎么办呢?或者你换了台电脑,又要重新pull然后在docker里install么?
当然不用,我们说过docker具有快速部署的优点。今天我们学习怎么构建我们自己的docker镜像。
一:docker commit
最直观的思路,自然是将我们的容器保存下来。而docker也为此提供了很方便的命令,即 docker commit。其格式为
docker commit -a "author" -m "message" <exiting-container> <hub-user>/<repo-name>[:<tag>]
是不是很像 git的commit命令?其中 exiting-container为你的容器名称或 id,hub-user 为你登录的 DockerHub 用户名,repo-name 为你想给新镜像起的名字。
镜像构建成功之后,就可以分享了
你可以使用 docker save <image> | gzip > fileName 将 image 保存为文件,然后拷贝给他人,再通过 docker load -i fileName导入使用。
这么做太麻烦了。推荐方式是使用 docker push 命令将其推送到 DockerHub,当然你需要先登录。
我把上次的 ubuntu 做了commit,并 push 到了 DockerHub,成功之后就可以在DockerHub看到自己的镜像了,之后可以随时拉取使用。
另外,通过docker history 可以看到容器的创建历史,我们可以试下。
我们发现,在软件内部增删软件、文件等操作在这里都无法体现,因此我们的镜像事实上成了一个黑盒,没有文档就不知道其具体有什么功能,即使你添加了文档,经过一次次更改文档很可能也没有同步了,非常不方便别人或者自己以后使用。事实上,docker 提供了更方便强大的方式构建镜像,即 Dockerfile。
另外,commit 方式在特定场景下也有其用途,比如程序崩溃、被入侵后保存现场时,就很有用。
二:Dockerfile
Dockerfile 是一个文本文件,其内包含了一条条的指令,描述我们的镜像该如何构建,就像我们程序的源码。在这之前,补充一个知识,就是我们的 docker 镜像文件事实上是分层的。你应该注意到拉取 ubuntu 的时候,它pull 了很多次。而分层的操作也方便不同镜像间共享相同的层,这显著的节省了客户机的存储空间以及下载时间。
例如,我再拉取一个 nginx 镜像,你会发现第一层已经存在了,直接复用。
为什么要讲这个?因为 Dockfile 的每一条指令的内容,就是描述该层应当如何构建。
我们首先来定制一个 nginx 镜像。
任意新建一个文件夹,建立一个文本文件,命名为 Dockerfile。
文本内容如下:
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
然后执行docker build命令构建镜像
docker build -t "name:tag" path
可以发现,如之前所说,新的容器分两层构建。注意命令最后有一个点,表示当前文件夹。
然后,执行 docker run 启动镜像。
这时访问http://127.0.0.1:8080应该就能看到我们的页面了。注意之前的镜像可能已经绑定了本机8080端口,你需要停止它或者换一个端口。还有-d 参数表示后台运行,注意-d 并不影响容器的运行时间,只是将其放到了后台,换句话说,如果你的容器本身没有运行不退出的进程,即使加了-d 容器依然会自动退出。
同样你可以将其推送到 DockerHub,或者仅仅将你的 Dockerfile 分享给他人就可使用。另外,你可以将 Dockerfile 与你的代码一同上传 github 或其他系统,更方便你的应用环境搭建,也可以进行docker 的版本管理。甚至,你还可以设置Docker Hub结合Github自动化构建镜像。
接下介绍常用的 Dockerfile 命令。
- FROM 命令
FROM为指定基础镜像,像之前我们基于官方 nginx 进行定制,这样可以省略大量的重复工作。DockerHub 上有大量的镜像,ubuntu、mysql、php、python 等等,一定要选择合适的作为基础镜像,比如开发 django 就选用 python 或者django 镜像,而不要去用 php 镜像。
2. RUN 命令
RUN 命令即用来在容器中执行命令的,相当于你之前手动在 docker 里面执行命令。
其格式也很简单,RUN <command>即可,command可以是容器能执行的任何命令。
这里说下本人的经验,RUN 命令不一定能成功执行,你不必一遍遍更改然后 build 实验,可以直接 run 一个基础镜像,然后去执行命令,等成功了再写入 Dockerfile,会方便很多。
另外,之前说过,每条命令会构建一层,所以不要写太多层 RUN,也不要只有一层,单独的功能模块组合到一层,更利于快速构建以及复用。比如下面这个例子:
FROM php:7.1.22-fpm
# Install PHP and composer dependencies
RUN apt-get update
&& apt-get install -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev
&& RUN apt-get clean
# Install needed extensions
RUN docker-php-ext-install pdo pdo_mysql mcrypt zip gd
3. COPY 命令
COPY 命令用于将文件拷贝到镜像中。
COPY <源路径> <目标路径>
你可以将需要的文件拷贝到镜像内,注意这里源路径是相对路径,即相对上文构建命令里的最后一个参数所指定的目录(上文例子为`.`),不能随意引用其它文件。另外,尽量不要在构建目录里放入无关文件,因为 docker 会将目录下所有文件打包发送给服务进程,如图所示
4. CMD命令
CMD 命令为容器启动时的默认命令,因为容器其实本质是一个进程(后面会细讲)。比如 ubuntu 镜像默认CMD 命令为/bin/bash,因此事实上我们只要执行 docker run -it ubuntu就可以得到一个交互式 shell。如果我们传入其它命令,比如 执行 docker run ubuntu uname,容器就会执行我们指定的命令。
以上就是最常用的Dockerfile 命令,建议大家自己尝试构建一个,会有更深的体会。另外还有很多命令以及细节,可以参考这篇文章。