自动启动应用镜像进行应用监控

本教程介绍了如何使用 Cloud LoggingPub/SubCloud Functions 自动启用数据包镜像,以便在 Virtual Private Cloud (VPC) 网络中监控流量并进行问题排查。本教程面向网络、安全和 DevOps 团队。此外,还假定您熟悉 Cloud Logging、Pub/Sub、Cloud Functions、数据包镜像、Compute Engine 和 Terraform。

简介

数据包镜像是一项功能,可让您实时监控 VPC 流量。使用数据包镜像,DevOps 团队可以对性能下降或生成错误消息的流量进行问题排查,或者注重安全的企业可以观察和应对可能恶意的流量模式。组织通常使用数据包镜像以及入侵检测系统 (IDS) 来帮助检测和减轻威胁。

数据包镜像会捕获所有入站和出站流量以及数据包数据(例如载荷和标头),然后导出流量,从而使您能够全面掌控网络。您可以将镜像的流量从带外发送到安全和监控设备,从而帮助您检测威胁、监控网络性能以及排查应用问题。

您可以在子网级层、网络标记或特定 VPC 实例上启用数据包镜像。您可以执行连续数据包镜像,也可以根据预定义的触发器启用和停用数据包镜像。

在本教程中,您将配置下图中的架构。

互联网流量通过全球负载平衡器路由到数据包镜像 VPC。

此架构的工作流如下:

  1. 系统将无效请求发送到负载平衡器,从而触发来自 Web 服务器的 HTTP 500 Internal Server Error 状态代码。
  2. Cloud Monitoring 生成事件,Cloud Logging 记录错误消息。
  3. 配置为 Cloud Monitoring 的接收器的 Pub/Sub 收到错误消息。
  4. Cloud Monitoring 将事件推送到 Pub/Sub,从而触发 Cloud Functions 来启用数据包镜像。
  5. 数据包镜像启用,流量镜像到收集器虚拟机,以便相应人员或团队可以进一步调查错误消息。在本教程中,您将使用 tcpdump 实用程序查看捕获的数据包。

为完成本教程,您需要使用 HashiCorp 的 Terraform 创建 VPC、子网、全局负载平衡器、Web 服务器和收集器虚拟机。然后,您可以手动配置数据包镜像政策及其关联的收集器虚拟机,以接收镜像的流量。最后,您需要配置 Cloud Logging、Cloud Functions 和 Pub/Sub 以触发数据包镜像。

虽然本教程介绍的是使用错误消息代码 (HTTP 500) 触发事件时可以执行的操作,但您可以针对其他使用场景和环境自定义此解决方案。例如,您可能希望触发日志记录,以便查看模式(例如特定的正则表达式模式)或应用指标(如 CPU 和内存用量)。

如需详细了解如何配置 Cloud Monitoring 和 Cloud Logging,请参阅 Cloud LoggingCloud Monitoring 相关文档。

目标

  • 使用 Terraform 部署基本环境。
  • 配置数据包镜像基础架构。
  • 触发应用错误消息。
  • 验证数据包镜像是否已启用。
  • 在收集器 Compute Engine 实例上显示数据包捕获。

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用情况来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 在控制台中,激活 Cloud Shell。

    激活 Cloud Shell

    您可以使用 Terraform 和 Google Cloud CLI 从 Cloud Shell 终端完成本教程的大部分内容。

  2. 在 Cloud Shell 中,更改本地工作目录并克隆 GitHub 代码库:

    cd $HOME
    git clone https://github.com/GoogleCloudPlatform/terraform-gce-packetmirror.git packetMirror
    

    该代码库包含完成本教程所需的所有文件。如需查看每个文件的完整说明,请参阅代码库中的 README.md 文件。

  3. 将所有 Shell 脚本设置为可执行脚本:

    cd $HOME/packetMirror
    sudo chmod 755 *.sh
    
  4. 确保您在本教程中使用的用户帐号具有完成本教程所需的 Identity and Access Management (IAM) 权限。

准备环境

在本部分中,您将设置环境变量并部署辅助基础架构。

设置 Terraform

  1. 按照 HashiCorp 文档中的步骤安装 Terraform。
  2. 在 Cloud Shell 中,初始化 Terraform:

    terraform init
    

    输出内容如下所示:

    ...
    Initializing provider plugins...
    The following providers do not have any version constraints in configuration, so the latest version was installed.
    ...
    Terraform has been successfully initialized!
    ...
    

