平台工程实战:用Score和Humanitec部署线上精品店示例应用

2024/12/30 21:03:21

本文主要是介绍平台工程实战:用Score和Humanitec部署线上精品店示例应用,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

最近,随着KubeCon Paris 2024和Google Cloud Next 2024的召开,我一直在准备并展示关于Score和Humanitec的大量演示。其中一个演示是基于Google Cloud提供的Online Boutique示例应用。你可以将其部署到任何Kubernetes集群(而不仅限于GKE,我也会使用Azure/AKS来做这个),使用纯Kubernetes配置文件、它的Helm Chart或者Kustomize叠加。十分方便。

让我向你展示使用Score和Humanitec部署Online Boutique示例应用的另一种方式。我们将看到如何操作,但更重要的是,我们将解释为什么。特别是围绕两个主要人物:

  • 程序员
  • 平台架构师

更具体地说,这里是我们将要涉及的案例:

作为一名开发者,我想:

  • 定义我的工作负载和它们的依赖关系;
  • 在本地部署我的工作负载;
  • 将我的工作负载部署到IDP上;
  • 在我的门户中查看工作负载。

作为一名平台工程师,我希望能实现:

  • 在我的现有工具和平台上构建我自己的IDP;
  • 集中管理和安全的最佳实践措施;
  • 为开发人员配置数据库依赖关系;

干吧!

程序员

作为一名开发者,我想专注于我的应用,而无需担心基础设施和相关的 Kubernetes。

在本节中,从开发者的角度来说,我们将看到开发体验如何变得更好,开发速度如何加快了,以及认知负担如何减轻。

作为开发者,我想定义我的工作负载及其依赖关系。

作为开发者,我不仅有我的代码,还有容器化工作负载的方法。现在我要定义的是,让我的工作负载能够顺利部署需要哪些条件。我不清楚技术细节,也不了解部署的位置和如何解决依赖问题。我会让IDP(身份提供程序)来帮我搞定这些问题。

我们来看一个例子,比如cartservice的Score文件(分数文件)。

apiVersion: score.dev/v1b1  
metadata:  
  name: cartservice  
容器:
  cartservice:  
    image: gcr.io/google-samples/microservices-demo/cartservice:v0.10.0  
    variables:  
      REDIS_ADDR: "${resources.redis-cart.host}:${resources.redis-cart.port},user=${resources.redis-cart.username},password=${resources.redis-cart.password}"  
    resources:  
      limits:  
        memory: "128Mi"  
        cpu: "300m"  
      requests:  
        memory: "64Mi"  
        cpu: "200m"  
resources:  
  redis-cart:  
    type: redis  
服务:
  端口:
    grpc:  
      port: 7070  
      targetPort: 7070

在这个第一个例子中,我们可以看到cartservice工作负载的开发人员定义了容器暴露的端口、资源限制和需求(如果有这些信息的话),以及对Redis数据库的依赖关系。至于这个Redis数据库在哪里以及连接字符串是什么,这又是另一个话题。在这个阶段,开发人员不需要关心这些细节。

比如,这里是frontend工作负载的评分文件。

apiVersion: score.dev/v1b1
metadata:
  name: frontend
