Dockerfile详解

Dockerfile是什么

Dockerfile就是一个纯文本,里面记录了一系列的构建指令,如选择基础镜像、拷贝文件、运行脚本等等,RUNCOPYADD指令都会生成一个 Layer,而 Docker 顺序执行这个文件里的所有步骤,最后就会创建出一个新的镜像出来。

一个简单的Dockerfile实例:

# Dockerfile.busybox
FROM busybox            # 选择构建使用的基础镜像
CMD echo "hello world"  # 启动镜像默认运行的命令

RUN, COPY, ADD 会生成新的镜像层,其它指令只会产生临时层,不影响构建大小。所以 Dockerfile 里不要滥用这些指令,尽量精简合并,否则太多的层会让镜像臃肿不堪。


docker build

Dockerfile 要经过 docker build 才能生效,命令行“docker”是一个简单的客户端,真正的镜像构建工作由服务器端的“Docker daemon”来完成,所以“docker”客户端只能把“构建上下文”目录打包上传(显示信息 Sending build context to Docker daemon ),这样服务器才能够获取本地的这些文件。

 

这个机制会导致一些麻烦,如目录里有的文件(例如 readme/.git/.svn 等)不需要拷贝进镜像,docker 也一股脑地打包上传,效率很低。

为避免这种问题,可以在“构建上下文”目录里建立一个 .dockerignore 文件,语法与 .gitignore 类似,排除那些不需要的文件。

gitignore

下面的简单的示例,表示不打包上传后缀是“swp”“sh”的文件:

# docker ignore
*.swp
*.sh

另外一般在命令行里用 -f 来显式指定 Dockerfile文件。但如果省略这个参数,docker build 会在当前目录下找名字是 Dockerfile 的文件。所以,若只有一个构建目标,文件直接叫“Dockerfile”最省事。

此时构建出来的镜像只有“IMAGE ID”,没有名字。为此可以加上一个 -t 参数,指定镜像的标签(tag),这样 Docker 就会在构建完成后自动给镜像添加名字。标签必须要符合一定的命名规范,用 : 分隔名字和版本,如果不提供版本默认“latest”。


docker inspect

docker inspect 来查看镜像的分层信息,Docker 会检查是否有重复层,如果本地已存在就不会重复下载,如果层被其他镜像共享就不会删除,这样可以节约磁盘和网络成本。

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

镜像分层好处:可重复使用未被改动的 layer,每次修改打包镜像,只需重新构建被改动的部分。


完整的 Dockerfile 示例

# Dockerfile
# docker build -t ngx-app .
# docker build -t ngx-app:1.0 .

ARG IMAGE_BASE="nginx"
ARG IMAGE_TAG="1.21-alpine"

FROM ${IMAGE_BASE}:${IMAGE_TAG}

COPY ./default.conf /etc/nginx/conf.d/

RUN cd /usr/share/nginx/html \
    && echo "hello nginx" > a.txt

EXPOSE 8081 8082 8083

Dockerfile参数

FROM

功能:指定基础镜像,必须是第一条指令,所以基础镜像的选择非常关键

如果关注的是镜像的安全和大小,那么一般会选择 Alpine;如果关注的是应用的运行稳定性,那么可选择 Ubuntu、Debian、CentOS。

FROM alpine:3.15                # 选择Alpine镜像
FROM ubuntu:bionic              # 选择Ubuntu镜像

如果不以任何镜像为基础,那么写法为:FROM scratch,同时意味着接下来所写指令将作为镜像第一层开始。

格式:

FROM <image>
FROM <image>:<tag>
FROM <image>:<digest> 

三种写法,其中<tag>和<digest> 是可选项,若没有选择,则默认值为latest。


RUN

功能:运行时指定的命令,可以执行任意 Shell 命令,如更新系统、安装应用、下载文件、创建目录、编译程序等,实现任意的镜像构建步骤,非常灵活。

两种格式:

1. RUN <command>
2. RUN ["executable", "param1", "param2"]

第一种后边直接跟shell命令

  • 在linux操作系统上默认 /bin/sh -c

  • 在windows操作系统上默认 cmd /S /C

第二种类似于函数调用。

可将executable理解成为可执行文件,后面是两个参数。

两种写法比对:

  • RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME
  • RUN ["/bin/bash", "-c", "echo hello"]

注意:多行命令不要写多个RUN,因为Dockerfile中每一个指令都会建立一层。多少RUN就构建多少层镜像,会造成镜像的臃肿、多层,不仅仅增加构件部署的时间,还容易出错。RUN书写时的换行符是\。

