Docker Dockerfile 常用指令详解

关于Dockerfile


在Docker中创建镜像最常用的方式,就是使用Dockerfile。Dockerfile是一个Docker镜像的描述文件,我们可以理解成火箭发射的A、B、C、D…的步骤。Dockerfile其内部包含了一条条的指令每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建

一个Dockerfile的示例如下所示: 

#基于centos镜像
FROM centos

#维护人的信息
MAINTAINER The CentOS Project

#安装httpd软件包
RUN yum -y update
RUN yum -y install httpd

#开启80端口
EXPOSE 80

#复制网站首页文件至镜像中web站点下
COPY index.html /var/www/html/index.html

#复制该脚本至镜像中,并修改其权限
COPY  run.sh /run.sh
RUN chmod 775 /run.sh

#当启动容器时执行的脚本文件
CMD ["/run.sh"]

由上可知,Dockerfile结构大致分为四个部分:

  (1)基础镜像信息

  (2)维护者信息

  (3)镜像操作指令

  (4)容器启动时执行指令。

  Dockerfile每行支持一条指令,每条指令可带多个参数,支持使用以#号开头的注释。下面会对上面使用到的一些常用指令做一些介绍。

DockerFile构建过程解析


基础知识

  • 每条保留字指令都必须为大写字母后面要跟随至少一个参数
  • 指令从上到下顺序执行 
  • #表示注释
  • 每条指令都会创建一个新的镜像层,并对镜像进行提交

大致流程

  1. docker从基础镜像运行一个容器
  2. 执行一条指令并对容器进行修改
  3. 执行类似于docker commit的操作提交一个新的镜像
  4. docker再基于别提交的新的镜像运行一个新的容器
  5. 执行dockerfile的下一个指令再从执行第2点直到没有指令

构建镜像对的命令


docker build -t nginx:v1 .

docker build -t 构建镜像   nginx:v1 构建镜像的名称和标签      . 代表当前目录(不完全正确,这里指的上下文)

举两个例子:

错误用法   copy ../index.html  /app (超越了上下文路径)
正确用法   copy ./test.html  /app

      首先我们要理解 docker build 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 Docker Remote API,而如 docker 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 docker 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。

    当我们进行镜像构建的时候,并非所有定制都会通过 RUN 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 COPY 指令、ADD 指令等。而 docker build 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?

     这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。

Dockerfile常用指令


FROM:指明构建的新镜像是来自于哪个基础镜像,例如:

FROM centos:6

From:选择基础镜像,选择基础镜像的时候就需要去考虑到底需要什么样的基础镜像,需要完备的操作系统呢,比如from unbuntu centos?

完备操作系统好处是什么指令都有,很方便,但是缺点也明显,第一就是很大,它传输效率就慢,同时占用了磁盘空间,对存储的要求就高了。第二,更严重的问题就是,万一你的base image里面有安全漏洞,包含了很多工具,这样会对你的生产系统造成破坏。

但是不是基于操作系统,比如基于stratch,那么你想进入容器调试容器,那么就丧失了这种能力,没有这些工具。

所以docker会推荐有个小的基础镜像,比如apline,也就是一个小的linux,然后在这个基础之上,需要什么工具,你可以使用命令基本的安装一下,这样就具备了基本的调试能力,之后在这基础之上安装你的应用。当然啥都不装也是可以的,那可以去主机上面做debug

通过lables可以将容器镜像,打上各种不同的标签,这个标签作用就是通过docker images命令去查看的时候可以通过-f的参数来filter来查询某类容器镜像,容器镜像比较多的时候还是很有用的。

RUN就是用来执行一些指令的,如果要去安装一个中间件的时候,这两条指令RUN apt-get update && apt-install连起来,如果分开执行,apt-agt update就会被缓存,新的包在下次执行的时候它就不会在更新了。后面的install就会失败。 

容器最后运行什么命令使用CMD,这里面可以指定可执行文件是什么,所定义的参数是什么。

MAINTAINER:指明镜像维护着及其联系方式(一般是邮箱地址),例如:

MAINTAINER Edison Zhou <edisonchou@hotmail.com>

RUN:构建镜像时运行的Shell命令,例如:

RUN ["yum", "install", "httpd"]
RUN yum install httpd

   又如,我们在使用微软官方ASP.NET Core Runtime镜像时往往会加上以下RUN命令,弥补无法在默认镜像下使用Drawing相关接口的缺憾:

FROM microsoft/dotnet:2.2.1-aspnetcore-runtime
RUN apt-get update
RUN apt-get install -y libgdiplus
RUN apt-get install -y libc6-dev
RUN ln -s /usr/lib/libgdiplus.so /lib/x86_64-linux-gnu/libgdiplus.so

CMD:启动容器时执行的Shell命令,例如:

CMD ["-C", "/start.sh"]
CMD ["/usr/sbin/sshd", "-D"]
CMD /usr/sbin/sshd -D

CMD类似于RUN的命令,用于运行程序,但二者的运行时间不同:

  • CMD 在docker run 时运行。

  • RUN 是在 docker build。

 EXPOSE:声明容器运行的服务端口,例如:

EXPOSE 80 443

expose用处不是特别大,就是发布端口,这样用处其实不是特别大,即使你不通过expose这个指令,事实上你的应用监听在80端口,那么80端口就能够对外访问。expose是镜像提供者告诉使用镜像的人服务发布的端口。

EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务.

在Dockerfile中写入这样的声明有两个好处:

  1. 帮助镜像使用者理解这个镜像服务的端口,以方便配置映射;

  2. 另个用处是在运行时使用随机端口映射时,也就是 docker run -P,会自动随机映射

启动tomcat:docker run -p 8080:8080 tomcat

    -P:左边宿主机端口,右边容器端口

    -p:宿主机段端口随机分配一个端口,让外部进行访问

 ENV:设置环境内环境变量,例如:

ENV MYSQL_ROOT_PASSWORD 123456
ENV JAVA_HOME /usr/local/jdk1.8.0_45

ADD:拷贝文件或目录到镜像中,例如:

ADD <src>...<dest>
ADD html.tar.gz /var/www/html
ADD https://xxx.com/html.tar.gz /var/www/html

ADD 命令和COPY命令差不多,比COPY高级

  • ADD 的优点:在执行 <源文件> 为 tar 压缩文件的话,压缩格式为 gzip, bzip2 以及 xz 的情况下,会自动复制并解压到 <目标路径>。

  • ADD 的缺点:在不解压的前提下,无法复制 tar 压缩文件。会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。具体是否使用,可以根据是否需要自动解压来决定。

  • PS:如果是URL或压缩包,会自动下载或自动解压。

处理from去指定一个image,除了apt-get去安装一个中间件,很多时候还是要从构建好的可执行文件加入到镜像里面,这里就需要拷贝和添加的动作。

在add时候可以改变文件的权限,最终就是告诉源文件和目标文件是什么。

值得注意的是ADD不是简单拷贝复制的动作,还有些附加的动作,比如本地是压缩文件,它在ADD的时候就可以去解压,所以不是那么直观。

 

COPY:拷贝文件或目录到镜像中,用法同ADD,只是不支持自动下载和解压,例如:

COPY ./start.sh /start.sh

COPY 复制文件:

    COPY <源路径>... <目标路径>

    COPY ["<源路径1>",... "<目标路径>"]    目标路径也可以成为镜像的目录

    COPY 指令将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置。

    例如:COPY package.json /usr/src/app/

ENTRYPOINT:

entrypoints上面是可执行文件 如果 这样写

ENTRYPOINT ["/bin/java -jar demo.jar"]
CMD ["-Xmx512m -Xms512m","--server.port=8000"]

会报错  unable to start container process: exec: "/bin/java -jar demo.jar": stat /bin/java -jar demo.jar: no such file or directory: unknown.

因为/bin/java -jar demo.jar这不是一条可执行文件 

entrypoint才是让容器面向应用的一个主要因素,容器镜像是面向应用的,一般生产系统里面不会有centos镜像跑在那,要不是http server,以及各种各样的服务。

所以容器镜像是面向某个应用的,entrypoint指定启动容器的时候要启动哪些服务,有了这个之后,容器镜像在加载之后不仅仅是一个操作系统,它可以是操作系统+应用,又可以是没有操作系统只有应用,所以是面向应用构建的容器镜像。

entrypoint一般是dockerfile的最后一条指令。

entrypoint最佳实践是:entrypoint定义主命令,cmd来定义它主要参数,这样在容器启动时候就是启动entrypoint这个命令并且附加cmd的参数。

启动容器时执行的Shell命令,同CMD类似,只是由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖,而且,这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序,例如:

ENTRYPOINT ["/bin/bash", "-C", "/start.sh"]
ENTRYPOINT /bin/bash -C '/start.sh'

