Deploying a Go backend to Kubernetes with Automatic Cluster Provisioning
Tags: go, kubernetes, tutorial, cloud
Published: 2023-12-14 13:13:11

**✨ Today we'll walk through how to automatically provision a Kubernetes cluster and deploy a Go backend to it.** This includes everything from IAM to ingress, for a secure and production-ready setup.

The process looks exactly the same for both GCP and AWS, and by the end you will have deployed an example Go backend application into your new cluster.🚀

## ⚡️TL;DR
In this guide, we'll be using [Encore](https://encore.dev), a backend development platform, to automatically provision your Kubernetes cluster, directly in your own cloud account.

**What's on deck:**
- Install Encore
- Create your backend app from a template
- Run locally
- Connect your cloud account
- Deploy to Kubernetes

_Don't feel like building something right now? Watch the video instead:_
[How to deploy a Go backend to Kubernetes](https://www.youtube.com/watch?v=NnNCKLiyNRY)

## 💽 Install Encore

Install the Encore CLI to run your local environment:
- **macOS:** `brew install encoredev/tap/encore`
- **Linux:** `curl -L https://encore.dev/install.sh | bash`
- **Windows:** `iwr https://encore.dev/install.ps1 | iex`

## 🔨 Create your app

To keep things simple as we explain how Encore works, we're going to clone a pre-made app from Encore's [template repo](https://encore.dev/templates). It's a simple monolith implementing a URL shortener. The app has a REST API and a PostgreSQL database.

Install it by running:

```bash
encore app create my-app-name --example=url-shortener
```

## 🏁 Running locally

To run the application locally, make sure you have [Docker](https://docker.com) installed and running. This is required to run Encore applications with SQL databases.

Then simply run:

```bash
encore run
```

You should see this:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzumjxut8v9n9ltb89k3.png)

## 🧪 Try the API

Next, call your endpoint:

```shell
curl http://localhost:4000/url -d '{"URL": "https://encore.dev"}'
```

You should see this:

```bash
{
  "ID": "5cJpBVRp",
  "URL": "https://encore.dev"
}
```

## 🕹 Open the developer dashboard

While `encore run` is running, open <http://localhost:9400/> to view Encore's [local developer dashboard](https://encore.dev/docs/observability/dev-dash).

Here you can see API documentation, use the API via an API explorer, and see traces using Encore's built-in distributed tracing.

![local dev dash](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qkijw3hmhxhsx84ftat7.png)


## 🧐 Take a look a the code

This app is now running locally, without any manual work to set up a local Kubernetes instance, and we're about to deploy it to our cloud account. So how does it work?

Let's take a look at the code.👇

```go
package url

import (
	"context"
	"crypto/rand"
	"encoding/base64"

	"encore.dev/storage/sqldb"
)

type URL struct {
	ID  string // short-form URL id
	URL string // complete URL, in long form
}

type ShortenParams struct {
	URL string // the URL to shorten
}

// Shorten shortens a URL.
//
//encore:api public method=POST path=/url
func Shorten(ctx context.Context, p *ShortenParams) (*URL, error) {
	id, err := generateID()
	if err != nil {
		return nil, err
	} else if err := insert(ctx, id, p.URL); err != nil {
		return nil, err
	}
	return &URL{ID: id, URL: p.URL}, nil
}

// Get retrieves the original URL for the id.
//
//encore:api public method=GET path=/url/:id
func Get(ctx context.Context, id string) (*URL, error) {
	u := &URL{ID: id}
	err := sqldb.QueryRow(ctx, `
		SELECT original_url FROM url
		WHERE id = $1
	`, id).Scan(&u.URL)
	return u, err
}

type ListResponse struct {
	URLs []*URL
}

// List retrieves all URLs.
//
//encore:api public method=GET path=/url
func List(ctx context.Context) (*ListResponse, error) {
	rows, err := sqldb.Query(ctx, `
		SELECT id, original_url FROM url
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	urls := []*URL{}
	for rows.Next() {
		var u URL
		if err := rows.Scan(&u.ID, &u.URL); err != nil {
			return nil, err
		}
		urls = append(urls, &u)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}
	return &ListResponse{URLs: urls}, nil
}

// generateID generates a random short ID.
func generateID() (string, error) {
	var data [6]byte // 6 bytes of entropy
	if _, err := rand.Read(data[:]); err != nil {
		return "", err
	}
	return base64.RawURLEncoding.EncodeToString(data[:]), nil
}

// insert inserts a URL into the database.
func insert(ctx context.Context, id, url string) error {
	_, err := sqldb.Exec(ctx, `
		INSERT INTO url (id, original_url)
		VALUES ($1, $2)
	`, id, url)
	return err
}
```

As you can see there's no Kubernetes configuration, YAML, or other infrastructure config. It's all Go code, and almost only business logic.

We've simply defined an API endpoint by using the `//encore:api` annotation and have imported the `encore.dev/storage/sqldb` package.

At compile time, Encore parses the code to understand what the infrastructure requirements are to run the application and then generates the boilerplate code necessary. 📚[Learn more in the docs](https://encore.dev/docs)

When running locally, the CLI takes care of setting up local versions of the infrastructure, and in the cloud Encore automatically provisions the cloud services you need based on your configuration options when you create a new environment. 

Now, let's go ahead and create our first cloud environment.

## ⛅️ Connect your cloud account

First, we need to connect a cloud account to Encore. Do this by opening the [Cloud Dashboard](https://app.encore.dev) **> (Select your app) > App Settings > Integrations > Connect Cloud**.

### AWS

If you are using AWS, follow the instructions on the page to create an IAM Role, and then connect the role with Encore.

For your security, make sure to check `Require external ID` and specify the external ID provided in the instructions.

![Connect AWS](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xup3ys66ri9pdidcdi2m.png)

### GCP

If you are using GCP, follow the instructions on the screen to create a GCP Service Account for your Encore application.

![Connect GPC](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gb2gvav894otf846leol.png)

## 🖼 Create Environment

Once you've connected your account, it's time to create a new environment. Do this by going to the **Environments** page in the [Cloud Dashboard](https://app.encore.dev) and click on **Create Environment**.

From here you can set up your new environment:
- Select **Type** as `Production`.
- Select **Deployment Trigger** as `Branch Push` and set the branch name to your git default (e.g. `main`, or run `git branch` in your app's root folder if you're unsure).
- Select the **Cloud** to use (Select the one you connect your account for).
- **Region** (Select the one you prefer).
- **Compute instance** (Select Kubernetes).
- **Process allocation** (Select `All processes in one service`).

Then click **Create** to save your environment.💾

![Create environment](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/28e37rfryou2j93d0xqx.png)

Now you're ready to deploy!✨

## 🚀 Deploy to Kubernetes

Before pushing the button, remember: Encore will provision a real Kubernetes cluster in your account, along with best practices security measures for IAM and Ingress. This means deploying will incur a cost on your cloud account.🤑

To deploy your app and create a new Kubernetes cluster, simply run:

```shell
$ git add -A .
$ git commit -m 'Initial commit'
$ git push encore
```

Encore will now build and test your app, provision the needed infrastructure, including a Kubernetes cluster and database cluster, and deploy your application to the cloud.🚀

After triggering the deployment, you will see a URL where you can view its progress in Encore's [Cloud Dashboard](https://app.encore.dev).👈

It will look something like this, and will take up to 1 hour due to the cloud provider's service provisioning:

![kubernetes provisioning](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pi2mu29l2ebrhej0zgu9.jpg)

**⏰ While you wait...** Why not check out the [Encore Docs](https://encore.dev/docs) to learn more about how it works and what else you can do with the platform.

🎉**Then, once everything has been provisioned, you'll have a fully-fledged Go backend running in Kubernetes. Well done!**

Note: _If you want to keep costs in check, you should delete all the resources created in case you don't want to keep using your new cluster. This is easy to do from your cloud provider's console._

### 🍰 Great job - you're done!

You now have the start of a scalable Go backend running in Kubernetes.

Keep building with these Open Source Encore [App Templates](https://encore.dev/templates).👈

If you have questions or want to share your work, join the developers hangout in [Encore's community Slack](https://encore.dev/slack).👈