本节内容:node
dockerfile是构建镜像的说明书。dockerfile提供了一种基于DSL语法的指令来构建镜像,经过代码化,镜像构建过程一目了然,咱们能看出镜像构建的每一步都在干什么。nginx
若要共享镜像,咱们只须要共享dockerfile就能够了。共享dockerfile文件,具备如下优势:sql
使用Dockerfile构建的流程:docker
1. RUN指令shell
RUN指令会在前一条命令建立的镜像的基础上启动一个容器,并在容器中运行指令的命令。在该命令结束后,会提交容器为新的镜像。新镜像会被dockerfile中的下一条指令所使用。vim
默认状况下,RUN指令会以shell的形式去执行命令。固然咱们也可使用exec格式的RUN指令。在exec方式中,咱们使用一个数组来指定要运行的命令和传递给该命令的参数。centos
exec中的参数会当成什么数组被docker解析,所以必须使用双引号,而不能使用单引号。数组
shell形式:缓存
RUN yum install -y nginx
exec形式:安全
RUN ["yum", "install", "-y", "nginx"]
2. EXPOSE指令
告诉docker该容器内的应用程序将会指定端口,可是这并不意味着咱们能够自动访问该容器内服务的端口。出于安全的缘由,docker并不会自动打开该端口。而是须要咱们在使用docker run的时候经过-p参数指定须要打开哪些端口。咱们可使用多个EXPOSE指令向外部暴露多个端口。注意不能够在dockerfile中指定端口映射关系。好比EXPOSE 80,这条指令是对的,而EXPOSE 8080:80这条指令就是错的。
【注意】:这会影响docker的可移植性。咱们应该在docker run命令中使用-p参数实现端口映射。
3. CMD指令
CMD 指令容许用户指定容器启动的默认执行的命令。此命令会在容器启动且 docker run 没有指定其余命令时运行。
CMD 有三种格式:
Exec 格式:CMD ["executable","param1","param2"] 这是 CMD 的推荐格式。 CMD ["param1","param2"] 为 ENTRYPOINT 提供额外的参数,此时 ENTRYPOINT 必须使用 Exec 格式。 Shell 格式:CMD command param1 param2
第一种格式:运行一个可执行的文件并提供参数。
第二种格式 CMD ["param1","param2"] 要与 Exec 格式 的 ENTRYPOINT 指令配合使用,其用途是为 ENTRYPOINT 设置默认的参数。
第三种格式:是以”/bin/sh -c”的方法执行的命令。
下面看看 CMD 是如何工做的。Dockerfile 片断以下:
CMD echo "Hello world"
运行容器 docker run -it [image] 将输出:
Hello world
但当后面加上一个命令,好比 docker run -it [image] /bin/bash,CMD 会被忽略掉,命令 bash 将被执行:
[root@76a5fd777648 /]#
【注意】:使用CMD指令时,最好使用数组语法。
4. ENTRYPOIN指令
ENTRYPOINT 看上去与 CMD 很像,它们均可以指定要执行的命令及其参数。不一样的地方在于 ENTRYPOINT 不会被忽略,必定会被执行,即便运行 docker run 时指定了其余命令。
实际上docker run命令行中指定的任何参数都会被看成参数再次传递给ENTRYPOINT指令中指定的命令。
ENTRYPOINT 有两种格式:
Exec 格式:ENTRYPOINT ["executable", "param1", "param2"] 这是 ENTRYPOINT 的推荐格式。 Shell 格式:ENTRYPOINT command param1 param2
在为 ENTRYPOINT 选择格式时必须当心,由于这两种格式的效果差异很大。
ENTRYPOINT ["/usr/sbin/nginx", "-g", "daemon off;"]
(1)Exec 格式
ENTRYPOINT 的 Exec 格式用于设置要执行的命令及其参数,同时可经过 CMD 提供额外的参数。
ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数能够在容器启动时动态替换掉。
好比下面的 Dockerfile 片断:
ENTRYPOINT ["/bin/echo", "Hello"] CMD ["world"]
当容器经过 docker run -it [image] 启动时,输出为:
Hello world
而若是经过 docker run -it [image] CloudMan 启动,则输出为:
Hello CloudMan
(2)Shell 格式
ENTRYPOINT 的 Shell 格式会忽略任何 CMD 或 docker run 提供的参数。
【注意】:使用CMD和ENTRYPOINT时,请务必使用数组语法。
【总结】:
建立一个目录单独存放各类Dockerfile:
[root@node1 ~]# mkdir /opt/docker-file
建立Nginx目录存放Nginx Dockerfile和相关文件:
[root@node1 ~]# mkdir nginx [root@node1 ~]# cd nginx
上传openresty-1.9.7.3.tar.gz到Nginx目录下,编写Dockerfile文件:
[root@node1 nginx]# vim Dockerfile
该Dockerfile内容以下:
# This dockerfile is for openresty # Version 1.0 # Base images. FROM centos:centos7 # Author. MAINTAINER jkzhao <01115004@wisedu.com> # Add openresty software. ADD openresty-1.9.7.3.tar.gz /usr/local # Define working directory. WORKDIR /usr/local/openresty-1.9.7.3 # Install epel repo RUN rpm -ivh http://mirrors.aliyun.com/epel/epel-release-latest-7.noarch.rpm # Install openresty. RUN yum install -y readline-devel pcre-devel openssl-devel gcc perl make RUN ./configure && gmake && gmake install RUN sed -i '1 i\daemon off;' /usr/local/openresty/nginx/conf/nginx.conf RUN sed -i 's@#error_log logs\/error.log;@error_log logs\/error.log debug;@' /usr/local/openresty/nginx/conf/nginx.conf # Define environment variables. ENV PATH /usr/local/openresty/nginx/sbin:$PATH # Define default command. CMD ["nginx"] # Expose ports. EXPOSE 80
利用该Dockerfile构建镜像:
[root@node1 nginx]# docker build -t jkzhao/mynginx:v2 /opt/docker-file/nginx/
查看构建的镜像:
[root@node1 nginx]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE jkzhao/mynginx v2 f61afc8ce858 17 minutes ago 581.6 MB
基于镜像启动容器:
[root@node1 nginx]# docker run -d -p 84:80 jkzhao/mynginx:v2
访问Nginx服务:
若是dockerfile中某条指令执行失败了,没有正常结束,那么咱们也将获得一个可使用的镜像。这对调试很是有帮助,咱们能够基于该镜像,运行一个具备交互功能的容器,使用最后建立的镜像对为何咱们的指令会失败进行调试。
建立Ruby目录存放Ruby Dockerfile和相关文件:
[root@node1 ~]# mkdir ruby [root@node1 ~]# cd ruby
该Dockerfile内容以下:
FROM centos:7 MAINTAINER jkzhao <01115004@wisedu.com> # 为镜像添加备注信息 LABEL version="2.2.2" lang="ruby" ENV RUBY_MAJOR 2.2 ENV RUBY_VERSION 2.2.2 RUN yum install -y wget tar gcc g++ make automake autoconf curl-devel openssl-devel zlib-devel httpd-devel apr-devel apr-util-devel sqlite-devel RUN cd /tmp \ && wget http://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.2.tar.gz \ && tar zxvf ruby-2.2.2.tar.gz \ && cd ruby-2.2.2 \ && autoconf \ && ./configure --disable-install-doc \ && make \ && make install \ && rm -rf /tmp/ruby-2.2.2* # skip installing gem documentation RUN echo -e 'install: --no-document\nupdate: --no-document' >> "$HOME/.gemrc" ENV GEM_HOME /usr/local/bundle ENV PATH $GEM_HOME/bin:$PATH ENV BUNDLER_VERSION 1.10.6 RUN gem install bundler --version "$BUNDLER_VERSION" \ && bundle config --global path "$GEM_HOME" \ && bundle config --global bin "$GEM_HOME/bin" # don't create ".bundle" in all our apps ENV BUNDLE_APP_CONFIG $GEM_HOME CMD [ "irb" ]
dockerfile构建镜像过程很是聪明,它会将每一步的构建结果提交为一个镜像,咱们能够将以前指令建立的镜像层看作缓存,好比第5条指令因为命令书写错误而致使构建失败,当咱们修改它以后,前面的4条指令不会再执行,而是直接使用以前构建过程当中保存的缓存。这样速度会提升不少,大大减小了构建的时间。
然而有时候咱们要确保构建过程当中不会使用缓存,好比对yum update等操做,要想跳过缓存,
docker build no-cache
缓存还有个好处,就是调试方便。好比第5条指令失败了,那么咱们能够基于第4条指令建立的镜像启动一个新的容器。在容器里手工执行第5条指令所要的操做,调查指令失败的缘由。