To learn how to deploy a blogging website using Hugo on Kubernetes
Hugo is one of the most popular open-source Static Site Generator (SSG) frameworks written in Go. It allows developers to build fast HTML websites by combining content and templates. Visit the official documentation of Hugo to know more about it.
In this blog, we will see how to deploy a simple HTML website using Hugo on Kubernetes. However, to understand the whole process clearly, we will first set up Hugo’s website on a simple VM and then try to set up in a Docker container. Lastly we deploy the Hugo site on Kubernetes. After setting up, we will also try to make some changes to the website and ensure that the website is updated.
Hugo on VM
In this step, we will see how to deploy Hugo Extended version on a simple VM. The following steps should also work in the desktop environment as well.
Install and extract the tarball
curl -L https://github.com/gohugoio/hugo/releases/download/v0.115.4/hugo_extended_0.115.4_Linux-64bit.tar.gz -o hugo_extended_0.115.4_Linux-64bit.tar.gz && tar -xzf hugo_extended_0.115.4_Linux-64bit.tar.gz
Make the binary executable and move inside /usr/local/bin/
chmod +x hugo && mv hugo /usr/local/bin/hugo
Our installation is done. Confirm by checking the version by running command `hugo version`.
hugo version
Now we will proceed with setting up a simple website.
Create a new Hugo site
Run the following command to bring a Hugo site with the name “hugosite”. Note that you can give any desired name to your site.
hugo new site hugosite
Initializing the Git repository
cd hugosite && git init
For the demo purpose, we will be using one of Hugo’s beautiful themes, Anake. There are many ways to set up Hugo’s theme. However, we will use a simple approach of adding a theme as “Git Submodule”. Perform the following steps to add “Anake” theme as Git submodule to our website repository and set the theme to configuration file.
git submodule add https://github.com/theNewDynamic/gohugo-theme-ananke.git themes/ananke && echo theme = \"ananke\" >> hugo.toml
Create your first blog post
It is quite easy to create a blog in Hugo. The `hugo new` command will create a blog post with a default template which we can modify as per our need. Run the following command to get started with your first blog post.
hugo new posts/my-first-post.md
Now, edit the post via `vim` editor or with an IDE provided in this lab which can be opened after clicking on the button “Open IDE” on the right hand side of the page.
--- title: "My First Post" date: 2019-03-26T08:47:11+01:00 draft: true --- This is my first experience with Hugo and I am loving it!
Now, start the Hugo Server. Make sure that you are inside the hugosite
directory where hugo.toml
file is present.
hugo server -D -p 30000 --bind 0.0.0.0
By default Hugo application binds with the localhost (127.0.0.1), but to access this application from the internet we have to bind the Hugo application to all network interfaces i.e. 0.0.0.0.
Once the server is running, you can access your website through the `app-port-30000` URL on the right side of the page under the `lab-urls` section.
NOTE: Anake is one simple Hugo theme. Hugo has many different themes for the website which you can find in the documentation of Hugo.
Hugo on Docker container
In the previous section, we have seen how to deploy a Hugo based website on a VM, now in this section, we will try to deploy the same website inside a Docker container.
For that, we will need to create a Dockerfile as below:
# Dockerfile FROM ubuntu:latest USER root RUN apt update -y RUN apt install wget git ca-certificates -y RUN wget https://github.com/gohugoio/hugo/releases/download/v0.115.4/hugo_extended_0.115.4_Linux-64bit.tar.gz && \ tar -xvzf hugo_extended_0.115.4_Linux-64bit.tar.gz && \ chmod +x hugo && \ mv hugo /usr/local/bin/hugo && \ rm -rf hugo_extended_0.115.4_Linux-64bit.tar.gz CMD ["/usr/local/bin/hugo", "-s", "/root/hugosite", "server", "-D", "--bind", "0.0.0.0", "--port", "30000"]
Following are few key points from the Dockerfile:
- Using
ubuntu:latest
as a base image. - Running a docker container as a ‘root’ user.
- Updating the package lists and installing required packages.
- Installing Hugo from tarball.
- In the last command , we specify an instruction that is to be executed when a Docker container starts. The command will start the Hugo Server in the container at port 30000, and use the content and templates from the /root/hugosite directory to render a website.
Now, build the Docker image from the Dockerfile and start the container by performing following steps:
docker build -t <DOCKERHUB_USERNAME>/ubuntu:hugo /root/.
The above command will create a docker image from the Dockerfile with a tag. Replace the <DOCKERHUB_USERNAME>
with your docker hub username and push the image to the docker registry.
docker run -dit -v /root/hugosite:/root/hugosite -p 30000:30000 --name hugo <DOCKERHUB_USERNAME>/ubuntu:hugo
The other command will create a container with a name hugo
from the Docker image we have created. It will also mount the host directory(/root/hugosite
) to the container directory and map port 30000 of the Hugo server to 30000 of our base VM. We can now use this port of the Base VM along with an IP address to connect to the Hugo Server.
Now you can access the Hugo application through app-port-30000 URL.
After accessing the website remove the container.
docker stop hugo && docker rm hugo
Setting Up Hugo on K8s
So far, we have seen how to deploy the Hugo website on Base VM and Docker containers. In this section we will deploy the website on Kubernetes.
One of the important questions that arises in the Kubernetes environment is “How can we serve the content and templates to the Hugo server that is running inside the pod?”
The solution to this problem is “Persistent Volume (PV)”.
Pod will use PersistentVolumeClaim to request physical storage. We will specify the hostpath as /root/hugosite ( a directory in the host machine) while creating PV . After installing the Hugo in the base VM, we will keep the contents and templates required for the website inside the /root/hugosite directory . By using PVC, pods can access the host directory. This way, the Hugo server which is running inside the container can access the content as well as templates and will render the web pages.
Create hostPath PersistentVolume
hostPath PersistentVolume uses a file or directory on the Node to emulate network-attached storage.
# pv.yaml apiVersion: v1 kind: PersistentVolume metadata: name: task-pv-volume labels: type: local spec: storageClassName: manual capacity: storage: 1Gi accessModes: - ReadWriteOnce hostPath: path: "/root/hugosite"
In the above yaml file, we have specified the hostPath as “/root/hugosite” which is the directory with contents and templates on the Base VM that is required by the Hugo Server to render a full HTML website.
Now, apply the above yaml file to create a PV and also verify in the list of persistent volumes.
kubectl apply -f pv.yaml && kubectl get pv
Create a PersistentVolumeClaim
The yaml file for PersistentVolumeClaim looks like below:
# pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: task-pv-claim spec: storageClassName: manual accessModes: - ReadWriteOnce resources: requests: storage: 1Gi
Two key points about above yaml file are:
- It requests a volume of at 1 gibibytes that can provide read-write access for at least one Node.
- This PVC is using the PV “task-pv” that we have created in the previous step.
Now, apply the above yaml file to create a PVC and also verify in the list of persistent volume claims.
kubectl apply -f pvc.yaml && kubectl get pvc
Creating a Deployment
The following is a yaml file for the deployment of our website.
#deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: hugo-app labels: app: hugo-app spec: replicas: 1 selector: matchLabels: app: hugo-app template: metadata: labels: app: hugo-app spec: volumes: - name: task-pv-storage persistentVolumeClaim: claimName: task-pv-claim containers: - name: hugo-app image: teamcloudyuga/ubuntu:hugo volumeMounts: - mountPath: "/root/hugosite" name: task-pv-storage
Apply the above yaml file to create a deployment and verify it in the list of deployments.
kubectl apply -f deploy.yaml && kubectl get deployment
Now, wait for some time for the deployment to finish.
Create Service
Once the deployment of the app is ready, create a service yaml file. In this file, we are asking the cluster load balancer that we want your service to expose to the public world so everyone can see the application running. For this use case we take the help of a NodePort. It exposes the service on each Node’s IP.
# svc.yaml apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: hugo-app type: NodePort ports: - protocol: TCP port: 30000 targetPort: 30000 nodePort: 30000
Apply the above service yaml file to create a service and verify it in the list of services.
kubectl apply -f svc.yaml && kubectl get svc
Now, access the Hugo application through app-port-30000 URL. You will see the exact same web page as in previous cases while deploying in a base VM or Docker container.
Updating the Hugo Website
In this section, we will see how to update our website content. Create another markdown file with the name “my-second-post.md”.
cd /root/hugosite/content/posts/
Now add the new content to “my-second-post.md” file.
vim my-second-post.md
Copy and paste the below content:
--- title: "My Second Post" date: 2019-03-26T08:47:11+01:00 draft: true --- This is my second post in hugo. It's exciting!
After copying the code use Shift + Insert to paste the code in the terminal. Lastly, use Ctrl + D to save the markdown file.
Verify the contents of markdown file as:
cat my-second-post.md
Now, if you open app-port-30000 URL, you will see the newer version of Hugo website with two blog posts.
Conclusion
In this blog we have seen how to deploy websites using Hugo on Base VM, on Docker and on Kubernetes. We made changes in websites using markdown files and see the newer version of the website is being displayed.