TGTGInsighttelegram intelligenceLIVE / telegram public index
← Welcome to the Black Parade
Welcome to the Black Parade avatar

TGINSIGHT POST

Post #40

@TheB1ackParade

Welcome to the Black Parade

Views126Post view count
PostedMar 203/02/2020, 03:52 PM
Post content

Post content

发现 docker cp 的实现跟屎一样. 一开始发现的问题是 docker-ce/components/client 里的 CopyToContainer 这个 interface, 签名是这样的: CopyToContainer(ctx context.Context, container, path string, content io.Reader, options types.CopyToContainerOptions) error 我注意到的问题是里面 path 必须是 dirname, 比如说你想 docker cp ./a.txt $CONTAINER_ID:/etc/b.txt, 那么 path 必须是 /etc, 否则直接报错. 那么问题来了, destination filename 都没传过去, dockerd 是怎么知道文件名的? 就比如上面的复制到 /etc/b.txt, 我都没把 b.txt 告诉 dockerd 它怎么可能做到的? 懒, 不想看代码, 先抓包, docker -H tcp://10.143.201.210:2376 cp /tmp/a.txt 4ea389f2119e:/etc/b.txt, 发现是这样的: PUT /v1.39/containers/4ea389f2119e/archive?noOverwriteDirNonDir=true&path=%2Fetc HTTP/1.1 Host: 10.143.201.210:2376 User-Agent: Docker-Client/19.03.5 (darwin) Transfer-Encoding: chunked Content-Type: text/plain PaxHeaders.0/b.txt..................................................................................0000000.0000000.0000000.00000000021.00000000000.011174. x....................................................................................................ustar.00....................................................................................................................................................................................................................................................... 那么一看到 PaxHeaders 就了然了, body 是一个 tarball, 所以 dest filename 也蕴藏其中. 那么问题来了, CopyToContainer interface 里的类型签名只说是 container io.Reader, 我直接 bytes.NewBuffer() 生成一个 reader 它岂不是要仆街? 看了一下源码, 结果在接口实现里注释写着: // CopyToContainer copies content into the container filesystem. // Note that `content` must be a Reader for a TAR archive func (cli *Client) CopyToContainer(ctx context.Context, containerID, dstPath string, content io.Reader, options types.CopyToContainerOptions) error { 我服了... 垃圾 API, 居然会在客户端创建一个临时文件.tar 再 open, 匪夷所思, 给我三个大脑我都想不出这种实现, 内存里不能压缩 bytes buffer 吗? 更垃圾在后面. 我发现 docker cp 连 process substitution 都不认, 具体的命令是这样的: docker cp /tmp/a.txt 4ea389f2119e:/etc/b.txt // ok docker cp <(cat /tmp/a.txt) 4ea389f2119e:/etc/b.txt // not ok 后者的命令不会出错, 然而如果你去看 cp 进去是啥文件的时候就: # docker exec -it f21996c9dab3 ls -l /etc/b.txt lrwxrwxrwx 1 root root 15 Mar 2 15:12 /etc/b.txt -> pipe:[38941509] 这垃圾居然直接把 process substitution 的 pipe 打到 tarball 里了, 就不能认认真真去 read 到内存里吗? 然后它还有一个 followLink 的 option, 这次直接报错了: # docker -H tcp://localhost:2376 cp --follow-link <(cat /tmp/a.txt) f21996c9dab3:/etc/b.txt lstat /proc/300038/fd/pipe:[38956641]: no such file or directory 这次直接报错了, 看了一下代码, 因为在 follow 的情况下它非要去 os.Lstat 一下 link 的 dest, 然而 pipe 在文件系统上是不存在的, 就挂了. 我 真 的 服 了. from https://gist.github.com/jschwinger23/d8486dac6cef0d4b88be40f5409515eb#file-docker-cp-sucks-md