GCP-DevOps-高级教程-一-

87 阅读1小时+

GCP DevOps 高级教程(一)

原文:Pro DevOps with Google Cloud Platform

协议:CC BY-NC-SA 4.0

一、DevOps 简介

DevOps,DevOps,DevOps,在我们的职业生涯中,几乎没有一天不听到这句口头禅。原因很简单:通过采用 DevOps 实践,一个公司可以减少“上市时间”,即识别一个新软件或其他需求并将其放置到位所必需的时间。

在这一章中,我介绍了 DevOps 的一些优点,以及为了适应和促进公司最大限度地利用它们而必须做出的改变。

DevOps 是什么?

术语 DevOps 来源于两个词的组合:开发者运营DevOps 用来定义一场运动,这场运动源于减少公司开发和运营团队之间障碍的需要。DevOps 的目标是缩短上市时间。这意味着采用 DevOps 实践,以减少所需的时间,从识别新需求到为客户投入使用的时间。DevOps 之旅引入了持续集成和持续交付等实践,这些实践有助于缩短上市时间并生产更高质量的软件。

与新版本相关的最重大、可能也是最昂贵的失败发生在 2012 年,当时全球金融服务公司骑士资本集团(Knight Capital Group)因其服务器在发布过程中出现故障而损失了 4 . 4 亿美元;2013 年,高盛(Goldman Sachs)的一次升级失败意外发送了订单,导致据信高达数百万美元的损失。DevOps 允许一组实践,可以减少潜在的昂贵错误。

当我们想到 DevOps 时,我们必须想到一场改变整个公司合作方式的运动。目标是建立一套实践,用于减少公司不同部门之间的沟通障碍。为了取得成功,DevOps 应该在公司的最高层得到提升,并被每个部门所接受。

DevOps 背后的理念诞生于 2008 年,在加拿大多伦多的敏捷大会上。在这次会议上,Patrick Debois 发表了他的演讲“基础设施和运营”在这篇文章中,Debois 阐述了敏捷方法在构建基础设施中的应用。他提供了三个案例研究,并在最后提出了一种更好的交流方法和其他改进,允许开发人员获得更多的 IT 技能,这是确保更平稳的发布过程所必需的系统知识。

这场运动一直在发展,直到 2014 年《凤凰计划》一书的出版。在本书中,作者 Gene Kim、Kevin Behr 和 George Spafford 描述了如何使用 DevOps 创建成功的企业。凭借这本书,DevOps 运动的开始变得正式,之后,随着 DevOps 工程师的出现,一个新的 IT 人物诞生了。

DevOps 工程师

DevOps 工程师的角色是最近才出现的。DevOps 工程师代表了开发人员和运营经理之间的一种桥梁。在大多数情况下,DevOps 工程师承担的角色是运营经理和开发人员的混合,这很好,因为这些工程师必须具备建议和管理来自这两个领域的问题的必要知识。

在某些情况下,DevOps 工程师的职责与持续集成和交付有关。与该职位相关的另一项职责是基础设施管理,通常是基础设施即代码(IaC ),并帮助在整个公司实施最佳开发运维实践。

一些公司将 DevOps 工程师视为站点可靠性工程师(SRE)的演变,其主要职责是在生产中维护软件,并在出现问题时自动执行所有步骤来解决问题,并采取适当的步骤来确保维护系统所需任务的正常管理。开发运维工程师的角色各不相同,可能会因公司而异,但他们的核心都是确保实施公司发起的开发运维实践所需的变更。

采用 DevOps

在一家公司采用 DevOps 就像开始了一段新的旅程。在这个过程中,管理必须有效,变革才能成功。下面的列表强调了旅程的重要路标。

  • 经理必须推动变革。

  • 开发者必须对软件负责。

  • 操作人员必须被视为“一等公民”

  • 必须建立持续集成和持续交付策略。

  • 必须消除 IT 部门的障碍。

  • 发布过程必须自动化。

  • 敏捷实践必须在整个公司推广。

要达到预期的结果,需要管理层发起开发运维变革,并将其融入公司文化。

这一步对于确保 DevOps 之旅的成功非常重要,当然,为了真正有效,还需要一些技术上的改变。让我们详细研究一下这意味着什么。

经理必须推动变革

要想成功,开发运维所需的变更必须首先被管理层推动和接受。为了使变革有效,它必须得到公司的大力支持。

例如,想象一下,在我们旅程的开始,我们开始了一个新的发展。我们想设计一个持续集成/持续交付(CI/CD)系统。为了做到这一点,我们决定采用 Scrum 作为我们的方法,而不是以前使用的瀑布。

有一天,首席技术官(CTO)提出了一个他/她绝对想在春天发布的新功能,只有几个月的时间,但要做到这一点,我们必须取消和推迟一些其他功能。Scrum Master 试图告诉 CTO 不要这么快引入新特性,因为这会延迟其他特性并导致一些问题。首席技术官坚持,并利用他/她的权力,推动该功能的春天。为了赶上这个日期,团队必须推迟一些其他的工作,更快地开发新的功能,从而产生一些软件质量问题。需求并不完全清晰,在 CI/CD 中,周期问题被识别出来,这使得软件质量很差,基本上没有准备好发布。

最终,团队对 DevOps 实践失去了信心,渐渐地,每个人都回到了通常的做事方式。

开发者必须对软件负责

在正常的开发周期中,当软件发布到实际生产时,开发团队的责任就结束了。之后,开发人员开发新的功能,只有在运营部门发现需要修复的错误时才会参与进来。但这意味着必须发布新的特性,运营团队必须找到一种方法来减少 bug。

如果我们想要有一个成功的 DevOps 之旅,我们必须授权给开发者。这意味着当运营团队发现软件中的错误时,从事该功能的开发人员必须参与修复。这有两个主要优点:

  • 开发人员可以更容易地识别问题并找到解决问题的方法。因为他/她了解软件,所以他/她更容易找到问题的根本原因。

  • 因为开发人员可以确定问题的根本原因,所以他/她更容易找到永久的解决方案。这与 CI/CD 实践相结合,减少了发布的上市时间,并提高了软件的质量和稳定性。

这需要公司文化的重大变革:为 DevOps 的另一个重要变革铺平道路,当然,这必须得到管理层的完全批准才能真正有效。最大的好处是这保证了软件质量的提高。

操作人员必须被视为“一等公民”

当我们设计一个新特性时,开发和架构团队必须和操作团队一起参与。这是因为负责实时软件正确运行的人员组成了运营团队。

在一个新特性的发布中,操作人员在架构决策过程中的角色尤其重要。例如,想象一下,我们必须为我们的系统设计一个新的特性。开发人员为 web 界面提出了一个奇特的新组件,并提供了一个迷你演示。当然,在开发人员的笔记本电脑上,不会出现任何问题。但是,当在实际的服务器上测试组件时,可能会出现问题。唯一能够有效应对此类问题的人是操作技术人员,他们知道应该在哪个服务器上安装和运行软件,并且知道与安全性、软件版本等相关的所有策略。在这种情况下,运营团队可以拒绝该组件,例如,因为它不符合公司标准,或者团队可以启动一个流程来测试服务器并为新组件做好准备。

这意味着,对于下一个 n-Sprint 阶段的这个 Sprint,组件不能被使用,但是当服务器准备好时,操作团队可以通知开发团队。让操作团队参与软件设计的另一个重要原因是日志级别。对于一个开发人员来说,信息可能很清楚,但这是因为他/她了解软件并理解正在发生的事情。操作人员必须能够主要通过阅读日志来了解问题。如果日志过于冗长或不清楚,这将妨碍对错误的正确分析,并导致在寻找解决方案和确定问题的根本原因方面的延迟。

必须建立持续集成和持续交付策略

使用 CI/CD 策略有助于开发和运营团队更快地发现软件的潜在问题。当我们建立 CI/CD 实践时,我们会不断收到反馈。每个 CI/CD 系统的一个重要部分是代码审查。当开发人员完成代码时,必须对其进行全面测试。首先,为了确保成功的集成,开发人员必须要求其他软件工程师审查代码,并提出在代码中发现的任何问题。对于 CI/CD 系统来说,这是一个非常重要的步骤。对于软件中的每个简单更新,开发人员必须开始采用测试驱动开发(TDD)实践。这样,每个提交都可以由 CI 软件(例如 Jenkins)进行全面测试,并提升到下一步,例如创建和交付到质量保证(QA)环境进行测试。

建立 CI/CD 系统有助于提高系统的质量和稳定性。其原因与系统的性质有关。每当我们向它提交一个新文件时,软件的整个生命周期就延长了。这意味着,在出现新的错误时,我们可以实时确定错误的原因,并且我们可以轻松地回滚软件交付。同时,我们获得了不断审查软件的能力。这有助于运营团队识别安全风险,并采取所有必要的措施来消除这些风险。

但是为了获得真正的成功,避免产生问题和破坏系统的稳定,软件工程师必须在单元测试和自动化测试上投入更多的时间。此外,我们必须在代码审查上投入时间。如果我们没有一个好的软件测试覆盖率,或者没有真正审查代码,我们可能会发布具有潜在危险的软件,并危及应用的安全性。

必须消除 IT 部门的障碍

通常,开发和运营团队在日常工作中使用不同的软件。如果我们想确保有效的 DevOps 之旅,这可能会造成必须消除的障碍。

DevOps 可以统一沟通工具,促进不同 IT 相关部门之间的沟通。通过这种方式,我们可以协调软件发布的时间框架,并且可以更好地计划所涉及的工作。例如,如果引入到 CI 系统中的新特性产生了安全缺陷,安全团队可以使用公共通道与开发团队进行沟通,这样就可以找到解决问题的方法。同时,操作团队可以被告知开发人员对软件所采取的每一个步骤,并且可以为发布做好准备。

发布过程必须自动化

通过分析错误率,我们可以明确地将人为因素确定为失败的主要原因。DevOps 的主要重点是减少人为和其他错误,并缩短上市时间。为了实现这一点,我们必须做的一个重要的改变是自动化发布过程。

通过发布软件的自动化过程,我们减少了人与系统的交互。更少的人工干预减少了失败的数量,因为我们有一个可预测和可重复的过程。

具有自动过程的另一个优点是定义 IaC 的可能性。有了 IaC,我们可以通过代码定义我们的软件需要什么类型的结构。此外,定义 IaC 使基础设施可预测,并允许更快地发布升级。在最好的情况下,自动化发布过程和定义基础设施减少或消除了人工交互,因此,有效地减少了错误率。

敏捷实践必须在整个公司推广

DevOps 诞生于一次敏捷会议期间,为了提高效率,公司必须开始在所有部门实施敏捷。通常,敏捷实践主要由开发团队使用,但是为了一个好的 DevOps 之旅,我们必须将这种实践也推广到基础设施和运营团队。如果整个团队都在 Sprint 中工作,如果可能的话,共享相同的 backlog,这将有助于改善沟通。在 Sprint 计划期间,其他团队可以调整工作,以使其更有效。

同时,有了敏捷,我们可以改善沟通,让工作在团队中更加明显。在冲刺阶段的最后,我们可以看到其他团队的工作演示。这有助于了解工作的有效性,以及如何改进从一个团队到另一个团队的迭代。

采用 DevOps 的原因

公司决定采用 DevOps 有不同的原因。通常,DevOps 理念的采用与软件质量的改进和管理其发布的更好的方法有关。

当一个公司采用 DevOps 时,第一步是改善跨团队的交流。DevOps 的这一特点是敏捷方法所共有的,只有在整个公司使用的工具协调一致的情况下才能实现。

这种变化并不总是容易被所有 IT 员工接受。最初的抵制通常是对采用 DevOps 所必需的文化的改变。通常,设计和实现基础设施的生命周期是使用 ITIL 来管理的。这意味着该过程遵循瀑布方法,因为如果服务器不在您手中,配置服务器基本上是不可能的。

采用 DevOps 意味着改变我们对基础设施的看法:在可能的情况下,将其迁移到云,采用基础设施作为代码,并采用案例的兼容性,使用 Sprint 来管理工作。这要求所有团队使用共同的项目方法,并创建与开发团队共享的共同产品 backlog,特别是当项目涉及新的基础设施时。

采用 DevOps 实践的另一个原因是发布的软件质量的提高。有了 DevOps,我们可以采用一些程序来提高软件的质量。为此,我们必须实施持续集成和持续交付。有了这些,当我们在存储库上推送代码时,很容易识别错误。此外,因为我们有连续交付,我们可以每天更多次在 QA 上直接发布软件。这确保了对软件的持续检查和对软件工程师的持续反馈。

这些只是推动 DevOps 之旅的一些常见原因。不管是什么原因,了解 DevOps 中涉及哪些角色是很重要的。为此,我们必须澄清一些通常与 DevOps 的使用有关的误解。在这一点上,我们必须尝试识别与 DevOps 相关的常见错误,并澄清其角色和涉及的人员。

DevOps 涉及哪些内容和人员?

在谈论 DevOps 时,我们可能会对它是什么以及谁参与其中产生一些误解。关于采用 DevOps 的第一个神话与处理它的专业人员有关。对于许多人来说,DevOps 只涉及软件工程师、系统工程师和系统管理员。

这种假设是不正确的。当一个公司决定采用 DevOps 时,首先需要做的改变是改善不同团队之间的沟通。这不仅意味着开发和运营,还意味着其他团队,如 QA、安全和业务团队。为了有效和成功,DevOps 之旅需要所有团队成员共同努力。DevOps 的目标是缩短上市时间。这意味着当一个新的特性被设计出来时,每个团队都必须进行沟通,以达到目标。QA 工程师必须对软件工程团队做出快速反应,并传达软件中发现的任何小故障。同时,软件工程师必须与安全团队沟通,描述软件做什么和使用什么库,并允许安全团队管理必要的资产以确保软件的安全性。业务分析师必须与软件架构师保持一致,软件工程师必须与客户的需求保持一致。

如您所见,要进行成功的开发运维之旅,整个组织都应该参与进来。每个团队都必须负责一小部分业务,但是要与其他团队合作。DevOps 寻求消除团队之间的沟通障碍,使在开发期间而不是在发布后识别和纠正错误变得更加容易。这确保了更好的软件,更及时地发布到市场,更好地符合客户的需求。

所有这些演员必须像管弦乐队中的音乐家一样一起工作。如果所有人都尊重交响乐,一切都很顺利,但如果一个团队开始出现问题或没有练习良好的沟通,预定的目标就会打折扣。由于这个原因,采用 DevOps 时最重要的工作是改善内部和外部团队的协调。

改变坐标

通过采用 DevOps,我们想要实现的目标之一是减少协调性。这意味着确保那些负责管理团队的人投入更少的时间来协调不同的操作。当将软件从开发服务器转移到 stage 服务器时,这变得非常重要。如果有 CI/CD 业务,软件会自动升级。

当更多的自动化过程被引入时,人与人之间的相互作用减少了,因此对协调的要求也减少了。这对于缩短上市时间是必要的。更少的人需要认可;因此,出现的延迟更少。这需要改变传统的协调过程。当我们采用非自动化的过程时,通常,当我们完成软件开发时,负责开发的团队传达开发的完成,然后与其他负责的团队协调,将软件移到前台。这种协调本质上是将不同的交流方式委托给人类,例如通过电子邮件。DevOps 试图改变这种协调方式,减少人类的互动,当然,也改变了协调的实现方式。

协调有不同的实现方式。这些变化取决于环境——团队是远程的、现场的还是部分远程的。良好协调所需的正常属性是

  • 直接的

  • 间接的

  • 坚持的

这三个属性定义了我们如何管理整个团队的协调。每种风格都有它的优点和缺点,所以我们必须确保使用正确的协调类型来达到我们的目的。错误的类型会导致不必要的资源消耗和不良的协调。我现在将讨论不同的风格以及何时使用一种而不是另一种。我将描述如何使用敏捷,提高协调性,并在敏捷方法的角色和工件之间进行划分。

各种协调的目标是改善沟通,从而缩短上市时间。记住:DevOps 的最终目标是缩短上市时间。

直接协调

通过直接协调,那些负责协调的人互相认识。这意味着协调员直接协调每个团队成员的工作。这种协调需要那些负责协调的人做大量的工作。通常,当使用 Scrum 管理团队时,这种努力可以减轻。这样,在站立期间,负责协调的工作人员可以接收关于团队状态的直接反馈,并就此做出决定。

间接协调

通过这种类型的协调,我们不只是协调人,我们协调一个团队,例如,系统管理,软件工程,等等。这种协调需要更大的协调,因为我们并不真正深入任务的细节,而是从更高的层次接近它。例如,想象一下,我们必须管理正在安装的新软件、新的基础设施和新的软件功能。我们想要的协调不是关于详细的任务,而是对某个特定任务状态的总体看法。这个视图给予协调者制定计划的能力,并开始转移到其他活动上来计算发布的估计时间。

这种计划通常委托给产品负责人。仍然保持 Scrum 风格的管理,产品负责人并不真正深入单个功能,但是他/她有一个全局观点。生产负责人负责整个项目,当然,可以帮助团队达到最佳结果,减少不必要的工作。

持续协调

这实际上不是一种协调,而是一种人工制品。持续协调指的是当一个项目做出决定时,所有的报告和电子邮件都会被发送出去。

坚持给团队所有的工具来保持产品故事的每日记录,并允许团队基于项目的历史做出新的决策,并防止对项目本身的任何误解。

DevOps 链

到目前为止,我只讨论了可用的协调类型,以及哪些类型可以用来改善沟通。然而,我们想回答的最重要的问题是,为什么协调在 DevOps 中如此重要。

原因很简单。DevOps 运动按照“工具链”进行本质上,该工具链用于定义生产过程的每一步。

图 1-1 显示了软件版本开发的各个阶段。每个阶段都可以由不同的团队管理。因此,强有力和明确的协调和沟通非常重要。

img/464715_1_En_1_Fig1_HTML.jpg

图 1-1

波特的 DevOps 价值链

为了更好地理解协调和沟通的重要性,我们必须理解每个阶段是如何连接到其他阶段的,从而为软件创建一个生产“链”。

第一阶段是代码。在此阶段,创建软件代码。每个开发人员都将代码放在一个公共的存储库中,例如 Git,这将导致链中的下一个环节。

第二阶段是建造。这个阶段与持续集成实践直接相关。先前提交的代码被下载到构建服务器中,然后以自动方式构建。同时,第一次进行测试。如果测试的所有要素都成功,下一阶段就开始了。

第三个阶段是测试阶段。以前构建的软件通过一些自动过程进行测试,但是这一次,软件被一起测试。在构建阶段,只执行与我们发布的特定功能相关的单元测试。如果系统没有发现任何问题,软件将被提升到下一个阶段。如果失败,软件将被拒绝,一个自动系统将通知开发者。

第四阶段是配置。这个阶段需要明确的区分。当我们有好的和经过测试的 DevOps 实践时,我们可以有连续的发布。显然,这意味着软件在产品中的持续发布。然而,对于任务关键的软件,这个阶段通常分为两个不同的部分。第一个版本是为数量有限的服务器设计的,叫做金丝雀服务器

注意

术语 canary server 指的是有意限制的用于测试新软件的服务器数量。canary 服务器的目的是允许真实用户使用新软件,并提供关于潜在错误和服务器质量的实时反馈。当我们希望确保不发布对公司有潜在破坏性的软件时,canary 服务器非常重要。同时,canary 软件可用于笔测试和提高系统安全性。

该链的第五阶段是释放。在这个阶段,配置服务器以及新软件的基础设施。这个阶段定义了 IaC。使用代码创建和管理服务器。Chef、Puppet、Ansible 和 CloudFormation 等软件都是创建 IaC 的软件示例。

注意

基础设施即代码(IaC)是 DevOps 的核心。IaC 提供了为新服务器创建基础结构的能力。这保证了每一个新发布的完整性,因为减少了人的交互,提高了发布的完整性。此外,IaC 允许 DevOps 根据请求创建不同的环境。这使得开发人员可以根据请求直接创建不同的环境和不同的测试环境。有了 IaC,我们可以创建并编排一个不可变的基础设施,也就是说,一个由一些不可变的组件组成的基础设施,这些组件在我们每次发布基础设施时都会被替换。我们可以简单地用必要的更新部署一个新的不可变组件,而不是为我们的基础设施更新一个组件。这保证了整个基础设施的稳定性,并确保基础设施在每次发布时总是产生相同的结果。

该链的第六阶段是监视器。这对于提供关于我们的软件和基础设施的持续反馈是极其重要的。在 DevOps 中,监控是非常重要的,因为它允许开发人员获得关于软件的反馈,包括失败的平均值、失败的种类等等。,同时可用于检查服务器的指标,并为自动缩放提供反馈。

协调和沟通对于建立完整的 DevOps 链至关重要。这是因为每个阶段的每一步都需要很好的协调。我们必须确保每一步都有可靠的反馈,因为我们必须对错误做出快速反应并调整系统,以防止新的错误。

定义开发流水线

为了确保 DevOps 之旅的成功,最重要的工作之一就是定义开发流水线。构建这个流水线本质上是 DevOps 所要求的变化的核心。

开发生命周期中的第一个变化是实施持续集成。这需要对我们的开发实践进行一些更改,这些更改可以总结如下:

  • 定义单元测试。

  • 定义分支策略。

  • 建立一个持续的集成系统。

这三个实践是开发流水线的主干。首先,单元测试发生在开发人员每次将代码提交到中央存储库的时候。

当代码被提交给软件进行持续集成时——例如 Jenkins——这将编译代码并执行与软件相关的单元测试。在失败的情况下,一封带有测试结果的电子邮件被发送给开发人员。

因为我们不想破坏主分支,所以我们采用第二种做法,即分支策略。这对保持一个干净的主分支很重要。当开发团队采用这种策略时,每个开发人员在开发一个特性时都会创建一个特定的分支。该策略与代码审查密切相关。对于每次与主版本的合并,在构建完成并正确测试之后,都会进行代码审查。本质上,对于每次提交,只构建分支。这样,在出现错误的情况下,母版不会被破坏,并且总是准备好释放。

在积极构建的情况下,我们可以要求进行代码审查,当代码审查完成时,将分支与主分支合并,当然,重新启动一个完整的系统进行持续集成。有了持续集成,我们每次提交到主节点或分支时都要进行构建和测试。

持续集成必须与良好的通信系统相匹配。特别是,我们必须有一个良好的邮件系统,向开发者发送电子邮件,以中断流水线中的连续性。

有了这条流水线,我们就有了持续的软件生产。关闭流水线的是发布和监控。

在开发生命周期中,发布不是在生产过程中进行,而是在 QA 和测试服务器时进行。这种释放是自动发生的。本质上,它是对持续集成系统构建的软件的提升。QA 工程师将此版本用于测试目的,以测试软件,向开发人员提供更快的反馈,并更快地修复任何错误。

