在 Cloud Run 中构建并创建 Go 作业

了解如何创建简单的 Cloud Run 作业,然后从源代码进行部署,以自动将代码打包到容器映像中,将容器映像上传到 Artifact Registry,接着部署到 Cloud Run。除了所示语言之外,您还可以使用其他语言。

准备工作

  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Install the Google Cloud CLI.
  5. To initialize the gcloud CLI, run the following command:

    gcloud init
  6. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  7. Make sure that billing is enabled for your Google Cloud project.

  8. Install the Google Cloud CLI.
  9. To initialize the gcloud CLI, run the following command:

    gcloud init
  10. 启用 Cloud Run Admin API 和 Cloud Build API:

    gcloud services enable run.googleapis.com \
        cloudbuild.googleapis.com

    启用 Cloud Run Admin API 后,系统会自动创建 Compute Engine 默认服务账号。

  11. 为了让 Cloud Build 能够构建来源,请运行以下命令,将 Cloud Build Service Account 角色授予给 Compute Engine 默认服务账号:

    gcloud projects add-iam-policy-binding PROJECT_ID \
        --member=serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.com \
        --role=roles/cloudbuild.builds.builder

    PROJECT_NUMBER 替换为您的 Google Cloud 项目编号,并将 PROJECT_ID 替换为 Google Cloud 项目 ID。 如需详细了解如何查找项目 ID 和项目编号,请参阅创建和管理项目

    向 Compute Engine 默认服务账号授予 Cloud Build Service Account 角色需要几分钟时间才能传播

编写示例作业

如需在 Go 中编写作业,请执行以下操作:

  1. 创建名为 jobs 的新目录,并转到此目录中:

    mkdir jobs
    cd jobs
    
  2. 在同一目录中,为实际作业代码创建一个 main.go 文件。将以下示例行复制到其中:

    package main
    
    import (
    	"fmt"
    	"log"
    	"math/rand"
    	"os"
    	"strconv"
    	"time"
    )
    
    type Config struct {
    	// Job-defined
    	taskNum    string
    	attemptNum string
    
    	// User-defined
    	sleepMs  int64
    	failRate float64
    }
    
    func configFromEnv() (Config, error) {
    	// Job-defined
    	taskNum := os.Getenv("CLOUD_RUN_TASK_INDEX")
    	attemptNum := os.Getenv("CLOUD_RUN_TASK_ATTEMPT")
    	// User-defined
    	sleepMs, err := sleepMsToInt(os.Getenv("SLEEP_MS"))
    	failRate, err := failRateToFloat(os.Getenv("FAIL_RATE"))
    
    	if err != nil {
    		return Config{}, err
    	}
    
    	config := Config{
    		taskNum:    taskNum,
    		attemptNum: attemptNum,
    		sleepMs:    sleepMs,
    		failRate:   failRate,
    	}
    	return config, nil
    }
    
    func sleepMsToInt(s string) (int64, error) {
    	sleepMs, err := strconv.ParseInt(s, 10, 64)
    	return sleepMs, err
    }
    
    func failRateToFloat(s string) (float64, error) {
    	// Default empty variable to 0
    	if s == "" {
    		return 0, nil
    	}
    
    	// Convert string to float
    	failRate, err := strconv.ParseFloat(s, 64)
    
    	// Check that rate is valid
    	if failRate < 0 || failRate > 1 {
    		return failRate, fmt.Errorf("Invalid FAIL_RATE value: %f. Must be a float between 0 and 1 inclusive.", failRate)
    	}
    
    	return failRate, err
    }
    
    func main() {
    	config, err := configFromEnv()
    	if err != nil {
    		log.Fatal(err)
    	}
    
    	log.Printf("Starting Task #%s, Attempt #%s ...", config.taskNum, config.attemptNum)
    
    	// Simulate work
    	if config.sleepMs > 0 {
    		time.Sleep(time.Duration(config.sleepMs) * time.Millisecond)
    	}
    
    	// Simulate errors
    	if config.failRate > 0 {
    		if failure := randomFailure(config); failure != nil {
    			log.Fatalf("%v", failure)
    		}
    	}
    
    	log.Printf("Completed Task #%s, Attempt #%s", config.taskNum, config.attemptNum)
    }
    
    // Throw an error based on fail rate
    func randomFailure(config Config) error {
    	rand.Seed(time.Now().UnixNano())
    	randomFailure := rand.Float64()
    
    	if randomFailure < config.failRate {
    		return fmt.Errorf("Task #%s, Attempt #%s failed.", config.taskNum, config.attemptNum)
    	}
    	return nil
    }
    

    Cloud Run 作业允许用户指定作业要执行的任务数量。此示例代码演示了如何使用内置 CLOUD_RUN_TASK_INDEX 环境变量。每个任务代表容器的一个正在运行的副本。 请注意,任务通常并行执行。如果每个任务都可以独立处理一部分数据,则使用多个任务非常有用。

    每个任务都知道其存储在 CLOUD_RUN_TASK_INDEX 环境变量中的索引。内置 CLOUD_RUN_TASK_COUNT 环境变量包含在执行作业时通过 --tasks 参数提供的任务数量。

    此处所示的代码还展示了如何使用内置 CLOUD_RUN_TASK_ATTEMPT 环境变量重试任务,该变量包含此任务重试的次数,从 0 开始(表示第一次尝试),然后每次连续重试时递增 1,上限为 --max-retries

    此外,通过代码,您还可以生成失败来测试重试并生成错误日志,从而查看失败情况。

  3. 创建一个包含以下内容的 go.mod 文件:

    module github.com/GoogleCloudPlatform/golang-samples/run/jobs
    
    go 1.21
    

您的代码已完成,可以封装在容器中。

构建作业容器,将其发送到 Artifact Registry 并部署到 Cloud Run

重要提示:本快速入门假定您在快速入门中使用的项目中拥有所有者或编辑者角色。否则,请参阅 Cloud Run Source Developer 角色,了解从源代码部署 Cloud Run 资源所需的权限。

本快速入门使用“从源代码部署”,以构建容器,将其上传到 Artifact Registry,然后将作业部署到 Cloud Run:

gcloud run jobs deploy job-quickstart \
    --source . \
    --tasks 50 \
    --set-env-vars SLEEP_MS=10000 \
    --set-env-vars FAIL_RATE=0.1 \
    --max-retries 5 \
    --region REGION \
    --project=PROJECT_ID

其中,PROJECT_ID 是您的项目 ID,REGION 是您的区域,例如 us-central1。请注意,您可以将各种参数更改为要用于测试的任何值。SLEEP_MS 模拟工作,FAIL_RATE 导致 X% 的任务失败,因此您可以试验并行情况并重试失败任务。

在 Cloud Run 中执行作业

如需执行刚刚创建的作业,请运行以下命令:

gcloud run jobs execute job-quickstart --region REGION

REGION 替换为您在创建和部署作业时使用的区域,例如 us-central1

后续步骤

如需详细了解如何使用代码源构建容器并推送到仓库,请参阅: