GCP-DevOps-高级教程-全-

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 系统的工具。我们可以使用代码来定义我们需要的每个阶段和每个环境。我们可以发布带有代码的文件,通过这种方式,每个开发人员都正确地配置了系统,这减少了维护时间,同时提高了项目的稳定性。

六、GCP 微服务架构

每天,我们可能都会使用基于微服务架构的服务。每次我们看《网飞》系列,本质上都是在使用基于微服务的服务。微服务提供了比传统架构更多的优势,非常适合云应用。在本章中,你将学习如何设计一个微服务架构,以及如何在谷歌云平台(GCP)中实现它。

微服务架构简介

微服务架构是一种直接源自面向服务架构(SOA)的编程技术。事实上,微服务架构是这种架构风格的变体。在微服务架构中,我们创建的服务是松散耦合的细粒度的,并通过轻量级协议连接。

松散耦合的服务不需要知道更多关于其他服务的定义。它只需要知道如何调用和得到什么;不需要其他细节。这意味着我们可以毫无问题地更改服务的实现。

服务通常是细粒度的。粒度是我们设计服务时必须考虑的一个关键因素,因为它定义了服务之间的业务功能和有效负载消息。在一个细粒度的服务中,我们可以定义一个小的业务案例,其中的有效负载消息可大可小。这取决于我们有多少交易。例如,我们可以定义一个微服务用户,为其定义创建和读取用户的接口。

采用微服务架构有助于将大架构分解成更小的部分。这有助于提高架构的可伸缩性。微服务通常采用容器架构部署。这意味着我们可以添加服务来响应高流量,并在不再需要时删除它们。

这种架构的另一个优点是,它允许团队并行编写代码并独立部署。这是因为,对于测试,我们只需要知道输入的数据和从输出接收的数据。因为整个服务是独立的,所以我们可以也必须轻松地实现持续集成和部署。微服务要想取得成功,采用持续集成和部署至关重要。

以下是与微服务架构相关的一些原则:

  • 微服务是为大系统设计的:微服务诞生于扩展大系统的必要性,但大系统如何定义是相对的。微服务背后的重要思想是设计一个能对变化做出反应的系统,并提高系统本身的可伸缩性。

  • 微服务是面向目标的:当我们设计微服务架构时,我们不必遵循既定的规则,但我们可以设计出解决特定问题的解决方案。例如,当我们必须向现有的体系结构添加另一个服务时,这一点变得更加明显。在这种情况下,我们必须考虑如何设计服务,以及我们必须使用什么服务来实现架构。

  • 微服务旨在被替代:当我们想到微服务架构时,我们设计一个服务与其他服务松散耦合,这允许我们将一个服务更改为另一个服务,而不会给系统带来问题。

这些原则直接来源于微服务架构的本质,因为当我们定义服务时,我们设计了一个模块化的架构。大多数微服务架构使用 Docker 或类似的容器技术来定义和部署服务。通过这种方式,我们可以轻松地在架构中部署新的服务,修复错误或扩展架构。

微服务架构自然引领了 CI/CD 的进程。这与架构的实现有关。当我们实现架构时,我们定义了一组用于交换少量数据的服务。通常,这些被设计为容易发布并驱动 CI/CD 过程。我们不断地集成软件,发布新的服务,为生产做好准备。

实施微服务架构

当我们考虑微服务架构时,我们必须考虑我们必须为正确实施而进行的所有文化变革。当我们实现一个微服务架构时,我们选择了一个小的服务来响应以最少的数据量进行通信的业务需求。这需要一些文化上的改变来适应。

这种变化尤其与微服务架构背后的理念有关,这是 Unix 的理念,即做一件事并把它做好。”基于此,我们可以定义微服务的理念和架构原则。

  • 服务的粒度很细,只提供一种功能。

  • 我们必须接受持续测试和持续集成策略。

  • 当我们设计系统时,我们必须接受系统中可能出现的失败和错误,并在失败的基础上改进我们的系统。

  • 服务必须有一些特定的约束。一定是

    • 弹性:每个服务必须能够伸缩。

    • 弹性:如果一个服务出现故障,该故障不会影响系统中的另一个服务。在一些系统中,我们可以让一个微服务依赖于另一个,在这种情况下,我们必须确保管理依赖关系的故障。

    • 可组合:因为微服务是松散耦合的,所以每个服务必须提供一个不会随时间变化的统一接口。

    • Minimal :每个服务都是为了做一件事,并且做好这件事而设计的。因此,服务被设计成一个高度内聚的实体,每个部分只专注于一件事。

    • 完整:每个微服务都要有完整的功能,因为每个服务都是专门化的。

这些架构实践需要公司文化的改变。这种变化类似于采用 DevOps 实践所需的变化。

当我们实现微服务架构时,必须考虑一些利弊(表 6-1 ):

表 6-1

微服务架构的利与弊

|

赞成的意见

|

骗局

|
| --- | --- |
| 强大的模块化:当我们从单一服务转移到微服务时,我们将应用设计成模块化的,可以分成小模块。当有一个大的交叉分布的团队时,这变得很关键。 | 微服务是分布式的:开发分布式系统可能更困难,因为系统在地理上可以托管在另一个地方。这可能会造成响应时间的问题。另一个问题是确定服务是否是活动的。 |
| 独立部署:当我们设计一个微服务时,我们设计一个小的、独立的服务。就其本质而言,该服务可以独立于其他服务发布和部署。 | 一致性:当我们部署系统时,我们必须确保服务跨实例正确部署,并且具有接口的一致性。我们必须记住,微服务通过消息相互交流。如果我们改变了什么,这可能会破坏系统。 |
| 语言独立性:由于每个服务都是彼此独立部署和开发的,所以我们可以选择任何我们想要的语言来开发服务。这意味着我们可以为我们开发的每个服务潜在地使用不同的语言。 | 操作的复杂性:当我们设计微服务架构时,我们有数百个相互连接的服务。这增加了系统操作的复杂性。我们需要一个非常有经验的团队来维护架构。 |

具有坞站和库的微服务体系结构

当我想到 GCP 的微服务架构时,我通常会想到 Docker 和 Kubernetes。我对栈的选择源于我想要实现的目标。我想设计一个模块化的、完全独立的基于服务的架构,我想使用一些 RESTful web 服务,使用 CD 原则进行管理和部署。

当我们考虑和设计使用 Kubernetes 的架构时,自然选择 Docker。借助 Docker,我们可以创建微服务架构所需的隔离级别。在设计我们想要实现的架构时,首先要记住的是它与单一架构有什么不同。图 6-1 说明了不同之处。

img/464715_1_En_6_Fig1_HTML.jpg

图 6-1

整体系统与微服务系统

当我们从单一应用转移到微服务应用时,我们必须将系统的每个部分都视为一个独立的应用。

例如,订单、客户和用户的每个应用都不是应用的简单组件,而是一个小应用本身。这是我们必须在架构中反映的第一个重大变化。为了实现这种转变,我们可以使用 Docker 和 Kubernetes。

这种应用可以由容器即服务(CaaS)来定义,其中每个 Docker 都是一个单独的应用,Kubernetes 帮助管理它们。

如前所述,微服务架构有一些特定的特征。

  • 独立性:每个服务必须独立于另一个服务。

  • 分散化:服务可以安装在不同的服务器上,而不会影响应用。

  • 基于消息的:每个服务使用一个消息与另一个服务通信。

  • 自动发布:我们在实现一个微服务架构的时候,通常会采用 CD 的做法来发布应用。

  • 隔离:每个服务必须与另一个服务隔离

这四个原则决定了 Docker 和 Kubernetes 的选择。有了 Docker,我们可以创建独立的容器,这些容器只能通过消息相互通信,并按照 CD 惯例发布。

设计 Kubernetes 微服务架构

当我们在 Kubernetes 中设计微服务架构时,我们必须在 Kubernetes 世界中“翻译”我们需要实现的内容。图 6-2 显示了 Kubernetes 中微服务应用的一个例子。

img/464715_1_En_6_Fig2_HTML.jpg

图 6-2

kuble micro services 体系结构

上图显示了从单片应用到 Kubernetes/Docker 微服务架构的转换。每个微服务都部署在一个单元中。这是我们建筑中最小的单元。通过在一个服务中管理更多的 pod,这创建了我们完整的应用。

在这个架构中,我们引入了一个新的组件,称为 ingress。该组件用于管理对服务的外部访问,通常使用 HTTP。通过 ingress,我们可以创建一个负载均衡器来管理对我们应用的访问。

在 GCP 创建微服务架构

我已经讨论了微服务架构背后的理论。这只是一个非常简单的介绍。要彻底理解微服务架构,需要一本专门针对该主题的书。鉴于这本书的范围,我的意图是只描述如何使用 GCP 来实现微服务架构,并使用 DevOps 实践来管理它。因此,让我们来尝试一下 GCP,实现一个简单的微服务架构。

注意

DevOps 和微服务架构有很大的区别。重要的区别在于,DevOps 是一种帮助公司缩短上市时间、在系统出现故障时恢复系统的时间,以及提高发布产品质量的实践。微服务架构是从 SOA 架构衍生出来的软件开发实践,和 DevOps 有一些共通点。当我们实现一个微服务架构时,我们实施了一些 DevOps 中常见的实践,特别是 CD。DevOps 所需的文化变革与从整体架构转变为微服务架构时取得成功所需的文化变革基本相同。

我们必须采取的第一步是设计微服务并定义 Kubernetes 组件、服务和 pod。一旦确定了第一步,并且设计了我们的架构的组件,我们就创建一个包含我们想要实现的所有组件的图表。在我们的例子中,我们希望实现我们之前设计的架构的一部分以及实现它所需的组件。为了创建微服务架构,我们必须将我们希望在微服务中进行转换的应用“dockerize”。在我们的例子中,我们希望实现一个基本的前端服务,用于显示后端系统检索到的用户列表。

要实现这一点,首先我们必须创建 Go 代码,然后创建 Dockerfile 来对应用进行 dockerize 化。当应用在 Docker 中时,我们可以开始将其集成到 CI 流水线中,并使用 Jenkins 和 Kubernetes 在现实世界中公开应用。

创建服务

