Dockerfile

Dockerfile

基本结构

Dockerfile 是一个文本格式的配置文件,用户可以使用 Dockerfile 快速创建自定义镜像。

Dockerfile 由一行行命令语句组成,并且支持以 # 开头的注释行。

Docker分为四部分:

  • 基础镜像信息
  • 维护者信息
  • 镜像操作指令
  • 容器启动时默认要执行的指令

例如:

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: seancheng
# Command format: Instruction [arguments / command] ...

# 第一行必须指定基于的基础镜像
FROM ubuntu    

# 维护者信息
LABEL MAINTAINER=' xiaoyang xiaoyang@163.com'    // 作者署名及邮箱

# 镜像操作指令
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# 容器启动时默认要执行的指令
CMD /usr/sbin/nginx

其中,一开始必须指明所基于的镜像名称,接下来一般会说明维护者信息。
后面则是镜像操作指令,例如RUN指令,RUN指令将对镜像执行跟随的命令。每运行一条RUN指令,镜像添加新的一层,并提交。
最后是CMD指令来指定运行容器时的操作指令。

一、Dockerfile Introduction

前面的docker镜像管理章节有说到,构建镜像的方式有两种:一种是基于容器制作,另一种就是通过Dockerfile。Dockerfile其实就是我们用来构建Docker镜像的源码,当然这不是所谓的编程源码,而是一些命令的组合,只要理解它的逻辑和语法格式,就可以编写Dockerfile了。

简要概括Dockerfile的作用:它可以让用户个性化定制Docker镜像。因为工作环境中的需求各式各样,网络上的镜像很难满足实际的需求。

二、Dockerfile Format(格式)

Dockerfile整体就两类语句组成:

  • # Comment 注释信息
  • Instruction arguments 指令 参数,一行一个指令。
编写时应注意:
  • Dockerfile文件名首字母必须大写。

  • Dockerfile指令不区分大小写,但是为方便和参数做区分,通常指令使用大写字母。

  • Dockerfile中指令按顺序从上至下依次执行。

  • Dockerfile中第一个非注释行必须是FROM指令,用来指定制作当前镜像依据的是哪个基础镜像。

  • Dockerfile中需要调用的文件必须跟Dockerfile文件在同一目录下,或者在其子目录下,父目录或者其它路径无效。

三、Dockerfile Instructions

FROM
  • Introduction

    • FROM指令必须为Dockerfile文件开篇的第一个非注释行,用于指定构建镜像所使用的基础镜像,后续的指令运行都要依靠此基础镜像所提供的的环境(简单说就是假如Dockerfile中所引用的基础镜像里面没有mkdir命令,那后续的指令是没法使用mkdir参数的。)
    • 实际使用中,如果没有指定仓库,docker build会先从本机查找是否有此基础镜像,如果没有会默认去Docker Hub Registry上拉取,再找不到就会报错。
  • Syntax(语法)

    • FROM <Repository>[:<Tag>]
    • FROM <Repository>@<Digest>
      • Digest:镜像的哈希码,防止镜像被冒名顶替。
MAINTAINER(维护者)
  • Introduction

    • 用于让Dockerfile的作者提供个人的信息
    • Dockerfile并不限制MAINTAINER指令的位置,但是建议放在FROM指令之后
    • 在较新的docker版本中,已经被LABEL替代。
  • Syntax(语法)

    • MAINTAINER “xiaoyang@163.com”
COPY
  • Introduction

    • 复制宿主机上的文件到目标镜像中
  • Syntax(语法)

    • COPY <src>… <dest>
    • COPY ["<src>",… “<dest>”]
      • <src>:要复制的源文件或者目录,支持通配符
      • <dest>:目标路径,即正创建的镜像的文件系统路径,建议使用绝对路径,否则,COPY指令会以WORKDIR为其起始路径。
      • 如果路径中如果包含空白字符,建议使用第二种格式用引号引起来,否则会被当成两个文件。
  • Rules

    • <src>必须是build上下文中的目录,不能是其父目录中的文件。
    • 如果<src>是目录,则其内部的文件或则子目录会被递归复制,但<src>目录本身不会被复制。
    • 如果指定了多个<src>,或者<src>中使用通配符,则<dest>必须是一个目录,且必须以 / 结尾。
    • 如果<dest>事先不存在,它将会被自动创建,包括其父目录路径。
