Deploying a Go-based app to AKS using Kubestack
Tags: kubernetes, azure, kubestack, go
Published: 2021-08-30 14:16:56

In this post, I would be showing you how to deploy a golang application to Kubernetes using [Kubestack](https://kubestack.com). Kubestack is a Gitops automation built on Terraform to reduce the risk of deploying and increasing development speed. That being said I would not be covering how to set up kubestack but rather how to deploy Kubernetes manifests using Kubestack, for more info on how to up a working Kubestack pipeline check out the getting started guide over [here](https://www.kubestack.com/framework/documentation/tutorial-get-started). 

Now let's dive in.

## Setup

Once you have your kubestack pipeline set up your folder structure should look something like this
```bash
    .
    ├── Dockerfile
    ├── Dockerfile.loc
    ├── README.md
    ├── aks_zero_cluster.tf
    ├── aks_zero_ingress.tf
    ├── aks_zero_providers.tf
    ├── manifests
    └── versions.tf
```

to deploy our Kubernetes manifest we would be making use of a cluster service module. Cluster service modules in Kubestack allows Terraform to interact directly with Kubernetes through Terraform, this covers things like deploying and creating namespaces, creating deployments..etc. 

Open up `ask-zero_cluster.tf` and add the following lines
```terraform
    ...
    
    module "custom_manifests" {
      providers = {
        kustomization = kustomization.aks_zero
      }
      source  = "kbst.xyz/catalog/custom-manifests/kustomization"
      version = "0.1.0"
      configuration = {
        apps = {
          namespace = "apps-${terraform.workspace}"
          resources = [
        		"${path.root}/manifests/apps/namespace.yaml",
            "${path.root}/manifests/apps/deployment.yaml",
            "${path.root}/manifests/apps/service.yaml",
            "${path.root}/manifests/apps/ingress.yaml"
          ]
          common_labels = {
            "env" = terraform.workspace
          }
        }
        ops = {}
        loc = {}
      }
    }
```

Here we initialize the custom manifest cluster service module and tell kustomize to use the existing `aks_zero` module using.
```terraform
     providers = {
        kustomization = kustomization.aks_zero
      }
```

The configuration block is where the magic happens, first, we specify we declare what workspace we want our resources to be deployed to using
```terraform

     apps = {
          namespace = "apps-${terraform.workspace}"
          resources = [
       			"${path.root}/manifests/apps/namespace.yaml",
            "${path.root}/manifests/apps/deployment.yaml",
            "${path.root}/manifests/apps/service.yaml",
            "${path.root}/manifests/apps/ingress.yaml"
          ]
          common_labels = {
            "env" = terraform.workspace
          }
        }
```

Next, we declare what namespace the manifests should be deployed to using `namespace = "apps${terraform.workspace}"` which would translate to `apps-ops` or `apps-apps` depending on your current workspace, next the resources block tells terraform to look in manifests folder in the current directory for the manifests we want to deploy.  Now that we have the cluster module set up let's create the manifests.
Run the following commands:

```bash
mkdir manifests/apps && cd manifests/apps && touch deployment.yaml service.yaml ingress.yaml
```

Now populate the files with the following code.

```yaml
# manifests/apps/namespace.yaml
---
apiVersion: v1
kind: Namespace
metadata:
  name: apps-apps

---
apiVersion: v1
kind: Namespace
metadata:
  name: apps-ops
```

Note: since this is a fresh Kubernetes cluster I have included a manifest for a namespace as kubestack would deploy the resources in the order specified in the module so the namespace `apps-apps` would need to exist first.

```yaml
#manifests/apps/deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ping-api
spec:
  selector:
    matchLabels:
      app: ping-api
  template:
    metadata:
      labels:
        app: ping-api
    spec:
      containers:
      - name: ping-api
        image: ghcr.io/s1ntaxe770r/evil-ekow:latest
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 8080
        env:
          - name: REDIS_CACHE_HOST
            value: "redis-svc"
          - name: REDIS_PORT
            value: "6379"

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: redis
spec:
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      containers:
      - name: redis
        image: redis:alpine
        resources:
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 6379

# manifests/apps/service.yaml
--
apiVersion: v1
kind: Service
metadata:
  name: redis-svc
spec:
  selector:
    app: redis
  ports:
  - port: 6379
    targetPort: 6379

---
apiVersion: v1
kind: Service
metadata:
  name: ping-svc
spec:
  selector:
    app: ping-api
  ports:
  - port: 80
    targetPort: 8080
# manifests/apps/ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myingress
  labels:
    name: myingress
spec:
  rules:
  - host: prod.evil.corp
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: ping-svc
            port:
              number: 80
```

The above manifests will deploy a version of an API that uses Redis to store key-value pairs. You can check the source code out [here](https://github.com/s1ntaxe770r/evil-ekow).

Next, you can test out these changes locally by running  `kbst apply local`, if all looks good you should be able to tag and deploy like this:

```bash
$ git tag apps-deploy-1
$ git push origin apps-deploy-1
```

## Accessing your deployment

Because I don't have a valid domain at the time of writing I would be using my ingress controller external IP to access the deployment, however, if you have your domain properly configured pls refer to this portion of the
[Kubestack](https://www.kubestack.com/framework/documentation/tutorial-provision-infrastructure) documentation.

First, grab your ingress controller\'s external IP using:

    $  kubectl get ingresses -A

Next, create the following entry in `/etc/hosts`

    10.10.20.5   prod.evil.corp

be sure to change the IP to your ingress controllers external IP ,now you should be able to access the swagger docs at
<http://prod.evil.corp>

![evil-corp](https://file.coffee/u/KwFlKr9wJoj3hk.png)

## Conclusion

In this post, I covered how to deploy your Kubernetes manifests using Kubestack on Azure do note that this is not limited to azure as Kubetack also supports GKE and AWS at the time of writing this post, so be sure to check out the
[documentation](https://www.kubestack.com/framework/documentation/tutorial-provision-infrastructure) for any provider-specific steps. Now go forth and git deploying 😄