第一步是为我们的 Go 服务创建代码。因此,让我们开始编写一个简单的代码来创建公开用户所必需的服务。在我们的例子中,我们不使用真正的数据库,所以我们读取一个文本文件并将这个文件提供给另一个服务。代码如清单 6-1 所示。

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    file, err := os.Open("users.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

Listing 6-1The Go Code for Reading a File

代码很简单:读取一个文本文件并将结果显示给 shell。在我们的例子中,我们直接在 Go 文件的文件夹中创建.txt文件。.txt文件如下所示:

John Doe
Pierluigi Riti

它本质上只是一个名单。创建微服务架构的下一步是构建 Dockerfile(清单 6-2 )。

FROM golang:latest
ADD . /app/
WORKDIR /app
RUN go build -o main .
CMD ["/app/main"]

Listing 6-2The Dockerfile for the Application

我们可以看到 Dockerfile 非常简单。我们首先从 Docker 导入最新的 Golang,然后使用以下命令为应用创建目录:

RUN mkdir /app

该命令在 Docker 基本映像上执行命令mkdir,并创建一个名为app的新目录。创建目录时,我们必须将路径中的文件添加到 Docker 目录中的 Dockerfile 中。为此,我们使用以下命令:

ADD . /app/

这将复制 Docker 容器中的所有文件。在我们的例子中,这是包含用户和 Go 文件的文件。docker 文件的下一行将工作目录移动到应用目录。对此的命令是

WORKDIR /app

然后,我们构建用于创建 Go 可执行文件的文件。

RUN go build -o main

这将创建 Go 可执行文件。每次运行映像时,我们都会调用这个文件,为此,我们添加了以下命令

CMD ["/app/main"]

这将执行之前用go build命令创建的文件。准备好 Docker 文件后,我们必须编译 Docker 映像并执行。构建 Docker 映像的命令是

docker build -t practicalgcpgo -f Dockerfile .

该命令的结果如图 6-3 所示。

img/464715_1_En_6_Fig3_HTML.jpg

图 6-3

Docker 构建的结果

现在已经创建了映像。我们可以通过执行 Docker 映像来测试我们的映像和代码(图 6-4 )。

img/464715_1_En_6_Fig4_HTML.jpg

图 6-4

执行的 Docker 映像

在注册表中发布映像

现在创建了映像,我们必须做的是在我们的私有回购中发布映像。这对于我们的架构来说是必不可少的一步,因为随着映像的发布,我们可以规划一个 CI/CD 系统。

谷歌有自己的内部私有存储库。在我们开始在存储库中提取和推送 Docker 映像之前,我们必须确保正确配置了凭证。为此,打开 Google SDK 并执行以下命令:

gcloud auth configure-docker

这个命令配置访问私有 Google Docker 注册表的凭证。该命令的结果如图 6-5 所示。

img/464715_1_En_6_Fig5_HTML.jpg

图 6-5

Google Cloud Docker 存储库凭据已配置

标记本地映像

配置好注册表后,我们可以开始将映像推入注册表,但是首先,要推入映像,我们必须创建一个带有注册表名称的标记。之后就可以推图了。

标记映像对于映像本身的管理非常重要。有了正确的标记,就很容易理解映像的位置以及映像中的代码版本。Google 建议按照以下命名约定创建标签:

[HOSTNAME]/[PROJECT-ID]/[IMAGE]

使用这个命名约定,我们可以很容易地根据项目 ID 和位置来识别我们的映像。主机名实际上是我们存储映像的地方。有四个主机名:

  • gcr.io:这个主机名在美国存储镜像,但是这个位置将来可以改变。

  • us.gcr.io:这总是在美国存储映像,但是使用不同的存储桶而不是gcr.io

  • 存储欧洲的映像。

  • 这存储了亚洲的映像。

当我们存储映像时,选择最近的位置是很重要的。这是因为我们不想在推送或拉取映像时有太多的延迟。我们可以使用以下命令在注册表中标记本地映像:

docker tag [SOURCE_IMAGE] [HOSTNAME]/[PROJECT-ID]/[IMAGE]

该命令用最新的版本标记映像。如果我们想创建一个特定的版本,我们可以使用以下语法:

docker tag [SOURCE_IMAGE] [HOSTNAME]/[PROJECT-ID]/[IMAGE]:[VERSION]

我们现在可以创建标记第一个映像的命令。SOURCE_IMAGE是我们刚刚用 Docker 命令构建的映像。在我们的例子中,命令是

docker tag practicalgcpgo eu.gcr.io/practicaldevopsgcpcli/practicalgpc:1.0

我们可以看到使用命令docker images标记的新映像。结果显示了我们系统中的所有映像(图 6-6 )。

img/464715_1_En_6_Fig6_HTML.jpg

图 6-6

Docker 映像结果带有标记的映像

从结果中,我们可以看到我们有两个相同大小和相同映像 ID 的映像。首先是 Docker 构建的结果。我们看到的是最新的版本,在我们发现我们的新映像标记了我们指定的命名约定之后的版本。

标记好映像后,我们现在可以将映像推送到注册中心。推送映像的语法遵循与创建标记相同的规则。语法如下所示:

docker push [HOSTNAME]/[PROJECT-ID]/[IMAGE]:[VERSION]

如果我们省略VERSION,则使用最新的标签来推送映像。

注意

我们必须确保选择正确的项目 ID。在我们这里是practicaldevopsgcpcli。如果选择了错误的项目 ID,Google 会显示一个错误,告诉我们 API 没有启用。

推动的结果如图 6-7 所示。

img/464715_1_En_6_Fig7_HTML.jpg

图 6-7

在注册表中推送我们的映像的结果

可以使用以下命令验证我们在注册表中推送的所有映像:

gcloud container images list-tags [HOSTNAME]/[PROJECTID]/[IMAGE]

该命令的结果显示了关于映像的信息(图 6-8 )。

img/464715_1_En_6_Fig8_HTML.jpg

图 6-8

推入注册表的映像列表

现在映像被推入注册中心,这意味着我们是公共注册中心的第一个服务。当然,该服务只显示文本文件的结果。我们现在需要用 JSON 响应更新服务,并创建一个 Kubernetes 集群来管理服务。

实施我们的微服务架构的下一步是创建集群,为 CD 部署一个系统,并在此基础上用最新的映像更新集群。

创建 Kubernetes 集群

Docker 映像创建完毕,下一步是在 Kubernetes 中创建一个集群,我们可以用它来扩展和维护应用。

在前一章中,您已经看到了如何创建 Kubernetes 集群,但是为了增强功能,我们重复了其中的一些步骤,并考虑了为什么要这样做。首先,我们必须创建基本集群。对于我们的基本应用,我们可以创建一个三节点集群。创建 Kubernetes 集群的步骤如下:

gcloud config set compute/zone us-east1-b

将计算区域设置为您的区域(在我的示例中为us-east1-b),然后执行命令创建集群。

gcloud container clusters create microservice-gcp --num-nodes 3 \
--scopes https://www.googleapis.com/auth/projecthosting,storage-rw

该命令创建一个名为微服务-gcp 的三节点集群。这是一个特定的集群,我们将在其中部署以前的应用。因为我们必须为 CI/CD 创建 Jenkins 集群,所以现在我们的系统中有两个 Kubernetes 集群(图 6-9 )。

img/464715_1_En_6_Fig9_HTML.jpg

图 6-9

我们云中的库本内特斯星团

为了更好地管理集群,我们创建了一个新的名称空间,称为microservice-gcp。创建名称空间的命令是

kubectl create ns microservice-gcp

创建了名称空间之后,我们现在可以开始创建 YAML 文件来定义我们想要实现的 Kubernetes 服务。

我们必须创建部署文件来下载我们之前创建的映像。该文件类似于清单 6-3 中的文件。

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: practical-microservice
spec:
  replicas: 1
  template:
    metadata:
      name: backend
      labels:
        app: gcp-microservice
        role: backend
        env: production
    spec:
      containers:
      - name: pracitcalgcp-microservice
        image: eu.gcr.io/practicaldevopsgcpcli/practicalgpc:1.0
        resources:
          limits:
            memory: "500Mi"
            cpu: "100m"
        imagePullPolicy: Always
        command: ["sh", "main"]

Listing 6-3The Service for Our Microservice

接下来,我们必须使用以下命令在之前创建的名称空间中创建新的部署:

kubectl --namespace=microservice-gcp apply service.yaml

服务现在已经创建好了,可以使用了。我们现在可以为 CD 创建一个系统,并继续开发微服务架构。

注意

GCP 的免费层有一个技术限制:只允许一个公共 IP。这意味着,当我们试图显示微服务架构的服务时,可能会遇到问题。如果发生这种情况,我们可以将服务与我们在第五章中部署的代码集成在一起,并使用该服务来代替 simple-app。

结论

在本章中,您了解了如何创建微服务架构。术语微服务,我们指的是一种实现和组织我们架构的方法。

微服务与 Docker 有着天然的联系。当我们为微服务创建容器时,我们可以轻松地为我们的架构创建微服务,更重要的是,我们可以将它集成到我们的 CD 系统中。使用 GCP,创建和维护微服务很容易,因为 Kubernetes 自然地集成到了 Google 中,允许以一种简单且非常灵活的方式维护集群。

七、GCP 的监控

监控是我们开发过程中最关键的阶段之一。拥有一个良好的监控系统对于获得关于我们所创建的基础设施和软件的准确反馈是至关重要的,当然,对于监督对系统的访问也是如此。在这一章中,我将讨论在谷歌云平台(GCP)中建立一个有效的监控系统的基础是什么。如果有人问:“为什么我必须使用监控系统?”你可以用这个比喻来回答:“拥有一个没有监控的大型分布式系统就像骑自行车一样,只是你在地狱,你和自行车都着火了。”

什么是监控系统?

给监控下一个正确的定义并不简单。例如,如果我们在谈论软件监控系统监控,我们必须首先定义。软件监控可以定义为根据服务级别协议(SLA)的条款监控软件以确保服务质量。这通常由一组在软件上设计并由特定软件绘制的指标组成。收集并分析这些指标,以提供软件及其工作方式的图片。

系统监控可以定义为监控系统以确保机器的正确状态。这包括一组监视器和警报,用于检查服务器和网络的一般状态,以确认当前基础架构的正确功能。

基于这两个定义,我们可以建立一个共同的定义。监控是确保系统状态所必需的一套实践和软件。它由软件监控和系统监控组合而成。当我们想要创建一个监控系统时,我们必须确保准备好所有的指标和警报,以确保对系统状态的持续反馈。

根据这一定义,我们可以确定监控系统的关键要求:

  • 指标:我们必须定义一组指标来收集关于系统当前状态的数据。

  • Alerts :我们必须定义一个系统,以便在指标显示不正确的值时提供警报。

  • 监控:我们必须使用软件来采集和显示系统的实际状态。通常,这是由特定的软件完成的。

  • 反馈:我们必须有积极的反馈,以防系统出错。这可以通过带有升级系统的电子邮件来提供。

这四个区域实质上是每个监控系统的核心。实时监控对于在生产中出现问题时进行调试至关重要。

有效的实时监控可以在出现问题时指示系统的实际状态,并提供系统所有状态的映像,不仅仅是软件,还包括网络、数据库、磁盘空间等。这些信息可以用来解决实际问题,更重要的是,可以用来防止同样的问题再次发生。这是因为它显示了系统失败的地方,并帮助我们确定与此相关的真正根本原因。

当我们有良好的实时监控时,我们可以很容易地识别错误,例如,当我们在软件中发布一个新特性时。我们可以看到发布的错误率,并且在我们观察到太多失败的情况下,我们可以很容易地推动代码的回滚,例如,通过使用连续交付系统(CD ),并发布软件的先前版本。

监控系统的另一个用途是预测分析。当我们有一个监控系统时,我们可以收集关于系统状态的数据。例如,基于通过 Splunk 等日志收集的这些数据,我们可以开始进行一些预测性分析。假设我们有一个显示 CPU 使用情况的图表(见图 7-1 )。

img/464715_1_En_7_Fig1_HTML.jpg

图 7-1

一个样本记忆图

上图显示了不同时间的 CPU 使用情况。我们可以看到一些尖峰和一些正常的用法。可以为不同的时间范围创建相同的图表,例如,天、周和月。该图表可以显示不同数据框的使用情况。此信息可用于执行计算,并确定我们的基础架构使用的最繁忙时段。

一个好的监控系统有助于我们识别这一时段,然后调整软件和基础设施,以更好地应对最繁忙的时段。此外,它还提供了有关系统历史性能的信息,当我们必须在基础架构中部署新服务器时,可以帮助我们校准和确定我们的需求。这转化为成本节约和更好地识别系统中的任何瓶颈。

当我们考虑监控时,我们可以识别两种类型:白盒监控和黑盒监控。白盒监控为我们提供了应用内部状态的详细信息,例如 HTTP 连接、数量或用户等。另一方面,黑盒监控从外部检查系统的运行情况。黑盒并没有给出关于 HTTP 连接数或实际连接到系统的用户数的信息。

白盒监控的一个好例子是 Prometheus,黑盒监控的一个好例子是 Nagios。我们可以使用这两种类型,来建立一个良好而强大的监控系统。

监控系统中涉及的因素

当我们考虑建立我们的监控系统时,我们必须考虑一些关键因素。我们必须做出的第一个假设是,监控没有明确的定义。但是,我们可以找出一些与监控系统相关的常用术语。

  • 监控:我之前已经定义了监控,但是,总结一下,它可以被定义为收集、处理、聚集和显示关于系统、错误、服务器生命周期、用户连接、已用空间等的实时信息。

  • 白盒监控:这种类型的监控是对系统暴露给外部的内部指标的监控。白盒监控的一个例子是记录服务以检查软件是否是活动的,例如 HTTP 检查,以及可以用于查看系统内部的任何其他服务。

  • 黑盒监控:这是一种从系统外部执行的监控,例如,监控一个应用。与白盒监控相反,它用于检查系统本身生成的日志。使用黑盒监控,我们并不真正检查日志,而是检查服务的输出,例如,检查服务是否是活动的。

  • 仪表板:仪表板本质上是一个图形用户界面(GUI),用于指示系统的状态。这通常是一个 web 应用。仪表板通常允许用户过滤或搜索系统上的特定资源。

  • 警报:警报是监控系统对特定情况的反应,例如,当我们有高 CPU 使用率或磁盘空间问题时。可以通过不同的方式发出警报。例如,我们可以有一个标签提醒,这意味着当系统发现一个问题时,一个标签被创建并直接分配给一个团队,或者一个邮件提醒,在这种情况下,系统在检测到一个问题时会向一个特定的邮件列表发送一封电子邮件。

  • 根本原因:根本原因本质上是问题的主要来源。确定问题的根本原因对于系统的稳定性是必不可少的,一个好的监控系统可以很容易地确定原因并发出新的警报以防止问题再次发生。

  • 节点/机器:这个术语表示发生错误的物理或虚拟硬件。每个节点或机器都可以运行多个服务,一个警报可以连接到每个服务器。

  • 推送:这是软件的一个发布,由自动系统制作,比如木偶或者厨师。

  • 回滚:这是发布旧版本软件使系统再次稳定所遵循的程序。这通常是通过在系统中推送旧版本的软件来完成的。

这个术语在所有监控系统中都是通用的,通常用来描述我们为解决系统问题而采取的操作。

为什么监控很重要

监控很重要,原因有很多,不仅是为了确保系统本身的稳定性。例如,监控系统可用于以下用途:

  • 时间分析:监控系统可用于观察系统基础设施的关键方面。例如,我们可以收集有关数据库增长的数据,或者我们可以确定数据库在一年或一个月中最活跃的时间。同样,我们可以在不同的版本中衡量我们软件的质量。

  • 比较数据:使用监控系统,我们可以从一个系统收集数据,并与另一个系统进行比较。这些数据可以用来决定什么样的软件更适合我们的需求。

  • 警报:如前所述,监控系统用于在系统出现错误时发出警报。监控系统检查系统的状态,并在出现错误的情况下发出警报。

  • 可视化系统状态:当我们有一个监控系统时,我们通常有一个仪表板来可视化系统的当前状态。监控系统提供了整个系统的清晰画面。

  • 调试:一个监控系统是基于日志分析的,这意味着我们可以使用系统来分析软件在特定时间的状态,看看是否有任何特定的条件导致了特定的错误。

以上是使用良好的监控系统来确保安全的一些最常见的原因。我们可以监控对系统的访问,并使用这些信息来防止数据泄露。一个好的监控系统必须回答两个简单的问题:

  • 什么坏了?

  • 为什么坏了?

每个监控系统都必须回答这两个问题,才能真正有效。

什么坏了,为什么?

一个良好和有效的监控系统必须能够给我们这些简单问题的答案。首先要确定什么坏了。这是对系统中工作不正常的组件的识别。这是监控系统的基础。

监控系统必须找到的第二个答案是,为什么它会坏?这个问题的答案更复杂,因为它涉及到误差分析。一个好的监控系统可以帮助我们做到这一点,因为它给我们一个系统状态的实际画面。

表 7-1 显示了每个系统中可能出现的两种常见错误。我们可以看到“什么”是错误的症状原因的结果。一个良好有效的监控系统必须能够识别什么并向工程团队发出警报,以开始分析并识别故障的原因

表 7-1

什么可能被破坏以及为什么会被破坏的例子

|

什么

|

为什么

|
| --- | --- |
| 网站显示有一个 505。 | 我们用来获取数据的网络服务关闭了,我们无法访问它。 |
| FPT 失败。 | 磁盘已满,文件无法上传。 |

白盒和黑盒监控

正如我前面提到的,有两种系统监控:白盒和黑盒监控。白盒监控可以在所有软件和系统上执行,并且可以设置为显示软件的内部状态。通过白盒监控,我们使用软件在操作系统级别检查系统。白盒监控与基础设施没有直接关系。例如,我们可以使用的白盒监控软件有 Prometheus、Nagios、Zabbix 和 Splunk。所有这些软件都用于检查基础设施的状态,并直接显示出来。

相反,黑盒监控通过使用指标来检查系统。将系统视为一个黑盒意味着我们可以直接窥视软件内部,但是基于由一些相关度量定义的统计。黑盒监控软件包括 Prometheus、Nagios 和 Splunk。

我们可以把黑盒监控看作是告诉我们出了什么问题,但不是以一种预测的方式。黑盒监控指示问题何时出现,但没有给我们一种方法来预测问题何时会出现。

白盒监控基于对系统实际状态的检查。这意味着我们可以使用收集的数据。例如,我们可以使用 Splunk 来分析日志,并查看何时存在导致潜在问题的条件。

白盒监控直接检查操作系统,并识别可用于调试或识别问题原因的任何条件或指标,允许用户防止问题发生。例如,通过白盒,我们可以看到系统何时开始使用过多的内存,这会降低系统的速度。

我们可以将一条黄金法则应用于监控系统。它定义了四个黄金信号,我们可以用它们来了解系统。这些信号是:

  • 潜伏

  • 交通

  • 错误

  • 浸透

每个信号用于描述一种不一定与错误相关但可能导致后续错误的情况。

潜伏

延迟是我们为请求提供服务所需的时间。高延迟并不总是一个问题,但它可能表明系统响应缓慢,在某些情况下,这可能会导致错误。通过监控系统的正常时间响应来识别何时存在延迟问题并更快地修复问题,从而防止错误是很重要的。

交通

流量不是一个实际的错误,但它可以用来理解如何扩展我们的系统以避免问题或故障。交通测量系统有多少需求。我们可以设置不同的高级度量来定义这一点,例如每秒请求多少页面或者我们的服务器每秒接收多少请求。根据我们必须监控的系统的性质,可以采用这种方法。

错误

当我们度量一个错误时,我们不仅要确定明显的错误,比如 500 内部服务器错误或 NullPointerException,还要确定函数是否完成了任何工作,比如返回 200 错误但具有不同内容(比如不同数据)的页面。跟踪这两种情况非常重要。通常,监控系统只跟踪明显的错误情况,但一个非常好的监控系统也可以跟踪部分错误情况,如 200 错误,以及任何导致的错误数据。

浸透

饱和度用来衡量我们的系统有多“满”。我们通常使用不同的指标来确定饱和度,例如内存、文件系统、网络和 I/O 流水线。饱和度可以用于预测性分析,因为,例如,我们可以使用这个度量来了解我们必须使数据库或文件系统饱和多少次。在一个复杂的系统中,确定价值并不容易,因为资源 10%的改进可能会降低整个系统的速度。拥有测量饱和度的有效方法对于防止我们系统中的严重错误非常重要。

监控所有这四个信号是建立一个非常好和有效的监控系统的良好开端。

建立一个监控系统

到目前为止,我们已经分析了监控系统必须包括的内容以及系统中可能识别的内容。我们现在必须做的是定义构建我们的监控系统所需的要求和软件。想象一下,我们想从头开始构建我们的监控系统。我们必须考虑我们系统的主要组件,例如:

  • OS 监控:我们必须定义一套脚本来与一个非常好且有效的监控器交互。对于每一项措施,我们都可以通过电子邮件发出警报,迅速采取行动,防止类似问题的发生。操作系统可以提供关于系统状态的反馈。

  • 日志分析:我们必须有能力读取日志,识别软件中的错误并收集数据。

  • 警报:我们必须有能力发送电子邮件或连接到一些外部软件,以跟踪系统中发生的任何错误。

  • 仪表板:为了系统的可视化表示,并获得可观察的分析,可以设计一个与软件一起使用的仪表板,我们可以使用仪表板来获得实际系统的映像。

  • 软件集成:我们必须设计一些第三方集成来使用和连接我们的软件,以及一些外部警报或数据分析系统。

  • 监控四个信号:要有一个非常好且有效的监控系统,我们必须监控所有四个信号——延迟、流量、错误和饱和度。当我们监控这些信号并发出适当的警报时——如果其中一个信号显示异常——我们就有了一个非常有效的监控系统的基础。

前面的要点实质上是我们系统的主要需求。我们可以从头开始构建系统,或者使用一些现有的软件来执行监控。GCP 有一个名为 Stackdriver 的综合监控系统。因此,让我们开始使用 Stackdriver 来监控我们的云。

在 GCP 上配置栈驱动程序

谷歌云为创建监控系统提供了自己的解决方案。它被称为 Stackdriver,能够在一个地方监控所有云应用。基于我们所拥有的服务,可以创建图表并添加我们想要的信号。

Stackdriver 提供了与一些用于我们应用的常用软件的各种集成,如 NGINX、Cassandra、Apache 和 Elasticsearch。Stackdriver 生成的警报可以直接发送到警报软件,如 PagerDuty,或者 chat,如 Slack 或 HipChat。

要在我们的 GCP 中使用 Stackdriver,第一步是创建一个帐户。为此,只需连接到 GCP 控制台,打开左侧菜单,并在 Stackdriver 部分选择 Monitoring(参见图 7-2 )。

img/464715_1_En_7_Fig2_HTML.jpg

图 7-2

GCP 的菜单

当监控板打开时,它会要求选择我们想要监控的项目,如果是第一次,还会提醒我们开始 30 天的免费试用(图 7-3 )。

img/464715_1_En_7_Fig3_HTML.jpg

图 7-3

栈驱动程序监控部分

要启动 Stackdriver,请选择项目 PracticalDevOpsGCP,然后单击 CreateAccount 按钮。在我们的例子中,当帐户被创建时,它与我们的 GCP 相关联。

下一步是在 Stackdriver 下关联我们想要监控的项目。在我们的例子中,我们可以看到两个项目呈现在我们的云中。第一个选择如图 7-4 所示。

img/464715_1_En_7_Fig4_HTML.jpg

图 7-4

添加要监视的项目的 Stackdriver 部分

向下滚动页面,然后单击继续。这将打开配置 Stackdriver 的下一页。下一页要求配置 AWS 帐户。我们可以跳过这一部分,因为这对我们的目的来说是不必要的。

最后一部分用于配置 Stackdriver 代理。本页显示了配置客户端必须执行的命令。代理运行在我们的云实例中,收集我们部署监控系统所需的所有信息(图 7-5 )。

img/464715_1_En_7_Fig5_HTML.jpg

图 7-5

在我们的平台中配置 Stackdriver 客户端的命令

要配置客户端,请打开命令行并执行配置客户端所需的命令。我们必须执行的第一个命令是curl,下载安装客户端所需的脚本。

curl -sSO https://dl.google.com/cloudagents/install-monitoring-agent.sh

文件非常小,命令会立即返回。我们执行的下一个命令是安装客户机的命令。

sudo bash install-monitoring-agent.sh

该脚本返回安装的输出,但是我们必须注意的部分是脚本的最后一行,在那里我们可以找到安装的状态。

Created new plugin context.
option = PIDFile; value = /var/run/stackdriver-agent.pid;
option = Interval; value = 60.000000;
option = Hostname; value = ;option = FQDNLookup; value = false;
Created new plugin context..
===========================================================================
Installation of stackdriver-agent-5.5.2-382 completed successfully.
Please consult the documentation for troubleshooting advice:
https://cloud.google.com/monitoring/agent
You can monitor the monitoring agent's logfile at:
       /var/log/syslog
===========================================================================

现在安装已经成功完成,我们已经正确安装了 Stackdriver 监控客户端。要完成 Stackdriver 的安装,我们必须安装日志客户端。该客户端用于从虚拟机和其他第三方软件传输日志。这可用于识别与读取应用日志相关的问题。要安装日志客户端,我们首先必须下载安装它所需的脚本。

curl -sSO https://dl.google.com/cloudagents/install-logging-agent.sh

当脚本下载后,我们现在可以执行脚本来安装客户端。

sudo bash install-logging-agent.sh

与其他脚本一样,这个脚本返回脚本的状态,但是对我们来说,重要的是脚本的最后部分,在那里我们可以看到安装的状态(清单 7-1 )。

Installing default conffile /etc/google-fluentd/google-fluentd.conf ...
invoke-rc.d: could not determine current runlevel
invoke-rc.d: policy-rc.d denied execution of start.
Setting up google-fluentd-catch-all-config (0.7) ...
Restarting google-fluentd: google-fluentd.
===========================================================================
Installation of google-fluentd complete.
Logs from this machine should be visible in the log viewer at:
  https://console.cloud.google.com/logs/viewer?project=&resource=gce_instance/instance_id/6349597365373579285
A test message has been sent to syslog to help verify proper operation.
Please consult the documentation for troubleshooting advice:
  https://cloud.google.com/logging/docs/agent
You can monitor the logging agent's logfile at:
  /var/log/google-fluentd/google-fluentd.log
===========================================================================

Listing 7-1The Result of Logging the Stackdriver Client Installation

既然我们已经正确地安装了 Stackdriver 日志记录客户机,现在我们已经拥有了开始部署我们的监控系统所需的所有客户机。

创建应用

现在我们已经配置并安装了 Stackdriver 的所有必要元素,我们可以开始看看 Stackdriver 是如何工作的。仅出于演示目的,我们在 GCP 用 Apache CloudStack 创建了一个新的 PHP 7,并看看我们如何监控该系统。

我们采取的第一步是创建一个新的计算引擎实例。为此,我们必须打开控制台并创建计算引擎。我们可以通过单击“创建新实例”弹出窗口中的“创建”按钮来创建实例。这显示了我们需要为新实例插入的细节(图 7-6 )。

img/464715_1_En_7_Fig6_HTML.jpg

图 7-6

用于创建我们想要监视的新实例的页面

因为该实例主要用于展示监控如何与 GCP 一起工作,所以我们将该实例称为 stackdriverinstance 。选择一个小实例,允许 HTTP 和 HTTPS 流量,将其余文件保留为默认细节,然后点击创建,创建实例(图 7-7 )。

img/464715_1_En_7_Fig7_HTML.jpg

图 7-7

在 GCP 上创建的 Stackdriver 实例

创建了实例后,我们现在必须安装想要监控的软件。为此,我们打开一个 SSH shell,连接到实例。我们只需点击 SSH 标签就可以做到这一点。我建议打开一个新的浏览器,因为这会直接在我们的 VM 实例上打开一个 shell(清单 7-2 )。

Connected, host fingerprint: ssh-rsa 2048 3A:76:C8:B9:E5:0C:9B:30:BF:47:B5:92:9C:23:CA:88:BA:09:4E:76:48:39:C5:32:D0:C9:0B:37:94:6D:C4:15
Linux stackdriverinstance 4.9.0-7-amd64 #1 SMP Debian 4.9.110-3+deb9u2 (2018-08-13) x86_64
The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.
Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
pierluigi_riti@stackdriverinstance:~$

Listing 7-2The SSH Connection with the New Instance Created

有了对实例的访问,我们现在必须做的是安装软件。我们想安装 PHP 7 和 Apache HTTP web 服务器。因此,我们需要执行的第一个命令是更新库。我们选择 Debian 操作系统,因此更新库的命令是

sudo apt-get update

此命令生成输出,其中包含从系统更新的程序包列表。命令完成后,我们现在必须安装 PHP 7 和 Apache Web 服务器。为此,我们使用以下命令:

sudo apt-get install apache2 php7.0

软件开始检查安装所需的软件包,并要求我们确认软件的安装。点击 Y,软件继续安装(列表 7-3 )。

pierluigi_riti@stackdriverinstance:~$ sudo apt-get install apache2 php7.0
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  apache2-bin apache2-data apache2-utils libapache2-mod-php7.0 libapr1 libaprutil1 libaprutil1-dbd-sqlite3
  libaprutil1-ldap libicu57 liblua5.2-0 libperl5.24 libxml2 perl perl-modules-5.24 php-common php7.0-cli
  php7.0-common php7.0-json php7.0-opcache php7.0-readline psmisc rename sgml-base ssl-cert xml-core
Suggested packages:
  www-browser apache2-doc apache2-suexec-pristine | apache2-suexec-custom php-pear perl-doc
  libterm-readline-gnu-perl | libterm-readline-perl-perl make sgml-base-doc openssl-blacklist debhelper
The following NEW packages will be installed:
  apache2 apache2-bin apache2-data apache2-utils libapache2-mod-php7.0 libapr1 libaprutil1
  libaprutil1-dbd-sqlite3 libaprutil1-ldap libicu57 liblua5.2-0 libperl5.24 libxml2 perl perl-modules-5.24
  php-common php7.0 php7.0-cli php7.0-common php7.0-json php7.0-opcache php7.0-readline psmisc rename sgml-base
  ssl-cert xml-core

0 upgraded, 27 newly installed, 0 to remove and 0 not upgraded.
Need to get 21.0 MB of archives.
After this operation, 95.1 MB of additional disk space will be used.
Do you want to continue? [Y/n]

Listing 7-3The PHP-Apache Installation Package

安装软件后,我们现在可以检查服务器是否有响应。为了连接到服务器,我们可以使用链接http://<external_ip>。在我这里,链接是http://35.227.27.47/。这显示了 Apache HTTP 服务器的默认页面(图 7-8 )。

img/464715_1_En_7_Fig8_HTML.jpg

图 7-8

Apache HTTP 服务器默认页面

使用 Stackdriver 进行日志分析

配置好 Stackdriver 和我们想要监控的应用后,我们现在可以开始了解 Stackdriver 是如何工作的。我们之前已经为 Stackdriver 安装了日志客户端。这意味着我们可以直接从计算引擎页面查看应用的日志。要访问日志,单击实例名称附近的三个点并选择查看日志(图 7-9 )。

img/464715_1_En_7_Fig9_HTML.jpg

图 7-9

查看新实例的日志命令

查看日志打开 Stackdriver 的日志仪表板(图 7-10 )。

img/464715_1_En_7_Fig10_HTML.jpg

图 7-10

Stackdriver 的日志仪表板

Stackdriver 是一个非常成熟的监控系统,logging dashboard 为我们提供了许多工具来进行分析。为了帮助分析日志,我们可以对我们选择查看的日志应用一些过滤器。

我们可以使用左边的第一个下拉菜单来更改我们想要分析的日志。我们可以选择想要分析哪种日志。例如,我们可以基于这些日志家族来读取日志:防火墙规则、项目、VM 实例和存储桶。这些只是不同种类原木的例子。对于每个部分,我们可以有子详细日志。例如,在 VM 实例的情况下,我们可以选择我们系统中的所有实例(图 7-11 )。

img/464715_1_En_7_Fig11_HTML.jpg

图 7-11

虚拟机实例子菜单

我们可以改变我们想要分析的日志类型,基于我们的选择,我们可以有更多的子菜单。这有助于我们对系统进行非常详细的分析。

我们可以添加另一个过滤器。例如,我们可以从菜单上的选项中选择想要读取的日志。默认情况下,我们看到“所有日志”,但是如果我们更改我们看到的日志,我们可以添加另一个过滤器。例如,我们可以选择只查看错误或调试信息。我们还可以添加一个时间过滤器。我们可以选择只查看最后一小时的日志。我们可以添加一个文本过滤器,在日志中进行自由文本搜索。

我们可以使用过滤器来分析日志和调试应用。我们可以使用过滤器来追溯时间,使用日志仪表板来分析和确定错误的根本原因。

栈驱动程序中的警报

没有好的警报系统,就不可能有好的监控系统。要在 Stackdriver 中创建警报,请打开 Stackdriver 部分中的 Monitor 页面。这显示了用于在 Stackdriver 中创建警报和管理警报策略的仪表板(图 7-12 )。

img/464715_1_En_7_Fig12_HTML.jpg

图 7-12

Stackdriver 监控仪表板

我们可以看到,我们已经创建了一些与应用相关的基本警报。我们想要创建的基本警报是正常运行时间,以了解应用是否是活动的。

要创建正常运行时间警报,我们必须首先创建一个监视器。为此,我们从监控正常运行时间下的控制面板创建检查中进行选择。这将打开警报的配置页面。我们可以在支票上配置不同的参数。我们可以设置警报的标题,在我们的例子中是practicaldevopsgcp _ uptime,以及我们想要的检查类型。我们可以选择三种不同的类型:

  • 超文本传送协议

  • 安全超文本传输协议

  • 三氯苯酚

在我们的例子中,我们使用 HTTP 类型。这是因为我们要检查的实例响应 HTTP 链接。为了有效,警报必须知道要检查什么类型的资源。这可以在资源类型部分进行配置。我们可以选择以下类型之一:

  • 情况

  • 云负载均衡器

  • 应用引擎

  • 统一资源定位器

因为我们想要监视一个实例,所以我们选择 instance。现在,我们可以选择将哪个实例应用于警报,以及是只应用于单个实例还是一组实例。我们可以在“应用于”部分进行配置。为了有效,警报必须在一定时间后检查系统。我们可以在“检查频率”部分进行配置。默认情况下,检查每五分钟开始一次。我们可以简单地通过从下拉菜单中选择不同的时间来改变这个时间间隔。现在,我们已经为警报设置了所有值,我们可以保存警报并开始监控我们的系统。我们可以测试警报,看看它是否正常工作。要测试它,我们只需点击测试按钮。这显示了操作的结果(图 7-13 )。

img/464715_1_En_7_Fig13_HTML.jpg

图 7-13

我们创建的警报的测试结果

我们可以看到测试被成功执行,结果是 a 200。这意味着警报可以到达我们想要监控的实例和服务。

策略警报配置

我们现在有一个警报来检查我们系统的状态。警报本身并没有多大用处。这是因为拦截它需要一个人站在仪表板前,看警报何时发出。

我们可以将警报与特定的策略警报相关联。策略警报用于通知人们系统中发生了错误。例如,我们可以发送电子邮件或在 HipChat 聊天室中添加通知,或者我们可以通过单击“创建警报策略”按钮来创建策略警报。这将显示一个页面,我们可以在该页面上配置我们的策略。我们可以配置策略的四个方面。

  • 情况

  • 通知

  • 文件

  • 名字

该页面允许我们为策略创建特定的值。第一部分是条件。条件是我们可以做出的所有选择来产生警报。我们可以选择四种类型来创建警报。

  • 基本类型:在这个部分,我们可以为我们的警报选择一些基本值。我们还可以指定一个指标阈值,例如 CPU 使用率或磁盘 I/O 读写。我们可以指出阈值的值,以及它是否必须高于或低于该阈值以及持续多长时间。我们可以设置的另一个条件是缺少度量标准。例如,如果我们在 30 分钟内没有任何 CPU 使用,我们可以发出警报。

  • 高级类型:该指标用于定义特定时间范围内资源使用的增加或减少。例如,它可以用于查看资源使用量的增加情况,并相应地进行放大或缩小。

  • 基本健康状况(Basic Health):这个指标用于对服务的正常运行时间进行基本检查。我们使用之前创建的警报来配置此条件。

  • 高级健康:用于检查实例上的进程。我们可以指定要检查的流程以及它的存活时间。

下一部分是通知。这用于向人发送警报以供处理或确认。基本通知是电子邮件。这意味着我们向我们配置的特定收件人发送电子邮件。在监控系统中,这种电子邮件可以发送给一个组。这样,该组的所有成员都会收到警报并可以解决它。

文档是一个字段,我们可以用它来记录我们想要的警报类型。为警报定义良好的文档非常重要。我们可以使用一些标记来创建与警报相关的文档。

最后一部分是策略的名称,本质上就是策略的名称。

我们现在可以为之前创建的警报创建策略。在“monitoring”部分,我们创建了一个新的策略警报。我们为正常运行时间创建了策略,但是我们必须创建一个基本的健康检查,以便在出现错误时通过电子邮件通知。我们选择基本健康状况和我们想要监控的实例。我们将一封电子邮件连接到通知部分,添加一些文档,给出一个名称,然后保存。现在我们有了一个与警报相关的策略。

创建仪表板

监控系统的一个基本组件是一个仪表板,用于图形化显示我们的系统。这用于获得系统状态的清晰和即时的概念。

要在 Stackdriver 中创建仪表板,我们选择仪表板,然后选择创建仪表板。这将把我们带到一个空白仪表板,我们可以用它来创建我们的个人仪表板(图 7-14 )。

img/464715_1_En_7_Fig14_HTML.jpg

图 7-14

我们用来创建个人仪表板的空白仪表板

第一步是更改仪表板的名称。我们可以简单地更改名称,只需单击无标题仪表板,然后为仪表板配置我们想要的名称,在我的例子中,是 Stackdriver GCP 仪表板。我们可以通过单击 ADD CHART 按钮来添加第一个图表。这将打开创建第一个图表的页面。首先,我们必须创建想要添加到图表中的资源。当我们单击 Find Resource type and metrics 文本框时,页面会显示一个下拉框,从中我们可以开始选择要添加到图表中的资源类型。在我们的例子中,我们添加了一个 GCE VM 实例。之后,我们可以选择想要监控的指标。在我们的案例中,我们选择正常运行时间。点击保存按钮创建图表(参见图 7-15 )。

img/464715_1_En_7_Fig15_HTML.jpg

图 7-15

“添加图表”页面

我们现在已经创建了第一个图表。我们可以在仪表板上添加另一个图表。如果我们想要添加 CPU 负载平均值,我们可以重复刚才的步骤,但是在这个实例中,我们检查指标 CPU 使用率。仪表板将如图 7-16 所示。

img/464715_1_En_7_Fig16_HTML.jpg

图 7-16

有两个监视器的仪表板

测试仪表板

现在我们已经有了我们的监控系统,我们必须确保它正常工作。因此,我们停止我们的实例,五分钟后,检查警报是否被正确发出。停止实例,看看发生了什么。当检查的时间到来时,我们可以看到仪表板揭示了事件(图 7-17 )。

img/464715_1_En_7_Fig17_HTML.jpg

图 7-17

仪表板显示正常运行时间的错误

因为我们设置了电子邮件提醒,所以我们会收到一条消息,告诉我们此时正在发生的事件。该消息如图 7-18 所示。

img/464715_1_En_7_Fig18_HTML.jpg

图 7-18

策略警报发送的邮件

监控系统显示错误,并通知人们系统中发生了错误。我们现在已经在 GCP 建立了第一个监控系统。

结论

创建一个良好的监控系统对于我们的 DevOps 取得良好的结果至关重要。建立一个好的监控系统并不容易。它需要经验和耐心观察我们的软件。监控系统的主要目标是识别出现的任何错误并对其做出反应,并且可能用于调试生产中的错误。有很多软件可以用来创建我们自己的监控系统。在本章中,我们使用 Google 提供的解决方案 Stackdriver 来监控 GCP。监控是一个非常大的领域,在这一章中,我们看到了可以用来设计系统的常用技术。所有的技术都可以适应你最喜欢的软件。监控的重要部分是其背后的理论和警报系统。

八、在 GCP 创建和管理基础设施

DevOps 和云最重要的方面之一是基础架构。当我们在云中设计和实现基础设施时,我们总是希望结果是相同的。为此,创建和维护我们自己的虚拟映像非常重要。在本章中,你将学习如何在谷歌云平台(GCP)中创建和管理你自己的虚拟映像。

基础设施作为代码

你已经看到 DevOps 运动在 2008 年多伦多敏捷大会上正式诞生,当时 Patrick Debois 发表了演讲“敏捷基础设施和运营”在他的演讲中,Debois 介绍了一种将开发与基础设施和运营功能结合在一起的方法。于是,DevOps 运动诞生了。

这一愿景为我们称之为代码或 IaC 的基础设施开辟了道路。当我们谈论 IaC 时,我们指的是可以以编程方式设计和发布的基础设施。例如,当我们创建与我们的软件相关的 Docker 映像时,或者当 Chef、Puppet 或任何其他配置软件被编程来准备我们的基础设施时。

当我们考虑采用 IaC 时,主要是确保整个服务器的一致性,并在每次执行相同的操作时获得一致的操作结果。

采用 IaC 可以大大减少操作和发布时间。这是因为我们可以使用与生产中完全相同的软件来创建临时服务器,这意味着开发团队可以在类似于生产的环境中测试软件。这是可能的,因为我们定义了基础设施,并以类似于部署软件的方式部署它。

有了 IaC,当我们设计和实现我们的基础设施时,我们基本上促进了在开发中使用的过程。这样做是为了提高操作的一致性、系统的稳定性以及操作的可重复结果。

当我们在基础架构中采用 IaC 时,是因为我们想要实现一些特定的目标。

  • 简单支持:当我们实现 IaC 时,支持更简单,因为更容易将功能状态返回给基础设施。

  • 制定基础设施变更程序:有了 IaC,我们可以每天或每小时更新我们的基础设施。这是因为我们遵循发布软件时使用的相同程序。

  • 易故障恢复:有了 IaC,在灾难发生的情况下,基础设施可以以确定的状态回来,因为我们可以添加软件,比如 Terraform 或者 GCD 来修复。

  • 持续改进:IaC 每天都在改进我们的基础设施,因为我们遵循与软件开发相同的原则。任何小的改进都在产品化阶段发布,经过测试,最后发布到产品中。

云是 IaC 的自然环境。当我们在云环境中创建新机器时,我们会创建一个新的虚拟机(VM ),它具有特定版本的操作系统和特定版本的软件。当我们想要启动一个新的服务器时,我们可以轻松地启动我们创建的虚拟机,并将其作为新服务器的基础。

基础设施作为代码原则

要将我们的基础设施转向 IaC,我们必须遵循一些基本原则:

  • 可重复性

  • 一致性

  • 一次性

这三项原则构成了 IaC 的支柱,我们在设计基础设施时必须牢记这三项原则。我们现在将更详细地考虑这些原则。

我们必须牢记的第一个原则是,每个基础设施都必须是可重复的。当我们设计 IaC 时,它必须是可重复的。当基础设施具有用于复制它的代码和/或过程时,该基础设施是可重复的,这些代码和/或过程总是具有相同类型的基础设施。

第二个原则直接来源于第一个原则。每个 IaC 都必须一致。这意味着,每次我们在基础架构中重建或添加新节点时,我们都必须得到相同的结果。至少,我们不能改变基础设施的定义。

我们必须遵循的最后一个原则是,每个 IaC 必须是可处置的。这意味着我们必须有能力在我们需要的每一个机会销毁、更新和调整大小。可处置的能力有助于扩展基础架构并在基础架构运行时修复问题。

要贯彻这个原则,必须遵循一定的做法。这些将推动我们的实施,并确保我们基础设施的正确功能。

  • 在文件中定义基础设施:为了确保一致和可重复的过程,它必须总是给出相同的结果。为此,我们可以在一个文件中定义我们的基础结构。在这个文件中,我们可以定义基础设施所需的一切,例如,DNS、磁盘空间、操作系统等。将所有这些信息保存在一个文件中有助于以编程方式定义我们的基础设施。

  • 定义文档系统和流程:明确定义系统或流程在我们的基础架构中的作用非常重要。为了实现这一点,记录系统和过程是非常重要的。拥有清晰的文档有助于长期维护基础设施。团队成员可以阅读脚本并遵循文档来进行改进或修复基础设施的问题。

  • 对基础设施文件进行版本控制:为了维护基础设施的一致性,并建立一个良好的流程,对基础设施的定义文件进行版本控制是非常重要的。通过对文件进行版本控制,我们可以轻松地将基础架构恢复到任何定义的状态。这有助于流程的一致性和基础设施的可用性。在出现致命错误的情况下,为基础结构的文件进行版本控制对于回滚基础结构非常重要。

  • 首先测试基础设施:我们在一个文件中定义我们的基础设施,并且我们使用一个程序将它放置到位。这意味着我们可以先测试基础设施,然后将其投入生产。例如,测试基础设施,创建一个临时基础设施来测试我们的定义文件。

  • 小改:对基础设施进行小改总比大改好。基础设施中的一个小变化使得隔离问题的根本原因并修复它变得更加容易。如果我们在基础设施中发布一个大的改变,那么做起来会更加困难,我们必须回滚所有的东西,让基础设施再次工作。

当我们遵循这些实践时,我们可以为我们的基础设施定义一个状态,然后确保我们有这个特定的状态。我们创建被定义为定义的配置状态

注意

通过定义的配置状态,我们指的是可以在特定的明确定义的状态下观察和发现的基础设施。这可以通过在文件中描述基础设施并在每一步定义基础设施的状态来实现。我们还可以查询基础设施,以获得特定时刻基础设施的答案。

当我们有一个处于已定义配置状态的基础设施时,我们可以很容易地观察和监控它。因为基础结构是在文件中定义的,所以我们可以定义基础结构每一步的状态,例如,我们可以在更新基础结构的某个特定部分时禁用该警报。这降低了噪音,使警报更有效。

作为代码的基础设施架构

当我们想要实现 IaC 时,我们本质上想要设计一个集中的系统来管理我们的基础设施。我们可以使用三种不同的方法来定义基础架构的定义文件:

  • 宣言的

  • 必要的

  • 聪明的

声明式方法描述了应该是什么样的配置。使用声明性方法,我们定义了基础设施的期望状态。系统执行将基础设施置于我们定义的状态所需的所有操作。

命令式方法专注于配置应该如何。系统一个接一个地执行操作,将系统移动到定义的状态。

智能方法专注于为什么基础设施必须具有这种特定的状态和状况。该决策考虑了基础架构所有组件的状态,并分析了基础架构的共同组件的状态。为了配置基础设施,配置工具可以使用两种不同的方法:

这两种方法的区别仅在于中央服务器如何配置客户端。使用 pull 方法,客户端询问中央服务器配置状态是什么。通过方法,服务器告诉客户端配置状态是什么。

谷歌云平台中的基础设施代码

在 GCP,我们可以使用名为Google Deployment Manager(GDM)的工具创建 IaC。这允许我们通过以声明的方式创建 YAML 文件来定义基础架构的所有资源。

我们还可以使用 Python 或 Jinja2 模板来创建可配置的模板文件。例如,我们可以为负载均衡器、自动伸缩和实例组定义一个参数。有了 GDM,我们可以定义一个模板。这可以在我们的部署中重用,当然,也可以存储在存储库中。

GDM 是围绕一些基本组件构建的,我们必须了解这些组件才能有效地使用该工具。这些组件是

  • 配置

  • 模板

  • 资源

  • 类型

  • 显示

  • 部署

配置

配置是 YAML 文件,我们在其中定义部署所需的资源列表。这可以是整个基础架构,或者更有可能是我们基础架构的一部分。

要在文件中定义一个资源,我们必须使用语法resources:。每个资源必须由三个部分定义:

  • name:我们要创建的资源的名称

  • type:我们想要创建的资源类型

  • properties:为资源定义的参数

清单 8-1 中给出了一个示例资源文件。

resources:
- name: gcp-devops-vm
  type: compute.v1.instance
  properties:
    zone: us-central1-a
    machineType: https://www.googleapis.com/compute/v1/projects/ practicaldevopsgcp/zones/us-central1-a/machineTypes/f1-micro
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/rhel-6-v20180611
    networkInterfaces:
    - network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp/global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

Listing 8-1Sample Configuration File

模板

当我们想要定义一个简单的映像时,这种配置很好,比如前面的清单,但是当我们想要定义一个更复杂的基础设施时,这是不够的。在这种情况下,我们可以创建一个模板。

模板本质上是一个可重用的配置。模板在外部文件中定义,在配置中用作type。该模板可以使用类似于 YAML 语法的 Jinja 2.8 语法编写,也可以使用 Python 2.7 编写。清单 8-2 展示了一个静态 Jinja 模板的例子。

- name: gcp-jinja-template
  type: compute.v1.instance
  properties:
    zone: us-central1-a
    machineType: zones/us-central1-a/machineTypes/n1-standard-1
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: projects/debian-cloud/globimg/debian-8
    networkInterfaces:
    - network: global/networks/default

Listing 8-2Example of a Jinja Template

我们可以看到 Jinja 模板类似于一个普通的模板定义。使用 Python 给了我们更多的灵活性。这是因为我们可以使用语言和库来定义我们的模板。Python 中定义的模板示例如下(清单 8-3 )。

"""Creates a Compute Engine Instance."""

def GenerateConfig(context):
  """Generate configuration."""

  resources = []
# [START use_basic_template]
  resources.append({
      'name': 'gcp-template',
      'type': 'compute.v1.instance',
      'properties': {
          'zone': 'us-central1-a',
          'machineType': 'zones/us-central1-a/machineTypes/n1-standard-1',
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage':
                      'projects/debian-cloud/globimg/debian-8'
              }
          }],
          'networkInterfaces': [{
              'network': 'global/networks/default'
          }]
      }
  })
