面向非潮人(即C和c++程序员)的DevOps和依赖管理——Johannes Nicolai, GitHub

短的、可重复的构建步骤对于任何持续交付管道都是必须的。在嵌入式和跨平台的C和c++世界中,声明式依赖管理仍然相对较新,并且是快速、可复制和安全发布的一大障碍。这个演讲展示了为什么包管理是一个好东西,以及柯南如何。io是一个开源的、平台独立的包管理器,管理C和c++库的依赖关系。演示展示了如何在几分钟内使用conan在许多平台上构建一个有趣的GitHub应用程序。io, AppVeyor, Travis CI和Jenkins。

视频记录

好的。欢迎大家参加面向非潮人的DevOps,也就是C和c++程序员的演讲。事实上,“非潮人”这部分可能需要一些解释。但在此之前,我先简单介绍一下我自己。

我是Johannes Nicolai[听不清00:00:22]GitHub。我在GitHub担任解决方案工程师。解决方案工程师,他们与社区和客户讨论各种与开发相关的挑战。这些是我负责的一些客户。我主要在讲德语的地区工作,我想你们很多人都知道,德国在工程方面有很好的传统。我们有很多汽车制造商,很多客户做咖啡机,MOT COT设备。因此,许多客户实际上在嵌入式领域做了一些事情,这并不令人惊讶。在C和c++空间中做一些事情。

我从德国坐飞机来参加这个会议,在法兰克福机场,我看到了这个广告,“为什么要在德国投资?”问问我们的机器吧。”我真的觉得这是一个非常愚蠢的广告。不应该问机器,它们不会说话。你应该问问为这些机器写代码的人。

我问过我的许多客户,也问过这里没有提到的其他客户,当涉及到DevOps或持续交付时,他们在使用这些机器时遇到的挑战。这张幻灯片上是这些人的匿名言论。

我本可以提到,你们中的一些人可能会同情这样的说法。通常从持续集成的想法开始,这是任何DevOps或持续交付的前提。在嵌入式C和c++领域,构建需要72小时是很正常的,如果算上整个管道,甚至会更长。

你只有一个单独的提交和测试,这种想法通常是不存在的。只有一个构建,它包含了你的提交,也包含了数百个同事的提交。如果你再深入研究一下,为什么构建要花那么长时间,你就会很好地了解到所有这些包都对其他包有很多依赖关系。这些依赖关系是从零开始一遍又一遍地建立的。

你会问,为什么他们总是从零开始做东西?为什么是以前的样子?有这么多不同的排列和优化,所以我们不能做其他任何事情。然后你会问,如何将所有这些依赖项组合在一起?他们说,我们有定制的构建脚本。

我说,等一下。你有自定义构建脚本,你如何找出,例如,如果你在开放SSL中有安全漏洞,心脏出血攻击,哪些组件受到影响?然后我得到了一个非常有趣的答案,那就是,嗯,我们雇佣实习生,这些实习生,他们浏览这些填写好的脚本,然后找出什么受到了影响。

我不知道我是否应该请你们举手这些名言听起来熟悉还是太尴尬了。好的。有些人很勇敢。谢谢你。实际上,我也问过我在美国的同事,这是否只是德国的问题,答案是否定的。在美国,许多汽车制造商和其他行业也面临着类似的挑战。

现在轮到非潮人部分了。有一个明确的解决方案可以解决这个问题,那就是声明式包管理。你不需要一直从头开始构建东西,你只需要用一种声明的方式来说明你所依赖的包。然后得到二进制。你不能直接把它们放到Git中。你有语义版本控制和所有这些东西。任何使用Java或Java Script的人都确切地知道我在说什么,但当我最初问这是否可以在C和c++领域中实现时,我得到了这样的回答:“包管理器,这难道不是那些时髦语言的东西吗?”Java Script, Java,因为它们很简单。它们只有一个二进制文件,对应一个版本。

在C和c++中,这取决于操作系统。这取决于编译器flex。这取决于架构。32位,64位,静态链接,非静态链接,所有这些都很重要。即使库的版本相同,也会有不同的二进制文件。

