Dockerfile的ADD和COPY

文章目录

  • 环境
  • ADD
    • 规则
    • 校验远程文件checksum
    • 添加Git仓库
    • 添加私有Git仓库
    • `ADD --link`
  • COPY
    • `COPY --parent`
  • 使用ADD还是COPY?
  • 参考

环境

  • RHEL 9.3
  • Docker Community 24.0.7

ADD

ADD 指令把 <src> 的文件、目录、或URL链接的文件复制到 <dest>

ADD 有两种写法:

  • ADD [--chown=<user>:<group>] [--chmod=<perms>] [--checksum=<checksum>] <src>... <dest>

例如:

ADD a.txt /mydir/
  • ADD [--chown=<user>:<group>] [--chmod=<perms>] ["<src>",... "<dest>"]

例如:

ADD ["a b.txt", "/mydir/"]

如果路径里包含空格,则必须用第二种写法。

一条ADD 指令里可以有多个 <src>

ADD a.txt b.txt /mydir/

<src> 可以使用通配符:

  • * :匹配任意0个、1个、或多个字符

例如:

ADD hom* /mydir/
  • ? :匹配任意1个字符

例如:

ADD hom?.txt /mydir/

<dest> 可以是绝对路径或相对路径。相对路径是建立在 <WORKDIR> 基础上的。

例如:

WORKDIR /dir1
ADD a.txt dir2/

则目标路径是 /dir1/dir2/

<src> 里包含特殊字符(比如 [] )时,需要按照Golang的规则转义,以避免被当作匹配模式。

比如,源文件名为 arr[0].txt ,则写法如下:

ADD arr[[]0].txt /mydir/