# [END use_basic_template]
  return {'resources': resources}

Listing 8-3Python Template Definition

当我们使用 Python 定义模板时,有两个必要的要求:

  • 代码必须定义一个名为GenerateConfigurationgenerate_configuration的方法。如果两种方法都使用,编译器会优先考虑generate_configuration方法。

  • 因为配置是在 YAML 文件中定义的,所以该方法必须返回有效的 YAML 文件。

在前面的代码中,我们简单地通过编写 YAML 文件定义了一个GenerateConfiguration方法。还有另一种方法来定义配置。我们可以使用一个 Python 变量,使文件的某些部分动态化(清单 8-4 )。

"""Creates a Compute Engine Instance."""

def GenerateConfig(context):
  """Generate configuration."""

  resources = []
# [START use_template_with_variables]
  resources.append({
      'name': 'gcp-' + context.env['deployment'],
      'type': 'compute.v1.instance',
      'properties': {
          'zone': 'us-central1-a',
          'machineType': ".join(['zones/', context.properties['zone'],
                                  '/machineTypes/n1-standard-1']),
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage':
                      'projects/debian-cloud/globimg/debian-8'
              }
          }],
          'networkInterfaces': [{
              'network': 'global/networks/default'
          }]
      }

  })
# [END use_template_with_variables]
  return {'resources': resources}