containers:
  frontend:
    image: gcr.io/google-samples/microservices-demo/frontend:v0.10.0
    livenessProbe:
      httpGet:
        path: /_healthz
        port: 8080
        httpHeaders:
        - name: Cookie
          value: shop_session-id=x-liveness-probe
    readinessProbe:
      httpGet:
        path: /_healthz
        port: 8080
        httpHeaders:
        - name: Cookie
          value: shop_session-id=x-readiness-probe
    variables:
      AD_SERVICE_ADDR: "${resources.adservice.name}:9555"
      CART_SERVICE_ADDR: "${resources.cartservice.name}:7070"
      CHECKOUT_SERVICE_ADDR: "${resources.checkoutservice.name}:5050"
      CURRENCY_SERVICE_ADDR: "${resources.currencyservice.name}:7000"
      ENABLE_PROFILER: "0"
      PAYMENT_SERVICE_ADDR: "${resources.paymentservice.name}:50051"
      PORT: "8080"
      PRODUCT_CATALOG_SERVICE_ADDR: "${resources.productcatalogservice.name}:3550"
      RECOMMENDATION_SERVICE_ADDR: "${resources.recommendationservice.name}:8080"
      SHIPPING_SERVICE_ADDR: "${resources.shippingservice.name}:5051"
      CYMBAL_BRANDING: "false"
      FRONTEND_MESSAGE: ""
      ENABLE_ASSISTANT: "false"
      SHOPPING_ASSISTANT_SERVICE_ADDR: "${resources.shoppingassistantservice.name}:8080"
    resources:
      limits:
        memory: "128Mi"
        cpu: "200m"
      requests:
        memory: "64Mi"
        cpu: "100m"
resources:
  dns:
    type: dns
  route:
    type: route
    params:
      host: ${resources.dns.host}
      path: /
      port: 80
  adservice:
    type: service
  cartservice:
    type: service
  checkoutservice:
    type: service
  currencyservice:
    type: service
  paymentservice:
    type: service
  productcatalogservice:
    type: service
  recommendationservice:
    type: service
  shippingservice:
    type: service
  shoppingassistantservice:
    type: service
service:
  ports:
    http:
      port: 80
      targetPort: 8080

在第二个例子中,我们能够看到frontend工作负载的开发者定义了更多内容,如livenessProbereadinessProbe、同时也定义了其他资源,例如与其他工作负载的依赖关系等,以及需要通过DNS将其暴露。

再次,所有这些依赖都被抽象出来了,相关的占位符在部署这个Score文件时将被平台编排器替换。

作为一名开发者,我可以专注于编写代码,同时利用IDP为我提供的这些良好支持的黄金路径。

使用 Humanitec VS Code 扩展 插件创建或编辑 Score 文件,以提高生产力,与 IDP 进行交互,查看开发人员可用的依赖资源等操作。

基于一名开发者的身份,我想本地运行我的应用或工作。