注:这一段话有点费解,参见我另一篇文档( https://blog.csdn.net/duke_ding2/article/details/135519942 )。

新创建的文件和目录,其UID和GID默认值都是0。可以通过 --chown 指定username/groupname(字符串)或者UID/GID(整数)。如果指定的是username/groupname字符串,则使用 /etc/passwd/etc/group 来转换为对应的UID和GID。若只指定username或只指定UID,则GID与UID相同。

例如:

ADD --chown=55:mygroup files* /somedir/
ADD --chown=bin files* /somedir/
ADD --chown=1 files* /somedir/
ADD --chown=10:11 files* /somedir/
ADD --chown=myuser:mygroup --chmod=655 files* /somedir/
  • 如果指定的username或groupname在 /etc/passwd/etc/group 里查找不到,则使用默认值0。

例如:

ADD --chown=xxx:yyy a.txt dir2/

结果为:

docker run kai0111_2 ls -l dir2
total 4
-rw-r--r--    1 root     root            12 Jan 11 07:20 a.txt
  • 如果指定的是整数,则不查看 /etc/passwd/etc/group ,指定的什么值就是什么值。

例如:

ADD --chown=111:222 a.txt dir2/

结果为:

docker run kai0111_2 ls -l dir2
total 4
-rw-r--r--    1 111      222             12 Jan 11 07:20 a.txt
  • 如果文件系统不包含 /etc/passwd/etc/group 文件,而在 ADD 指令里指定了username/groupname字符串,则构建会失败。

  • 如果 <src> 是远程的文件或URL,则目标文件的权限是“600”。

例如:

ADD https://github.com/dockersamples/buildme/blob/main/README.md dir1/

构建后,结果为:

docker run kai0111_3 ls -l dir1
total 136
-rw-------    1 root     root        137739 Jan  1  1970 README.md
  • 在获取远程文件时,如果response里有HTTP last-modified header,则该值会被用来指定目标文件的mtime。

在上一个例子中,response里没有HTTP last-modified header,所以mtime被指定为1970年1月1日。

下面是一个有HTTP last-modified header的例子:

在这里插入图片描述

添加该URL:

ADD https://docs.docker.com/guides/get-started/ dir1/

构建后,结果为:

docker run kai0111_4 ls -l dir1
total 68
-rw-------    1 root     root         68424 Jan 10 15:29 get-started

可见,修改时间时间和response里 last-modified header的值是一致的。

注意:mtime不会用来判断文件是否有修改以及是否要更新缓存。

  • 如果URL的文件受验证保护,则需要使用 RUN wgetRUN curl 或者其它方法,因为 ADD 指令不支持身份验证。

  • 如果 <src> 的内容有变化,则 ADD 指令的cache失效,随后的指令的cache也都失效,包括 RUN 指令。显然,应该合理放置 ADD 指令,使其只影响与源文件变化相关的步骤。

规则

ADD 指令有如下规则:

  • <src> 必须在build context里。不能使用 ../xxx ,也不能使用绝对路径。
  • 如果 <src> 是目录,则目录的所有内容都会复制,包括文件系统元数据。(注意:目录本身并不复制,只复制目录里面的内容。)
  • 如果 <src> 是URL,且 <dest>/ 结尾,则文件名由URL推断,下载到 <dest>/<filename> 。例如, ADD http://example.com/foobar / 会复制文件到 /foobar 。URL必须能推断出文件名,像 http://example.com 这样的URL是不行的。
  • 如果 <src> 是本地的压缩的tar包(比如 identitygzipbzip2xz ),则会被解压为一个目录。但如果 <src> 是远程文件,则不会解压。当目录被复制或解压时,其行为和 tar -x 是相同的。结果是以下的结合:
    1. Whatever existed at the destination path and
    2. The contents of the source tree, with conflicts resolved in favor of “2.” on a file-by-file basis.

注:这段话我没看懂是什么意思……我猜测意思是:目标路径下可能已经存在一些文件和目录,现在把tar包解压到目标路径时,可能会产生冲突,会按照“favor of “2.””逐个文件的解决冲突,但是不清楚“favor of “2.””是什么意思。

我测试了一下,如果遇到同名文件,解压的文件会覆盖原有的文件。

a.txt 文件内容如下:

hello

a.tar.gz 文件打包了以下目录结构:

.
├── a
│   ├── a.txt
│   └── b.txt
└── a.tar.gz

其中 a.txt 文件内容如下:

hi

创建 Dockerfile 文件如下:

FROM alpine

ADD a.txt a/a.txt

ADD a.tar.gz .

构建:

docker build -t kai0111_5 .

查看

docker run kai0111_5 sh -c "tree a && cat a/a.txt"
a
├── a.txt
└── b.txt

0 directories, 2 files
hi

可见, a.txt 文件内容是tar包里的文件内容。

注:不能写成 docker run kai0111_5 tree a && cat a/a.txt ,否则会被识别为 docker run kai0111_5 tree acat a/a.txt 两条命令的组合。

  • 文件是否被看做压缩文件并自动解压,是由文件内容决定的,而不是由文件的后缀名决定的。
FROM alpine

ADD a.tar.gz .

其中, a.tar.gz 是一个空文件。

构建:

docker build -t kai0111_6 .

查看:

docker run kai0111_6 ls -l
total 12
-rw-r--r--    1 root     root             0 Jan 11 10:48 a.tar.gz
......

可见,仍然是 a.tar.gz ,没有解压。

  • 对于其它类型的文件,则连同元数据一起复制到目标路径。如果 <dest>/ 结尾,则被当作是一个目录, <src> 的内容会被写入该目录。

  • 如果有多个 <src> ,或者使用了通配符,则 <dest> 必须是目录,并以 / 结尾。

  • 如果 <dest> 不是以 / 结尾,则会看作文件名。

  • 如果 <dest> 路径里的目录不存在,则会自动创建。

校验远程文件checksum

语法:

--checksum=<checksum>

例如:

ADD --checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz /

注:可用 sha256sum 命令生成checksum:

sha256sum linux-0.01.tar.gz
24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d  linux-0.01.tar.gz

这样,如果文件发生变化,checksum对不上,就会及时报错。

创建 Dockerfile 文件如下:

# syntax=docker/dockerfile:1

FROM alpine

ADD --checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d https://mirrors.edge.kernel.org/pub/linux/kernel/Historic/linux-0.01.tar.gz /

注意: # syntax=docker/dockerfile:1 这一行不能缺少,否则会报错:

ERROR: failed to solve: instruction 'ADD --checksum=<CHECKSUM>' requires the labs channel

构建:

docker build -t kai0111_7 .

查看:

docker run kai0111_7 ls -l    
-rw-------    1 root     root         73091 Oct 30  1993 linux-0.01.tar.gz
......

如果文件发生了变化(此处用checksum变化来模拟文件变化,总之都是二者不匹配),构建时报错:

docker build -t kai0111_8 .
......
ERROR: failed to solve: digest mismatch sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d: sha256:e97d6191145d306c052bb0205e66ad507e934f189edd1b7f58f17ab1a7387fd0

--checksum 只支持HTTP来源的文件。若是本地文件,则会报错。

创建 Dockerfile 文件如下:

# syntax=docker/dockerfile:1

FROM alpine

ADD --checksum=sha256:24454f830cdb571e2c4ad15481119c43b3cafd48dd869a9b2945d1036d1dc68d linux-0.01.tar.gz /

构建,报错如下:

docker build -t kai0111_9 .
......
ERROR: failed to solve: checksum can't be specified for non-HTTP sources

添加Git仓库

语法:

ADD [--keep-git-dir=<boolean>] <git ref> <dir>

例如:

# syntax=docker/dockerfile:1
FROM alpine
ADD --keep-git-dir=true https://github.com/moby/buildkit.git#v0.10.1 /buildkit

注意: # syntax=docker/dockerfile:1 这一行不能缺少,否则会报错:

ERROR: failed to solve: instruction ADD <git ref> requires the labs channel

构建:

docker build -t kai0111_10 .

查看:

docker run kai0111_10 tree -L 1 /buildkit

注:结果很长,不在这里展示了。tree 命令的 -L 1 选项(只显示一层结构)无效,可能是因为alpine image里的 tree 命令做了简化。

--keep-git-dir=true 选项表示是否添加 .git 目录,其默认值是false。

添加私有Git仓库

语法:

ADD git@git.example.com:foo/bar.git /bar

git.weixin.qq.com 为例。

首先在网站上配置好ssh key,确认git clone没有问题:

git clone git@git.weixin.qq.com:dukeding/test0522.git
Cloning into 'test0522'...
sign_and_send_pubkey: signing failed for RSA "/home/ding/.ssh/id_rsa" from agent: agent refused operation
remote: Total 101 (delta 0), reused 101 (delta 0)
Receiving objects: 100% (101/101), 617.38 KiB | 666.00 KiB/s, done.
Resolving deltas: 100% (33/33), done.

创建 Dockerfile 文件如下:

# syntax=docker/dockerfile:1

FROM alpine

ADD git@git.weixin.qq.com:dukeding/test0522.git /dir1

构建:

docker build --ssh default -t kai0111_11 .

注意:多了 --ssh default 选项,如果不加该选项,会报错:

ERROR: failed to solve: failed to load cache key: unset ssh forward key default

查看:

docker run kai0111_11 ls -l dir1
total 16
-rw-r--r--    1 root     root           583 Jan 11 13:41 README.md
drwxr-xr-x    4 root     root            52 Jan 11 13:41 cloudfunctions
drwxr-xr-x    5 root     root           135 Jan 11 13:41 miniprogram
-rw-r--r--    1 root     root          1847 Jan 11 13:41 project.config.json
-rw-r--r--    1 root     root          1722 Jan 11 13:41 project.private.config.json
-rw-r--r--    1 root     root           223 Jan 11 13:41 uploadCloudFunction.bat

注:在RedHat 9.3中,遇到问题:

问题1:

Unable to negotiate with **** port 22: no matching host key type found. Their offer: ssh-rsa

解决方法:

创建 ~/.ssh/config 文件,权限是600。

Host *
HostkeyAlgorithms +ssh-rsa
PubkeyAcceptedKeyTypes +ssh-rsa

参考:

  • https://www.jianshu.com/p/764249229bc4

问题2:

ssh_dispatch_run_fatal: Connection to 212.64.118.180 port 22: error in libcrypto

解决办法:

sudo update-crypto-policies --show
sudo update-crypto-policies --set DEFAULT:SHA1

参考:

  • https://zhuanlan.zhihu.com/p/557504425
  • https://access.redhat.com/articles/3666211

ADD --link

参见我另一篇文档( https://blog.csdn.net/duke_ding2/article/details/135543261 )。

注意:该功能貌似目前被disable了。

COPY

COPY 指令和 ADD 指令大同小异。其差别在于, COPY 指令只能复制本地文件,无法复制远程文件,比如URL远程文件和Git远程仓库。另外, ADD 指令在添加本地文件时,会自动把tar文件解压。

此外, --parent 选项只能用于 COPY ,不能用于 ADD

COPY --parent

语法:

COPY [--parents[=<boolean>]] <src>... <dest>

创建 Dockerfile 文件如下:

# syntax=docker/dockerfile-upstream:master-labs

FROM alpine

WORKDIR mydir

COPY ./xxx/yyy/a.txt ./xxx/zzz/a.txt dir1/

COPY --parents ./xxx/yyy/a.txt ./xxx/zzz/a.txt dir2/

构建:

docker build -t kai0111_12 .

查看:

docker run kai0111_12 tree .
.
├── dir1
│   └── a.txt
└── dir2
    └── xxx
        ├── yyy
        │   └── a.txt
        └── zzz
            └── a.txt

5 directories, 3 files

其中, dir1/a.txt./xxx/zzz/a.txt

注:其行为类似于Linux cp 命令的 --parents 选项:

cp --parents xxx/yyy/a.txt xxx/zzz/a.txt /tmp/

tree /tmp/xxx    
/tmp/xxx
├── yyy
│   └── a.txt
└── zzz
    └── a.txt

但有一点不同:如果没有 --parents 选项,则文件冲突时, cp 命令会报错:

cp xxx/yyy/a.txt xxx/zzz/a.txt /tmp/ 
cp: will not overwrite just-created '/tmp/a.txt' with 'xxx/zzz/a.txt'

COPY 指令则会覆盖文件。

通常layer的数量越少越好,所以可以利用 --parents ,尽量少用 COPY 指令,在指令里包含多个 <src> ,保持目标文件和源文件结构一致。

注: --parents 只对 COPY 有效,在 ADD 指令里使用 --parents 会报错:

ERROR: failed to solve: dockerfile parse error on line 9: unknown flag: parents

使用ADD还是COPY?

前面提到,二者的区别在于, COPY 指令只能复制本地文件,无法复制远程文件,比如URL远程文件和Git远程仓库。另外, ADD 指令在添加本地文件时,会自动把tar文件解压。那么,一般情况下,使用时该如何选择呢?

我的理解是: COPY 包含最基本的“文件复制”功能,而 ADD 包含一些“高级”功能。因此,如果是“高级”用法,则只能用 ADD ,而如果是常规的文件复制,则用 COPY (直观,没有歧义)。

参考

  • https://docs.docker.com/engine/reference/builder
  • https://docs.docker.com/develop/develop-images/instructions/#add-or-copy

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

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

相关文章

Makefile编译原理 make和makefile

一.什么是makefile 如图所示&#xff0c;一个工程中的源文件不计其数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c; Makefile 文件定义了一系列的规则来指定哪些文件需要先编译&#xff0c;哪些文件需要后编译&#xff0c;哪些文件需要重新编译&#xff…

Python中如何简化if...else...语句

一、引言 我们通常在Python中采用if...else..语句对结果进行判断&#xff0c;根据条件来返回不同的结果&#xff0c;如下面的例子。这段代码是一个简单的Python代码片段&#xff0c;让用户输入姓名并将其赋值给变量user_input。我们能不能把这几行代码进行简化&#xff0c;优化…

JS数组函数 reduce() 的用法—包含基础、进阶、高阶用法

目录 一、语法剖析 二、实例讲解 1. 求数组项之和 2. 求数组项最大值 3. 数组去重 三、其他相关方法 1. reduceRight() 2. forEach()、map()、every()、some()和filter() 四、重点总结 先看w3c语法 ✔ 常见用法 数组求和 数组最大值 ✔ 进阶用法 数组对象中的用…

vmware和ubuntu镜像下载地址

这里有vmware16和ubuntu20.0下载 链接&#xff1a;https://pan.baidu.com/s/1i9IC-KnJlrVDbl6SJ5SIKQ?pwdy2dd 提取码&#xff1a;y2dd 链接&#xff1a;https://pan.baidu.com/s/1imqJVD2dLE1TB6jIrq1-Fg?pwd690f 提取码&#xff1a;690f 这个是我本人下的vmware17 密钥可…

关于tex中的表格设置

文章目录 控制表格列宽和行高控制表格列宽的同时实现居中tex中多表格排列单元格的合并与分割对单个单元格进行操作 控制表格列宽和行高 将下面的代码放在table环境内&#xff0c;放在tabular环境外 调整表格宽度和高度&#xff1a; \resizebox{\textwidth}{2cm}{%第一个{}是表…

【计算机二级考试C语言】C数据类型

C 数据类型 在 C 语言中&#xff0c;数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间&#xff0c;以及如何解释存储的位模式。 C 中的类型可分为以下几种&#xff1a; 序号类型与描述1基本数据类型 它们是算术类型&#x…

如何使用WinDiff浏览和对比Windows源代码中的符号和系统调用信息

关于WinDiff WinDiff是一款功能强大的Windows二进制源代码安全分析与调试工具&#xff0c;该工具完全开源&#xff0c;基于Web实现其功能&#xff0c;可以帮助广大研究人员在不同版本的操作系统中浏览和对比Microsoft Windows二进制文件的符号、类型和系统调用信息。其中&…

alibaba学习笔记03(小滴课堂)

自定义Ribbon负载均衡策略实战 启动3个视频服务和一个订单服务&#xff1a; 我们可以看到它是随机调用的。 也可以使用其他负载均衡策略。 讲解新一代负载均衡组件feign介绍 这种方式去写死接口肯定是不妥当的。 于是我们使用feign负载均衡组件&#xff1a; 改造微服务 集成F…

S1-07 事件组

事件组 在 FreeRTOS 中&#xff0c;事件组&#xff08;Event Group&#xff09;是一种用于任务间通信的机制&#xff0c;用于在多个任务之间同步和传递事件。 事件组主要包含一下两个概念&#xff1a; 事件标志位&#xff08;Event Flags&#xff09;&#xff1a;每个事件标志…

【漏洞复现】优卡特脸爱云一脸通智慧管理平台权限绕过漏洞CVE-2023-6099(1day)

漏洞描述 脸爱云一脸通智慧管理平台1.0.55.0.0.1及其以下版本SystemMng.ashx接口处存在权限绕过漏洞,通过输入00操纵参数operatorRole,导致特权管理不当,未经身份认证的攻击者可以通过此漏洞创建超级管理员账户。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当…

HTTP协议请求详解

✏️✏️✏️今天给大家分享的是 HTTP 请求部分的基础知识。 清风的CSDN博客 &#x1f6e9;️&#x1f6e9;️&#x1f6e9;️希望我的文章能对你有所帮助&#xff0c;有不足的地方还请各位看官多多指教&#xff0c;大家一起学习交流&#xff01; ✈️✈️✈️动动你们发财的小…

【教程】华为数据恢复的5个简单方法

您刚刚不小心从华为手机中删除了一些重要文件&#xff0c;现在您迫切希望将它们找回来。如果是这样&#xff0c;那么您现在可能会感到沮丧和无助。您可能已向您的朋友寻求帮助或在互联网上搜索答案&#xff0c;但似乎无济于事。 华为数据恢复的5个简单方法 好吧&#xff0c;别…

windows下如何搭建Yapi环境

今天使用YApi时发现原网址无法访问。这下只能本地部署了&#xff08;官方文档&#xff09;。 第一步&#xff1a;安装node.js 获取资源 nodejs: https://nodejs.org/en/downloadLinux安装yum install -y nodejs查看node版本node -v查看npm版本npm -v第二步&#xff1a;安装mo…

Python 网络爬虫入门详解

什么是网络爬虫 网络爬虫又称网络蜘蛛,是指按照某种规则在网络上爬取所需内容的脚本程序。众所周知,每个网页通常包含其他网页的入口,网络爬虫则通过一个网址依次进入其他网址获取所需内容。 优先申明:我们使用的python编译环境为PyCharm 一、首先一个网络爬虫的组成结构…

【CAN】Basic CAN和Full CAN

文章目录 1 Basic CAN和Full CAN区别2 Basic CAN和Full CAN使用场景 >>返回总目录<< 1 Basic CAN和Full CAN区别 Basic CAN和Full CAN的定义在AUTOSAR_SWS_CANDriver中的描述如下&#xff0c;Basic表示一个HardwareObject可以处理多个L-PDUs&#xff0c;Full表示…

C#,入门教程(17)——条件语句(if-else)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(16)——可变数据类型&#xff08;var&#xff09;的基础知识与使用禁忌https://blog.csdn.net/beijinghorn/article/details/124032216 程序的核心是逻辑。 逻辑的核心是布尔条件表达式。 逻辑的主要体现形式之一是 if-else 语句…

探寻编程深渊:那些你无法想象的‘最差程序员’

在IT行业&#xff0c;有一类人让其他程序员闻风丧胆&#xff1a;那些最差的程序员。 他们的代码可能是漏洞百出&#xff0c;效率低下&#xff0c;甚至难以运行。他们可能对基本的编程概念一无所知&#xff0c;却自认为是个编程天才。那么&#xff0c;这些最差的程序员到底是什…

F-score 和 Dice Loss 原理及代码

文章目录 1. F-score1. 1 原理1. 2 代码2. Dice Loss2.1 原理2.2 代码 通过看开源图像语义分割库的源码&#xff0c;发现它对 Dice Loss 的实现方式&#xff0c;是直接调用 F-score 函数&#xff0c;换言之&#xff0c;Dice Loss 是 F-score的特殊情况。于是就研究了一下这背后…

python实现网络爬虫代码_python如何实现网络爬虫

python实现网络爬虫的方法&#xff1a;1、使用request库中的get方法&#xff0c;请求url的网页内容&#xff1b;2、【find()】和【find_all()】方法可以遍历这个html文件&#xff0c;提取指定信息。 python实现网络爬虫的方法&#xff1a; 第一步&#xff1a;爬取 使用reque…

定时任务-理论基础

什么是小顶堆 小顶堆&#xff08;Min Heap&#xff09;是一种特殊的二叉堆&#xff0c;它满足以下条件&#xff1a; 它是一个完全二叉树&#xff0c;即除了最后一层外&#xff0c;其他层的节点数都是满的&#xff0c;并且最后一层的节点从左到右依次排列。树中的每个节点的…
最新文章