本篇文章是关于Dockerfiles的,这是Docker系列文章的第三部分。如果您还没有读过第一部分,请先阅读它,您可以从全新的角度了解Docker容器概念。 第二部分是Docker生态系统的简要介绍。在以后的文章中,我将研究精简Docker映像、Docker CLI命令以及使用Docker的数据。

现在,让我们跳进这十几个Dockerfile说明中去吧!

图:跳进来

Docker映像

Docker容器是栩栩如生的Docker映像,它是一个独立的、最小的操作系统,带有应用程序代码。

Docker映像在构建时创建的,而Docker容器是在运行时创建。

Dockerfile是Docker的核心,Dockerfile告诉Docker如何构建用于制作容器的映像。

每个Docker映像都包含一个名为Dockerfile的文件,没有扩展名。调用Dockerfile docker build 创建映像时,假定该Dockerfile位于当前工作目录中,可以使用文件标志( -f )指定其他位置。

容器是由一系列层构建而成的,除位于最后一层之上的最终容器层外,每一层都是只读的。Dockerfile告诉Docker添加哪些层以及添加顺序。

每一层实际上只是一个文件,其中包含自上一层以来的更改。在Unix中,几乎所有内容都是文件。

基础图像提供了初始层,基本图像也称为父图像。

当图像从远程存储库拉到本地计算机时,只下载本地计算机上尚未存在的层。Docker就是通过重用现有层来节省空间和时间。

图:基本(跳跃)图像

Dockerfile指令是一行开头的大写单词,后跟其参数。Dockerfile中的每一行都可以包含一条指令。构建图像时,说明从上到下进行处理。说明如下:

FROM ubuntu:18.04
COPY . /app

只有指令FROM,RUN,COPY和ADD才能在最终图像中创建图层,其他的指令可配置事物,添加元数据或告诉Docker在运行时执行某些操作,例如公开端口或运行命令。 在本文中,我假设您正在使用基于Unix的Docker映像。您也可以使用基于Windows的映像,但这是一个较慢,较不愉快,较不常见的过程。因此,如果可以,请使用Unix。

让我们快速浏览一下我们将探索的十二个Dockerfile指令吧。

Dockerfile指令

FROM —指定基本(父)图像。

LABEL —提供元数据,包括维护者信息。

ENV —设置持久性环境变量。

RUN —运行命令并创建图像层,用于将软件包安装到容器中。

COPY -将文件和目录复制到容器。

ADD -将文件和目录复制到容器,可以支持本地.tar文件。

CMD —为执行中的容器提供命令和参数,可以覆盖参数,只能有一个CMD。

WORKDIR —为以下说明设置工作目录。

ARG —定义在构建时传递给Docker的变量。

ENTRYPOINT —为执行中的容器提供命令和参数。争论依然存在。

EXPOSE —暴露端口。

VOLUME —创建目录安装点以访问和存储持久数据。

让我们开始吧!

说明和示例

Dockerfile可以像下面这样简单:

FROM ubuntu:18.04

FROM

Dockerfile必须以FROM指令或ARG指令开头,后跟FROM指令。

FROM 关键字告诉Docker使用与提供的存储库和标签匹配的基础映像,基本图像也称为父图像。

在此示例中,ubuntu是映像存储库。Ubuntu是 官方Docker存储库的名称,该存储库提供了流行的Linux操作系统的Ubuntu版本的基本版本。

图:Linux吉祥物Tux

请注意,此Dockerfile包含基础映像的标记:18.04,这个标签告诉Docker在ubuntu仓库中镜像的哪个版本。如果不包含标签,则默认情况下,Docker将采用最新标签。为了使您的意图清晰明了,最好指定一个基本图像标签。

当上述Dockerfile首次用于在本地构建映像时,Docker下载ubuntu映像中指定的层。可以将这些层视为彼此堆叠。每一层都是一个文件,具有与前一层不同的一组文件。

创建容器时,可以在只读层的顶部添加可写层。

图:从 Docker文档

为了提高效率,Docker使用了一种写时拷贝策略。如果一个层存在于图像的前一层,而另一层需要对其进行读访问,Docker将使用现有文件。不需要下载任何内容。

当一个图像正在运行时,如果一个层需要被容器修改,那么该文件将被复制到顶部的可写层中。

更具实质性的Dockerfile

虽然我们的单线图很简洁,但它也很慢,提供的信息很少,并且在容器运行时什么也不做。让我们看一个较长的Dockerfile,它构建一个小得多的图像,并在容器运行时执行脚本。

FROM python:3.7.2-alpine3.8
LABEL maintainer="jeffmshale@gmail.com"
ENV ADMIN="jeff"
RUN apk update && apk upgrade && apk add bash
COPY . ./app
ADD https://raw.githubusercontent.com/discdiver/pachy-vid/master/sample_vids/vid1.mp4 \
/my_app_directory
RUN ["mkdir", "/a_directory"]
CMD ["python", "./my_script.py"]

