Cloud Build等待制品上传后执行步骤及构建失败标记问题
解决Cloud Build部署Compute Engine的两个关键问题
问题1:确保Cloud Build等待制品上传完成后再执行后续步骤
Cloud Build的步骤默认是串行执行的,理论上gsutil cp完成后才会进入下一个步骤,但由于Cloud Storage的最终一致性,可能出现上传完成后短时间内无法读取文件的情况,导致实例启动脚本拉取失败。
解决方法是在上传制品后添加一个文件存在验证步骤,通过重试机制确保文件确实可访问后,再执行创建实例模板的操作:
steps: # 构建、归档二进制文件的步骤(你的原有步骤) - name: 'golang:1.21' args: ['go', 'build', '-o', 'app', './cmd'] - name: 'ubuntu' args: ['tar', '-czf', 'app.tar.gz', 'app'] # 上传制品到Cloud Storage - name: 'gcr.io/cloud-builders/gsutil' args: ['cp', 'app.tar.gz', 'gs://bucket-name/deploy/service-name/${COMMIT_SHA}/'] # 验证文件存在(带重试,适配最终一致性) - name: 'gcr.io/cloud-builders/gsutil' entrypoint: 'bash' args: - '-c' - | RETRIES=5 DELAY=10 FILE_PATH="gs://bucket-name/deploy/service-name/${COMMIT_SHA}/app.tar.gz" for ((i=0; i<RETRIES; i++)); do if gsutil stat "$FILE_PATH"; then echo "制品已确认存在,继续后续步骤" exit 0 fi echo "制品暂不可访问,$DELAY秒后重试(第$((i+1))次)" sleep "$DELAY" done echo "重试$RETRIES次后仍无法访问制品,构建失败" exit 1 # 创建实例模板的步骤(你的原有步骤) - name: 'gcr.io/cloud-builders/gcloud' args: ['compute', 'instance-templates', 'create', 'TEMPLATE_NAME', ...]
这个验证步骤会循环检查文件状态,直到成功或重试耗尽,确保后续步骤执行时制品已经可以被实例的启动脚本读取。
问题2:启动脚本失败时标记Cloud Build为失败,阻止实例组更新
启动脚本在实例内部执行,默认不会反馈状态到Cloud Build,导致构建成功但实例启动异常。可以通过两种方式实现状态反馈:
方式1:利用实例组健康检查等待稳定状态
如果你的实例组配置了健康检查(推荐配置),可以在Cloud Build中添加步骤,等待实例组更新完成并进入稳定状态:
# 创建实例模板后,更新实例组的步骤(你的原有步骤) - name: 'gcr.io/cloud-builders/gcloud' args: ['compute', 'instance-groups', 'managed', 'rolling-action', 'start-update', 'INSTANCE_GROUP_NAME', '--template', 'TEMPLATE_NAME', '--zone', 'ZONE'] # 等待实例组进入稳定状态 - name: 'gcr.io/cloud-builders/gcloud' args: - 'compute' - 'instance-groups' - 'managed' - 'wait-until-stable' - 'INSTANCE_GROUP_NAME' - '--zone' - 'ZONE' - '--timeout' - '300s' # 5分钟超时,可根据实际调整
如果启动脚本执行失败,实例会无法通过健康检查,wait-until-stable命令会返回非零退出码,导致整个Cloud Build标记为失败,从而阻止后续的实例组更新。
方式2:启动脚本主动上报执行状态到Cloud Storage
在启动脚本中添加状态上报逻辑,将执行结果写入Cloud Storage的指定文件,然后Cloud Build轮询该文件获取状态:
启动脚本片段:
# 执行部署与初始化逻辑 ./deploy_and_init.sh # 上报执行状态 STATUS=$? STATUS_FILE="/tmp/startup_status" REMOTE_STATUS_PATH="gs://bucket-name/deploy/service-name/${COMMIT_SHA}/startup_status" if [ $STATUS -eq 0 ]; then echo "SUCCESS" > "$STATUS_FILE" gsutil cp "$STATUS_FILE" "$REMOTE_STATUS_PATH" else echo "FAILED" > "$STATUS_FILE" gsutil cp "$STATUS_FILE" "$REMOTE_STATUS_PATH" exit $STATUS fi
Cloud Build中添加轮询步骤:
# 更新实例组后,添加轮询步骤 - name: 'gcr.io/cloud-builders/gsutil' entrypoint: 'bash' args: - '-c' - | RETRIES=10 DELAY=30 STATUS_FILE="gs://bucket-name/deploy/service-name/${COMMIT_SHA}/startup_status" for ((i=0; i<RETRIES; i++)); do if gsutil stat "$STATUS_FILE" 2>/dev/null; then STATUS=$(gsutil cat "$STATUS_FILE") if [ "$STATUS" = "SUCCESS" ]; then echo "启动脚本执行成功" exit 0 else echo "启动脚本执行失败" exit 1 fi fi echo "等待启动脚本状态上报,$DELAY秒后重试(第$((i+1))次)" sleep "$DELAY" done echo "超时未收到启动脚本状态,构建失败" exit 1
这种方式不需要依赖健康检查,适合无法配置健康检查的场景,但需要确保实例的服务账号有写入Cloud Storage的权限。
内容的提问来源于stack exchange,提问作者Avishay28
相关产品推荐
相关产品推荐