RUN 通常是 Dockerfile 里最复杂的指令,会包含很多的 Shell 命令,但 Dockerfile 里一条指令只能是一行,所以有的 RUN 指令会在每行的末尾用续行符 \,命令间也会用 && 来连接,这样保证在逻辑上是一行,就像下面这样:

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

在 Dockerfile 里写这种超长的 RUN 指令不美观,且一旦写错,每次调试都要重新构建也很麻烦。所以可以采用一种变通的技巧:把这些 Shell 命令集中到一个脚本文件里,用 COPY 命令拷贝进去再用 RUN 来执行

COPY setup.sh  /tmp/                # 拷贝脚本到/tmp目录

RUN cd /tmp && chmod +x setup.sh \  # 添加执行权限
    && ./setup.sh && rm setup.sh    # 运行脚本后再删除脚本

CMD

功能:容器启动时运行的命令

三种格式:

1. CMD ["executable","param1","param2"]
2. CMD ["param1","param2"]
3. CMD command param1 param2

第一种和第二种都是可执行文件加上参数的形式。

第三种就是shell的执行方式和写法。

举例说明两种写法:

  • CMD [ "sh", "-c", "echo $HOME" 
  • CMD [ "echo", "$HOME" ]

补充细节:这里包括参数的要用双引号,原因是参数传递后,docker解析的是一个JSON array。


RUN & CMD

RUN:构建镜像时就运行的命令且提交运行结果。

CMD:启动镜像时执行的命令,构建时仅指定了这个命令是个什么样子。


LABEL

功能:为镜像指定标签

格式:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

Dockerfile中可有多个LABEL,如下:

LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

但并不建议这样写,最好写成一行,如太长需换行的话则用\符号,如下:

LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"

说明:LABEL会继承基础镜像中的LABEL,如遇到key相同,则值覆盖。


MAINTAINER

功能:指定作者

格式:

MAINTAINER <name>

EXPOSE

功能:暴漏容器运行时的监听端口给宿主机。暴露容器内部端口给外部。

但EXPOSE并不能使容器访问宿主机的端口,

想使容器与主机的端口有映射关系,须在容器启动的时候加上 -P或-p参数。

EXPOSE 443    # 默认是tcp协议
EXPOSE 53/udp # 可以指定udp协议

ENV

功能:设置环境变量,镜像层和容器层参数

两种格式:

1. ENV <key> <value>
2. ENV <key>=<value> ...

区别:第一种是一次设置一个,第二种是一次设置多个。

# ARG 定义了基础镜像的名字(可以在FROM指令中使用),ENV 定义了两个环境变量
ARG IMAGE_BASE="node"
ARG IMAGE_TAG="alpine"

ENV PATH=$PATH:/tmp
ENV DEBUG=OFF

ADD

功能:复制命令,把文件复制到镜象中。

如果把宿主机和容器想象成两台linux服务器的话,那这个命令就类似于scp,只是scp需要加用户名和密码的权限验证,而ADD不用。

格式:

1. ADD <src>... <dest>
2. ADD ["<src>",... "<dest>"]

<src>可以是一个本地文件或是一个本地压缩文件,还可以是一个url。

<dest>路径的填写可是容器内绝对路径,也可以是相对于工作目录的相对路径。

如果把<src>写成一个url,那么ADD就类似于wget命令

如下写法可以:

  • ADD test relativeDir/ 
  • ADD test /relativeDir
  • ADD http://example.com/foobar /

<scr>尽量不要是一个文件夹。如果<src>是一个文件夹,会复制整个目录的内容,包括文件系统元数据。


COPY

功能:复制命令

在本机上开发测试时会产生一些源码、配置等文件,需打包进镜像里,这时可用 COPY 命令,它的用法和 Linux  cp 差不多,不过拷贝的源文件必须是“构建上下文”路径里的,不能随意指定文件。也就是说,如果要从本机向镜像拷贝文件,就必须把这些文件放到一个专门的目录,然后在 docker build 里指定“构建上下文”到这个目录才行。

这里两个 COPY 命令示例:

COPY ./a.txt  /tmp/a.txt    # 把构建上下文里的a.txt拷贝到镜像的/tmp目录
COPY /etc/hosts  /tmp       # 错误!不能使用构建上下文之外的文件

格式:

1. COPY <src>... <dest>
2. COPY ["<src>",... "<dest>"]

与ADD的区别

COPY的<src>只能是本地文件,其他用法一致。


ENTRYPOINT

功能:启动时的默认命令

格式:

1. ENTRYPOINT ["executable", "param1", "param2"]
2. ENTRYPOINT command param1 param2

第一种是可执行文件加参数。

第二种就是写shell。

与CMD比较说明(这俩命令太像了,而且还可以配合使用):

1. 相同点:

  • 只能写一条,如果写了多条,只有最后一条生效。

  • 容器启动时才运行。

2. 不同点:

  •  ENTRYPOINT不会被运行镜像时的command覆盖,而CMD会被覆盖。

  •  如果在Dockerfile中同时写了ENTRYPOINT和CMD,且CMD指令不是一个完整的可执行命令,那么CMD指定的内容将会作为ENTRYPOINT的参数。

如下:

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
  • 如果在Dockerfile同时写了ENTRYPOINT和CMD,且CMD是一个完整的指令,那么谁在最后谁生效。

如下:

FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ls -al

那么将执行ls -al,top -b不会执行。


VOLUME

功能:实现挂载功能,可以将内地文件夹或其他容器中的文件夹挂在到该镜像的容器中。

格式:

VOLUME ["/data"]

说明:

  ["/data"]可以是一个JsonArray ,也可以是多个值。所以如下几种写法都正确:

VOLUME ["/var/log/"]
VOLUME /var/log
VOLUME /var/log /var/db

一般的使用场景为需要持久化存储数据。

容器使用的是AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。


USER

设置启动容器的用户。

可以是用户名或UID,所以,下面的两种写法都是正确的

格式:

  • USER daemo
  • USER UID

注意:如果设置了容器以daemon用户去运行,那么RUN, CMD 和 ENTRYPOINT 都会以这个用户去执行。


WORKDIR

功能:设置工作目录,对RUN,CMD,ENTRYPOINT,COPY,ADD生效。若不存在则会创建,也可以设置多次。

格式:

WORKDIR /path/to/workdir

如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

pwd执行的结果:/a/b/c

WORKDIR也可以解析环境变量

如:

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

pwd的执行结果:/path/$DIRNAME


ARG

功能:定义镜像层的环境变量

格式:

ARG <name>[=<default value>]

设置变量命令,ARG命令定义了一个变量,在docker build创建镜像的时候,使用 --build-arg <varname>=<value>来指定参数

如果用户在build镜像时指定了一个参数但没有定义在Dockerfile中,那么将有一个Warning

提示如下:

[Warning] One or more build-args [foo] were not consumed.

也能给参数一个默认值:

FROM busybox
ARG user1=someuser
ARG buildno=1
...

若给了ARG定义的参数默认值,那么当build镜像时没有指定参数值,将会用这个默认值。

例:

# ARG 定义了基础镜像的名字(可以在FROM指令中使用),ENV 定义了两个环境变量
ARG IMAGE_BASE="node"
ARG IMAGE_TAG="alpine"

ENV PATH=$PATH:/tmp
ENV DEBUG=OFF

ONBUILD

功能:这个命令只对当前镜像的子镜像生效。

格式:

ONBUILD [INSTRUCTION]

如当前镜像为A,在Dockerfile种添加:

ONBUILD RUN ls -al

这个 ls -al 命令不会在A镜像构建或启动时执行,若有一个镜像B是基于A镜像构建,则这个ls -al 命令会在B镜像构建的时候被执行。


STOPSIGNAL

功能:当容器退出时给系统发送什么样的指令。

格式:

STOPSIGNAL signal

HEALTHCHECK

功能:容器健康状况检查命令。

两种格式:

1. HEALTHCHECK [OPTIONS] CMD command
2. HEALTHCHECK NONE

第一个的功能是在容器内部运行一个命令来检查容器的健康状况。

第二个的功能是在基础镜像中取消健康检查命令。

[OPTIONS]选项支持三中选项:

    --interval=DURATION 两次检查默认的时间间隔为30秒

    --timeout=DURATION 健康检查命令运行超时时长,默认30秒

    --retries=N 当连续失败指定次数后,则容器被认为不健康,状态为unhealthy,默认次数是3

注意:

HEALTHCHECK命令只能出现一次。出现了多次,只有最后一个生效。

CMD后边的命令的返回值决定了本次健康检查是否成功,具体的返回值如下:

0: success - 表示容器健康

1: unhealthy - 表示容器不健康

2: reserved - 保留值

例子:

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

健康检查命令是:curl -f http://localhost/ || exit 1

两次检查的间隔时间是5秒

命令超时时间为3秒


总结

  1. 创建镜像需编写 Dockerfile,写清楚创建镜像的步骤,每个指令都会生成一个 Layer
  2. Dockerfile 里,第一个指令必须是 FROM,用来选择基础镜像,常用的有 Alpine、Ubuntu 等。其他常用的指令有:COPYRUNEXPOSE,分别是拷贝文件,运行 Shell 命令,声明服务端口号。
  3. docker build 需要用 -f 来指定 Dockerfile,如果不指定就使用当前目录下名字为“Dockerfile”的文件。
  4. docker build 需要指定“构建上下文”,其中的文件会打包上传到 Docker daemon,所以尽量不要在“构建上下文”中存放多余的文件。
  5. 创建镜像的时候应使用 -t 参数,为镜像起一个有意义的名字,方便管理。
  6. Dockerfile 里的构建命令不区分大小写,如“FROM”也可写成“from”。

    参考:

如何编写正确高效的Dockerfile - 牛奔 - 博客园 (cnblogs.com)icon-default.png?t=N3I4https://www.cnblogs.com/niuben/p/17178426.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/26952.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分布式系统概念和设计——分布式事务

分布式系统概念和设计 分布式事务 访问多个服务器管理的对象的事务称为分布式事务。 当一个分布式事务结束时&#xff0c;事务的原子特性要求所有参与事务的服务器必须全部提交或全部放弃。 实现&#xff1a; 其中一个服务器承担了协调者的角色&#xff0c;保证在所有的服务器…

“微商城”项目(5登录和注册)

1.我的信息 在pages\User.vue文件中编写HTML结构代码&#xff0c;示例代码如下。 <template><div class"member"><div class"header-con"><router-link :to"{ name: login }" class"mui-navigate-right">&l…

Spring Cloud Alibaba - Sentinel(一)

目录 一、Sentinel介绍 1、什么是Sentinel 2、Sentinel好处 3、Sentinel下载和安装 二、搭建Sentinel项目 1、创建项目cloudalibaba-sentinel-service8401 三、Sentinel流控规则 1、流控规则基本介绍 2、新增流控 2.1、QPS直接失败案例 2.2、线程数直接失败案例 3、…

容器(第一篇)docker安装、基础操作命令

docker是什么&#xff1f; docker是一个go语言开发的应用容器引擎。 docker的作用&#xff1f; ①运行容器里的应用&#xff1b; ②docker是用来管理容器和镜像的一种工具。 容器 与 虚拟机 的区别&#xff1f; 容器 虚拟机所有容器共享宿主机…

【论文阅读】Neuralangelo:高保真神经表面重建

【论文阅读】Neuralangelo&#xff1a;高保真神经表面重建 Abstract1. Introduction2. Related work3. Approach3.1.预备工作3.2.数值梯度计算3.3.渐进细节层次3.4.优化 4. Experiments4.1. DTU Benchmark4.2. Tanks and Temples4.3.细节水平4.4.消融 5. Conclusion paper proj…

深度学习基础知识-tf.keras实例:衣物图像多分类分类器

参考书籍&#xff1a;《Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow, 2nd Edition (Aurelien Geron [Gron, Aurlien])》 本次使用的数据集是tf.keras.datasets.fashion_mnist&#xff0c;里面包含6w张图&#xff0c;涵盖10个分类。 import tensorflo…

Jmeter性能测试 (入门)

Jmeter是一款优秀的开源测试工具&#xff0c; 是每个资深测试工程师&#xff0c;必须掌握的测试工具&#xff0c;熟练使用Jmeter能大大提高工作效率。 熟练使用Jmeter后&#xff0c; 能用Jmeter搞定的事情&#xff0c;你就不会使用LoadRunner了。 本文将通过一个实际的测试例…

IPV6地址基础

IPv6是英文“Internet Protocol Version 6”&#xff08;互联网协议第6版&#xff09;的缩写&#xff0c;是互联网工程任务组&#xff08;IETF&#xff09;设计的用于替代IPv4的下一代IP协议。其地址数量号称可以为全世界的每一粒沙子编上一个地址 1. ipv6地址表示方法 IPv6的…

MQTT与EMQ

文章目录 1 MQTT协议与EMQ中间件1.1 物联网消息协议MQTT1.1.1 什么是MQTT1.1.2 MQTT相关概念1.1.3 消息服务质量QoS——信息的可靠投递1.1.3.1 QoS0——消息服务质量为0&#xff0c;消息发送至多一次1.1.3.2 QoS1——消息发送至少一次1.1.3.3 QoS2——消息发送仅一次1.1.3.4 不…

Oracle中的数据导出(4)

目录 法一&#xff1a;使用SQL plus命令脚本 法二&#xff1a;使用PLSQL Developer工具 前几篇文章描述了如何将Oracle中的数据导出到库外&#xff0c;但是导出的数据结果都是文本文档&#xff0c;这样页面查看不和谐&#xff0c;编辑又略显麻烦。因此这篇文章将描述如何将Or…

Pb协议的接口测试

【摘要】 Protocol Buffers 是谷歌开源的序列化与反序列化框架。它与语言无关、平台无关、具有可扩展的机制。用于序列化结构化数据&#xff0c;此工具对标 XML &#xff0c;支持自动编码&#xff0c;解码。比 XML 性能好&#xff0c;且数据易于解析。更多有关工具的介绍可参考…

氟化物选择吸附树脂Tulsimer ®CH-87 ,锂电行业废水行业矿井水除氟专用树脂

氟化物选择吸附树脂 Tulsimer CH-87 是一款去除水溶液中氟离子的专用的凝胶型选择性离子交换树脂。它是具有氟化物选择性官能团的交联聚苯乙烯共聚物架构的树脂。 去除氟离子的能力可以达到 1ppm 以下的水平。中性至碱性的PH范围内有较好的工作效率&#xff0c;并且很容易再生…

Vue.js 中的过渡动画是什么?如何使用过渡动画?

Vue.js 中的过渡动画是什么&#xff1f;如何使用过渡动画&#xff1f; 在 Vue.js 中&#xff0c;过渡动画是一种在元素插入、更新或删除时自动应用的动画效果&#xff0c;可以为应用程序增加一些动态和生动的效果。本文将介绍 Vue.js 中过渡动画的概念、优势以及如何使用过渡动…

Nginx正则表达式、location、rewrite

目录 一、常用的Nginx正则表达式 二&#xff1a;localtion 1、location 分类 2、 location 常用的匹配规则 3、location 优先级 4、 location 示例 5、优先级总结 6、实际网站使用中&#xff0c;至少有三个匹配规则定义 &#xff08;1&#xff09;第一个必选规则 &…

chatgpt赋能python:将一行数变成列——Python简单实现

将一行数变成列——Python简单实现 在数据处理时&#xff0c;我们常常会遇到将一行的数据转换成列的情况&#xff0c;例如将多个数据在Excel表格中拆分为不同的列。这时候&#xff0c;Python可以帮助我们快速实现这个功能。 什么是Python&#xff1f; Python是一种高级&…

N-propargyloxycarbonyl-L-lysine,1215204-46-8,是一种基于赖氨酸的非天然氨基酸 (UAA)

产品描述&#xff1a; N-ε-propargyloxycarbonyl-L-lysine (H-L-Lys(Poc)-OH) 是一种基于赖氨酸的非天然氨基酸 (UAA)。 广泛用于多种生物体中荧光探针的生物偶联。 N- ε- Propargyloxycarbonyl-L-lysine (H-L-Lys (Poc) - OH) is a non natural amino acid (UAA) based on …

IDEA 终端命令行设置

一、说明 在使用 IDEA 进行程序开发时&#xff0c;需要使用到终端 Terminal 的功能&#xff0c;便于能够快速使用 shell 命令&#xff0c;进行各种相关的操作。 这些操作可以包括代码的版本控制、程序的打包部署等等 比如&#xff0c;前后端的集成开发环境&#xff08;IDEA、We…

.gitignore忽略文件不生效

前言 .gitignore忽略文件时git仓库很重要的一个配置&#xff0c;在创建仓库时就会有模板选择和忽略文件。 .gitignore忽略文件意思是在上传到代码仓库时&#xff0c;控制把哪些代码文件不上传到代码仓库。 在实际开发中其实写的代码是没有多大的&#xff0c;主要的是插件本地…

基于牛顿拉夫逊的配电网潮流计算研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Symfony v6.2.11 正式发布,经典 PHP Web 开发框架

导读Symfony v6.2.11 发布了&#xff01;Symfony 是一款基于 MVC 架构的 PHP 框架&#xff0c;致力于减少重复代码的编写&#xff0c;以加速 Web 应用的开发和维护。Symfony 与许多关系型数据库集成的也非常好&#xff0c;成本也较小。 此外&#xff0c;Symfony 致力于在企业背…