Skip to content

Instantly share code, notes, and snippets.

@enzo-santos
Last active May 30, 2022 10:37
Show Gist options
  • Save enzo-santos/b413fe5ec444e759aa22db89cffb5bdf to your computer and use it in GitHub Desktop.
Save enzo-santos/b413fe5ec444e759aa22db89cffb5bdf to your computer and use it in GitHub Desktop.
Cloud Functions + GitLab CI tutorial

Cloud Functions + GitLab CI tutorial

This tutorial will teach you how to deploy a Python 3.8-based Cloud Function using GitLab CI.

Glossary

If you encounter a variable you don't recognize while reading, take a look here.

Variable name Description
... Sensitive value.
GIST_PROJECT_NAME The Google Cloud project name.
GIST_FUNCTION_NAME The Google Cloud function name.
GIST_SERVICE_ACCOUNT_NAME The service account name that will be created in this tutorial.
GIST_SERVICE_ACCOUNT_EMAIL The service account email, often but not always created as "GIST_SERVICE_ACCOUNT_NAME@GIST_PROJECT_NAME.iam.gserviceaccount.com"
GIST_SERVICE_ACCOUNT_KEY_PATH The path where the credentials file will be saved in your local machine. This path should not point to a directory.

Tutorial

Requirements

Service setup

Go to https://console.cloud.google.com/?project=GIST_PROJECT_NAME. Open lateral menu > IAM & Admin > Service Accounts and click in "Create service account", in the upper left corner. Alternatively, go to https://console.cloud.google.com/iam-admin/serviceaccounts/create?project=GIST_PROJECT_NAME.

Fill the form. Here, fields with (open) can be customized according to your project. Values followed by : can be used as example in your setup.

  • Service account details
  1. Service account name (open, GIST_SERVICE_ACCOUNT_NAME): continuous-integration
  2. Service account ID (automatically filled, GIST_SERVICE_ACCOUNT_EMAIL)
  3. Service account description (open): Manage continuous integration services.
  • Grant this service account access to project

In the dropdown list, select the Cloud Functions Admin role.

  • Grant users access to this service account

Do nothing and click "Done".

Local setup

Check if the new service account is appearing:

$ gcloud iam service-accounts list
DISPLAY NAME                          EMAIL                                            DISABLED
GIST_SERVICE_ACCOUNT_NAME             GIST_SERVICE_ACCOUNT_EMAIL                       False
App Engine default service account    GIST_PROJECT_NAME@appspot.gserviceaccount.com    False

Run the following to create the service account credentials:

$ gcloud iam service-accounts keys create GIST_SERVICE_ACCOUNT_KEY_PATH --iam-account GIST_SERVICE_ACCOUNT_EMAIL --key-file-type=json
created key [...] of type [json] as [GIST_SERVICE_ACCOUNT_KEY_PATH] for [GIST_SERVICE_ACCOUNT_EMAIL]

The created file will be of format

{
  "type": "service_account",
  "project_id": "GIST_SERVICE_ACCOUNT_NAME",
  "private_key_id": "...",
  "private_key": "...",
  "client_email": "GIST_SERVICE_ACCOUNT_EMAIL",
  "client_id": "...",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://oauth2.googleapis.com/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "..."
}

We'll run our Cloud Function in CI as the default service account and deploy it with the created service account. Thus, we need to give the default service account the user permission. Run

$ gcloud iam service-accounts add-iam-policy-binding GIST_PROJECT_NAME@appspot.gserviceaccount.com --member=serviceAccount:GIST_SERVICE_ACCOUNT_EMAIL --role=roles/iam.serviceAccountUser
Updated IAM policy for serviceAccount [GIST_PROJECT_NAME@appspot.gserviceaccount.com].
bindings:
- members:
  - serviceAccount:GIST_SERVICE_ACCOUNT_EMAIL
  role: roles/iam.serviceAccountUser
etag: ...
version: ...

Create a .gitlab-ci.yml file in your project root directory with contents

variables:
  GCLOUD_PROJECT_NAME: GIST_PROJECT_NAME
  GCLOUD_SERVICE_ACCOUNT_EMAIL: GIST_SERVICE_ACCOUNT_EMAIL
  GCLOUD_FUNCTION_NAME: GIST_FUNCTION_NAME
  GCLOUD_HOST_NAME: https://us-central1-$GCLOUD_PROJECT_NAME.cloudfunctions.net

stages:
  - build
  - deploy
  - deploy-test

build-job:
  image: python:3.8.13
  stage: build
  script:
    - python -m pip install -r requirements.txt

deploy-job:
  image: google/cloud-sdk:387.0.0
  stage: deploy
  script:
    - echo $GCLOUD_SERVICE_ACCOUNT_KEY_CONTENTS > key.json
    - gcloud auth activate-service-account $GCLOUD_SERVICE_ACCOUNT_EMAIL --project $GCLOUD_PROJECT_NAME --key-file key.json
    - gcloud functions deploy $GCLOUD_FUNCTION_NAME --runtime python38 --trigger-http --allow-unauthenticated

deploy-test-job:
  image: python:3.8.13
  stage: deploy-test
  script:
    - python -c "import urllib.request; print(urllib.request.urlopen('$GCLOUD_HOST_NAME/$GCLOUD_FUNCTION_NAME').getcode())"

Replace the fields appropriately, specially

  • the GCLOUD_HOST_NAME variable, if your function is not running on "us-central1"
  • the build-job configuration, if you're not using Python 3.8 or want to add more commands
  • the last deploy-job command, if you're not using Python 3.8 or want to change the command-line arguments

CI setup

Create a new CI variable named "GCLOUD_SERVICE_ACCOUNT_KEY_CONTENTS". Copy the contents of GIST_SERVICE_ACCOUNT_KEY_PATH and paste into this variable.

Commit and let the pipeline run! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment