CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛
CXYVIP官网源码交易平台_网站源码_商城源码_小程序源码平台-丞旭猿论坛

最简单的技巧(太疯狂了)有几个技巧,N个技巧,编写更高效 Dockerfile,丞旭猿免费源码交易平台,

1.编程的技巧

大家好,我是陈泽锋,我在云效负责Flow流水线编排、任务调度引擎相关的工作在云效的产品体系下,我们服务了各种研发规模、技术深度的的企业用户,收到了非常多的用户反馈对于使用 Flow 进行云上构建的用户来说,构建速度是大家普遍关心的关键要素,在深入分析用户案例的过程中,我们发现了许多通用问题,只需要修改优化自己的项目或工程配置,就可以大大提升构建的性能,从而进一步加速 CICD 的效率。

2.特别的技巧

今天我们会以容器镜像构建作为切入点,总结一些在实际工程中,非常实用的优化技巧云原生时代下软件的构建和部署离不开容器技术提到容器,几乎大家下意识都会联想到 Docker 而 Docker 中有两个非常重要的概念,一个是Image(镜像),一个是Container(容器)。

3.编程的基本规则与技巧

前者是一个静态视图,打包了应用的目录结构、运行环境等;后者是一个动态视图(进程),展示的是程序的运行状态(cpu、memory、storage)等信息接下来的文章主要分享的是如何编写能使 Dockerfile 构建过程更快速、构建镜像更小的技巧。

4.3个技巧写作

镜像定义首先我们先来了解一下 Docker 镜像,它由多个只读层堆叠到一起,每一层是上一层的增量修改基于镜像创建新容器时,将在基础层的顶部添加一个新的可写层该层通常称为“容器层”下图展示了一个基于 http://

5.学编程技巧和方法

docker.io/centos 基础镜像构建的应用镜像,创建出容器时的视图。

6.编写程序的技巧

从图中我们可以看到镜像构建、容器启动的过程首先是拉取基础镜像 http://docker.io/centos;基于 http://docker.io/centos 来启动一个容器,运行指令 yum update 后进行 docker commit 提交出一个新的只读层 v1(可以理解为生成了一个新的临时镜像 A,只不过用户并不会直接引用到它);。

基于临时镜像A启动新的容器,运行安装和配置 http server等软件后,提交出一个新的只读层 v2,也生成了这里最终被开发者引用的镜像版本 B;基于镜像版本B运行的容器,会再追加一层读写层(对容器的文件创建、修改、删除等操作,都在这一层生效);

镜像来源镜像主要是 Docker 通过读取、运行 Dockerfile 的指令来生成举官网上的一个 Dockerfile 例子:FROM ubuntu:18.04 COPY . /app RUN make /app CMD python /app/app.py。

它的核心逻辑是定义引用的基础镜像 base image,执行如 COPY 指令从上下文 context 里复制文件到容器中,运行 RUN 执行用户自定义构建脚本,最后定义容器启动的 CMD 或 ENTRYPOINT。

构建更高效的镜像也要围绕上述涉及到的概念进行优化Dockerfile 优化技巧使用国内的基础镜像Flow 作为云上构建产品,每次构建都会给用户提供全新的构建环境,以避免环境污染导致带来过高运维成本正因为如此,Flow 每次构建都会重新去下载 Dockerfile 中指定的基础镜像。

如果 Dockerfile 中指定基础镜像来源于 Docker Hub,则有可能因为网络延时问题导致下载缓慢,比如:From NginxFrom java:8FROM openjdk:8-jdk-alpine

典型现象如下:

可以将自己的基础镜像文件转存至国内镜像仓库,并修改自己的 Dockerfile 文件,操作步骤如下:将境外镜像在 pull 到本地docker pull openjdk:8-jdk-alpine;将基础镜像 push 到阿里云镜像仓库(。

http://cr.console.aliyun.com)的国内 region(比如北京、上海等)docker tag openjdk:8-jdk-alpine http://registry.cn-beijing.aliyuncs.com。

/yournamespace/openjdk:8-jdk-alpinedocker push http://registry.cn-beijing.aliyuncs.com/yournamespace/openjdk:8-jdk-alpi

;修改你的 dockerfile 中 FROM,从你自己的镜像仓库下载镜像 From http://registry.cn-beijing.aliyuncs.com/yournamespace/openjdk:8-jdk-alpine。

;尽量小的、够用的基础镜像大镜像除了占用更多的磁盘空间外,在应用部署时也会占用更多的网络消耗,导致更长的服务启动耗时使用更小的基础镜像,例如使用 alpine 作为 base image这里我们看一个打包 mysql-client 二进制的镜像,基于 alpine 和 ubuntu 的镜像大小对比。

FROM alpine:3.14 RUN apk add –no-cache mysql-client ENTRYPOINT [“mysql”]

FROM ubuntu:20.04 RUN apt-get update \ && apt-get install -y –no-install-recommends mysql-client \ && rm -rf /var/lib/apt/lists/* ENTRYPOINT [“mysql”]

由此可以看到使用尽量小的 base 镜像有利于大幅度减少镜像的大小减少上下文关联目录文件docker 是 c/s 的架构设计,当用户执行 docker build 时并不是在 client 直接进行构建,而是将 build 指定的目录作为上下文传递到 server 端,再执行上述提到的镜像构建的过程。

如果执行镜像构建的上下文中关联大量不必要的文件,那可以使用 .dockerignore 来忽略这些文件(与 .gitignore 类似,定义的文件不会被跟踪、传输)以下举一个官网上的例子,通过构建日志可以观察看 context 的大小只有几十 byte:

mkdir myproject && cd myproject echo “hello” > hello echo -e “FROM busybox\nCOPY / /\nRUN cat /hello” > Dockerfile docker build -t helloapp:v1 –progress=plain . #7 [internal] load build context #7 sha256:6b998f8faef17a6686d03380d6b9a60a4b5abca988ea7ea8341adfae112ebaec #7 transferring context: 26B done #7 DONE 0.0s

当我们在 myproject 下放置一个与程序无关的大文件(或无关小文件,如应用构建的依赖包等)时,重新构建 helloapp:v3 时发现需要传输 70 MB的内容到服务端,并且镜像大小到 71MB#5 [internal] load build context #5 sha256:746b8f3c5fdd5aa11b2e2dad6636627b0ed8d710fe07470735ae58682825811f #5 transferring context: 70.20MB 1.0s done #5 DONE 1.1s

减少层的数量、控制层的大小如果把镜像构建的简单等同为 bash 等脚本指令执行的过程,往往就会踩中镜像层过多,镜像层包含无用文件的坑下面让我们看三个 dockerfile 的写法和它们分别构建出来的镜像大小。

首先是 centos_git_nginx:normal 镜像,它基于 centos 基础镜像增加了两层,分别安装了 git 和 nginx两个二进制,可以看到镜像的大小大概在 402MBFROM centos RUN yum install -y git RUN yum install -y nginx。

接着我们对 dockerfile 做一下优化,将它改成以下只增加一层的写法,可以看到镜像的大小缩减到 384 MB,证明了层的减少能减少镜像的大小FROM centos RUN yum install -y git && yum install -y nginx。

由于 yum install 过程会生成一些缓存数据,这些在应用运行过程中是不需要的,我们在安装完软件后立即将其删除后观察镜像再次缩小到 357 MBFROM centos RUN yum install -y git && \ yum install -y nginx && \ yum clean all && rm -rf /var/cache/yum/*。

TIPS: 我们知道了镜像构建过程生成每一层为只读层是不能再被修改的,以下的写法并不能对减少镜像的大小起到作用,反而还增加了一层无用镜像层FROM centos RUN yum install -y git && \ yum install -y nginx RUN yum clean all && rm -rf /var/cache/yum/*。

需要注意的是过于追求层次的少也不一定是好的做法,这样会使得构建或拉取镜像时减少了层被缓存的概率将不变层放到前面,可变层放到后面当我们在同个时间内多次执行 docker build 可以发现,在构建完一次镜像后再次构建,docker 会利用缓存中的镜像数据直接进行复用。

事实上 Docker 会逐步完成 Dockerfile 中的指令,并按指定的顺序执行每个指令在检查每条指令时,Docker在其缓存中查找可以重用的现有镜像Docker 从缓存中已存在的父镜像开始,将下一条指令与从该基本镜像派生的所有子镜像进行比较,以查看其中是否有一条是使用完全相同的指令生成的。

否则,缓存将无效举个例子,我们可以将简单、经常被依赖到的基本软件如 git、make等不常变化却常用的指令放到前面执行,这样镜像构建的过程层就能直接利用前面生成的缓存,而不是重复的下载软件,即浪费带宽又消耗时间。

这里我们对两种写法进行对比,首先初始化相关目录与文件:mkdir myproject && cd myproject echo “hello” > hello第一种 dockerfile 的写法为先 COPY 文件,再进行 RUN 安装软件操作。

FROM ubuntu:18.04 COPY /hello / RUN apt-get update –fix-missing && apt-get install -y \ aufs-tools \ automake \ build-essential \ curl \ dpkg-sig \ libcap-dev \ libsqlite3-dev \ mercurial \ reprepro \ ruby1.9.1 \ && rm -rf /var/lib/apt/lists/*

通过对 time docker build -t cache_test -f Dockerfile . 进行镜像构建,构建成功再多次执行可以发现后续构建直接命中缓存生成镜像time docker build -t cache_test -f Dockerfile . [+] Building 59.8s (8/8) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 35B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/ubuntu:18.04 0.0s => [internal] load build context 0.0s => => transferring context: 26B 0.0s => [1/3] FROM docker.io/library/ubuntu:18.04 0.0s => CACHED [2/3] COPY /hello / 0.0s => [3/3] RUN apt-get update && apt-get install -y aufs-tools automake build-essential curl dpkg-sig && rm -rf /var/lib/apt/lists/* 58.3s => exporting to image 1.3s => => exporting layers 1.3s => => writing image sha256:5922b062e65455c75a74c94273ab6cb855f3730c6e458ef911b8ba2ddd1ede18 0.0s => => naming to docker.io/library/cache_test 0.0s docker build -t cache_test -f Dockerfile . 0.33s user 0.31s system 1% cpu 1:00.37 total time docker build -t cache_test -f Dockerfile . docker build -t cache_test -f Dockerfile . 0.12s user 0.08s system 34% cpu 0.558 total。

修改 hello 文件的内容, echo “world” >> hello ,再次执行 time docker build -t cache_test -f Dockerfile . , 此时镜像构建的耗时又回到了1分钟左右。

第二种写法的 dockerfile 如下,我们将基本不变的基础软件安装放到上面,将可能变化的 hello 文件放到下面FROM ubuntu:18.04 RUN apt-get update && apt-get install -y \ aufs-tools \ automake \ build-essential \ curl \ dpkg-sig \ && rm -rf /var/lib/apt/lists/* COPY /hello /。

通过对 time docker build -t cache_test -f Dockerfile . 进行镜像构建,第一次构建耗时在1分钟左右(构建成功再多次执行一样命中缓存生成镜像)修改 hello 文件的内容, date >> hello ,再次执行 time docker build -t cache_test -f Dockerfile . , 此时镜像构建的耗时在1s内,即成功复用第二层构建过的缓存层。

使用多阶段来分离 build 和 runtime这里举一个 golang 的例子,首先将 example 代码库 https://github.com/golang/example clone 到本地,添加一个 dockerfile 进行构建应用镜像。

FROM golang:1.17.6 ADD . /go/src/github.com/golang/example WORKDIR /go/src/github.com/golang/example RUN go build -o /go/src/github.com/golang/example/hello /go/src/github.com/golang/example/hello/hello.go ENTRYPOINT [“/go/src/github.com/golang/example/hello”]

我们可以看到镜像的大小是 943 MB,程序正常输出 Hello, Go examples!

接着让我们使用多阶段构建和尽量小的 runtime 来优化以上的过程FROM golang:1.17.6 AS BUILDER ADD . /go/src/github.com/golang/example RUN go build -o /go/src/github.com/golang/example/hello /go/src/github.com/golang/example/hello/hello.go FROM golang:1.17.6-alpine WORKDIR /go/src/github.com/golang/example COPY –from=BUILDER /go/src/github.com/golang/example/hello /go/src/github.com/golang/example/hello ENTRYPOINT [“/go/src/github.com/golang/example/hello”]。

可以看到目前的镜像大小只有 317 MB。通过多阶段构建将应用构建和运行时依赖进行分离,只有将 runtime 依赖的软件会最终打到应用镜像中去。

原文链接本文为阿里云原创内容,未经允许不得转载。

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
相关推荐
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容