Listing 8-4The Template Created with Python Variables

在前面的代码中,我们定义了一些变量来使配置更加动态,而不是只创建 YAML 文件。

使用 Python,我们可以很容易地创建一个非常复杂的动态配置,并且我们可以根据环境变量修改配置,例如修改名称。例如,当我们希望根据虚拟机运行的服务器来识别虚拟机时,这非常有用。

当我们定义了模板后,我们可以将它导入到我们的配置文件中。我们可以导入带有扩展名.py.jinja或者任何非模板扩展名的模板,比如.txt。为了导入文件,我们可以指定文件的路径,如果我们想要指定一个名称,请指定名称。可以导入多个模板,当然,我们可以混合使用模板。例如,可以导入一个 Python 和一个 Jinja 模板(清单 8-5 )。

imports:
  - path: /path/to/gcp_template.jinja
  - path: gcp_new_template.py
    name: gcp_infrastructure.py

Listing 8-5Code to Import a Template in the Configuration File

当我们导入模板后,我们可以像在配置文件中使用type一样使用它(清单 8-6 )。

imports:
  - path: /path/to/gcp_template.jinja
resources:
 - name: my_gcp_template
 -type: /path/to/gcp_template.jinja

Listing 8-6Importing and Using a Template

可以使用没有配置文件的模板。为此,我们可以使用命令行并直接编译模板。相关命令如清单 8-7 所示。

gcloud deployment-manager deployments create my-vm-gcp \
    --template gcp_template.jinja \
    --properties zone:us-central1-b

Listing 8-7How to Invoke the Template by the Command Line

资源

资源表示单个 API 资源。这个 API 可以由 Google 提供,或者我们可以通过定义一个新的类型来创建我们的资源。例如,谷歌计算引擎就是一种资源。我们可以使用 Jinja 或 Python 模板创建我们的资源。资源是我们创建配置文件时必须定义的基本信息。

类型

资源用于定义我们想要做什么,但是类型用于标识资源并允许我们创建部署过程。

类型用于标识资源。有两种类型的资源:

  • 基础类型:这是一个单一的资源,就像 Google Compute Engine。

  • 复合类型:这是一个集合或资源。

基本类型本质上是 Google 提供的单一类型的资源,如 Google 计算引擎、基本存储或 SQL。

复合类型包含一个或多个模板。这些模板被预先配置为协同工作,当然,也可以使用 Python 或 Jinja 进行定义。复合类型通常用于定义模板的一部分,可以很容易地重用。例如,我们可以为我们的网络创建一个负载均衡器资源。

显示

清单是对我们原始配置的描述。这是只读的,通常用于提供关于部署的尽可能多的信息。我们使用清单来描述资源和配置类型。清单以如下形式返回值(清单 8-8 )。

config:
  content: |
    resources:
    - name: gcp-first-vm
      type: compute.v1.instance
      properties:
        zone: us-east1-b
        machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/f1-micro
        disks:
        - deviceName: boot
          type: PERSISTENT
          boot: true
          autoDelete: true
          initializeParams:
            sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-9
        networkInterfaces:
        - network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
          accessConfigs:
          - name: External NAT
            type: ONE_TO_ONE_NAT
    - name: gcp-second-vm
      type: compute.v1.instance
      properties:
        zone: us-east1-b
        machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/g1-small
        disks:
        - deviceName: boot
          type: PERSISTENT
          boot: true
          autoDelete: true
          initializeParams:
            sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-9
        networkInterfaces:
        - network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
          accessConfigs:
          - name: External NAT
            type: ONE_TO_ONE_NAT
expandedConfig: |
  resources:
  - name: gcp-first-vm
    properties:
      disks:
      - autoDelete: true
        boot: true
        deviceName: boot
        initializeParams:
          sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-9
        type: PERSISTENT
      machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/f1-micro
      networkInterfaces:
      - accessConfigs:
        - name: External NAT
          type: ONE_TO_ONE_NAT
        network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
      zone: us-east1-b
    type: compute.v1.instance
  - name: gcp-second-vm
    properties:
      disks:
      - autoDelete: true
        boot: true
        deviceName: boot
        initializeParams:
          sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-9
        type: PERSISTENT
      machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/g1-small
      networkInterfaces:
      - accessConfigs:
        - name: External NAT
          type: ONE_TO_ONE_NAT
        network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
      zone: us-east1-b
    type: compute.v1.instance
id: '2742029496220147117'
insertTime: '2018-08-16T13:49:06.882-07:00'
layout: |
  resources:
  - name: gcp-first-vm
    type: compute.v1.instance
  - name: gcp-second-vm
    type: compute.v1.instance
name: manifest-1534452546871
selfLink: https://www.googleapis.com/deploymentmanager/v2/projects/practicaldevopsgcp-197023/global/deployments/gcp-vm-deployment/manifests/manifest-1534452546871

Listing 8-8Example of a Manifest

我们可以看到清单描述了在我们的部署中实际定义和安装的所有资源。

部署

部署是部署和管理的资源的集合。我们可以使用配置文件和命令行来创建部署。执行部署的命令如清单 8-9 所示。

gcloud deployment-manager deployments create gcp-first-deployment \
    --config gcp-vm.yaml

Listing 8-9Command for Creating a New Deployment in GCP

要定义配置文件,我们必须使用--config选项,后跟配置文件的名称。当部署完成时,我们可以通过使用以下命令(清单 8-10 )来查看部署是否正确配置:

gcloud deployment-manager deployments describe gcp-first-deployment

Listing 8-10Command to Describe the Deployment

从谷歌云部署管理器开始

到目前为止,您已经看到了 Google Cloud Deployment Manager 的基本概念。至此,我们可以开始定义自己的 IaC 了。

如您所知,我们可以通过创建配置文件来定义我们的基础架构,并且可以通过命令行来创建部署。这实质上是在我们的基础设施中部署我们的资源。第一步是定义我们的配置文件。首先,我们用 Debian 映像创建一个简单的虚拟映像。该文件如清单 8-11 所示。

resources:
- type: compute.v1.instance
  name: gcp-first-configuration-vm
  properties:
    zone: us-east1-b
    machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-central1-f/machineTypes/f1-micro
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-9
    networkInterfaces:
    - network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

Listing 8-11The First Configuration File for Our Google Cloud

注意

当我们创建基础设施时,我们可以为我们的 VM 指明系列,如前面的代码所示。我们可以在这里找到我们操作系统的所有系列: https://cloud.google.com/compute/docs/images#os-compute-support

在这个配置文件中,我们指出了定义我们的虚拟机所必需的一些信息。我们定义了一个zone,在我们的例子中是us-east1-b;一个machineType,在我们这里是f1-micro;启动盘;和一个随机的 IP 地址。这些值是识别和部署我们的虚拟机所必需的。我们现在必须部署我们的基础设施。为此,我们必须使用命令行。我们用于部署的命令是

gcloud deployment-manager deployments create gcp-first-deployment --config gcp_vm.yaml

注意

YAML 文件是一个空间敏感文件。在这本书出版的时候,一些映像可能已经被删除。因此,建议您从与本书相关的 Git 下载该文件。

该命令实质上就是我们描述的创建新部署的内容。我们现在可以在 Google Cloud 控制台中构建或导入文件并运行它。它部署我们的资源并显示操作的结果(清单 8-12 )。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud deployment-manager deployments create gcp-first-deployment --config gcp_vm.yaml
The fingerprint of the deployment is 4Sb9EZuc2qx92SWmbnmLig==
Waiting for create [operation-1534453555625-57393cf1b7c2b-27da25f9-d094a48e]...done.
Create operation operation-1534453555625-57393cf1b7c2b-27da25f9-d094a48e completed successfully.
NAME                        TYPE                 STATE      ERRORS   INTENT
gcp-first-configuration-vm  compute.v1.instance  COMPLETED  []

Listing 8-12Result of the Google Deployment of Our Resource

我们现在已经成功完成了第一部分基础设施的安装。我们可以看到我们新的基础设施的细节,并且可以描述它。我们必须用来描述基础设施的命令是

gcloud deployment-manager deployments describe gcp-first-deployment

这个命令返回关于我们部署的详细信息(清单 8-13 )。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud deployment-manager deployments describe gcp-first-deployment
---
fingerprint: 4Sb9EZuc2qx92SWmbnmLig==
id: '4465263803798936028'
insertTime: '2018-08-16T14:05:55.750-07:00'
manifest: manifest-1534453555753
name: gcp-first-deployment
operation:
  endTime: '2018-08-16T14:06:14.117-07:00'
  name: operation-1534453555625-57393cf1b7c2b-27da25f9-d094a48e
  operationType: insert
  progress: 100
  startTime: '2018-08-16T14:05:56.070-07:00'
  status: DONE
  user: pierluigi.riti@gmail.com
NAME                        TYPE                 STATE      INTENT
gcp-first-configuration-vm  compute.v1.instance  COMPLETED

Listing 8-13Command Used to Describe the Deployment

这个命令告诉我们,我们的 VM 已经创建好了,可以使用了。如果我们转到计算引擎并连接到它,我们可以通过计算引擎页面直接检查并连接到虚拟机(图 8-1 )。

img/464715_1_En_8_Fig1_HTML.jpg

图 8-1

计算引擎页面,显示了我们的第一个配置

因为我们使用 GDM 创建了基础设施,所以我们也可以使用 GDM 页面检查配置。我们可以在工具菜单下找到资源(图 8-2 )。

img/464715_1_En_8_Fig2_HTML.jpg

图 8-2

“部署管理器”页面

现在我们可以看到代码定义了我们的第一个基础设施。为了完成生命周期,我们必须学会如何摧毁基础设施。我们用来摧毁基础设施的命令是

gcloud deployment-manager deployments delete gcp-first-deployment

这显示了一个关于删除我们的基础结构的确认问题。我们可以按 y 确认操作。这将删除我们的基础结构。该操作可能需要几分钟才能完成。结果是一条消息通知我们基础设施已经被删除(清单 8-14 )。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud deployment-manager deployments delete gcp-first-deployment
The following deployments will be deleted:
- gcp-first-deployment
Do you want to continue (y/N)?  y
Waiting for delete [operation-1534453886807-57393e2d8ebd8-e48703b3-d3b921ed]...done.
Delete operation operation-1534453886807-57393e2d8ebd8-e48703b3-d3b921ed completed successfully.

Listing 8-14Command to Delete an Infrastructure

您已经学习了如何创建基本的 IaC,但是我们的示例非常简单。真正的基础设施比简单的虚拟机更复杂。让我们把注意力转向如何创建更复杂的基础设施,更重要的是,如何使用模板来定义我们的基础设施。

升级我们的基础设施

当我们定义基础架构时,我们并不只有一台虚拟机。我们的基础设施可以有多个单一资源。例如,我们可以拥有不同的虚拟机、负载均衡器等。在上一节中,您定义了一个只有一个虚拟机的配置文件。现在,您将看到如何使用我们的示例创建一个包含多个资源的配置文件。假设我们想要为我们的基础架构创建两个简单的虚拟机。为此,我们将另一个虚拟机添加到前面的虚拟机中。其文件如下(列表 8-15 )。

resources:
- name: gcp-first-vm
  type: compute.v1.instance
  properties:
    zone: us-east1-b
    machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/f1-micro
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-8
    networkInterfaces:
    - network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT
- name: gcp-second-vm
  type: compute.v1.instance
  properties:
    zone: us-east1-b
    machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/g1-small
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-8
    networkInterfaces:
    - network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

Listing 8-15The Configuration File with Two Different VMs

在这个文件中,我们创建了另一个不同大小的虚拟机。为了创建一个新资源,在本例中是一个虚拟机,我们可以向我们的部署添加另一个resource。在我们的案例中,我们从

- name: gcp-second-vm
  type: compute.v1.instance
  properties:
    zone: us-east1-b
    machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/g1-small
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-8
    networkInterfaces:
    - network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/default
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT

您可以看到,我们重新开始了一个新的部分来定义一个新的资源。我们不必重新打开另一个资源部分。这是因为,在一个资源下,我们可以拥有多个单一资源。我们现在可以部署我们的资源,查看在系统上创建的两个实例。部署的命令是

