用 Dockerfile 构建镜像
我们不推荐使用 docker commit 的方法来构建镜像。
推荐使用 Dockerfile 定义文件和 docker build 命令来构建镜像。 Dockerfile 使用基本的基于 DSL 语法的指令来构建一个 Docker 镜像,之后使用 docker build 命令基于该 Dockerfile 中的指令构建一个新的镜像。
此处用一个实例来演示用简单的 dockerfile 来构建镜像:
mkdir static_web #这里我们创建了一个目录,用来保存 Dockerfile ,这个目录我们用来作为构建环境 ( build environment ) ,也叫上下文 ( context ) 。 Docker 会在构建镜像时将构建上下文和该上下文中的文件和目录上传到 Docker 守护进程。这样 Docker 守护进程就能直接访问你想在镜像中存储的任何代码、文件或者其他数据。
cd static_web
vim Dockerfile
Dockerfile 由一系列指令和参数组成。每条指令都必须为大写字母,且要跟一个参数。例如此处的 FROM ubuntu:14.04 。
Dockerfile 也是从上往下依序执行,所以应该根据实际需求合理安排顺序。
每条指令都会创建一个新的镜像层并对镜像进行提交。Docker 大体按照如下流程执行 Dockerfile 中的指令:
- Docker 从基础镜像运行一个容器。
- 执行一条指令,对容器做出修改。
- 执行类似 docker commit 的操作,提交一个新的镜像层。
- Docker 再基于刚提交的镜像运行一个新的容器。
- 执行 Dockerfile 中的下一条指令,直到所有的指令都执行完毕。
从上面看到,如果 Dockerfile 由于某些原因(如某条指令失败了)没有正常结束,那么你仍将得到一个可以使用的镜像,你可以基于该镜像运行一个具备交互功能的容器,使用最后创建的镜像进行调试。
每个 Dockerfile 的第一条指令都应该是 FROM 。 FROM 指定一个已经存在的镜像,后续指令都基于此镜像进行,这个是基础镜像。
接着是 MATINTAINER 指令,指定镜像作者和邮件地址。
接着是三条 RUN 指令。 RUN 指令会在当前镜像中运行指定的命令(就是后面的参数)。
默认情况下, RUN 指令会在 shell 里使用命令包装器 /bin/sh -c 来执行。如果当前环境不支持 shell 或者你不希望在 shell 中运行,可以使用 exec 格式的 RUN 指令:
RUN [ “apt-get”, “install”, “-y” “nginx” ]
在这种方式中,使用数组来指定命令和传递参数。
接着是 EXPOSE 指令。 这条指令告诉 Docker 该容器内的应用程序会使用该容器的指定端口。出于安全原因, Docker 不会自动开启一个端口,这需要在运行容器的时候指定需要打开的端口。 可以指定多个EXPOSE 指令来向外部公开多个端口。
接着上面的 Dodckerfile ,可以开始构建一个新的镜像了:
docker build -t=”wangyanfu/nginx:v1″ .
docker build 用来构建新镜像, -t 选项为新镜像设置了仓库和名称,此处仓库名 wangyanfu ,镜像名 nginx ,并且为 nginx 镜像加了一个 v1 的标签。注意最后还有一个 ” . ” 指定了Dockerfile 的路径为当前目录。
之后可以看到 Dockerfile 的每条指令都会被按顺序执行。
Dockerfile 和构建缓存
因为每一步构建都会将结果提交为镜像,这里我们可以将其看做缓存。如果不想使用缓存,可以在 docker build 后面加上 –no-cache 。
查看新做好的镜像:
docker images wangyanfu/nginx:v1
如果想深入探求镜像是如何构建出来的,可以使用 docker history 命令:
docker history wangyanfu/nginx:v1
从新镜像启动容器:
root@docker:~# docker run -d -p 80:80 –name my_nginx wangyanfu/nginx:v1 nginx -g “daemon off;”
-d :指定 Docker 以 分离 ( detached ) 的方式在后台运行。此处对于我们的 Nginx 守护进程非常适合。nginx -g “daemon off;” 指定容器中需要运行的命令。
-p : 指定公开端口。(这里一般是 宿主机端口:容器内端口 这种写法来绑定,也可以直接标注一个容器内端口,来绑定到宿主机的随机端口,还可以加上宿主机的具体网卡 :127.0.0.1:8080:80 ,还可以通过端口绑定时使用 /udp 后缀来指定 UDP 端口)
运行一个容器时, Docker 可以通过两种方式在宿主机上分配端口。
- Docker 可以在宿主机中随机选择一个位于 49000~49900 的一个比较大的端口号来映射到容器中的 80 端口上。
- 可以在 Docker 宿主机中指定一个具体的端口号来映射到容器中的 80 端口上。
docker ps 命令可以查看端口映射的情况。
docker port wangyanfu/nginx:v1 80 可以查看到映射情况的容器。
-P : 对外公开 EXPOSE 命令公开的所有端口。
端口映射到宿主机后,我们就可以通过宿主机的IP进行访问了。
Dockerfile 指令
1. CMD
CMD 指令用于指定一个容器启动时要运行的命令。和之前我们用过的 RUN 不一样的是, RUN 是指定镜像被构建时要运行的命令,而 CMD 是指定镜像被启动时要运行的命令。
CMD [ “/bin/bash” ] 或者 CMD [ “/bin/bash”, “-l” ]
需要注意的是,要运行的命令是存放在一个数组结构中的。这将告诉 Docker 按指定的原样来运行该命令。当然也可以不按照数组结构来指定命令,但这时候 Docker 会在指定的命令前面加上 /bin/sh -c ,这在执行该命令的时候可能会导致意外失败。
最后,使用 dokcer run 命令可以覆盖掉 CMD 的命令。 如果我们在 Dockerfile 里指定了 CMD 指令,而同时在 docker run 命令中也指定了要运行的命令,命令行中指定的命令会覆盖 Dockerfile中的 CMD 指令。
在 Dockerfile 中只能指定一条 CMD 指令。如果指定了多条 CMD 指令,也只有最后一条 CMD 会被使用。
2.ENTRYPOINT
ENTRYPOINT 和 CMD 类似,但不容易被覆盖,实际上 docker run 命令行中指定的任何参数都会被当作参数再次传递给 ENTRYPOINT 指令中指定的命令。
ENTRYPOINT [ “/usr/bin/nginx” ]
ENTRYPOINT [ “/usr/bin/nginx”, “-g”, “daemon off;” ]
此处有个小技巧,可以使用 ENTRYPOINT 指定要运行的命令,然后再在 docker run 的时候指定参数。
例如 Dockerfile 里面写上 ENTRYPOINT [ “/usr/bin/nginx” ] ,然后启动容器时 docker run -t -i 镜像名 -g “daemon off;”
或者像这样:
ENTRYPOINT [ “/usr/bin/nginx” ]
CMD [ “-g”, “daemon off;” ]
如果想要覆盖,可以在加上参数 : docker run –entrypoint
3.WORKDIR
WORKDIR 指令用来在从镜像创建一个新容器时,在容器内部设置一个工作目录,ENTRYPOINT 和/ 或 CMD 指定的程序会在这个目录下执行。
我们可以使用该指令为 Dockerfile 中后续的一系列指令设置工作目录,也可以为最终的容器设置工作目录。
docker run -w 覆盖。
4.ENV
5.USER
6.VOLUEM
7.ADD
8.COPY
9.ONBUILD
参考 http://docs.docker.com/reference/builder/