作为一名开发者,我需要在将工作负载及其依赖项提交到我的 Git 仓库和相关的 CI/CD 管道之前,在本地进行测试。当然,我可以在 IDE 中运行代码,但如果我想重用工作负载及其依赖项的定义并在本地部署它们之前,我该如何操作呢?这时可以使用 [score-compose](https://docs.score.dev/docs/score-implementation/score-compose/), 这也就是 Score 的一种实现,将 Score 文件转换成 compose.yaml 文件:

    score-compose init  

    score-compose 生成 score.yaml 文件  

    docker compose up --build -d (启动并构建容器)

init 命令会创建 score-compose 的工作区,并使用默认的资源提供者(如 redisdnsamqppostgres 等)。

generate 命令将根据工作负载的 Score 文件,并通过解析资源依赖关系来生成 compose.yaml 文件。

然后可以运行 docker compose up,并在本地测试这些容器。

    [+] 运行 13/13  
     ✔ 容器: onlineboutique-demo-redis-iVXhMw-1                                 运行   
     ✔ onlineboutique-demo-routing-Rv5fqb-1                               运行  
     ✔ onlineboutique-demo-wait-for-resources-1                           已启动  
     ✔ onlineboutique-demo-emailservice-emailservice-1                    已启动  
     ✔ onlineboutique-demo-cartservice-cartservice-1                      已启动  
     ✔ onlineboutique-demo-checkoutservice-checkoutservice-1              已启动  
     ✔ onlineboutique-demo-frontend-frontend-1                            已启动  
     ✔ onlineboutique-demo-recommendationservice-recommendationservice-1  已启动  
     ✔ onlineboutique-demo-shippingservice-shippingservice-1              已启动  
     ✔ onlineboutique-demo-paymentservice-paymentservice-1                已启动  
     ✔ onlineboutique-demo-productcatalogservice-productcatalogservice-1  已启动  
     ✔ onlineboutique-demo-currencyservice-currencyservice-1              已启动  
     ✔ onlineboutique-demo-adservice-adservice-1                          已启动

挺酷的吧,对吧?

作为开发者,我想将我的应用部署到idp上。

作为一名开发者,我现在需要将我的工作负载部署到IDP环境,以便将其部署到 staging 和生产环境中。使用 Humanitec CLI(人类科技命令行工具),您可以使用之前定义并用过的相同 Score 文件(评分文件)来完成这个过程。

    humctl 评分部署 \  
        --app ${APP_ID} \  
        --env ${ENVIRONMENT_ID} \  
        -f score.yaml

在实际环境中,当你在 main 分支或任何功能分支中提交时,你的 CI/CD 流水线会立即处理部署。通常,这些 CI/CD 流水线模板是由平台团队提供的,开发人员可以在自己的 Git 仓库中使用这些模板。

这样的CI/CD管道可以帮助创建临时测试环境,以便测试功能分支代码。下面是一个Pull Request的示例,展示了临时环境中部署的相关信息。

在这个阶段,有趣的是我们可以查看此部署的资源图表。在那里,平台正在为开发人员解析并抽象化一个复杂的依赖关系网络。

作为开发者,我希望在我的控制面板上看到我的任务。

一旦我的工作负载部署到某个特定环境后,我希望能够查看它们的部署情况、依赖关系等。这就引出了开发者门户这个概念。Humanitec 默认提供了 Humanitec 门户,以使开发人员在这些方面更加自主:

我也能够获取特定工作负载的依赖情况、容器日志等信息,而无需直接操作Kubernetes。

这一部分全部围绕着开发者的角色。我们看到了开发者的认知负担已经减少,让他们更加专注于描述他们想要的东西,而无需考虑所有技术细节的实现,比如云服务商和Kubernetes中的实现。在接下来的部分中,我们将看到,这种抽象层次是如何通过平台工程师在平台编排器(Platform Orchestrator)中配置和连接所有组件和方案来实现的。

平台架构师

作为一名平台工程师,我希望能够集中管理一个一致和安全的平台,让开发人员更自主。

在本节中,从平台工程师的视角,我们将看到如何将治理、安全和可观测性标准化并抽象化到平台。

一个平台工程师想要在我的现有工具和平台上构建自己的个人发展计划 (IDP)。

如下面的参考架构所示,您基于现有的工具构建自己的内部开发者平台(IDP)。如图中黄色部分所示,您将 Humanitec Orchestrator 集成到 CI/CD 流水线中,并要求开发人员通过 Score 来定义他们想要怎样部署工作负载(参见前一节)。最后,不同角色可以通过 Humanitec 门户查看所有相关资源。重要的是,您的 IDP 是由工具和团队的组合构成的。安全、可观测性团队、SRE 和云团队将与平台工程团队合作,通过贡献各自的方案来支持 IDP 的构建。

你的工具链 + Humanitec = 成功的IDP

_注意:此图中显示了刚刚在 Google Cloud Next 2024 上宣布的新 Google Cloud App Hub 服务。我们与 Google 合作了,展示 Humanitec 如何轻松地将项目和工作负载迁入新的 App Hub 服务中。想了解更多关于 App Hub 的信息,可以观看这两个会议:OPS105 和 OPS100。

通过Humanitec平台被引入Google Cloud App Hub的项目和任务负载

你可以创建你自己的平台工程参考架构,也可以在Azure、AWS、Google Cloud等公有云上应用Humanitec的参考架构。

《作为一名平台工程师,我希望集中管理和安全的最佳做法。》

平台团队(如运维、可观测性、安全等)报告的一个问题是,开发人员因为大多数情况下这些Kubernetes清单文件(如Helm、Kustomize等)由他们自己拥有并在自己的Git仓库中托管,所以很难制作和维护这些文件。这时,平台编排器工具就能发挥作用,平台工程师可以定义一组想要标准化的标准模板。这可以是Kubernetes清单、Terraform模块等。

以下是一个示例配置,可确保Kubernetes中的任何Namespace启用Istio服务网格并强制执行Pod安全标准(PSS):

apiVersion: entity.humanitec.io/v1b1  
kind: Definition  
metadata:  
  id: custom-namespace  
entity:  
  name: custom-namespace  
  type: k8s-namespace  
  driver_type: humanitec/template  
  driver_inputs:  
    values:  
      templates:  
        init: |  
          name: ${context.app.id}-${context.env.id}  
        manifests: |-  
          namespace.yaml:  
            location: cluster  
            data:  
              apiVersion: v1  
              kind: Namespace  
              metadata:  
                labels:  
                  pod-security.kubernetes.io/enforce: restricted  
                  istio-injection: enabled  
                name: {{ .init.name }}  
      outputs: |  
        namespace: {{ .init.name }}  
criteria:  
  - {}  

你也可以在这里添加更多的标签注释给你的可观测性工具或FinOps(财务管理操作)工具。

一旦通过Humanitec Orchestrator部署后,我们就能看到工作负载现在透明地纳入启用了Google Cloud Service Mesh的GKE集群中。

通过Humanitec Orchestrator部署的工作负载的Google Cloud中的服务网格拓扑结构。

但现在,你是否要求你的开发人员在他们的工作负载中添加 securityContext 以提高安全性?当然不行!作为平台工程师,你也应该将这一点抽象出来。这里又有一个方案来确保和标准化这一点。

apiVersion: entity.humanitec.io/v1b1  
kind: Definition  
metadata:  
  id: custom-workload  
entity:  
  name: custom-workload  
  type: workload  
  driver_type: humanitec/template  
  driver_inputs:  
    values:  
      templates:  
        outputs: |  
          update:  
            - op: add  
              path: /spec/automountServiceAccountToken  
              value: false  
            - op: add  
              path: /spec/securityContext  
              value:  
                fsGroup: 1000  
                runAsGroup: 1000  
                runAsNonRoot: true  
                runAsUser: 1000  
                seccompProfile:  
                  type: RuntimeDefault  
          {{- range $containerId, $value := .resource.spec.containers }}  
            - op: add  
              path: /spec/containers/{{ $containerId }}/securityContext  
              value:  
                allowPrivilegeEscalation: false  
                capabilities:  
                  drop:  
                    - ALL  
                privileged: false  
                readOnlyRootFilesystem: true  
          {{- end }}  
criteria:  
  - {}

您的集群和工作负载现在默认就是安全的,并会在您的 GKE 安全态势仪表板中自动监控。

GKE 安全状况面板

作为平台工程师,我希望能为我的开发人员配置所需的数据库依赖。

当一个工作负载即将部署,并需要数据库支持时,该如何处理呢?作为平台工程师来说,这又是一个我可以提供的支持方案和最佳实践。

在 Humanitec Orchestrator 中可以这样简化配置集群内的 Redis 数据库:如下所示:

apiVersion: entity.humanitec.io/v1b1  
kind: Definition  
metadata:  
  id: redis-in-cluster  
entity:  
  name: redis-in-cluster  
  type: redis  
  driver_type: humanitec/template  
  driver_inputs:  
    values:  
      templates:  
        init: |-  
          name: redis  
          port: 6379  
          username: ""  
          password: ""  
        manifests: |-  
          deployment.yaml:  
            location: namespace  
            data:  
              apiVersion: apps/v1  
              kind: Deployment  
              metadata:  
                name: {{ .init.name }}  
              spec:  
                selector:  
                  matchLabels:  
                    app: {{ .init.name }}  
                template:  
                  metadata:  
                    labels:  
                      app: {{ .init.name }}  
                  spec:  
                    containers:  
                    - name: {{ .init.name }}  
                      image: redis:alpine  
                      ports:  
                      - containerPort: {{ .init.port }}  
          service.yaml:  
            location: namespace  
            data:  
              apiVersion: v1  
              kind: Service  
              metadata:  
                name: {{ .init.name }}  
              spec:  
                type: ClusterIP  
                selector:  
                  app: {{ .init.name }}  
                ports:  
                - name: tcp-redis  
                  port: {{ .init.port }}  
                  targetPort: {{ .init.port }}  
      outputs: |  
        host: {{ .init.name }}  
        port: {{ .init.port }}  
      secrets: |  
        username: {{ .init.username }}  
        password: {{ .init.password }}  
criteria:  
  - {}

在这种情况下,我们使用 humanitec/template 来部署任何 Kubernetes 资源清单。它是你可以用来定义资源的 Humanitec 驱动类型之一。你可以用它来与云提供商互动的另一个驱动类型是 humanitec/terraform。下面是一个简化版本,如果你想要部署一个 Google Cloud Memorystore(Redis)数据库,你可以这样定义:

apiVersion: entity.humanitec.io/v1b1  
kind: Definition  
metadata:  
  id: redis-memorystore  
entity:  
  driver_type: humanitec/terraform  
  name: redis-memorystore  
  type: redis  
  driver_inputs:  
    values:  
      script: |-  
        terraform {  
            required_providers {  
                google = {  
                    source  = "hashicorp/google"  
                }  
            }  
        }  
        provider "google" {  
        }  
        resource "google_redis_instance" "memorystore" {  
            name                = "redis-cart"  
            memory_size_gb      = 1  
            redis_version       = "REDIS_7_0"  
            region              = "REGION"  
            auth_enabled        = true  
            # 认证启用 (zhèngmíng qǐyòng)  
        }  
        output "host" {  
            value = google_redis_instance.memorystore.host  
        }  
        output "port" {  
            value = google_redis_instance.memorystore.port  
        }  
        output "username" {  
            value     = ""  
            sensitive = true  
        }  
        output "password" {  
            value     = google_redis_instance.memorystore.auth_string  
            sensitive = true  
        }  
criteria:  
  - {}  

在这个示例中,Memorystore (Redis) 实例与 GKE 集群位于同一网络中,并通过密码进行认证访问。

在这个阶段再次,当一个新的工作负载部署发生时,它将无缝地采用新的 redis 实现,开发人员无需做任何改动。

搞定了!

在您自己的平台及其工具之上,我们看到您如何能够提高开发者的效率,同时通过提升内部开发者平台(IDP)的稳定性和安全性来改进其抽象层次。这由三个关键组件构成:工作负载定义、编排器工具和门户。

3个关键支柱,以确保您成功实施和采用IDP。

但平台工程不仅仅关乎工具。你需要踏上平台即产品的旅程。这将确保你的开发人员感到被听到,他们会使用你的平台因为它确实帮助了他们,你简化了他们的日常工作,你为他们提供了具体的投资回报率(ROI),你提供了一系列受支持的最佳实践路径等。

平台工程不仅仅是关于工具这点,尽早开启将平台视为产品的旅程也非常重要。

资源
  • 请求免费试用 (humanitec.com)
  • 获取 GoogleCloudPlatform/microservices-demo 用于 Score 和 Humanitec
  • Score 在巴黎 KubeCon EU 上的展示
  • 在巴黎举行的 2024 年 KubeCon 的平台工程
  • 在您的平台中让本地和远程环境无缝对接
  • 参考架构
  • 资源包
  • [PlatformCon 24] 平台编排器:内部开发者平台中的缺失组件

快乐跳跃着,快乐航行着,干杯!



这篇关于平台工程实战:用Score和Humanitec部署线上精品店示例应用的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程