Enabling a Go app on GKE using Backstage templates and Github actions

From repo creation to enabling CI/CD pipeline and deploying on Kubernetes

Muralish Clinton
Level Up Coding

--

Introduction

Backstage is an open-source platform for constructing Internal Developer Portals (IDPs). Internal Developer Portals serve as a one-stop shop, that provides a unified view of all our resources. IDPs enable us to seamlessly create, manage, monitor, and document our software resources from a single location. The primary goal of the IDP is to eliminate the reliance on the connection between DevOps and developers.

I want to explore setting up a GO boilerplate application, complete with a CI/CD pipeline and deployment to GKE, all achievable with the click of a button to enhance developer efficiency.

Prerequisites

  1. Setup Backstage locally — Getting Started | Backstage Software Catalog and Developer Platform
  2. Have a GCP account with enough credits to run GKE — How To Create a Free Tier Account on GCP? — GeeksforGeeks
  3. Integrated GitHub Auth with Backstage — Using GitHub Auth with Backstage | Roadie
  4. Generate a service account key to deploy from Github Actions to GKE — Set up authentication for Docker | Artifact Registry documentation | Google Cloud

Deep Dive

Set up GO boilerplate

  • Create a templates folder on the root of the backstage application and create files as
  • catalog.info — contains the information to display once the component created via the template is registered on the catalog
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: ${{values.name | dump}}
description: ${{values.description | dump}}
annotations:
github.com/project-slug: ${{values.destination.owner + "/" + values.destination.repo}}
github.com/actions-workflow: https://github.com/${{values.destination.owner}}/${{values.destination.repo}}/actions
spec:
type: service
lifecycle: experimental
owner: ${{values.owner | dump}}
  • Dockerfile — Contains the script to containerize the application
# Start from a Debian-based image with Go installed
FROM golang:1.17 AS build

# Set the working directory inside the container
WORKDIR /app

# Copy the Go module files
COPY go.mod .
# COPY go.sum .

# Download and install dependencies
RUN go mod download

# Copy the source code into the container
COPY . .

# Build the Go application
RUN go build -o app .

# Start a new stage from a lightweight base image
FROM debian:buster-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the built executable from the previous stage
COPY --from=build /app/app .

# Expose port 8080 to the outside world
EXPOSE 8080

# Command to run the executable
CMD ["./app"]
  • template.yaml — contains the workflows to execute when running the boilerplate template
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
name: go-repo-template
title: GO Template
description: This repository template provides a framework for generating a Golang HTTP server with a clean architecture. Clean architecture emphasizes separation of concerns and maintainability by organizing code into distinct layers, making it easier to understand, test, and modify.
tags:
- recommended
- clean
- go
spec:
owner: muralish
type: service

parameters:
- title: Fill in some steps
required:
- name
- description
properties:
name:
title: Name
type: string
description: Unique name of the component
ui:field: EntityNamePicker
ui:autofocus: true
description:
title: Description
type: string
description: A description for the component
owner:
title: Owner
type: string
description: Owner of the component
ui:field: OwnerPicker
ui:options:
allowedKinds:
- Group
- title: Choose a location
required:
- repoUrl
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com

# This template is meant to be used on top of an existing template.
# By adding the following and fetching from an absolute URL you can
# add in the docs template
steps:
- id: fetch
name: Template Docs Skeleton
action: fetch:template
input:
url: ./skeleton
values:
name: ${{ parameters.name }}
description: ${{ parameters.description }}
destination: ${{ parameters.repoUrl | parseRepoUrl }}
owner: ${{ parameters.owner }}

- id: publish
name: Publish
action: publish:github
input:
allowedHosts: ['github.com']
description: This is ${{ parameters.name }}
repoUrl: ${{ parameters.repoUrl }}

- id: register
name: Register
action: catalog:register
input:
repoContentsUrl: ${{ steps.publish.output.repoContentsUrl }}
catalogInfoPath: '/catalog-info.yaml'

output:
links:
- title: Repository
url: ${{ steps.publish.output.remoteUrl }}
- title: Open in catalog
icon: catalog
entityRef: ${{ steps.register.output.entityRef }}

Replace the following with your own boilerplate.

  • main.go — a sample code to initialize and run the application.
  • go.mod — is generated as a result of running go mod init and go mod tidy.
  • add.go — contains a sample go function to add 2 numbers.
  • add_test.go — contains the unit test to test the unit test pipeline.