在 QA 中发布是很重要的,不仅是为了修复 bug,也是为了开始监控阶段。在 DevOps 中,监控对于减少和防止系统中出现错误非常重要。

监控对于检查和维护系统的稳定性非常重要。一个好的监控系统必须不仅检查系统的可用性,例如,网络是否可用或软件是否工作,而且可以用于防止未来的错误。

有很多用于监控的软件,例如 Nagios、Prometheus、Zabbix,或者 ELK 组合、Elasticsearch、Logstash 和 Kibana。所有这些软件都有其独特的优势,可以结合使用以达到最佳效果。

有效监控的一个重要原因是日志。有了好日志,就很容易启动一些日志分析策略。该策略旨在隔离常见的错误条件,并定义一些减少错误的实践,同时为开发人员提供修复软件的关键空间。

集中楼宇服务器

集中构建服务器对于构建正确的流水线至关重要。当我们设计 DevOps 架构时,我们必须考虑减少故障点。

当我们采用构建服务器时,我们将一切都集中在一个服务器上。这意味着我们使用不同的软件来发布我们的新软件。只有一台服务器或集群来构建新软件意味着只有一个故障点。任何问题都只集中在一点上。这降低了维护软件的成本,并加快了发布过程。

构建服务器通常与工件存储库服务器相连。存储库服务器是存储和保存构建的地方。它与持续释放有关。本质上,通过这种实践,我们每次都构建软件并将其发布到服务器。这个服务器本质上维护了我们在服务器上构建的软件的不同版本。通常,我们建立一个命名策略来维护不同的软件版本。这是因为我们希望唯一地标识软件的每个版本。

有了工件服务器,我们可以很容易地集中一个点来发布软件。通过这种方式,我们可以拥有相同软件的不同版本,并且,如果我们使用 Docker,我们可以同时在同一台服务器上拥有不同的版本。我们也可以同时启动它们,只需做一些小的调整。例如,这允许 QA 工程师进行一些回归测试,并在出现新错误的情况下,准确地识别哪个版本有缺陷。这使得开发人员能够准确地理解是什么样的代码更改引入了错误。

监控最佳实践

为了有效,监控必须与其他一些做法相结合。日志分析是防止错误和理解系统如何运行的最重要的实践。需要一些软件来分析日志并进行相关预测。

最常用的软件是 ELK (Elasticsearch,Logstash,和 Kibana)。这个生态系统非常有用,因为它提供了一个完整的日志分析系统,不仅提供了警报,还提供了错误和日志的图形表示。

日志分析对于提高软件质量非常重要。我们可以实施的一个重要实践是,拥有一些软件,不仅可以识别错误数量,还可以绘制这些错误。

具有错误的图形表示对于提供关于软件的可视反馈很重要,而不需要阅读日志来理解软件的状态。

监控是每个 DevOps 实践的基础,如果我们想要一个非常成功的旅程,我们必须确保有一个良好的监控系统。与此同时,我们不仅要开始监控产品,还可能要监控金丝雀服务器。这是因为它可以揭示错误,我们可以在发布到生产之前解决它。监控可以采取两种形式。黑盒监控测试一段代码,就好像它在黑盒里一样。它只揭示系统的状态,以确定它是否还活着。它并不能真正表明内部发生了什么,因为监控是外部的。Nagios 就是黑盒软件监控的一个例子。

与此相反的是白盒监控 *。*这种类型的监控提供了系统内部的清晰画面,例如,打开的 HTTP 连接的数量、错误的数量等。Prometheus 就是白盒监控软件的一个例子。

运营的最佳实践

在 DevOps 中,运营团队对实现最佳结果有很大的影响。运营团队的重要性与软件质量和客户对公司的看法密切相关。

如果出现错误,运营团队是公司的第一面。这个团队通常被委派在生产环境中维护软件。

与软件的唯一接触点是日志。由于这个原因,当软件被设计时,一些操作团队的成员必须被包括在内,并且,更重要的是当软件被发布用于测试时,他们可以提供反馈。这是因为如果日志不足,运营团队就无法真正识别错误,这意味着需要更多时间来解决问题。

同时,运营团队可以帮助识别常见问题,并提供文档来更快地解决这些问题。这个文档实际上是解决问题的一个步骤。它主要由一线运营工程师使用。它是“活动的”,这意味着它永远不会关闭,必须小心管理,以便与最新的软件更新保持一致。

文档必须指出日志中的常见错误,并显示如何解决问题的根本原因。该文档应由不了解该系统的人编写,基于此,必须提供关于要采取的适当步骤的具体细节。

我们可以实施的另一项运营实践是*开发人员随叫随到。*这一实践为运营界引入了一个新的形象。随叫随到的开发人员本质上是一名软件工程师,与操作专业人员一起解决生产中的错误。这有两个主要优点。首先是减少了发现和解决问题所需的时间。因为其中一个开发人员会处理这个问题,所以他/她可以很容易地识别出哪里出了问题,以及代码的哪个部分产生了这个问题。这可以推动运营团队努力修复它。

第二个好处是提高责任水平。因为开发人员致力于解决一个现存的问题,所以他/她更好地理解了软件的问题所在,从而可以改进他/她编写软件和日志的方式,因为一个糟糕的日志可能会导致他/她将来做更多的工作。

结论

在这一章中,我简要介绍了 devo PS——它是什么以及一项运动是如何诞生的。DevOps 对于云计算来说非常重要。云开发要求软件始终是活动的,并被设计为发布更快、质量更高。

DevOps 强调质量和上市时间。由于与持续集成和交付相关的实践有助于在系统上交付更快的服务,因此它允许简单设计的微服务架构。

DevOps 对现代软件开发非常重要,越来越多的公司开始采用它,因为它促进了一些提高软件质量所必需的最佳实践。DevOps 不仅需要改变我们对基础架构的看法,还需要改变我们设计和组织公司内部基础架构的方式。

DevOps 本质上代表了企业文化的改变。为了确保其最佳实践,改变组织是很重要的,以便其优先事项与文化变革、成功实践以及由管理驱动的变革的要求相一致,当然,这种变革是由工程师批准的。

二、GCP 简介

云是当今最常采用的技术之一。越来越多的公司已经开始在他们的项目中使用这项技术。这种变化是由不同的因素驱动的,例如,启动新项目的成本降低。当然,这只是部分正确。通常,从长远来看,云可能会更昂贵,但是,另一方面,它会减少资源。在云中,利用应用所需的所有环境来构建新的操作系统更容易。

同时,云解决方案在成本节约方面提供了传统基础架构所不具备的灵活性。有了云,我们通常为我们使用的东西付费。这意味着如果我们不需要,或者只是不使用一个实例,我们就不会为它付费。对于一个需要提升业务的小公司来说,这是一笔巨大的节省。对于传统的基础设施,我们无论如何都必须为服务器支付电费。云领域的主要竞争对手基本上有三个:亚马逊网络服务、微软 Azure 和谷歌云平台(GCP)。2017 年,谷歌被 Gartner 魔力象限评为最具远见的云,这是基于其在云计算领域的领先地位。

在这一章中,我简要介绍了云,然后介绍了 GCP。此外,我回顾了亚马逊网络服务(AWS)和 GCP 之间的相似之处。

云计算简介

云计算是一种新的 IT 模式。云计算的定义直接来自美国国家标准与技术研究所(NIST),如下所示:

云计算是一种支持对可配置计算资源(例如,网络、服务器、存储、应用和服务)的共享池进行无处不在、方便、按需的网络访问的模型,可配置计算资源可通过最少的管理工作或服务提供商交互来快速供应和发布。这个云模型由五个基本特征、三个服务模型和四个部署模型组成。

从前面的定义中,我们可以看到云本质上是网络中的共享资源,这是另一个重要的区别点。云还有一些其他的特征,这些特征对于定义云计算是必不可少的。

  • 自助服务和按需服务:客户可以单方面定义所需的资源,并在使用时支付费用。

  • 网络接入:根据 NIST 的定义,云是一组通过网络提供的服务。这意味着客户可以通过网络访问不同的资源,并且可能跨不同的平台,例如移动电话、平板电脑和瘦客户端计算机。

  • 资源池:云允许不同的人同时共享资源。此外,不同的用户可以有不同的配置。云计算可以管理这种必要性,并与不同的用户共享资源。

  • 对变更更快的响应:云的这种能力允许更快的发布,大多数时候是以自动的方式。这意味着云必须更快地适应,以便共享新资源。

  • 可测量的服务:每个云系统必须允许用户管理和检查所选择的资源。云必须有一个系统来控制和管理所使用的资源,包括停止其使用的能力。通常,云使用按使用付费的模式,这意味着用户只为消耗的资源付费。

云计算的另一个重要特征是其不同的服务和部署模型。事实上,这些不同的服务和模型定义了我们如何使用云。

云计算服务模式

云最重要的区别是服务模式。服务模型定义了如何向客户提供云。每个型号都有不同的特点。服务模式包括

  • SaaS(软件即服务):SaaS 模式允许用户设计和推广他们的软件。基于云的基础设施,这意味着消费者本质上共享网络上的一些资源,并在该资源上运行他们的软件。消费者无法管理底层基础设施,如网络、操作系统等。,他们只能使用该软件。该软件可在不同平台上使用,如手机、平板电脑或电脑。SaaS 的好例子包括 Google Docs、Office 365 和 Zendesk,在大多数情况下,消费者只需要连接互联网就可以访问不同平台的软件。

  • PaaS(平台即服务):PaaS 模型为用户提供了在云环境中部署他/她自己的软件、库、数据库和软件所需的一切的能力,而无需考虑底层基础设施。有了 PaaS,消费者可以使用提供的语言开发一个应用,基本上不关心资源。PaaS 的一个例子是 Heroku,其中消费者使用他或她自己喜欢的语言开发应用,唯一需要的工作是创建软件基础设施,如数据库,并部署软件以允许用户访问他/她的云资源。

  • IaaS(基础设施即服务):IaaS 模式是最易管理的服务模式。借助 IaaS,用户可以运行自己喜欢的软件。软件在用户创建的基础设施中运行。这包括操作系统、应用或其他任何需要的东西。同时,用户能够管理一些底层网络功能,例如防火墙和负载均衡器。IaaS 的好例子包括 GCP、AWS 和 Azure。

所有这些服务模型对于定义我们想要使用的云类型都很重要。每种服务模式都会影响客户如何接触和使用云,无论是仅通过网络跨不同类型的平台使用软件,还是定义操作系统并以此为基础构建应用。

部署模型

在云计算中,我们可以确定四种不同类型的部署模型。模型中的差异仅由我们如何在云上发布来定义,而不是由我们将部署什么样的云来定义。这意味着每个服务模型都可以根据四个部署模型来定义。

这些模型及其差异如下所示:

  • 私有:云计算的私有模型仅供内部使用。这种云通常被构建为跨不同业务部门的私有公司的股份。这种云完全由提供商(通常是公司的另一个业务部门)拥有和管理。

  • 社区:这种云模型旨在跨特定社区共享,例如安全社区或慈善机构。在这种类型的部署中,社区拥有并管理云基础设施。

  • Public :这个云模型本质上可以被任何人使用。平时都是租的。这种模式的例子有亚马逊网络服务、GCP 和微软 Azure。

  • 混合:这种类型的云模型结合了不同的模型。出于安全原因,通常在需要对部分数据(例如客户数据)保密时使用。拥有混合云模型的目的是结合不同种类的云模型的特征,例如,对隐私的需求,但同时对与社区共享一些信息的需求。

不同的部署模型旨在响应潜在用户的不同需求。例如,当我们希望部署 SaaS 云,但又担心数据的安全性时,可以采用混合解决方案,因为某些法规要求必须保护我们的数据管理方式。在这种情况下,我们可以将 SaaS 放在公共云中,例如 GCP,但将数据存储在我们的内部私有云。这基本上可以满足所有的法律要求,同时为我们的客户提供公共软件。

为什么要使用云?

正如每一种技术一样,云有一些好处,也有一些坏处。云可以节省成本。特别是,如果我们想开始一项新业务,采用云可能比购买和维护内部服务器更便宜。

此外,公共云允许服务级别协议。大约 99%的时间,这意味着我们的网站和我们的业务基本上总是在线。公共云还可以帮助公司扩展业务。我们可以在几分钟内轻松地为我们的实际基础设施增加更多电力,并在我们不再需要这种电力时缩减规模。

这有助于节省公司资金并提高盈利能力。另一个重要的考虑因素是总运营成本(TCO)。总体拥有成本通常根据硬件和维护的所有成本来计算。云的总拥有成本非常低,因为我们实际上是按月租用硬件,而且我们总是可以根据业务需要,通过减少使用的资源来降低成本。

因为云基础架构本质上可以随着我们的业务需求而增长,这创造了巨大的投资回报(ROI)。这是因为我们只为我们真正使用的东西付费。对于传统基础架构,我们降低了初始投资回报率,因为我们必须偿还初始硬件投资,而我们的业务无法支持这一点。

谷歌云平台简介

GCP 是谷歌提供的公共云。它由一套服务组成,运行在谷歌为客户运行软件的同一基础设施上,如 YouTube 和 Gmail。

GCP 于 2011 年 10 月首次公开发售。从那时起,它的受欢迎程度持续增长,现在是第三大最受欢迎的云平台,仅次于 AWS 和 Azure。

GCP 提供广泛的服务,可分为以下几个领域:计算和托管、存储、网络、大数据和机器学习。

对于每个领域,GCP 提供了一整套产品,可用于构建我们自己的云应用。最受欢迎的服务是

  • 谷歌计算引擎(Google Compute Engine):这提供了创建虚拟机来运行操作系统的能力。它允许在云中创建不同的“计算机”。

  • Google App Engine :构建应用的 PaaS 组件。使用 App Engine,可以创建使用不同类型的语言和框架的应用。在编写的时候,App Engine 支持 Go,PHP,Java,。NET、Ruby、Python 和 Node.js。

  • Google Kubernetes 引擎:容器的托管协调器,用于部署、缩放和发布容器。

  • Google Cloud Bigtable :由 Google 开发的压缩高性能专有数据存储特性。

  • Google BigQuery :这是一个 RESTful web 服务,用于分析大量数据集。

  • 谷歌云功能:事件驱动的无服务器云平台。有了函数,就有可能将基础设施创建为代码,即由代码设计和实现的基础设施。

  • Google Cloud Datastore :基于 Bigtable 和 Megastore 技术的高度可伸缩的完全托管的 NoSQL 文档数据库

  • 谷歌存储:这是一个在 GCP 上存储数据的 RESTful 服务。它可以与亚马逊 S3 服务相媲美。

这只是一个简短的服务列表,但足以开始我们的旅程。下一步是设置和配置 GCP 实例。

从 GCP 开始

我们可以按照一些简单的步骤创建一个新的 GCP 帐户。首先,我们必须连接到站点: https://cloud.google.com/

这是连接到 GCP 的初始页面。要创建一个新帐户,只需点击免费试用 GCP 按钮。插入所有参数,创建免费账户。要完成这个过程,我们必须提供信用卡信息,但不用担心,不收取任何费用。免费使用一年,或者,直到我们没有达到 300 美元的免费试用限额。之后,该帐户会自动转换为付费使用帐户。

当账户初始化时,我们将看到如图 2-1 所示的页面。

img/464715_1_En_2_Fig1_HTML.jpg

图 2-1

谷歌云平台屏幕

GCP 的初始页面显示了使用的资源和我们在云中的项目。当然,因为我们刚刚开始,页面是空白的。

我们必须采取的第一个行动是创建一个新的项目。项目本质上是一个容器,用于重组与特定云项目相关的所有 IT 和非 IT 资源。每个项目都由一些特定的参数标识,如下所示:

  • Name :用于标识和描述项目的字符串。该名称仅供用户参考,可随时更改。使用自由层,我们可以创建 24 个项目。

  • 项目 ID :这是一个全局唯一的字符串,用于标识项目。它从项目名称开始创建。可以编辑和更改项目 ID。为了创建项目 ID,我们可以使用任何小写字母、数字和连字符。唯一的要求是名称的唯一性。输入后,就不能再更改了。

  • 项目编号:这是 GCP 自动生成的参数。我们无法管理或更改这个号码。

要创建新项目,只需单击“创建新项目”按钮。这将启动创建新项目的过程(图 2-2 )。

img/464715_1_En_2_Fig2_HTML.jpg

图 2-2

与 GCP 一起创建新项目

现在我们可以看到,要创建一个新项目,我们必须添加一个新名称。在这种情况下,我们指定名称 PracticalDevOpsGCP ,保留项目 ID 的默认值,并单击 Create。

单击 Create 按钮为我们创建项目。这需要几秒钟。当项目准备好了,我们可以点击右侧返回到主页面。在这种情况下,我们回到仪表板。

主页仪表板显示创建新项目或选择另一个项目的命令(图 2-3 )。

img/464715_1_En_2_Fig3_HTML.jpg

图 2-3

进入谷歌仪表板

因为我们之前已经创建了项目,所以我们单击“选择”按钮。这将把我们带到另一个页面,从中选择我们想要打开的项目(图 2-4 )。

img/464715_1_En_2_Fig4_HTML.jpg

图 2-4

选择项目

选择我们的项目,然后单击 Open,打开项目。当我们打开项目时,我们会看到来自 GCP 的完整仪表板(图 2-5 )。这个仪表板可以根据我们的需求进行配置。

img/464715_1_En_2_Fig5_HTML.jpg

图 2-5

我们项目的谷歌仪表板

在左侧,我们可以看到“项目信息”这显示了关于我们项目的一般信息。屏幕中央是 API 请求(请求/秒)。这是一个重要的参数,它显示了每秒钟对我们的 API 的请求数量。对于大多数服务,使用 GPC,可以在运行时启用 API 接口。这意味着我们可以在生产中以编程方式使用该服务。当我们在生产时,只要看一下这个面板,我们就能很容易地发现问题。例如,如果我们看到每秒的请求数量显著下降,这可能表明我们的基础设施存在问题。

在右边,我们看到谷歌云平台的状态,这表明了平台的一般状态。这有助于识别平台上的一般问题。

除了技术信息之外,仪表板还显示一些非技术信息,如计费。要访问计费信息,请单击仪表板上的计费部分。

了解 GCP 的开单

计费是我们流程的重要组成部分,因为它本质上是我们业务的回报。GCP 提供了一个非常好的平台来理解计费以及账单是如何构成和阅读的。

GCP 确定以下项目的计费帐户:

  • 付费账户 ID :这是 Google associates 为我们的账户生成的唯一 ID。

  • 计费账户名称:与计费账户 ID 关联的名称。我们可以更改这个名称,使其对用户更友好。

  • 状态:表示计费的状态,是关闭还是打开。

  • 项目数量:当我们创建一个计费账户时,我们可以将它链接到多个项目。这是用来有一个支付点,但不同的客户或项目不同的法案。

计费仪表板还显示有关预算和警报的信息(图 2-6 )。我们可以创建一个月度预算,并在达到预算上限时发出警报。当我们必须控制项目的费用时,这是非常有用的。

img/464715_1_En_2_Fig6_HTML.jpg

图 2-6

GCP 开单控制板

默认情况下,只有谷歌云账户的所有者负责管理账单。我们只需单击控制面板右侧的权限部分,即可轻松添加另一个用户。添加用户时,我们可以为他们选择角色。所有角色都可以通过身份和访问管理(IAM)部分进行管理。

GCP 计费仪表板非常清晰,易于管理。我们可以很容易地确定有多少项目与计费帐户相关联,以及显示所有付款详细信息的付款概览。根据公司的需要,我们可以很容易地改变这个参数。

GCP 资源

当我们谈论云时,我们本质上也在谈论硬件。在 GCP,我们“租用”谷歌的基础设施。

谷歌在不同的地理区域托管资源,因为这降低了系统宕机的风险。两个不同地点同时发生自然灾害或其他问题的可能性极小。拥有不同资源位置的另一个显著优势是减少了延迟。

这些位置中的每一个都被称为区域。一个地区本质上是一个谷歌数据中心。在数据中心,我们可以找到构建谷歌云应用所需的所有资源。这些资源包括物理服务器、网络组件和虚拟机。实际区域位于美国中部、西欧或东亚。

每个区域本质上都是一个区域的集合。区域是云平台的部署区域。区域应被视为基础架构中的单点故障。

因为一个区域可能会有停机时间,为了确保容错和高可用性,我们必须考虑跨不同的区域部署我们的应用,并且可能跨不同的地区。不同的区域和分区有助于我们在云中设计完整的容错和高可用性。

该区域有一个特定的名称。这是使用区域名称和一个数字创建的,该数字标识区域的编号,例如 europe-west2。

注意

当我们设计云应用时,考虑它的可用性是很重要的。这本质上与我们对部署模型做出的决定有关。如果我们希望我们的服务具有高可用性,我们必须使用区域性应用,比如 App Engine,或者托管的多区域应用,比如云存储。如果我们想要为遵循这种策略的数据构建灾难恢复,我们使用一些基于多区域的服务,比如 Google 云存储或 Google Cloud Datastore。如果我们使用地区或区域性服务,我们将在多区域资源中对数据进行快照。数据应该在不同的区域或分区中复制。这样,如果一个区域出现故障,我们将有另一个区域可用。对于计算,使用区域或地区资源,如 Google App Engine,但在失败的情况下,要有一个在另一个区域或地区启动应用的机制。当然,对于总体高可用性,我们必须有一个负载均衡器,以平衡跨区域或地区的资源,并将数据与多区域服务连接起来。

以下是一些不同类型的 GCP 资源:

  • 区域:区域资源是可以在特定区域的所有区域中冗余部署的资源。这为区域资源提供了高可用性。

  • 区域:区域资源在单个区域内运行。如果区域变得不可用,资源本身也会变得不可用。

  • 多区域:这项云服务由谷歌直接管理,是冗余的,分布在不同的区域和地区。存储在多区域区域中的数据存储在不同的区域中,而不是仅存储在一个区域或区域中

  • 全局资源:这种资源可以被另一个资源访问,与区域或地域无关。全局资源通常是预配置的磁盘、快照或网络。

当我们决定部署云架构时,了解资源的类型非常重要。这是因为它本质上驱动了架构的设计。

当我们计划我们的架构时,根据我们选择的资源种类,理解操作的范围是非常重要的。例如,如果我们必须创建一个网络,我们就创建一个全局资源,因为它可以跨不同的区域和地区共享。但是,当我们分配 IP 时,这实际上是一个区域操作,因为 IP 地址会根据区域而变化。

当考虑云时,我们必须根据架构的效率做出选择。这意味着我们永远不会使用不同地区的硬盘资源,因为等待时间太长了。正确规划资源是好的和坏的云项目之间的关键区别。

谷歌 SDK

GCP 提供了一个很好的命令行界面,我们可以用它来管理我们的云。这个软件开发工具包(SDK 或 devkit)叫做 Google Cloud SDK。它适用于不同的操作系统,可以从以下链接下载: https://cloud.google.com/sdk/docs/

为您的操作系统选择正确的 SDK 并安装。这通常是一个自动过程。安装 SDK 后,我们就可以初始化我们的云环境了。安装 SDK 后,我们可以打开命令行并输入以下命令:

gcloud init

这个命令开始配置 Google Cloud SDK。当要求登录时,只需输入“y”,这将打开浏览器。现在登录 GCP(图 2-7 )。

img/464715_1_En_2_Fig7_HTML.jpg

图 2-7

Google SDK 初始化

现在我们已经看到,我们可以选择创建一个新项目或使用我们刚刚创建的项目,按 1 并完成 Cloud SDK 配置。

gcloud现已配置完毕。我们可以使用这个工具通过命令行来访问和管理我们的云环境中的不同资源。也可以从控制台使用命令行。要启动命令行,只需单击搜索栏右侧的 cloud shell 按钮。该命令直接在控制台上打开一个外壳(图 2-8 )。

img/464715_1_En_2_Fig8_HTML.jpg

图 2-8

谷歌 SDK 在控制台上打开

gcloud有非常全面的帮助系统。例如,我们可以看到我们可以在计算机上执行的所有操作,使用命令

gcloud compute -h

这会产生如下所示的输出:

C:\Users\user\AppData\Local\Google\Cloud SDK>gcloud compute -h
Usage: gcloud compute [optional flags] <group | command>
  group may be           accelerator-types | addresses | backend-buckets |
                         backend-services | commitments | disk-types | disks | firewall-rules | forwarding-rules | health-checks | http-health-checks | https-health-checks | images | instance-groups | instance-templates | instances | interconnects | machine-types | networks | operations | os-login | project-info | regions | routers | routes | shared-vpc | snapshots | ssl-certificates | target-http-proxies | target-https-proxies | target-instances | target-pools | target-ssl-proxies | target-tcp-proxies | target-vpn-gateways | url-maps | vpn-tunnels | xpn | zones
  command may be         config-ssh | connect-to-serial-port | copy-files |
                         reset-windows-password | scp | ssh

我们可以通过使用命令行gcloud compute –help获得关于该命令的更多信息。该命令实质上是为特定命令生成一个手册页。在该手册页上,可以找到有关该命令以及我们可以使用它执行的操作的更多信息。

gcloud可用于另一种脚本语言,用于自动化操作。例如,我们可以使用命令在我们的项目中拥有所有的活动实例并管理它们。这可以是 Jenkins 脚本的一部分,用于指示部署期间实例的状态,例如,停止对该特定实例的监视。GCP 上的另一个重要特性是 REST API,称为云 API。这其实是云市场上最强大的 API。有了 GCP REST API,我们可以在云中执行任何操作。例如,我们可以使用 API 创建虚拟私有云(VPC)或防火墙。支持的语言有

  • 爪哇

  • Java Script 语言

  • 。网

  • 对象-C

  • 计算机编程语言

  • PHP (Beta 版)

  • Dart(测试版)

  • Go (Alpha)

  • Node.js(Alpha)

  • 红宝石色(阿尔法)

有了这个 API,就有可能集成 GCP 的任何组件。

结论

这一章简要介绍了 GCP。我们讨论了不同的服务,并且配置了我们将在本书的剩余部分继续工作的项目。

除了如何安装和使用 Google 的基本 SDK 工具之外,还讨论了与云计算相关的不同产品。您看到了 Google Cloud 是如何配置的,以及什么是区域和专区。在决定如何构建我们的云项目时,这些都很重要。

GCP 提供一系列服务。在本书中,我将集中讨论 DevOps 所必需的服务。您将了解如何使用 App Engine 并创建继续我们的 DevOps 之旅所需的所有服务。

三、持续集成和交付简介

持续集成(CI)和持续交付(CD)日益流行。这是因为它们对于缩短上市时间和提高软件质量至关重要。

随着 CI 和 CD 的实践,每次我们从一个中央存储库发布软件,它都被构建并发布以进行测试。这意味着每天有数百次递送。CI 和 CD 是严格联系的,一个是另一个的延伸。这两种做法都有一些相关的成本和节省。在这一章中,我将介绍 CI 和 CD,并尝试说明它们对我们的 DevOps 之旅有多么重要。

持续集成的定义

CI 的定义很简单。这是一种开发实践,要求开发人员将代码集成到一个中央共享存储库中。每次开发人员提交代码时,它都会与其他代码集成,并通过测试的执行来验证。

每当我们将代码提交到集中共享的存储库时,CI 就开始了。这意味着每次我们改变一些东西,例如,一个 HTML 页面上的标签,或者一个变量,我们测试整个解决方案,因为我们在每次提交时都测试解决方案。我们可以在构建中更快更容易地找到错误并修复它们。我们在每次提交时所做的基本上是构建整个解决方案。

采用 CI 成本低。本质上,我们只需要一个有 Jenkins 的服务器,就可以开始使用了。CI 可以总结为三个简单的阶段(见图 3-1 )。

img/464715_1_En_3_Fig1_HTML.png

图 3-1

持续集成链

  1. 发展

  2. 试验

  3. 部署

当我们有了 CI 之后,我们每次在我们的存储库中提交代码时都会执行这个循环。当我们使用 CI 时,我们每次都创建一个构建。这是 CI 的本质:我们每次提交代码时都有一个完整的软件生命周期。关于这一点有两种观点。通常,CI 向 QA 发布。

在持续集成场景中构建什么?

在 CI 场景中,构建不仅仅是编译软件。一个构建由发布软件所需的所有操作组成。构建本质上是一个将所有代码放在一起并验证所有代码都工作正常的过程。

如果我们考虑一个典型的项目,我们可以看到不同的人参与不同的领域。开发人员创建特性,并在必要时更改表的脚本。数据库管理员(DBA)放置脚本,并在数据库准备就绪时通知开发人员。从那里开始,开发人员继续开发。

在开发的最后,软件被集成和测试在一起。这可能需要数周的工作才能完成,如果出现错误,软件可能会返回给开发人员进行修复。这需要时间,并且会降低软件的质量。这是因为,在经典的项目管理专业(PMP),又名瀑布,方法,质量是三个变量的结果:

  • 范围

  • 时间

  • 费用

如果我们忽略了这三个变量中的任何一个,我们就会降低软件的质量。在 CI 场景中,所有 CI 流程都从提交存储库中的源代码开始。

可以通过以下简单步骤设计 CI 场景:

  1. 开发人员提交存储库中的代码。

  2. CI 服务器汇集存储库,下载最后的代码,并开始测试。如果所有测试都通过了,服务器就编译它。

  3. CI 服务器通过电子邮件、slack 等发送通知。,并提供关于集成的反馈。

  4. CI 服务器继续汇集存储库,以检查新的更改。

图 3-2 显示了一个示例 CI 系统。在这里,我们可以看到我们有一个邮件服务器,用于向开发人员发送反馈。反馈对于一个好的 CI 系统是至关重要的,因为它提供了对构建的即时评论,开发人员可以使用它来更快地解决任何问题。

img/464715_1_En_3_Fig2_HTML.jpg

图 3-2

竞争情报系统

每当开发人员在存储库中提交代码时,这个循环就开始了,这意味着它每天会开始一百次。

代码库服务器

代码库服务器是我们存储软件的地方。这本质上是一个回购软件,像 Git 或 SVN。服务器可以是内部的,这意味着我们有一个内部服务器,也可以是外部的,在这种情况下,我们不直接管理服务器,例如,当我们将代码放入 Bitbucket 时。

一个好的 CI 系统必须有一个存储服务器。这基本上是我们流程的起点。每当开发人员提交时,我们就开始这个过程。我们可以在回购中有许多分支,但只有一个主分支,这实质上是我们每次集成其他分支的地方。

持续集成服务器

持续集成服务器负责在我们每次提交代码时运行集成脚本。为此,我们可以使用不同的软件,例如 Jenkins、Travis CI、TeamCity 等。

CI 服务器执行一些特定的操作。

  1. 从存储库服务器检索代码

  2. 将最后一次提交与旧软件合并

  3. 对软件执行测试

  4. 构建软件

  5. 发送带有结果的反馈

没有必要拥有配置项服务器。我们可以用一个简单的脚本来执行这个操作,比如 Bash、Ant、Maven 或 Makefile。我们可以编写一个简单的脚本来合并和构建软件,如下所示:

#!/bin/bash
function integrate_code() {
    SOURCE=$1
    DEST=$2

    git checkout $DEST

    git pull --ff-only origin $DEST
    result=$?
    if [ $result -ne 0]
    then
        echo "Error in pull"
        exit 1
    fi

    git merge --ff-only $SOURCE --no-edit
    result=$?
    if [ $result -ne 0]
    then
        echo "Error in merge"
        exit 1
    fi

    git push origin $DEST
    result=$?
    if [ $result -ne 0]
    then
        echo "Error in a push"
        exit 1
    fi
    return 0
}

这个脚本将一个分支的代码与另一个分支的代码合并。这是一个非常简单的脚本,只是一个更复杂的构建系统的一部分。

当我们使用 CI 服务器时,我们可以减少需要维护的脚本数量,因为 CI 服务器以自动方式开始构建。例如,我们可以配置 Jenkins 以不同的方式开始构建(图 3-3 )。

img/464715_1_En_3_Fig3_HTML.jpg

图 3-3

Jenkins 构建触发器配置

从上图可以看出,我们可以把 Jenkins 和大部分的源码控制管理系统连接起来,比如 Git,Mercurial 等。,我们可以用不同的选项触发构建,例如 GitHub hook。这样,当我们在 Git 中提交软件时,Jenkins 会自动开始构建。通过添加到自动构建中,我们可以在某个时间进行构建,或者使用外部脚本来开始构建。

注意

当我们使用定期构建时,我们并没有真正使用持续集成方法,因为当软件提交给代码时,构建并没有开始。这种构建策略可能是好的,例如,当我们希望有一个可以与 CI 策略集成的每日构建时。

此外,有了 CI 服务器,我们就有了一个仪表板,从中我们可以看到哪些构建是好的,哪些构建是失败的。这可以为我们的软件提供即时的可视化状态报告。

持续交付

CD 是一种软件工程实践,用于在短周期内发布软件。这意味着每一次构建,我们都会创建整个软件的新构建。这并不意味着我们将软件发布到产品中,但是如果我们愿意,我们可以发布它。这就是持续交付和持续部署的区别。

注意

我们必须了解连续交付连续部署的区别。概念是相似的,但是两者之间有本质的区别。通过连续交付,我们指的是创建构建的流水线,但不一定是我们打算发布到产品的流水线。通过持续部署,我们每次都将构建发布到生产环境中。持续交付和部署之间的这种相对较小的差异对公司的业务产生了很大的影响。

有了 CD 实践,我们总是有一个随时可用的构建。这允许 QA 团队立即开始测试有限数量的特性,并立即向开发团队提供反馈。这减少了修复问题的时间,并提高了软件本身的质量。当然这个更多的是看环境。在大多数具有 CI/CD 系统的环境中,我们并不真正需要 QA 团队来执行测试。

这种方法有助于降低成本。在开发生命周期中维护软件或解决问题肯定比在软件生产出来后修复问题更有效。此外,使用 CD,我们总是测试软件的一小部分,因为 CD 发生在每次提交中,因此降低了发布带有破坏性错误的软件的风险。

CD 背后的想法类似于通知 DevOps,但它们是两种不同的实践。DevOps 更专注于改变整个公司文化。相反,CD 专注于生产新的软件版本。然而,因为 DevOps 本质上代表了文化的变化,CD 和 CI 实践属于它的范围。CD(见图 3-4 )是 CI 的延伸,因为 CD 在 CI 的基础上又增加了一步。正因如此,要想有好的 CD 到位,就要有很强的 CI 基础。

img/464715_1_En_3_Fig4_HTML.png

图 3-4

连续 d 链

持续集成和持续交付之间的区别

CI 和 CD 是相似的,但是在这些实践之间有一些差异。CI 致力于将软件与每次提交集成起来。这发生在单元测试之后。

CD 扩展了 CI,因为它在软件的集成和测试完成后增加了另一层。CD 构建软件并为潜在的发布做准备。

CI 非常重视测试阶段。这对 CI 非常重要,尤其是当代码与主分支合并时。CI 的目标是在合并后不失去功能。

另一方面,CD 非常重视构建软件。有了 CD,我们可以决定每天发布新软件。2011 年,亚马逊平均每 11.6 秒发布一次新软件。这是每天发布的巨大数量。通过持续发布,我们可以自动化实现这一结果所需的任何步骤和过程。

持续交付的策略

为确保良好的 CD,我们必须具备以下条件:

  • 良好的分支策略

  • 强大的单元测试策略

  • 自动测试阶段

  • 自动代码升级

前面所有的实践都是紧密联系在一起的,有助于制作出好的、坚固的 CD。其中有些是和 CI 联系在一起的,比如分支策略和单元测试;其他人则更多地与 CD 联系在一起。

良好的分支策略

在 CI 中,目标是将软件与主分支集成。考虑到这一点,我们可以制定我们的分支战略。

最常见的分支策略(图 3-5 )是为我们工作的每个特性/bug 创建一个分支。这样,我们可以将单个特征与主分支合并。

img/464715_1_En_3_Fig5_HTML.png

图 3-5

分支战略到位

因为我们对 bug 的每个特性都有不同的分支,所以我们可以在不破坏代码主线的情况下处理代码。这意味着在开发过程中,我们总是有一个可构建和潜在可发布的代码行。

当我们在我们的分支中发布软件时,我们对我们的分支执行单元测试。我们不仅测试我们开发的功能,而且测试整个系统。通过这种方式,我们可以对我们在代码中引入的任何错误获得即时反馈。

如果所有的测试都通过了,并且特性是绿灯,我们就可以开始将我们的分支与代码主线集成起来。当我们合并时,我们开始另一组测试。我们也可以开始一些代码分析,如果它是绿灯,我们可以发布一个具有新特性的新版本。

强大的单元测试策略

为了有效,好的 CD 必须有强大的 CI,并且对于强大的 CI,我们必须有强大的单元测试策略。如果我们想要构建一个好的 CI 系统,单元测试是必不可少的,因为测试可以识别我们打算发布的产品中的错误。

单元测试不仅对于识别错误很重要,而且因为它可以用来验证业务需求。开发之前必须编写单元测试。这意味着我们必须编写通过单元测试的代码。这种技术被称为测试驱动开发(TDD)。使用 TDD,我们根据业务需求编写测试,然后开始编写代码。这确保了需求和我们发布的代码之间的正确关联。

TDD 通常与代码覆盖值相关联。这意味着我们要确保测试覆盖了一定比例的代码。代码覆盖率的一个很好的百分比大约是 85%。这基本上涵盖了所有的代码,我们可以非常确定我们的代码质量与这个百分比测试。

测试的另一个重要实践是测试金字塔。这个短语是一个隐喻,用来描述一个桶中不同粒度的测试。这个概念是由迈克·科恩在《?? 用敏捷取得成功》一书中定义的。当我们想到测试金字塔时,我们必须包括三种类型的测试。

  • 单元测试(金字塔的底部)

  • 服务测试(中间)

  • UI 测试(金字塔的顶端)

这个金字塔有助于测试软件的所有重要方面。建立一个测试金字塔是很重要的。这是因为它有助于捕捉大多数错误和设计更可靠的系统。

自动测试阶段

测试对于保证代码的质量非常重要。除了单元测试,我们还可以进行另一种类型的测试。通常,我们有一个集成测试,用于检查所有软件是否与系统的其他组件正确集成。我们可以增加一个验收测试。这种测试被设计为在整个系统上执行。

当我们执行集成测试时,我们实质上移除了测试的所有模拟部分,而使用真实的系统。通常,我们在单元测试阶段创建模拟部件,因为我们还没有为测试准备好系统的任何部分。出于这个原因,我们为此创建了一个假的响应。

集成测试对于测试整个系统和验证我们的集成非常重要。如果在集成测试中出现任何错误,我们必须恢复代码的集成。

验收测试对于降低意外删除特性和构建不符合业务需求的风险是很重要的。通常,验收测试是由 QA 工程团队设计的,旨在测试与系统的任何集成。该测试通常测试系统的用户界面/UX,尽管它不是为了测试软件本身,而是更一般地,测试系统和与之相关的功能。

自动代码升级

代码升级是持续发布的基础,因为它用于定义软件的什么版本可以发布。当测试阶段正确通过并且代码构建没有任何问题时,代码升级就发生了。

通常,像 Jenkins 这样的 CI 服务器有能力提升代码本身。一般来说,这是通过以特定的方式标记代码或者为发布创建新的分支来完成的。当我们发布一个版本时,我们本质上做的是在不同类型的服务器上发布代码。例如,我们将代码从开发服务器移动到登台服务器。例如,QA 工程团队可以使用不同的服务器来执行一些额外的手动测试。

当我们有一个 CI 系统时,通常我们有一个文件来定义工件。这个文件描述了所有的库和软件的每一部分的关系。这由术语“工件不变性”来描述,并由 Maven 来举例说明,使用它我们可以定义系统及其所有依赖项,以安装和构建软件。

代码检查

另一个与 CI 和 CD 相关的重要实践是代码检查,也称为林挺。这种实践对于保持良好的代码架构师水平非常重要。

这种技术用于探索代码,并创建一个清晰的外观图片。例如,我们可以识别方法是否太长或太复杂。

我们可以使用 CI 来进行质量代码检查。以 Ruby 为例,我们可以使用 RuboCop。该工具分析代码并显示其中识别的所有错误。在 Python 中,我们可以使用 PEP8 来执行一些规则。这些规则的使用提高了软件的质量,因为所有的开发都遵循一些特定的规则。

对软件的另一个重要检查是圈复杂度。这是一种用于确定程序复杂性的度量。它通过方法测量独立线性路径的数量。这些是由条件分支的数量和复杂性决定的。当我们的圈复杂度较低时,这意味着该方法易于阅读、理解和测试。

持续集成和持续交付的优势

到目前为止,我已经讨论并介绍了 CI 和 CD 之间的区别。这两种实践都有一些成本和收益(见表 3-1 ),我们在采用它们的实践时必须考虑这些成本和收益。

表 3-1

CI 和 CD 的成本和收益

|

实践

|

成本/变化

|

利益

| | --- | --- | --- | | 连续累计 | 开发人员必须使用 TDD 实践来编写代码。代码必须与单元测试相关联。 | 因为我们测试每一个版本,我们可以减少我们在生产中发布的 bug 数量。 | |   | 我们需要安装一个新的 CI 服务器,它必须用于监控回购,并在每次提交时开始构建。 | 一个 bug 很快被识别,这意味着它可以很快被修复。这加快了修复速度,当然也节省了资金 | |   | 开发人员必须每天至少集成一次软件,这使得集成不会有太大的差异。 | 软件每天至少集成一次,这意味着我们每天至少可以发布一次软件。 | |   | 我们对每一个构建的反馈必须到位。 | 有了 CI 服务器,我们可以减少测试时间,因为我们可以并行执行更多的测试,然后减少完成测试的时间。 | |   |   | QA 团队可以减少测试单个功能所需的测试数量,这意味着我们可以花更多的时间来提高软件测试真实场景的质量。 | | 持续交付 | 对于 CD,我们必须有强大的 CI,因为 CD 是 CI 的延伸。 | CD 减少了交付软件的时间,因为交付发生在每次过程结束的时候。 | |   | 我们必须自动化部署软件的所有过程,并消除人工交互。 | 我们可以提高发布的数量,潜在地提高到每天一个以上的发布,而不是每六个月一个大的发布。这加快了客户提供的反馈,可以推动我们的发展。 |

从上表中我们可以看出,这两种实践都需要一些改变。这些变化与开发人员编写代码的方式以及它与基础设施的连接方式有关。

主要成本实质上是创建和维护 CI 服务器,因为我们必须为我们添加的每个新功能配置它。我们可以通过创建一个 Jenkinsfile 来降低这个成本。这是 Jenkins 的一项功能,允许我们为 CD 创建一个流水线。使用 Jenkinsfile,我们可以自动化并在存储库中存储我们的流水线流程。

设计持续集成和持续交付系统

为了建立一个完整的 CI 和 CD 系统,我们必须对我们的基础设施和架构进行一些更改。架构的变化与软件本身没有直接联系,更多的是我们生产和发布软件的方式。

我们必须做的第一个改变是如何编写代码。开发人员必须开始为我们发布的每个类或功能编写单元测试,但是为了真正有效,我们必须使用 TDD 技术。这是因为,否则,我们冒着编写测试来通过代码的风险,而不是测试我们想要实现的需求,这会降低 CI 的好处。

我们必须发起的另一个变化是迫使开发人员尽快集成软件——至少每天一次。否则,我们可以花更多的时间来集成软件和测试阶段,而修复一个 bug 可能需要很长时间。

我们还必须制定一些关于准则的规则。我们可以实现一个代码检查系统,使用像脸书开发的推断这样的工具,它可以检查多种语言,比如 C、C++和 Java,并生成一个报告指出一行代码有错误。这有助于提高软件的质量,减少潜在的错误。其他工具,如 PEP8 或 RuboCop,使用特定的语言,通常用于强制执行一些规则,这些规则涉及方法的复杂性、方法中执行的操作数量、方法或类的行数以及代码行的长度。这些规则有助于代码的可读性和可维护性。这不会直接提高代码的质量,但是有助于减少代码所需的维护时间。

我们必须为 CI 系统以及随后的 CD 系统做出的最重要的改变是构建软件的自动脚本。这个脚本必须用来产生一些自动操作来编译和构建软件,更重要的是,必须能够从命令行启动。

要创建脚本,我们可以使用 Maven、Ant 或 MSBuild 等软件,也可以使用 Bash 或 PowerShell 中的简单命令行脚本。语言并不重要,但我们必须有一些东西,每次在相同的条件下构建时,我们都可以从这些东西开始,并且总是产生相同的结果。

CI 服务器中最重要的变化是 CI 和 CD 实践的构建块。有很多应用可以用于此目的。有些是免费的,如 Jenkins,其他人需要专业使用许可证,如 Travis CI。