gcloud deployment-manager deployments create gcp-second-deployment --config gcp-two-vm.yaml

结果显示了两个虚拟机的状态(列表 8-16 )。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud deployment-manager deployments create gcp-vm-deployment --config gcp-two-vm.yaml

The fingerprint of the deployment is tReLCx9Y7aa7QzguA54V2Q==
Waiting for create [operation-1534452546303-5739392f27419-af9ff154-2b46debe]...done.
Create operation operation-1534452546303-5739392f27419-af9ff154-2b46debe completed successfully.
NAME           TYPE                      STATE      ERRORS  INTENT
gcp-first-vm   compute.v1.instance       COMPLETED  []
gcp-second-vm  compute.v1.instance       COMPLETED  []

Listing 8-16Result When We Deploy Both VM Images

扩展和定制我们的部署

您已经学习了如何用一两个资源创建一个简单的配置文件。这很好,但仍不足以创建我们的部署。当我们在配置文件中创建多个资源时,我们可能会遇到与资源之间的依赖关系相关的问题,例如,当我们使用我们创建的第一个网络时。默认情况下,GDM 并行创建所有资源,这意味着我们不确定在需要时资源是否到位。

GDM 帮助我们定义使用资源的顺序。我们可以为资源创建一个层次结构,由 GDM 决定安装资源的顺序和并行度。我们不能推动部署的顺序,但是我们可以以动态的方式定义类型的依赖关系。我们可以通过定义一个引用来实现。

该引用用于强制 Deployment Manager 依赖于依赖项,然后仅当所有依赖项都可用时才创建特定的资源。在我们的部署中使用引用为我们提供了一些优势。

  • 我们可以给部署经理一个命令来解析资源。例如,想象一下,我们必须创建一个连接了网络的虚拟机。部署管理器首先创建网络,然后在网络可用时创建虚拟机。

  • 我们可以使用引用来自引用配置的内部资源。我们可以用一个selfLink来表示内部资源。例如,我们可以添加对特定 IP 网络的引用,并使用它来定义我们的网络

可以使用以下语法在我们的配置文件中指明资源

$(ref.RESOURCE_NAME.PROPERTY)

假设我们想为之前的虚拟机指定一系列 IP 地址。为此,我们首先必须在配置文件中创建资源,然后将我们的 VM 引用到这个新资源。要创建网络资源,我们首先必须将其添加到配置文件的末尾。我们添加的是

- name: gcp-network
  type: compute.v1.network
  properties:
    IPv4Range: 10.10.0.1/16

既然我们已经创建了新的网络资源,我们需要连接我们之前的配置文件上的网络接口,并用下面的代码更新它(清单 8-17 )。

networkInterfaces:
- network: $(ref.gcp-network.selfLink)
How we can see we use the reference for use the previous configuration network we had created, the final file is like that:
resources:
- name: gcp-first-vm
  type: compute.v1.instance
  properties:
    zone: us-east1-b
    machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/f1-micro
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-8
    networkInterfaces:
    - network: $(ref.gcp-network.selfLink)
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT
- name: gcp-second-vm
  type: compute.v1.instance
  properties:
    zone: us-east1-b
    machineType: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/machineTypes/g1-small
    disks:
    - deviceName: boot
      type: PERSISTENT
      boot: true
      autoDelete: true
      initializeParams:
        sourceImage: https://www.googleapis.com/compute/v1/projects/debian-cloud/globimg/debian-8
    networkInterfaces:
    - network: $(ref.gcp-network.selfLink)
      accessConfigs:
      - name: External NAT
        type: ONE_TO_ONE_NAT
- name: gcp-network
  type: compute.v1.network
  properties:
    IPv4Range: 10.10.0.1/16

Listing 8-17Configuration File with the Two VMs and the Network Configuration

我们现在尝试新的配置文件。部署配置文件的命令是

gcloud deployment-manager deployments create gcp-two-vm-network --config gcp-two-vm-network.yaml

操作结果如清单 8-18 所示。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud deployment-manager deployments create gcp-two-vm-network --config gcp-two-vm-network.yaml
The fingerprint of the deployment is FTlZdQbTWsmDfmCGHfbZZQ==
Waiting for create [operation-1534455595680-5739448b44101-0e70d587-5dc88e00]...done.
Create operation operation-1534455595680-5739448b44101-0e70d587-5dc88e00 completed successfully.
NAME                TYPE                 STATE      ERRORS  INTENT
gcp-first-vm        compute.v1.instance  COMPLETED  []
gcp-network         compute.v1.network   COMPLETED  []
gcp-second-vm       compute.v1.instance  COMPLETED  []

Listing 8-18Resource Created with the Network Reference

我们可以看到,分配给虚拟机的新 IP 只是打开 Google Cloud 控制台,然后转到 Google Compute Engine 部分。这表示实例和分配的新 IP(见图 8-3 )。

img/464715_1_En_8_Fig3_HTML.jpg

图 8-3

地址由新网络资源分配的两个虚拟机映像

新的内部 IP 现在是我们先前在网络中定义的系列之一。因为我们已经定义了资源,所以虚拟机和网络只在一个配置文件中。如果我们转到部署管理器页面,我们只会看到一个配置(图 8-4 )。

img/464715_1_En_8_Fig4_HTML.jpg

图 8-4

部署管理器页面,显示新的配置文件

现在我们可以看到只有一个部署文件,但是在其中,我们可以创建多个资源。这有助于我们构建基础设施。我们可以创建多个配置文件,并在其中部署不同的资源。我们现在可以清理环境,并学习如何使用 Python 构建一个模板来创建更动态的配置。要清理环境,我们可以使用以下命令:

gcloud deployment-manager deployments delete gcp-two-vm-network

为我们的部署创建模板

有了 GDM,使用一个简单的配置文件就可以很容易地创建 IaC。我们可以使用引用来管理跨资源的依赖关系,但是如果我们想要有更大的灵活性,我们必须使用模板。

使用模板给了我们使用编程语言定义和创建资源的灵活性。模板是使用 Jinja 语法或 Python 语言编写的独立文件。在我们的例子中,我们使用 Python 语言。我们要做的是使用 Python 语法重新创建我们为两个网络虚拟机开发的配置文件。当我们定义一个 Python 模板时,我们必须遵守两个简单的规则。

  • 我们必须定义一个GenerateConfig()generate_config()文件。在我们定义两者的情况下,Python 使用了generate_config()

  • 我们必须返回一个有效的 YAML 文件。

当我们使用模板进行配置时,我们可以创建一个模块,我们可以重用它来定义代码片段。我们可以使用模板来定义我们在配置中使用的资源。

为了展示如何在一个单独的文件中创建一个定义两个虚拟机的模板,第一个文件类似于清单 8-19 。

COMPUTE_URL_BASE = 'https://www.googleapis.com/compute/v1/'

def GenerateConfig(unused_context):
  """Creates the first virtual machine."""

  resources = [{
      'name': 'gcp-first-template',
      'type': 'compute.v1.instance',
      'properties': {
          'zone': 'us-east1-b',
          'machineType': ".join([COMPUTE_URL_BASE, 'projects/practicaldevopsgcp-197023',
                                  '/zones/us-east1-b/',
                                  'machineTypes/f1-micro']),
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage': ".join([COMPUTE_URL_BASE, 'projects/',
                                          'debian-cloud/global/',
                                          'images/family/debian-8'])
              }
          }],
          'networkInterfaces': [{
              'network': '$(ref.a-new-network.selfLink)',
              'accessConfigs': [{
                  'name': 'External NAT',
                  'type': 'ONE_TO_ONE_NAT'
              }]
          }]
      }
  }]
  return {'resources': resources}

Listing 8-19A Template Resource Created with Python

Python 代码在方法GenerateConfiguration()中为我们想要定义的资源定义了 YAML 文件。这段代码中有趣的部分是这一行

'network': '$(ref.a-new-network.selfLink)',

我们本质上使用一个引用来定义模板中的配置文件。这开始揭示模板的真正力量。

模板本质上是一段我们可以重用的配置代码。当我们使用模板时,我们的配置文件导入模板,然后用它来定义资源。例如,这允许我们创建一个通用组件,如 VM、负载均衡器、Docker 配置等。,并在整个项目中重用它。

因为文件保存在 Python 文件中,所以可以很容易地在我们的代码库中保存和版本化。我们可以定义基础设施的不同版本,发布我们需要的版本,当然,也可以回滚基础设施。为了测试,我们现在可以使用一个虚拟机,我们可以为我们的配置文件创建第一个版本,看起来像清单 8-20 。

imports:
- path: gcp-first-template.py

resources:
- name: gcp-vm-1
  type: gcp-first-template.py
- name: gcp-network
  type: compute.v1.network
  properties:
    IPv4Range: 10.10.0.1/16

Listing 8-20The First Version of Our Configuration File Using the Template

在这种情况下创建的配置文件非常紧凑,易于阅读。配置文件首先导入模板。这是通过imports部分完成的。

imports:
- path: gcp-first-template.py

在导入项下,我们标明了path。这表明了我们想要在配置文件中导入的文件的路径。导入文件后,我们可以在resources部分使用它。与任何其他资源一样,我们定义了资源的名称和type。在本例中,类型是我们之前导入的模板文件。

- name: gcp-vm-1
  type: gcp-first-template.py

这一行定义并使用了我们的模板。我们可以在配置文件中定义任意多的模板。这些可以用来定义非常复杂的配置文件,并重用我们之前定义的资源。

该文件使用模板文件中定义的资源。这并没有更好的可重用性,因为我们为所有的资源赋予了相同的类型。对于一个完整的可重用模板,我们必须将其模块化。我们可以通过使用模板中的环境变量来做到这一点。

用环境变量定义模板

使用环境变量,我们可以创建一个模板,我们可以跨区域、区域和项目重用它。环境变量在配置文件中定义,然后可以针对我们想要创建的特定项目进行个性化设置。我们可以在模板中使用下面的代码引用一个环境变量:context.properties["property-name"]。如果我们想在配置文件中定义属性,我们可以使用context.env["environment name"],以防我们想使用 Google Cloud 的环境变量。我们现在可以改变我们之前的模板,为我们的资源定义一个环境变量(清单 8-21 )。

def GenerateConfig(context):
  """Creates the virtual machine with environment variables."""

  resources = [{
      'name': context.env['name'],
      'type': 'compute.v1.instance',
      'properties': {
          'zone': context.properties['zone'],
          'machineType': ".join(['https://www.googleapis.com/compute/v1/',
                                  'projects/practicaldevopsgcp-197023/zones/',
                                  context.properties['zone'], '/machineTypes/',
                                  context.properties['machineType']]),
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage': ".join(['https://www.googleapis.com/compute/v1/', 'projects/',
                                          'debian-cloud/global/',
                                          'images/family/debian-8'])
              }
          }],
          'networkInterfaces': [{
              'network': '$(ref.' + context.properties['network']
                         + '.selfLink)',
              'accessConfigs': [{
                  'name': 'External NAT',
                  'type': 'ONE_TO_ONE_NAT'
              }]
          }]
      }
  }]
  return {'resources': resources}

Listing 8-21Adding an Environment Variable to the Template

在此模板中,我们添加了以下环境变量:

  • context.properties['network']

  • context.properties['zone']

  • context.properties['machineType']

这些变量用于在我们的模板中创建一个不同的值。在这种情况下,不同的值使我们能够使用不同的机器类型和不同的网络在不同的区域中部署资源。我们需要修改配置文件,以使用环境变量。在配置文件中,我们必须添加变量,并定义我们想要分配给它的值。新的配置文件如清单 8-22 所示。

COMPUTE_URL_BASE = 'https://www.googleapis.com/compute/v1/'

def GenerateConfig(context):
  """Creates the virtual machine with environment variables."""

  resources = [{
      'name': context.env['name'],
      'type': 'compute.v1.instance',
      'properties': {
          'zone': context.properties['zone'],
          'machineType': ".join([COMPUTE_URL_BASE, 'projects/',
                                  context.env['project'], '/zones/',
                                  context.properties['zone'], '/machineTypes/',
                                  context.properties['machineType']]),
          'disks': [{
              'deviceName': 'boot',
              'type': 'PERSISTENT',
              'boot': True,
              'autoDelete': True,
              'initializeParams': {
                  'sourceImage': ".join([COMPUTE_URL_BASE, 'projects/',
                                          'debian-cloud/global/',
                                          'images/family/debian-8'])
              }
          }],
          'networkInterfaces': [{
              'network': '$(ref.' + context.properties['network']
                         + '.selfLink)',
              'accessConfigs': [{
                  'name': 'External NAT',
                  'type': 'ONE_TO_ONE_NAT'
              }]
          }]
      }
  }]
  return {'resources': resources}

Listing 8-22New Template with the Environment and Properties Files Defined in the Context

我们需要更新配置文件,以便将新变量发送到模板文件。新的配置文件如清单 8-23 所示。

imports:
- path: gcp-first-template.py

resources:
- name: gcp-vm-1
  type: gcp-first-template.py
- name: gcp-network
  type: compute.v1.network
  properties:
    machineType: f1-micro # Sets the 'machineType' template property
    zone: us-east1-b # Sets the 'zone' template property
    network: gcp-network # Sets the 'network' template property
    IPv4Range: 10.10.0.1/16

Listing 8-23New Configuration File Created

我们可以使用相同的命令来部署资源。使用环境和属性变量有助于定义一个完全可重用的模板。我们可以定义我们的变量,并在区域、地区以及项目中重用它。

结论

在本章中,我们看到了如何使用 GDM 来定义 IaC。GDM 是一个使用定义文件创建基础设施的非常强大的工具。使用一个文件来定义我们的基础设施允许我们的基础设施版本化。在 GDM,我们可以使用模板定义基础架构。模板是一段可重用的代码,它定义了基础设施的资源。模板可以模块化,增加一些环境和属性值。属性值在配置文件中定义。相反,环境变量直接从 GCP 获得。IaC 是 DevOps 最重要的实践之一,为了定义、开发和维护我们的基础设施,学习它是至关重要的。

九、GCP 的认证和访问管理

今天,识别用户对于保证我们应用的正确隐私级别是很重要的。在这种背景下,身份和访问管理(IAM)变得越来越重要。谷歌云平台(GCP)为定义我们的 IAM 和保证我们资源的隐私需求提供了一个很好的解决方案。

什么是身份和访问管理?

IAM 是一个框架,用于识别用户并只允许授权用户访问资源。IAM 是一个总括术语,用来描述一组定义用户访问级别的软件和规则。通过 IAM,我们定义了每个用户的访问级别。

用户可以是客户,在这种情况下,我们可以谈论客户身份管理(CIM)。对于雇主,我们谈论雇主身份管理(EIM)。IAM、CIM 和 EIM 都有相同的核心目标:唯一地识别用户并确保用户就是他/她。

当我们想到 IAM 框架时,我们必须考虑两个主要组件:

  • 软件

  • 管理

从 IT 的角度来看,软件是我们放置框架所需的所有工具。治理是我们定义的授权用户访问特定资源的规则。

当我们建立一个新的 IAM 框架时,是因为我们想要实现一些特定的目标。IAM 系统用于识别用户,然后在系统上认证用户。基于定义治理的规则,我们可以确定允许用户使用哪些 IT 资源。这为用户创建了一个数字实体

定义数字实体

每天,我们都会了解到新的网络欺诈或网络攻击。这提出了一些关键的必要条件。我们必须确定了解我们交往的人。仅使用电子邮件地址和密码来识别用户是不够的,因为黑客可以窃取这些信息并伪造登录。

当我们创建一个数字实体时,我们想要确定我们的用户到底是谁。为此,我们直接从浏览器或手机收集一组数字实体。我们使用所有这些数据来识别用户。在收集了所有数据之后,当用户尝试一个动作时,例如新的登录,系统使用特定的收敛模式将旧数据与新数据进行比较。这将检查共同点,并使用特定的风险模式(例如基于风险的身份验证(RBA ))返回一个数字。该号码用于唯一识别用户。

当我们创建一个数字实体时,我们将该实体与一个或多个身份属性相关联。这些属性与实体相关联。例如,这可能包括医疗数据、电子邮件、电话号码、用户名、银行交易等。所有这些数据都是在我们每次使用数字实体时收集和增长的。算法使用所有这些交易来识别用户。例如,想象一下,我们只有来自同一浏览器、同一时间和同一地点的网上银行交易。如果我们改变一次浏览器、时间和地点,系统可以挑战我们,例如,要求我们引入另一个代码或回答一些安全问题。质询用户的一个基本示例是 2FA,即双因素身份认证。当我们使用谷歌认证器访问脸书等网站时,系统会要求我们在密码后输入另一个代码。这用于验证我们的身份,并允许我们访问网站。

数字身份的重要性

今天,几乎每个人都有数字生活——电子邮件、社交媒体账户、智能手机,可能还有网上银行账户。所有这些定义了我们的“数字生活”,在这种生活中,我们只能通过电子邮件和密码来识别。但是这些真的足以让我们认出自己吗?

仅在美国,在过去的三年中,身份盗窃的发生率迅速增加。标枪战略与研究公司(Javelin Strategy & Research)进行的一项研究显示,自 2012 年以来,近 6000 万美国公民成为身份盗窃的受害者,总计超过 1000 亿美元被盗,而在 2017 年,美国身份盗窃受害者人数上升至 1670 万。

注意

完整的报告可在 www.javelinstrategy.com/coverage-area/2018-identity-fraud-fraud-enters-new-era-complexity# 获得。

根据思科进行的研究,到 2021 年,每个人平均将有四台设备连接到网络。这个数字表明了为什么数字身份将变得越来越重要。

如果我们想一想我们每天都在做什么,我们可以估计在日常活动中我们通过网络(主要是互联网)移动了多少数据。例子可能包括使用谷歌地图寻找更快或替代的工作路线,或者使用信用卡在我们最喜欢的咖啡店购买冰镇星冰乐,或者只是在我们的智能手机上查看电子邮件。对于这些基本活动,我们通过网络移动大量数据。所有这些数据形成了一个与我们的数字生活相连的“实体”,因为它识别了我们的品味、位置和银行细节。

在这种情况下,定义安全的数字身份至关重要。这是因为当我们进行交易时,交易者必须确信我们是我们应该成为的人。

现在我们知道,数字实体是定义我们的一组属性。每次我们点一杯冰镇星冰乐,登录我们的电子邮件账户,或者在我们喜欢的社交媒体网站上发帖,我们都在为我们的数字生活增加一个实体,这增加了我们的数字实体。我们的数字实体本质上类似于我们的 DNA。当我们考虑开发云应用时,我们必须制定一套规则来唯一地识别用户并保护他/她的数字实体。

