使用 GitHub 在开发周期的早期估算 GKE 费用


本教程介绍了使用 GitLab 将 Google Kubernetes Engine (GKE) 费用可见性转移到开发团队的最佳做法。在开发过程的早期阶段建立费用意识有助于您避免在 Google Cloud 账单中发生意外。将任务或信息移动到流程的较早阶段有时被称为左移

本教程适用于希望优化 GKE 集群中的费用并在生产环境中使用 GitLab 的开发者、运维人员和 FinOps 从业人员。如果改为使用 GitHub,请参阅使用 GitHub 在开发周期的早期阶段估算 GKE 费用

本教程假定您熟悉以下技术:

概览

许多采用公有云的团队都不习惯随用随付结算模式。他们通常并不完全了解其应用的运行环境(在本例中为 GKE)。FinOps 运营模式促进了这种财务责任文化。FinOps 最佳做法是向团队提供有关支出的实时信息,以便在出现费用问题时加以解决。

本文档介绍如何更进一步,在费用结算之前加以评估。估算费用的最佳时机是在开发过程早期和代码审核期间。这样,从业人员就能够在新增功能和 bug 修复的费用出现问题之前,了解和讨论替代方案。下图总结了此类做法。

尽早估算费用的最佳做法。

如图所示,开发者可以估算其本地环境中的 GKE 费用,最好是在构建时。通过这次估算,他们可以充分了解每月生产工作负载的费用。当功能或问题修复完成代码时,他们可以提议触发 GitLab CI/CD 流水线合并请求查看新旧费用之间的差异。如果增加量超过预定义的阈值,流水线会自动请求新的代码审核。这种做法可帮助开发者更好地了解其工作负载容量,并主动修复应用问题,而不是每次在生产环境中发现不稳定性问题时就添加更多资源。

目标

  • 构建并推送 Kubernetes cost estimator 映像。
  • 创建一个新的 GitLab 项目。
  • 将 GitLab 运行程序配置为在 GKE 集群上运行。
  • 将示例代码推送到 GitHub 代码库。
  • 更改代码并提出和平请求以查看实际费用估算。

费用

在本文档中,您将使用 Google Cloud 的以下收费组件:

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

完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 在 Google Cloud Console 中,转到项目选择器页面。

    转到“项目选择器”

  2. 选择或创建 Google Cloud 项目。

  3. 确保您的 Google Cloud 项目已启用结算功能

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

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。

准备环境

  1. 在 Cloud Shell 中,克隆 gke-shift-left-cost GitHub 代码库:

    git clone https://github.com/GoogleCloudPlatform/gke-shift-left-cost
    cd gke-shift-left-cost
    

    此代码库中的代码构建到以下文件夹中:

    • 根文件夹:包含用于构建 cost estimator 映像的 Dockerfile 文件,以及用于实现 cost estimator 命令行逻辑的 main.go 文件。
    • api/:包含用于操控 Kubernetes 对象和估算费用的 Go API。
    • samples/:包含 Kubernetes 清单示例,以便您可以先尝试执行相关过程,然后再在您的组织中实现。
  2. 设置您的 Google Cloud 项目 ID 以及 GitLab 用户账号和电子邮件地址:

    export GCP_PROJECT_ID=YOUR_PROJECT_ID
    export GITLAB_USER=YOUR_GITLAB_USER
    export GITLAB_EMAIL=YOUR_GITLAB_EMAIL_ADDRESS
    
    gcloud config set project $GCP_PROJECT_ID
    
    gcloud services enable cloudbilling.googleapis.com \
        compute.googleapis.com \
        container.googleapis.com \
        iamcredentials.googleapis.com \
        artifactregistry.googleapis.com
    
    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-f
    

    请替换以下内容:

  • YOUR_PROJECT_ID :您在本教程中使用的项目的 Google Cloud 项目 ID。
  • YOUR_GITLAB_USER :您用于登录 GitLab 账号的用户账号。
  • YOUR_GITLAB_EMAIL_ADDRESS:您在 GitHub 账号中使用的电子邮件。

在本教程中,您可以选择使用其他区域和可用区

构建并推送 Kubernetes cost-estimator 映像