这是怎么回事能?让我们逐步了解并揭开神秘面纱。 基本映像是带有标签3.7.2-alpine3.8的正式Python映像。从 源代码中可以看到,该映像包含Linux、Python和其他一些内容。高山图像之所以受欢迎,是因为它们体积小,速度快且安全。但是,Alpine映像并没有很多操作系统优点。如果需要,您必须自己安装这样的软件包。

LABEL

下一条指令是LABEL。LABEL将元数据添加到图像中。在本例中,它提供图像维护者的联系信息。 Labels不会减慢构建速度或占用空间,它们提供了有关Docker映像的有用信息,因此一定要使用它们。有关LABEL元数据的更多信息,请参见 此处

ENV

ENV设置在容器运行时可用的持久环境变量。在上面的例子中,您可以在创建Docker容器时使用ADMIN变量。

ENV非常适合设置常量,如果您在Dockerfile中的多个位置使用常量,并且想在以后更改其值,则可以在一个位置进行更改。

图:环境

对于Dockerfiles,通常可以通过多种方式完成同一件事。针对您的案例,最好的方法是平衡Docker约定、透明性和速度。例如,RUN、CMD和ENTRYPOINT具有不同的用途,并且均可用于执行命令。

RUN

RUN在构建时创建一个层。每次运行后,Docker都会提交映像的状态。

RUN通常用于将软件包安装到映像中  在上面的示例中, RUN apk update && apk upgrade 告诉Docker从基础映像更新软件包  && apk add bash 告诉Docker将bash安装到映像中。

apk代表Alpine Linux软件包管理器。如果您使用的是Alpine以外的其他版本的Linux基础映像,则应使用RUN apt-get而不是apk安装软件包。apt代表高级包工具。在后面的示例中,我将讨论安装包的其他方法。

图:跑

RUN及其同级命令CMD和ENTRYPOINT可以在exec表单或shell表单中使用。Exec form使用的JSON数组语法如下:

例如: RUN ["my_executable", "my_first_param1", "my_second_param2"] 。

在上面的示例中,我们使用格式为的shell形式 RUN apk update && apk upgrade && apk add bash 。

稍后在Dockerfile中,我们使用首选的exec形式 RUN ["mkdir", "/a_directory"] 创建目录。别忘了对exec form使用JSON语法的字符串使用双引号!

COPY

该 COPY . ./app 指令告诉Docker在本地构建上下文中获取文件和文件夹,并将它们添加到Docker映像的当前工作目录中。如果不存在,复制将创建目标目录。

图:复制

ADD

ADD与COPY执行相同的操作,但有两个以上的用例。ADD可用于将文件从远程URL移动到容器,ADD可以提取本地TAR文件。

我在上面的示例中使用ADD将文件从远程URL复制到容器的 my_app_directory中 。Docker文档不建议以这种方式使用远程URL,因为您无法删除这些文件。额外的文件会增加最终图像的大小。

Docker文档还建议尽可能使用COPY而不是ADD来提高清晰度。Docker没有将ADD和COPY合并到一个命令中,以减少Dockerfile指令的数量来保持直线,这太糟糕了。

注意,ADD指令包含 \ 换行符,使用它可以通过将一条长指令拆分成几行来提高可读性。

CMD

CMD向Docker提供了一个在容器启动时运行的命令。它不会在构建时将命令的结果提交给映像。在上面的示例中,CMD将使Docker容器在运行时运行my_ script.py 文件。

图:那是CMD!

有关CMD的其他几件事:

  • 每个Dockerfile仅一个CMD指令。否则,除最后一个以外的所有内容都将被忽略。
  • CMD可以包含一个可执行文件。如果存在没有可执行文件的CMD,则必须存在ENTRYPOINT指令。在这种情况下,CMD和ENTRYPOINT指令都应为JSON格式。
  • 命令行参数将 docker run 覆盖Dockerfile中提供给CMD的参数。

    准备好了吗?

让我们在另一个Dockerfile示例中介绍更多说明。

FROM python:3.7.2-alpine3.8
LABEL maintainer="jeffmshale@gmail.com"
# Install dependencies
RUN apk add --update git
# Set current working directory
WORKDIR /usr/src/my_app_directory
# Copy code from your local context to the image working directory
COPY . .
# Set default value for a variable
ARG my_var=my_default_value
# Set code to run at container run time
ENTRYPOINT ["python", "./app/my_script.py", "my_var"]
# Expose our port to the world
EXPOSE 8000
# Create a volume for data storage
VOLUME /my_volume

请注意,您可以在Dockerfiles中使用注释。注释以 # 开头。 软件包安装是Dockerfiles的主要工作。如前所述,有几种方法可以使用RUN安装软件包。

