我尝试从/bin 运行 ls 的副本(并尝试在容器中不在/bin 中的任何可执行文件上)并得到错误。
docker run -v ~/Desktop/test:/test alpine:latest /test/ls
exec /test/ls: no such file or directory
当我运行另一个命令时,我可以看到该文件。docker run -v ~/Desktop/test:/test alpine:latest ls /test
ls
我尝试设置 chmode
+x 在此文件上,但不进行任何更改。改变形象也没有任何影响。我已经更新了 docker 并且全部由 apt-get update && apt-get upgrade
打包.我使用 Ubuntu 20.04.3 LTS。
Docker 版本 20.10.17,构建 100c701
检查
{
"Id": "abd66c400e6f58313d71c63c92688472874cdd4393d42e22df23a90264083b33",
"Created": "2022-06-13T19:59:49.630474765Z",
"Path": "/test/ls",
"Args": [],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 1,
"Error": "",
"StartedAt": "2022-06-13T19:59:50.017551978Z",
"FinishedAt": "2022-06-13T19:59:50.016401363Z"
},
"Image": "sha256:0ac33e5f5afa79e084075e8698a22d574816eea8d7b7d480586835657c3e1c8b",
"ResolvConfPath": "/var/lib/docker/containers/abd66c400e6f58313d71c63c92688472874cdd4393d42e22df23a90264083b33/resolv.conf",
"HostnamePath": "/var/lib/docker/containers/abd66c400e6f58313d71c63c92688472874cdd4393d42e22df23a90264083b33/hostname",
"HostsPath": "/var/lib/docker/containers/abd66c400e6f58313d71c63c92688472874cdd4393d42e22df23a90264083b33/hosts",
"LogPath": "/var/lib/docker/containers/abd66c400e6f58313d71c63c92688472874cdd4393d42e22df23a90264083b33/abd66c400e6f58313d71c63c92688472874cdd4393d42e22df23a90264083b33-json.log",
"Name": "/cranky_pare",
"RestartCount": 0,
"Driver": "overlay2",
"Platform": "linux",
"MountLabel": "",
"ProcessLabel": "",
"AppArmorProfile": "docker-default",
"ExecIDs": null,
"HostConfig": {
"Binds": [
"/home/egor/Desktop/test:/test"
],
"ContainerIDFile": "",
"LogConfig": {
"Type": "json-file",
"Config": {}
},
"NetworkMode": "default",
"PortBindings": {},
"RestartPolicy": {
"Name": "no",
"MaximumRetryCount": 0
},
"AutoRemove": false,
"VolumeDriver": "",
"VolumesFrom": null,
"CapAdd": null,
"CapDrop": null,
"CgroupnsMode": "host",
"Dns": [],
"DnsOptions": [],
"DnsSearch": [],
"ExtraHosts": null,
"GroupAdd": null,
"IpcMode": "private",
"Cgroup": "",
"Links": null,
"OomScoreAdj": 0,
"PidMode": "",
"Privileged": false,
"PublishAllPorts": false,
"ReadonlyRootfs": false,
"SecurityOpt": null,
"UTSMode": "",
"UsernsMode": "",
"ShmSize": 67108864,
"Runtime": "runc",
"ConsoleSize": [
0,
0
],
"Isolation": "",
"CpuShares": 0,
"Memory": 0,
"NanoCpus": 0,
"CgroupParent": "",
"BlkioWeight": 0,
"BlkioWeightDevice": [],
"BlkioDeviceReadBps": null,
"BlkioDeviceWriteBps": null,
"BlkioDeviceReadIOps": null,
"BlkioDeviceWriteIOps": null,
"CpuPeriod": 0,
"CpuQuota": 0,
"CpuRealtimePeriod": 0,
"CpuRealtimeRuntime": 0,
"CpusetCpus": "",
"CpusetMems": "",
"Devices": [],
"DeviceCgroupRules": null,
"DeviceRequests": null,
"KernelMemory": 0,
"KernelMemoryTCP": 0,
"MemoryReservation": 0,
"MemorySwap": 0,
"MemorySwappiness": null,
"OomKillDisable": false,
"PidsLimit": null,
"Ulimits": null,
"CpuCount": 0,
"CpuPercent": 0,
"IOMaximumIOps": 0,
"IOMaximumBandwidth": 0,
"MaskedPaths": [
"/proc/asound",
"/proc/acpi",
"/proc/kcore",
"/proc/keys",
"/proc/latency_stats",
"/proc/timer_list",
"/proc/timer_stats",
"/proc/sched_debug",
"/proc/scsi",
"/sys/firmware"
],
"ReadonlyPaths": [
"/proc/bus",
"/proc/fs",
"/proc/irq",
"/proc/sys",
"/proc/sysrq-trigger"
]
},
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/115140c86b11eb773f30d2329ec05388224e75dfbcef9977c87bb3f0e32e8769-init/diff:/var/lib/docker/overlay2/22348355d634b9183f798e9622f71809a9a62a7c6accdf723aa72d979e3ba7f4/diff",
"MergedDir": "/var/lib/docker/overlay2/115140c86b11eb773f30d2329ec05388224e75dfbcef9977c87bb3f0e32e8769/merged",
"UpperDir": "/var/lib/docker/overlay2/115140c86b11eb773f30d2329ec05388224e75dfbcef9977c87bb3f0e32e8769/diff",
"WorkDir": "/var/lib/docker/overlay2/115140c86b11eb773f30d2329ec05388224e75dfbcef9977c87bb3f0e32e8769/work"
},
"Name": "overlay2"
},
"Mounts": [
{
"Type": "bind",
"Source": "/home/egor/Desktop/test",
"Destination": "/test",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
"Config": {
"Hostname": "abd66c400e6f",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": true,
"AttachStderr": true,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/test/ls"
],
"Image": "alpine:latest",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"NetworkSettings": {
"Bridge": "",
"SandboxID": "fb64f195f203033f02b9c5555e0b1d2cdd2819f3ac12f3046416f8e13378bc3d",
"HairpinMode": false,
"LinkLocalIPv6Address": "",
"LinkLocalIPv6PrefixLen": 0,
"Ports": {},
"SandboxKey": "/var/run/docker/netns/fb64f195f203",
"SecondaryIPAddresses": null,
"SecondaryIPv6Addresses": null,
"EndpointID": "",
"Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"MacAddress": "",
"Networks": {
"bridge": {
"IPAMConfig": null,
"Links": null,
"Aliases": null,
"NetworkID": "cb261cd6eca2ddb567984e2d7d9e6df14008c774d73f0d76129da6c8edc6955c",
"EndpointID": "",
"Gateway": "",
"IPAddress": "",
"IPPrefixLen": 0,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "",
"DriverOpts": null
}
}
},
"CreatedTime": 1655150389630
}
最佳答案
程序二进制本身(在大多数情况下)不是自给自足的;相反,它依赖于许多动态库(通常至少是一个 C 标准库)和一个动态链接器(也称为解释器),它知道如何将二进制文件加载到内存中并将其与这些库链接。
有关系统必须使用什么链接器来启动特定程序以及程序需要哪些库的信息存储在程序二进制文件本身中。您可以使用 ldd
等工具轻松提取此信息。和 readelf
.例如,您可以通过以下方式找到所有内容 /bin/ls
需要成功运行:
$ ldd /bin/ls
linux-vdso.so.1 (0x00007ffec7b46000)
libcap.so.2 => /usr/lib/libcap.so.2 (0x00007f805384c000)
libc.so.6 => /usr/lib/libc.so.6 (0x00007f8053642000)
/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007f80538ae000)
或者,更详细地说:$ readelf -a /bin/ls
...
Program Headers:
Type Offset VirtAddr PhysAddr
...
INTERP 0x0000000000000318 0x0000000000000318 0x0000000000000318
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
...
Dynamic section at offset 0x22a58 contains 28 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libcap.so.2]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
备注 : 我的例子取自 Arch box,所以你的结果可能不同,但接近这个。这里的要点是二进制依赖于ld-linux
。 ,这对于 Arch 和 Ubuntu 都是如此(见下文)。当你要求内核执行
ls
,它读取程序二进制文件,在文件系统中查找程序所需的链接器(/lib64/ld-linux-x86-64.so.2
)并运行它。然后链接器加载 ls
并将其所有依赖项放入内存;只有在此之后 ls
的代码本身被执行。问题是不同的 Linux 发行版(以及它们的不同版本)有不同的环境。虽然发行版通常使用 GNU libc (
glibc
) 作为 C 标准库及其 ld-linux
作为链接器,也有使用其他 libc 变体和其他链接器的发行版。 Alpine 就是这样的发行版之一:它使用 musl
libc 和它自己的链接器 ld-musl
:$ docker run -it alpine
/ # ldd /bin/ls
/lib/ld-musl-x86_64.so.1 (0x7f40c772d000)
libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1 (0x7f40c772d000)
这意味着 Alpine 没有 /lib64/ld-linux-x86-64.so.2
文件(默认情况下;见 Running glibc programs)。这就是为什么运行 ls
专为 glibc
打造基于 Alpine 容器内的基于系统(在您的情况下为 Ubuntu)失败:内核读取二进制文件,获悉它需要 /lib64/ld-linux-x86-64.so.2
加载这个程序,并在文件系统中找不到这样的文件。当内核无法找到程序请求的解释器时,它会返回与您要求它执行不存在的文件时相同的错误:
man 2 execve
...
ENOENT The file pathname or a script or ELF interpreter does not exist.
这就是为什么你会得到 no such file or directory
,这有点误导,因为它没有说明找不到哪个文件——链接器,而不是程序本身。鉴于
ls
没有很多依赖项,您尝试使用的技巧很可能适用于具有 glibc
的容器内部基于系统:$ docker run -it -v /bin/ls:/test/ls alpine /test/ls
exec /test/ls: no such file or directory
...
$ docker run -it -v /bin/ls:/test/ls ubuntu /test/ls
bin dev home lib32 libx32 mnt proc run srv test usr
boot etc lib lib64 media opt root sbin sys tmp var
...
$ docker run -it -v /bin/ls:/test/ls archlinux /test/ls
bin dev home lib64 opt root sbin sys tmp var
boot etc lib mnt proc run srv test usr
然而,在容器内运行主机二进制文件并不是你应该做的,也不能保证事情不会发生困惑,所以在使用这些技巧时要确保你真的知道你在做什么。
关于docker - docker中的任何可执行文件上都没有这样的文件或目录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72608596/