本教程介绍了使用 GitLab 将 Google Kubernetes Engine (GKE) 费用可见性转移到开发团队的最佳做法。在开发过程的早期阶段建立费用意识有助于您避免在 Google Cloud 账单中发生意外。将任务或信息移动到流程的较早阶段有时被称为左移。
本教程适用于希望优化 GKE 集群中的费用并在生产环境中使用 GitLab 的开发者、运维人员和 FinOps 从业人员。如果改为使用 GitHub,请参阅使用 GitHub 在开发周期的早期阶段估算 GKE 费用。
本教程假定您熟悉以下技术:
- Docker
- GitLab
- Kubernetes
- Cloud Build
- GKE
- Linux
概览
许多采用公有云的团队都不习惯随用随付结算模式。他们通常并不完全了解其应用的运行环境(在本例中为 GKE)。FinOps 运营模式促进了这种财务责任文化。FinOps 最佳做法是向团队提供有关支出的实时信息,以便在出现费用问题时加以解决。
本文档介绍如何更进一步,在费用结算之前加以评估。估算费用的最佳时机是在开发过程早期和代码审核期间。这样,从业人员就能够在新增功能和 bug 修复的费用出现问题之前,了解和讨论替代方案。下图总结了此类做法。
如图所示,开发者可以估算其本地环境中的 GKE 费用,最好是在构建时。通过这次估算,他们可以充分了解每月生产工作负载的费用。当功能或问题修复完成代码时,他们可以提议触发 GitLab CI/CD 流水线的合并请求查看新旧费用之间的差异。如果增加量超过预定义的阈值,流水线会自动请求新的代码审核。这种做法可帮助开发者更好地了解其工作负载容量,并主动修复应用问题,而不是每次在生产环境中发现不稳定性问题时就添加更多资源。
目标
- 构建并推送 Kubernetes cost estimator 映像。
- 创建一个新的 GitLab 项目。
- 将 GitLab 运行程序配置为在 GKE 集群上运行。
- 将示例代码推送到 GitHub 代码库。
- 更改代码并提出和平请求以查看实际费用估算。
费用
在本文档中,您将使用 Google Cloud 的以下收费组件:
您可使用价格计算器根据您的预计使用情况来估算费用。
完成本文档中描述的任务后,您可以通过删除所创建的资源来避免继续计费。如需了解详情,请参阅清理。
准备工作
-
在 Google Cloud Console 中,转到项目选择器页面。
-
选择或创建 Google Cloud 项目。
-
在 Google Cloud 控制台中,激活 Cloud Shell。
Cloud Shell 会话随即会在 Google Cloud 控制台的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Google Cloud CLI 且已为当前项目设置值的 Shell 环境。该会话可能需要几秒钟时间来完成初始化。
准备环境
在 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 清单示例,以便您可以先尝试执行相关过程,然后再在您的组织中实现。
- 根文件夹:包含用于构建 cost estimator 映像的 Dockerfile 文件,以及用于实现 cost estimator 命令行逻辑的
设置您的 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 工具,或者提出有关所需改进的拉取请求。
在 Cloud Shell 中,允许
application-default
使用您的凭据:gcloud auth application-default login
构建 Kubernetes cost estimator 二进制文件:
mkdir ./bin go test ./api go build -v -o ./bin/k8s-cost-estimator .
通过在示例文件夹中执行费用估算来测试二进制文件:
./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!
构建 Kubernetes cost estimator 容器映像:
docker build . -t \ us-central1-docker.pkg.dev/$GCP_PROJECT_ID/docker-repo/k8s-cost-estimator:v0.0.1
创建 Artifact Registry Docker 代码库以存储映像:
gcloud artifacts repositories create docker-repo \ --repository-format=docker \ --location=us-central1 \ --description="Docker repository"
将
gcloud
注册为 Docker 配置文件的凭据帮助程序。gcloud auth configure-docker us-central1-docker.pkg.dev
如果出现提示,请确认文件更新。
将该映像推送到 Artifact Registry。
docker push us-central1-docker.pkg.dev/$GCP_PROJECT_ID/docker-repo/k8s-cost-estimator:v0.0.1
创建一个新的 GitLab 项目
在 Cloud Shell 中,将目录更改为 GitLab 示例:
cd samples/k8s-cost-estimator-gitlab
在 GitLab 个人访问令牌页面上,创建一个访问令牌:
- 在名称字段中,输入您要创建的令牌的名称。
- 在范围字段中,选择 api,然后点击创建个人访问令牌。
- 复制您的新个人访问令牌值。
在 Cloud Shell 中,将您的个人访问令牌保存在变量中。
GITLAB_API_TOKEN=YOUR_NEW_PERSONAL_ACCESS_TOKEN
将
YOUR_NEW_PERSONAL_ACCESS_TOKEN
替换为您创建的 GitLab 个人访问令牌。创建一个新的 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")
设置费用估算器工具的变量,以便在创建合并请求时使用:
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
确认您的项目和变量是否已创建:
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 使用总价,且不考虑抢占式虚拟机或任何折扣。
在 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
从您创建的项目中获取 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"
在您的 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
在 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"
验证您部署的运行程序是否已在 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 代码库
创建 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 ..
将内容推送到新的 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
更改代码并提出和平请求以查看实际费用估算
在 Cloud Shell 中,获取 GitLab Web 集成开发环境 (IDE) 的网址:
echo "https://gitlab.com/-/ide/project/$GITLAB_USER/k8s-cost-estimator-gitlab/tree/main/-/wordpress/wordpress_hpa.yaml"
Ctrl + 点击(在 macOS 上按 Cmd + 点击)输出网址可导航到 GitLab Web IDE。
在 GitLab Web IDE 中,按如下方式修改
./wordpress/wordpress_hpa.yaml
文件:- 将
minReplicas
值从2
更改为5
。 - 点击提交。
- 将
如以下屏幕截图所示,选择创建新分支和发起新的合并请求,然后点击提交。
在新合并请求屏幕上,点击页面底部的创建合并请求。
除了创建新的合并请求之外,此步骤还会根据
.gitlab-ci.yml
文件触发费用估算流水线。此流水线使用您在上一部分中创建的容器映像。此流水线还确定何时需要 FinOps 批准。简单起见,.gitlab-ci.yml
为每个合并请求库添加批准,但您可以定义和重复使用在 GitLab 项目级层定义的批准规则。等待流水线完成,大约需要一分钟时间。完成后,系统会在合并请求中添加一条包含费用详情的注释。由于您建议的代码费用增加超过阈值 $10,因此需要 FinOps 审核者。
输出类似于以下内容:
在本教程中,默认配置用于合并请求批准。您可以为 Gitlab 中的合并请求选择不同的配置。例如,要防止作者批准合并请求,您可以前往设置 >常规设置 >合并请求 (MR) 审批 >审批设置。
清理
为避免系统因本教程中使用的资源向您的 Google Cloud 账号收取费用,您可以删除您的项目。
删除项目
- 在 Google Cloud 控制台中,进入管理资源页面。
- 在项目列表中,选择要删除的项目,然后点击删除。
- 在对话框中输入项目 ID,然后点击关闭以删除项目。
删除 GitLab 项目
如果您不想保留 GitLab 项目,请执行以下操作:
在 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
后续步骤
- 如需详细了解 GKE 费用优化,请参阅在 GKE 上运行费用经过优化的 Kubernetes 应用的最佳做法。
- 了解如何使用 GitHub 在开发周期早期估算 GKE 费用。
- 在 Google Cloud 架构框架:费用优化中查找优化 Google Cloud 工作负载费用的设计建议和最佳实践。
- 探索有关 Google Cloud 的参考架构、图表和最佳做法。查看我们的云架构中心。