Learn how you can migrate your Hugo static sites over to OpenFaaS including custom domains, TLS and CI/CD through OpenFaaS Cloud (or GitLab).
OpenFaaS is one of the most popular open-source platforms on the CNCF Landscape for building and deploying functions and microservices on Kubernetes. It’s likely that you are used to seeing OpenFaaS used only to deploy functions, but in this post we want to show you what is possible through the project’s templating system.
Each programming language has its own custom-built template, which contains an entrypoint, a Dockerfile, and a sample handler to edit. Users can create their own templates with OpenFaaS in a very short period of time and in this tutorial I will show the one I created for Hugo, a popular static site generator.
A note from Alex Ellis, OpenFaaS Founder:
Now many people may associate “Serverless” only with SaaS products and cloud functions, but major players in the computing industry such as IBM, Red Hat, Pivotal and Google are all making an investment in Serverless 2.0. Serverless 2.0 is portable between any cloud or computer and is not subject to the limitations of SaaS products. Some users may perfectly happy with being locked into a single vendor and whatever the boundaries are, but for everyone else there’s a new world. Find out more in my Serverless Beyond the Hype video.
This tutorial is for users who may want to deploy any number of workloads including: microservices, blogs, functions, batch jobs, legacy HTTP servers, and static sites.
The goal of this tutorial is not to show you that you need a Kubernetes cluster to run a static website, it’s to show what can be done with OpenFaaS and Kubernetes. You can set up a managed Kubernetes service for as little as 15 USD / mo and after following this tutorial, it will have very little administrative overhead. I’d also recommend you read the tutorial on how to build a complete Serverless Single Page App to understand more behind the workflow and developer experience.
Pre-reqs
- Kubernetes
You need to have a Kubernetes cluster up & running. I would recommend DigitalOcean, it’s cost-efficient and very easy to set-up. Get free credits.
You’ll need helm to install the components in the cluster. If you can’t install Tiller
, helm’s server-side component, in your cluster then you can use helm template
.
For this tutorial we will configure OpenFaaS to use a Kubernetes IngressController. Traffic will reach the OpenFaaS gateway through the IngressController, instead of through a network LoadBalancer.
Set ingress.enabled=true
and exposeServices=false
when installing with helm:
helm repo update
helm upgrade openfaas --install openfaas/openfaas \
--namespace openfaas \
--set basic_auth=true \
--set functionNamespace=openfaas-fn \
--set ingress.enabled=true \
--set exposeServices=false
If you already have OpenFaaS installed, then you can run the command again to upgrade to the latest versions.
- Install an IngressController
The new IngressOperator will create TLS records and Ingress entries for our new sites. This means that users can reach our endpoints through a custom domain.
The IngressOperator supports the followin options: Nginx, Traefik and Zalando’s Skipper.
I will be using Nginx for today’s tutorial:
helm install stable/nginx-ingress --name nginxingress --set rbac.create=true
- Install the cert-manager (optional)
Installing the cert-manager to get automatic TLS certificates is optional but I highly recommend it. I will be using it today.
You’ll need to install the Hugo CLI. Use it to create a static site and to serve it locally for development and test.
- DNS management
You’ll need to create an A record to serve your static site. I’m using DigitalOcean which offers free domain management at time of writing.
The easiest way to create DNS records is with the DigitalOcean CLI: install doctl.
Install the IngressOperator
The IngressOperator automatically manages the creation of custom ingress rules and TLS certificates for your functions using a new CRD called FunctionIngress
introduced by Alex in the previous blog post.
Lets deploy the operator to our cluster following the instructions at the documentation:
git clone https://github.com/openfaas-incubator/ingress-operator
cd ingress-operator
kubectl apply -f ./artifacts/operator-crd.yaml
kubectl apply -f ./artifacts/operator-rbac.yaml
kubectl apply -f ./artifacts/operator-amd64.yaml
Meet the Hugo template
Templates in OpenFaaS are like a scaffold for an application, they lay down some common components which you don’t have to think about. It means that you can save hundreds of lines of repetitive boiler-late coding and YAML.
Templates can then be shared through the The Template Store.
Let’s take a look at my template for Hugo static sites called openfaas-hugo-template. This template copies the contents of your Hugo site, builds it into the public
directory and then uses the OpenFaaS of-watchdog to serve the content. It also provides metrics and a health check for Kubernetes that follows the OpenFaaS runtime contract.
Create a new Hugo site
Create a new Hugo site using faas-cli
(or its alias faas
):
# Create a new folder
mkdir -p my-blog/
cd my-blog
# Initialize a git repo
git init
# Pull in my †emplate using its URL
faas template pull https://github.com/matipan/openfaas-hugo-template
# Get an account at https://hub.docker.com
export DOCKER_HUB_USER="your-hub-account"
# Now create a Hugo site called `example-site`
faas new --lang hugo example-site --prefix ${DOCKER_HUB_USER}
This will create a folder called example-site
where we will place the content for the site along with its configuration and any themes we may want.
The example-site.yml
file is called a stack file and can be used to configure the deployment on OpenFaaS.
The following steps are based upon the Hugo quick-start guide:
cd example-site
# Create a new site
hugo new site .
# Add a custom theme
git submodule add https://github.com/budparr/gohugo-theme-ananke.git themes/ananke
# Append the theme to the config file
echo 'theme = "ananke"' >> config.toml
When you are developing new content you’ll probably want to see what it looks like before deploying it. To do this, you can use the hugo server
command inside the function’s directory:
hugo server
Watching for changes in /home/capitan/src/gitlab.com/matipan/openfaas-hugo-blog/blog/{archetypes,content,static}
Watching for config changes in /home/capitan/src/gitlab.com/matipan/openfaas-hugo-blog/blog/config.toml
Environment: "development"
Serving pages from memory
Running in Fast Render Mode. For full rebuilds on change: hugo server --disableFastRender
Web Server is available at http://localhost:1313/ (bind address 127.0.0.1)
Press Ctrl+C to stop
Now go to http://localhost:1313 and check out what your site looks like.
Edit to config.toml
and set the baseURL
to the custom DNS domain that you will be using.
Now deploy your site:
export OPENFAAS_URL="" # Set when installing OpenFaaS
faas-cli up -f example-site.yml
Create the DNS A record for your sub-domain
I’m using my personal domain matiaspan.dev
that I registered at namecheap.com. I can now create sub-domains for each function or website I deploy with OpenFaaS.
If you are familiar with DNS, then create an A record for the IP address of the LoadBalancer created for your Nginx IngressController:
Find its EXTERNAL-IP
:
kubectl get svc -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginxingress-nginx-ingress-controller LoadBalancer 10.245.116.147 159.89.221.240 80:31113/TCP,443:30596/TCP 2d21h
You can see that my external IP address is: 159.89.221.240
.
If you are getting started with DNS management I recommend DigitalOcean which is free and easy to use. Here is how I use the CLI to create the record for my-site.matiaspan.dev
:
doctl compute domain create my-site.matiaspan.dev --ip-address 159.89.221.240
Domain TTL
my-site.matiaspan.dev 0
And this is how the UI looks like:
Map the custom domain with a FunctionIngress
Now it’s time to map our Hugo blog to the custom domain above. We can do that with a FunctionIngress
record, just like Alex showed in his previous blog post.
Save the following in a YAML file example-site-fni.yml
:
apiVersion: openfaas.com/v1alpha2
kind: FunctionIngress
metadata:
name: example-site-tls
namespace: openfaas
spec:
domain: "my-site.matiaspan.dev"
function: "example-site"
ingressType: "nginx"
tls:
enabled: true
issuerRef:
name: "letsencrypt-prod"
kind: "Issuer"
- For the
name
I used a convention of the function’s name plus a suffix of -tls if using TLS. - Edit the
domain
to point as your own DNS A record or sub-domain - For the
issuerRef
, you can use the-staging
or-prod
issuer which you set up earlier using the OpenFaaS docs. If you are not using TLS then remove thetls
section from the file.
Now apply the file with kubectl apply -f example-site-fni.yml
Check out your brand new site!
After creating the FunctionIngress
our IngressOperator will detect the recently created CRD and create a Kubernetes Ingress record. If you are using TLS this ingress will be decorated with annotations that the cert-manager then detects and creates the TLS certificate for your site.
Check the ingress record:
kubectl get ingress -n openfaas
NAME HOSTS ADDRESS PORTS AGE
example-site-tls my-site.matiaspan.dev 165.22.164.208 80, 443 56s
Now the certificate:
kubectl get cert -n openfaas
NAME READY SECRET AGE
example-site-tls-cert True example-site-tls-cert 89s
If you modify or delete the FunctionIngress
then the certificate and the Ingress record will be updated too.
Navigate to your domain and check out your new site:
You can now repeat the above for any other static-sites, microservices, functions, or endpoints that you want to deploy.
CI/CD (optional)
- Continuous Integration - the act of building whatever is pushed into our Git repositories, continuously to become aware of any integration problems or regression in tests. This results in artifacts such as Docker images.
- Continuous Delivery - the process of deploying new artifacts to production as soon as they become available.
Some tools combine both CI/CD into one pipeline. The classic example is Heroku which enables a “git-based” workflow, or “GitOps”. A more modern alternative is Netlify, which is very popular with those hosting static sites such as their documentation or blogs.
I’m going to go over two different ways you can get this same experience using OpenFaaS Cloud or OpenFaaS combined with GitLab.
CI/CD with OpenFaaS Cloud
OpenFaaS Cloud offers you an integrated and automated experience, with a single push you can have your site deployed in seconds. You’ll also get logs and metrics linked directly to your commit.
If this sounds interesting to you check out the “one-click” bootstrap tool to install OpenFaaS Cloud in less than 100 seconds or apply for an account on The Community Cluster for free shared access.
In OpenFaaS Cloud by default we limit the templates that the users can use. This means we need to add the Hugo template before being able to deploy a function. To do this, edit the git-tar
deployment in the openfaas-fn
namespace and add the Hugo template URL https://github.com/matipan/openfaas-hugo-template
to the custom_templates
environment variable.
Create a repository on GitHub (or your self hosted GitLab) called example-site
. Add this repository to your GitHub app or add the openfaas-cloud
tag if you are using GitLab.
Since we don’t do recursive clones in OpenFaaS Cloud you will need to clone your site’s theme instead of adding it as a submodule. Once you’ve done that commit and push your changes.
After a while, if you head over to your personal dashboard(find it at system.<your domain>/dashboard/<username>
) you will see your new function deployed.
Edit the FunctionIngress
so that it points to our brand new function kubectl edit -n openfaas functioningresses.openfaas.com example-site
:
apiVersion: openfaas.com/v1alpha2
kind: FunctionIngress
metadata:
name: example-site-tls
namespace: openfaas
spec:
domain: "my-site.matiaspan.dev"
function: "<username>-example-site"
ingressType: "nginx"
tls:
enabled: true
issuerRef:
name: "letsencrypt-prod"
kind: "Issuer"
- You need to update the
function
to match with the new one. OpenFaaS Cloud deploys function using the following convention:<username>-<function name>
. If you are unsure about the name you can find it withfaas-cli list
.
We won’t be using our previous function anymore so remove it:
faas-cli remove example-site
Try creating a new blog post with:
hugo new posts/my-first-post.md
Remember to run that command inside the folder of your Hugo site, not the root folder of the project.
Commit and push your changes again, after OpenFaaS Cloud does its thing you will be able to see your new changes deployed.
CI/CD with GitLab.com
There are two ways to operate GitLab, the first involves installing and hosting it yourself and then you can run as many builds as you have capacity for. The second option is to use GitLab.com, the hosted SaaS product which is similar to GitHub.com.
GitLab.com offers 2000 hours per month to use for free automated builds. We can take advantage of the Shared Runners to build Docker images for OpenFaaS.
Save the following content from OpenFaaS’s official documentation to a YAML file in the root folder of the repository .gitlab-ci.yml
:
image: docker:stable
stages:
- build
services:
- docker:dind
before_script:
- apk add --no-cache git
- if [ -f "./faas-cli" ] ; then cp ./faas-cli /usr/local/bin/faas-cli || 0 ; fi
- if [ ! -f "/usr/local/bin/faas-cli" ] ; then apk add --no-cache curl git && curl -sSL cli.openfaas.com | sh && chmod +x /usr/local/bin/faas-cli && /usr/local/bin/faas-cli template pull && cp /usr/local/bin/faas-cli ./faas-cli ; fi
build:
stage: build
script:
- git submodule init && git submodule update
# Build Docker image
- /usr/local/bin/faas-cli template pull https://github.com/matipan/openfaas-hugo-template
- /usr/local/bin/faas-cli build --tag=sha -f example-site.yml
# Login & Push Docker image to private repo
- echo -n "$CI_DOCKER_LOGIN" | docker login --username $(echo -n "$CI_DOCKER_USERNAME") --password-stdin
- /usr/local/bin/faas-cli push --tag=sha -f example-site.yml
- echo -n "$CI_OF_PWD" | /usr/local/bin/faas-cli login --gateway $(echo -n "$CI_OF_GATEWAY") --username admin --password-stdin
# Deploy function from private repo
- /usr/local/bin/faas-cli deploy --tag=sha -f example-site.yml
only:
- master
Go to your project’s CI/CD page and set the following variables:
CI_DOCKER_LOGIN
: the password for docker hubCI_DOCKER_USERNAME
: your docker usernameCI_OF_PWD
: the password for your OpenFaaS gatewayCI_OF_GATEWAY
: the URL for your OpenFaaS gateway
Commit and push your changes and see how your function gets automatically built and deployed!
Wrapping up
Using a mixture of the new Hugo template, the IngressOperator and either OpenFaaS Cloud, or GitLab we were able to host a static site with TLS and CI/CD in a very short period of time. You can use this to host any static site such as documentation, blog posts, and landing pages for websites.
I chose Hugo for today’s blog post but you could create another template using your favourite tooling. When we were chatting about this blog post in the OpenFaaS Slack Community, someone suggested writing a template for GatsbyJS. We would love to see that happen, and if you want to have a go, you can just fork my Hugo template and update it.
Did you know that you can run any Docker image on OpenFaaS as long as it follows the workload contract? This makes OpenFaaS one of the easiest ways to run any containerised workload on Kubernetes.
Do you want to deploy any of the following to Kubernetes?
— OpenFaaS (@openfaas) August 16, 2019
💻 Static webpages
💻 Microservices
💻 Blogs - Ghost/Wordpress
💻 CLIs
💻 Functions
Get productive as quick as possible with @OpenFaaS on your own laptop or your favourite Kubernetes engine. https://t.co/bcD7493eUD pic.twitter.com/2zZSeYRFbS
Connect with us
Connect with us to discuss this blog post, or to share what you’re building with OpenFaaS.
- Join OpenFaaS Slack community
- Follow @OpenFaaS on Twitter
You may also like
- How to build a Serverless Single Page App
- k3sup (“ketchup”) - the fastest way to create local, remote, or edge clusters with Kubernetes.