本教程随附的 Kubernetes cost estimator 工具只是一个可以执行的操作的示例。该工具具有估算 DaemonSet、Deployment、StatefulSet、ReplicaSet、HorizontalPodAutoScaler 和 PersistentVolumeClaim Kubernetes 对象费用的功能。您还可以实现自己的 cost-estimation 工具,或者提出有关所需改进的拉取请求。

  1. 在 Cloud Shell 中,允许 application-default 使用您的凭据:

    gcloud auth application-default login
    
  2. 构建 Kubernetes cost estimator 二进制文件:

    mkdir ./bin
    go test ./api
    go build -v -o ./bin/k8s-cost-estimator .
    
  3. 通过在示例文件夹中执行费用估算来测试二进制文件:

    ./bin/k8s-cost-estimator \
        --k8s ./samples/k8s-cost-estimator-local/app-v1  \
        --config ./samples/k8s-cost-estimator-local/example-conf.yaml \
        --v trace
    

    在输出中,您会看到一个 Markdown 表,其中详细说明了 ./samples/k8s-cost-estimator-local/app-v1/ 文件夹的每月估算费用。为了更好地了解应用的每月生产费用,开发者可以在将代码推送到远程代码库之前运行此步骤。

    INFO[0000] Starting cost estimation (version v0.0.1)...
    ...
    
    |         KIND          | MIN REQUESTED (USD) | MIN REQ + HPA CPU BUFFER (USD) | MAX REQUESTED (USD) | MIN LIMITED (USD) | MAX LIMITED (USD) |
    |-----------------------|---------------------|--------------------------------|---------------------|-------------------|-------------------|
    | Deployment            |             $133.31 |                        $198.71 |             $266.54 |           $312.83 |           $579.29 |
    | StatefulSet           |              $36.33 |                         $36.33 |              $36.33 |            $72.67 |            $72.67 |
    | DaemonSet             |              $29.68 |                         $29.68 |              $29.68 |            $53.19 |            $53.19 |
    | PersistentVolumeClaim |              $28.88 |                         $28.88 |              $28.88 |            $33.68 |            $33.68 |
    | **TOTAL**             |         **$228.20** |                    **$293.60** |         **$361.43** |       **$472.38** |       **$738.83** |
    
    INFO[0002] Finished cost estimation!
    
  4. 构建 Kubernetes cost estimator 容器映像:

    docker build . -t \
    us-central1-docker.pkg.dev/$GCP_PROJECT_ID/docker-repo/k8s-cost-estimator:v0.0.1
    
  5. 创建 Artifact Registry Docker 代码库以存储映像:

    gcloud artifacts repositories create docker-repo \
            --repository-format=docker \
            --location=us-central1 \
            --description="Docker repository"
    
  6. gcloud 注册为 Docker 配置文件的凭据帮助程序。

    gcloud auth configure-docker us-central1-docker.pkg.dev
    

    如果出现提示,请确认文件更新。

  7. 将该映像推送到 Artifact Registry。

    docker push us-central1-docker.pkg.dev/$GCP_PROJECT_ID/docker-repo/k8s-cost-estimator:v0.0.1
    