ADD
  • Introduction

    • ADD指令跟COPY类似,不过它还支持使用tar文件和URL路径。
      • 当拷贝的源文件是tar文件时,会自动展开为一个目录并拷贝进新的镜像中;然而通过URL获取到的tar文件不会自动展开。
    • 主机可以联网的情况下,docker build可以将网络上的某文件引用下载并打包到新的镜像中。
  • Syntax(语法)

    • ADD<src>… <dest>
    • ADD ["<src>",… “<dest>”]
WORKDIR
  • Introduction

    • 同docker run -w
    • 指定工作目录,可以指多个,每个WORKDIR只影响他下面的指令,直到遇见下一个WORKDIR为止。
    • WORKDIR也可以调用由ENV指令定义的变量。
  • Syntax(语法)

    • WORKDIR 相对路径或者绝对路径
    • 相对路径是相对于上一个WORKDIR指令的路径,如果上面没有WORKDIR指令,那就是当前Dockerfile文件的目录。
VOLUME
  • Introduction

    • docker run -v简化版
    • 用于在镜像中创建一个挂载点目录。上一章中有提到Volume有两种类型:绑定挂载卷和docker管理的卷。在dockerfile中只支持docker管理的卷,也就是说只能指定容器内的路径,不能指定宿主机的路径。
  • Syntax(语法)

  • VOLUME <mountpoint>

  • VOLUME ["<mountpoint>"]

EXPOSE
  • Introduction

    • 同docker run --expose
    • 指定容器中待暴露的端口。比如容器提供的是一个https服务且需要对外提供访问,那就需要指定待暴露443端口,然后在使用此镜像启动容器时搭配 -P 的参数才能将待暴露的状态转换为真正暴露的状态,转换的同时443也会转换成一个随机端口,跟 -p :443一个意思。
    • EXPOSE指令可以一次指定多个端口,例如:EXPOSE 11111/udp 11112/tcp
  • Syntax(语法)

    • EXPOSE <port>[/<protocol>] [<port>[/<protocol>] …]
    • <protocol>用于指定协议类型,如果不指定,默认TCP协议。
ENV
  • Introduction

    • 同docker run -e
    • 为镜像定义所需的环境变量,并可被ENV指令后面的其它指令所调用。调用格式为 v a r i a b l e n a m e 或 者 variable_name或者 variablename{variable_name}
    • 使用docker run启动容器的时候加上 -e 的参数为variable_name赋值,可以覆盖Dockerfile中ENV指令指定的此variable_name的值。但是不会影响到dockerfile中已经引用过此变量的文件名。下面有举例说明:
  • Syntax(语法)

    • ENV <key> <value>
    • ENV <key>=<value> …
      • 第一种格式一次只能定义一个变量,之后所有内容都会被视为的组成部分
      • 第二种格式一次可以定义多个变量,每个变量为一个"="的键值对,如果中包含空格,可以用反斜线 \ 进行转义,也可以为加引号,另外参数过长时可用反斜线做续行。
      • 定义多个变量时,建议使用第二种方式,因为Dockerfile中每一行都是一个镜像层,构建起来比较吃资源。
  • Example (事例)

基于busybox启动一个镜像,将test文件拷贝至容器内的/usr/local/abc/目录下。
[root@docker ~]# echo "hello" >/opt/test
[root@docker ~]# cd /opt/
[root@docker opt]# ls
containerd  test  web

[root@docker opt]# vim dockerfile //编写dockerfile文件
# Description: test image
FROM busybox
ENV file=abc
ADD ./test /usr/local/$file/
[root@docker opt]# docker build -t busy:v1 ./     生成镜像
Sending build context to Docker daemon  15.87kB
Step 1/3 : FROM busybox
 ---> d23834f29b38
Step 2/3 : ENV file=abc
 ---> Running in 14746f7f8682
Removing intermediate container 14746f7f8682
 ---> 747fe5b741b7
Step 3/3 : ADD ./test /usr/local/$file/
 ---> d4ba46729151
Successfully built d4ba46729151
Successfully tagged busy:v1

#查看新生成的镜像
[root@docker opt]# docker images
REPOSITORY            TAG       IMAGE ID       CREATED         SIZE
busy                  v1        d4ba46729151   4 minutes ago   1.24MB
busybox               latest    d23834f29b38   7 days ago      1.24MB

# 根据此镜像启动容器并查看文件是否拷贝成功,并且查看file变量的值
[root@docker opt]# docker run --name busy --rm busy:v1 ls /usr/local/abc
test