创建一个良好的 CI 和 CD 系统,必须坚持一些原则。

  • 经常提交代码。对代码的每一个小的更新都必须被提交和测试。

  • 不要用提交来破坏代码。在第一次提交时,执行本地构建和测试,因为我们提交的代码不会停止 CI 的循环。

  • 开发单元测试。每个提交都必须与一个强单元测试相关联,因为我们必须测试来验证代码。

  • 创建用于自动构建软件的脚本。我们必须减少人工交互,这意味着我们必须为构建软件创建一个脚本,并确保它每次都能工作,从而为我们的系统提供所需的一致性。

  • 为不同的环境构建软件。使用 CD 系统,软件开发有不同的阶段。通常,我们有一个开发服务器,一个试运行/测试服务器,以及一个或多个生产服务器。每个环境都有不同的特征,当然还有连接参数。我们必须创建一个系统来在这些环境中构建软件。

  • 为软件发布设计流水线。为了提高质量,我们必须创建一个在每个阶段自动提升软件的脚本。

  • 设计一个在每个阶段发布软件的策略。因为涉及到不同的阶段,我们必须为软件发布设计一个策略和架构。例如,当软件在开发中构建并且测试通过时,这必须在这个阶段进行推广。通过这样做,我们可以轻松地创建一个 Docker 映像,在注册表中发布,并通过软件进行编排,在 stage 中发布。

这些原则是一个好的 CI/CD 系统的基础。当然,它们必须适应特定的公司需求,但是,总的来说,如果我们遵循这些原则,我们一定会减少人与人之间的互动,并建立一个良好的 CI/CD 系统。

构建持续集成和持续交付流水线

要建立一个好的 CI 和 CD 系统,我们必须创建一个流水线。有了流水线,我们可以定义构建软件的必要步骤,并最终将它发布到产品中。

当我们构建软件时,我们可以识别不同的阶段。每个阶段负责软件的特定验证。基本流水线由三个阶段组成。

  1. 发展

  2. 脚手架

  3. 生产

当我们定义一个流水线时,我们实际上创建了一个系统,当某个条件具备时,将软件从一种状态提升到另一种状态。这个过程必须以编程方式管理,以便可以轻松地更改/更新,并减少人工交互。今天,有很多软件我们可以使用,例如,GoCD、Travis CI、GitBucket、Circle CI 和 Jenkins。

所有这些软件都可以用来可视化地创建一个流水线,其中包含我们想要的软件的不同阶段。它大部分支持某种类型的脚本语言。拥有一个流水线脚本是很重要的,因为我们可以将脚本保存在软件仓库中。如果我们必须创建另一个环境,我们只需下载脚本。

为了在 Jenkins 中创建这样的脚本,我们使用 Jenkinsfile。有了这个文件,我们可以为我们的流水线定义所有我们想要的步骤,当然,使用它来将软件从一个阶段提升到另一个阶段。下面是一个 Jenkinsfile 的例子(清单 3-1 ):

pipeline {
    agent any

    stages {
        stage('Build') {
            steps {
                echo 'Building..'
            }
        }
        stage('Test') {
            steps {
                echo 'Testing..'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying....'
            }
        }
    }
}

Listing 3-1A Basic Example of a Jenkinsfile

从文件中,我们可以定义不同的阶段和不同的代理,这对我们的 CI/CD 系统很重要。不同的阶段可以有不同的工作参数。比如系统访问密码的 DB 连接。在阶段部分,我们可以准备我们的系统来获取这个值,并在不同的阶段改变响应行为。并非每个 CI 系统都只有三个阶段,但它们是一个很好的起点。

我们 CI/CD 系统的另一个重要部分是反馈系统。这实际上是一封发送给用户的关于构建状态的电子邮件。这条消息非常重要,因为开发人员可以做出反应并修复构建中任何损坏的部分。电子邮件可以非常简单。它必须只包括版本号、发生的错误和失败的测试。这些信息有助于开发人员识别问题并更快地修复它们。如果 n-build 无法抄送给团队领导,系统必须上报电子邮件。

持续数据库集成

当我们发布软件时,通常我们有一个数据库,当我们改变软件时,我们在其中存储数据。这可能与数据结构的变化有关,例如,一个新表。在这种情况下,重要的是建立一个持续的数据库集成,通常称为数据库迁移

持续数据库集成是指每次我们发布软件时,发布和重建所有数据库并用新数据填充数据库的过程。

遵循这个过程可以确保我们有一个总是与最后一个代码保持一致的数据库,当然,也提供了一组新的测试数据。这个过程的另一个好处是,它可以在我们每次发布软件时识别数据库的任何问题。为了利用这个过程,我们可以为数据操作语言(DML)和数据定义语言(DDL)创建和维护脚本。这个脚本必须存储在软件 repo 中,就像系统中的每一段代码一样,然后由 DBA 团队审查批准或拒绝。

要采用这一流程,我们可以在构建过程中遵循以下简单步骤:

  1. 删除整个数据库。这样,每次我们构建软件时,我们都有一个全新的数据库。

  2. 在代码报告中维护 DML 和 DDL 脚本。创建数据库的脚本必须在代码报告中,并在我们每次发布软件时集成。

  3. 有一个重新创建数据库的阶段。因为我们不断地集成数据库,所以我们的流水线必须有一个创建和维护数据库的阶段。

  4. 对 DML 和 DDL 脚本进行代码审查。DBA 团队必须意识到我们在数据库中进行的任何更改,这样我们就可以轻松地识别任何相关的问题。此外,我们必须进行代码审查,以确保更新不会破坏数据库。

  5. 确保测试数据总是对齐的。因为我们可以改变数据库结构,所以我们必须调整测试数据以反映我们在数据库中所做的改变。

当我们自动化这个过程时,我们可以简单地通过调用重新创建数据库的过程来容易地修复数据的任何问题。这保证了一组数据总是正确的,并消除了与系统中错误数据相关的问题的可能性。当然,与 CI/CD 中的每个过程一样,这必须适应您的系统。不是所有的系统每次都有完整的数据库版本。这必须是一个主要基于你正在工作的系统的决定。

注意

数据库迁移并不总是简单的。在某些情况下,这个过程可能非常危险。例如,如果您在金融部门工作,您不希望每次发布软件时都销毁并重新创建数据库。

使用现代开发框架,如 Rails。网等。,我们可能使用对象关系映射(ORM)。这通常包括一个称为代码优先的过程,这意味着我们创建代码,并在此基础上生成数据库。在生产中,不建议这样做。在这种情况下,采用持续的数据库集成有助于保持对数据库的持续控制。

连续测试和检查

一个公司想要通过建立一个 CI 和 CD 系统来实现的主要目标是提高发布软件的质量。为确保这一点,良好的 CI 和 CD 系统必须具备持续测试和持续检查。

持续测试的范围是在每次发布时创建可靠的软件。为了实现这一点,我们创建了不同类型的测试,这些测试可以在每次构建软件时自动执行。

我们自动化的第一种测试是单元测试。每次我们编译和构建软件时,都必须执行这个测试。这个测试是测试软件发布和质量的第一步。单元测试还可以检查我们软件的代码覆盖率。代码覆盖率对于理解我们软件的代码行被覆盖和测试的百分比是很重要的。

代码覆盖率的百分比没有具体的最佳数字,但是好的代码覆盖率被认为在 80%到 90%之间。清楚的是,要编写一个好的单元测试,要有效地测试功能,而不仅仅是实现代码覆盖率。代码覆盖到位的原因主要有两个。

  • 它提高了单元测试的质量,确保在测试阶段覆盖更多的代码和拦截更多的错误。

  • 它让我们确信,当我们开发一个新的特性时,我们不会在生产环境中发布一个新的 bug。

单元测试只是我们测试系统的第一步。我们必须在系统中包括的另一个测试是集成测试。这种类型的测试旨在用真实的组件测试软件。这个测试阶段发生在单元测试之后,并使用真实数据来执行它。在单元测试期间,我们可以模拟一些数据。例如,当我们必须与外部 web 服务通信时,集成测试将不同的组件组合在一起,并一起测试所有的软件。

注意

集成测试是持续集成的一个重要阶段,因为它负责测试整个系统,而不仅仅是我们开发的代码。当我们开始集成测试时,我们实际上将不同的软件放在一起,并删除测试中使用的任何模拟库。在这一阶段,我们主要使用真实数据来测试系统,看看它是否能与新软件配合使用。

测试的最后阶段由验收测试验证测试组成。这个测试阶段由 QA 工程团队设计,从用户的角度测试系统。这意味着在有接口的情况下,测试本质上是在接口上设计的。这个测试阶段的目标是验证用户需求并确认它们。在这个阶段,我们已经建立了测试金字塔,可以很容易地使用它进行测试。

除了不同的测试阶段,另一个重要的阶段是代码检查。这个阶段是对代码的检查,使用一组规则来生成关于软件本身的报告。代码检查可以分为两个不同的阶段。

  • 代码审查:这个阶段在最终集成之前就已经存在了。

  • 静态代码分析:这个阶段发生在我们集成软件的时候。

代码审查是代码检查的第一阶段。在代码审查期间,代码必须首先得到另一个开发人员的批准,才能集成到主分支中。团队的其他成员审查代码并留下反馈。代码开发人员记下笔记,根据评论调整代码,并要求再次评审。当所有的注释都被处理后,代码可以被批准并最终合并到主分支中。

静态代码分析由两个不同的阶段组成。第一种情况发生在开发人员执行本地提交时。在这个阶段,代码可以通过一些规则进行验证。例如,这些规则检查以下内容:

  • 方法的复杂性

  • 每种方法的行

  • 每行的字符数

  • 对方法或类的注释

有更多的工具可用于进行这种分析,例如 Python 中的 PEP8,不同的语言可以应用不同类型的规则。

我们对代码进行的另一种类型的分析是静态代码分析。这种分析的目的是突出与代码直接相关的问题。有不同的工具可以做到这一点,但它通常是由自动化工具执行的。这对于识别代码中可能出现的潜在运行时错误并在发布到产品之前修复它们是很重要的。当所有的分析和测试都被执行后,代码就可以最终构建并准备发布了。

准备发布版本

CI/CD 系统的最后一步是为构建准备发布。这遵循一些简单的规则。

  • 识别存储库中的代码。

  • 创建构建报告。

  • 将构建放在共享位置。对于大多数现代软件来说,我们可以为我们的工件建立一个联系,允许我们在每个系统中重建软件。

这些基本规则可以用来确定软件是否已经准备好进行构建。一个现成的构建可以更快地发布到产品中,或者在我们的 QA 环境中。

识别存储库中的代码

识别存储库中的代码对于理解我们何时有一个生产就绪的构建非常重要。我们可以用不同的方法识别代码。

  • 在 repo 中创建一个标签:识别 repo 中最后一个构建代码的最快方法就是简单地识别代码。

  • 标记代码:一种更复杂的识别代码的方式是标记。这意味着在 repo 中创建一个标记,其值用于标识版本。

  • 创建分支:另一种识别代码的方法是创建一个新的分支。这类似于标记技术,只是我们使用了分支。

为了识别代码,我们必须创建具有唯一名称的软件。要创建名称,我们可以使用如下命名约定:

PracticalDevOpsGP.1.1.0

我们创建的命名约定使用以下语法: <特性>。<重大发布>。<次要发布>。<构建编号> 。我们在构建的时候,本质上只改变了编号的最后一个版本,比如 PracticalDevOpsGP.1.1.1,1.1.2 等。与所有其他功能一样,这必须由 CI/CD 系统以自动方式创建。

创建构建报告

构建报告包括我们在构建之后发布的重要信息。该报告必须考虑技术人员以外的工作人员,以便报告中的内容能够被普遍理解。

为了使构建报告有效,它们必须包括以下信息:

  • 版本的名称

  • 发布的功能

  • 从哪里获得发布

该报告可以采用自动生成消息的形式,由 CI/CD 系统生成,并在每次发布就绪时发送给特定的用户列表。在构建过程中可以很容易地包含该字段。最棘手的部分可能是发布的特性,但与此相关的数据可以通过简单地将 CI 系统与维护和设计软件的系统连接起来来收集。例如,通过将 Jenkins 与吉拉联系起来,我们可以确定我们还在努力联系什么任务。这样,当 Jenkins 收到构建的代码时,它可以包含特性的描述,这可以添加到我们的报告中。

报告可以用来识别特性,当然,QA 团队也可以用来识别计划发布的特性和实际发布的特性之间的差异。

将构建放在共享位置

当我们完成构建时,我们必须与其他团队共享它。我们把构建放在哪里取决于我们用来发布软件的策略。

例如,如果我们发布一个 WAR 或 MSI 文件,我们可以将软件直接放在共享服务器中。例如,如果我们想要创建一个 Docker 映像,这个映像必须在一个内部注册表中发布,该注册表用于检索要构建的最后一个映像。

我们必须记住一个非常简单的概念:构建的不变性。当 QA 测试一个特定的构建版本并验证它时,我们必须发布 QA 中使用的构建版本。系统不需要进行另一次构建;它只是使用 QA 中传递的文件进行发布。

发布构建

发布构建是我们 CI/CD 系统的最后一个阶段。构建版本并不仅仅用于生产,而是可以用于限制服务器的数量,与生产服务器镜像,一些客户可以在其上尝试新功能。这种类型的服务器被称为金丝雀服务器

注意

Canary 服务器限制允许客户使用某项功能的服务器数量。canary 服务器的使用是为了减少产品中潜在的错误,并获得关于发布的真实反馈。因为 canary 服务器与产品发布中使用的服务器相同,所以我们可以使用它来获得关于软件质量的反馈。例如,我们可以看到软件如何在实时环境中工作,并拦截生产中出现的内存泄漏和其他错误。

另一个重要的考虑是,当我们在云中发布 SaaS 时,我们如何发布。我们不希望客户在使用软件时受到任何干扰。要做到这一点,我们必须确定发布软件的具体方式,并确保软件本身的可靠性。

在其他情况下,我们可以安排软件维护窗口。在特定的时间,我们实质上停止了软件的功能,并发布了软件本身的新版本。

注意

有了 CD,我们不必每次都在产品中发布软件。这个特性是 CD 的一部分,了解这一点很重要。

为了在不中断功能的情况下发布软件,我们可以通过一些特定的过程来发布它。最常用的包括

  • 蓝色/绿色部署

  • 金丝雀部署

  • 增量部署

这些技术的主要目标是不中断软件的功能,并尽可能快地拦截基础设施和软件的潜在问题。

蓝色/绿色部署

蓝/绿部署是一种降低停机风险的软件发布技术。它被称为“蓝色/绿色”,因为我们发布了两个生产环境:一个称为蓝色,另一个称为绿色。

使用蓝/绿部署,我们只有一个实时环境。光盘系统在非实时环境中发布软件的新版本。当软件准备好并经过测试后,它将被安装在另一个环境中,然后切换到生产环境中。对于蓝/绿部署,我们本质上有两个相似的环境,我们只需在两者之间切换(参见图 3-6 )。

img/464715_1_En_3_Fig6_HTML.jpg

图 3-6

蓝色/绿色部署

蓝/绿部署有一些好处,也有一些代价。借助蓝/绿部署,我们可以在出现错误的情况下轻松回滚环境,因为我们始终有一个可用于生产的环境。

成本与我们必须牢记的一些建筑设计有关。第一个问题涉及数据库。当我们发布软件时,在进行蓝/绿部署之前,我们可能需要修改表格。首先,我们必须释放数据库。当我们发布了数据库之后,我们就可以切换环境了。

我们必须记住的另一个要点是用户会话和软件可以使用的其他数据。我们必须有一个环境共用的缓存,以便不丢失这些信息,并允许毫无问题地使用这些信息。

金丝雀部署

金丝雀部署旨在降低与释放相关的风险。我们在基础设施的一小部分中发布软件,这意味着只有很小比例的客户会被这个版本所影响。如果失败,我们可以很容易地回滚发布。该版本旨在增加用户数量。我们在一定时间后递增用户数量,这样就不会达到 100%(见图 3-7 )。

img/464715_1_En_3_Fig7_HTML.jpg

图 3-7

金丝雀部署

这种类型的部署可以与蓝/绿部署相关联。区别在于我们如何切换基础架构。我们创建我们的新环境,当我们满意时,我们开始释放新服务器中的用户子集。

Canary deployment 用于从有限数量的用户那里提供关于部署的即时反馈。这有助于在没有完全回滚的情况下识别和解决问题,因为我们只向有限数量的用户发布。在由于任何问题导致回滚的情况下,我们可以只向少数服务器发布。

canary 部署的另一个好处与用户数量的缓慢增长有关。当我们发布新功能时,为了分析内存的使用情况和与功能相关的其他问题,用户的缓慢增加是首选。同时,允许为软件创建特定的监控值。

增量部署

当我们希望生产中只有一个硬件时,使用增量部署(图 3-8 )。通过这种技术,我们一次只发布给一定比例的用户,例如,5%的用户。当我们对第一个版本感到满意时,我们转向另一组用户。

img/464715_1_En_3_Fig8_HTML.jpg

图 3-8

增量部署流程

增量部署过程仅用于一种硬件。这是因为一次只使用软件的一小部分。这种类型的部署的好处与我们发布的少量服务器有关。

因此,我们可以更好地监控特性,立即识别软件的任何问题,并调整基础设施或代码来解决问题。

结论

在这一章中,我讨论了 CI 和 CD。这两种技术都是 DevOps 的核心,也是云部署的基础。

CI 和 CD 是两种紧密联系的实践。这是因为一个是另一个的进化。重要的是要花时间来充分理解这些实践,以便正确地实施它们。

这两种实践都要求我们开发软件和设计软件的方式发生彻底的改变。我们必须对开发者施加一些压力,让他们遵循 CI 和 CD 的指导方针。

另一方面,一个好的 CI 和 CD 系统提高了我们软件的质量,并有助于将发布增加到一年一两次以上。

四、Docker 和 Kubernetes 容器化

今天,当我们想到云时,我们会想到容器化。容器化可以被看作是虚拟化的发展。借助虚拟化,我们通常会重新创建一个完整的操作系统(OS ),并将其托管在主机上。

使用一个容器软件,比如 Docker,可以创建我们应用的完整映像,并通过公共注册表发布。为了管理和发布这个映像,我们可以使用称为容器编排器的软件,比如 Kubernetes。

当我们采用 CI 和 CD 的实践时,使用 Docker 之类的容器和 Kubernetes 之类的 orchestrators 有助于加速自动发布过程,同时,我们可以有一个强大的回滚策略。

Docker 简介

Docker 可能是最著名的容器化软件。它提供了一个称为操作系统级虚拟化的虚拟化级别。这就是所谓的容器化。

这种类型的隔离允许我们在一个操作系统中运行多个操作系统。例如,可以用 Red Hat 在 Ubuntu Linux 中创建一个容器。容器和虚拟机(VM)之间有一个重要的区别,那就是容器不需要完整的操作系统来运行。当我们创建一个虚拟机时,我们实际上是重新创建一个完整的操作系统。当我们创建一个容器时,我们得到的只是操作系统的一部分。这将缩小映像的大小。当我们谈论虚拟化时,我们可以识别两种类型:基于虚拟机管理程序的虚拟化操作系统虚拟化。在基于虚拟机管理程序的虚拟化中,系统模拟硬件。这意味着我们可以重新创建网络、硬盘驱动器(HDD)等。在操作系统虚拟化中,虚拟化是在操作系统级别进行的。主机将每个容器彼此隔离,特别是,主机隔离每个容器的文件系统,但是它们在单个主机中运行。因为容器具有文件系统隔离的 OS,所以容器失去了灵活性。您只能在同一主机上运行容器。例如,不可能在 Linux 主机上运行 Windows,因为操作系统虚拟化是由系统运行的。Windows 和 Linux 有两种不同的操作系统内核和文件系统结构,这不允许 Windows 在 Linux 上运行。

与虚拟机管理程序虚拟化相比,容器的安全性较低。这是因为当我们创建一个 Linux 容器时,我们使用 libcontainers。这些访问五个基本的名称空间——网络、进程、挂载、主机名和共享内存——但是,例如,不使用 SELinux、cgroups 和其他用于增强操作系统安全性的库。这意味着恶意软件阻止执行和操作的可能性更大。相反,容器是一个孤立的环境。这意味着在容器被破坏的情况下,不太可能在主机系统上产生问题,因为容器不与主机操作系统共享任何东西。

Docker 容器创建复杂,难以维护和自动化。Docker 本质上是一款基于系统操作虚拟化的软件,旨在创建其他软件。Docker 在虚拟化容器的顶部添加了一个应用部署。使用 Docker,很容易创建一个类似于生产环境的完整的运行时环境,这可以加快开发过程。

Docker 帮助开发人员消除软件在生产中开发和发布时可能产生的差异。这是因为当我们在生产中发布软件时,Docker 容器在用于开发软件的相同 OS 配置中工作。

使用 Docker,我们可以轻松地为 CI 和 CD 创建我们的流程,因为当我们提交代码时,我们可以直接创建和编译 Docker 映像,并在测试中发布它。

从架构的角度来看,使用 Docker,很容易实现微服务架构。这是因为任何容器都可以是应用的一个部分,我们可以独立于另一个部分来管理这个部分。

Docker 由以下不同的组件组成:

  • Docker 引擎是一个客户端-服务器应用。客户端与服务器应用(称为守护程序)进行通信。守护进程负责执行容器。客户端和守护程序可以在同一台机器上,也可以在不同的机器上。

  • 映像是我们 Docker 架构的基础。映像用于启动我们的容器,我们可以从另一个基本映像开始创建个人映像。

  • 注册表是存储映像的地方。我们可以识别两种类型的注册中心:私有的和公共的。可以通过地址 https://hub.docker.com 联系公共注册表。这是官方的 Docker Hub 注册表。在这里,我们可以创建一个帐户,并开始在注册表中存储我们的映像。

  • 容器基本上是一个被执行的映像。一个容器内部可以有多个正在运行的进程,这取决于映像的设计和创建方式。容器本质上是一个轻量级的独立应用。我们可以组合多个容器来执行一个复杂的应用。

为什么要用 Docker?

Docker 越来越受云的欢迎,因为当我们必须构建 PaaS 云时,它非常有用。原因很简单。容器的概念允许我们创建一层不同的隔离容器,其中包含一个应用。我们可以轻松构建特定于单个客户的 PaaS。这允许在与我们的 PaaS 设计的发布相关的所有方面有很大的灵活性。

Docker 还可以用于

  • 为开发人员创建一个类似于生产环境的环境。Docker 可以拥有与生产中使用的相同的软件栈。

  • 创建面向服务或微服务架构的构建块。使用 Docker,我们可以轻松地隔离应用,并使用它来构建我们的微服务或面向服务的架构(SOA)。

  • 创建 CI 和 CD 系统。我们可以使用 Docker 来隔离应用,并在测试环境甚至生产环境中轻松部署。

  • 为测试和实验创建独立的轻量级应用环境。