所以,谢谢,但不,谢谢包管理器,这是一个东西,我们自己做了一些东西,但没有我们可以使用的东西,我想,好吧,我不是一个潮人。我认为您可以使用包管理器。[听不清00:04:57]那里有包管理器,比如柯南。所以接受挑战。

这并不是关于使用这些技术或指向这些技术,而是关于你如何说服你的同事,你如何说服你的老板,为C和c++采用一个包管理器,这真的是值得的,而不仅仅是蛋糕上的樱桃?

从这次演讲的议程来看,我将用前三分之一的时间讨论辩论。你如何说服你的老板,你如何说服你的同事,包装管理器是一个好东西,而不仅仅是一个好看的东西。这些论点在我一开始介绍的公司中是有效的。

第二部分是柯南IO。显然,这是一个伟大的包管理器,它是基于C和c++所需的任何东西指定的。我想,我在做一个有趣的例子,不是一个hello world的例子,而是一个接近GitHub和我们的核心理念的例子。

最后但并非最不重要的是,那些C和c++的非时髦人士有一个观点,即您需要所有这些不同的排列,而不仅仅是一个版本的工件。因此,我将向您展示如何使用ESHA管道和一些其他CI系统轻松构建这样的东西。让我们直接开始吧。

你如何说服那些只会说“这只是为潮人准备的,我们并不真的需要它”的人。我会说现在的任何公司都是敏捷的,我在今天的主题演讲中从Nicole那里学到了一个很好的术语,完全敏捷。基本上就是假装你很敏捷。

如果你真的想说到做到,你可以让你的经理或同事看看敏捷宣言。敏捷宣言在第一句话中说:“我们的最高优先级是通过早期和持续交付有价值的软件来满足客户。”

这有两个重要方面。第一个是持续交付。你总是想要运送薄片产品。你不希望风险累积在几个月,甚至几年。你可以为你的公司做一个试金石,你是否真的敏捷,你可以问问你是否还在庆祝你的发布。如果你确切地知道你要发布哪个版本,如果你要庆祝它们,或者如果你也不得不阻止你的假期,如果有一个特定的月份没有人能去度假,因为这个风险积累得太多了,你不知道会发生什么,你不是真正的敏捷,你没有做任何远离持续交付的事情。

发布必须是无聊的,它们必须是一个无关紧要的事件,没有什么值得真正庆祝的。这并不意味着您总是必须向客户交付这些版本,但它至少必须是可发布的。这只有通过包管理和适当的依赖项管理才能实现,稍后您将看到这一点。

另一方面是关于这种价值。这不是什么秘密,即使顾客说他们想要什么,在他们尝试的那一刻,这并不一定是他们需要的。我认识一些汽车行业的客户,他们花了两年的时间来开发他们的导航系统的某些功能,当这个功能推出时,客户只是说,“我手机上的谷歌地图已经有了这个功能,它工作起来要好得多。实际上,我唯一需要的是让我的谷歌地图在你的抬头显示。给我那种功能。”

所以基本上两年的开发努力都白费了。如果你有能力更频繁地向更好的客户群体发货,那么你也有能力纠正方向,因为你的客户并不比你更了解他们想要什么。他们只有用了才知道。如果你想做B级测试和课程修正,那么包管理就很重要。

为什么它如此重要?如果你看看DevOps圣经中的这七个原则,我想没有一个DevOps演讲不引用那本书是不完整的。其中一个方面是,只构建一次二进制文件。

只构建一次二进制文件是什么意思?好吧,它指的是你管道中的各个阶段。其思想是在第一阶段编译所需的所有内容,然后将其作为不可变工件使用,以便您可以确保当您对该工件运行测试时,这与您在生产中使用的工件相同。

我曾见过一些汽车制造商在真正的汽车上出现问题,因为他们在汽车的最新阶段使用了不同的编译器,所有的性能测试都是针对不同的编译器版本进行的。只有当您拥有不可变的构建时,才有可能避免这种情况。如果你只构建了一个东西,然后就可以确信你所做的测试实际上与你要在生产中推出的东西相悖。