设置环境变量

  1. 如果您的 Google Cloud 用户帐号属于 Google Cloud 组织,请在 Cloud Shell 中运行以下命令:

    1. TF_VAR_org_id 变量设置为您的 Google Cloud 组织的名称:

      export TF_VAR_org_id=$(gcloud organizations list \
          --format="value(ID)" \
          --filter="DISPLAY_NAME:YOUR_ORGANIZATION_NAME")
      

      YOUR_ORGANIZATION_NAME 替换为您要在本教程中使用的 Google Cloud 组织名称。

    2. 将 Terraform 变量 org_id 添加到项目资源中:

      sed -i "s/#org_id          = var.org_id/org_id          = var.org_id/" main.tf
      
    3. 验证您是否正确设置该环境变量:

      echo $TF_VAR_org_id
      

      输出内容会以数字形式列出您的组织 ID,如下所示:

      ...
      123123123123
      ...
      
  2. 设置结算帐号名称:

    1. 列出您的有效结算帐号:

      gcloud beta billing accounts list
      

      复制您要用于本教程的结算帐号的名称。下一步中您需要用到该名称。

    2. 设置结算帐号环境变量:

      TF_VAR_billing_name="YOUR_BILLING_ACCOUNT_NAME"
      export TF_VAR_billing_name
      

      YOUR_BILLING_ACCOUNT_NAME 替换为您在上一步中复制的结算帐号的名称。

  3. 设置其余的环境变量:

    source $HOME/packetMirror/set_variables.sh
    
  4. 验证您是否正确设置该环境变量:

    env | grep TF_
    

    输出内容如下所示:

    ...
    TF_VAR_billing_account=YOUR_BILLING_ACCOUNT_NAME
    TF_VAR_org_id=YOUR_ORGANIZATION_NAME
    TF_VAR_region=YOUR_REGION
    TF_VAR_user_account=YOUR_USER_ACCOUNT
    TF_VAR_pid=YOUR_PROJECT_ID
    TF_VAR_zone=YOUR_ZONE
    ...
    

    在此输出中:

    • YOUR_REGION:您的 Google Cloud 项目所在的地区,例如 us-west1
    • YOUR_USER_ACCOUNT:您的帐号 ID,例如 user@example
    • YOUR_PROJECT_ID:您的 Cloud 项目 ID
    • YOUR_ZONE:您的 Cloud 项目所在的区域,例如 us-west1-b
  5. 如果 TF_VAR_billing_account 变量未正确设置,请在控制台中,从管理结算帐号页面转到结算帐号概览页面,复制结算帐号 ID 编号,然后设置以下环境变量:

    export TF_VAR_billing_account=BILLING_ACCOUNT_ID
    

    BILLING_ACCOUNT_ID 替换为您在此步骤前面复制的帐号 ID 编号。

  6. 创建环境变量文件:

    $HOME/packetMirror/saveVars.sh
    

    此命令会将您创建的环境变量重定向到名为 TF_ENV_VARS 的文件中。每个变量前面都会附加 export 命令。如果您的 Cloud Shell 会话终止,则可以使用此文件来重置变量。这些变量可供 Terraform 脚本、Cloud Shell 脚本和 Google Cloud CLI 使用。

    如果您以后需要重新初始化这些变量,则可以运行以下命令:

    source $HOME/packetMirror/TF_ENV_VARS
    

部署基础架构

  1. 在 Cloud Shell 中,部署 Terraform 辅助基础架构:

    cd $HOME/packetMirror
    terraform apply
    
  2. 出现提示时,输入 yes 以应用任一配置。

    terraform apply 命令指示 Terraform 部署项目、网络、子网、全球负载平衡器和 Web 服务器。如需了解如何以声明方式定义基础架构,您可以浏览 Terraform 清单(扩展名为 .tf 的文件)。

    Terraform 可能需要几分钟时间来部署组件。系统会创建以下对象:

    • VPC
    • 防火墙规则
    • 子网
    • Web 服务器的实例模板
    • Web 服务器的代管式实例组
    • 收集器虚拟机的非代管式实例组
    • Cloud NAT
    • 全球负载均衡器和关联的健康检查

    您可以通过控制台或使用 gcloud 命令浏览您的项目来查看这些项。

创建数据包镜像资源

在以下步骤中,您将创建并验证数据包镜像基础架构。

