GCP-DevOps-高级教程-二-

241 阅读1小时+

GCP DevOps 高级教程(二)

原文:Pro DevOps with Google Cloud Platform

协议:CC BY-NC-SA 4.0

六、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 安全异常。