第二个方面是时间。这是一个标准的部署管道。现在想象一下,你从头开始在每一个绿点上编译所有东西。那么很容易解释为什么它需要72小时,甚至更长的时间。而这整个反馈循环,你可以纠正的整个想法,你可以单独测试的整个想法,发布不是一个事件,你可以忘记它。对于持续交付,您需要一些适当的工件管理,否则您将永远等待直到看到结果。

有些人可能会说,这有什么大不了的?不变性。我明白了。我编译所有内容,然后将其检入Git。这通常是汽车或OEM客户第一次开始采用Git时给我们的电话,“我们的服务器崩溃了。”原因是,假设这是开放SSL库。它是100兆字节,它更小,但这是一个例子。然后你有了这个Heartbleed漏洞,你修复了它,然后你有了另一个功能修复。

那么Git存储库中就已经有301兆字节了。只是这个库的一个版本。Git的设计是,如果你克隆,你会得到你的二进制文件的每一个版本。你可以为任何[听不清00:11:31]或任何客户或开发人员提供300兆字节。现在想象一下你没有三个版本,而是100个版本,你有不同的构建排列。

然后,我们看到客户的每一个克隆都是800g的。这不仅对您的网络有问题,对Git服务器的主存也有问题。所以不管你是GitHub还是其他服务器,它们都会崩溃,就这样。如果您启用或允许开发人员从Perforce或Subversion迁移而保留二进制文件,那么您将遭受无尽的痛苦。

这就是依赖管理是一件好事的原因。我想每个人,甚至是目前没有使用标准包管理器的人都知道这在理论上是一件好事,但许多人不知道这些额外的功能。这些现代的包管理器,不仅仅是下载一些二进制文件。他们确切地知道所有不同的构建系统和操作系统。

假设有一个新的GCC版本,或者有一个新的Mac OS编译器。然后通常这些公司开始构建和修改他们的自定义构建脚本,通常甚至是多次,所以检测新的编译器版本,或者你只是使用像Conan IO这样的东西,社区会注意所有这些都是透明处理的。

那些现代的包管理器,他们也知道你所依赖的软件的许可证。你甚至可以阻止某些许可,所以你不需要让这个实习生弄清楚是否通过某种魔法包含预处理器宏,你包含了一个GPL组件。你把这个作为报告拿出来。

安全警报也是如此。如果你愿意,你也可以定义自己的安全警报或功能缺陷,并立即看到所有组件受到影响,这样你的实习生就可以做更重要的事情,而不仅仅是容易出错的调试。

然后,如果你有任何一种SDK,假设你在管理一个物联网集群,那么数百甚至数百万的设备,无论是汽车还是烤面包机,都必须下载你的软件。您不希望从单个服务器上下载它。这是不可能的。你想自动地把它分布到全球。你希望有适当的访问出口限制。你想要有报告。你需要签名。所有这些都包含在现代包管理器中。如果您只是使用最先进的技术,您不必从头开始构建这样的东西。

最后但并非最不重要的,这可能是我最喜欢的功能。我曾见过这样的情况,你基本上是在使用一个新版本的库,它就崩溃了,你只是在启动时得到一个[听不清00:14:25],你不知道为什么。源代码看起来和旧版本完全一样。然后,它实际上是关于使用不同的编译器选项优化级别。

如果你有这个元数据,什么编译器版本,什么优化已经被用于生成你的每个库,你可以在版本之间比较它们,使故障排除更容易。

这些包管理器也有一个内置的推广概念,这意味着你可以,例如,在探索阶段给你的开发人员自由使用任何类型的C和c++库,然后你说,现在它进入实际生产,现在我想把它锁定在10个包,我真的很信任。

所有这些都是在包管理器的基本功能之上的下一张幻灯片我就不在这里讲了。更重要的是,如果你不知道这些日期,如果你想做一个内部演示,你的公司,你只有材料。就像为什么子模块不是银弹一样。比如LFS不能代替适当的依赖管理。

简而言之,他们有一定的功能,但他们没有任何像安全警报、语义版本控制、依赖和检测或许可证检测这样的好东西,而且他们不知道交易,Git LFS也不知道与C或c++相关的不同维度。

让我们转换一下话题,再谈一谈Conan,它是一个专门为C和c++程序员的需求而设计的包管理器。