[root@docker opt]# docker run --name busy --rm busy:v1 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=051371212bbd
file=abc
HOME=/root


# 接下来我们在启动容器的时候加上-e参数为file变量指定一个新值,并且查看file变量的值
[root@docker opt]# docker run --name busy -e file=bug --rm busy:v1 printenv
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=277595f1e5e7
file=bug
HOME=/root

# 此时再看test的文件,依然是在abc的目录下的
[root@docker opt]# docker run --name busy -e file=bug --rm busy:v1 ls /usr/local/abc
test

# 这是因为docker build属于第一阶段,而docker run属于第二阶段。第一阶段定义file变量的值abc已经被引用了,后续阶段再改file变量的值也影响不了abc。
RUN
  • Introduction

    • 用于指定docker build过程中运行的程序,可以是任何命令。
    • RUN指令后所执行的命令必须在FROM指令后的基础镜像中存在才行。
  • Syntax

    • RUN <command>
    • RUN [“executable”, “param1”, “param2”]
      • <command>通常是一个shell命令,系统默认会把后面的命令作为shell的子进程来运行,以"/bin/sh -c"来运行它,也就意味着此进程在容器中的PID一定不为1,如果是1完事就结束了哇。
      • 第二种格式的参数是一个JSON格式的数组,其中"executable"为要运行的命令,后面的"paramN"为传递给命令的选项或参数。此格式指定的命令不会以"/bin/sh -c"来发起,也就是直接由内核创建,因此不具备shell特性,类似于RUN [ “echo”, “$HOME” ],是无法识别 $ 的;如果想要依赖shell特性,可以替换命令为这样的格式[ “/bin/sh”, “-c”, “echo $HOME” ]。
CMD
  • Introduction

    • 指定启动容器的默认要运行的程序,也就是PID为1的进程命令,且其运行结束后容器也会终止。如果不指定,默认是bash。
    • CMD指令指定的默认程序会被docker run命令行指定的参数所覆盖。
    • Dockerfile中可以存在多个CMD指令,但仅最后一个生效。因为一个docker容器只能运行一个PID为1的进程。
    • 类似于RUN指令,也可以运行任意命令或程序,但是两者的运行时间点不同
      • RUN指令运行在docker build的过程中,而CMD指令运行在基于新镜像启动容器(docker run)时。
  • Syntax(语法)

    • CMD command param1 param2 在/bin/sh中执行,提供给需要交互的应用
    • CMD [“executable”,“param1”,“param2”] 使用exec执行,推荐方式
    • CMD [“param1”,“param2”] 提供给ENTRYPOINT的默认参数

前两种语法格式同RUN指令。第一种用法对于CMD指令基本没有意义,因为它运行的程序PID不为1。
第三种则需要结合ENTRYPOINT指令使用,CMD指令后面的命令作为ENTRYPOINT指令的默认参数。如果docker run命令行结尾有参数指定,那CMD后面的参数不生效。

ENTRYPOINT
  • Introduction

    • 类似CMD指令的功能,用于为容器指定默认运行程序。
    • Dockerfile中可以存在多个ENTRYPOINT指令,但仅最后一个生效
    • 与CMD区别在于,由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且这些命令行参数会被当做参数传递给ENTRYPOINT指令指定的程序。
    • 不过,docker run的–entrypoint选项的参数可覆盖ENTRYPOINT指定的默认程序。示例如下:
  • Syntax(语法)

    • ENTRYPOINT command param1 param2
    • ENTRYPOINT [“executable”, “param1”, “param2”]
  • Example(事例)

以httpd服务做举例,CMD指令
[root@docker opt]# vim dockerfile  //编写 部署站点
# Description: test image
FROM busybox
ENV WEBDIR="/usr/web/html"
RUN mkdir -p ${WEBDIR} && \
    echo 'this is a test web' > ${WEBDIR}/index.html
CMD [ "sh","-c","/bin/httpd -f -h ${WEBDIR}" ]
[root@docker opt]# docker build -t httpd:v1 ./

此时为前台运行,重启打开一个窗口,kill掉容器,然后开始docker run结尾传入新的指令
[root@docker opt]# docker run --name web -it --rm httpd:v1
[root@docker local]# docker kill web
web
[root@docker local]# docker run --name web -it --rm httpd:v1 ls /usr/web/html   //发现此时的执行命令为 ls
index.html

# 可以看出命令行的参数已经替代了原本的CMD指令指定的程序