Setup Github Actions

  • .github/workflows/unit-test.yaml — runs the unit test every time code is pushed to master.
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]

jobs:

build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.20'

- name: Build
run: go build -v ./...

- name: Test
run: go test -v ./...
  • .github/workflows/deployment.yaml — creates a trigger to deploy the application on GKE.
---
name: Build and Push GO Image to Google Cloud Platform
on:
workflow_dispatch:
jobs:
build-push-gcr:
name: Build and Push to GCP
runs-on: ubuntu-latest
env:
IMAGE_NAME: backstage
PROJECT_ID: prj-dev-platform-next
steps:
- name: Checkout
uses: actions/checkout@v2

- uses: google-github-actions/setup-gcloud@v0
with:
service_account_key: ${{ secrets.SERVICE_ACCOUNT_KEY }}
project_id: prj-dev-platform-next
export_default_credentials: true
install_components: 'gke-gcloud-auth-plugin'

- name: Build Docker Image
run: docker build -t $IMAGE_NAME:latest .

- name: Configure Docker Client
run: |-
gcloud auth configure-docker --quiet
gcloud auth configure-docker us-west2-docker.pkg.dev --quiet

- name: Push Docker Image to Container Registry (GCR)
env:
GIT_TAG: v0.1.0
run: |-
docker tag $IMAGE_NAME:latest gcr.io/$PROJECT_ID/$IMAGE_NAME:latest
docker tag $IMAGE_NAME:latest gcr.io/$PROJECT_ID/$IMAGE_NAME:$GIT_TAG
docker push gcr.io/$PROJECT_ID/$IMAGE_NAME:latest
docker push gcr.io/$PROJECT_ID/$IMAGE_NAME:$GIT_TAG

- name: Push Docker Image to Artifact Registry
env:
GIT_TAG: v0.1.0
run: |-
docker tag $IMAGE_NAME:latest us-west2-docker.pkg.dev/$PROJECT_ID/images/$IMAGE_NAME:latest
docker tag $IMAGE_NAME:latest us-west2-docker.pkg.dev/$PROJECT_ID/images/$IMAGE_NAME:$GIT_TAG
docker push us-west2-docker.pkg.dev/$PROJECT_ID/images/$IMAGE_NAME:latest
docker push us-west2-docker.pkg.dev/$PROJECT_ID/images/$IMAGE_NAME:$GIT_TAG

- name: Deploy to Kubernetes
run: |
gcloud container clusters get-credentials go-app --region us-west2
kubectl apply -f .github/manifests/deployment.yaml --validate=false
  • .github/manifests/deployment.yaml — kubernetes yaml to deploy the containerized application on Kubernetes.
---
apiVersion: v1
kind: Service
metadata:
name: go-app
spec:
type: LoadBalancer
selector:
app: go-app
ports:
- port: 8080
targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-app
labels:
app: go-app
spec:
replicas: 1
selector:
matchLabels:
app: go-app
template:
metadata:
labels:
app: go-app
spec:
containers:
- name: go-app
image: gcr.io/prj-dev-platform-next/backstage:latest

Link the GO app template on Backstage

  • Update the app-config.yaml to contain the following in the catalog section. Reference the template.yaml file from the templates folder you had created.
catalog:
locations:
- type: file
target: C:\Users\Muralish\Downloads\backstage\backstage\templates\go-boilerplate\template.yaml
rules:
- allow: [Component, Group, User, Resource, Location, Template]

Setup GKE & Container Registry

  1. Go to Google Cloud Console.
  2. Browse container registry, enable it and create a folder called backstage to store the docker images.
  3. Browse Kubernetes Engine and create a new cluster — Choose autopilot mode and configure with the same settings as mentioned in .github/workflows/deployment.yaml.
  4. Provide sufficient resources for the application to run and/or enable autoscaling.

Test

  1. Run yarn dev to start the backstage application.
  2. Click on create on the sidebar and select the GO template that shows up on the main page.

3. Fill out the info that’s being prompted and confirm the creation

4. Once the application is created, you can view the application on the catalog page including ci/cd status.

5. Add the SERVICE_ACCOUNT_KEY in the GitHub actions secrets of the newly created repo

6. The CD pipeline can be triggered from the GitHub actions view.

Summary

The application is created in a way that allows you to generate a Go boilerplate code with best practices for your organization, eliminating the need to worry about setting up the CI/CD and infrastructure. Backstage will completely automate this process for you and handle all the necessary configurations.

--

--