一些推动这个项目的人就在这个房间里,他们知道的比我多。他们也在楼上柯南的展览现场。所以如果你还有问题就问他们。他们基于几十年来在C和c++领域的大量咨询经验开发了这个程序。他们知道他们的东西,他们确保它可以在多个操作系统上运行,因为它是用Python编写的。它是跨平台的,包括交叉编译。它知道大量不同的构建系统、操作系统以及它们之间的所有细节。这不仅仅是一个爱好项目。事实上,JFrog收购了柯南的商业部分,并为其提供支持。

Conan的工作方式与其他包管理器没有什么不同,因为它只是计算出依赖关系图。它也有冲突解决,所以在这张图中,你可以看到开放SSL和boost,它们都依赖于[Zlive 00:17:11],柯南协商,他们得到了一个其他消费者都能接受的版本,这样你就不会陷入任何有趣的链接问题。

Conan有一个免费的开源参考实现,它在那里提供包,但它也被内置到JFrog Artifactory和JFrog Bintray中,并且对几乎所有单独的CI CD解决方案都有DSL支持。当我介绍Azure管道的现场演示时,您将看到更多内容。

柯南的工作方式是,无论何时你要求一个库,比如这台Mac的lip boost,然后是64位,然后它就会使用所有不同的维度,创建一个校验和,并检查柯南服务器中是否已经有二进制。如果是,它会直接下载。如果没有,柯南也有所谓的构建食谱的概念。因此,如果它们还不在上游存储库中,也可以在本地构建。这不仅仅是二进制文件,这也是一个如何产生这些二进制文件的配方。

我说,这就是足够的理论。我们来看一个例子。当我在GitHub工作时,我认为我不应该只做简单的hello world项目,这更接近于GitHub。在GitHub,你在哪个团队工作并不重要,我们一直遵循12条共同原则。这有点像地面上的命令,只是做正确的事情。我们称之为GitHub的禅宗。关于GitHub上的这些禅宗,我可以轻松地花上整整一个小时,但不要担心我不会这么做。

我想提的唯一一件事是,你也可以得到GitHub的禅宗,如果你调用我们的API, APIGotHub/Octocat,你总是在这个ASCII艺术中得到一个随机的GitHub禅宗。只有快了才算完全出货。

所以现在的想法是让我们构建一个Conan应用程序,使用[lip co 00:00:19:19], C库,来获取GitHub的随机Zen,但只有当你在一个好的存储库中时才会这样做。如果工作目录是一个好的存储库,那么就给我们这个漂亮的Octocat ASCII艺术,否则就不要。

该项目被称为GitHub的禅宗,并在GitHub上。在存储库中,它准确地描述了它是如何工作的以及输出应该是什么。您还可以看到这个依赖关系图,源代码非常简单。这只是包括禅宗的GitHub,自定义库和唇部套件。

如果我们在一个好的存储库中,我们在这个If语句中计算出来,然后调用Zen of GitHub main函数。否则,我们只是说您必须切换到每个存储库。

对Conan库的依赖我们在Conanfile.TXT中完成,在这里你可以看到我们依赖于GitHub库的自定义Zen和lip kit。还可以指定版本范围。尽管这在C和c++世界中是非常不寻常的。

现在让我们从柯南那里得到这些依赖关系。这些步骤在GitHub存储库中有文档。我首先克隆一些东西。看得见吗?是的,我想是的。我只是把它放大一点。我正在克隆这个存储库,并创建一个构建目录。这是我做的第一件事。然后我做的第二件事是我想在lip kit中安装这些依赖到lips和GitHub。柯南安装,这实际上会失败。

它失败的原因是while lip kit是一个公共项目,一个公共存储库,它在他们的标准Conan Central Artifactory repository中,这不是GitHub中的自定义lips的情况。我刚刚为自己构建了这个,所以我必须将另一个柯南遥控器添加到我的Bintray中,在那里我有自定义库的包配方。在公司内部,你通常只使用自定义遥控器来确保控制的不变性。