创建收集器内部负载平衡资源

  • 在 Cloud Shell 中,创建后端服务和转发规则:

    gcloud compute backend-services create collector-ilb \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --load-balancing-scheme=internal \
        --protocol=tcp \
        --health-checks=http-basic-check
    
    gcloud compute backend-services add-backend collector-ilb \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --instance-group=collector-ig \
        --instance-group-zone="$TF_VAR_zone"
    
    gcloud compute forwarding-rules create fr-ilb-packet-mirroring \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --load-balancing-scheme=internal \
        --network=packet-mirror-vpc \
        --subnet=collectors \
        --ip-protocol=TCP \
        --ports=all \
        --backend-service=collector-ilb \
        --backend-service-region="$TF_VAR_region" \
        --is-mirroring-collector
    

    输出内容如下所示:

    ...
    Created [https://www.googleapis.com/compute/v1/projects/pm-pid-1357261223/regions/us-west1/backendServices/collector-ilb].
    NAME           BACKENDS  PROTOCOL
    collector-ilb            TCP
    ...
    Updated [https://www.googleapis.com/compute/v1/projects/pm-pid-1357261223/regions/us-west1/backendServices/collector-ilb].
    ...
    Created [https://www.googleapis.com/compute/projects/pm-pid-1357261223/regions/us-west1/forwardingRules/fr-ilb-packet-mirroring].
    ...
    

创建并停用数据包镜像政策

  1. 在 Cloud Shell 中,创建并停用数据包镜像政策:

    gcloud compute packet-mirrorings create pm-mirror-subnet1 \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --network=packet-mirror-vpc \
        --collector-ilb=fr-ilb-packet-mirroring \
        --mirrored-subnets=webservers \
        --no-enable
    
    gcloud compute packet-mirrorings describe pm-mirror-subnet1 \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region"
    

    输出内容如下所示:

    ...
    Created [https://www.googleapis.com/compute/projects/pm-pid-1357261223/regions/us-west1/packetMirrorings/pm-mirror-subnet1].
    ...
    collectorIlb:
    ...
    enable: 'FALSE'
    ...
    

    数据包镜像政策默认处于启用状态。在此步骤中,您停用该政策,因为您希望在 Cloud Logging 检测到问题之前关闭数据包镜像。

创建自动化以触发数据包镜像

  1. 在 Cloud Shell 中,创建用于 Cloud Logging 接收器的 Pub/Sub 主题:

    gcloud pubsub topics create stackdriver_logging \
        --project="$TF_VAR_pid"
    

    输出内容如下所示:

    ...
    Created topic [projects/pm-pid-1357261223/topics/stackdriver_logging].
    ...
    
  2. 创建 Cloud Logging 接收器:

    gcloud logging sinks create stackdriver_logging pubsub.googleapis.com/projects/"$TF_VAR_pid"/topics/stackdriver_logging \
        --log-filter='resource.type="http_load_balancer" \
            AND http_request.status>=500' \
        --project=$TF_VAR_pid
    

    Cloud Logging 接收器会过滤 500 范围内的全球 HTTP 状态代码(例如 500501502),并将事件发送到此 Pub/Sub 主题。

    输出内容如下所示:

    Created [https://logging.googleapis.com/v2/projects/pm-pid-1357261223/sinks/stackdriver_logging].
    Please remember to grant `serviceAccount:p422429379846-984011@gcp-sa-logging.iam.gserviceaccount.com` the Pub/Sub Publisher role on the topic.
    More information about sinks can be found at https://cloud.oogle.com/logging/docs/export/configure_export
    

    复制输出中的 serviceAccount 值。下一步中您需要用到该值。

  3. 向服务帐号授予 Pub/Sub Publisher IAM 角色 (roles/pubsub.publisher):

    gcloud pubsub topics add-iam-policy-binding stackdriver_logging \
        --project="$TF_VAR_pid" \
        --member serviceAccount:UPDATE_ACCOUNT \
        --role roles/pubsub.publisher
    

    UPDATE_ACCOUNT 替换为上一步中 serviceAccount 的值。

    输出内容如下所示:

    ...
    Updated IAM policy for topic [stackdriver_logging].
    bindings:
    - members:
      - serviceAccount:UPDATE_ACCOUNT
      role: roles/pubsub.publisher
    etag: notuCRmpoyI=
    version: 1
    ...
    
  4. 更新 main.py 文件。

    net_id="$(gcloud compute networks describe packet-mirror-vpc --project="$TF_VAR_pid" --format='value(id)')"
    sed -i "s/PROJECT-ID/"$TF_VAR_pid"/g" main.py
    sed -i "s/REGION/"$TF_VAR_region"/g" main.py
    sed -i "s/NETWORK-ID/"$net_id"/g" main.py
    

    代码库中的 main.py 文件包含您将用于创建 Cloud Functions 函数的 packet_mirror_pubsub 函数。在创建 Cloud Functions 函数之前,上述命令会更新 Python 文件中的 Google Cloud 项目 ID、地区和网络信息。

  5. 创建 Cloud Functions 函数:

    gcloud functions deploy packet_mirror_pubsub \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --runtime python37 \
        --trigger-topic stackdriver_logging
    

    如果您看到以下警告,请输入 N

    Allow unauthenticated invocations of new function
    [packet_mirror_pubsub]? (y/N)?
    

    输出内容如下所示:

    ...
    availableMemoryMb: 256
    entryPoint: packet_mirror_pubsub
    eventTrigger:
      eventType: google.pubsub.topic.publish
      failurePolicy: {}
      resource: projects/pm-pid--1517226903/topics/stackdriver_logging
      service: pubsub.googleapis.com
    ingressSettings: ALLOW_ALL
    labels:
      deployment-tool: cli-gcloud
    name: projects/pm-pid--1517226903/locations/europe-west1/functions/packet_mirror_pubsub
    runtime: python37
    serviceAccountEmail: pm-pid--1517226903@appspot.gserviceaccount.com
    ...
    status: ACTIVE
    ...
    

    部署 Cloud Functions 函数可能需要几分钟时间才能完成。

  6. 如果您收到错误消息,请执行以下操作:

    • 如果您收到有关启用 Cloud Build API 的错误消息,请启用该 API:

      gcloud services enable cloudbuild.googleapis.com
      

      重试第 5 步。

    • 如果您收到有关访问 Cloud Storage 的错误消息,请重试第 5 步。当您快速连续运行命令时,可能会发生此错误。

验证解决方案

在以下步骤中,您将触发并验证解决方案。

  1. 在 Cloud Shell 中,验证新的 Pub/Sub 订阅:

    gcloud pubsub subscriptions list --project="$TF_VAR_pid"
    

    输出内容如下所示:

    ...
    ---
    ackDeadlineSeconds: 600
    expirationPolicy: {}
    messageRetentionDuration: 604800s
    name: projects/pm-pid--1517226903/subscriptions/gcf-packet_mirror_pubsub-europe-west1-stackdriver_logging
    pushConfig:
      attributes:
        x-goog-version: v1
      pushEndpoint: https://e147a3acbd9a5314f553d1710671be9c-dot-efdbf9529ce1147d5p-tp.appspot.com/_ah/push-handlers/pubsub/projects/pm-pid--1517226903/topics/stackdriver_logging?pubsub_trigger=true
    topic: projects/pm-pid--1517226903/topics/stackdriver_logging
    ...
    
  2. 登录收集器虚拟机:

    gcloud compute ssh collector \
        --tunnel-through-iap \
        --project="$TF_VAR_pid" \
        --zone="$TF_VAR_zone"
    
  3. 登录收集器虚拟机后,安装并启用 tcpdump 实用程序:

    sudo apt-get install tcpdump -y
    sudo tcpdump -n not net 172.16.21.0/24
    

    输出内容如下所示:

    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
    

    让此 Cloud Shell 会话保持打开状态。

  4. 打开一个新的 Cloud Shell 终端窗口,然后通过生成 HTTP 500 错误来触发 Cloud Functions 函数:

    cd $HOME/packetMirror
    source TF_ENV_VARS
    lb_ip=$(gcloud compute forwarding-rules describe packet-mirror-gfr --project=$TF_VAR_pid --global --format="value(IPAddress)")
    curl http://"$lb_ip"/error500
    

    输出内容如下所示:

    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
    <html><head>
    <title>500 Internal Server Error</title>
    </head><body>
    <h1>Internal Server Error</h1>
    <p>The server encountered an internal error or
    misconfiguration and was unable to complete
    your request.</p>
    <p>Please contact the server administrator at
     webmaster@localhost to inform them of the time this error occurred,
     and the actions you performed just before this error.</p>
    <p>More information about this error may be available
    in the server error log.</p>
    <hr>
    <address>Apache/2.4.25 (Debian) Server at 35.241.40.217 Port 80</address>
    
  5. 返回收集器虚拟机的 Cloud Shell 会话,并观察 tcpdump 命令的输出。收集器虚拟机接收流量,即表示 Web 服务器实例上的健康检查探测。

    输出内容如下所示:

    ...
    07:33:41.131992 IP 130.211.2.146.53702 > 172.16.20.2.80: Flags [S], seq 4226031116, win 65535, options [mss 1420,sackOK,TS val 2961711820 ecr 0,nop,wscale 8], length 0
    07:33:41.132149 IP 130.211.2.146.53702 > 172.16.20.2.80: Flags [.], ack 3978158577, win 256, options [nop,nop,TS val 2961711821 ecr 4348156], length 0
    ...
    
  6. 如需停止 tcpdump 命令的输出,请按 Control+C

  7. 输入 exit 以退出收集器虚拟机。

  8. 在 Cloud Shell 中,查看日志以验证 Cloud Functions 函数是否已运行:

    gcloud functions logs read \
        --limit 50 \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region"
    

    输出内容如下所示:

    LEVEL  NAME                  EXECUTION_ID     TIME_UTC                 LOG
    D      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:39.206  Function execution started
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:39.222  HTTP 500 Error Detected in: {"httpRequest":{"remoteIp":"136.27.39.107","requestMethod":"GET","requestSize":"85","requestUrl":"http://35.241.40.217/error500","responseSize":"801","serverIp":"172.16.20.2","status":500,"userAgent":"curl/7.52.1"},"insertId":"nb4g1sfdrpm04","jsonPayload":{"@type":"type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry","enforcedSecurityPolicy":{"configuredAction":"ACCEPT","name":"policy","outcome":"ACCEPT","priority":2147483647},"statusDetails":"response_sent_by_backend"},".............
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:39.222  Activating Packet Mirroring For Analysis
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:39.329  ya29.c.KqUBvwfoZ5z88EmHKPXkgd1Gwqwwca88wWsyqjxrEFdhK8HjJDwmBWBIX_DAnC4wOO5W2B6EOQArgHQ03AIVwFnQMawXrB2tLGIkBYFuP3Go5Fylo6zZAvgtXF3LvrXiarwaASkfAM73lXfQiT20PYn4ML4E2Kli9WmhZDu6AdAe1aH-FK2MEoca84zgG65tirRGe04EJGY_hYHejlG_xrRWeaojVlc3
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:40.100  {
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:40.100    "id": "1924200601229605180",
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:40.100    "name": "operation-1582270419413-59f110a49a878-b68f2d26-c8f66a7b",
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:40.100    "operationType": "patch",
    I      packet_mirror_pubsub  999875368753102  2020-02-21 07:33:40.100    …..
     Function execution took 900 ms, finished with status: 'ok'
    

    日志显示系统已基于 Web 服务器实例生成的 HTTP 500 错误代码触发数据包镜像。

  9. 验证数据包镜像功能状态:

    gcloud compute packet-mirrorings describe pm-mirror-subnet1 \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region"
    

    输出内容类似如下:

    ...
    collectorIlb:
    ...
    enable: 'TRUE'
    ...
    

清除数据

为避免因本教程中使用的资源导致您的 Google Cloud 帐号产生费用,请执行以下操作。

删除基础架构

  1. 在 Cloud Shell 中,移除自动化资源:

    gcloud functions delete packet_mirror_pubsub \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --quiet
    gcloud logging sinks delete stackdriver_logging \
        --project="$TF_VAR_pid" \
        --quiet
    gcloud pubsub topics delete stackdriver_logging \
        --project="$TF_VAR_pid" \
        --quiet
    gcloud compute packet-mirrorings delete  pm-mirror-subnet1  \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --quiet
    gcloud compute forwarding-rules delete fr-ilb-packet-mirroring \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --quiet
    gcloud compute backend-services delete collector-ilb \
        --project="$TF_VAR_pid" \
        --region="$TF_VAR_region" \
        --quiet
    
  2. 销毁本教程中的所有组件:

    pushd $HOME/packetMirror
    terraform destroy
    popd
    

    出现提示时,输入 yes 以销毁配置。

  3. 移除 Git 代码库:

    rm -rf $HOME/packetMirror
    

后续步骤