IAM 和数字实体

IAM 在定义和保护数字实体方面起着非常重要的作用。IAM 系统可以包含四个基本功能:

  • 纯粹的认同

  • 用户访问

  • 批准

  • 联合识别

这四个功能涵盖了 IAM 的各个方面。定义我们的数字实体的第一个也是最重要的功能是纯粹的识别。这可以使用小型公理集合来构建,这些公理集合了所有的东西来定义用户的唯一身份。因为我们可以无限制地定义用户,所以我们定义了一个“纯身份”因为没有约束,所以没有为特定系统专门定义用户。当我们定义一个纯粹的身份时,我们实质上定义了一组用于新的数字实体的属性。

我们可以从图 9-1 中看到我们如何创建我们的数字实体。我们可以看到我们的身份如何对应于特定用户的数字实体。当我们识别出用户后,我们可以对不同的帐户使用相同的数字身份,例如邮件帐户、银行帐户等。我们可以看到,我们的身份是使用不同的属性构建的。这些属性用于唯一识别我们。特别是,如果我们考虑 RBA 系统,属性为我们提供了唯一标识用户的代码。

img/464715_1_En_9_Fig1_HTML.png

图 9-1

我们如何定义和创建我们的数字身份

注意

基于风险的认证是非静态认证系统。通过这种类型的身份验证,系统会收集用户的特定信息,并为每条信息分配一个“权重”基于每个属性的权重,系统决定是否允许用户进入系统。我们可以看到这一决定的结果,例如,当我们试图从另一个国家登录时,谷歌或我们的银行要求我们回答安全问题。

IAM 系统的另一个功能是用户访问。用户访问将特定用户连接到他/她的数字实体。我们使用纯身份来定义用户是否可以访问系统。在访问时,我们创建一个数字实体,在整个系统中使用。

用户访问涉及 IAM 系统的另一个功能:授权。当我们谈到授权时,我们必须考虑系统中允许用户访问的所有操作、组和资源。

IAM 系统的最后一个功能是联合识别。联合身份通常被称为单点登录(SSO)。当我们想到联合识别时,我们使用我们的数字实体来访问我们系统的外部资源。这意味着我们使用我们的数字实体无需输入密码。

IAM 系统是为管理用户而生的。这意味着我们可以创建、删除和更新用户的身份。这些功能创建了一个实体,并允许用户在系统上执行操作。IAM 系统的主要组件和功能包括

  • 证明

  • 批准

  • 角色

所有这些操作和功能都由 IAM 连接和管理。我们的数字实体成为我们的数字 DNA,这是一种独特的品质,允许我们在系统内移动和导航。

证明

认证是 IAM 系统的第一步。身份验证是我们识别实体并验证它声称是什么/谁的阶段。当用户尝试进入系统(例如,输入用户名和密码)时,系统会对用户进行身份验证。在这个阶段,我们定义我们的数字实体。

批准

授权是每个 IAM 系统的第二步。授权是我们定义数字实体可以做什么的阶段。这是在每个特定应用的上下文中定义的。例如,用户可以被授权运行财务报告,但不能修改它。授权由一组规则定义。这些规则定义了我们的实体被允许做什么。

角色

角色是定义用户被授权做什么的基础。角色是一组组和操作。角色用于集中管理用户执行的操作。每个实体都有一些附属的角色,这定义了实体有权执行或访问的操作或组。

谷歌云平台中的 IAM

如果我们想要一个有效的 IAM,那么在云中拥有一个正确定义的 IAM 是必不可少的。这是因为应用的性质。在云中,我们让所有用户访问相同的数据库和相同的共享资源。在这种情况下,为资源定义正确的粒度对于定义对资源的访问是至关重要的。

GCP 提供了一个非常简单和先进的系统来管理 IAM。有可能有非常精细的粒度,这允许管理员描述云中和 Google 生态系统中资源的个性化访问。有了 GCP,就有可能定义一个 IAM,让我们管理拥有什么访问哪个资源的身份,角色。Google Cloud IAM 允许我们向特定的成员授予访问权限,为每个成员分配一个或多个角色。分配的成员和角色用于回答 IAM 的三个问题:

  • 什么

  • 哪个

Google IAM 的成员可以是以下类型之一:

  • 谷歌账户

  • 服务帐户

  • 谷歌集团

  • g 套件域

  • 云身份域

谷歌账户可以由任何拥有与谷歌账户相关的电子邮件地址的成员组成。它可以是与google.com或任何其他域相关联的身份。这包括开发人员、管理员或任何其他与 GCP 交互的人。

第二个成员是服务帐户。这本质上是一个属于特定应用的帐户。它用于代替用户调用 API。我们可以根据需要创建任意多的服务帐户。这些用于对不同的应用进行逻辑分段。

Google Group 是 Google 账户和服务账户的命名集合。每个组都有一个唯一的电子邮件标识。Google Group 允许我们创建一个组,并将多个用户关联到该组。通过组,管理员可以同时将一个角色与多个用户相关联。

G 套件域是所有谷歌账户的分组。这代表一个组织的互联网域名。当我们创建一个 G Suite 域时,我们可以根据需要在域中创建任意多的用户。这是根据特定角色对用户进行分组的一种便捷方式。

最后的成员是云身份域。这代表了一个虚拟的用户组。与 G Suite 域一样,我们可以使用这个组轻松地向一组人授予访问权限。

除了前面的成员,我们还有一些特殊的标识符。这些用于表示与 Google 帐户连接的用户。这些标识符是:

  • 这是一个特殊的标识符,代表通过 Google 帐户或服务帐户认证的任何人。这个标识符并不代表所有的匿名用户或未经验证的用户。

  • allUsers:这是一个特殊的标识符,用于对所有用户进行分组,无论是否经过验证。一些 GCP API 限制标识符为allUsers的用户访问。在这种情况下,用户组只需要经过身份验证的用户。

当 Google IAM 必须对用户进行身份验证时,它会根据一组特定的规则来指示允许用户执行什么操作。这些规则基本上由三个要素组成:

  • 资源

  • 许可

  • 角色

资源用于授予对特定 GCP 资源的访问权限。这些例子包括项目、计算引擎和云存储。通常,当我们给一个项目分配一个权限时,该项目下的所有资源都具有相同的权限。有些资源是不同的,例如,云发布/订阅。在这种情况下,我们使用更详细的粒度为发布者而不是订阅者分配不同的权限。

我们必须牢记的一个重要概念是权限。这定义了允许用户在特定资源中执行什么操作。在云 IAM 中,我们可以使用以下语法定义权限:

<service>.<resource>.<verb>

例如,pubsub.subscription.consume,当我们定义一个权限时,通常(但不总是)与 1:1 REST 方法相关联。这意味着,通常情况下,每个 GCP 服务都会暴露给 REST 服务,而 REST 服务本身也会被暴露。

GCP IAM 中定义的最后一个元素是角色。角色是权限的集合。在 GCP IAM 中,我们可以将权限直接分配给用户,但是我们也可以将角色分配给特定的用户,然后定义分配给该角色的所有权限集。

在 Google IAM 中,有三种主要的角色类型:

  • 原始角色

  • 预定义角色

  • 自定义角色

原始角色是 GCP 现在的历史角色。他们是

  • 物主

  • 编者ˌ编辑

  • 电视观众

这些角色在引入 Google Cloud IAM 之前就已经存在,并且是同心的。所有者角色包括编辑者和查看者角色。表 9-1 中总结了基本角色及其相关权限。

表 9-1

IAM 中的三种角色类型

|

角色名称

|

角色头衔

|

权限

|
| --- | --- | --- |
| roles/viewer | 电视观众 | 此角色允许用户只读操作,但不修改现有数据。 |
| roles/editor | 编者ˌ编辑 | 该角色拥有所有查看者权限,以及修改数据状态的权限。 |
| roles/owner | 物主 | 该角色还具有编辑权限。此外,它还可以执行以下操作:管理角色和权限设置开单项目 |

另一类角色是预定义角色。这些扩展了基本角色,并允许我们为特定的服务功能创建更精细的粒度。通过预定义的角色,我们可以将特定于用户的角色分配给特定的应用。一个用户可以被分配多个角色。预定义的角色允许我们为 GCP 的所有项目创建角色,例如:

  • 项目

  • 应用引擎

  • BigQuery(大查询)

  • 云大表

  • 云计费

  • 云数据流

  • 云数据探测器

  • 云数据存储

  • 安静点

对于每个项目,我们可以定义一组特定的角色。例如,这允许用户拥有 Kubernetes 引擎的管理员角色和计算引擎的查看者角色。

我们可以在 GCP IAM 上定义的最后一种角色是自定义角色。这包括我们根据我们的系统定义的所有角色。当我们创建一个自定义角色时,我们可以混合一个或多个角色,或者从一个角色获得特定的权限并将其与另一个角色混合。当我们创建一个定制角色时,我们定义了一个新的特定角色,它被设计来满足我们的治理需求。

注意

自定义角色非常强大,但需要注意。首先,我们必须确保我们想要创建的角色可以应用到我们想要与之关联的对象。并非所有对象都具有相同类型的权限。这是因为并非所有的对象都是相同的。您将在本章的后面看到如何创建自定义角色。

谷歌 IAM 策略

首先,要了解如何在 GCP 创建角色,我们必须了解 IAM 策略是如何工作的。要创建一个新角色,我们必须阐明一个 IAM 策略。这是用于定义谁拥有什么类型的访问权限的语句集合。策略连接到资源,用于控制对资源的访问。

图 9-2 中的图表显示了 Google IAM 的结构。Google 中的每个 IAM 都由 IAM 策略对象表示。该对象由绑定列表创建。每个绑定都是成员及其相关角色的列表。清单 9-1 中显示了一个 IAM 策略的示例。

img/464715_1_En_9_Fig2_HTML.jpg

图 9-2

Google IAM 结构

{
"bindings": [
  {
    "role": "roles/storage.objectAdmin",
     "members": [
      "user:cgpdevops@devops.com",
       "serviceAccount:my-other-app@appspot.gserviceaccount.com",
        "group:admin@devops.com",
        "domain:gcpdevops.com" ]
   },
   {
    "role": "roles/storage.objectViewer",
    "members": ["user:test@devops.com"]
    }
   ]
}

Listing 9-1A Sample of an IAM Role

创建新 IAM 策略的代码非常简单。首先我们定义角色:"role": "roles/storage.objectAdmin"。角色的格式为 roles/ 。当我们必须定义我们想要使用的角色时,我们可以定义与该角色相关联的成员。

"members": [
      "user:cgpdevops@devops.com",
       "serviceAccount:my-other-app@appspot.gserviceaccount.com",
        "group:admin@devops.com",
        "domain:gcpdevops.com" ]

此部分将 Google 成员与角色相关联。在前面的代码中,我们将所有四个成员与同一个角色相关联。这是因为我们可以将一个或多个成员与一个或多个角色相关联。

创建和管理 IAM 策略

IAM 策略的目标是允许用户在项目中拥有一定级别的访问权限。到目前为止,我已经讨论了 IAM 策略的一般概念。现在是时候了解如何创建 IAM 策略来管理我们云的安全性了。IAM 策略用于授予特定用户对特定资源的访问权限。IAM 策略可以通过两种方式创建:使用 GCP 控制台和发送带有策略的 JSON 文件,或者通过setIamPolicy()方法。

创建策略的常见模式是读取实际策略,然后更新策略。这可能会产生问题。例如,想象下面的基本场景。两个用户必须同时将 IAM 策略更新到一个对象。用户 1 发送请求,稍有延迟,用户 2 发送相同的请求。作为回报,User1 接收策略,然后更新策略。用户 2 拥有较旧的策略,然后决定更新 IAM 策略,这将覆盖用户 1 设置的策略。

这种情况比你想象的更常见。在 Google IAM 中解决这个问题,我们可以使用etag。只有在设置了 IAM 策略的情况下,该值才允许我们更改 IAM 策略。它用于防止前面描述的并发情况。Google 将与策略相关的etag与新策略进行比较。如果etag不同,角色不会更新。首先,为了更新策略,我们必须使用getIamPolicy()方法收集实际的策略。这就回报了策略与实际的etag。我们可以使用我们收集的etag来更新策略。如果前面的策略没有etag,它就不会发送etag。这是因为谷歌不更新策略。

注意

当我们想要管理角色时,etag值非常有用,但是并不是所有的角色都有一个etag。首先,要更新一个策略,我们必须读取实际的策略,如果需要的话,获取etag值,但是并不是所有的策略都设置了etag。如果策略没有etag,我们就不能为策略设置etag。这是因为谷歌拒绝更新。如果我们希望整个策略有一个etag,我们可以用正确的etag值销毁并重新创建策略。

我们可以用一些不同的方式更新策略。

  • JSON 文件通过gcloud

  • 通过 REST 的 API

  • 通过 Java 代码的 API

  • 安慰

现在你将看到如何使用 Google 允许的不同方法来创建角色。

创建 JSON 文件

创建新 IAM 策略的第一种方法是使用 JSON 文件来定义它。这种方式可能是最常见和最快的,因为我们只需创建 JSON 文件并使用 GCP 控制台。使用这种方法,我们可以很容易地集成 IaC。我们可以创建我们的 JSON 文件,并将其存储在我们存储文件的位置,以定义基础设施,并且随着我们发布的每个版本,发布新的 IAM 策略。为了修改实际的 IAM 策略,我们可以连接到gcloud或 Google 控制台,并执行读取实际策略和写入 JSON 文件的命令。命令是

gcloud projects get-iam-policy <your project name> --format json > iam.json

这个命令在我们的路径中创建新的 JSON 文件,其中包含实际的策略。我们可以打开文件,阅读我们实际拥有的内容。在我的例子中,它就像清单 9-2 中的那样。

{
  "bindings": [
    {
      "members": [
        "serviceAccount:service-152799671751@compute-system.iam.gserviceaccount.com"
      ],
      "role": "roles/compute.serviceAgent"
    },
    {
      "members": [
        "serviceAccount:service-152799671751@container-engine-robot.iam.gserviceaccount.com"
      ],
      "role": "roles/container.serviceAgent"
    },
    {
      "members": [
        "serviceAccount:152799671751-compute@developer.gserviceaccount.com",
        "serviceAccount:152799671751@cloudservices.gserviceaccount.com",
        "serviceAccount:service-152799671751@containerregistry.iam.gserviceaccount.com",
        "serviceAccount:service-152799671751@containerregistry.iam.gserviceaccount.com"
      ],
      "role": "roles/editor"
    },
    {
      "members": [
        "user:pierluigi.riti@gmail.com"
      ],
      "role": "roles/owner"
    }
  ],
  "etag": "BwVt1uqAKmk=",
  "version": 1
}

Listing 9-2The Actual IAM Policy Connected with the Project

您可以看到 IAM 策略定义了一个etag和版本。该版本是只读的。我们不需要更新版本。要创建新的策略,我们用编辑器打开 IAM 文件,并向策略添加新成员或从中删除成员。

如果我们想添加另一个用户帐户作为我们的 GCP 的所有者,我们必须创建新成员并将他们与角色相关联。我们必须将以下内容添加到 IAM 代码中:

{
  "members": [
    "user:example1@gmail.com"
  ],
  "role": "roles/viewer"
}

当我们添加这个部分时,我们将邮件与查看者的角色联系起来。我们可以更新 IAM 文件,然后执行它。要执行该文件,我们必须使用以下命令:

gcloud projects set-iam-policy <your project name> iam.json

该命令的结果显示了添加到 Google Cloud 中的用户(清单 9-3 )。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud projects set-iam-policy practicaldevopsgcp-197023 iam.json
Updated IAM policy for project [practicaldevopsgcp-197023].
bindings:
- members:
  - serviceAccount:service-152799671751@compute-system.iam.gserviceaccount.com
  role: roles/compute.serviceAgent
- members:
  - serviceAccount:service-152799671751@container-engine-robot.iam.gserviceaccount.com
  role: roles/container.serviceAgent
- members:
  - serviceAccount:152799671751-compute@developer.gserviceaccount.com
  - serviceAccount:152799671751@cloudservices.gserviceaccount.com
  - serviceAccount:service-152799671751@containerregistry.iam.gserviceaccount.com
  - user:example.1@gmail.com
  role: roles/editor
- members:
  - user:pierluigi.riti@gmail.com
  role: roles/owner
- members:
  - user:example.1@gmail.com
  role: roles/viewer
etag: BwVzp_aDURQ=
version: 1

Listing 9-3The Cloud IAM Policy Result

注意

使用 JSON 文件,不可能创建用户所有者。只有使用 setIamPolicy 方法才能做到这一点。这是因为当我们邀请新的所有者时,他或她必须接受电子邮件中的邀请,并加入 Google Cloud 帐户。

除了使用 JSON 文件之外,还可以使用命令行添加具有角色的单个成员。使用gcloud projects add-iam-policy-binding命令可以做到这一点。这将向绑定添加一个用户。为此,命令如下:

gcloud projects add-iam-policy-binding practicaldevopsgcp-197023 \
                      --member user:example2@gmail.com --role roles/editor

该命令使用现有成员插入另一个用户,但不创建新成员。要创建新成员,我们必须使用 Cloud IAM 中的现有功能。

通过 REST 使用 API

修改 Google IAM 的另一种方法是调用setIamPolicy。这是一个 RESTful web 服务。我们必须调用它并发送主体来更新 IAM。REST 调用的终点是

https://cloudresourcemanager.googleapis.com/v1/projects/<add your project name>:setIamPolicy

我们必须为请求创建主体。在这种情况下,我们必须创建一个策略,因为通过设置一个新的策略,格式类似于以前用于 JSON 的格式(清单 9-4 )。

{
  "policy":{
    "bindings":[
      {
        "role":"roles/owner",
        "members":[
          "user:email1@gmail.com",
          "user:email2@gmail.com",
          "user:email3@gmail.com"
        ]
      },
      {
        "role":"roles/editor",
        "members":[
          "serviceAccount:example1app@appspot.gserviceaccount.com"
        ]
      }
    ]
  }
}

Listing 9-4The setIamPolicy Body

当我们发送请求时,Google IAM 解析它并创建一个特定的响应。响应的结果如清单 9-5 所示。

{
  "bindings":[
    {
      "role":"roles/owner",
      "members":[
        "user:email1@gmail.com",
        "user:email2@gmail.com",
        "user:email3@gmail.com"
      ]
    },
    {
      "role":"roles/editor",
      "members":[
        "serviceAccount:my-other-app@appspot.gserviceaccount.com"
      ]
    }
  ]
}

