写点什么

创建容器镜像:如何编写正确、高效的 Dockerfile

作者:okokabcd
  • 2022-10-20
    山东
  • 本文字数:2048 字

    阅读完需:约 1 分钟

创建容器镜像:如何编写正确、高效的Dockerfile

镜像的内部机制是什么

镜像是一个打包文件,里面包含了应用程序和依赖的环境,例如:文件系统、环境变量和配置参数等。


环境变量和配置参数比较简单一个 manifest 清单就可以管理,真正麻烦的是文件系统。为了保证容器运行环境的一致性,镜像必须把应用程序所在操作系统的根目录,也就是 rootfs 都包含进来。


容器是共享宿主机内核,文件系统不包含系统内核,假如有一千个镜像基于 Ubuntu 系统打包,那么这些镜像里就会重复一千次 Ubuntu 目录,对于磁盘存储、网络传输都是很大的浪费。


把重复的部分抽取出来存放到一份根目录文件,然后让其他镜像以某种方式共享这部分数据。这个思路正是容器的分层,Layer。


一个镜像是由许多的镜像层组成的,每层都是只读不可修改的一组文件,相同的层可以在镜像之间共享,多个层像搭积木一样推叠起来,再使用一种“Union FS 联合文件系统”的技术把它们合并在一起,就形成了容器最终看到的文件系统。


docker inspect可以查看镜像分层信息,比如 nginx:alpine 镜像


docker inspect nginx:alpine
复制代码


在执行docker pull 或 docker rmi命令时输出的信息其实就是镜像里的各个 Layer,Docker 会检查是否有重复的层,如果本地已经存在就不会重复下载,如果层被其他镜像共享就不会被删除,这样就可以节约磁盘和网络成本。


以上就是镜像的内部结构和基本原理。

Dockerfile 是什么

Dockerfile 如果是施工图纸,镜像就是由旋工图纸建造的房子,而容器就是住进人的房子。


Dockerfile 就是一个纯文本文件,里面记录了构建指令,比如选择基础镜像、拷贝文件、运行脚本,每个指令都会生成一个 Layer,Docker 顺序执行这个文件里的所有指令,最终生成一个新的镜像文件。


最简单的 Dockerfile


# filename:Dockerfile.busyboxFROM busyboxCMD echo "hello world"
复制代码


第一条指令:选择基础镜像


第二条指令:启动容器时默认运行的命令


根据图纸建造房子是由施工团队完成的,这里就是docker build


docker build -f Dockerfile.busybox .
复制代码


查看、运行镜像


docker imagesdocker inspect image_iddocker run image_id
复制代码

怎样编写正确、高效的 Dockerfile

选择镜像 - FROM 指令

选择基础镜像非常关键,如果关注镜像的安全和大小一般选择 Alpine,如果关注应用运行的稳定性一般选择 CentOS、Ubuntu、Debian。


FROM alpine:3.15FROM ubunt:bionic
复制代码

复制文件 - COPY 指令

如果要将源码、配置文件打包进镜像,就需要 COPY 指令,注意复制的源文件路径必须是构建上下文里的路径,也就是用相对路径。


# 把构建上下文里的a.txt拷贝到镜像的/tmp目录COPY ./a.txt /tmp/a.txt
# 下面方式不正确,不能用构建上下文之外的文件COPY /etc/hosts /tmp
复制代码

执行命令 - RUN 指令

执行任意 shell 命令,就需要 RUN 指令,Dockerfile 里一条指令只能是一行,所以 RUN 指令每行末尾用\续行符,命令之间用&&连接,这样就能保证在逻辑上是一行,减少 Layer。



RUN apt-get update \ && apt-get install -y \ build-essential \ curl \ make \ unzip \ && cd /tmp \ && curl -fSL xxx.tar.gz -o xxx.tar.gz\ && tar xzf xxx.tar.gz \ && cd xxx \ && ./config \ && make \ && make clean
复制代码


这样写一是不美观,二是一旦写错每次调试都要重新构建很麻烦,可以采用一种变通的技巧:把这些 shell 命令集中到一个脚本文件中,用 COPY 命令复制进去,再用 RUN 来执行。


COPY setup.sh  /tmp/                # 拷贝脚本到/tmp目录
RUN cd /tmp && chmod +x setup.sh \ # 添加执行权限 && ./setup.sh && rm setup.sh # 运行脚本然后再删除
复制代码

定义变量 - ARG 与 ENV 指令

ARG 和 ENV 都是定义变量,区别是 ARG 定义的变量只能在镜像构建过程中可见,ENV 创建的变量不仅能在构建镜像过程中使用,也能在容器运行时以环境变量的形式被应用程序使用

开放端口 - EXPOSE 指令

它用来声明容器对外服务的端口号,对基于 Node.js、Tomcat、Nginx、Go 等开发的微服务系统来说非常有用。


# 默认是TCP协议EXPOSE 443# 指定UDP协议EXOPSE 53/udp
复制代码

docker build 是怎么工作的

排除文件 - .dockerignore

一个机制,docker 是个客户端,真正的镜像构建工作是由 Docker daemon 来完成的,所以 Docker 会把构建上下文的目录打包上传,这样服务器才能获取本地文件。


如果构建上下文中一些文件不需要打包到镜像,docker 也会全部打包上传,效率很低。


可以在构建上下文目录中创建一个.dockerignore 文件,将那些不需要的文件写入


# docker ignore*.swp*.sh
复制代码

指定文件

用-f 来指定 Dockerfile 文件,如果不指定默认文件名为 Dockerfile


docker build -f xxx
复制代码

指定标签

:分割名字和标签,如果不指定标签默认为 latest

小结

1、容器镜像是由多个只读的 Layer 构成的,同一个 Layer 可以被不同的镜像共享,减少了存储和传输成本


2、Dockerfile 中每个指令都会生成一个 Layer


3、Dockerfile 第一条指令必须是 FROM


4、docker build 使用-f 来指定 Dockerfile,不指定默认国为当前目录下的以 Dockerfile 为名的文件


5、尽量不要在构建上下文中存入多余的文件


6、尽量使用-t 参数,为镜像起一个有意义的名字,方便管理

发布于: 刚刚阅读数: 3
用户头像

okokabcd

关注

还未添加个人签名 2019-11-15 加入

一年当十年用的Java程序员

评论

发布
暂无评论
创建容器镜像:如何编写正确、高效的Dockerfile_Docker_okokabcd_InfoQ写作社区