如何通过 Google Cloud Build 中的步骤设置环境或替换变量?

Posted

技术标签:

【中文标题】如何通过 Google Cloud Build 中的步骤设置环境或替换变量?【英文标题】:How do I set an environment or substitution variable via a step in Google Cloud Build? 【发布时间】:2019-02-19 14:42:26 【问题描述】:

基本上,在使用 Google Cloud Build 时,如何在后续步骤中读取在早期构建步骤中写入的值?

具体来说,我想制作一个基于时间戳和 $SHORT_SHA 组合的自定义图像标签。像下面这样的东西。虽然,它不起作用,因为 docker 抱怨“导出”,而且,即使它起作用了,它也可能是一个不同的环境:

  # Setting tag in a variable:
  - name: 'ubuntu'
    args: ['export', '_BUILD_TAG=`date', '-u', '+%Y%m%dT%H%M%S_$SHORT_SHA`']

然后,在后面的步骤中:

  # Using tag from the variable:
  - name: gcr.io/cloud-builders/docker
    args: ['build', '-t', 'gcr.io/$PROJECT_ID/$_BUILD_TAG', '.']

那么,如何在另一个步骤中使用一个步骤的输出?我可以将date 的内容写入文件,然后读取它,但我又回到不知道如何从我读取的文件中设置变量(或以其他方式插入其结果以形成 docker build 的参数) .

【问题讨论】:

【参考方案1】:
- name: gcr.io/cloud-builders/docker
  entrypoint: sh
  args
    - '-c'
    - 'docker build -t gcr.io/$PROJECT_ID/$(date -u +%Y%m%dT%H%M%S_$SHORT_SHA) .'

【讨论】:

谢谢。这解决了部分问题。剩下的问题是,因为我想在其他几个步骤中重复使用这个标签,所以我也需要它在那里可用。如果时间在步骤之间流逝,我不能每次都使用相同的 shell 调用,因为标签值会改变。所以,我需要尽早生成它,然后在其他步骤中引用它。对此有何建议? (1) 从版本号中删除 T%H%M%S 部分(推荐),或 (2) 将版本号保存到文件中,以便在其他步骤中重复使用:echo $(date -u +%Y%m%dT%H%M%S_$SHORT_SHA) > ./VERSION 好的,谢谢!在此评论之前,我实际上最终使用了后一种方法,并在后续步骤中结合在子外壳中读取该文件。请参阅下面的答案。【参考方案2】:

我从来没有找到在一个构建步骤中设置可以在其他步骤中读取的环境变量的方法,但我最终通过以下方式构建康斯坦丁的答案来实现相同的效果:

在早期步骤中,我生成基于​​日期的标签并将其写入文件。文件系统(/workspace)在步骤之间保留,并用作我的环境变量的存储。然后,在我需要引用该值的每个步骤中,我都会将该文件放在适当的位置。诀窍是使用 sh 或 bash 作为每个容器中的入口点,以便从文件中读取的子 shell 可以执行。

这是一个例子:

## Set build tag and write to file _TAG
- name: 'ubuntu'
  args: ['bash', '-c', 'date -u +%Y%m%dT%H%M_$SHORT_SHA > _TAG']

...

# Using the _TAG during Docker build:
- name: gcr.io/cloud-builders/docker
entrypoint: sh
args: ['-c', 'docker build -t gcr.io/$PROJECT_ID/image_name:$(cat _TAG) .']

需要注意的一点是,如果您在 JSON 对象或需要双引号的东西中以这种方式进行 bash 插值,则在容器中执行时,您需要 subshel​​l 调用永远不会被单引号包围, 只有 double,这可能需要转义内部双引号来构建 JSON 对象。这是一个示例,我使用 _TAG 文件值修补 kubernetes 配置以部署新构建的映像:

