Dockerfile详解
文章目录一、构建材料`run.sh`二、完整示例三、指令详解`FROM[必选]``LABEL[可选]``EXPOSE[可选]``ENV[可选]`验证`ADD[可选]``COPY[可选]``VOLUME[可选]``USER[可选]``WORKDIR[可选]``RUN[可选,但用的频率非常高]``CMD[与ENTRYPOINT至少有一个]``ENTRYPOINT``ONBUILD[可选]`四、CMD与
Dockerfile 由一行行命令语句组成,并且支持以 # 开头的注释行。
AUFS不能超过 127 层
通常,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。
一、构建材料
run.sh
#!/bin/bash
echo "Hello Docker"
tail -f /dev/null
二、完整示例
FROM docker.io/nginx
LABEL author="运维@小兵" version="1.0" describe="构建nginx镜像"
ENV USERNAME nginx
USER ${USERNAME}
EXPOSE 80 443
WORKDIR /opt
VOLUME /data
ADD jdk-8u191-linux-x64.tar.gz /opt
COPY run.sh /tmp
RUN echo "${USERNAME}" >> /tmp/test.txt
CMD /tmp/run.sh
三、指令详解
FROM[必选]
用于指定构建镜像时依赖的基础镜像
格式:
FROM <image> 或 FROM<image>:<tag> #不填tag默认为latest
例如:FROM docker.io/nginx
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
FROM scratch
如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
MAINTAINER[可选]
设置镜像作者相关信息,如作者名字,日期,邮件,联系方式等
MAINTAINER
示例:MAINTAINER 运维@小兵 邮箱地址
LABEL[可选]
指定该dockerfile的基本信息,如维护者、版本等
例如:LABEL author=“运维@小兵” version=“1.0” describe=“构建nginx镜像”
EXPOSE[可选]
例如:EXPOSE 80 443
告诉 Docker 服务,容器需要暴露的端口号
可通过docker history 镜像名查看暴露的端口号,在启动容器时通过 -p 参数让 Docker 主机分配一个端口转发到容器的指定端口
ENV[可选]
格式:
#一次设置一个变量
ENV <key> <value>
#设置多个环境变量
ENV <key1>=<value1> <key2>=<value2> \
<key3>=<value3>
指定一个环境变量,会被后续 RUN 指令使用,在容器中通过export可以查看
例如:ENV USERNAME nginx
验证
通过RUN命令把USERNAME的值保存到/tmp/test.txt
RUN echo "${USERNAME}" >> /tmp/test.txt
进入容器查看/tmp/test.txt的内容
使用export查看USERNAME的值
ADD[可选]
复制文件到镜像(ADD与COPY的区别在于,ADD会自动解压tar、zip、tgz、xz等归档文件,而COPY不会,同时ADD指令还可以接一个url下载文件地址)
一般建议使用COPY复制文件即可,用ADD会解压,导致镜像变大
例如:ADD jdk-8u191-linux-x64.tar.gz /opt
注意
:ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。
COPY[可选]
Dockerfile 语法又增强了!
与ADD类似,对于压缩文件,不会自动解压
run.sh脚本需提前准备好,放在上下文路径下,通常为当前路径
例如:COPY run.sh /home/
注意:不能直接拷贝目录,但可以拷贝目录下的内容
COPY config/ /opt/config/ #把当前config目录下所有文件拷贝到/opt/config/下,如果/opt/config不存在,会创建
COPY --parents /app1/src/ /to/dest/dir/ #1.7版本新功能,复制 src 目录中的文件并为这些文件在目标路径重新创建 app1/src 目录
COPY --exclude=*.md --exclude=README app /dest/ #不包括 Markdown 文件和名为 README 的文件
COPY --exclude=**/*.md app /dest/ #使用 ** 双星通配符不仅排除复制目录中的 Markdown 文件,还排除任何子目录中的 Markdown 文件
COPY --exclude=**/*.md --exclude=!**/important.md app /dest/ #important.md仍将复制
还需要注意一点:使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。
VOLUME[可选]
容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。
为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,我们可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
例如:VOLUME /data
这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。
在容器中会自动创建/data目录
使用docker inspect 容器名可以看到/data目录挂载到了本地机器上的 /var/lib/docker/volumes/***中
当然,运行时可以覆盖这个挂载设置。比如:
docker run -d -v mydata:/data 镜像名
使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置
VOLUME /var/data /var/log
指定容器中的/var/log挂载到宿主机的/var/data目录,等同于-v /var/data:/var/log
USER[可选]
指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户
例如:USER nginx
会创建nginx组及用户,进入到容器后是在nginx用户下,而不是root用户
WORKDIR[可选]
为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录,如果目录不在,则会自动创建目录
例如:WORKDIR /opt
进入到容器后是在/opt目录下,默认是在/下
ARG[可选]
设置构建镜像要传递的参数
ARG [=]
例如:ARG name=sss
RUN[可选,但用的频率非常高]
每条 RUN 指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行,多条命令用&&来连接,避免执行多个RUN,使得镜像层数变多。最好只留一个RUN
CMD
指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条 CMD 命令,只有
最后一条会被执行。如果用户在启动容器时指定了要运行的命令,则会覆盖掉 CMD 指定的命令。
SHELL模式
实际的命令会被包装为 sh -c 的参数的形式进行执行
CMD bash /home/run.sh
上述会变为
CMD [ "sh", "-c", "bash /home/run.sh" ]
EXEC模式(推荐)
这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 ",而不要使用单引号。
CMD ["bash", "/home/run.sh"]
为什么推荐使用EXEC模式
因为使用 shell 模式之后,程序会以 /bin/sh -c 的子命令启动,并且 shell 格式下不会传递任何信号给程序。这也就导致,在 docker stop 容器的时候,运行的程序捕捉不到发送的信号,从而导致不能优雅的关闭容器。
参考文章:如何优雅的关闭容器?
ENTRYPOINT
配置容器启动后执行的命令
每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个 ENTRYPOINT 时,只有最后一个生效。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:
ENTRYPOINT ["executable", "param1", "param2"]
例如:ENTRYPOINT [“bash”, “/home/run.sh”]
ONBUILD[可选]
格式为:ONBUILD [INSTRUCTION]
配置当所创建的镜像作为其他新创建镜像的基础镜像时,所执行的操作指令。例如,Dockerfile 使用如下的内容创建了镜像 image-A。
…ONBUILD ADD . /app/srcONBUILD RUN /usr/local/bin/python-build –dir /app/src…
四、构建镜像
docker build -t nginx:1.0 .
-t #指定镜像的名字和tag号
. #代表安全上下文的相对路径
五、CMD与ENTRYPOINT在docker run时覆盖方法的不同处
CMD的覆盖方式
docker run -itd nginx:latest sleep 3600
ENTRYPOINT覆盖方式
docker run -itd --name a5 --entrypoint="" nginx:latest sleep 3600
六、注意事项
减少镜像层
- 一次RUN指令形成新的一层,Shell命令都写在一个RUN指令里面,减少镜像层。
- 文件比较多时,放入到一个目录中,使用COPY拷贝整个目录
避免使用ADD
优化镜像大小:清理无用数据
一次RUN形成新的一层,如果没有在同一层删除,无论文件是否最后删除,都会带到下一层,所以要在每一层清理对应的残留数据,减小镜像大小。
如执行完yum装包后需用yum clean all清除缓存包
多阶段进行镜像构建
例如,构建JAVA项目镜像:
# git clone https://github.com/lizhenliang/tomcat-java-demo
# cd tomcat-java-demo
# vi Dockerfile
FROM maven AS build
ADD ./pom.xml pom.xml
ADD ./src src/
RUN mvn clean package
FROM lizhenliang/tomcat
RUN rm -rf /usr/local/tomcat/webapps/ROOT
COPY --from=build target/*.war /usr/local/tomcat/webapps/ROOT.war
# docker build -t demo:v1 .
# docker container run -d -v demo:v1
首先,第一个FROM 后边多了个 AS 关键字,可以给这个阶段起个名字。
然后,第二部分FROM用的我们上面构建的Tomcat镜像,COPY关键字增加了—from参数,用于拷贝某个阶段的文件到当前阶段。这样一个Dockerfile就都搞定了。
COW写时复制技术决定不能在不同指令中操作同一目录
例如:
COPY files /opt/files
RUN chown -R test.test /opt/files
这会导致COPY层和RUN层均会复制一次/opt/files,如果/opt/files的大小为400M,那么RUN层的大小为400+M
建议:把类似chown -R test.test /opt/files的写操作放入init_container.sh脚本中,该脚本执行完毕后自己把自己删除
参考文章:
更多推荐
所有评论(0)