Listing 9-5The Response from the Google IAM API

注意

当我们使用 API 时,我们必须确保将 OAuth 2.0 集成到我们的系统中。这是因为在另一个案例中,请求被拒绝。这是谷歌管理的另一个安全层,旨在提高与云相关的安全性。

通过 Java 代码创建 API

GCP 提供了一个 API 来创建 IAM。这个库在我们的 Java 项目中使用,它允许我们创建或修改 IAM 策略。图书馆在

import com.google.api.services.cloudresourcemanager.model.Policy;
import com.google.api.services.cloudresourcemanager.model.SetIamPolicyRequest;
import com.google.api.services.cloudresourcemanager.model.GetIamPolicyRequest;
import com.google.api.services.cloudresourcemanager.model.Binding;

这个对象允许我们创建 IAM 策略和管理它所必需的绑定。下面是我们可以使用这个对象创建的代码片段(清单 9-6 )。

import com.google.api.services.cloudresourcemanager.model.Policy;
import com.google.api.services.cloudresourcemanager.model.SetIamPolicyRequest;
import com.google.api.services.cloudresourcemanager.model.GetIamPolicyRequest;
import com.google.api.services.cloudresourcemanager.model.Binding;
import java.util.LinkedList;
import java.util.Arrays;

...

String[] myViewers = new String[] {"user:testviewer1@gmail.com",
    "user:testviewer2@gmail.com"};

// The name of the role, using the format `roles/<role-name>`.
String targetRole = "roles/viewer";

Policy policy =
    client.projects().getIamPolicy(projectId,
    new GetIamPolicyRequest()).execute();

Binding targetBinding = null;

// Make a local copy of the bindings for modification.
LinkedList<Binding> bindings =
    new LinkedList<Binding>(policy.getBindings());

// Search for the existing binding having role name of
// targetRole.
for (Binding binding : bindings) {
    if (binding.getRole().equals(targetRole)) {
        targetBinding = binding;
    break;
    }
}

// If no matching targetBinding is found, construct a new Binding object
// and add it to the bindings list.
if (targetBinding == null) {
    targetBinding = new Binding();
targetBinding.setRole(targetRole);
bindings.add(targetBinding);
}

// Finally, set the list of members as the members of targetBinding.
targetBinding.setMembers(Arrays.asList(myViewers));

// Write the policy back into the project by calling SetIamPolicy.
SetIamPolicyRequest setIamPolicyRequest = new SetIamPolicyRequest();
    setIamPolicyRequest.setPolicy(policy);
client.projects().setIamPolicy(projectId,
    setIamPolicyRequest).execute();
...

Listing 9-6Code Snippet for Using the Java Code

使用控制台

创建新策略的最后一种方法是使用 GCP 控制台。这是管理成员和策略的最简单方式。

要通过控制台管理角色和策略,请遵循以下简单步骤:

  1. Open the Google Console and select IAM & admin from the menu (see Figure 9-3).

    img/464715_1_En_9_Fig3_HTML.jpg

    图 9-3

    GCP 控制台上的 IAM 菜单

  2. Then select IAM. This opens the IAM board, from which it is possible to manage the Google IAM. From the board, it is possible to add or remove members or roles, manage identity, etc. (Figure 9-4).

    img/464715_1_En_9_Fig4_HTML.jpg

    图 9-4

    IAM 许可委员会

董事会允许我们创建和维护云中的所有角色。我们可以用简单的方式导航角色、权限和身份。使用委员会可以让我们有效地管理 IAM 策略中涉及的角色和其他实体。

结论

本章讨论了 Google IAM 策略。首先,我介绍了 IAM 是什么,以及它在云环境中的重要性。在我介绍了在 GCP 创建 IAM 的不同方法之后。Google IAM 提供了一个非常灵活和强大的工具来管理整个系统的安全性,并提供了基于特定项目级别启动治理的灵活性。我们可以很容易地设计我们的治理模型,并通过 JSON 文件创建和维护它。这使我们能够改进我们的 IaC 系统,并将其与我们的安全策略相结合。将 Google IAM 策略与 IaC 相结合可以大幅减少系统中的安全问题,这意味着更多的安全性和更少的 SOC 安全异常。

十、GCP 的网络配置和管理

当我们提到云环境时,我们指的是通过网络共享的资源。这些资源必须通过网络进行配置和管理,因此我们必须确保公开和维护它们所需的安全级别。当我们创建云应用时,我们必须考虑让应用全天候运行。只有当我们设计的架构能够在网络上可靠运行时,这才有可能。在本章中,你将看到我们如何在谷歌云平台(GCP)中设计和维护良好的网络架构。

GCP 网络基础

首先,要定义和管理我们的网络,我们必须确定和了解 GCP 的网络是如何组织的。在 GCP,网络分为两个主要部分:

  • 地区

  • 区域

地区是我们管理资源的地理区域。一个区域可以包含一个或多个区域。位于一个区域中的资源,比如持久性磁盘,被称为区域资源。其他资源,如外部互联网协议(IP),被称为区域资源。同一区域中的每个资源都可以使用区域资源。并非所有的资源都是区域性的。GCP 的区域和带状资源列于表 10-1 。

表 10-1

GCP 的区域和地带性资源

|

类型

|

资源

|

描述

|
| --- | --- | --- |
| 地区的 | 地址 | 项目中定义的任何静态外部 IP 的集合 |
| 地区的 | 子网络 | 区域子网用于分割网段中的地址。它用于识别主网络中的子网。 |
| 地区的 | 区域管理的实例组 | 这些资源是跨越同一地区多个区域的同类资源的集合。 |
| 地区的 | 区域持久磁盘 | 这用于跨不同区域提供持久的数据存储和复制。在故障转移的情况下,我们可以将资源连接到另一个区域,将数据迁移到云中。 |
| 带状的 | 例子 | 每个实例必须位于一个区域中,并且可以访问同一区域中的一个或多个全局资源。 |
| 带状的 | 永久磁盘 | 同一区域中的所有资源都可以访问永久磁盘。可以仅将磁盘连接到同一区域中的活动资源。 |
| 带状的 | 机器类型 | 这些是预分区资源。实例和磁盘只能在同一区域中使用机器类型。 |

在考虑我们的架构时,我们必须定义在哪里放置我们的资源,仔细选择我们的区域和资源。这是因为正确的规划有助于实现网络的两个主要目标。

  • 处理故障:如果我们跨区域和地区分配资源,我们可以使用 Google Cloud 来设计在出现故障时彼此独立的区域。每个区域都是完全独立的,一个区域的故障不会导致其他区域的故障。当我们在一个区域出现故障时,我们可以将数据移动到另一个区域,从而实现云的高可用性。

  • 降低网络延迟:为了降低网络延迟,我们必须将资源放置在离我们最近的位置。这意味着如果我们想让我们的应用全球化,我们必须在不同的地区复制资源。

除了地区性和区域性资源,我们还有一些全球资源。这种类型的资源可以在整个云中全局使用。全球资源包括

  • 形象

  • 快照

  • VPC 网络

  • 防火墙

  • 路线

全球资源非常重要,因为它们可以在每个区域和每个地区使用。创建网络的基本要求是虚拟私有云(VPC)。这是展示和创建网络架构的基本资源。

虚拟私有云简介

VPC 为我们提供了在全球和地区级别上扩展和控制资源工作负载的灵活性。当我们将我们的资源连接到 GCP 时,我们可以访问我们的 VPC,而不必在每个地区复制连接。这是因为 VPC 为我们做了这些。一个单独的谷歌 VPC 不需要访问互联网就可以在不同地区共享资源。单个连接点可以跨区域共享资源。这降低了我们组织的复杂性。

在图 10-1 中,你可以看到使用谷歌 VPC,我们有不同的子网。每个子网都有一个带有内部 IP 的应用。VPC 与外部世界共享 IP,从而也与外部世界共享网络。

img/464715_1_En_10_Fig1_HTML.jpg

图 10-1

普通 VPC 和谷歌云平台 VPC 的区别

VPC 的另一个重要属性是它隔离项目的能力。有了 VPC,我们可以很容易地分离项目,每个项目有不同的账单。然而,我们可以通过云互联组件共享信息。VPC 网络通常简称为“网络”每次我们创建一个新项目,它都与一个默认网络相关联。这实质上是物理网络的虚拟表示,并提供与所有其他组件的连接,如计算引擎或 Kubernetes 引擎。每个 VPC 都有一些特定的属性。

  • VPC 网络及其所有相关组件(如防火墙规则和路由)都是全球性的。它们不与特定的区域或地区相关联。

  • 子网是一种区域性资源。每个子网定义一个 IP 地址范围。

  • 进出不同实例的流量可以由防火墙规则控制。

  • 资源可以使用内部私有 IPv4 地址跨不同子网进行通信。通信遵循为 VPC 定义的防火墙规则。

  • 如果我们为一个实例定义一个私有 IP,那么只有当我们为该实例所在的子网启用私有 Google 访问时,它才能与 Google API 通信。

  • 可以使用 IAM 角色来定义谁可以管理网络。

  • 一个网络可以使用 VPC 网络对等连接到另一个 VPC。

  • 使用云 VPN 或 Google Cloud Interconnect,可以在混合环境中安全地连接 VPC 网络。

  • VPC 网络仅支持 IPv4 单播流量。它不支持多播和广播或 IPv6 流量。可以使用 IPv6 来访问网络中的资源。

  • VPC 网络的最大传输单位(MTU)为 1460 字节。根据 RFC 2132,该 MTU 值通过 DHCP 选项 26 提供给实例。为大于 1460 字节的数据包配置 MTU 可能会导致数据包丢失。

这些属性有助于理解 VPC 的工作原理及其局限性。我们已经知道 VPC 允许子网之间相互通信,因此了解子网在 GCP 中的工作方式非常重要。

网络和子网

每个 VPC 网络由一个或多个 IP 范围组成。这些 IP 范围被称为子网。每个子网都与一个区域相关联。VPC 网络可以在一个或多个区域中包含一个或多个子网。这意味着我们的 VPC 网络可以跨越不同的地理位置,以确保云的高可用性。这意味着,在一个地区出现故障的情况下,另一个地区是可用的,我们可以向我们的客户保证云的功能。我们可以用两种方式定义 VPC 网络:

  • 自动模式:跨不同地区自动创建网络。

  • 自定义模式:网络是在没有任何关联子网的情况下创建的,这使得我们可以定义我们网络的所有特征。

当我们创建自动模式网络时,GCP 会为每个区域创建一个子网。子网与 0.128.0.0/9 CIDR(无类域间路由)块中预定义的一组 IP 范围相关联。当一个新的 GCP 区域变得可用时,一个新的子网被添加到该区域,并且网络使用与该区域相关联的 IP 网络。除了通过 GCP 添加子网之外,还可以手动向区域添加子网,并使用自动子网之外的 IP。当我们创建自定义模式网络时,GCP 不会提供默认子网。这种类型的网络留给用户来决定定义他们的网络的能力。我们可以使用我们想要的每个 IP 范围,并在我们决定使用的每个区域创建子网。

当我们规划网络时,我们必须考虑每种网络的优缺点。这有助于了解哪种类型的网络最适合我们的架构。自动模式网络易于设置和使用。它最适合以下使用情形:

  • 对于在每个区域自动创建的子网

  • 当预定义的 IP 范围不与我们为我们的体系结构配置的任何 IP 地址重叠时

另一方面,定制模式网络提供了更大的灵活性,更适合生产环境。这是因为我们可以定义我们的 IP 地址范围,并且我们可以更仔细地计划在哪里创建子网网络。我们可以确定一些基本使用案例,在这些案例中,强烈建议使用定制模式网络。

  • 当我们不需要在每个区域自动创建子网时

  • 当我们想要决定分配给我们子网的 IP 范围时,因为预定义的 IP 范围与网络上的其他 IP 重叠

  • 当我们计划使用 VPC 网络、对等网络或云 VPC 连接我们的子网时。

定义子网的 IP 范围

当我们创建自定义模式网络时,我们必须根据某些规则设计我们的 IP 范围。这些规则用于帮助我们正确实施子网的 IP 范围。

  • 子网必须有一个主地址范围。该范围必须是有效的 RFC 1918 CIDR 块。

  • 同一网络中的子网必须具有唯一的 IP 范围,但是不同网络中的子网,即使是同一项目的一部分,也可以使用相同的 IP 范围,并在另一个网络中重复使用。

  • 当我们定义 RFC 1918 CIDR 块时,我们必须对我们可以创建的 IP 范围施加一些限制。

  • 同一网络中的子网必须有唯一的 IP 地址。

  • 当我们使用 VPC 网络对等或云 VPC 连接 VPC 时,子网必须具有唯一的 IP 地址。

  • 当我们为通过云 VPN 或 Google Cloud Interconnect 连接的内部网络定义 IP 范围时,它们不应与 GCP 中定义的任何其他 IP 范围冲突。

  • 可以给一个子网分配一个或多个 IP 地址范围。这些范围保留给虚拟机(VM)实例。这些可以使用 RFC 1918 CIDR 块,并且必须遵守先前定义的相同规则。

  • 我们不必为我们在同一网络中定义的子网定义一个连续的 IP 范围。

  • 子网 CIDR 的最小大小是/29

注意

CIDR,有时也称为超网划分,是一种在分配互联网协议(IP)时允许更大灵活性的方法。这减缓了 IPv4 地址的耗尽。CIDR 引入了可变长度子网掩码(VLSM) 的概念。这种技术允许我们为前缀指定任意长度。CIDR 引入了一种表示 IP 地址的新方法,叫做 CIDR 符号。使用 CIDR 表示法时,地址或路由前缀会加上一个表示前缀位数的后缀,例如,IPv4 的 192.10.0.2/24。

当我们定义一个新的子网时,我们必须确保在其主 IP 范围内保留四个 IP 地址。这些保留地址在表 10-2 中列出。

表 10-2

为子网保留的 IP 地址

|

保留地址

|

描述

|

例子

|
| --- | --- | --- |
| 网络 | 子网主 IP 范围中的第一个 IP 地址 | 24 中的 10.10.1.0 |
| 默认网关 | 子网主 IP 范围中的第二个 IP 地址 | 10.10.1.0/24 |
| 倒数第二次预订 | 子网主 IP 范围中倒数第二个 IP 地址 | 10.10.1.0/24 |
| 广播 | 子网主 IP 范围中的最后一个 IP 地址 | 24 中的 10 . 10 . 1 . 0/25 |

保留的 IP 地址仅存在于主 IP 地址中。这意味着当我们定义辅助 IP 地址时,我们在辅助地址中没有保留地址。

路由和防火墙

路由和防火墙是每个网络的一些基本组件。一条路由是一对定义的地址,代表目的地网关。当我们试图使用特定的网关将数据包发送到特定的目的地时,就会用到路由。我们可以确定三种目的地。

  • 单个主机

  • 子网络

  • 默认

网关也有三种类型。

  • 单个主机

  • 界面或链接

  • 以太网硬件地址(MAC)

当我们创建一个 VPC GCP 来自动生成一条系统生成的路线时,这条路线在其中被创建和管理,如下所示:

  • GCP 创建了一条目的地为 0.0.0.0/0 且下一跳是默认 Internet 网关的路由。

  • 对于我们创建的每个子网,都会创建一个路由来定义其资源的路径。这些子网路由在 GCP 控制台中显示虚拟网络的下一跳。通信许可由防火墙规则定义。

  • 子网路由的优先级固定为 1000。

  • 自动模式网络会自动为我们创建的每个子网创建路由。

  • 对于我们创建的每个子网,都会自动创建一个子网路由。

  • 当我们删除子网时,所有路由都会自动删除。

我们可以识别系统生成的路线,因为名称以default-route-前缀开头。这有助于识别哪些路线是手动添加的,哪些是由系统生成的。例如,如果我们打开 GCP 并移动到路线部分,我们可以看到如图 10-2 所示的内容。

img/464715_1_En_10_Fig2_HTML.jpg

图 10-2

默认路由示例

与应用于路由的默认规则一样,在 GCP,我们可以找到一些通用的防火墙规则。这些规则允许我们定义允许或拒绝哪些流量进出虚拟机。

这些规则总是被使用,即使我们呆在我们的网络中,流量也是被控制的。这意味着当我们从一个私有内部虚拟机移动到另一个私有内部虚拟机时,防火墙会应用我们之前定义的规则。每个 VPC 都有两条隐含的防火墙规则。这些不会显示在 GCP 控制台中,但是在我们创建新的 VPC 时会用到和定义。这些规则是:

  • 隐含允许出口规则:该规则允许出口流量。允许到达目的地 0.0.0.0/0 的传入流量,优先级尽可能最低,为 65535。

  • 隐含的拒绝进入规则:如果源是 0.0.0.0/0 并且优先级尽可能低,该规则拒绝进入流量,65535。

这些隐含的规则不能被删除,但可以被覆盖。要覆盖这些规则,我们必须创建一个优先级更高的新规则,其优先级小于 65535。这是因为两个隐含规则的优先级都是 65535。默认网络添加了一些额外的默认规则,可以根据需要进行修改或删除。这些规则是:

  • default-allow-internal:该规则允许网络中实例间所有协议和端口的入口连接。此规则的优先级是 65534。

  • default-allow-sh:该规则允许从任何源或实例到网络的 TCP 与端口 22 的连接。此规则的优先级是 65534。

  • default-allow-rdp:该规则允许 TCP 与端口 3389 的连接。此规则的优先级为 65534,用于允许 Windows 远程桌面。

  • default-allow-icmp:此规则允许网络上任何来源或实例的 ICMP 流量。该规则的优先级为 65534。

在 GCP,默认情况下,一些流量被阻止。不可能创建防火墙规则来允许此流量。阻塞的流量是

  • GRE 流量

  • TCP、UDP、ICMP、IPIP 之外的协议

  • TCP 和端口 25 上的出口流量

  • TCP 和端口 465 或 587 上的出口流量(SSL/TLS 上的 SMTP)

了解路由和防火墙的基本知识对于理解如何在 GCN 设计和实施 VPC 非常重要。

标签网络

一个非常有用的功能是网络标签。网络标签是一种属性,我们可以将其添加到计算引擎虚拟机中,并与防火墙和路由一起使用,仅适用于某些特定的计算引擎虚拟机。标签不需要在多个 VPC 网络中是唯一的。