这些只是采用 Docker 的几个例子。越来越多的公司采用 Docker 来加速发布或部署过程。现代集成开发环境,如 Visual Studio 或 IntelliJ,现在有一个插件来“dockerize”我们的应用。这使得开发人员每次构建软件时都可以轻松地创建 Docker 映像。

在 GCP 中使用 Docker

学东西最好的方法就是把手弄脏,所以在谷歌云平台(GCP)学习 Docker 最好的方法就是使用它。

有了 Docker,我们可以创建我们的 SaaS 和 IaaS,因为我们可以用它来提供我们的基础设施。这是因为我们可以创建一个容器,例如,为我们的数据库,和我们的应用,例如,我们的基于 Java 或 Ruby 的网站。在 GCP,我们可以使用谷歌计算引擎创建 Docker 容器。首先,要理解如何创建和使用 Docker,理解它是什么以及如何在 Compute Engine 中创建实例是很重要的。

谷歌计算引擎简介

谷歌计算引擎是 GCP 提供的 IaaS 组件。Google 计算引擎由三个基本组件组成:

  • 虚拟计算机

  • 电路的组成部分

  • 永久磁盘

使用 Google Compute Engine,我们可以创建一个工作流,用于从单个实例扩展到全球分布式实例。当我们创建 Google 计算引擎时,我们可以选择 CPU、内存和空间方面的任何配置。谷歌计算引擎也使得使用预定义的实例成为可能。这些是

  • 标准:这种机器每个 CPU 3.75 GB 内存。我们可以选择多达 8 种处理器配置,从 1 到 96。我们可以使用的永久磁盘的最大数量是 16 个。这种配置非常适合需要在 CPU 和内存之间取得良好平衡的任务。

  • 高内存:这种配置每个 CPU 6.5 GB 内存。我们可以选择最少 2 个 CPU,最多 96 个。我们可以使用的永久磁盘的最大数量是 16 个。当需要比 CPU 更多的内存时,这种配置是理想的。

  • 高 CPU :这种配置每个 CPU 有 0.90GB 内存。我们可以选择最少 2 个 CPU,最多 96 个。我们可以使用的永久磁盘的最大数量是 16 个。当需要比内存更多的 CPU 时,这种配置是理想的。

注意

当我们在计算引擎中创建新的持久磁盘时,有一些限制。永久磁盘的最大大小为 64TB。如果我们必须创建一个更大的磁盘,我们必须创建一个持久磁盘集群。每个实例最多可以连接 16 个磁盘。有一个测试功能,例如,在持久磁盘的限制可以连接到每个实例。对于共享核心,只能连接 16 个实例。对于 1 个 CPU,最多可以连接 32 个磁盘。对于 2 到 4 个 CPU,最多可以连接 64 个磁盘。对于 8 个或更多 CPU,最多可以连接 128 个磁盘。

Google 计算引擎的核心组件是一个实例。这是一个托管在谷歌基础设施上的虚拟机。

可以在 Windows 服务器上创建 Google 提供的基于 Linux 的实例,也可以通过运行 Docker 映像来创建或导入自定义映像。在这种情况下,Google 为运行 Docker 映像提供了一个优化的操作系统。这个操作系统基于 Chromium 操作系统。

创建计算引擎实例

要创建一个新的计算引擎,我们可以通过命令gcloud compute使用 Google SDK,也可以使用控制台。要创建实例,我们必须遵循以下两个步骤:

  1. 配置gcloud

  2. 选择地区和区域。

第一步,配置gcloud,创建 OAuth2 来验证和访问资源。要使用gcloud compute,首先我们必须配置 Google SDK 来创建授权所需的令牌。

为了配置 Google SDK,我们必须打开命令行,使用命令gcloud init。这使用默认配置,并且适用于大多数配置。打开 Google SDK 并运行命令gcloud init,它会给出以下输出:

Welcome! This command will take you through the configuration of gcloud.

Settings from your current configuration [default] are:
core:
  account: pierluigi.riti@gmail.com
  disable_usage_reporting: 'False'
  project: practicaldevopsgcp-197023

Pick configuration to use:
 [1] Re-initialize this configuration [default] with new settings
 [2] Create a new configuration
Please enter your numeric choice:

在这种情况下,选择数字 1,然后按 Enter 键。我们需要这个额外的输出:

Your current configuration has been set to [default]

You can skip diagnostics next time by using the following flag:
  gcloud init --skip-diagnostics

Network diagnostic detects and fixes local network connection issues.
Checking network connection...done.
Reachability Check passed.
Network diagnostic (1/1 checks) passed.

Choose the account you would like to use to perform operations for
this configuration:
 [1] pierluigi.riti@gmail.com
 [2] Log in with a new account
Please enter your numeric choice:

选择我们要用于配置 Google SDK 的邮件。我们可以使用登录时的用户身份或登录新帐户。

You are logged in as: [pierluigi.riti@gmail.com].

Pick cloud project to use:
 [1] practicaldevopsgcp-197023
 [2] Create a new project
Please enter numeric choice or text value (must exactly match list
item):

选择我们要使用的项目或创建一个新项目。在这种情况下,创建一个新的,名为practicaldevopsgcpcli。要创建它,请选择选项 2,它将给出如下所示的输出:

Please enter numeric choice or text value (must exactly match list
item):  2

Enter a Project ID. Note that a Project ID CANNOT be changed later.
Project IDs must be 6-30 characters (lowercase ASCII, digits, or
hyphens) in length and start with a lowercase letter.

插入实例的名称,然后按 Enter 键。

Your current project has been set to: [practicaldevopsgcpcli].

Not setting default zone/region (this feature makes it easier to use
[gcloud compute] by setting an appropriate default value for the
--zone and --region flag).
See https://cloud.google.com/compute/docs/gcloud-compute section on how to set
default compute region and zone manually. If you would like [gcloud init] to be
able to do this for you the next time you run it, make sure the
Compute Engine API is enabled for your project on the
https://console.developers.google.com/apis page.

Your Google Cloud SDK is configured and ready to use!

* Commands that require authentication will use pierluigi.riti@gmail.com by default
* Commands will reference project `practicaldevopsgcpcli` by default
Run `gcloud help config` to learn how to change individual settings

This gcloud configuration is called [default]. You can create additional configurations if you work with multiple accounts and/or projects.
Run `gcloud topic configurations` to learn more.

Some things to try next:

* Run `gcloud --help` to see the Cloud Platform services you can interact with. And run `gcloud help COMMAND` to get help on any gcloud command.
* Run `gcloud topic -h` to learn about advanced features of the SDK like arg files and output formatting

最后,我们可以登录控制台,在选择项目的部分,我们可以看到创建的新项目(图 4-1 )。

img/464715_1_En_4_Fig1_HTML.jpg

图 4-1

用命令行创建的新项目

该命令用于创建新项目。现在我们已经设置了项目,下一步是创建新的实例。因为我们有两个项目,所以我们必须设置默认项目。

gcloud config set project practicaldevopsgcpcli
Updated property [core/project].

这将主项目配置为我们刚刚创建的新项目。要使用命令行,我们必须为项目启用 API。对于链接,只需在控制台上创建以下命令:

gcloud compute instances create example-instance --zone us-centra1-f

因为没有启用 API,所以输出为

ERROR: (gcloud.compute.instances.create) Could not fetch resource:
 - Project 377223342623 is not found and cannot be used for API calls. If it is recently created, enable Compute Engine API by visiting https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=377223342623 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.

要启用 API,只需获取链接并将其放入浏览器,然后启用 API。当 API 启用时,控制台显示如图 4-2 的选项。

img/464715_1_En_4_Fig2_HTML.jpg

图 4-2

用于创建新计算引擎的控制台

我们最终可以使用gcloud命令创建实例。

gcloud compute instances create practicaldevopsgcp --zone us-east1-b

输出是

Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcpcli/zones/us-east1-b/instances/practicaldevopsgcp].
NAME                ZONE        MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP   STATUS
practicaldevopsgcp  us-east1-b  n1-standard-1               10.142.0.2   35.227.53.98  RUNNING

这个实例终于被激活了。结果显示了具有内部和外部 IP 的实例的访问信息。可以检查在控制台中创建的实例。只需连接到仪表板并查看新实例(参见图 4-3 )。

img/464715_1_En_4_Fig3_HTML.jpg

图 4-3

可以看到计算引擎使用的仪表板

为了连接到我们刚刚创建的实例,我们必须转到计算引擎仪表板。在“连接”列下,选择连接到新浏览器。这将在新页面中打开连接(图 4-4 )。

img/464715_1_En_4_Fig4_HTML.jpg

图 4-4

选择打开实例上的连接

选择在浏览器窗口中打开,在另一个浏览器窗口中打开连接(图 4-5 )。

img/464715_1_En_4_Fig5_HTML.jpg

图 4-5

我们实例上的连接在另一个浏览器中打开

我们刚刚完成了一审。默认情况下,基本实例是用 Debian Linux 创建的。我们可以在系统中安装 Docker,或者简单地使用 Docker 的特定映像创建一个不同的实例。谷歌为 Docker 设计了四种预建映像。

  • 谷歌的容器优化操作系统:这是基于 Chromium 操作系统项目直接创建的谷歌映像。这个映像包括 Docker 和 Kubernetes。映像项目为cos-cloud,映像族为cos-stable

  • CoreOS :这是一款基于 Linux 的操作系统,专为容器化设计。它包括 Docker、rkt 和 Kubernetes。映像项目叫coreos-cloud,映像族叫coreos-stable

  • Ubuntu :这是一张基于 Ubuntu 版本 16.04 LTS 的图片,包括 LXD。映像项目为ubuntu-os-cloud,映像族为ubuntu-1604-lts

  • Windows :这是 Windows 按照容器化设计的核心版本,包括 Docker。映像项目为windows-cloud,映像族为windows-1709-core-for-containers

因为我们想为我们的系统创建一个 Docker 映像,所以我们现在基于其中一个 Docker 映像创建一个新实例。

实例组

在计算引擎中,我们可以创建一个命名的虚拟机组。当我们创建一组实例时,我们可以同时管理所有的实例。我们可以创建两种类型的实例组:托管的非托管的

当我们创建一个托管实例组时,我们使用一个实例模板。这用于创建一组相同的实例。使用托管实例,我们可以将所有实例作为一个实例来管理,这意味着当我们修改实例模板时,我们同时修改了所有实例。托管实例组有几个好处。

  • 自动扩展:当应用需要更多资源时,我们可以扩展实例以适应新的需求。

  • 负载均衡:因为所有的实例都作为一个整体来管理,所以资源在实例之间共享和平衡。当然,我们必须在此基础上创建负载均衡器以确保功能性。

  • 不健康实例的管理:当组中的一个实例停止或崩溃时,它会自动重新创建,并与前一个实例同名。

有两种类型的托管实例:

  • 一个分区管理组:所有实例都在同一个分区中。

  • 一个区域管理组:所有实例都在同一个区域。

Google 建议使用一个区域性的管理小组,而不是一个地区小组,让应用分布在不同的地理区域。将应用分布在不同的地理位置可以降低故障风险,因为不同的地理区域可能会同时出现由自然灾害或人为错误导致的故障。因此,区域管理可以提高我们软件的可用性。

Google 不推荐非托管实例,它没有托管实例的任何好处。该组由不同的实例组成,只能使用负载均衡器。只有当有一些旧的实例,并且我们想在其上使用负载均衡器时,才能使用这种方法。

GCP 中的容器应用

容器可能是发布微服务并将其投入生产的最佳方式。为了在 GCP 创建一个容器,我们可以用一个容器优化的操作系统构建一个新的计算引擎。这是一个操作系统家族,针对计算引擎中的运行容器进行了优化。比如 CoreOS 就是操作系统之一。

注意

CoreOS 是基于 Linux 内核的轻量级操作系统,旨在为集群提供基础设施。CoreOS 有一个最小的命令集,可以自动化所有与容器应用相关的工作。使用 CoreOS,可以很容易地以编程方式部署和维护容器化的基础设施,并且因为它是基于集群的,所以我们可以确保我们的应用总是可访问的。

我们现在用 CoreOS 操作系统构建一个新的计算引擎,然后在其中创建一个容器。以下是在 GCP 创建容器应用的方法之一。

  1. 连接到我们的 GCP 实例并选择项目practicaldevopsgcpcli

  2. Click the board Compute Engine, which opens the page to manage our Compute Engine instance (Figure 4-6).

    img/464715_1_En_4_Fig6_HTML.jpg

    图 4-6

    计算引擎板,摘自谷歌控制台仪表板

  3. 这将打开我们的计算引擎页面。我们现在必须基于一个容器优化的操作系统创建一个新的实例。从工具栏中选择“创建实例”按钮。

  4. In the page for creating the instance, type the name practicaldevopscontainerinstance and check the Container box (Figure 4-7). This changes the default OS from Debian to one that is container-optimized.

    img/464715_1_En_4_Fig7_HTML.jpg

    图 4-7

    创建新的容器实例

  5. 在容器映像文本框中,我们可以确定我们想要使用的 Docker 映像。对于我们的测试,我们可以使用 busybox 映像。在文本框中,我们必须插入以下字符串: gcr.io/google-containers /busybox

注意

Google 为 Docker 容器提供了一个私有注册表。这个注册表可以用来存储我们的私有 Docker 映像和创建一个新的实例。通过此链接可以看到注册表中的所有映像: https://console.cloud.google.com/gimg/google-containers/GLOBAL

img/464715_1_En_4_Fig8_HTML.jpg

图 4-8

新实例是用 Docker 创建的

  1. 单击 Create 按钮,然后使用 Docker 映像创建新的计算引擎实例(图 4-8 )。

我们可以通过单击名称来访问新实例。这将打开实例的详细信息。然后向下滚动到远程访问并点击 SSH(图 4-9 )。

img/464715_1_En_4_Fig9_HTML.jpg

图 4-9

远程访问选择

这将为带有 Docker 和我们的映像的实例打开新的浏览器窗口。我们可以看到实际安装的映像,命令为docker images。当我们在实例中执行命令时,我们得到如图 4-10 所示的输出。

img/464715_1_En_4_Fig10_HTML.jpg

图 4-10

Docker 映像命令输出

创建一个计算引擎实例,在其中创建我们的容器是一个很好的起点。Google 提供了一个私有注册中心,我们可以上传我们的图片到注册中心,并在我们的实例中使用。使用谷歌注册,我们可以把我们的 CI/CD 基本系统。例如,我们可以使用 Jenkins 创建映像并将其放入注册表中。

使用计算引擎实例来创建 Docker 映像确实有一些限制。

  • 可以在 VM 中只部署一个容器。

  • 只能使用优化的容器操作系统来创建实例。

在计算引擎中,也可以使用 Docker Hub 注册表中的映像;但是,虚拟机只能有一个容器。如果我们想设计一个微服务应用,我们可能需要在每个虚拟机上使用多个容器。为此,谷歌提供了另一项名为 Kubernetes Engine 的服务。

Kubernetes Engine 是部署和管理容器化应用的托管环境。该引擎运行 Kubernetes,这是一个旨在管理和扩展容器的开源平台。使用 Kubernetes 引擎允许使用多个容器。在开始创作之前,我们必须了解 Kubernetes 是什么。

什么是 Kubernetes?

Kubernetes,有时被称为 K8s,是由 Google 开发的开源平台,由云原生计算基金会管理。它用于管理和扩展集群中的容器化应用,如 Docker。

注意

Kubernetes 是一个非常复杂的应用。我可以用一整本书来讨论 Kubernetes,这不在本章的讨论范围之内。我只想简单介绍一下 Kubernetes 平台。

Kubernetes 定义了一些创建和管理资源的基本构件。所有组件都被设计成松散耦合的。Kubernetes 构建模块的第一个组件叫做 pod 。pod 是 Docker 容器的集合,所有容器都具有相同的 IP 地址。每个 pod 可以由一个或多个容器组成,托管在同一主机中并共享资源。每个 pod 与群集共享一个唯一的 IP 地址,并且可以通过 API 或控制器进行手动管理。

Kubernetes 中定义的另一个资源叫做标签。这些用于创建密钥对值以识别资源,例如 pod。标签没有提供任何独特性。在 Kubernetes 中,可以有多个同名的对象,并创建一个所谓的标签选择器,或选择器

使用选择器,可以识别一组对象。选择器是 Kubernetes 中对对象进行分组的核心原语。实际上,当我们想要识别一个资源时,我们可以使用两种类型的选择符,基于等式的选择符和基于集合的选择符。标签选择器可以由多个需求组成,用逗号分隔。在这种情况下,必须满足所有要求。

基于集合的标签选择器用于允许过滤,使用根据一组值的键。选择器支持三种类型的运算符:innotinexist。例如,选择器可用于仅选择一种类型的资源,如环境=生产或层(在生产阶段)。

控制器用于管理集群的状态。为了改变集群的状态,控制器管理 pod。有三种控制器。

  • 复制控制器:用于在 Kubernetes 集群中复制服务。复制控制器的另一个重要用途是在一个 pod 出现故障时启动新的 pod。它用于维持最少数量的节点运行,并确保集群的高可用性。

  • DaemonSet 控制器:当我们在集群中添加一个节点时,这个控制器负责确保所有节点运行一个 pod 的副本。此服务将 pod 添加到新节点。

  • 作业控制器:用于运行一个 pod,直到部分或全部运行成功完成。它检查 pod 的状态,当某个数量完成时,作业会自行终止。

构建模块的最后一块是服务。在 Kubernetes 中,服务是一组协同工作的 pod。例如,我们可以创建一个包含完整微服务架构的 pod,并以此定义服务。服务由标签定义和标识。这有助于识别服务本身。使用 Kubernetes,可以通过服务发现来搜索服务,并使用 IP 地址或 DNS 名称来查找我们需要管理的服务。

注意

松耦合系统是这样设计和实现的,每个服务都不知道或很少知道其他服务的定义。这些服务只为它们之间交换的数据而相互识别,并且不知道该服务是如何实现的以及是否使用了另一个服务。

Kubernetes 是一个容器编排器,用于自动化容器化应用(例如 Docker)的部署、管理和伸缩。该容器在 pod 中定义。这提供了高层次的抽象,将容器化的应用分组。pod 是每个 Kubernetes 应用的基本构建块。POD 帮助我们释放容器。我们可以释放容器,而不是一次释放一个容器。一个 pod 在定义中可以有多个容器,这有助于一次释放多个容器。

使用 Kubernetes,我们可以创建一个集群来管理系统。集群是一个集合节点。Kubernetes 集群有一个主节点和零个或多个工作节点。这意味着我们可以拥有一个只有一个主节点的 Kubernetes 集群。在 Kubernetes 中,节点是一台机器,可以在其中运行和调度 pod。机器可以是物理的,也可以是虚拟的。因为节点本质上是群集的基本块,所以每个节点都可以是主节点或工作节点。它只取决于我们为集群选择的配置。

Kubernetes 的关键组件之一是 etcd 。这是一个由 CoreOS 开发的轻量级分布式持久密钥库。该组件用于随时存储集群的状态。etcd 用于定义高可用性集群。

使用 Kubernetes 的原因可以从 DevOps 本身的性质中找到。当一家公司决定采用 DevOps 实践时,通过 Kubernetes,我们可以使用 Docker 更快地发布和部署我们的应用。

与此同时,Kubernetes 可以随着业务需要而发展。这是因为服务本身的可伸缩性。使用 Kubernetes,我们可以在集群中添加或删除一个节点,Kubernetes 负责管理我们的应用中的 pods。

使用 Kubernetes 引擎部署应用

Kubernetes 发动机在 GCP 提供了 K8s 的灵活性和动力。使用 Kubernetes 引擎,可以创建一个 Docker 容器集群来设计我们的微服务架构。现在您将看到如何使用 Kubernetes 引擎来创建和管理 Docker 容器和集群。

要访问 Kubernetes 引擎,我们必须安装另一个命令行:Kubernetes 命令行名为。这个命令行为我们提供了管理 Kubernetes 集群所需的命令。这个命令行内置在 Google Cloud Shell 中。我们可以直接从谷歌云控制台打开命令外壳。我们可以将这个控制台添加到我们的 Google SDK 中,如下所示:

*img/464715_1_En_4_Fig11_HTML.jpg

图 4-11

Kubernetes version 命令显示命令行的版本

  1. 打开谷歌云 SDK。

  2. 运行命令gcloud components install kubectl。这将开始下载并安装 Kubernetes 命令行工具。

  3. 当安装完成时,我们可以使用命令kubectl version来验证所有的安装是否正确。该命令显示安装在 Kubernetes 命令行上的版本(图 4-11 )。

可以使用谷歌控制台中的命令行。我们所要做的就是打开 GCP 控制台并点击控制台按钮。这将打开控制台底部的外壳(图 4-12 )。我们可以使用相同的命令并看到相同的输出

img/464715_1_En_4_Fig12_HTML.jpg

图 4-12

谷歌云外壳按钮

首先,要继续,我们必须确保gcloud配置了正确的项目 id 和区域。要设置项目 id,在我们的实例 practicaldevopsgcpcli 中,使用以下命令:

gcloud config set project practicaldevopsgcpcli

这个命令的输出是一个简单的行:Updated property [core/project]。这表明我们已经正确地更新了我们的项目。

我们需要更新的下一个属性是默认计算区域。这样,我们就可以准确地在我们想要的地方创建集群。为此,我们可以使用以下命令行:

gcloud config set compute/zone us-east1-b

这个命令的输出是一个简单的行:Updated property [compute/zone]。这表明我们已经正确更新了计算区域。

现在我们已经设置了区域和代码项目,我们可以开始创建我们的 Kubernetes 集群了。在 Kubernetes 中,一个集群至少是一台服务器。该节点实际上是计算引擎虚拟机,我们在其上运行 Kubernetes 流程,作为集群的一部分。第一步是在gcloud中创建集群。

gcloud container clusters create practicaldevopsgcpcluster

注意

我们第一次执行这个命令时会得到一个错误,因为我们没有配置 Kubernetes API。我们必须从以下链接进入: https://console.cloud.google.com/apis/api/container.googleapis.com/overview?project=practicaldevopsgcpcli

该命令开始创建 Kubernetes 集群,这可能需要几个小时才能完成。最后,我们可以在控制台中看到集群。操作的结果是一行,告诉我们集群可以使用了。我们可以导航到页面。默认集群默认有三个节点。节点中使用的映像是容器优化的操作系统。基本映像是由 Google 为容器创建的。机器的大小是 n1 标准,相当于 1 个 CPU,3.75GB 内存。这基本上足以满足 Kubernetes 的基本使用。

https://console.cloud.google.com/kubernetes/workload_/gcloud/us-east1b/practicaldevopsgcpcluster?project=practicaldevopsgcpcli

要查看我们集群的实例,如果所有完成的操作都是好的,您将看到类似图 4-13 的内容。

img/464715_1_En_4_Fig13_HTML.jpg

