专栏策动|雅纯
意愿编辑|冯朝凯、橙蜂
之前咱们举了《集装箱扭转世界》(作者:马克. 莱文森)中的一个例子,书中提到上世纪五六十年代,集装箱的应用,使得整体货运老本升高了 95%,大部分的码头工人都面临着就业。
这件事件看起来很简略,但却给经济全球化带来了十分大的影响。前面美国企业的订单能够下到中国、以及中国成为“世界工厂”,都与之有很大的关系。集装箱的背地是标准化和基于统一标准的产业链,这里有两点比拟重要的,一个是标准化,另外一个是不可变。
那么,在软件开发的过程中,咱们怎样才能享受产业生态的红利,实现软件交付过程的标准化呢?软件交付当中的集装箱应该是什么样的?
如何保障软件交付过程的标准化
近十几年,软件交付状态产生了很大的变动,从最开始买物理机、建机房到虚拟机再到当初的容器。这两头为什么会产生这样的变动呢?
容器自身的底层技术是 namespace 和 cgroup,然而这两个货色在十几二十年前就呈现了。最早利用这些技术的是对资源利用率和隔离有明确诉求的云厂商,比如说阿里云不心愿跑在机器上不同用户的货色相互串,最好的方法就是能限度每个用户的资源,如 CPU、内存等。有了这个诉求,就会用 LXC 等形式去隔离,去限度资源。然而这还是没有产生容器。为什么呢?问题是各个云厂商只能在本人外部做,然而不能对外散发。所以 Docker 的平凡之处并不是在底层做了多大地翻新,而是提供了一个能够对外散发的容器镜像。
容器镜像是一个散发的模式,咱们能够把容器镜像分发给他人,或者是让他人继承咱们的镜像。同时 Docker 又提供了 Dockerfile。Dockerfile 容许咱们通过一个文件的模式去形容镜像。一旦可能定义镜像就能够合作了。有了这样的能力当前,容器就很快被大家所承受了。所以容器的承受看起来如同是技术倒退的过程,其实是随着云原生、云市场的倒退必然带来的后果。
咱们很多人认为的“集装箱”就是容器,这个容器很多时候咱们都认为是 docker 容器。在 K8s 外面反对很多个容器运行,大部分的状况都是用 docker 容器。docker 容器的劣势就是方才说的两点:镜像和 Dockerfile。这两点使得 docker 镜像能够像集装箱一样做散发。
此外,容器还提供了很好的资源隔离,能够在比拟小的粒度上进行隔离。虚拟机尽管也做了隔离,然而它的粒度比拟大。不仅如此,容器还提供了十分弹性的资源管理形式,这点比虚拟机和物理机都有十分大的改善。实质上它就是物理机上的一个过程,这是它和虚拟机的实质的差异。
理解了软件集装箱是什么后,而后咱们再来理解下容器镜像的组成。
如上图所示,这张图十分形象地展现了容器镜像的内部结构。当咱们本人执行 dockerbuild 构建镜像的时候,你会发现它进去的日志有很多 hash 值,一层一层的。实际上它是由很多层组成的,咱们通过 LXC 或者其余的技术,把容器的过程创立进去,这个过程通过 namespace 和 cqroup 做了资源隔离和限度。容器镜像都有一个 BaseImage,咱们晓得运行一个程序,对操作系统的环境是要求的,比方依赖的 library 等。这个程序如果轻易在一台物理机和虚拟机部署,会随着机器的环境不同而不同,有可能导致危险。所以容器镜像给了一个根本镜像,把这个货色放外面了。再往上是 Addemacs 和 Addapache,这两层咱们会在 Dockerfile 中去写。而后最下面的是 Writable,就是咱们在容器 Container 运行的时候真正能够去写的货色。
那么容器镜像的特点是什么呢?它是分层的,每一层都是能够复用的,咱们在某个机器上有很多个容器,如果 Base 镜像一样,只有下一次就行了。能够看到镜像的大小是所有的层堆起来的,堆的货色越少,这个镜像就会越小。容器镜像有一个最小的镜像叫 scratch,就是一个最原始的根底镜像。这外面简直什么都没有,基于它构建一个十分十分小的容器的话,可能就是几兆的大小。然而如果你基于 CentOS 根底镜像可能就是上 G 的大小。
容器镜像有一个十分重要的概念叫“One process per container”(容器生命周期 = 过程生命周期)。咱们能够认为容器就是 K8s 上的一个过程,如果把 K8s 比作操作系统,那么容器就是它下面运行的一个过程。过程的生命周期是能够被治理的。尽管容器有这么多的长处,但理论在用的时候也会遇到很多的问题。
上面咱们聊一聊容器镜像的一些常见的问题和倡议。
容器镜像常见问题及实际倡议
容器镜像常见的问题:
- 把所有的货色都装到一个容器外面,把容器当虚拟机来用。
- 把 ENTRYPOINT 设置为 systemd:systemd 治理的过程运行的后果和状态和的容器状态是不统一的,有可能外面的过程曾经僵死了,或者 Crash 了,然而 systemd 还活着,从内部看起来这个容器没问题。
- 私有化部署的时候带一堆导出的镜像 tar 包。tar 包是不分层的,它不晓得外面是有很多层。
- 每次把根底镜像下发到整个集群,导致网络变得特地拥挤
咱们的实际倡议是:
- 尽量采纳轻量的根底镜像和确定的镜像版本。
- 通过分层来复用镜像内容,防止反复拉取。
- 防止采纳 systemd,包含 supervisord 和相似这样的 daemon 治理服务来做 ENTRYPOINT。
- 采纳本地的 dockerregistry 等以层为粒度来离线拷贝镜像。
- 防止同时要做大量的 pull,可采纳 P2P 的形式(如应用 dragonfly)晋升镜像散发效率。
容器镜像能够实现软件交付过程的标准化。标准化是伎俩不是目标,标准化是帮忙咱们更高效的复用的技术。
回到软件交付的终态,咱们的目标是心愿提供一个稳固可预期的零碎。
而达成这个指标的前提是,要有确定的运行环境和软件制品 。确定的环境是指代码(及其依赖)、构建环境、构建脚本与预期统一的产出软件制品,这一点如何做到咱们前面再作分享。 咱们先看如何保障软件制品的一致性。
如何保障软件制品的一致性
要保障软件制品的一致性,软件制品应该有确定的格局、惟一的版本、可能追溯到源码、可能追溯到生产和生产过程,这样能力使继续交付更好地服务于企业的制品治理与开发。
在制品构建过程中,常常会遇到一些问题。例如利用的代码库里没有 Makefile,package.json,go.mod 而没法确定依赖,或者制品能构建胜利但缺失几个依赖,又或是在本人的开发环境运行失常而在生产环境呈现了开发环境没有的 bug。导致这些问题呈现的起因是因为构建自身是可变的,当你构建可变时,就会带来一系列的问题。为此,咱们须要通过不可变构建来使制品与预期统一。
要实现不可变构建,咱们须要保障有:
- 雷同的代码
- 雷同的构建环境
- 雷同的构建脚本
雷同的代码
例如程序员开发时,不在依赖形容文件(如 go.mod,package-lock.json,pom.xml,requirements.txt 等)中指定依赖的版本,则会默认应用最新的版本作为依赖,这样产出的制品会随着依赖的更新而不能保持一致,这将带来齐全不在预期内的危险。
雷同的构建环境
对于构建环境来说,Dockerfile 能够用来在容器平台下形容环境,通过 Dockerfile 咱们能为制品应用统一的环境。很多时候咱们并不需要在运行中应用构建环境的很多依赖,而构建镜像的体积往往比拟惊人,这个时候咱们就须要将构建环境与运行环境离开,以失去尽可能轻量的镜像制品。
雷同的构建脚本
对应的,应用雷同的,与代码实现无关的构建脚本也是十分重要的,在 Dockerfile 的环境中必须指定确定的环境依赖版本。
只有在同一份代码(及同一个依赖)、同样构建环境的形容、和同样构建脚本的环境下,所产生的软件制品才是雷同的。这里强调的是说所有的货色都要保障一致性,如果说三者是一样的话,那产生进去的制品也是一样的,即便构建工夫不同,产出的制品也是雷同的。
做好不可变基础设施,首先要标准化最终交付制品的状态,并且明确此交付状态的运维治理形式。而要保障不可变,那首先要做好不可变的构建,而后能力有统一的软件制品。
NOTE:构建准确性,永远比构建更快重要 。制品的构建信息不精确, 导致构建制品不统一、版本不可控,所有后续的工作都是节约。
如何晋升构建效率
在构建这块,一个须要关注的点的是如何晋升构建效率。咱们先看一个简略的计算问题:
这是一个十分大的数据,也是十分大的损耗。很多时候一个我的项目的工程效率太低的起因就是因为构建太慢。构建耗时过长使得制品迭代十分慢,性能更新和 bug 修复也会受到影响。
那咱们如何晋升构建的效率呢?上面是咱们的一些实际倡议:
1 个根本准则: 保障构建的准确性,构建的准确性永远优于构建的效率。只有在保障准确性的前提下晋升效率才有意义。
5 点倡议:
- 利用瘦身:查看利用的依赖状况,利用包体积是否过大,依赖项是否过多,是否去除不必要的依赖,是否构建更小的镜像。
- 分层构建:底层的货色先构建进去当前被下层所复用,而后就能够做增量式的了。
- 构建缓存:构建过程中拉取依赖是很耗时的,要防止反复拉取。
- 网络优化:次要是保障代码、构建机器和制品库之间的低网络延时。代码和构建机器是否是在同一个低时延链路中。例如代码在 Github 上而应用云效构建,此时的延时绝对于内网会高出许多。
- 仓库镜像:仓库镜像能够极大地缩小拉取依赖项的工夫。在国内的网络环境下,如果从源仓库获取依赖,可能延时会十分长,这时能够应用镜像网络升高延时。例如 nodejs 开发者常应用淘宝的 npm 镜像源,而 Python 开发者应用清华的镜像源。对于企业来说也能够构建本人的镜像仓库以晋升可靠性与升高延时。云效也应用了镜像仓库,来缩小拉取的工夫。
(小编举荐:云效流水线 Flow 是一款云原生时代的流水线工具,通过容器技术让企业解脱对虚拟机构建环境的依赖。您甚至能够依据您的应用需要,在同一条流水线上应用不同的构建环境。此外,云效流水线 Flow 还提供了各种语言的容器环境,满足不同的构建应用场景。点击文末浏览原文,理解详情)
总结
本篇文章,咱们从软件交付的终态登程,提出了不可变构建的概念。心愿通过:雷同的源码 + 雷同的环境 + 雷同的构建脚本 => 带来统一的软件制品。而这些货色都是保留在源代码里的,所以源代码的治理十分重要。
下篇文章,咱们将分享 如何对源代码进行无效治理。
浏览上篇:做到这 4 点,才是真正的继续交付
点击下方链接立刻体验云效继续交付流水线 Flow。
https://www.aliyun.com/product/yunxiao/flow?channel=yy_rccb_36