- name: gcr.io/cloud-builders/kubectl
entrypoint: bash
args: ['-c', 'gcloud container clusters get-credentials --zone $$CLOUDSDK_COMPUTE_ZONE $$CLOUDSDK_CONTAINER_CLUSTER ; kubectl patch deployment deployment_name -n mynamespace -p "\"spec\":\"template\":\"spec\":\"containers\":[\"name\":\"image_name\",\"image\":\"gcr.io/$PROJECT_ID/image_name:$(cat _TAG)\"]"']
env:
- 'CLOUDSDK_COMPUTE_ZONE=us-central1-b'
- 'CLOUDSDK_CONTAINER_CLUSTER=my-google-proj-cluster-name'

【讨论】:

谢谢@JJC。如果您添加一个“/”(根目录),它适用于所有目录。示例:echo "staging" > /_ENV && cd some_dir && echo "The value of \"_ENV\" is $(cat /_ENV)."。请记住始终用引号将"$(cat /_ENV)" 括起来(例如mv /workspace/vars.auto.tfvars "environment/env/$(cat /_ENV)/vars.auto.tfvars"【参考方案3】:

虽然这不能解决您的问题,但我确实想发布此答案,因为您问题的第一句话是“基本上,在使用 Google Cloud Build 时,我如何读取写入的值后续步骤中的较早构建步骤?”。你就是这样做的。

来自the official documentation:

Volume 是一个 Docker 容器,它安装在构建步骤中以跨构建步骤持久保存文件。当 Cloud Build 运行构建步骤时,它会自动将工作区卷挂载到 /workspace。您可以使用步骤的 volumes 字段指定要挂载到构建步骤的容器中的其他卷。

这是一个由某人who asked this question in a github issue 实现的示例,但将日期放入卷中以供稍后阅读:

steps:
- name: 'ubuntu'
  volumes:
  - name: 'vol1'
    path: '/persistent_volume'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
        date -u +%Y%m%dT%H%M_$SHORT_SHA > /persistent_volume/file
- name: 'gcr.io/cloud-builders/docker'
  volumes:
  - name: 'vol1'
    path: '/persistent_volume'
  args: ['run', '-v', 'vol1:/data', 'alpine', 'cat', 'data/file']

但是,对于您的特殊情况,我将使用子shell 命令对其进行标记,就像它已完成 in this answer here:

$(date -u +%Y%m%dT%H%M%S_$SHORT_SHA)

【讨论】:

谢谢。但是,如果您阅读了上面已经给出的答案,您会发现我基本上就是在现有的持久环境/磁盘中这样做,而无需在每个步骤上安装额外的卷。有没有我错过的优势? 对于您的特定情况,没有任何优势。包括所有可以内联的开销可能是一个净负面因素。但是,如果您稍后有人想要这样做,但使用 ssh-keygen 或具有可观输出的东西,则使用卷在步骤之间共享数据可能会清理干净。【参考方案4】:

您不需要导出或安装卷。

steps:
- name: 'ubuntu'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
        printenv

- name: gcr.io/cloud-builders/docker
  entrypoint: 'bash'
  args:
  - '-c'
  - |
        printenv

会输出

BUILD
Starting Step #0
Step #0: Pulling image: ubuntu
Step #0: Using default tag: latest
Step #0: latest: Pulling from library/ubuntu
Step #0: Digest: sha256:eb70667a801686f914408558660da753cde27192cd036148e58258819b927395
Step #0: Status: Downloaded newer image for ubuntu:latest
Step #0: HOSTNAME=XXXXXXXXXXX
Step #0: BUILDER_OUTPUT=/builder/outputs
Step #0: PWD=/workspace
Step #0: HOME=/builder/home
Step #0: SHLVL=1
Step #0: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Step #0: _=/usr/bin/printenv
Finished Step #0
Starting Step #1
Step #1: Already have image (with digest): gcr.io/cloud-builders/docker
Step #1: HOSTNAME=XXXXXXXXXXX
Step #1: PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Step #1: PWD=/workspace
Step #1: SHLVL=1
Step #1: HOME=/builder/home
Step #1: DEBIAN_FRONTEND=noninteractive
Step #1: BUILDER_OUTPUT=/builder/outputs
Step #1: _=/usr/bin/printenv
Finished Step #1

所以你可以使用/workspace/builder/home,但由于我们不能在yaml 文件中使用defined substitution 以外的变量,所以将它们作为脚本放入repo 中,如下所示:

steps:
- name: 'ubuntu'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
        bash test.bash

- name: gcr.io/cloud-builders/docker
  entrypoint: 'bash'
  args:
  - '-c'
  - |
        bash result.bash

test.bash

#!/bin/bash
SHORT_SHA=myvar
date -u +%Y%m%dT%H%M_$SHORT_SHA > /workspace/myfile.txt

结果.bash

#!/bin/bash
_BUILD_TAG=`cat /workspace/myfile.txt`
echo "the transferred value is: $_BUILD_TAG"

输出:

BUILD
Starting Step #0
Step #0: Pulling image: ubuntu
Step #0: Using default tag: latest
Step #0: latest: Pulling from library/ubuntu
Step #0: Digest: sha256:eb70667a801686f914408558660da753cde27192cd036148e58258819b927395
Step #0: Status: Downloaded newer image for ubuntu:latest
Finished Step #0
Starting Step #1
Step #1: Already have image (with digest): gcr.io/cloud-builders/docker
Step #1: the transferred value is: 20190708T1706_myvar
Finished Step #1
PUSH
DONE

【讨论】:

【参考方案5】:

这是我刚刚自己在另一个中重用来自 GitVersion 的输出的一个示例。它建立在@chetabahana 发布的答案之上。

steps:
- id: 'Gitversion: Unshallow repo'
  name: gcr.io/cloud-builders/git
  args: [fetch, --unshallow]

- id: 'Gitversion: Parse'
  name: gittools/gitversion:latest-linux
  entrypoint: /bin/bash
  args:
    - -c 
    - |
      dotnet /app/GitVersion.dll > /workspace/gitversion.json

- id: 'Gitversion: Env file'
  name: stedolan/jq
  entrypoint: /bin/bash
  args:
    - -c
    - |
      for s in $(cat /workspace/gitversion.json | jq -r "to_entries|map(\"\(.key)=\(.value|tostring)\")|.[]" ); do
        echo "export $s" >> /workspace/gitversion.env
      done

- id: 'Build and push API image'
  name: gcr.io/cloud-builders/docker
  entrypoint: /bin/bash
  args:
    - -c
    - |
      source /workspace/gitversion.env
      docker build -t gcr.io/xxxx/example:$$SemVer-$$BuildMetaData example-app
      docker push gcr.io/xxxx/example:$$SemVer-$$BuildMetaData

神奇的成分是 $$ 转义替换变量,这样构建作业就不会尝试替换,而是将其留给 bash 替换。

【讨论】:

嗨,快速提问。我们试图用 args 中的“-c”暗示什么:-c? -c 是 bash 的命令开关,它可以将下一个字符串作为命令解释和执行。见askubuntu.com/questions/831847/what-is-the-sh-c-command【参考方案6】:

很遗憾,Google (尚)不支持此功能。但是,我一直在使用以下简单的方法,并且效果很好。请记住,文件默认保存到/workspace,在容器之间共享。如果您需要它在不同的目录中,请将其保存或复制到其他地方。

# Save variable to file
- name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    _id=$(openssl rand -hex 16,,)
    echo "$$_id" > id.txt

# Set variable from file
- name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    _id=$(cat id.txt)
    echo "$$_id"

【讨论】:

是的,如果您阅读了最佳答案,这就是我最终要做的。谢谢。

以上是关于如何通过 Google Cloud Build 中的步骤设置环境或替换变量?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 googleapis Artifacts API 以编程方式查找和删除通过 Google Cloud Build 构建的容器映像?

将 env 变量从 Google 的 Secret Manager 加载到在 Google Cloud Run 上运行但未通过 Cloud Build 部署的 Docker 容器中?

Google Cloud Build 可以通过工件目录递归吗?

如何在 Google App Engine 标准环境中使用 Google Cloud Build 或其他方法设置环境变量?

带有 python 入口点的 Google Cloud Build gsutil

如何禁用对 Google Cloud Build 的 Github 检查