当我们为网络创建标记时,我们可以将防火墙规则或路由应用于一组特定的实例。有了标签,我们可以

  • 使用目标标记和源标记,将防火墙规则应用于特定实例

  • 通过使用标签,将路线应用于特定实例

在 GCP,每个防火墙都有一个目标。默认情况下,所有防火墙都有目标all instances of the network。我们可以使用目标标签或目标服务帐户将实例指定为目标。一个目标标签被用来标识规则所应用到的 GCP 虚拟机。该规则适用于主要内部 IP 地址和具有相同网络标记的所有实例。

要在我们的网络中创建一个标签,我们必须使用这个命令:

gcloud compute instances add-tags <INSTANCE-NAME> --zone <ZONE> --tags <TAGS>

这会向实例中添加一个新标记,这意味着命令不会删除或修改现有标记。该命令的结果如清单 10-1 所示。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud compute instances add-tags test-tag --zone us-east1-b --tags test-tag
Updated [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/zones/us-east1-b/instances/test-tag].

Listing 10-1Result of the add-tags Command

我们可以使用命令remove-tags移除标签。语法是

gcloud compute instances remove-tags <INSTANCE-NAME> --zone <ZONE> --tags <TAGS>

当我们想要将防火墙规则或路由应用到一组特定的虚拟机实例时,标记非常有用。这使我们能够对网络有一个“逻辑”的表示,并且我们可以很容易地确定哪些规则适用于特定的虚拟机。

实施 VPC 网络

到目前为止,我已经介绍了 VPC 网络的理论和基础,但是要更好地理解这些,您必须亲自动手。所以,让我们开始为我们的谷歌云平台创建一个 VPC 网络。创建 VPC 的简单方法是通过 GCP 控制台创建一个自动模式网络。我们可以通过这些简单的步骤创建 VPC:

  1. 连接到 GCP 控制台。

  2. Move to the VPC networks page (Figure 10-3).

    img/464715_1_En_10_Fig3_HTML.jpg

    图 10-3

    用于创建新 VPC 网络的网络部分

  3. This opens the page from which it is possible to find all the VPC networks and create a new one. To create a new network, just click the CREATE VPC NETWORK button (Figure 10-4).

    img/464715_1_En_10_Fig4_HTML.jpg

    图 10-4

    创建新 VPC 网络的按钮

创建 VPC 网络的页面现已打开。在此页面上,我们可以添加并指明创建 VPC 所需的所有信息。首先,在适当的字段中,我们必须指明网络的名称。这必须是所有小写字母,没有空格。我们可以在网络名称中使用数字或连字符。有了名称,我们可以添加网络描述。这可以用来表示网络的范围。当我们创建名称并添加描述时,我们必须创建子网来连接 VPC。我们可以通过两种方式创建子网:

  • 自动的

  • 习俗

如果我们选择自动模式来创建 VPN,我们只需为我们的子网选择区域,GCP 就会为该区域分配默认的 IP 地址范围(图 10-5 )。

img/464715_1_En_10_Fig5_HTML.jpg

图 10-5

自动子网创建选项

当我们选择创建自定义子网时,我们必须为子网指定更多的值。我们可以指明网络的名称,遵守与 VPC 相同的规则。该名称必须全部为小写字母,只能包含字母、数字或连字符,不能包含空格。可以为子网添加描述。这对于指示子网的类型和用途很有用。

我们需要为子网指定的另一个参数是我们想要创建 VPC 的区域。该区域是我们可以在 Google Cloud 中使用的经典区域。在 IP 地址范围部分,我们可以确定 VPC 的私有 IP 范围。总是可以添加第二个 IP 范围。在这种情况下,我们可以指定一个名称和我们想要使用的 IP 范围。我们子网的最后两个选项是

  • 私有 Google 访问:该选项表示我们的 VPC 是否可以在不分配外部 IP 的情况下访问其他服务。

  • Flow logs : This option is used to generate the log of the subnet. It doesn’t slow down any service but generates Stackdriver traffic and can improve the cost of the service (Figure 10-6).

    img/464715_1_En_10_Fig6_HTML.jpg

    图 10-6

    用于生成自定义子网的选项

最后一个选项是动态路由模式。此选项用于确定哪些子网在云路由器中可见。我们可以设置两种类型的路由:

  • 地区的

  • 全球的

如果我们选择区域路由,Google 云路由器会在定义子网的区域中通告并传播我们的子网。通过全局路由,云路由器将我们的 VPC 中定义的所有子网通告给本地网络。云路由器将学习到的路由传播到 VPC 的所有地区。设置好所有选项后,我们可以单击“Create”按钮,这样就创建了 VPC。我们可以在谷歌控制台中看到创建的网络(图 10-7 )。

img/464715_1_En_10_Fig7_HTML.jpg

图 10-7

Google 控制台显示了我们刚刚创建的网络的信息

创建 VPC 的另一种方法是使用gcloud命令行。要创建网络和子网,我们必须执行两个不同的步骤。首先,我们必须创建网络,然后是子网。要创建网络,我们必须使用以下格式的命令:

gcloud compute --project=<project name> networks create <network name> --subnet-mode=auto/custom

如果我们想创建一个自定义模式的网络,我们必须添加一个子网。创建子网的命令是

gcloud compute --project=<project name> networks subnets create <subnet name> --network=<network name> --region=<region name> --range=<ip range> --secondary-range=<secondary range name>=<ip range> --bgp-routing-mode <routing mode>

路由模式部分是可选的,用于指示我们想要使用哪种类型的路由模式。我们现在可以尝试使用该命令来创建网络。第一步是创建网络。完整的命令是

gcloud compute --project=<your project name> networks create practicaldevopsgcp --subnet-mode=auto

该命令的结果显示了我们刚刚想要创建的网络的信息。我们可以看到清单 10-2 中命令的结果。

 pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud compute --project=practicaldevopsgcp-197023 networks create practicaldevopsgcp --subnet-mode=auto
Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp197023/global/networks/practicaldevopsgcp].
NAME               SUBNET_MODE   BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
practicaldevopsgcp  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 practicaldevopsgcp --allow tcp,udp,icmp --source-ranges <IP_RANGE>
$ gcloud compute firewall-rules create <FIREWALL_NAME> --network practicaldevopsgcp --allow tcp:22,tcp:3389,icmp

Listing 10-2The Result of the Command Line Network Creation

在 Google 控制台中,可以看到新创建的网络。因为我们已经在自动模式下创建了网络,所以控制台会显示任何地区的 IP 地址。全局路由被禁用,因为我们没有为网络指定任何路由,当然,我们也没有任何特定的防火墙规则。如果我们想创建我们的子网网络,我们必须用自定义模式创建网络。执行此操作的命令是

gcloud compute --project=<your project name> networks create practicaldevopsgcp-custom --subnet-mode=custom

该命令的结果显示了网络的创建和用于创建防火墙规则的命令(参见清单 10-3 )。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud compute --project=practicaldevopsgcp-197023 networks create practicaldevopsgcp-custom --subnet-mode=custom
Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/practicaldevopsgcp-custom].
NAME                      SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
practicaldevopsgcp-custom CUSTOM       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 practicaldevopsgcp-custom --allow tcp,udp,icmp --source-ranges <IP_RANGE>
$ gcloud compute firewall-rules create <FIREWALL_NAME> --network practicaldevopsgcp-custom --allow tcp:22,tcp:3389,icmp

Listing 10-3The Result of the Command to Create the Network in Custom Mode

网络现在显示在谷歌云控制台上,但因为我们没有在网络上添加任何子网,这只是在自定义模式下创建的名称(图 10-8 )。

img/464715_1_En_10_Fig8_HTML.jpg

图 10-8

创建的没有子网的自定义网络

下一步是创建与网络相关的子网。创建与网络相关联的子网的命令是

gcloud compute --project=<you project name> networks subnets create practicaldevopsgcp-subnet --network=practicaldevopsgcp-custom --region=us-central1 --range=10.0.0.0/24

用于创建网络的参数首先需要子网的名称。我们将网络的新子网与参数--network相关联。该命令的结果如清单 10-4 所示。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud compute --project=practicaldevopsgcp-197023 networks subnets create practicaldevopsgcp-subnet --network=practicaldevopsgcp
-custom --region=us-central1 --range=10.0.0.0/24
Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/regions/us-central1/subnetworks/practicaldevopsgcp-subnet].
NAME                      REGION      NETWORK                   RANGE
practicaldevopsgcp-subnet  us-central1  practicaldevopsgcp-custom  10.0.0.0/24

Listing 10-4The Result of the Subnet Associated with the Network

该命令将新的子网与我们之前创建的网络相关联。我们可以在控制台中看到与网络相关联的子网(图 10-9 )。

img/464715_1_En_10_Fig9_HTML.jpg

图 10-9

与先前网络相关联的子网

我们现在已经创建了一个网络,并将其与一个子网网络相关联。对于自定义网络,我们必须为要复制 VPC 的每个区域创建一个子网。

创建和维护防火墙规则

你已经学会了如何创造一个新的 VPC,但是创造一个 VPC 是不够的。为了保证访问的安全性,我们必须配置防火墙。防火墙是网络安全的基本组件之一,防火墙的目的是在流量不遵守某些规则的情况下拒绝或允许流量进入网络。

我们看到,当我们创建 VPC 时,GCP 为我们的 VPC 分配了一些默认防火墙规则。这是为了定义 VPC 允许或拒绝哪些流量,但这些规则不足以管理网络。为此,我们必须创建和管理管理 VPC 所需的防火墙规则。现在您将学习如何使用gcloud创建新的防火墙规则。创建新防火墙规则的命令是

gcloud compute firewall-rules create [NAME] \
    [--network [NETWORK]; default="default"] \
    [--priority [PRIORITY];default=1000] \
    [--direction (ingress|egress|in|out); default="ingress"] \
    [--action (deny | allow )] \
    [--target-tags [TAG][,TAG,...]] \
    [--target-service-accounts=[IAM Service Account] \
    [--source-ranges [CIDR-RANGE][, CIDR-RANGE...]] \
    [--source-tags [TAG][,TAG,...]] \
    [--source-service-accounts=[IAM Service Account] \
    [--destination-ranges [CIDR-RANGE][,CIDR-RANGE...]] \
    [--rules ([PROTOCOL][:PORT[-PORT]],[PROTOCOL[:PORT[-PORT]],...]] | all ) \
    [--disabled | --no-disabled]

所有这些参数都用于定义我们想要创建的防火墙规则的类型。以下是所有参数及其在创建防火墙规则中的作用。

  • --network:该参数表示我们创建防火墙规则的网络。如果我们没有指明任何网络,规则是在default网络中创建它。

  • --priority:表示规则优先级的数值。最低级别是 65535,通常分配给默认防火墙规则。数字越小,规则的优先级越高。数字越大,表示规则的优先级越低。

  • --direction:该参数表示规则的方向。入口是用来表示从源到目标的流量的方向。出口是指示从目标到目的地的流量的方向。

  • --action:此参数表示我们希望对防火墙规则执行什么操作。我们可以定义两个动作之一:

    • allow:这允许连接

    • deny:这阻止了连接

防火墙规则必须应用于目标。该目标可以是网络或服务用户。定义目标的两个不同值是

  • --target-tags:用于表示防火墙规则适用的网络。

  • --target-service-accounts:表示该规则适用的服务账户。

如果我们没有定义任何目标值,那么规则只适用于整个网络。配置的另一个重要部分是我们想要用来定义规则方向的流量方向。对于入口规则,我们必须指定一个源。我们可以指定三种类型的来源。

  • --source-ranges:通过这个标志,我们可以指定 CIDR 格式的源地址的范围。

  • 有了这个标志,我们将规则应用于所有来自特定标记网络的 IP 地址。

  • 使用这个标志,我们可以为一个服务帐户指定所有的 IP 地址。只有在不配置源标签的情况下,我们才能使用这个标签。

如果我们不指定任何来源,我们将在任何地方应用该规则,因为它适用于 IP 0.0.0.0/0。我们可以定义一个出口规则来使用目的地。这只有以下选择:

  • --destination-range:该标志使用 CIDR 格式定义目的 IP 的范围。

如果我们省略 destination 选项,出口目的地将应用于 IP 0.0.0.0/0 的任何地方。--rules选项定义规则适用于哪些协议和端口。如果我们使用All,我们将该规则应用于 VPC 网络中的所有协议和端口。防火墙规则的一个例子是

gcloud compute --project=<your project name> firewall-rules create test --direction=INGRESS --priority=1000 --network=practicaldevopsgcp-custom --action=deny --rules=tcp

该命令的结果是创建规则(参见清单 10-5 )。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud compute --project=practicaldevopsgcp-197023 firewall-rules create test --direction=INGRESS --priority=1000 --network=practical
devopsgcp-custom --action=deny --rules=tcp
Creating firewall...Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/firewalls/test].
Creating firewall...done.
NAME  NETWORK                    DIRECTION    PRIORITY    ALLOW  DENY
test  practicaldevopsgcp-custom  INGRESS      1000        tcp

Listing 10-5The Newly Created Firewall Rule

如果我们想要更新防火墙规则,我们可以使用相同的基本语法,只是我们不使用firewall-rules create,而是使用firewall-rules update命令。

在 GCP 创建和维护路线

在本章的开始,你学习了什么是路线。每个网络都有一些由系统生成的从 VPC 网络到 Internet 的路由。我们不能覆盖到同一网络中另一个子网的路由,但我们可以覆盖默认路由 0.0.0.0/0。我们可以创建到特定目的地范围的路线。这是为了将流量定向到 VPN 隧道、特定实例或其他目的地。

当我们规划网络时,我们希望确保不要覆盖任何现有路由。我们可以用这个简单的命令列出网络中的实际路由:

gcloud compute routes list

该命令的结果显示了系统中实际配置的所有路由。我们可以在清单 10-6 中看到路由的摘录。

pierluigi_riti@practicaldevopsgcp-197023:~$ gcloud compute routes list
NAME                            NETWORK  DEST_RANGE     NEXT_HOP             PRIORITY
default-route-1d7c378c35a58bf1  default  10.142.0.0/20  default              1000
default-route-2733f6b435addfb8  default  10.146.0.0/20  default              1000
default-route-27ee2255201b7609  default  10.152.0.0/20  default              1000
default-route-2ba825e5b5d3dd2a  default  10.160.0.0/20  default              1000
default-route-2eca2b974eff32fb  default  0.0.0.0/0      default-internet-gateway     1000

Listing 10-6The Route List Result

要查看单个路由的详细信息,我们可以使用以下命令:

gcloud compute routes describe <route name>

例如,我们可以使用以下命令行查看其中一条默认路由的详细信息:

gcloud compute routes describe default-route-9702087d924fe3ad

结果是路线的描述。

creationTimestamp: '2018-07-29T14:07:03.497-07:00'
description: Default local route to the subnetwork 10.132.0.0/20.
destRange: 10.132.0.0/20
id: '1148425788491797144'
kind: compute#route
name: default-route-9702087d924fe3ad
network: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/practicaldevopsgcp
nextHopNetwork: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/networks/practicaldevopsgcp
priority: 1000
selfLink: https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/routes/default-route-9702087d924fe3ad

路由描述向我们显示了与路由相关的信息,例如,路由的创建、路由的种类、与路由相关联的网络以及网络的下一跳。

我们希望对路由执行的最重要的操作是创建新的静态路由。创建新静态路由的语法是

gcloud compute routes create [ROUTE] \
--destination-range [DEST_RANGE] \
--network [NETWORK]\
[--tags=TAG,[TAG,...]] \
[--next-hop-address=[ADDRESS]] |
[--next-hop-gateway=default-internet-gateway]  |
[--next-hop-instance=[INSTANCE_NAME] |
[--next-hop-vpn-tunnel=[VPN_TUNNEL]]

用于创建路线的参数指示了我们在 GCP 创建新路线所需的所有信息。

  • --destination-range:该值表示输出数据包的目的地范围。

  • --network:表示我们定义规则的网络。我们必须小心地正确指示网络,否则我们可能会将路由应用到另一个网络。

  • --tags:我们可以使用这个选项来指示我们想要将哪个虚拟机关联到该路由。

跃点参数只能指定一次。我们可以在同一个定义中有两个跳跃参数。参数说明如下:

  • --next-hop-address:用于指定路由想要发送流量的 IP 地址。

  • --next-hop-gateway:此标志用于指定我们可以通过哪个子网将数据包发送到互联网连接。

  • --next-hop-instance:该标志用于指定我们要用来发送数据包的实例。

  • --next-hop-vpn-tunnel:该标志用于指定我们要通过哪个 VPN 隧道发送数据包。

生成新路由的命令是

gcloud compute --project=<your project name> routes create route-gcp --network=default --priority=1000 --destination-range=10.0.0.0/16 --next-hop-gateway=default-internet-gateway

操作的结果显示了路线的创建(列表 10-7 )。

gcloud compute --project=practicaldevopsgcp-197023 routes create route-gcp --network=default --priority=1000 --destination-range=
10.0.0.0/16 --next-hop-gateway=default-internet-gateway
Created [https://www.googleapis.com/compute/v1/projects/practicaldevopsgcp-197023/global/routes/route-gcp].
NAME       NETWORK  DEST_RANGE   NEXT_HOP                     PRIORITY
route-gcp  default  10.0.0.0/16  default-internet-gateway     1000

Listing 10-7The Result of the Route Creation

对于 GCP,默认情况下,我们不能将 IP 转发到另一个实例。这意味着我们可以向一个 IP 发送数据包,除非该 IP 与目的地不匹配,但是当我们想要创建一个新的路由或使用路由数据包的实例时,我们必须转发该 IP。为了转发 IP,我们必须创建带有标志--can-ip-forward的实例。为此,命令行如下:

gcloud compute instances create ... --can-ip-forward

这将创建能够转发 IP 的实例,并允许实例使用路由的帮助。我们必须研究的最后一个命令是删除路由。要删除一条路由,我们可以使用以下命令:

gcloud compute routes delete [ROUTE]

此命令删除名称中指示的路由。

结论

本章讲述了在 GCP 创建和维护网络和私有云资源的基础知识。当我们规划云项目时,这一功能非常重要。我们必须仔细规划网络,确保网络始终可达,并与客户保持准确的 SLA。此外,精心规划的网络有助于防止黑客攻击导致的数据泄露和服务中断。除了良好的路由之外,拥有强大的防火墙规则有助于加快网络速度,并防止黑客最常见的攻击。

posted @ 2024-08-12 11:18  绝不原创的飞龙  阅读(8)  评论(0编辑  收藏  举报