This is part 2 of our JAMstack series. Make sure you've read part 1 so you can follow along!

Now that a Gitpod development environment is in place, let's set up the groundwork for the M part in JAMstack - a markdown oriented static site. This site will host all our user-accessible static content and our dynamic front-end pieces as they are implemented.

Environment Setup

We will be utilizing Hugo for our static site generation. Therefore, a large part of this tutorial will be based on Alejandro's excellent article on deploying a Hugo site, but leveraging the Gitpod environment for development and deployment.

First, ensure your Gitpod environment is set up and has access to Civo:

civo k3s list  #Ensure no error is reported back 

Let's set that up in our Gitpod environment as we are using Hugo. Your Gitpod environment has access to Homebrew so we will use that. Although there are several ways to install in a Gitpod environment, I prefer to embed software that will be used regularly into a custom Docker image for my environment. Create a .gitpod.Dockerfile, and add the following:

 FROM quay.io/ssmiller25/gitpod-k8s:latest

 # Install Hugo
 RUN brew install hugo

Edit your .gitpod.yml configuration, and change the image link to match below. (the rest of the file was set up in the first article)

image:
  file: .gitpod.Dockerfile
tasks:
  - name: Login to Cloud Resources
    command: |
      bash $HOME/scripts/00-cloudinit.sh
vscode:
  extensions:
    - ms-azuretools.vscode-docker
    - ms-kubernetes-tools.vscode-kubernetes-tools

Save and commit all your changes. Then relaunch Gitpod for your repository. The first time you open will take a while (several minutes)! Once the new container is built, the launch time should be drastically reduced. Once up, let's verify we have Hugo installed. Run this from the terminal in your Gitpod instance

hugo version

If that returns a version number, we are good.

Initial Site Development

Let's use Hugo to set up an initial site:

hugo new site jamstacksite -f yml

A new jamstackapp directory will be created in your repo, with the standard layout for a Hugo site. We will use the PaperMod theme to get started, but most others should work.

git submodule add --depth=1 https://github.com/adityatelange/hugo-PaperMod.git jamstacksite/themes/papermod

Update jamstacksite/config.yml to match the following:

baseURL: http://example.org/
languageCode: en-us
title: "My JamStack Site"
theme: "papermod"

Now let's make sure we have a functional site. Run the following commands in the Gitpod terminal:

cd jamstacksite/
hugo serve

Once Hugo begins serving, Gitpod will detect the newly open port and give you a chance to preview the site.

Site preview button

Click the "Open Browser", and a new window should appear with the website as it is now:

Preview of our site in the browser

Press Ctrl + C to exit the Hugo session when finished.

If everything looks good, let's tell Hugo to build the site:

hugo

Hosting on Civo

Ok, now let's leverage Gitpod to push a docker container to Civo. In production, you would usually embed this into a pipeline as described in Alejandro's article, but we'll leave this as a manual deployment as we are developing our new app. First, let's create a new Civo cluster. From the Gitpod command interface:

cd ${GITPOD_REPO_ROOT}
civo k3s create jamstacksite -n 2 --wait
civo k3s config jamstacksite --save --merge
kubectl config use-context jamstacksite
kubectl get nodes  #Ensure we see nodes from the new cluster

Create a Dockerfile in the root of your repository:

FROM nginx
COPY ./jamstacksite/public /usr/share/nginx/html

Create a k8s.yaml to deploy the site. Make sure to change the ssmiller25 to your username

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jamstackapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: jamstackapp
  template:
    metadata:
      labels:
        app: jamstackapp
    spec:
      containers:
        - image: ssmiller25/jamstackapp:latest
          name: jamstackapp
          imagePullPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: jamstackapp
  labels:
    app: jamstackapp
spec:
  ports:
    - name: "http"
      port: 80
  selector:
    app: jamstackapp
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: jamstackapp-ingress
  labels:
    app: jamstackapp
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: jamstackapp
                port: 
                  number: 80
      # Not required, but useful if serving multiple hostnames through the same ingress
      #host: CLUSTER_ID.k8s.civo.com

How? Let's build and push our docker container. Make sure to substitute ssmiller25 with your Dockerhub username:

docker buildx build --push -t ssmiller25/jamstackapp:latest .

Deploy the kubernetes manifests

kubectl apply -f k8s.yaml

Now let's check out the site. First, find out the public IP of your cluster by running civo k3s show jamstacksite and look for the "DNS A record". Go to that address, and you should see the site you previewed on your Gitpod environment!

The live site being hosted on Civo

Conclusion

We now have a static site hosted on Civo: The M In our JAMStack. With this basis, we can begin exploring implementing more dynamic content leveraging Javascript and APIs - both externally and internally.

More Information