图 4-13

Kubernetes 引擎集群创建了

集群准备就绪后,我们需要做的是获取访问集群的凭证。要获取凭证,我们必须使用以下命令:

gcloud container clusters get-credentials practicaldevopsgcpcluster.

该命令的结果如下:

Fetching cluster endpoint and auth data.
kubeconfig entry generated for practicaldevopsgcpcluster.

我们现在准备好部署我们的集群了。对于我们的例子,我们使用一个 Google 就绪的应用。目前的目标是部署在 Kubernetes。这个应用叫做 hello-server,是一个简单的 Go 服务器应用。

部署我们的第一款 Kubernetes 应用

如您所见,Kubernetes 创建了一个集群,并允许我们从一个单点管理它。当我们在 Kubernetes 中创建集群时,我们部署在主节点上,而主节点部署在每个节点上。

这种行为由 etcd 管理。当主节点看到节点上的不同状态时,etcd 是用于记住集群状态的键值。节点中的守护进程安装应用并对齐每个节点。服务维护通常一次完成一个节点。这意味着当一个节点正在安装或更新时,另一个节点是可访问的。为了在集群中安装应用,我们可以使用kubectl命令行启动以下命令:

kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080

这个命令非常简单,它采用服务器的名称、映像参数和发送到 Docker 的端口作为参数。操作的结果是一个简单的行:deployment "hello-server" created。我们可以在集群中看到新的服务器(图 4-14 )。

img/464715_1_En_4_Fig14_HTML.jpg

图 4-14

带有 hello-server 应用的 Kubernetes 集群

花一些时间在命令行上分析参数是很有用的。参数--image用于指示我们希望在集群中部署的映像。该映像可以是来自 Docker Hub 的公共 get,也可以是来自 Google Repository 的。重要的是映像的版本。在 Docker 中,我们可以通过在:后添加值来标识映像的版本,例如hello-app:1.0表示映像hello-app的版本为 1.0。当我们为映像修复一个版本时,如果我们想要将 CI/CD 系统放在适当的位置,我们必须确保总是部署相同的映像版本。例如,我们可以将映像命名为 stable ,因为我们想要使用映像的最后一次构建。

使用特定的名称来标记我们的 Docker 映像有助于在生产中始终部署具有该名称的最后一个映像。当我们将映像发布到注册表时,Kubernetes 会检查该映像是否是最新的,并下载带有相同标记的映像进行安装。这意味着如果我们把一个新的稳定在注册表中,这是自动部署在我们的系统中。

我们现在已经部署了我们的应用,但是为了使它有用,它必须可以被其他用户访问。为了在互联网上公开我们的应用,我们必须配置一个负载均衡器。

负载均衡器用于平衡集群中的资源。当我们必须管理来自互联网的流量时,这是很重要的。因为 Kubernetes 创建了一个资源集群,所以通常使用集群来管理流量。在 Kubernetes 中,我们可以使用来自kubectl命令行的命令kubectl expose来公开集群。

kubectl expose deployment hello-server --type "LoadBalancer"

当我们添加参数--type "LoadBalancer"时,我们已经定义了负载均衡器。这将我们的服务公开给互联网,并配置一个负载均衡器来管理请求。如果我们想向外界公开服务,那么LoadBalancer命令是很重要的。LoadBalancer是在 GCP 创建的,并且在 IP 被分配给集群的外部 IP 之后。通过这种方式,可以连接到集群并显示服务运行。

使用外部 IP 将应用公开在互联网上。要查看 IP,我们必须查看有关我们群集的信息。我们可以通过使用命令kubectl get service来获取信息。这显示了关于我们在 Kubernetes 中指出的服务的信息。为了获得关于我们的应用的信息,我们使用以下命令:

kubectl get service hello-server

结果是一个显示服务信息的表格。

NAME          TYPE          CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE

hello-server  LoadBalancer  10.35.247.216  35.196.27.26  8080:32104/TCP 20m

注意

外部 IP 用于识别我们的应用。这将是不同的,取决于你的谷歌云配置。

我们可以看到我们的应用正在工作(图 4-15 )。打开浏览器并点击以下链接:

http://35.196.27.26:8080

img/464715_1_En_4_Fig15_HTML.jpg

图 4-15

实时 hello-server 应用

恭喜你!我们现在已经部署了我们的第一个 Kubernetes 应用。

配置 Kubernetes 仪表板

到目前为止,我们已经看到了如何通过命令行使用 Kubernetes,但 Kubernetes 有一个令人惊叹的界面。要在 GCP 使用它,我们必须将其配置为在该平台上访问。

警告

对于新版本的 GCP,谷歌强烈建议你禁用经典的 Kubernetes 用户界面,而使用谷歌控制台仪表板。

通过安装和配置 Kubernetes,我们已经创建了一个集群。它有不同的基本组件,可以通过 HTTP/HTTPS 访问。我们可以使用以下命令查看有关群集的信息:

kubectl cluster-info

这向我们显示了关于集群的信息(图 4-16 )。

img/464715_1_En_4_Fig16_HTML.jpg

图 4-16

Kubernetes 集群信息

要访问仪表板,我们需要点击以下链接:

https://<kubernetes master ip>/api/v1/namespaces/kube-system/services/https:kubricks 控制板:/proxy

如果我们试图访问 UI,我们会得到如图 4-17 所示的错误。这是因为我们还没有配置服务帐户来访问 Kubernetes 集群。

img/464715_1_En_4_Fig17_HTML.jpg

图 4-17

尝试访问 Kubernetes 用户界面时出现的错误

要访问 Kubernetes UI,我们必须为服务帐户配置身份和访问管理(IAM)。

注意

你将在第九章中了解更多关于我的信息。现在,您只需要知道如何配置 Kubernetes UI 所需的用户访问。

第一步是为用户获取密钥。为此,我们使用连接到命令行界面。我们只需使用控制台仪表板,转到 IAM & Admin 部分,然后从那里转到 Service Account 屏幕(参见图 4-18 )。

img/464715_1_En_4_Fig18_HTML.jpg

图 4-18

Google 控制台上的服务帐户屏幕

现在我们可以看到,对于我们的项目,我们没有任何服务帐户的键。这产生了我们在尝试访问 Kubernetes UI 时看到的错误。首先,我们必须为帐户创建密钥,然后下载 JSON 格式的密钥,并使用它来配置对 Kubernetes UI 的访问。

要创建密钥,我们只需点击操作下的三个点,然后选择创建密钥菜单选项(参见图 4-19 )。

img/464715_1_En_4_Fig19_HTML.jpg

图 4-19

用于创建新密钥的菜单

创建关键点打开一个新的模态窗口,从中选择我们想要创建的关键点类型。选择 JSON,然后单击创建。这将创建一个与我们的服务帐户相关联的新密钥。同时,密钥被下载到我们的电脑上。该密钥可用于在 Kubernetes 用户界面上配置访问权限(参见图 4-20 )。

img/464715_1_En_4_Fig20_HTML.jpg

图 4-20

与服务帐户关联的密钥

有了创建并与我们的服务帐户关联的密钥,我们就可以为 Kubernetes UI 配置服务帐户访问。我们需要配置gcloud授权的命令如下:

gcloud auth activate-service-account practicaldevopsgcpcli@appspot.gserviceaccount.com --key-file=<path where we download the JSON key>

我们可以在 Cloud SDK 中执行该命令。命令的结果告诉我们,服务帐户现在已被激活(参见图 4-21 )。

img/464715_1_En_4_Fig21_HTML.jpg

图 4-21

通过 Cloud SDK 激活服务帐户

现在,我们已经使用我们的服务帐户配置了对 Kubernetes 的访问。下一步是使用以下命令更新我们之前在 GCP 创建的集群,并使用我们新的服务帐户凭据进行更新:

gcloud container clusters get-credentials practicaldevopsgcpcluster  --zone us-east1-b --project practicaldevopsgcpcli

该命令在为集群生成的kubeconfig中添加一个新条目,并将服务帐户与集群相关联。要运行仪表板,首先我们必须看到集群中的所有 Kubernetes 服务。我们可以使用以下命令来实现这一点:

kubectl get secrets -n kube-system

注意

如果我们在运行kubectl命令时遇到错误,我们可以将它作为 Google Cloud SDK 的一部分进行安装。安装的命令是gcloud components install kubectl。这将在 SDK 上安装并下载 Kubernetes 命令行。

这显示了我们集群中的所有令牌。我们可以使用它来查看有关令牌的信息,我们需要这些信息来访问我们的仪表板(参见图 4-22 )。

img/464715_1_En_4_Fig22_HTML.jpg

图 4-22

我们的 Kubernetes 集群中存在的 Kubernetes 令牌

要连接到 UI,我们必须有一个令牌。当我们需要进入 Kubernetes UI 时,使用这个令牌。我们可以使用以下命令获取令牌:

kubectl -n kube-system describe secret replicaset-controller-token-thsvx

请注意,replicaset-controller-token的值可能与您的配置不同,尤其是最后一部分。当我们运行这个命令时,我们可以看到如图 4-23 所示的输出。

img/464715_1_En_4_Fig23_HTML.jpg

图 4-23

用于访问仪表板的 Kubernetes 令牌

有了令牌,我们终于可以运行命令kubectl proxy。这将启动我们计算机上的服务器,并显示访问它的地址和端口。这通常是 127.0.0.1:8001。我们可以运行服务器并在浏览器中插入地址localhost:8001/ui/。这将执行 Kubernetes 仪表板,并询问我们如何在服务器上进行认证(参见图 4-24 )。

img/464715_1_En_4_Fig24_HTML.jpg

图 4-24

Kubernetes 仪表板登录

我们可以插入之前获得的令牌,并使用它登录仪表板。仪表盘显示警告。这是因为每个服务都有不同的令牌。仪表板如图 4-25 所示。

img/464715_1_En_4_Fig25_HTML.jpg

图 4-25

Kubernetes 的 UI

我们可以看到谷歌建议我们禁用经典的 Kubernetes UI,使用云控制台 UI。这是因为后者更好地集成到系统中。

探索 Kubernetes 实例

当我们创建 Kubernetes 集群时,我们实际上创建了一个不同的计算引擎。我们可以在 Kubernetes 计算引擎仪表板中检查创建的实例(图 4-26 )。

img/464715_1_En_4_Fig26_HTML.jpg

图 4-26

为管理集群而创建的 Kubernetes 计算引擎

我们可以识别实例,因为它们都是用前缀gke-创建的。我们可以通过单击实例来检查实例的详细信息。

使用控制台,可以探索集群的 Kubernetes 配置。如果我们打开控制台并检查 Kubernetes 计算引擎,我们可以看到关于集群的信息(图 4-27 )。

img/464715_1_En_4_Fig27_HTML.jpg

图 4-27

Kubernetes 集群信息

我们可以看到,集群的最小值是三个节点。可以通过点击右侧的铅笔图标来更改聚类的值(图 4-28 )。

img/464715_1_En_4_Fig28_HTML.jpg

图 4-28

Kubernetes 集群的节点池部分

可以在“节点池”部分管理群集的节点。在这一部分,我们可以配置集群的不同参数,例如,改变池中节点的数量。这是在大小值中配置的。我们可以通过在集群中添加更多节点来更改该值。例如,我们可以将该值更改为 5,并向群集中添加另外两个节点。在此部分中,可以为集群启用自动缩放。这将自动向集群添加和删除节点(图 4-29 )。

img/464715_1_En_4_Fig29_HTML.jpg

图 4-29

新的计算引擎实例

删除立方结构丛集

为了完成我们的 Kubernetes 之旅,我们必须了解如何摧毁我们的 Kubernetes 集群。有时,这对于维持公司成本或淘汰旧的基础架构是必要的。

我们可以删除 Kubernetes 集群,使用这个命令:kubectl delete service hello-server。这将删除 Kubernetes 负载均衡器。删除负载均衡器后,我们现在必须删除集群。删除集群的命令是gcloud container clusters delete practicaldevopsgcpcluster。这个命令销毁我们在 Google Cloud 中创建的集群,并要求确认删除集群。选择 Y 删除集群。

结论

使用容器对于加快上市时间非常重要,并且有很多好处。容器应用可用于复制开发生产环境。通过容器应用,我们可以实施 CI 和 CD 实践。本章简要介绍了 Kubernetes,并展示了如何在 GCP 配置经典的 Kubernetes UI。目的不是深入解释 Kubernetes。为此,我建议在 www.youtube.com/watch?v=GNj63thbiCM&t=222s 可以看到大卫·冈萨雷斯的演讲。它很好地介绍了 Kubernetes。

在 GCP,我们可以使用计算引擎以编程方式创建一个实例。计算引擎是用于容器应用和容器的虚拟机。管理容器的最佳方式是使用 Kubernetes 计算引擎。这样,就有可能创建一个带有特定操作系统的引擎集群来维护容器。在接下来的章节中,您将学习如何使用 Kubernetes 并将其与 CI 和 CD 实践相结合,以实现 DevOps 的原则。*

五、GCP 和 Jenkins 持续交付

持续交付作为持续集成的延伸,本质上是后者的进化,也是 DevOps 的支柱之一。有各种各样的软件和工具可以将 CD 系统放置到位,比如 Travis CI、GoCD 和 Bitbucket,但是最常用的工具可能是 Jenkins。在本章中,您将看到如何使用 Jenkins 在谷歌云平台(GCP)中设计和定义我们的 CD 流水线。

Jenkins 简介

Jenkins 可能是与持续集成和交付(CI/CD)相关的最著名的软件。Jenkins 是一个用 Java 编写的开源软件,用于帮助自动化软件开发过程中出现的所有非人类过程。

Jenkins 几乎可以使用任何类型的存储库,比如 CVS、Git、Subversion、Mercurial 等等。可以非常容易地自动化与 CI/CD 相关的所有过程。

Jenkins 中最重要的概念是插件。几乎可以找到任何必需品的插件。插件被分成不同的用途,例如

  • 。净发展

  • Android 开发

  • 认证和用户管理

  • 命令行界面

  • 构建通知程序

  • 部署

这只是插件有用的所有不同领域的简短列表。有了插件,我们可以使用 Jenkins 来覆盖持续集成和交付的每个方面。

Jenkins 的受欢迎是由于以下原因:

  • 跨不同操作系统易于安装和提供一致的界面

  • 模块化的可能性,为扩展 Jenkins 写一个插件

  • 使用和配置简单的界面

  • 通过主/从架构,在不同的操作系统中使用主和从的可能性,以从相同的构建中获得不同的结果

  • 简单的配置

  • 轻松报告构建的历史状态

这些就是让 Jenkins 无处不在的原则。Jenkins 经常用于 CI,因为 CI 的焦点是提交后的快速构建和任务的快速可靠的自动化。使用 Jenkins,可以在提交后使用触发器来启动构建,并且我们可以自动化与此相关的任何单个任务。这意味着每次我们发布时,我们都要执行构建软件所需的所有任务。

与 Jenkins 的持续集成和交付

Jenkins 为创建 CI 和 CD 流水线提供了非常好的支持。我使用术语流水线是因为我们必须执行并重复一系列步骤来达到相同的结果(见图 5-1 )。

img/464715_1_En_5_Fig1_HTML.png

图 5-1

持续集成和交付的流水线

CI/CD 的流水线要求将一些步骤落实到位。我现在将讨论这些步骤,并解释如何使用 Jenkins 来自动化这些步骤,从而为 CI/CD 创建一个完整的流水线。

密码

创建流水线的第一步是代码。当代码准备好了,并且开发人员已经完成了它并且在本地测试了它,它通常被发布到中央存储库。因为我们正在实现持续集成,所以我们通常在另一个分支中发布。通常,这与分支策略有关。例如,我们可以创建一个包含我们想要实现的任务数量的分支。使用分支策略有助于定义实际开发中的不同任务,并确定实际发布的代码部分。

单元测试

开发流水线的下一步是单元测试。这一步包括为代码编写测试,并在我们开始 CI 和 CD 的过程时直接执行它。Jenkins 可以用一种简单的方式自动完成这一过程。使用 Jenkins,当代码被直接提交给 repo 时,可以连接一个存储库并触发单元测试。Jenkins 从回购处下载了代码。下载完成后,我们可以构建软件并执行测试套件。建成后,如果测试通过,我们就可以进入下一步了。当我们配置项目并将代码连接到回购时,Jenkins 可以轻松管理整个过程。

代码集成

当代码被测试后,是时候将它集成到主分支中了。这可以是自动的,或者在大多数情况下,在代码审查策略之后。使用代码评审策略来加强代码的质量,并帮助共享系统的知识。根据这个策略,在代码被合并到主分支之前,我们需要至少两个人首先批准代码。当代码被集成时,我们可以在主分支上开始另一组单元测试。对于 CD 策略,代码必须自动集成,但是这意味着没有人工执行的审查。在这种情况下,我们可以使用静态代码分析来识别代码的潜在问题。使用 Jenkins,如果构建失败,可以使用工具来执行静态代码分析,有时称为代码分析。

系统试验

当代码集成到主分支中时,就有可能开始一些系统测试。这种测试不同于单元测试,因为我们使用真实的数据,而不是模拟的数据。这种测试有时被称为集成测试。通过这种类型的测试,我们集成并测试系统的所有组件。这种类型的测试的范围是识别新特性引入的任何新错误,并且数据来自系统的真实组件,而不是模拟组件,因为我们想要测试整个系统。

阶段释放

当系统测试被批准后,Jenkins 可以负责软件的发布。这可能以不同的方式发生,取决于我们如何发布软件。例如,如果我们想要发布 Docker,Jenkins 可以将代码直接放入我们用于 stage 服务器的注册表中。有了 Jenkins,我们还可以在 Bash 或 Windows 中执行脚本,在服务器上部署软件。在阶段服务器中发布对于确保软件质量至关重要。该服务器由 QA 工程团队使用,必须与生产环境中使用的服务器相似。通过这种方式,可以模拟真实环境,并在发布到生产环境之前捕获更多错误。

用户接受度

用户接受是构建持续发布流水线的一个重要阶段。从用户的角度来看,它由一系列验证功能的测试组成。这个阶段可以是自动的,基于 QA 工程团队编写的测试,但是它可以包括一些手动执行。关于连续交付,这个测试首先使用 Jenkins 来执行,在一些 canary 服务器上发布代码之后,QA 团队手工测试软件。该测试用于与最终用户就最需要发布的软件达成一致。

生产发布

流水线流程的最后也是最关键的阶段是产品发布。为了与连续交付策略保持一致,这个阶段可以一天发生多次。此外,我们可以决定不直接发布到产品中,而是选择在 canary 服务器上发布一个示例,并使用这些服务器在有限数量的用户中测试该版本。这有助于识别任何问题并在系统中创建警报。在 CD 中,我们自动化了所有的过程,并将代码中的任何变化直接发布到产品中。这可以是一个简单的映像变化或一个标签或一个错误的修复。有了纯粹的连续交付策略,我们基本上每天发布更多次软件的一小部分。Jenkins 可以帮助自动化该阶段的所有方面,并为 CD 创建完整的渠道。

设计良好的分支策略

设计一个好的分支策略对于良好的持续集成和交付是必不可少的。最流行的分支策略是为每个特性创建一个分支(图 5-2 )。当我们在分支上发布软件时,我们执行单元测试,如果它是绿灯,我们可以合并代码。为此,Git 工作流软件非常有用。该软件由 Atlassian 构建和管理,是事实上的行业标准。

img/464715_1_En_5_Fig2_HTML.jpg

图 5-2

特征分支策略

这个策略是最简单的,并且是完全自动的。每次我们将代码合并到主分支中时,Jenkins 都会在构建就绪时构建代码。Jenkins 执行一些其他步骤来检查我们发布的软件的质量。

第一步是代码分析或静态代码分析。这是使用插件来执行的。如果代码分析是肯定的,我们可以进入下一步,即系统测试集成测试。这通常是为了用真实数据而不是模拟数据测试系统而设计的。目的是使用真实组件进行测试。

我们执行的最后一步是验收测试。通常,这个测试是由工程师编写的,但在某些情况下,这个测试可以由 QA 工程团队编写,目标是从用户的角度测试功能。该测试旨在检查质量,以确保所有功能都已完全实现。该测试的另一个用途是确保旧的功能与新的软件实现一起工作。

如果所有阶段都已正确执行,我们就创建了一个新的构建,它可以安装在生产环境中,或者根据策略安装在 canary 服务器中。每当我们提交一个新特性或修复一个 bug 时,这些步骤都会被执行。例如,在存储库中,当我们只更新一个标签或更改一个映像时,这个过程就可以开始了。

借助 Jenkins,可以使用多分支流水线作业创建流水线。这种工作告诉 Jenkins 在回购上创建新的分支时创建新的流水线。这个特性由 multiline 插件管理。

当我们想在公司实施 CI 和 CD 时,我们可能需要为我们的 Jenkins 创建一个多分支系统。可以使用 Jenkinsfile 创建一个多分支项目。

当我们决定使用分支策略时,多分支解决方案是公认的选择。这是因为我们可以为回购中创建的每个新分支创建 CI/CD 渠道。

在 GCP 利用 Jenkins

到目前为止,您已经了解了 Jenkins 以及我们可以用来创建 CI/CD 流水线的一些特性。现在你将开始学习如何在 GCP 使用 Jenkins。在云中创建我们的 CI/CD 系统的原因是,我们可以根据我们的业务需求,使用它来启动新的服务器并进行扩展。

要在 GCP 上使用 Jenkins,我们需要在通过 Kubernetes 创建的集群中使用 Google Kubernetes 引擎和 Jenkins。使用 Kubernetes 创建我们的 CD 流水线给系统带来了一些重要的好处。

  • 在微服务架构或多操作系统环境中,在 Kubernetes 中创建的一个虚拟主机可以针对不同的操作系统运行作业。服务器从架构使这成为可能。

  • 有了 Kubernetes,我们有了短暂的执行者,这意味着我们可以在每次执行新任务时在一个干净的环境中执行构建。这可以消除不干净环境中的错误。

  • 构建执行器在几秒钟内运行。

  • Kubernetes 集群仅在我们的特性处于活动状态时使用,这样可以节省资源并让集群空闲下来。

首先,我们必须设置我们的 Kubernetes 集群引擎,它是创建我们的 Jenkins 环境的基础。

在 Kubernetes 上配置 Jenkins

为了创建 Jenkins CI/CD 渠道,我们必须遵循以下步骤:

  1. 创建一个新的 Kubernetes 集群。

  2. 在集群上安装 Jenkins。

  3. 安装用于 Kubernetes 的 Jenkins 插件。

  4. 配置 Kubernetes 以启动 Jenkins 进程。

第一步是连接 GCP,打开 Google Shell,然后创建我们的 Kubernetes 集群。首先,我们为 Kubernetes 集群创建一个新的网络组件。创建网络的命令是

