Skip to content

Latest commit

 

History

History
162 lines (127 loc) · 5.15 KB

02.md

File metadata and controls

162 lines (127 loc) · 5.15 KB

更多的案例

来列举下更多的 overlay diff 遗留案例

案例场景

有些场景下,xxx.tar.gz 并没有可供下载的直链,例如存在对象存储里,而 ARG 指令传递 aksk 之类的是会在 docker 镜像构建信息里看到,可以本地起一个监听 docker0 web(这样非本机无法访问,杜绝安全问题),类似下面这样:

RANDOM_PORT := $(shell shuf -i 1024-65535 -n 1)
CTR_NAME ?= build-$(RANDOM_PORT)
DOCKER0_IP ?= $(shell ip -4 addr show docker0 | awk -F '[ /]+' '/inet/{print $$3;exit}')
FILE_URL ?= $(DOCKER0_IP):$(RANDOM_PORT)

download:
    mc cp xxx/ops/xxx/xxx.tar.gz ./
    docker run -d --name $(CTR_NAME) \
        -v $$PWD/xxx.tar.gz:/shared/xxx.tar.gz \
        -p $(RANDOM_PORT):80 webshare-img
image: download
    docker build . -t xxx --build-arg GZ_URL=$(FILE_URL)
    docker rm -f $(CTR_NAME)

然后几个相关文件内容为如下:

# .gitignore
xxx.tar.gz

# .dockerignore
xxx.tar.gz

# Dockerfile
FROM xxxx
ARG GZ_URL
RUN set -eux; \
    wget http://${GZ_URL}/xxx.tar.gz; \
    tar zxf xxx.tar.gz; \
    cd xxx; \
    ./configure; \
    make; \
    make install; \
    rm -rf xxx;

除了下层添加,上层删除以外,实际上在上层修改也会产生这样的问题,镜像会附带 底层文件 + 上层修改后的文件 ,类似下面这种:

FROM xxx
WORKDIR /opt/app
COPY . .
RUN chown -R node:node /opt/app
...

例如上面这个,假设源码有 100M,最后镜像附带就有两份大小了,可以下面这样避免:

FROM xxx
WORKDIR /opt/app
COPY --chown=node:node . .

同样的还有下面这样类似:

FROM xxx
ADD xxx.tar.gz /opt/xxx/font
RUN chmod -R 755 /opt/xxx/font

上面这种最好文件打包 xxx.tar.gz 前就处理好目录权限,或者多阶段构建处理:

FROM xxx as build
ADD xxx.tar.gz /opt/xxx/font
RUN chmod -R 755 /opt/xxx/font

FROM xxx
COPY --from=build /opt/xxx/font /opt/xxx/font

不光是目录,单个文件的属性变更也一样,这个是之前业务 golang 里之前一个优化的案例:

FROM golang as build
WORKDIR /opt/app
COPY . .
RUN make build

FROM base-run
COPY --from=build --chown=app:app /opt/app/bin_file /opt/app
RUN chmod +x /opt/app/bin_file # <---

编译的二进制有 154M,然后让业务去掉第二阶段结尾 RUN 后镜像缩减了 154M,go build 的文件都有 chmod a+x 权限的,不需要额外 chmod。

接下来介绍一个插件案例,同事在 es 镜像里添加一些自研插件:

FROM elasticsearch:7.x
RUN set -eux; \
    es_version=$(./bin/elasticsearch -V | awk -F'[: ,]' '{print $3}'); \
    ./bin/elasticsearch-plugin install https://xxx; \
    chown -R elasticsearch:root /usr/share/elasticsearch

镜像里目录大小 516M /usr/share/elasticsearch ,让镜像浪费了挺多空间的,后面修改为 find 配合 chown 只处理 plugins 目录解决:

    ...; \
    find plugins \! -user elasticsearch -exec chown elasticsearch '{}' +; \

同时,不要以为 owner 一样,再次执行 chown 同样属性文件还是一样的,例如下面:

$ cat > Dockerfile << EOF
FROM alpine
RUN dd if=/dev/urandom of=random-file bs=1M count=10
RUN chown root:root random-file
EOF
$ docker build . -t test
$ docker history test
IMAGE          CREATED              CREATED BY                                      SIZE      COMMENT
81f6f4a0e427   About a minute ago   /bin/sh -c chown root:root random-file          10.5MB    
9d7678242207   About a minute ago   /bin/sh -c dd if=/dev/urandom of=random-file…   10.5MB    
1d34ffeaf190   8 days ago           /bin/sh -c #(nop)  CMD ["/bin/sh"]              0B        
<missing>      8 days ago           /bin/sh -c #(nop) ADD file:e3abcdba177145039…   7.79MB
# 折叠的话记得看最右边的 Size --->

可以看到文件同样的 owner 是 root:root ,因为 overlayfs 是遵循写时复制,对文件会产生修改行为的,都会从下层复制到本层里处理。对于以上 overlay diff 浪费了容量的,可以使用 dive 工具分析:

$ CI=true dive test
  Using default CI config
Image Source: docker://test
Fetching image... (this can take a while for large images)
Analyzing image...
  efficiency: 63.5422 %
  wastedBytes: 20971520 bytes (21 MB)
  userWastedPercent: 100.0000 %
Inefficient Files:
Count  Wasted Space  File Path
    2         21 MB  /random-file

如果不在 CI 构建里使用,可以去掉前面的 CI=true 交互使用,dive 会分析 layer 里上下层重复以及添加的文件。例如上面左下角,说 /random-file 出现了两次。

总结

  • COPY/ADD 添加文件后权限会变化的,可以 COPY --chown 、多阶段或者 Makefile 之类预处理解决
  • COPY 添加的文件被后面的 RUN 使用完删除掉的,换成 RUN 里下载
  • 不要直接 chown 和 chmod 修改下层的文件,而是使用 find 条件 + -exec 组合使用,只修改不符合的文件

链接