Dockerfile 是什么?

Dockerfile 是由一系列命令和参数构成的脚本,是一个包含创建镜像所有命令的脚本文件,通过docker build命令执行Dockerfile中的一系列命令自动构建镜像

Dockerfile 是 shell 脚本吗?

不是,Dockerfile 提供RUN这样的命令,看起来很像.sh脚本,同时也能运行shell命令,但 Dockerfile 还是和 shell 不一样,Dockerfile 实际上是描述镜像的每一层要如何构建,Dockerfile 中每一个指令都会建立一层,所以每一个RUN是一个独立的一层。所以一定要理解“分层存储”的概念。上一层的东西不会被物理删除,而是会保留给下一层,下一层中可以指定删除这部分内容,但实际上只是这一层做的某个标记,说这个路径的东西删了。但实际上并不会去修改上一层的东西。每一层都是静态的,这也是容器本身的 immutable 特性,要保持自身的静态特性

所以很多新手会常犯下面这样的错误,把 Dockerfile 当做 shell 脚本来写了:

RUN yum update
RUN yum -y install gcc
RUN yum -y install python
ADD jdk-xxxx.tar.gz /tmp
RUN cd xxxx && install
RUN xxx && configure && make && make install

这是相当错误的。除了无畏的增加了很多层,而且很多运行时不需要的东西,都被装进了镜像里,比如编译环境、更新的软件包等等。结果就是产生非常臃肿、非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错

正确的写法应该是把同一个任务的命令放到一个 RUN 下,多条命令应该用 && 连接,并且在最后要打扫干净所使用的环境。比如下面这段摘自官方 redis 镜像 Dockerfile 的部分:

RUN buildDeps='gcc libc6-dev make' \
    && set -x \
    && apt-get update && apt-get install -y $buildDeps --no-install-recommends \
    && rm -rf /var/lib/apt/lists/* \
    && wget -O redis.tar.gz "$REDIS_DOWNLOAD_URL" \
    && echo "$REDIS_DOWNLOAD_SHA1 *redis.tar.gz" | sha1sum -c - \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && rm redis.tar.gz \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

那我把所有命令都合并到一个 RUN 就对了吧?

不是把所有命令都合为一个 RUN,要合理分层,以加快构建和部署,合理分层就是将具有不同变更频繁程度的层,进行拆分,让稳定的部分在基础,更容易变更的部分在表层,使得资源可以重复利用,以增加构建和部署的速度

以 node.js 的应用示例镜像为例,其中的复制应用和安装依赖的部分,如果都合并一起,会写成这样:

COPY . /usr/src/app
RUN npm install

但是,在 node.js 应用镜像示例中,则是这么写的:

COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app

从层数上看,确实多了一层。但实际上,这三行分开是故意这样做的,其目的就是合理分层,充分利用 Docker 分层存储的概念,以增加构建、部署的效率

在 docker build 的构建过程中,如果某层之前构建过,而且该层未发生改变的情况下,那么 docker 就会直接使用缓存,不会重复构建。因此,合理分层,充分利用缓存,会显著加速构建速度

第一行的目的是将 package.json 复制到应用目录,而不是整个应用代码目录。这样只有 pakcage.json 发生改变后,才会触发第二行 RUN npm install。而只要 package.json 没有变化,那么应用的代码改变就不会引发 npm install,只会引发第三行的 COPY . /usr/src/app,从而加快构建速度

而如果按照前面所提到的,合并为两层,那么任何代码改变,都会触发 RUN npm install,从而浪费大量的带宽和时间。

合理分层除了可以加快构建外,还可以加快部署,要知道,docker pull 的时候,是分层下载的,并且已存在的层就不会重复下载。

比如,这里的 RUN npm install 这一层,往往会几百 MB 甚至上 GB。而在 package.json 未发生变更的情况下,那么只有 COPY . /usr/src/app 这一层会被重新构建,并且也只有这一层会在各个节点 docker pull 的过程中重新下载,往往这一层的代码量只有几十 MB,甚至更小。这对于大规模的并行部署中,所节约的东西向流量是非常显著的。特别是敏捷开发环境中,代码变更的频繁度要比依赖变更的频繁度高很多,每次重复下载依赖,会导致不必要的流量和时间上的浪费

Dockerfile 的基本语法结构

FROM MAINTAINER RUN CMD EXPOSE ENV ADD COPY ENTRYPOINT VOLUME USER WORKDIR ONBUILD

FROM         ubuntu:14.10
MAINTAINER    linx

#把java与tomcat添加到容器中
ADD jdk-8u31-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-8.0.20.tar.gz /usr/local/

#配置java与tomcat环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_31
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.0.20
ENV CATALINA_BASE /usr/local/apache-tomcat-8.0.20
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin

#容器运行时监听的端口
EXPOSE  8080

FROM

FROM指定构建镜像的基础源镜像,如果本地没有指定的镜像,则会自动从 Docker 的公共库 pull 镜像下来 FROM必须是 Dockerfile 中非注释行的第一个指令,即一个 Dockerfile 从FROM语句开始 FROM可以在一个 Dockerfile 中出现多次,如果有需求在一个 Dockerfile 中创建多个镜像 如果FROM语句没有指定镜像标签,则默认使用latest标签

MAINTAINER

命令允许您设置生成的镜像的作者字段

RUN

RUN指令将在当前image之上的新层中执行任何命令,并提交结果。生成的已提交image将用于Dockerfile中的下一步 分层RUN指令和生成提交符合Docker的核心概念,其中提交很轻量,可以从image历史中的任何点创建容器,就像源代码控制一样

ADD

复制本地主机文件、目录或者远程文件 URLS,还可以是一个tar文件(自动解压为目录)添加到容器指定路径中

CMD

CMD指定在 Dockerfile 中只能使用一次,如果有多个,则只有最后一个会生效。 CMD的目的是为了在启动容器时提供一个默认的命令执行选项。如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令

ENV

指定一个环节变量,会被后续RUN指令使用,并在容器运行时保留

EXPOSE

Docker 服务端容器对外映射的本地端口,需要在 docker run 的时候使用-p或者-P选项生效

results matching ""

    No results matching ""