简介:本篇文章,我们从软件交付的终态出发,提出了不可变构建的概念。在软件开发的过程中,我们怎样才能享受产业生态的红利,实现软件交付过程的标准化呢?软件交付当中的集装箱应该是什么样的?

专栏策划|雅纯

志愿编辑|冯朝凯、橙蜂

之前我们举了《集装箱改变世界》(作者:马克.莱文森)中的一个例子,书中提到上世纪五六十年代,集装箱的使用,使得整体货运成本降低了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 还提供了各种语言的容器环境,满足不同的构建使用场景。点击文末阅读原文,了解详情)

总结

本篇文章,我们从软件交付的终态出发,提出了不可变构建的概念。希望通过:相同的源码+相同的环境+相同的构建脚本=>带来一致的软件制品。而这些东西都是保存在源代码里的,所以源代码的管理非常重要。

下篇文章,我们将分享如何对源代码进行有效管理

原文链接

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

构建制品不一致,后续工作都是白费 | 研发效能提升36计相关推荐

  1. 80%的软件环境管理问题,根因都在这里 | 研发效能提升36计

    专栏策划|雅纯 志愿编辑|jimmy.吕瑞星 软件交付的终态是提供稳定可预期的系统,要做到这一点,我们需要确保:一.软件制品的一致性:二.运行环境的一致性. 第3讲我们分享了如何保证软件制品的一致性, ...

  2. 构建制品不一致,后续工作都是白费

    专栏策划|雅纯 志愿编辑|冯朝凯.橙蜂 之前我们举了<集装箱改变世界>(作者:马克.莱文森)中的一个例子,书中提到上世纪五六十年代,集装箱的使用,使得整体货运成本降低了95%,大部分的码头 ...

  3. 2018-2021,60+篇阿里研发效能提升合集,都在这里了

    今年,研发效能特别火,不少企业的CTO都把研发效能提升作为部门的年度重点.但是,大家都希望提升研发效能,很多却不知道从何开始. 事实上,从2018年开始,云效已经在系统地向业界输出阿里的研发效能提升方 ...

  4. 为什么都开始搞研发效能?

    本文作者:茹炳晟,腾讯 TEG 工程效能专家 研发效能是目前互联网企业和传统软件企业都高度关注的领域,一线互联网企业希望通过"研发效能"实现持续的研发能力提升以应对日趋复杂的产品开 ...

  5. 关于研发效能提升的思考,每个P8以及以上都应该懂!

    研发效能提升是最近比较热门的一个话题,本人根据这几年的工作心得,做了一些思考总结,由于个人深度有限,暂且抛转引入. 三要素 任何生产力的提升都离不开这三个因素:人.流程和工具,少了其中任何一个因素都无 ...

  6. DevMind:构建效能提升的“导航仪”和“发动机”,实现从数据到价值的跃迁

    原博文: DevMind:构建效能提升的"导航仪"和"发动机",实现从数据到价值的跃迁 文档库-便宜云服务器引擎 本次实践总结,致力于探索效能度量领域的终极解决 ...

  7. 研发效能怎么分析?方法论、模型、误区都在这里了

    本文正文内容共计2615字,建议阅读时间:5-6分钟. 阅读本文你将收获: 1.为什么要做研发效能分析? 2.怎么分析研发效能,效能分析模型方法论是什么? 3.研发效能分析有哪些需要注意的误区 作者简 ...

  8. 阿里老兵深度雄文:不懂这些,你的复盘都是白费功夫!

    工作中,你真的会复盘吗? 分享 | 王建和(阿里铁军9年陈.阿里巴巴文化布道官) 来源 | 中信出版社 *摘自<复盘工作法> 许多企业都在做类似工作复盘的绩效管理,但往往流于形式,最终收效 ...

  9. 架构师的工作都干些什么?!想做架构师必看

    转载自  架构师的工作都干些什么?!想做架构师必看 之前有网友说想看架构师升级的文章,所以写了本文.先给本文中架构师做个定义:第一,能力上达到(似乎是废话),第二,公司肯承认,不仅能给架构师的头衔,更 ...

最新文章

  1. Servlet笔记1
  2. matlab学习记录之基本操作整理
  3. 2019.02.07 bzoj4316: 小C的独立集(仙人掌+树形dp)
  4. 18 个 jQuery Mobile 开发贴士和教程
  5. CodeForces - 1343D Constant Palindrome Sum(思维+差分数组)
  6. c#中invoke和beginvoke的区别
  7. 在任何无法理解的情况下,请编写脚本
  8. IndexNotReadyException: Please change caller according to com.intellij.openapi.project.IndexNotReady
  9. 信息系统状态过程图_操作系统中的增强型过程状态图
  10. 重写equals方法---java
  11. Java中的回调机制,这篇给你整的明明白白的
  12. opensuse ati 显卡驱动安装
  13. POJ 1006 同余方程组
  14. 使用 com.Lowagie.itext 导出html的内容到word
  15. ADS EM MODEL 问题
  16. 阿里云大学:百度贴吧自动签到实现
  17. Prometheus+k8s之告警通知
  18. 2021 Anomaly Detection (李宏毅
  19. 语义分割评价指标代码:混淆矩阵计算详解
  20. C++中关于数据小数点,取整的方法

热门文章

  1. 转 安卓解决 IDEA 下 struts.xml 中 extends=“struts-default“ 报红的问题
  2. fortify hp 价格_惠普推出“惠普Fortify软件安全中心套件”
  3. git ssh创建分支_将git项目导入GitHub的方法(附创建分支)
  4. 自学python 编程基础知识_python学习-基础知识-1
  5. split函数python 未定义_python split函数基本用法
  6. ktv管理系统_KTV经营管理的几个原则
  7. 【学习笔记】第五章——I/O(设备分类、控制方式、软件层次结构、假脱机、缓冲)
  8. 【学习笔记】数据链路层——流量控制:停止等待协议、后退N帧协议(GBN)、选择重传协议(SR)
  9. jnativecpp.dll一定要放到系统目录下吗_电脑硬盘有必要分区吗,分几个区最好?...
  10. ieee754浮点数转换工具_关于JS浮点数运算不精确的原因和解决方案