# 用ENTRYPOINT指令做测试。
[root@docker opt]# vim dockerfile 
# Description: test image
FROM busybox
ENV WEBDIR="/usr/web/html"
RUN mkdir -p ${WEBDIR} && \
    echo 'this is a test web' > ${WEBDIR}/index.html
ENTRYPOINT [ "sh","-c","/bin/httpd -f -h ${WEBDIR}" ]

# 生成镜像
[root@docker opt]# docker build -t httpd:v2 ./     
[root@docker opt]# docker run --name sed -it --rm httpd:v2   # 也是前台启动,重新一个窗口,kill掉容器,然后开始docker run结尾传入新的指令

[root@docker opt]# docker run --name sed -it --rm httpd:v2 ls /usr/web/html

# 可以看到没有反应,这种情况其实是吧ls /usr/web/html当做参数传给了/bin/httpd -f -h ${WEBDIR}程序。只是httpd不识别。

# 我们kill掉容器, 加上--entrypoint参数再试一下
[root@docker opt]# docker run --name sed1 -it --rm --entrypoint="" httpd:v2 ls /usr/web/html
index.html

使用--entrypoint参数替换命令成功。
USER
  • Introduction

    • 用于指定docker build过程中任何RUN、CMD等指令的用户名或者UID。
    • 默认情况下容器的运行用户为root。
  • Syntax

    • USER <user>[:<group>]
    • USER <UID>[:<GID>]
      • 实践中UID需要是/etc/passwd中某用户的有效UID,否则docker run命令将运行失败。
ONBUILD
  • Introduction
    • 用于在Dockerfile中定义一个触发器。
    • ONBUILD后面指定的指令在docker build时是不会执行,构建完的镜像在被另一个Dockerfile文件中FROM指令所引用的时才会触发执行。
  • Syntax(语法)
    • ONBUILD [INSTRUCTION]
      • 几乎任何指令都可以成为触发器指令,但ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令,多数情况是使用RUN或者ADD。
      • 另外在使用COPY指令时,应该注意后续引用该镜像的Dockerfile的同级目录下是否有被拷贝的文件。
  • Example
例如,Dockerfile使用如下的内容创建了镜像image-A

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]
此时,如果基于image-A创建新的镜像时,新的Dockerfile中使用FROM image-A指定基础镜像时,会自动执行ONBUILD指令的内容,等价于在后面添加了两条指令。

FROM image-A

# Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src
使用ONBUILD指令的镜像,推荐在标签中注明,例如ruby:1.9-onbuild。

创建镜像

编写完成Dockerfile后,可以通过docker build命令来创建镜像。

基本的格式为docker build [选项] 路径,该命令将读取指定路径下(包括子目录)的Dockerfile,并将该路径下所有内容发送给Docker服务端,由服务端来创建镜像。因此一般建议放置Dockerfile的目录为空目录。

另外,可以通过 .dockerignore 文件(每一行添加一条匹配模式)来让Docker忽略路径下的目录和文件。

要指定镜像的标签信息,可以通过-t选项。

例如,指定Dockerfile所在路径为/tmp/docker_builder/,并且希望生成镜像标签为build_repo/first_image,可以使用下面的命令:

docker build -t build_repo/first_image /tmp/docker_builder/
源码apache的Dockerfile文件

目录结构

[root@docker apache]# tree /apache/
/apache/
├── dockerfile
└── files
    ├── apr-1.7.0.tar.gz
    ├── apr-util-1.6.1.tar.gz
    └── httpd-2.4.48.tar.gz


和脚本一样,在编写前先创建相关目录以及源码包

[root@docker ~]# mkdir -p /apache/files
[root@docker ~]# ls /apache/
files

将相关的源码包上传至指定目录

[root@docker ~]# ls /apache/files/
apr-1.7.0.tar.gz  apr-util-1.6.1.tar.gz  httpd-2.4.48.tar.gz

编写dockerfile文件