您可以使用apk在Alpine Docker映像中安装软件包,apk就像常规Linux构建中的apt-get。例如,带有基本Ubuntu映像的Dockerfile中的软件包可以像这样更新和安装: RUN apt-get update && apt-get install my_package 。

除了apk和apt-get 之外 ,还可以通过pip,wheel和conda安装Python软件包。其他语言可以使用各种安装程序。

基础层需要向安装层提供相关的软件包管理器。如果您在安装软件包时遇到问题,请在安装软件包管理器之前尝试使用它们。

您可以将RUN与pip一起使用,并在Dockerfile中直接列出要安装的软件包。如果这样做,则将您的软件包安装到一条指令中,并用换行符(\)将其分开。与多个RUN指令相比,此方法提供了清晰度和更少的层数。

或者,您可以在文件中列出软件包要求,然后在该文件上运行软件包管理器。人们通常将文件命名为 requirements.txt 。我将在下一篇文章中分享一个推荐的模式,以利用build.ca缓存与 requirements.txt 一起使用。

WORKDIR

WORKDIR会更改容器中的工作目录,以供后面的COPY、ADD、RUN、CMD和ENTRYPOINT指令使用。几点注意事项:

  • 最好使用WORKDIR设置绝对路径,而不是使用Dockerfile中的 cd 命令在文件系统中导航。
  • 如果该目录不存在,则WORKDIR会自动创建该目录。
  • 您可以使用多个WORKDIR指令。如果提供了相对路径,则每个WORKDIR指令都会更改当前工作目录。

图:某种工作目录

ARG

ARG定义了一个在构建时从命令行传递到映像的变量。可以在Dockerfile中为ARG提供默认值,如示例所示: ARG my_var=my_default_value 。

与ENV变量不同,ARG变量不适用于运行中的容器。但是,在生成映像时,可以使用ARG值在命令行中为ENV变量设置默认值。然后,ENV变量在容器运行期间一直存在。

ENTRYPOINT

ENTRYPOINT指令还允许您在容器启动时提供默认命令和参数。它看起来与CMD相似,但是如果使用命令行参数运行容器,则ENTRYPOINT参数不会被覆盖。

而是将传递给的命令行参数 docker run my_image_name 附加到ENTRYPOINT指令的参数中。例如, docker run my_image bash 将参数 bash 添加到ENTRYPOINT指令的现有参数的末尾。

图:进入某处

Dockerfile应该至少具有一个CMD或ENTRYPOINT指令。

Docker文档中有一些关于在CMD和ENTRYPOINT之间选择初始容器命令的建议:

  • 当您 需要每次运行 相 同 的 命令时,请 选择 ENTRYPOINT。
  • 当 容器 将 用作可执行程序时, 首选入口点 。
  • 当您 需要提供 额外的默认参数, 可以从命令行 重写 时, 使用 CMD。

在上面的示例中, ENTRYPOINT ["python", "my_script.py", "my_var"] 让容器在容器开始运行时运行带有参数 my_var 的python脚本 my_script.py 。 然后, my_script可以 通过argparse使用 my_var 。请注意, my_var 具有ARG先前在Dockerfile中提供的默认值。因此,如果未从命令行传递参数,则将使用默认参数。

Docker建议您通常使用ENTRYPOINT:的exec形式 ENTRYPOINT ["executable", "param1", "param2"] 。这种形式是使用JSON数组语法的形式。

EXPOSE

EXPOSE指令显示要发布哪个端口以提供对正在运行的容器的访问。EXPOSE实际上不会发布端口。相反,它充当构建映像的人员与运行容器的人员之间的文档。

图:暴露

docker run 与 -p 标志一起使用,以在运行时发布和映射一个或多个端口。大写 -P 标志将发布所有公开的端口。

VOLUME

VOLUME指定您的容器将在哪里存储和/或访问持久数据。

图:卷

总结

Dockerfile也许是Docker掌握的关键组件。我希望本文能帮助到你们。

原文作者:Jeff Hale

原文地址:https://towardsdatascience.com/learn-enough-docker-to-be-useful-1c40ea269fa8

翻译:猪齿鱼技术团队


本文由猪齿鱼技术团队原创,转载请注明出处:猪齿鱼官网

关于猪齿鱼

猪齿鱼Choerodon全场景效能平台,提供体系化方法论和协作、测试、DevOps及容器工具,帮助企业拉通需求、设计、开发、部署、测试和运营流程,一站式提高管理效率和质量。从团队协同到DevOps工具链、从平台工具到体系化方法论,猪齿鱼全面满足协同管理与工程效率需求,贯穿端到端全流程,助力团队效能更快更强更稳定。戳此处试用猪齿鱼

Docker入门系列之三:十二个Dockerfile指令的更多相关文章

  1. Docker入门系列之三:如何将dockerfile制作好的镜像发布到Docker hub上

    这个系列的前两篇文章,我们已经把我们的应用成功地在Docker里通过nginx运行了起来,并且用dockerfile里制作好了一个镜像. Docker入门系列之一:在一个Docker容器里运行指定的w ...

  2. SpringBoot入门系列(十二)统一日志收集

    前面介绍了Spring Boot 异常处理,不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/category/1657780.html. 今 ...

  3. Java并发编程系列之三十二:丢失的信号

    这里的丢失的信号是指线程必须等待一个已经为真的条件,在開始等待之前没有检查等待条件.这样的场景事实上挺好理解,假设一边烧水,一边看电视,那么在水烧开的时候.由于太投入而没有注意到水被烧开. 丢失的信号 ...

  4. Scala入门系列(十二):隐式转换

    引言 Scala提供的隐式转换和隐式参数功能,是非常有特色的功能.是Java等编程语言所没有的功能.它可以允许你手动指定,将某种类型的对象转换成其他类型的对象.通过这些功能可以实现非常强大而且特殊的功 ...

  5. ABP框架系列之三十二:(Logging-登录)

    Server Side(服务端) ASP.NET Boilerplate uses Castle Windsor's logging facility. It can work with differ ...

  6. Java入门系列(十二)Java反射

    Why--指的是为什么做这件事,也既事物的本质. 反射之中包含了一个“反”的概念,所以要想解释反射就必须先从“正”开始解释,一般而言,当用户使用一个类的时候,应该先知道这个类,而后通过这个类产生实例化 ...

  7. Golang 入门系列(十二)ORM框架gorm

    之前在已经介绍了用的github.com/go-sql-driver/mysql 访问数据库,不太了解的可以看看之前的文章 https://www.cnblogs.com/zhangweizhong/ ...

  8. [OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    http://blog.csdn.net/poem_qianmo/article/details/25560901 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...

  9. CRL快速开发框架系列教程十二(MongoDB支持)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  10. Citrix 服务器虚拟化之三十二 XenConvert

    Citrix 服务器虚拟化之三十二  XenConvert 简介: Citrix XenConvert 是用于实现物理到虚拟(P2V)转换的工具,可将工作负载从运行 Windows 的服务器或桌面计算 ...

随机推荐

  1. Dubbo 跨语言调用神兽:dubbo-go-pixiu

    简介: Pixiu 是基于 Dubbogo 的云原生.高性能.可扩展的微服务 API 网关.作为一款网关产品,Pixiu 帮助用户轻松创建.发布.维护.监控和保护任意规模的 API ,接受和处理成千上 ...

  2. [LLM] 开源 AI 大语言模型的本地化定制实践

      LLM(Large Language Model,大型语言模型)是一种基于深度学习的自然语言处理模型,旨在理解和生成人类语言. 它们在大量的文本数据上进行训练,可以执行广泛的任务,包括文本总结.翻 ...

  3. [DApp] Moralis 无服务架构方式构建 DApp

    Moralis 提供的使用功能包括三个方面: 进一步封装Web3功能的 Moralis SDK,极大方便了开发者对于DApp基础功能的开发,比如 MetaMask登录验证,签名,IPFS集成,DApp ...

  4. netcore依赖注入通过反射简化

    aspnetcore里面用到许多的service,好多业务代码都要通过Service.AddScoped.Singleton.Transient等注入进去,类太多了写起来和管理起来都很麻烦,所以借鉴了 ...

  5. Linux内核之I2C协议

    I2C协议标准文档 THE I2C-BUS SPECIFICATION VERSION 2.1 JANUARY 2000: https://www.csd.uoc.gr/~hy428/reading/ ...

  6. js部分数组方法

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. vue使用bus.js在兄弟组件传值

    A组件往B组件传递数据data 1.src下创建文件eventBus.js,内容: import Vue from 'vue' export default new Vue() 2.在A,B组件分别引 ...

  8. 利用Navicat的历史日志查询表的索引信息(还可以查询很多系统级别的信息)

    1.使用前提 所有的能用Navicat连接的数据库都可以使用这个方法 DDL/DML语句都有 2.Navicat中的历史日志 3.比如查询mysql的表的索引 先打开"历史记录" ...

  9. linux基础命令及bash shell特性

    linux基础命令及bash shell特性 目录 linux基础命令及bash shell特性 1.linux基础命令 1.1 查看内核版本和linux发行版本 1.2 查看服务器硬件信息 1.3 ...

  10. 密码学—Vigenere破解Python程序

    文章目录 概要 预备知识点学习 整体流程 技术名词解释 技术细节 小结 代码 概要 破解Vigenere需要Kasiski测试法与重合指数法的理论基础 具体知识点细节看下面这两篇文章 预备知识点学习 ...