现在我编辑这个远程,现在我试图再次安装依赖项,这一次它应该实际工作。它正在下载这些依赖项。如果我只是想获得配方,然后自己在本地构建一些东西,我也可以使用这个minusminusbuild参数,然后它会在GitHub中为我本地构建嘴唇。

现在我已经安装了所有东西,我必须以某种方式将它包含在我的make文件中。为此,我为发布版本生成了一个代码段,然后将此代码段包含在我的主C make文件中。我基本上有一个宏,它正在初始化柯南,然后它会填充柯南生命环境变量,然后我只是把它作为链接目标添加到它。

最后但并非最不重要的是,我将构建整个系统并进行测试。我进入bin目录并调用GitHub的Zen,这只是告诉我,我没有在一个好的存储库中。因此,让我们创建一个好的存储库,并重复,然后开始。保持逻辑上的卓越。

我做的所有步骤,都是幻灯片的一部分。就像这个依赖关系图一样,你可以看到也有一些间接依赖关系被画了进来。您将看到如何添加自定义柯南遥控器。如何克隆存储库。如何安装依赖项。语义版本控制如何工作。如何从头开始构建某些库。如果您只对构建配方感兴趣,那么您可以如何更改这些维度,以及您想要静态地还是动态地链接它,以及如何调用帮助。

说得好。当你需要柯南的任何帮助时,它是一个有大量文档的开源社区。所以如果你去GitHub上查看文档,你会找到你需要的一切。

现在我们已经看到了如何使用柯南依赖项。那么如何创建一个可以被消费的库呢?有人称之为Conanify库。来个柯南版的。我只是基于GitHub的禅宗,嘴唇和GitHub库来展示这个,它基本上是一个漂亮的唇卷包装。如果你查看lip和GitHub的源代码,它基本上只是使用lip curl调用这个API GitHub.com/Octocatendpoint。

但它确实有一个包含构建配方的清单。只要找到它。我在GitHub上展示得最好。这基本上就是清单,它说,我是这个库,这是我的版本,这是我的许可证,这是这些选项的位置。我支持静态和动态链接选项。我希望包含源代码,这是我所依赖的依赖项。

我不需要从头开始想出来。如果你安装了Conan,有一个很好的命令叫做Conannew,它会自动为你创建一个脚手架,这样你的库的清单就会自动创建。更棒的是,有一个名为Bin Crafters的社区,在那里你可以找到数百个已经以Conanified方式存在的标准库。如果你想知道如何Conanify lip open SSL,不要自己去做,也不要自己去做lip Q、lip boost或open SSL。这些都已经在GitHub上了。只要搜索柯南和库的名称,您可能就会找到一个很好的示例,它是如何工作的。

让我们在本地也建一个柯南库。在本例中,我只是将lips和GitHub、存储库CD克隆到其中,运行测试用例,并构建整个解决方案。如果成功了,我们将会看到另一只可爱的章鱼猫。这是可行的。最后一点是将它上传到我们的远程,以便其他人也可以使用这个库。

现在可能会说,好吧,约翰内斯,这有什么大不了的?你只需要为你的目标架构构建一个版本的库,也就是Mac,这通常不是一个嵌入的东西,他不会在生产中使用。我如何为我的公司所需的所有操作系统和排列进行构建?我怎样才能涵盖所有这些32位、64位、Mac、Windows、GCC、5.4、5.3等等的排列呢?

我喜欢基于Azure管道来演示所有这些东西。这一切又要从GitHub开始。我会进入我为这个热身版本创建的pool request,这个lips和GitHub的热身版本。

你能看到的是构建定义,我如何提出这样一个管道是源代码本身的一部分。知道声明式管道的请举手?可能是70%到80%。这在C和c++之外是相当标准的。至少从我的经验来看,在C和c++社区中,这仍然是不寻常的。这个想法是我们在主题演讲中听到的。

GitHub,你想安全地试验你的构建管道。你是否经常会对发行经理说:“我有一个好主意,如果我们使用这个和这个技巧,我们可以将构建时间缩短一半。”他们说:“好主意,但我们两周后就要发布了。”两周后,你就有别的事要做了。因此,如果您的源代码中有构建管道版本,您可以安全地在拉请求中进行实验。