gcloud compute networks create jenkinscicd

此命令创建新的虚拟专用云(VPC)网络。我们使用这个网络来创建我们的 Kubernetes 引擎。操作结果如清单 5-1 所示。

pierluigi_riti@practicaldevopsgcpcli:~$ gcloud compute networks create jenkinscicd
Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcpcli/global/networks/jenkinscicd].
NAME         SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
jenkinscicd  AUTO         REGIONAL
Instances on this network will not be reachable until firewall rules
are created. As an example, you can allow all internal traffic between
instances as well as SSH, RDP, and ICMP by running:
$ gcloud compute firewall-rules create <FIREWALL_NAME> --network jenkinscicd --allow tcp,udp,icmp --source-ranges <IP_RANGE>
$ gcloud compute firewall-rules create <FIREWALL_NAME> --network jenkinscicd --allow tcp:22,tcp:3389,icmp

Listing 5-1Result for the Kubernetes VPC Creation

在我们的例子中,我们不需要将我们的集群暴露在外面,我们可以很容易地跳过防火墙的配置。现在我们已经创建了一个新的 VPC,我们可以开始构建我们的 Kubernetes 集群了。

注意

虚拟私有云是谷歌能够在互联网上显示或隐藏我们的实例的方式。默认情况下,对于 Google 实例,我们可以看到一个默认的 VPC 被启用和公开。我们可以通过防火墙管理连接,允许或阻止特定端口或协议的流量。在每个项目中,可能有多个 VPC,并且所有 VPC 都是 IPv4,因为 IPv6 不受支持。需要记住的重要一点是,每个 VPC 都有一个“隐藏”的防火墙规则:每个传输控制协议(TCP)连接在 10 分钟不活动后都会被断开。

我们创建 VPC 是因为我们不想使用默认的 VPC。这是因为 Jenkins 用于内部应用,我们不想配置不同的规则。如果我们呆在同一个网络中,我们创建的规则会在整个网络中反映出来。

现在我们已经创建了新的网络,我们可以使用 Kubernetes 引擎构建新的 Kubernetes 集群。

gcloud container clusters create jenkins-cd \
--network jenkinscicd \
--zone us-east1-b \
--scopes "https://www.googleapis.com/auth/projecthosting,storage-rw"

我们添加一个属性–scopes。它允许集群访问谷歌云存储谷歌云容器注册表。参数–scopes用于指定节点实例的类型。我们可以为实例定义不同的范围。我们也可以定义多个作用域,但是必须用逗号分隔。使用完整的 URI 定义范围。在 GCP,我们可以定义这个 URI。(见表 5-1 。)

表 5-1

最常见的谷歌云资源 URI

|

别名

|

上呼吸道感染

| | --- | --- | | bigquery | https://www.googleapis.com/auth/bigquery | | cloud-platform | https://www.googleapis.com/auth/cloud-platform | | cloud-source-repos | https://www.googleapis.com/auth/source.full_control | | cloud-source-repos-ro | https://www.googleapis.com/auth/source.read_only | | compute-ro | https://www.googleapis.com/auth/compute.readonly | | compute-rw | https://www.googleapis.com/auth/compute | | datastore | https://www.googleapis.com/auth/datastore | | default | https://www.googleapis.com/auth/devstorage.read_only | |   | https://www.googleapis.com/auth/logging.write | |   | https://www.googleapis.com/auth/monitoring.write | |   | https://www.googleapis.com/auth/pubsub | |   | https://www.googleapis.com/auth/service.management.readonly | |   | https://www.googleapis.com/auth/servicecontrol | |   | https://www.googleapis.com/auth/trace.append | | gke-default | https://www.googleapis.com/auth/devstorage.read_only | |   | https://www.googleapis.com/auth/logging.write | |   | https://www.googleapis.com/auth/monitoring | |   | https://www.googleapis.com/auth/service.management.readonly | |   | https://www.googleapis.com/auth/servicecontrol | |   | https://www.googleapis.com/auth/trace.append | | logging-write | https://www.googleapis.com/auth/logging.write | | monitoring | https://www.googleapis.com/auth/monitoring | | monitoring-write | https://www.googleapis.com/auth/monitoring.write | | pubsub | https://www.googleapis.com/auth/pubsub | | service-control | https://www.googleapis.com/auth/servicecontrol | | service-management | https://www.googleapis.com/auth/service.management.readonly | | sql | https://www.googleapis.com/auth/sqlservice | | sql-admin | https://www.googleapis.com/auth/sqlservice.admin | | storage-full | https://www.googleapis.com/auth/devstorage.full_control | | storage-ro | https://www.googleapis.com/auth/devstorage.read_only | | storage-rw | https://www.googleapis.com/auth/devstorage.read_write | | taskqueue | https://www.googleapis.com/auth/taskqueue | | trace | https://www.googleapis.com/auth/trace.append | | userinfo-email | https://www.googleapis.com/auth/userinfo.email |

我们可以使用 URI 来定义实例的范围。属性--network标识了我们想要为我们的集群使用的 VPC。(该命令需要一些时间才能完成。)我们还必须定义要创建集群的区域。这是由属性–zone定义的。最后,我们看到这个结果:

WARNING: Starting in 1.12, new clusters will have basic authentication disabled by default. Basic authentication can be enabled (or disabled) manually using the `--[no-]enable-basic-auth` flag.
WARNING: Starting in 1.12, new clusters will not have a client certificate issued. You can manually enable (or disable) the issuance of the client certificate using the `--[no-]issue-client-certificate` flag.
WARNING: Currently VPC-native is not the default mode during cluster creation. In the future, this will become the default mode and can be disabled using `--no-enable-ip-alias` flag.
Use `--[no-]enable-ip-alias` flag to suppress this warning.
This will enable the autorepair feature for nodes. Please see
https://cloud.google.com/kubernetes-engine/docs/node-auto-repair for more
information on node autorepairs.
WARNING: The behavior of --scopes will change in a future gcloud release: service-control and service-management scopes will no longer be added to what is specified in --scopes.
To use these scopes, add them explicitly to --scopes. To use the new behavior, set container/new_scopes_behavior property (gcloud config set container/new_scopes_behavior true).
WARNING: Starting in Kubernetes v1.10, new clusters will no longer get compute-rw and storage-ro scopes added to what is specified in --scopes (though the latter will remain included in the default --scopes).
To use these scopes, add them explicitly to --scopes. To use the new behavior, set container/new_scopes_behavior property (gcloud config set
container/new_scopes_behavior true).
Creating cluster jenkins-cd...done.
Created [https://container.googleapis.com/v1/projects/practicaldevopsgcpcli/zones/us-east1-b/clusters/jenkins-cd].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-east1-b/jenkins-cd?project=practicaldevopsgcpcli
kubeconfig entry generated for jenkins-cd.
NAME        LOCATION    MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
jenkins-cd  us-east1-b  1.9.7-gke.3     35.231.107.135  n1-standard-1  1.9.7-gke.3   3          RUNNING

在这个配置中,我们使用 Google Cloud Container Registry 和 Google Cloud Storage,因为我们必须允许 Jenkins 访问服务。

通过 Google Cloud Container Registry,我们允许 Jenkins 访问我们可以用来存储代码的存储库,在本例中是projecthosting。另一个范围是 Google 云存储,它允许 Jenkins 访问云存储,并存储容器或我们创建的构建。参数-rw表示读写时的访问。下一步是为我们的环境创建 Jenkins/Kubernetes 架构。

我们必须获取我们创建的集群的凭据。这些凭证用于访问托管我们的 Jenkins 部署的 Kubernetes 集群。为此,我们可以使用以下命令:

gcloud container clusters get-credentials <cluster name>

在我们的例子中,集群名是jenkins-cd。该命令的结果如下:

pierluigi_riti@practicaldevopsgcpcli:~$ gcloud container clusters get-credentials --zone us-east1-b jenkins-cd
Fetching cluster endpoint and auth data.
kubeconfig entry generated for jenkins-cd.

凭证现在已经配置好了,我们现在可以考虑并实现我们的 Jenkins 架构了。

设计 Jenkins 架构

我们要设计的是主/从 Jenkins 架构。这种类型的架构用于管理分布式 Jenkins 架构。简而言之,主设备与一个从设备一起启动作业,并监控从设备以检查作业的状态。

这种类型的体系结构尤其适用于我们想要扩展 Jenkins 配置的情况。作业可以在任何可用的从节点上执行,或者我们可以配置主节点在特定的从节点上执行作业。例如,当我们必须在特定的 SO 中执行作业时,或者当我们必须使用特定的配置时,可以在从属节点中固定作业。

图 5-3 显示了我们想要用来在 Kubernetes 中实现 Jenkins CD 流水线的架构。这个配置使用两个节点构建,这是 Kubernetes 集群的最低要求。集群允许我们扩展测试的必要性,增加另一个节点,以防测试数量增加。

img/464715_1_En_5_Fig3_HTML.jpg

图 5-3

库伯内特斯的 Jenkins 主/从建筑

这种架构本质上是在多节点集群中部署 Jenkins。我们可以有两个以上的节点。为了不使其他网络过载,我们将 Jenkins master 部署到 Kubernetes 集群中一个单独的名称空间中。在单独的名称空间中部署 Jenkins 有两个主要优势:

  • 命名空间允许我们创建特定的配额。

  • 命名空间用于创建与 Jenkins 和另一个部署的逻辑分离。

首先,为了继续我们的部署,我们必须更好地理解 Kubernetes 中的名称空间、pod、服务、配额和部署的概念。

Kubernetes 中的名称空间、pod、服务、配额和部署

在 GCP 建造 Jenkins 流水线需要大量使用 Kubernetes。首先,要继续介绍 Jenkins 的配置,我必须定义并解释一些与 Kubernetes 相关的重要概念。

命名空间

名称空间是我们在集群中部署的一个逻辑部分。在集群中对不同的资源进行逻辑组织是很重要的。

一个 Kubernetes 集群可以满足多个用户或用户组,这意味着不同的用户可以在同一个集群中拥有不同的项目。使用名称空间有助于识别每个团队的项目。这是在我们满足两个要求时完成的:

  • 提供名称空间的名称

  • 启动一种机制来定义和附加不同的策略和授权,以访问集群的一个子部分

每个用户或用户组可能想要为我们的资源创建一个不同的隔离环境,对于这个环境,我们想要定义我们自己的策略和授权。这是通过创建名称空间来完成的。每个名称空间都允许用户执行一些独特的功能。

  • 唯一命名的资源

  • 将管理权限委托给定义的用户

  • 使用配额限制资源消耗的能力

首先,为了创建我们的名称空间,我们想看看是否有另一个名称空间可以使用。使用 Kubernetes,我们可以看到所有名称空间的列表,命令如下:

kubectl get namespace

该命令产生类似于以下内容的输出:

pierluigi_riti@practicaldevopsgcpcli:~$ kubectl get namespace
NAME            STATUS        AGE
default         Active        2d
kube-public     Active        2d
kube-system     Active        2d

Kubernetes GCP 有三个基本的名称空间:

  • default:该名称空间用于所有没有特定名称空间的对象。

  • kube-public:这个名称空间对所有用户都是可读的,包括那些没有经过身份验证的用户。

  • kube-system:这个名称空间用于由 Kubernetes 系统创建的对象。

这些名称空间存在于每个集群中,是在我们构建集群本身时创建的。建立集群后,我们可以使用以下命令查看其摘要:

kubectl get namespace <name>

例如,如果我们想要查看关于默认名称空间的所有细节,我们可以使用命令kubectl get namespace default。结果看起来像这样:

pierluigi_riti@practicaldevopsgcpcli:~$ kubectl get namespace default
NAME          STATUS       AGE
default       Active       2d

如果我们想要更多的细节,我们可以使用这个命令:

kubectl describe namespace <name>

结果如下:

pierluigi_riti@practicaldevopsgcpcli:~$ kubectl describe namespace default
Name: default
Labels: <none>
Annotations: <none>
Status: Active
No resource quota.
Resource Limits
Type       Resource  Min  Max  Default Request  Default Limit Max Limit/                                                              Request Ratio
----       --------  ---  ---  ---------------  ------------- -------------
Container  cpu       -    -    100m             -             -

我们可以看到,该命令为我们提供了关于名称空间的详细信息。为了在 Kubernetes 中设计一个名称空间,我们必须用创建名称空间所需的值构造一个 YAML 文件。该值是命名空间的名称,必须与 DNS 规则兼容。清单 5-2 中给出了这个文件的一个例子。

apiVersion: v1
kind: Namespace
metadata:
  name: practicaldevopsgcpnamespace

Listing 5-2The Code for Creating a Namespace in Kubernetes

文件准备好后,我们可以运行命令:

Kubectl create -f ./<namespace file>.yaml

Pods

在库伯内特人的世界里,吊舱是最小的可部署单位。它是由一个或多个容器组成的组,例如 Docker,具有共享的网络/存储。例如,在图 5-3 中,pod 共享已安装节点的网络。

例如,pod 可用于运行软件,如 NGINX。我们可以使用多个 pod 来创建一个栈,但是 pod 的主要作用是操作和支持协同定位和共同管理的软件。我们在架构中使用 pod 来操作和管理 CI/CD 流水线的 Jenkins 节点。

服务

服务是一种抽象,它定义了 pod 的逻辑组和访问它们的策略。我们可以将服务视为微服务。本质上,我们用不同的 pod 创建了一组服务分组。因为每个 pod 基本上都是一个容器,所以我们可以将不同的应用组合在一起,作为一个实体进行回复。

要创建服务,我们必须用创建服务的参数定义一个 YAML 文件。例如,我们可以有一个类似清单 5-3 中的文件:

kind: Service
apiVersion: v1
metadata:
  name: practicaldevopgcp-service
spec:
  selector:
    app: PracticalDevOpsGCPApp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8080

Listing 5-3A YAML file for Defining a Service

服务文件使用元数据将名称分配给服务本身。在这种情况下,practicaldevopgcp-service。该服务指出任何带有标签PracticalDevOpsGCPApp和端口 8080 暴露的 pod。该服务公开了一个公共端口,在本例中是 80。因为我们使用了选择器,所以服务创建了称为practicaldevopgcp-service的端点。我们可以创建一个没有选择器的服务,在这种情况下,我们必须手动创建一个端点文件,这个文件是暴露服务的 IP 和端口所必需的(参见清单 5-4 )。

kind: Endpoints
apiVersion: v1
metadata:
  name: practicaldevopgcp-service
subsets:
  - addresses:
      - IP: 1.2.3.4
    ports:
      - port: 8080

Listing 5-4The End Points YAML File Definition

定额

配额是限制 Kubernetes 集群资源使用的一种方式。使用对象ResourceQuota定义配额,这由集群的管理员启动。

我们可以限制的资源类型如下:

  • CPU :我们可以限制请求的数量或者非终端状态下 pod 使用的 CPU 数量。

  • 内存:我们可以限制请求的数量,或者限制非终结状态下 pod 可以使用的内存。

  • 存储:我们可以限制所有存储卷的请求总数和存储卷的数量。

为配额设置正确的限制对于定义我们的集群至关重要。

部署

部署用于声明和管理 pod 和副本集的状态。我们可以在部署对象中描述对象的状态,部署控制器将对象的状态更改为所需的状态。这基本上是定义高可用性的基础,因为部署会处理状态,并且在状态发生变化时,会强制 pod 或复制集以部署中定义的状态运行。

部署本质上是 Kubernetes 集群的核心。它用于创建我们想要的 pod 数量,并更改 pod 的状态。清单 5-5 中提供了一个示例部署。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

Listing 5-5Sample YAML Deployment

在这种情况下,我们为 NGINX 服务创建一个新的部署。我们可以定义定义价值副本的单元的数量,在我们的例子中是三个。这表示部署必须创建的单元数量。

部署定义了运行所需的容器种类,在本例中,是 NGINX 版本 1.7.9 的副本。这在容器一节中定义。模板部分定义了与容器相关联的标签,在本例中是 NGINX。这意味着所有的 pod 使用相同的名称并公开相同的端口。

创建 Jenkins 服务

既然我们已经讨论了 Kubernetes 的一般用途,我们将把我们的 Jenkins 图转换成一组 Kubernetes 文件。这是创建 CI/CD 流水线所必需的。

我们架构的第一部分定义了 Kubernetes 集群所需的两个 Jenkins 服务:

  • Jenkins UI

  • Jenkins 发现

我们创建的第一个服务是 UI。相关文件在清单 5-6 中给出。

  kind: Service
  apiVersion: v1
  metadata:
    name: jenkins-ui
    namespace: jenkinsgcp
  spec:
    type: NodePort
    selector:
      app: master
    ports:
      - protocol: TCP
        port: 8080
        targetPort: 8080
        name: ui

Listing 5-6The Code for Creating the 
service-ui

代码定义了一个名为jenkis-ui的类型化服务,它在名称空间jenkinsgcp中定义。这使用了类型NodePort,用于允许外部服务和 pod 访问 Jenkins UI。该服务是我们必须为集群创建的两个服务中的第一个。

我们必须创建的另一个服务是发现服务。创建该服务的 YAML 文件如下(列表 5-7 ):

  kind: Service
  apiVersion: v1
  metadata:
    name: jenkins-discovery
    namespace: jenkinsgcp
  spec:
    selector:
      app: master
    ports:
      - protocol: TCP
        port: 50000
        targetPort: 50000
        name: slaves

Listing 5-7Code for Creating the Discovery Service

在本例中,我们公开了端口 50000,内部 Jenkins 服务使用该端口与主服务器通信并执行作业。完整的文件如清单 5-8 所示。

# [START jenkins_service_ui]
---

  kind: Service
  apiVersion: v1
  metadata:
    name: jenkins-ui
    namespace: jenkinsgcp
  spec:
    type: NodePort
    selector:
      app: master
    ports:
      - protocol: TCP
        port: 8080
        targetPort: 8080
        name: ui
# [END jenkins_service_ui]
# [START jenkins_service_discovery]
---

  kind: Service

  apiVersion: v1
  metadata:
    name: jenkins-discovery
    namespace: jenkinsgcp
  spec:
    selector:
      app: master
    ports:
      - protocol: TCP
        port: 50000
        targetPort: 50000
        name: slaves
# [END jenkins_service_discovery]

Listing 5-8Code for the Complete Service File Created

我们必须创建的最后也是最重要的文件是部署文件,它在清单 5-9 中给出。

# [START jenkins_deployment]
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: jenkins
  namespace: jenkinsgcp
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: master
    spec:
      containers:
      - name: master
        image: jenkins/jenkins:tls
        ports:
        - containerPort: 8080
        - containerPort: 50000
        readinessProbe:
          httpGet:
            path: /login
            port: 8080

          periodSeconds: 10
          timeoutSeconds: 5
          successThreshold: 2
          failureThreshold: 5
        env:
        - name: JENKINS_OPTS
          valueFrom:
            secretKeyRef:
              name: jenkins
              key: options
        - name: JAVA_OPTS
          value: '-Xmx1400m'
        volumeMounts:
        - mountPath: /var/jenkins_home
          name: jenkins-home
        resources:
          limits:
            cpu: 500m
            memory: 1500Mi
          requests:
            cpu: 500m
            memory: 1500Mi
      volumes:
      - name: jenkins-home
        gcePersistentDisk:
          pdName: jenkins-home
          fsType: ext4
          partition: 1
# [END jenkins_deployment]

Listing 5-9Deployment File Code for the Kubernetes Jenkinsfile

部署文件用于定义服务以及每个服务需要多少副本。对于主服务,我们定义了 1 的副本。

spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: master

这是因为我们希望确保此时集群中只有一个主节点是活动的。如果集群的一个节点出现故障,Kubernetes 会在集群的其他地方启动另一个节点。部署定义了我们想要在这个部分中运行的容器的类型。

    spec:
      containers:
      - name: master
        image: jenkins/jenkins:tls
        ports:
        - containerPort: 8080
        - containerPort: 50000

本节定义了我们想要使用的 Docker 映像,在本例中,是 Jenkins 的最新版本,以及两个容器端口。现在我们定义端口 8080 和 50000。UI 服务使用的端口是 8080,发现服务使用的端口是 50000。

我们可以使用readinessProbe部分定义何时必须重启容器。当我们试图创建一个新的 Docker 映像时,readinessProbe非常重要,因为有些映像有很多数据,不能立即使用。例如,如果我们启动一个新的 Jenkins,它可以在几分钟内变成活动的。在这个场景中,Docker 删除了映像。通过readinessProbe部分,我们可以预先指出等待检查的秒数。我们还可以指定一个超时时间,以及在终止映像之前要尝试多少次。periodSeconds参数表示我们尝试执行探测的频率。timeoutSeconds参数表示第一次探测后等待的时间。successThreshold参数设置映像激活前要考虑的成功探测次数。failureThreshold参数表示镜像启动失败前需要考虑的失败次数。这个的代码是

        readinessProbe:
          httpGet:
            path: /login
            port: 8080
          periodSeconds: 10
          timeoutSeconds: 5
          successThreshold: 2
          failureThreshold: 5

本节定义了 kubelet 使用的参数,用于识别容器何时准备好接受流量。当容器中的所有容器都准备好时,容器就准备好了。

通过readinessProbe,我们定义了用于监控容器的参数,并检查容器是否有生命。第一个参数是periodSeconds。这个值定义了 kubelet 检查容器需要多少秒钟,在我们的例子中是每十秒钟。

有些容器需要一些外部参数才能正常工作。在我们的部署中,我们使用以下代码段发送该参数:

env:
        - name: JENKINS_OPTS
          valueFrom:
            secretKeyRef:
              name: jenkins
              key: options
        - name: JAVA_OPTS
          value: '-Xmx1400m'

最后两个部分用于定义与 Jenkins master 相关的配额和容量。配额的代码是

        resources:
          limits:
            cpu: 500m
            memory: 1500Mi
          requests:
            cpu: 500m
            memory: 1500Mi

资源部分需要一点解释,特别是关于如何定义资源。我们定义的第一个资源是 CPU。该值为 1,表示 1 个 GCP 核心。在我们的例子中,因为我们使用 GCP,所以我们设置了一个值500m。该值表示我们希望使用最大 500 毫核。我们看到的另一个价值是内存。内存使用以字节表示。我们可以用一个后缀来表示我们想要使用的内存值。这些后缀是 E、P、T、G、M 和 k。在我们的例子中,我们也可以有两个字母。在本例中,我们在后缀后添加了字母 I。这里我们定义了 1500 兆字节的限制。

资源的最后一部分是卷。这表明数据存储在哪里,在我们的例子中,它被分成两部分。首先是

        volumeMounts:
        - mountPath: /var/jenkins_home
          name: jenkins-home

第一部分指出了我们想要挂载的路径及其名称。第二部分定义了我们使用什么类型的卷。

      volumes:
      - name: jenkins-home
        gcePersistentDisk:
          pdName: jenkins-home
          fsType: ext4
          partition: 1

在这种情况下,我们定义使用只有一个分区的 ext4 文件系统。现在,我们可以定义创建 Jenkins 部署所需的所有参数。

将 Jenkins 部署到库伯内特

准备好部署和服务的文件后,我们必须定义一些其他文件,以允许 Jenkins 正常工作。我们必须创建的第一个文件是选项文件,在这个文件中,我们可以为 Jenkins 设置密码。该文件只包含一行:

--argumentsRealm.passwd.jenkins=CHANGE_ME --argumentsRealm.roles.jenkins=admin

我们可以设置自己的密码,也可以在运行时生成密码。要生成密码,我们可以使用以下命令:

openssl rand -base64 15

这将生成一个随机密码,我们可以将它放在选项文件中。我们现在可以将密码更新为CHANGE_ME,更新为密码生成的值。下一步是在 Kubernetes 中创建一个秘密。为此,我们可以使用以下命令:

kubectl create secret generic jenkins --from-file=options --namespace=jenkinsgcp

注意

Kubernetes secret 用于保存敏感信息,如密码、OAuth 令牌或 SSH 密钥。与在 pod 或容器中存储相同的值相比,秘密提供了更多的安全性和灵活性。

该命令的结果是一个简单的行,告知已经正确创建了秘密。

pierluigi_riti@practicaldevopsgcpcli:~/practicalgcp-jenkins$ kubectl create secret generic jenkins --from-file=options --namespace=jenkinsgcp
secret "Jenkins" created

创建好密码后,我们希望将我们的帐户添加到基于角色的访问控制(RBAC)的管理角色中。这赋予了我们管理集群的权利。这样做的命令是

kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account)

