使用Dockerfile来构建镜像
Dockerfile原理
关于Docker中使用Dockerfile来构建镜像,可以简单的理解为:将所有的操作都写在一个脚本中,这个脚本的名称就叫Dockerfile,执行构建镜像的命令时,就会去执行这个命令。
上面只是通俗的说法,其实,你写完Dockerfile之后,执行构建镜像的时候,并不是直接执行Dockerfile中命令,而是将这个文件所在目录的所有文件,都交给docker的守护进程,让docker守护进程来解析之后,再执行。
上面说的是将Dockerfile所在目录的所有文件,都交给守护进程,至于为什么要这样做,待会再解释。
但是,如果是初学Dockerfile,建议建立一个空的目录,在空目录中创建一个Dockerfile文件。
创建Dockerfile
-> ~ # mkdir TestDockerfile -> ~ # cd TestDockerfile -> TestDockerfile # touch Dockerfile -> TestDockerfile # echo "hello world" > index.html #创建一个测试文件
Dockerfile实例
下面的Dockerfile会构建出一个安装有nginx和vim的docker镜像
#VERSION 0.0.1 FROM ubuntu:16.04 MAINTAINER ganlixin ubuntu "1355036599@qq.com" RUN apt-get update && apt-get install -y nginx RUN apt-get install -y vim RUN rm -rf /var/www/html/index.html ADD ./index.html /var/www/html/index.html WORKDIR /var/www/html RUN echo "hello docker" > demo.html WORKDIR /root RUN echo "hello root" > hey.txt ENV SELF_DEFINE_ENVIROMENT_VAR "123456789" RUN echo $SELF_DEFINE_ENVIROMENT_VAR > env_var.html USER root:root COPY index.html /tmp/test CMD /bin/bash EXPOSE 80
下面会针对这个Dockerfile中每一项,进行详细的说明。
Dockerfile指令
注释
在Dockerfile中,每一行“#”之后的内容,都会被当成注释。
一般用来写一些提示信息,比如版本信息,镜像说明
FROM
FROM ubuntu:16.04
表示使用当前Dockerfile在构建镜像时,是基于哪一个镜像。一般来说,不会从零开始构建镜像,一般都会选择一个已有的镜像作为基础,在上面安装各种软件,然后构建出一个加工过的镜像。
示例中,使用ubuntu:16.04作为基础
MAINTAINER
MAINTAINER beyond ubuntu ”1355036599@qq.com"
用来提示用户,这个Dockerfile的作者、以及作者的联系方式
RUN
RUN apt-get update && apt-get install -y nginx
RUN用来执行一条Linux命令,执行的时机:在构建镜像的时候。
不是在使用该镜像创建容器后,运行容器的时候。
一个Dockerfile文件中可以有多个RUN命令,也就是说,在构建镜像时,可以执行多个指定的命令
ADD
ADD ./index.html /var/www/html/index.html
还记得在创建Dockerfile的时候,创建了一个测试文件吗?他的内容是hello world,文件名是index.html
这个命令的作用就是将Dockerfile文件所在目录中的某个文件,添加到要构建的镜像中。
如果将Dockerfile所在的环境叫做外部环境,而构建出的镜像成为内部环境的话,ADD命令的作用就是将外部环境中的文件 添加到 内部环境中。
注意,目标路径,如果不是以 / 结尾,表示的文件;如果以 / 结尾,表示目录
WORKDIR
设置一个工作目录,后面的RUN命令、CMD命令都会在这个工作目录中执行。
相当于cd path
WORKDIR /var/www/html RUN echo "hello docker" > demo.html WORKDIR /root RUN echo "hello root" > hey.txt
上面的代码表示,在/var/www/html目录下的demo.html中输入内容“hello world”;
然后切换工作目录,在/root目录中,在hey.txt中输入内容,“hello root”
ENV
定义环境变量,相当于全局变量
ENV SELF_DEFINE_ENVIROMENT_VAR "123456789"
在镜像中设置一个环境变量,SELF_DEFINE_ENVIROMENT_VAR ,值为123456789
之后可以使用$SELF_DEFINE_ENVIROMENT_VAR来使用这个变量
USER
指定启动容器后,是以什么身份来运行,可以同时设置组和用户
USER root:root
表示启动容器后,是以root组的root身份运行
COPY
COPY命令和ADD命令功能类似,
COPY index.html /tmp/test
将外部环境的index.html拷贝到内部环境/tmp目录下,重命名为test。如果是要将index.html拷贝到/tmp/test目录下,即,将test看作是目录,那么,就要在test后面加 / ,即 /tmp/test/
注意,目标路径,如果不是以 / 结尾,表示的文件;如果以 / 结尾,表示目录
CMD
CMD /bin/bash
表示在使用镜像,启动容器的时候,会运行的命令。
和RUN的区别:
1、RUN是在构建镜像的时候运行,而CMD是在容器启动的时候运行。可以理解为,RUN命令只是在构建时执行一次,而CMD命令在每次启动容器时,都会执行一次。
2、一个Dockerfile中可以有多个RUN命令,多个RUN命令都会执行;而CMD命令虽然可以出现多次,但是只有最后一个会被执行。
3、RUN命令只运行一次,所以说不存在命令覆盖的情况,而CMD会出现命令覆盖的情况(即第2点区别),在启动容器时,如果指定了要执行的命令,那么Dockerfile中的CMD命令同样会被覆盖。
EXPOSE
EXPOSE 80,表示将容器的80端口,映射到外部环境(宿主机)的某个端口上。
可以通过启动容器的时候指定映射到宿主机的哪个端口
构建Docker镜像
使用命令docker build命令即可触发构建操作,但是需要注意,构建时,要指定构建出的镜像名称(使用-t选项)以及tag,以及Dockerfile所在的路径。
一般构建的时候,都是进入到Dockerfile所在的目录
假设现在使用前面的Dockerfile构建一个镜像,镜像名为beyond/test,标签为v1,则执行如下命令
-> ~ # cd TestDockerfile -> TestDockerfile # docker build -t="beyond/test:v1" . .......等待 ........ .......构建完成 -> TestDockerfile # docker run -i -t --name first_test beyond/test root@38ff683ba587:~# #进入到容器中
注意,此时,在运行容器的时候,并没有在后面加上/bin/bash,这是因为Dockerfile中CMD命令已经指定了在启动容器时执行的命令。
测试
现在我们逐个检查Dockerfile中的操作是否成功,即,看对应操作的结果是否保留下来
1、看/var/www/html/index.html这个文件的内容是不是hello world
2、看/var/www/html下面是不是有一个demo.html,内容为hello docker
3、看/root目录下hey.txt的内容是不是hello root
4、使用env命令,看是不是有SELF_DEFINE_ENVIROMENT_VAR这个环境变量
5、看/root目录下env_var.html的内容是不是环境变量SELF_DEFINE_ENVIROMENT_VAR的值123456789
6、看一下/tmp/test文件内容是不是hello world
指定宿主机和容器的端口映射
-> TestDockerfile # docker run -d --name second_test -p 8080:80 beyond/test:v1 nginx -g "daemon off;" 7d74273277cc925ae5656591367c5b3cc6e4b9ed90d251123eeaed42b463367c
外部访问你的ip:8080,然后就可以看到结果了。