[root@docker ~]# vim /apache/dockerfile 
FROM centos
LABEL MAINTAINER='aoman xx@163.com'
ENV apr_version=1.7.0 apr_util_version=1.6.1 httpd_version=2.4.48
ENV dir=/usr/src/ install_dir=/usr/local/
ADD files/* ${dir}
WORKDIR ${dir}
RUN yum -y install openssl-devel pcre-devel pcre  expat-devel libtool gcc gcc-c++ make  && \
    cd apr-${apr_version}   &&       sed -i '/$RM "$cfgfile"/d' configure && \
    ./configure --prefix=${install_dir}/apr  &&     make && make install  && \
    cd ../apr-util-${apr_util_version}  && \
    ./configure --prefix=${install_dir}/apr-util --with-apr=${install_dir}/apr  && \
    make && make install  &&  \
    cd ../httpd-$httpd_version    &&  \
    ./configure --prefix=${install_dir}/apache \
    --enable-so \
    --enable-ssl \
    --enable-cgi \
    --enable-rewrite \
    --with-zlib \
    --with-pcre \
    --with-apr=${install_dir}/apr \
    --with-apr-util=${install_dir}/apr-util/ \
    --enable-modules=most \
    --enable-mpms-shared=all \
    --with-mpm=prefork   && \
    make && make install && \
    sed -i '/#ServerName/s/#//g' ${install_dir}/apache/conf/httpd.conf
EXPOSE 80  443
VOLUME ["${install_dir}/apache/htdocs/"]
CMD ["-D","FOREGROUND"]
ENTRYPOINT ["/usr/local/apache/bin/httpd"]


构建镜像

[root@docker ~]# docker build -t xm17671855780/file_httpd:v0.1
Sending build context to Docker daemon   11.1MB
Step 1/9 : FROM centos
 ---> 5d0da3dc9764
Step 2/9 : LABEL MAINTAINER='bravealove1 593582553@qq.com'
 ---> Using cache
 ---> 0ac4d3e6c784
Step 3/9 : ADD  files/* /usr/src/
 ---> Using cache
 ---> 1a0a3fe9d89f
Step 4/9 : WORKDIR /usr/src/
 ---> Using cache
 ---> 61a8f1dbb8ab
Step 5/9 : RUN yum -y install openssl-devel pcre-devel pcre  expat-devel libtool gcc gcc-c++ make  &&     cd apr-1.7.0   &&       sed -i '/$RM "$cfgfile"/d' configure &&     ./configure --prefix=/usr/local/apr  &&     make && make install  &&     cd ../apr-util-1.6.1  &&     ./configure --prefix=/usr/local/apr-util --with-apr=/usr/local/apr  &&     make && make install  &&      cd ../httpd-2.4.49    &&      ./configure --prefix=/usr/local/apache     --enable-so     --enable-ssl     --enable-cgi     --enable-rewrite     --with-zlib     --with-pcre     --with-apr=/usr/local/apr     --with-apr-util=/usr/local/apr-util/     --enable-modules=most     --enable-mpms-shared=all     --with-mpm=prefork   &&     make && make install
 ---> Using cache
 ---> d400eb8c4286
Removing intermediate container d798f042fe3a
 ---> 754ad8ba9043
Step 6/8 : EXPOSE 80  443
 ---> Running in aac357026dfb
Removing intermediate container aac357026dfb
 ---> aaa530543669
Step 7/8 : VOLUME ["/usr/local/apache/htdocs/"]
 ---> Running in e7782ec5a8ae
Removing intermediate container e7782ec5a8ae
 ---> 1cab72f37a7b
Step 8/8 : CMD  ["/usr/local/apache/bin/apachectl","-D","FOREGROUND"]
 ---> Running in c4607cd29504
Removing intermediate container a4607cd29504
 ---> c7598a3b24b3
Successfully built a563454c4a5f
Successfully tagged xm17671855780/file_httpd:v0.2


使用新的镜像运行一个容器进行测试

[root@docker apache]# docker images
REPOSITORY                 TAG       IMAGE ID       CREATED              SIZE
xm17671855780/file_httpd   v0.2     a563454c4a5f   About a minute ago   701MB
centos                     latest    5d0da3dc9764   2 months ago         231MB

[root@docker ~]# docker run -dit --name apache -p 80:80 a563454c4a5f
2bd14742fa4bc405728bfdf01fc70d78c2968c4eaa50e5b45eb68d3a672c9293

[root@docker apache]# ss -anlt
State               Recv-Q               Send-Q                             Local Address:Port                             Peer Address:Port              
LISTEN              0                    128                                      0.0.0.0:111                                   0.0.0.0:*                 
LISTEN              0                    128                                      0.0.0.0:80                                    0.0.0.0:*                 
LISTEN              0                    128                                      0.0.0.0:22                                    0.0.0.0:*                 
LISTEN              0                    128                                         [::]:111                                      [::]:*                 
LISTEN              0                    128                                         [::]:80                                       [::]:*                 
LISTEN              0                    128                                         [::]:22                                       [::]:*                 

访问测试
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值