该命令创建一个集群角色绑定,并将我们的实际帐户添加到角色cluster-admin中。这使我们有权管理 Kubernetes 集群。这个命令的结果显示了我们添加的用户和结果。

pierluigi_riti@practicaldevopsgcpcli:~/practicalgcp-jenkins$ kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=$(gcloud config get-value account)
Your active configuration is: [cloudshell-20307]
clusterrolebinding "cluster-admin-binding" created

配置好所有安全性之后,剩下的工作就是创建我们需要与 Jenkins 一起使用的卷。要创建这个卷,我们可以使用以下命令:

gcloud compute images create jenkins-home-image --source-uri https://storage.googleapis.com/solutions-public-assets/jenkins-cd/jenkins-home-v3.tar.gz
gcloud compute disks create jenkins-home --image jenkins-home-image

该命令的结果如清单 5-10 所示。

pierluigi_riti@practicaldevopsgcpcli:~$ gcloud compute images create jenkins-home-image --source-uri https://storage.googleapis.com/solutions-public-assets/jenkins-cd/jenkins-home-v3.tar.gz
Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcpcli/globimg/jenkins-home-image].
NAME                          PROJECT            FAMILY  DEPRECATED  STATUS
jenkins-home-image   practicaldevopsgcpcli                           READY

Listing 5-10Result of the Creation for the Volume

有了创建的卷,我们最终可以运行 Kubernetes 部署和服务。运行部署的命令是

kubectl apply -f k8s/

该命令执行文件夹 K8s 中的所有 YAML 文件。该命令的结果如下:

deployment "jenkins" created
service  "jenkins-ui" created
service "jenkins-discovery" created

在集群中,我们现在可以看到我们已经创建了一个部署和两个服务。接下来,我们必须检查吊舱是否正在运行。我们可以使用以下命令检查主服务器:

kubectl get pods --namespace jenkinsgcp

这显示了关于容器的信息。

NAME                     READY  STATUS             RESTARTS    AGE
jenkins-87c47bbb8-5mgh4  1/1    ContainerCreating  0           4m

我们可以使用以下命令检查服务的状态:

kubectl get svc --namespace jenkinsgcp

该命令的结果向我们显示了这两个服务及其相关信息(图 5-4 )。

img/464715_1_En_5_Fig4_HTML.jpg

图 5-4

Jenkins 服务的状态

公开服务

现在我们已经配置了服务,我们必须做的是将服务公开给互联网。Kubernetes 为使用Ingress资源提供了一个非常好的 API 系统。这用于允许外部资源访问内部群集,通常是 HTTP 资源。

为此,我们在 K8s 文件夹中创建一个名为ingress.yaml的新文件。该文件包含我们公开服务所需的所有信息。该文件如下所示:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: jenkins
  namespace: jenkinsgcp
spec:
  tls:
  - secretName: tls
  backend:
    serviceName: jenkins-ui
    servicePort: 8080

我们需要一个 TLS 通信来暴露端口。为此,我们必须创建自己的证书。我们可以用openssl命令创建证书。该命令确保证书是自签名的,因此浏览器可以引发与此相关的异常。我们必须接受证书和浏览器停止,以提出错误。

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=jenkins/O=jenkins"

这实际上创建了一个有效期为 365 天的证书。该命令创建证书并将其放入 tmp 文件夹。现在我们可以使用这个秘密来存储我们的证书并打开通信。

kubectl create secret generic tls --from-file=/tmp/tls.crt --from-file=/tmp/tls.key --namespace jenkinsgcp

现在我们有了秘密,我们可以运行位于ingress文件夹中的ingress.yaml文件。

kubectl apply -f ingress/

创建入口后,我们现在可以找到与之关联的 IP。为此,我们可以使用以下命令:

kubectl get ingress --namespace jenkinsgcp

此操作可能需要几分钟才能完成。因此,GCP 创建了一个新的负载均衡器。这是用来暴露 Jenkins 我们的基础设施。美丽而强大的 GCP 负责创建必要的基础设施来为我们公开服务,在这种情况下,通过负载均衡器。结果显示了一个表,其中包含与 Jenkins UI 关联的 IP。

NAME       HOSTS    ADDRESS         PORTS         AGE
jenkins    *        35.x.x.x        80, 443       2m

我们看到的 IP 是我们可以用来访问 Jenkins UI 的 IP。首先,要使用 UI,我们必须检查负载均衡器,实例在哪里是健康的。我们可以使用以下命令来检查负载均衡器:

kubectl describe ingress jenkins --namespace jenkinsgcp

该命令返回集群的详细信息,通过这些信息,我们可以看到 UI 何时可用(图 5-5 )。

img/464715_1_En_5_Fig5_HTML.jpg

图 5-5

库伯内特人的健康状况

我们可以看到,在 Google Console 中创建的负载均衡器移动到网络服务,然后从菜单中选择负载均衡选项。要查看负载均衡器的详细信息,单击它,会显示类似于图 5-6 的内容。

img/464715_1_En_5_Fig6_HTML.jpg

图 5-6

由 GCP 创建的负载均衡器详细信息

根据群集的运行状况,我们现在可以访问 Jenkins。要访问 Jenkins UI,我们可以使用负载均衡器的 IP,在我的例子中是 35.186.199.190。这向我们展示了 Jenkins 登录页面(图 5-7 )。

img/464715_1_En_5_Fig7_HTML.jpg

图 5-7

Jenkins 页面在集群上运行

对于访问,我们可以使用用户名 Jenkins 密码与我们在文件中定义的密码相同。我们现在准备创建我们的 CD 流水线。

创建连续交付项目

随着 Jenkins 页面的启动和运行,我们终于可以创建我们的 CD 流水线了。为了测试我们的 Jenkins 功能,我们创建了一个非常简单的应用,如下所示:

For trying the feature of Continuous Delivery in GCP we use the example code build by Google, this is a very simple page showing the information about the system because what we really need is understand only how to create the environment, the project is based on the gceme image present in the Google repository, gcr.io/cloud-solutions-images/gceme

为了创建 CD 流水线,我们首先必须创建用于发布软件的不同环境。我们实际上创造了三种环境。

  • 生产:这是我们发布软件用于生产的环境。

  • 服务:我们用服务环境来描述我们有多少层。在我们的例子中,我们有后端和前端。这不是一个类似于生产或金丝雀的环境,但是我们可以用它来对我们的服务层进行更合理的划分。这样可以更好地管理它。

  • Canary :创建用于系统的 Canary 服务器。

注意

canary 服务器是在 CD 中使用的服务器,用于在发布用于生产之前在真实环境中测试一个特性。canary 服务器模拟生产环境,只是以一种有限的方式。canary 服务器可用于隔离代码中的错误,通常用于为生产目的而选择的有限数量的用户。使用这些服务器不仅有助于发现错误,还有助于测试应用的 UI/UX。

这些环境用于模拟 CD 流水线开发的各个阶段。为了模拟它们,我们首先必须在 Kubernetes 中创建新的名称空间。

kubectl create ns practical-gcp-production

这个命令在 Kubernetes 集群中创建新的名称空间。我们使用这个名称空间来构建我们的生产环境。创建了新的名称空间后,我们现在可以创建代码所需的服务了。我们创建的第一个服务是用于生产。生产有两个服务:后端和前端(见清单 5-11 )。

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: practical-gcp-backend-production
spec:
  replicas: 1
  template:
    metadata:
      name: backend
      labels:
        app: gceme
        role: backend
        env: production
    spec:
      containers:
      - name: backend
        image: gcr.io/cloud-solutions-images/gceme:1.0.0
        resources:
          limits:
            memory: "500Mi"
            cpu: "100m"
        imagePullPolicy: Always
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
        command: ["sh", "-c", "app -port=8080"]
        ports:
        - name: backend
          containerPort: 8080

Listing 5-11Kubernetes File for Creating the Back-End Service in the Production Namespace

前面是用于为生产创建后端服务的 Kubernetes 文件。现在我们可以看到服务使用了应用gceme,版本 1.0.0。

containers:
      - name: backend
        image: gcr.io/cloud-solutions-images/gceme:1.0.0
        resources:
          limits:
            memory: "500Mi"
            cpu: "100m"
        imagePullPolicy: Always

该映像来自 Google 存储库,构建在 Kubernetes 文件中。我们需要创建的另一个服务是前端服务(清单 5-12 )。

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: practical-gcp-frontend-production
spec:
  replicas:
  template:
    metadata:
      name: frontend
      labels:
        app: gceme
        role: frontend
        env: production
    spec:
      containers:
      - name: frontend
        image: gcr.io/cloud-solutions-images/gceme:1.0.0
        resources:
          limits:
            memory: "500Mi"
            cpu: "100m"
        imagePullPolicy: Always
        readinessProbe:
          httpGet:
            path: /healthz
            port: 80
        command: ["sh", "-c", "app -frontend=true -backend-service=http://gceme-backend:8080 -port=80"]
        ports:
        - name: frontend
          containerPort: 80

Listing 5-12Front-End Production Service

这些文件在同一个文件夹中,我们可以将它们用于部署。部署用于创建和设置 pod 和副本集的特定状态。我们使用以下命令来部署我们的应用:

kubectl --namespace=practical-gcp-production apply -f k8s/production

该命令的结果是

deployment gceme-backend-production" created
deployment "gceme-frontend-production" created

现在,当我们创建服务时,我们可以为服务和金丝雀创建其他部署。本质上,我们告诉 Kubernetes 创建一个 Kubernetes 服务,即服务线,并为 canary 服务创建一个部署。第一个命令创建一个服务。Kubernetes 中的服务是一个类似于 pod 的 REST 对象。当我们创建一个服务时,我们实际上是将资源提交给 API 服务器,以创建新的资源。当我们创建金丝雀时,我们实际上创建了一个新的部署。在 Kubernetes 中,这遵循每个部署的规则:应用位于前一个 Kubernetes 应用之下。在这种情况下,我们可以使用负载均衡器部署到互联网上,并且可以从外部访问。

kubectl --namespace=practical-gcp-production apply -f k8s/services
kubectl --namespace=practical-gcp-production apply -f k8s/canary

我们不改变名称空间,因为我们想使用相同的环境。创建所有部署后,我们可以扩展前端的生产。我们这样做是因为我们希望在前端应用中运行多个实例。

kubectl --namespace=practical-gcp-production\
           scale deployment practical-gcp-frontend \
          --replicas=4

扩展部署后,我们可以使用以下命令确定分配给应用的外部 IP:

kubectl --namespace=practical-gcp-production get service practical-gcp-frontend

这个命令显示了我们可以用来访问我们的应用的服务的 IP 地址(图 5-8 )。

img/464715_1_En_5_Fig8_HTML.jpg

图 5-8

我们应用的 IP,部署在 Kubernetes

我们可以看到正在运行的应用何时将公共 IP 放入浏览器。它看起来有点像图 5-9 。

img/464715_1_En_5_Fig9_HTML.jpg

图 5-9

应用启动并运行

创建存储库

CD 开发过程的下一步是创建存储库,我们可以将代码放入其中,以用于我们的流水线。在谷歌云中,我们有一个谷歌云存储库。这是一个私有的 Git 库,我们可以用它来维护我们的代码。

我们必须创建一个存放代码的仓库。为此,打开 Google Cloud 控制台,在 find 文本框中,键入 repository 。这将产生一个下拉菜单。选择源代码,打开源代码库窗口(图 5-10 )。

img/464715_1_En_5_Fig10_HTML.jpg

图 5-10

谷歌云平台中的源库服务

创建一个名为 practicaldevopscgp 的新存储库。随着回购的创建,我们看到类似图 5-11 的东西。

img/464715_1_En_5_Fig11_HTML.jpg

图 5-11

带有新存储库的源代码

创建了 repo 之后,我们现在可以开始初始化 Git,将代码推送到远程存储库,插入 sample-app 文件夹,并使用命令初始化存储库。为此,我们必须执行以下命令:

gcloud init && git config credential.https://source.developers.google.com.helper gcloud.cmd

这为我们的 Git 存储库创建了基本配置。之后,我们必须使用以下命令使我们的存储库远程化:

git remote add google https://source.developers.google.com/p/practicaldevopsgcpcli/r/practicaldevopscgp

创建并配置好回购后,我们可以开始推送回购中的第一个文件。首先,我们必须将文件添加到本地存储库中。

git add .
git commit -m "First commit"

前面的命令用于在我们的本地存储库中添加和提交文件。提交文件后,我们现在必须在远程存储库中推送文件。

git push --all google

这将推送远程存储库中的所有代码,并创建一个名为 master 的新分支(图 5-12 )。

img/464715_1_En_5_Fig12_HTML.jpg

图 5-12

我们存储库中的第一个提交

我们最终将所有文件提交给了远程存储库。我们可以直接从谷歌平台上管理和查看有关文件的信息。我们只需要连接到平台并移动到存储库部分(图 5-13 )。

img/464715_1_En_5_Fig13_HTML.jpg

图 5-13

提交了文件的回购

创建 Jenkins 流水线

存储库完成后,我们必须在 Jenkins 中为 CD 创建一个流水线。为此,我们设置了服务必须使用的凭证。因为它是一个自动服务,并且我们没有到 UI 的用户连接,所以我们想为该服务提供一个不同的凭证。要添加凭证,请连接到 Jenkins UI,打开凭证部分,然后单击链接 Global,这将打开一个页面,在该页面上可以在服务器上添加新凭证(图 5-14 )。

img/464715_1_En_5_Fig14_HTML.jpg

图 5-14

Jenkins 的证件区

单击窗口左侧面板上的添加凭据链接。这将打开另一个页面,我们可以在其中选择凭据。从元数据中选择谷歌服务账户(图 5-15 )。

img/464715_1_En_5_Fig15_HTML.jpg

图 5-15

Google 帐户的凭证

单击“确定”,为服务添加凭据。这会将凭据添加到系统中。因为 Jenkins 在 GKE 手下工作,谷歌暴露了他的身份。该凭证可以由 Jenkins 自动获取并在其中配置。凭证现在看起来如图 5-16 所示。

img/464715_1_En_5_Fig16_HTML.jpg

图 5-16

更新的凭据

配置好凭证后,我们现在需要的是为构建创建作业。在 Jenkins UI 中,选择创建新作业的链接,然后选择项目的多分支流水线类型。这将打开项目的配置部分(图 5-17 )。

img/464715_1_En_5_Fig17_HTML.jpg

图 5-17

多分支项目配置部分

要为流水线添加源代码,必须单击 Add Source 下拉菜单,然后选择 Git 存储库、之前创建的凭证,最后是我们之前创建的代码存储库的链接。最后应该会看到类似图 5-18 的东西。

img/464715_1_En_5_Fig18_HTML.jpg

图 5-18

Jenkins GIT 配置部分

现在我们必须定义何时执行构建。因为我们希望拥有 CD,所以我们每次提交存储库时都会触发构建。这个实践引导着 CD,我们每天可以有数百个构建。要配置 Jenkins 做到这一点,我们必须选择 GitHub hook trigger for git SCM polling 选项(图 5-19 )。

img/464715_1_En_5_Fig19_HTML.jpg

图 5-19

GitHub 触发器的配置

在主页面上,我们可以看到 GitHub 钩子的日志,因为我们已经将我们的 Jenkins 与 Git 连接起来了(图 5-20 )。

img/464715_1_En_5_Fig20_HTML.jpg

图 5-20

带有 GitHub 钩子日志的菜单

保存配置。这开始了我们在分支中的第一次构建(图 5-21 )。

img/464715_1_En_5_Fig21_HTML.jpg

图 5-21

Jenkins 的第一栋建筑

因为我们的项目是一个多流水线项目,所以我们必须配置一个 Jenkinsfile 来配置和管理构建。您现在必须学习的是如何创建 Jenkinsfile。

创建 Jenkinsfile

我们现在已经让 Jenkins 在服务器上运行了。现在我们必须为应用和 CD 流水线创建结构。

要用 Jenkins 构建 CD 流水线,我们必须创建一个流水线项目和一个 Jenkinsfile。这个文件用于描述我们想要部署的分支,并且必须放在我们创建的每个分支上。当我讨论微服务时,您会看到关于这些步骤的更多细节。现在,理解 Jenkinsfile 的结构很重要(清单 5-13 )。

node {
  def project = 'practicaldevopsgcp-197023'
  def appName = ' pdopsgcp'
  def feSvcName = "${appName}-frontend"
  def imageTag = "gcr.io/${project}/${appName}:${env.BRANCH_NAME}.${env.BUILD_NUMBER}"

  checkout scm

  stage 'Build image'
  sh("docker build -t ${imageTag} .")

  stage 'Run Go tests'
  sh("docker run ${imageTag} go test")

  stage 'Push image to registry'
  sh("gcloud docker -- push ${imageTag}")

  stage "Deploy Application"
  switch (env.BRANCH_NAME) {
    // Roll out to canary environment
    case "canary":
        // Change deployed image in canary to the one we just built
        sh("sed -i.bak 's#gcr.io/cloud-solutions-images/pdopsgcp:1.0.0#${imageTag}#' ./k8s/canary/*.yaml")
        sh("kubectl --namespace=production apply -f k8s/services/")
        sh("kubectl --namespace=production apply -f k8s/canary/")
        sh("echo http://`kubectl --namespace=production get service/${feSvcName} --output=json | jq -r '.status.loadBalancer.ingress[0].ip'` > ${feSvcName}")
        break

    // Roll out to production
    case "master":
        // Change deployed image in canary to the one we just built
        sh("sed -i.bak 's#gcr.io/cloud-solutions-images/ pdopsgcp:1.0.0#${imageTag}#' ./k8s/production/*.yaml")
        sh("kubectl --namespace=production apply -f k8s/services/")
        sh("kubectl --namespace=production apply -f k8s/production/")
        sh("echo http://`kubectl --namespace=production get service/${feSvcName} --output=json | jq -r '.status.loadBalancer.ingress[0].ip'` > ${feSvcName}")
        break

    // Roll out a dev environment
    default:
        // Create namespace if it doesn't exist
        sh("kubectl get ns ${env.BRANCH_NAME} || kubectl create ns ${env.BRANCH_NAME}")
        // Don't use public load balancing for development branches
        sh("sed -i.bak 's#LoadBalancer#ClusterIP#' ./k8s/services/frontend.yaml")
        sh("sed -i.bak 's#gcr.io/cloud-solutions-images/ pdopsgcp:1.0.0#${imageTag}#' ./k8s/dev/*.yaml")
        sh("kubectl --namespace=${env.BRANCH_NAME} apply -f k8s/services/")
        sh("kubectl --namespace=${env.BRANCH_NAME} apply -f k8s/dev/")
        echo 'To access your environment run `kubectl proxy`'
        echo "Then access your service via http://localhost:8001/api/v1/proxy/namespaces/${env.BRANCH_NAME}/services/${feSvcName}:80/"
  }
}

Listing 5-13The Jenkinsfile Necessary for Continuous Deployment

pipelines 项目使用这个文件来构建我们制作的软件。现在我们可以看到名为 Dockerfile 的文件。这是因为我们为我们的软件构建 Docker 映像。

该文件用于定义 Jenkins 可以自动执行流水线的阶段,特别是我们在以下阶段找到的文件:

  • Build :用于创建最后一个代码的 Docker 映像

  • 运行:对刚刚创建的映像运行一些测试

  • Push :如果测试通过,将映像推送到 repo 上

  • 部署:用于在不同的服务器上部署应用

这些步骤是我们流水线的基础(图 5-22 )。现在我们可以为分支配置一些参数。每当开发人员创建一个新的分支时,这些都可以改变,它们代表了创建多分支策略的最佳方式。

警告

在 Jenkinsfile 中选择项目 ID 时要小心。你可以从我们 GCP 的主页上获得这些信息。

img/464715_1_En_5_Fig22_HTML.jpg

图 5-22

已建流水线

现在我们可以看到 Jenkins 根据我们在 Jenkinsfile 中定义的阶段创建了流水线。

  stage 'Build image'
  sh("docker build -t ${imageTag} .")

  stage 'Run Go tests'
  sh("docker run ${imageTag} go test")

  stage 'Push image to registry'
  sh("gcloud docker -- push ${imageTag}")

现在,当我们对代码进行任何更改时,我们从头到尾执行所有流水线,这允许我们从代码开始创建一个完整的软件。

结论

本章介绍如何为 CD 创建流水线项目。当我们想要实现一个完整的 DevOps 资源时,这是非常重要的。Jenkins 为实现 CD 提供了一个非常有效的工具,通过 GCP 的 Kubernetes,我们可以创建一个非常强大的 CD 系统。有了我们新的多分支项目,我们可以有一个其阶段的图形表示。Jenkinsfile 是一个非常简单而强大的配置 CD 系统的工具。我们可以使用代码来定义我们需要的每个阶段和每个环境。我们可以发布带有代码的文件,通过这种方式,每个开发人员都正确地配置了系统,这减少了维护时间,同时提高了项目的稳定性。