创建一个新的 GitLab 项目

  1. 在 Cloud Shell 中,将目录更改为 GitLab 示例:

    cd samples/k8s-cost-estimator-gitlab
    
  2. 在 GitLab 个人访问令牌页面上,创建一个访问令牌:

    转到 GitHub 个人访问令牌页面

    1. 名称字段中,输入您要创建的令牌的名称。
    2. 范围字段中,选择 api,然后点击创建个人访问令牌
    3. 复制您的新个人访问令牌值。
  3. 在 Cloud Shell 中,将您的个人访问令牌保存在变量中。

    GITLAB_API_TOKEN=YOUR_NEW_PERSONAL_ACCESS_TOKEN
    

    YOUR_NEW_PERSONAL_ACCESS_TOKEN 替换为您创建的 GitLab 个人访问令牌。

  4. 创建一个新的 GitLab 项目:

    GITLAB_PROJECT_OUTPUT=$(curl -X POST -H "content-type:application/json" -H "PRIVATE-TOKEN:$GITLAB_API_TOKEN" -d '{"name":"k8s-cost-estimator-gitlab","visibility":"public"}'        https://gitlab.com/api/v4/projects)
    GITLAB_PROJECT_ID=$(echo $GITLAB_PROJECT_OUTPUT | jq ".id")
    GITLAB_FINOPS_REVIEWER_ID=$(echo $GITLAB_PROJECT_OUTPUT | jq ".owner.id")
    
  5. 设置费用估算器工具的变量,以便在创建合并请求时使用:

    curl -X POST -H "content-type:application/json" -H "PRIVATE-TOKEN:$GITLAB_API_TOKEN" -d "{\"key\": \"GITLAB_API_TOKEN\",\"value\": \"$GITLAB_API_TOKEN\", \"masked\":\"true\"}" https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/variables
    
    curl -X POST -H "content-type:application/json" -H "PRIVATE-TOKEN:$GITLAB_API_TOKEN" -d "{\"key\": \"GITLAB_FINOPS_REVIEWER_ID\",\"value\": \"$GITLAB_FINOPS_REVIEWER_ID\"}" https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/variables
    
    curl -X POST -H "content-type:application/json" -H "PRIVATE-TOKEN:$GITLAB_API_TOKEN" -d "{\"key\": \"GITLAB_FINOPS_COST_USD_THRESHOLD\",\"value\": \"10\"}" https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/variables
    
  6. 确认您的项目和变量是否已创建:

    curl -s --header "PRIVATE-TOKEN:$GITLAB_API_TOKEN" \
    https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/variables | jq
    

    输出类似于以下内容:

    [
        {
        "variable_type": "env_var",
        "key": "GITLAB_API_TOKEN",
        "value": "Ex...n1",
        "protected": false,
        "masked": true,
        "environment_scope": "*"
        },
        {
        "variable_type": "env_var",
        "key": "GITLAB_FINOPS_REVIEWER_ID",
        "value": "88..87",
        "protected": false,
        "masked": false,
        "environment_scope": "*"
        },
        {
        "variable_type": "env_var",
        "key": "GITLAB_FINOPS_COST_USD_THRESHOLD",
        "value": "10",
        "protected": false,
        "masked": false,
        "environment_scope": "*"
        }
    ]
    

    GitLab 项目中配置的变量供 ./samples/k8s-cost-estimator-gitlab/templates/.gitlab-ci.yml.tpl 文件更新合并请求,如下所示:

    • GITLAB_API_TOKEN:您的 GitLab 个人访问令牌。
    • GITLAB_FINOPS_REVIEWER_ID:每当费用超过给定阈值时,需要代码审核者。简单起见,本教程将您自己的用户 ID 设置为审核者。但是,在生产环境中,我们建议您配置团队而不是个人。
    • GITLAB_FINOPS_COST_USD_THRESHOLD:以美元为单位的阈值,在本例中为 10 美元。当新旧费用之间的差值超过此阈值时,会强制执行特别批准。您还可以为其他值设置阈值。要探索此功能,您可以在构建和推送 Kubernetes cost estimator 映像中执行 ./bin/k8s-cost-estimator 命令时附加 --output 参数。此参数生成一个扩展名为 .diff 的文件,您可通过该扩展名查看可用选项。

将 GitLab 运行程序配置为在 GKE 集群上运行

在本部分中,您将在使用 Workload Identity 在 GKE 集群中安装 GitLab 运行程序,以允许 Kubernetes Estimator 工具查询 Google Cloud 价格目录。Estimator 使用总价,且不考虑抢占式虚拟机或任何折扣。

  1. 在 Cloud Shell 中创建一个 GKE 集群:

    gcloud beta container clusters create gitlab-runners \
        --enable-ip-alias \
        --release-channel=stable \
        --workload-pool=$GCP_PROJECT_ID.svc.id.goog \
        --enable-autoprovisioning --min-cpu 1 --min-memory 1 --max-cpu 4 --max-memory 16 \
        --autoscaling-profile=optimize-utilization \
        --preemptible
    
  2. 从您创建的项目中获取 GitLab 运行程序注册令牌:

    export GITLAB_RUNNER_TOKEN=$(curl -s --header "PRIVATE-TOKEN:$GITLAB_API_TOKEN" https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID | jq -r '.runners_token')
    [ -z "$GITLAB_RUNNER_TOKEN" ] && echo "GITLAB_RUNNER_TOKEN is not exported" || echo "GITLAB_RUNNER_TOKEN is $GITLAB_RUNNER_TOKEN"
    
  3. 在您的 GKE 集群中安装 GitLab 运行程序:

    kubectl create namespace gitlab
    
    helm repo add gitlab https://charts.gitlab.io
    
    sed "s/GCP_PROJECT_ID/$GCP_PROJECT_ID/g; s/GITLAB_RUNNER_TOKEN/$GITLAB_RUNNER_TOKEN/g" templates/gitlab-runner-values.yaml.tpl > gitlab-runner-values.yaml
    
    helm install --namespace gitlab --version 0.24.0 gitlab-runner -f gitlab-runner-values.yaml gitlab/gitlab-runner
    
    kubectl -n gitlab wait --for=condition=available deployment gitlab-runner --timeout=5m
    
    gcloud iam service-accounts create gitlab-runner --display-name=gitlab-runner
    gcloud iam service-accounts add-iam-policy-binding \
        --role roles/iam.workloadIdentityUser \
        --member "serviceAccount:$GCP_PROJECT_ID.svc.id.goog[gitlab/gitlab-runner]" \
        gitlab-runner@$GCP_PROJECT_ID.iam.gserviceaccount.com
    
  4. 在 GitLab 项目中停用共享运行程序

    curl -s --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" -X PUT "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID" --form "shared_runners_enabled=false"
    
  5. 验证您部署的运行程序是否已在 GitLab 项目中启用:

    curl -s --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" "https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID/runners?status=active" | jq '.[] | select(.is_shared==false)'
    

    输出类似于以下内容:

    {
        "id": 49345561,
        "description": "gitlab-runner-gitlab-runner-788459d488-jlscn",
        "ip_address": "35.178.223.199",
        "active": true,
        "is_shared": false,
        "name": "gitlab-runner",
        "online": true,
        "status": "online"
    }
    

将示例代码推送到 GitLab 代码库

  1. 创建 SSH 密钥对,以将示例代码推送到 GitLab 代码库:

    mkdir -p ssh && cd ssh
    ssh-keygen -t rsa -b 4096 -N '' -f gitlab-key
    eval `ssh-agent` && ssh-add $(pwd)/gitlab-key
    curl -s --request POST --header "PRIVATE-TOKEN:$GITLAB_API_TOKEN" https://gitlab.com/api/v4/user/keys --form "title=k8s-cost-estimator-key" --form "key=$(cat gitlab-key.pub)"
    cd ..
    
  2. 将内容推送到新的 GitHub 代码库:

    sed "s/GCP_PROJECT_ID/$GCP_PROJECT_ID/g; s/GITLAB_USER/$GITLAB_USER/g; s/GITLAB_EMAIL/$GITLAB_EMAIL/g;" templates/.gitlab-ci.yml.tpl > .gitlab-ci.yml
    
    GITLAB_SSH_URL_REPO=$(curl -s --header "PRIVATE-TOKEN:$GITLAB_API_TOKEN" https://gitlab.com/api/v4/users/$GITLAB_FINOPS_REVIEWER_ID/projects | jq '.[] | select(.name=="k8s-cost-estimator-gitlab")' | jq -r '.ssh_url_to_repo')
    [ -z "$GITLAB_SSH_URL_REPO" ] && echo "GITLAB_PROJECT_SSH_URL is not exported" || echo "GITLAB_PROJECT_SSH_URL is $GITLAB_SSH_URL_REPO"
    
    git config --global user.email $GITLAB_EMAIL
    git config --global user.name $GITLAB_USER
    git init
    git remote add origin $GITLAB_SSH_URL_REPO
    git add -A .
    git commit -m "Initial commit"
    git checkout -b main
    git push -u origin main
    

更改代码并提出和平请求以查看实际费用估算

  1. 在 Cloud Shell 中,获取 GitLab Web 集成开发环境 (IDE) 的网址:

    echo "https://gitlab.com/-/ide/project/$GITLAB_USER/k8s-cost-estimator-gitlab/tree/main/-/wordpress/wordpress_hpa.yaml"
    
  2. Ctrl + 点击(在 macOS 上按 Cmd + 点击)输出网址可导航到 GitLab Web IDE。

  3. 在 GitLab Web IDE 中,按如下方式修改 ./wordpress/wordpress_hpa.yaml 文件:

    1. minReplicas 值从 2 更改为 5
    2. 点击提交
  4. 如以下屏幕截图所示,选择创建新分支发起新的合并请求,然后点击提交

    启动合并请求。

  5. 新合并请求屏幕上,点击页面底部的创建合并请求

    除了创建新的合并请求之外,此步骤还会根据 .gitlab-ci.yml 文件触发费用估算流水线。此流水线使用您在上一部分中创建的容器映像。此流水线还确定何时需要 FinOps 批准。简单起见,.gitlab-ci.yml 为每个合并请求库添加批准,但您可以定义和重复使用在 GitLab 项目级层定义的批准规则

  6. 等待流水线完成,大约需要一分钟时间。完成后,系统会在合并请求中添加一条包含费用详情的注释。由于您建议的代码费用增加超过阈值 $10,因此需要 FinOps 审核者。

    输出类似于以下内容:

    合并请求中的费用详情注释。

    在本教程中,默认配置用于合并请求批准。您可以为 Gitlab 中的合并请求选择不同的配置。例如,要防止作者批准合并请求,您可以前往设置 >常规设置 >合并请求 (MR) 审批 >审批设置

清理

为避免系统因本教程中使用的资源向您的 Google Cloud 账号收取费用,您可以删除您的项目。

删除项目

  1. 在 Google Cloud 控制台中,进入管理资源页面。

    转到“管理资源”

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

删除 GitLab 项目

如果您不想保留 GitLab 项目,请执行以下操作:

  1. 在 Cloud Shell 中,删除 GitLab 项目:

     curl -X DELETE -H "content-type:application/json" -H "PRIVATE-TOKEN:$GITLAB_API_TOKEN" https://gitlab.com/api/v4/projects/$GITLAB_PROJECT_ID
    ```
    
     The output is similar to the following:
    
     ```none {:.devsite-disable-click-to-copy}
     {"message":"202 Accepted"}
    

    如果与 Cloud Shell 的连接中断,您必须再次设置以下变量:

    • GITLAB_API_TOKEN
    • GITLAB_PROJECT_ID

后续步骤