Set up Google Cloud Run Service & Scheduler — via Terraform
The article will help you in setting up the Google Cloud Run service which will be invoked by the cloud scheduler.
Why to choose — Google Cloud Run + Google Scheduler for scheduling jobs ?
While designing applications (especially micro-services based) we come across situations to create some utility which should perform specific tasks on specific time like generation of reconciliation reports in evening, details of daily onboarded customers, failed payments details 1 am GMT etc.
Check some of the best suitable use-cases here.
I had an opportunity to implement above scenario in my previous project we named it as “backoffice processor”.
Back-office processor — was a Spring based application container which was executing scheduled jobs using Quartz Scheduler.
The downside of the above approach was :
- Application container was running all throughout the day
- High costs — Since deployment architecture was based on micro services, there were multiple back-office processors which were running on multiple node to handle specific business requirement
When we switched to Google Cloud Platform, we removed Quartz Schedulers from our Back-office Processors and started using Google Scheduler. Below are the highlighted benifits.
- Cloud Run Service invoke only when it receives request from Google Scheduler based on job specific time
- Economical — Google Cloud Run charges only for the resources used
In this article I will walk you through how to schedule a job at 6 pm GMT using Google Scheduler which will invoke Google cloud run service. For set up and configuration I will use Terraform. For authentication I will be using OpenID Connect.
Pre Requisite
- User should have Google cloud platform(GCP) account
- Basic knowledge of Terraform and GCP
- Basic knowledge of Maven, Docker
Create Project
Before you begin, you have to create a Google Cloud Project , which is a top level container that holds your App Engine application resources as well as other Google Cloud resources.
Create Service account
Creating a service account is similar to adding a member to your project, but the service account belongs to your applications rather than an individual end user.
You are required to provide permissions. This could also be provided even after creating Service Account.
To access resources of this google project you need private key file. Refer to the below :
Enable Google Container Registry (GCR)
As Google Cloud run enables us to write code in our way and deploy any container that listens for requests or events.
To store, manage, and secure our Docker container images we need to enable Google Container Registry Service.
Build Report Generation Service
Do checkout sample code which is being referred in this article. Run the below command to build docker image.
$ mvn clean install -f report-generator-service/pom.xml
Create a Tag
$ docker tag demo/report-generator-service:latest gcr.io/charged-library-280615/report-generator-service:latest
Login to Google Container Registry
In the below command “charged-library-280615–9fa9b79158d5.json” is the key file which we have downloaded in earlier step.
$ docker login -u _json_key -p "$(cat ./charged-library-280615-9fa9b79158d5.json)" https://gcr.io
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
Login Succeeded
Push Docker Image to GCR
$ docker push gcr.io/charged-library-280615/report-generator-service:latest
The push refers to repository [gcr.io/charged-library-280615/report-generator-service]
035c61f5476b: Preparing
ceaf9e1ebef5: Preparing
9b9b7f3d56a0: Preparing
f1b5933fe4b5: Preparing
denied: Token exchange failed for project 'charged-library-280615'. Caller does not have permission 'storage.buckets.create'. To configure permissions, follow instructions at: https://cloud.google.com/container-registry/docs/access-control
Ooops, you are not able to push image to GCR because you have’nt created Google Cloud Storage yet.
Google Cloud Storage — is globally unified, scalable, and highly durable object storage.
Enable Cloud Storage
Create Bucket
In Google Cloud Storage — you store objects in containers called buckets.
Define Permission To Access Bucket
Now again push Docker Image to GCR
$ docker push gcr.io/charged-library-280615/report-generator-service
The push refers to repository [gcr.io/charged-library-280615/report-generator-service]
035c61f5476b: Pushed
ceaf9e1ebef5: Layer already exists
9b9b7f3d56a0: Layer already exists
f1b5933fe4b5: Layer already exists
latest: digest: sha256:2773451b3cc1fdeba04a9cdb512d474a289fe73745ace1e6949e772a86a0926b size: 1159
Before applying terraform configuration changes, make sure that the below google services/api are enabled :
- Google Cloud Run
- Compute Engine API
- IAM API
- Google Scheduler API
Enable Cloud Run
Enable Compute Engine API
Enable IAM API
Enable Google Scheduler API
Let’s Understand Cloud Run infrastructure code
resource "google_cloud_run_service" "report-generator-service" {
name = "report-generator-service"
location = "us-east4" template {
spec {
containers {
image = "gcr.io/charged-library-280615/report-generator-service"
}
}
} traffic {
percent = 100
latest_revision = true
}
}
Above code will create Google Cloud Run service named “report-generator-service” which will create a containergcr.io/charged-library-280615/report-generator-service
.
In case of latest version deployment, Cloud Run will redirect 100% traffic to latest revision. If required, we could manage traffic sharing ratio between latest version and last version. Although GCR will keep running the existing requests.
Let’s Understand Google Scheduler job infrastructure code
resource "google_cloud_scheduler_job" "updater" {
name = "daily-report-generation-job"
description = "It will generate daily report"
schedule = "0 0 18 ? * * *"
time_zone = "GMT" http_target {
http_method = "GET"
uri = "${google_cloud_run_service.report-generator-service.status[0].url}/v1/weather/hello1" oidc_token {
service_account_email = "terraform@charged-library-280615.iam.gserviceaccount.com"
}
}
}
Above code will create a Google Scheduler job which will invoke provided Http GET Api daily at 6 pm GMT. Open ID Connect is being used for authentication.
Let’s Run Terraform Code Configuration
terraform init
— command will initialise a working directory containing Terraform configuration files.
$ terraform init
Initializing the backend...Initializing provider plugins...Terraform has been successfully initialized!You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
terraform apply
— Command is used to apply the changes required to reach the desired state of the configuration.
$ terraform apply
data.google_compute_default_service_account.default: Refreshing state...
google_cloud_run_service.report-generator-service: Refreshing state... [id=locations/us-east4/namespaces/charged-library-280615/services/report-generator-service]An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ createTerraform will perform the following actions: # google_cloud_run_service.report-generator-service will be created
+ resource "google_cloud_run_service" "report-generator-service" {
+ id = (known after apply)
+ location = "us-east4"
+ name = "report-generator-service"
+ project = (known after apply)
+ status = (known after apply) + metadata {
+ annotations = (known after apply)
+ generation = (known after apply)
+ labels = (known after apply)
+ namespace = (known after apply)
+ resource_version = (known after apply)
+ self_link = (known after apply)
+ uid = (known after apply)
} + template { + spec {
+ serving_state = (known after apply) + containers {
+ image = "gcr.io/charged-library-280615/report-generator-service"
}
}
} + traffic {
+ latest_revision = true
+ percent = 100
}
} # google_cloud_scheduler_job.updater will be created
+ resource "google_cloud_scheduler_job" "updater" {
+ description = "It will generate daily report"
+ id = (known after apply)
+ name = "daily-report-generation-job"
+ project = (known after apply)
+ region = (known after apply)
+ schedule = "* 18 * * *"
+ time_zone = "GMT" + http_target {
+ http_method = "GET"
+ uri = "https://report-generator-service-zrjjlwqnjq-uk.a.run.app/v1/weather/hello1" + oidc_token {
+ service_account_email = "terraform@charged-library-280615.iam.gserviceaccount.com"
}
}
}Plan: 1 to add, 0 to change, 0 to destroy.Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve. Enter a value: yesgoogle_cloud_run_service.report-generator-service: Creating...
google_cloud_run_service.report-generator-service: Still creating... [10s elapsed]
google_cloud_run_service.report-generator-service: Still creating... [20s elapsed]
google_cloud_run_service.report-generator-service: Still creating... [30s elapsed]
google_cloud_run_service.report-generator-service: Still creating... [40s elapsed]
google_cloud_run_service.report-generator-service: Creation complete after 49s [id=locations/us-east4/namespaces/charged-library-280615/services/report-generator-service]
google_cloud_scheduler_job.updater: Creating...
google_cloud_scheduler_job.updater: Creation complete after 6s [id=projects/charged-library-280615/locations/us-east4/jobs/daily-report-generation-job]Apply complete! Resources: 1 added, 0 changed, 0 destroyed.Outputs:url = https://report-generator-service-zrjjlwqnjq-uk.a.run.app
Is Cloud Run & Scheduler Job Running ?
Yes, both services are up and running. Do check logs for more details.
Let’s invoke Cloud Run API
Let’s try to invoke Cloud Run via curl command. To invoke Cloud Run api we need google OIDC token. Follow the below steps to generate OIDC token.
Activate Service account
In the below command key.json
is the private key which we have downloaded earlier
$ gcloud auth activate-service-account --key-file=key.json
Activated service account credentials for: [service-account]
Print Token
In the below command, audiences
is the service url of Cloud Run Service.
$ gcloud auth print-identity-token --audiences=https://report-generator-service-zrjjlwqnjq-uk.a.run.app
eyJhbGciOiJSUzI1NiIsImtpZCI6ImIxNm........
Curl — Cloud Run API
$ curl -ivk --location --request GET 'https://report-generator-service-zrjjlwqnjq-uk.a.run.app' \
--header 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImIxNm.....'* Trying 216.239.36.53...
* TCP_NODELAY set
* Connected to test-bmgsrd6uza-uc.a.run.app (216.239.36.53) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
* CAfile: /etc/ssl/cert.pem
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-ECDSA-CHACHA20-POLY1305
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=US; ST=California; L=Mountain View; O=Google LLC; CN=*.a.run.app
* start date: May 26 15:20:56 2020 GMT
* expire date: Aug 18 15:20:56 2020 GMT
* issuer: C=US; O=Google Trust Services; CN=GTS CA 1O1
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7feac4808c00)
> GET /v1/weather/hello1 HTTP/2
> Host: test-bmgsrd6uza-uc.a.run.app
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImIxNm......
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
HTTP/2 200
< content-type: text/plain;charset=UTF-8
content-type: text/plain;charset=UTF-8
< date: Tue, 16 Jun 2020 06:30:58 GMT
date: Tue, 16 Jun 2020 06:30:58 GMT
< server: Google Frontend
server: Google Frontend
< content-length: 8
content-length: 8
< alt-svc: h3-27=":443"; ma=2592000,h3-25=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
alt-svc: h3-27=":443"; ma=2592000,h3-25=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"<
* Connection #0 to host test-bmgsrd6uza-uc.a.run.app left intact
test message
Delete Cloud Run Service & Scheduler job
The terraform destroy
command is used to destroy the Terraform-managed infrastructure.
$ terraform destroy
Thanks for reading !
Hope you have enjoyed reading the article.
Hit 👏 button (up to 50 times) . It encourages me to keep writing :)
Also do checkout my articles