你可以看到我的第一个实验实际上失败了,但是在第二次提交中,在这个拉请求中,你可以看到所有的测试都成功地运行了。所以对待你的构建管道S代码。如果我们研究一下Azure的声明性管道,你会发现我们正在做一个度量构建。所以我们在Linux、Mac和Windows上并行开发。您还可以指定要在其上测试的所有不同体系结构。最后你基本上只是调用构建脚本,在这里,第58行完成,我想给你们更多关于这个构建脚本的想法因为这是所有这些排列的来源。

您的CI CD系统中的指标为您提供了操作系统,但是要获得所有这些静态链接、共享链接或编译器版本的不同排列,这是作为构建之前的一部分完成的,作为所谓的Conan多打包器的一部分,但是您指定了您想要迭代的所有维度。

如果这太抽象,让我们检查一下Azure管道对这个拉请求做了什么。如果我们去到GitHub的检查选项卡,看看这个实际上是如何构建的?然后我们看到这个度规点。我们看到这三种不同的音符,在这些步骤中,建筑,解决方案,然后你可以发现它实际上跨越了许多不同的排列,32位,64位,静态链接,动态链接,我猜在你的公司你可能会有许多自定义维度,你可以添加到柯南,然后你只是说在这些维度中通过哪个。然后还可以删除某些子集。

如果你说,例如,对于Windows,我不关心32位二进制文件,你也可以从管道中删除它们,然后重新设置。这是一个使用Azure管道实现的示例,但您也可以轻松地使用任何其他CI系统实现此操作。

例如,柯南社区本身就在使用Jenkins,他们有一个实时的CI[听不清00:30:49]柯南,CI JFrog信息,在那里你也可以锁定为只读,你可以学习他们如何,例如,构建包。

在这里,这是groovy DSL,它们可以在多个操作系统上运行,甚至在Jenkins文件中还内置了一些DSL,您可以轻松地连接到Artifactory存储库并上传包。如果你进入我的GitHub存储库嘴唇,那么GitHub,你还会找到Advair和Travis CI的例子。

我希望您已经了解了DevOps或非潮人管道的样子。首先,您现在可以安全地试验您的构建定义了。您不必祈求构建管理人员为每个人更改构建定义。你只需要单独试验你的pull请求,如果它运行良好,那么它就可以被合并,每个人都能从中受益。

这个拉请求将触发你的CI系统,越来越依赖你的图书馆,你指定JFrog Artifactory然后这柯南多包装机在操作系统和所有的排列构建flex来生成所有你需要的包,然后上传回Artifactory如果运维同意客户想发布他们居住生活,他们可以通过工件推广JFrog Bintray,或者JFrog发行版,这样你也可以从CDN下载它们,它们不仅仅影响你的单个服务器,而是从地球上最近的服务器为你的客户提供服务。

你为什么要做这些?因为你想要回你的周末。你不想把发布当作生活中的大事,这样你就不能去度假了,你会祈祷有这么多的风险积累。你也想要开发一些你的客户真正想要的东西,你能做到这一点的唯一方法是你的客户告诉你,有时他们指定的不是他们真正需要的,你不想让你的实习生浪费在计算影响和所有这些事情上。你想让他们做有成效的事情。你不会想要浪费任何人的时间来为Mac Yosemite增加你自己的支持,甚至可能是你组织中三个不同部分的三个不同的人。你只要依靠开源社区站在巨人的肩膀上就可以了。

重要的是,首先不是关于工具。我已经向你展示了如何使用GitHub、Conan、Artifactory和Azure管道做到这一点,但如果你想说服你的团队谈论为什么,而不是如何做,这是非常重要的。

他们首先要让你明白为什么要这么做。为什么你想要回周末。为什么你不应该庆祝你的释放。为什么你需要不变性。

我希望这次演讲能给你们一些想法,让你们知道如何实现这个目标,不仅仅是如何实现,还有为什么要实现。所以请回到你的团队,告诉他们这件事,你会发现,即使你不认为自己是潮人,我当然不认为自己是潮人,加入持续交付和DevOp消费的行列是有意义的。谢谢你的时间。

试试JFrog免费!