脚本宝典收集整理的这篇文章主要介绍了Docker容器的优化思路🐟,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
不能忽视对
dockerfile
的优化,但也不要为了优化而优化。
缩短构建时间
选择合适产品的基础镜像,这点相对来说非常重要。选择一个合适的基础镜像,需要能够满足运行应用所需要的最小的镜像。理论上是能用小的就不要用大的,能用轻量级的就不要用重量级的,能用性能好的就不要用性能差的,能用稳定版就不要用开发版。
比如我们构建 Java
语言的程序,最好的方式就是使用官方提供的 openjdk:8
作为基础镜像,而非使用比较大的 ubuntu:18.04
作为基础镜像。另外,还有一个需要我们注意的地方就是,尽可能使用官方的特定版本的镜像,而不要使用 latest
这个频繁变动的 tag
作为基础镜像。
FROM ubuntu:18.04
FROM openjdk:latest
FROM openjdk:8
缩短构建时间
构建 Docker
镜像的时候,会缓存 Dockerfile
中尚未更改的所有步骤。所以,如果新构建时更改任何指令,将后的指令步骤将会重新来不再使用缓存。举例来说,就是指令 3
发生了变更,其后的 4-n
就会重跑并重新生成缓存。
因此,编写 Dockerfile
的时候,就需要将最不可能产生更改的指令放在前面。比如,可以把 WORKDIR
/ENV
等命令放在前面,COPY
/ADD
等命令放在后面。这样,在构建过程中较多的使用了缓存,就可以节省很多时间了。
FROM ubuntu:18.04
WORKDIR /opt/app
ARG env=env
ENV ENV=${env}
COPY . /opt/app/
RUN apt update -y htop
COPY
/ADD
等命令的时候越具体越好,最好只复制所需的内容。FROM ubuntu:18.04
WORKDIR /opt/app
COPY target/docker_patch.py /opt/app/
RUN apt update -y htop
缩短构建时间
Dockerfile
的时候,每一个指令都会创建一层并构成新的镜像。当运行多个指令时,会产生非常多层的镜像,不仅仅增加了构建部署的时间,也很容易出错。因此,在这种情况下,我们需要将同类型的指令合并然后再一起运行。合并指令时,一定要注意格式(比如换行、缩进、注释等)会让维护、排障更为容易。RUN apt update -y &&
DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends -y
python3.6-dev
python3-distutils
nginx
vim.tiny
supervisor
ca-certificates
tzdata
locales
build-essential
fontconfig
减少镜像大小
rm -rf /var/lib/apt/lists/* /tmp/*
rm -rf /etc/nginx/sites-enabled/default
减少镜像大小
.dockerignore
文件用于忽略那些镜像构建时非必须的文件,这些文件可以是开发文档、日志、其他无用的文件。注意再添加文件或者文件夹的时候,最好的方式是先全部排除,之后再添加。*
.*
!config/config-docker.yml
!config/logging.conf
!docker/nginx
!docker/supervisor
......
提高可维护性
Dockerfile
文件来维护就变得越来越笨重,且构建时间也越来越长。为了便捷且高效的管理,就需要我们构建基本的 runtime
镜像。在 runtime
镜像中,主要包含项目所依赖的基础环境包,多为很少改动的内容。而我们的项目就不用从基础镜像开始,而是从 runtime
镜像开始,大大的加速的构件速度。FROM ubuntu:18.04
ENV LANG=en_US.UTF-8
WORKDIR /opt/app
COPY misc/prod-requirements.txt /opt/app/misc/prod-requirements.txt
RUN sed -i s/archive.ubuntu.com/mirrors.aliyun.com/g /etc/apt/sources.list &&
apt update -y &&
DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends -y
python3.6-dev
python3-distutils
nginx
vim.tiny
supervisor
提高可维护性
Docker v17.05
开始支持多阶段构建。多段构建的目的就是,使用多阶段构建来删除构建依赖项。容器多阶段构建可由多个 FROM
语句构成,每个 FROM
语句开始一个新的阶段。它们可以用 AS
关键字进行别名命名。我们用它来别名作为我们的第一阶段构建器,以便稍后引用,它将在一致的环境中包含所有构建依赖项。
第二阶段是我们的最后阶段,将产生最终镜像包。它将包括运行时的严格必要条件,在本例中是基于 Alpine
的最小 JRE
(Java
运行时)。中间构建器阶段将被缓存但不会出现在最终映像中。要将构建的内容添加到最终镜像的话,请使用 COPY --from=STAGE_NAME
参数进行制定。
# 使用as来为某一阶段命名
FROM golang:1.9-alpine as builder
# 当我们只想构建builder阶段的镜像时,增加--target=builder参数即可
$ docker build --target builder -t username/imagename:tag .
# 我们也可以复制任意镜像中的文件
$ COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
使用 buildx 构建多种系统架构支持的 Docker 镜像
我们知道使用镜像创建一个容器,该镜像必须与 Docker
宿主机系统架构一致,例如 Linux x86_64
架构的系统中只能使用 Linux x86_64
的镜像创建容器。否则,我们尝试在其他平台系统上面,则根本获取不到对应镜像内容。
为不同系统架构打包不同的镜像,则会导致十分繁琐而且很难维护,所以后来官方引入了 manifest
和 buildx
(在Docker19.03+
版本引入) 两个子命令来处理上述问题。但是,BuildKit
是下一代的镜像构建组件。
# 可以看到现在都处于试验阶段
$ docker buildx build xxx
docker manifest is only supported on a Docker cli with experimental cli features enabled
# 命令属于实验特性必须设置环境变量
$ export DOCKER_CLI_EXPERIMENTAL=enabled
Docker
默认的 builder
实例不支持同时指定多个 --platform
,我们必须首先创建一个新的 builder
实例。$ docker buildx create --name mybuilder
$ docker buildx use mybuilder
Dockerfile
文件。使用 buildx
命令构建镜像,注意将 myusername
替换为自己的 Docker Hub
用户名,而 --push
参数表示将构建好的镜像推送到 Docker
仓库。# 构建多平台镜像
$ docker buildx build --platform
linux/arm,linux/arm64,linux/amd64
-t <myusername>/hello . --push
# 查看镜像信息
$ docker buildx imagetools inspect <myusername>/hello
# arm
$ docker run -it --rm myusername/hello
Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2019 armv7l Linux
# arm64
$ docker run -it --rm myusername/hello
Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2019 aarch64 Linux
# amd64
$ docker run -it --rm myusername/hello
Linux buildkitsandbox 4.9.125-linuxkit #1 SMP Fri Sep 7 08:20:28 UTC 2019 x86_64 Linux
使用 BuildKit 提供的 Dockerfile 新指令来更快的构建 Docker 镜像。 => 官网地址
RUN --mount=type=cache
RUN --mount=type=bind
RUN --mount=type=tmpfs
RUN --mount=type=secret
RUN --mount=type=ssh
目前,几乎所有的程序都会使用依赖管理工具,例如 Node.js
中的 npm
等等,当我们构建一个镜像时,往往会重复的从互联网中获取依赖包,难以缓存,大大降低了镜像的构建效率。
例如一个前端工程需要用到 npm
工具,使用多阶段构建,构建的镜像中只包含了目标文件夹 dist
,但仍然存在一些问题,当 package.json
文件变动时,RUN npm i && rm -rf ~/.npm
这一层会重新执行,变更多次后,生成了大量的中间层镜像。
FROM node:alpine as builder
WORKDIR /app
COPY package.json /app/
RUN npm i --registry=https://registry.npm.taobao.org
&& rm -rf ~/.npm
COPY src /app/src
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /app/dist
为解决这个问题,进一步的我们可以设想一个类似 数据卷 的功能,在镜像构建时把 node_modules
文件夹挂载上去,在构建完成后,这个 node_modules
文件夹会自动卸载,实际的镜像中并不包含 node_modules
这个文件夹,这样我们就省去了每次获取依赖的时间,大大增加了镜像构建效率,同时也避免了生成了大量的中间层镜像。BuildKit
提供了 RUN --mount=type=cache
指令,可以实现上边的设想。
第一个 RUN
指令执行后,id
为 my_app_npm_module
的缓存文件夹挂载到了 /app/node_modules
文件夹中。多次执行也不会产生多个中间层镜像。
第二个 RUN
指令执行时需要用到 node_modules
文件夹,node_modules
已经挂载,命令也可以正确执行。
第三个 RUN
指令将上一阶段产生的文件复制到指定位置,from
指明缓存的来源,这里 builder
表示缓存来源于构建的第一阶段,source
指明缓存来源的文件夹。
Option | Description |
---|---|
id |
id 设置一个标志,以便区分缓存。 |
source |
来源的文件夹路径。 |
target (必填) |
缓存的挂载目标文件夹。 |
from |
缓存来源(构建阶段),不填写时为空文件夹。 |
ro ,readonly |
只读,缓存文件夹不能被写入。 |
sharing |
有 shared private locked 值可供选择。sharing 设置当一个缓存被多次使用时的表现,由于 BuildKit 支持并行构建,当多个步骤使用同一缓存时(同一 id )会发生冲突。shared 表示多个步骤可以同时读写,private 表示当多个步骤使用同一缓存时,每个步骤使用不同的缓存,locked 表示当一个步骤完成释放缓存后,后一个步骤才能继续使用该缓存。 |
FROM node:alpine as builder
WORKDIR /app
COPY package.json /app/
RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked
--mount=type=cache,target=/root/.npm,id=npm_cache
npm i --registry=https://registry.npm.taobao.org
COPY src /app/src
RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked
# --mount=type=cache,target=/app/dist,id=my_app_dist,sharing=locked
npm run build
FROM nginx:alpine
# COPY --from=builder /app/dist /app/dist
# 为了更直观的说明 from 和 source 指令,这里使用 RUN 指令
RUN --mount=type=cache,target=/tmp/dist,from=builder,source=/app/dist
# --mount=type=cache,target/tmp/dist,from=my_app_dist,sharing=locked
mkdir -p /app/dist && cp -r /tmp/dist/* /app/dist
二进制文件安装和使用都相对来说很简单,考虑自己维护还是依赖第三方镜像了!- 来自
binary
)工具的时候,比如 docker-compose
、jq
、kubectl
、docker
等命令,可以考虑直接从他们的镜像里直接 COPY
过来,替代使用 wget
/curl
下载安装的方式。
chmod
、清理临时文件等操作CPU
体系架构docker build
的缓存base image
中引入 wget/curl/tar/gzip/ca-certificates/openssl
这类工具的依赖OCI Artifacts
更加云原生# docker
COPY --from=docker:20.10.12-dind-rootless /usr/local/bin/docker /usr/local/bin/docker
FROM golang:1.17
ARG GOLANG_LINT_VERSION=V1.43.0
ENV GOV PATH=/usr/local/bin/govc
COPY --from=vmware/govc:v0.27.2 /govc /usr/local/bin
CoPY --from=dtzar/helm-kubectl:3.8.0 /usr/local/bin /usr/local/bin
COPY --from=koalaman/shellcheck:stable /bin/shellcheck /usr/local/bin/shellcheck
COPY -from=docker: 20.10.12-dind-rootless /usr/local/bin/docker /usr/local/bin/docker
COPY --from=hashicorp/packer:1.8 /bin/packer /usr/local/bin
COPY --from=quay.io/argoproj/argocli:v3.2.6 /bin/argo /usr/local/bin
DockerHub
被墙的环境下,大家都会依赖五花八门的 Registry
加速,而 Docker
默认对于镜像的 checksum
支持很差,容易被加料一波带走。而且国内大部分用 curl
的研发也没 checksum
的习惯。—— @Manjusaka_Lee# 镜像加MD5校验
docker:20.10.15-dind-rootless@sha256:dcc529a...51223c19
以上是脚本宝典为你收集整理的Docker容器的优化思路🐟全部内容,希望文章能够帮你解决Docker容器的优化思路🐟所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。