PS:Dockerfile文件中也可以存在多个ENTRYPOINT指令,但仅有最后一个会生效。

VOLUME:指定容器挂载点到宿主机自动生成的目录或其他容器,例如:

VOLUME ["/var/lib/mysql"]

PS:一般不会在Dockerfile中用到,更常见的还是在docker run的时候指定-v数据卷。

 作用:Docker容器的数据持久化.

定义:数据卷是一个可以提供一个或多个容器使用的特殊目录,它绕过UFS

 特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立即生效
  • 对数据卷的更新,不会影响镜像
  • 数据卷会默认一直存在,即使容器被删除
  • 命令:-v数据卷启动
docker run -p 8080:8080 --name tomcat -d -v usr/local/docker/tomcat/ROOT:usr/local/tomcat/webapps/ROOT tomcat

USER:为RUN、CMD和ENTRYPOINT执行Shell命令指定运行用户,例如:

USER <user>[:<usergroup>]
USER <UID>[:<UID>]
USER edisonzhou

WORKDIR:为RUN、CMD、ENTRYPOINT以及COPY和AND设置工作目录,例如:

WORKDIR /data

HEALTHCHECK:告诉Docker如何测试容器以检查它是否仍在工作,即健康检查,例如:

HEALTHCHECK --interval=5m --timeout=3s --retries=3 \
    CMD curl -f http:/localhost/ || exit 1

其中,一些选项的说明:

  •  --interval=DURATION (default: 30s):每隔多长时间探测一次,默认30秒

  •  -- timeout= DURATION (default: 30s):服务响应超时时长,默认30秒

  •  --start-period= DURATION (default: 0s):服务启动多久后开始探测,默认0秒

  •  --retries=N (default: 3):认为检测失败几次为宕机,默认3次

  一些返回值的说明:

  •  0:容器成功是健康的,随时可以使用

  •  1:不健康的容器无法正常工作

  •  2:保留不使用此退出代码

ARG 作用范围

Docker中传递变量主要使用ARG和ENV,虽然功能相同,但是他们的作用范围是不一样的

ARG传递变量:
ARG只在Dockerfile中生效,且在docker build阶段生效,构建好的镜像内不存在此环境变量。意味着在容器启动后ARG定义的变量已经无效,如果想让其生效,需要将其赋值给ENV。

#Dockerfile

From java

VOLUME /tmp

ARG JAR_FILE=*.jar

COPY ${JAR_FILE} helloworld.jar

ARG OPTS="-Xmx512m -Xms512m -Dspring.profiles.active=test"

ENV JAVA_OPTS=${OPTS}

ENTRYPOINT ["/bin/sh","-c","java $JAVA_OPTS -jar /helloworld.jar"]

#build
docker build -t helloworld:v1 .

#run
docker run -p 8080:8080 --name helloworld helloworld:v1

#docker exec -it 29a90e0be2b3 /bin/bash

root@29a90e0be2b3:/# ps -ef|grep java
root 6 1 10 13:22 ? 00:00:07 java -Xmx512m -Xms512m -Dspring.profiles.active=test -jar /helloworld.jar

在构建镜像时,指定一些参数,例如:

FROM centos:6
ARG user # ARG user=root
USER $user

这时,我们在docker build时可以带上自定义参数user了,如下所示:

docker build --build-arg user=edisonzhou Dockerfile .

综合Dockerfile案例


下面是一个Java Web应用的镜像Dockerfile,综合使用到了上述介绍中最常用的几个命令:

FROM centos:7
MAINTANIER www.edisonchou.com

ADD jdk-8u45-linux-x64.tar.gz /usr/local
ENV JAVA_HOME /usr/local/jdk1.8.0_45

ADD apache-tomcat-8.0.46.tar.gz /usr/local
COPY server.xml /usr/local/apache-tomcat-8.0.46/conf

RUN rm -f /usr/local/*.tar.gz

WORKDIR /usr/local/apache-tomcat-8.0.46
EXPOSE 8080
ENTRYPOINT ["./bin/catalina.sh", "run"]

有了Dockerfile,就可以创建镜像了:

docker build -t edc-tomcat:v1 .

最后,可以通过以下命令创建容器:

docker run -itd --name=tomcate -p 8080:8080 \
    -v /app/webapps/:/usr/local/apache-tomcat-8.0.46/webapps/ \
    edc-tomcat:v1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值