The second post about Headr. This time I talk about the architecture design of Headr.
In my Last post I mentioned Headr for the first time, till now I have made great deal of progress and also magnificent changes comparing to the intial idea. So I figure it is time for another post.
The picture above shows the current implemented architecture of Headr. Every part on the picture is implemented more or less. I will introduce each of them here.
The whole backend consists of 3 main microservices (
contentmgr), 2 helpers (
k8s-helper) and 1 api gateway (
apigateway). Third party services including RabbitMQ as the message queue and PostgresSQL as the database server. The backend is deployed in a Kubernetes cluster (currently it’s just minikube on my local machine). A persistent volume is mounted on the Kubernetes cluster, and is used by the
repoctl microservice, the
hugo-helper and all the user site servers (I will talk about this part in details later). I wrote the fontend Single Page Application (SPA) with Vue.js. Auth0 is a third party authentication provider, I use it to save my work of user management and their authentication/authorization (I should write another post about this part!!!).
Given the big picture, now I explain how Headr works.
Hugo-helper and Caddy server
A headr blog site is still a static (please read my last post for the definition) website generated by hugo. Each user’s site is generated by
hugo-helper, stored in the persistent volume, and served by a container (which also connects to the persistent volume for sure) running a Caddy server. From the name you can tell that
hugo-helper is a hugo related program, in fact, its image is the only one with hugo binary installed.
hugo-helper is a consumer of the RabbitMQ server, it listens to the message queue and runs the hugo binary to answer each hugo related (like
hugo new, or just
hugo for generating) request. There can be multiple copies of
hugo-helper running in the cluster at the same time thanks to the convenient scaling ability of Kubernetes, each connects to both the persistent volume and the RabbitMQ server.
The Caddy server which serves the user’s static site, is also managed by the Kubernetes cluster with a Pod (as it is defined in Kubernetes semantics), and served to the outside of the cluster by a Kubernetes Service. Since Kubernetes has a service discovery mechanism (I chose CoreDNS instead of the default cube-dns, I should write another post about this part!!!) which enables Services to be discovered by the outside.
If I managed to explain myself successfully in the last two paragraphs, it may be clear now how Headr is serving its users’ static websites. Now it’s time we talk about management. Among all three,
repoctl is the only management microservice that is connected to the persistent volume. The static resource (both the site’s
public folders, as well as themes and configurations) of each user composes his repository, which is a directory identified by the user’s identity and saved on the persistent volume.
repoctl is the service which manages all these repositories. It deals mainly with file operations such as read/write, delete/create etc., while it’s also a publisher to the message queue. Although hugo claims itself to be the world’s fastest static generator, it should still be costly if the
repoctl microservice tries to generate each site each time on itself synchronously. Therefore, each time some files are changed, the
repoctl microservice will publish a
SiteUpdatedEvent to the message queue after what should be done by itself is done. The rest is left to the listener of that event, i.e., the
sitemgr and k8s-helper
sitemgr, short for site manager, is responsible for accepting site create/delete/configure requests and make corresponding responses. It talks to
repoctl, so as to manage site related files such as configurations.
sitemgr connects to the database to store site related information such as the site’s name, corresponding user ID, the name of currently using theme etc. But it does not do the actual site creation. As I’ve said earlier, each user’s site is served by a Caddy server running in a Kubernetes Pod and exposed by a Kubernetes Service. Thus, I need a
k8s-helper to talk to the Kubernetes master to actually create the Pod (or Deployment to be exact) and the Service for the site. The
k8s-helper, just like the
hug0-helper, is nothing but just a program listenning to events on the message queue. Once received an event sent by the
k8s-helper talks to the cluster master and do its job. Since it’s running in an in-cluster Pod, the authentication process is relatively simple (another post???).
contentmgr manages contents. A blog sites must have contents and they must be managed, since first, hugo requires a special format of the content to be properly generated and displayed; second, users need to write, edit or delete their blogs.
contentmgr also connects to
repoctl to manage static files, only that it concerns merely the blog posts (not configurations). A database connection is also required since metadata of posts need stored for query.
The apigateway gathers the gRPC interfaces of
sitemgr together and expose them to the outside through a http RESTish API. It also acts as the gate which authenticate requests and blocks the illegal ones, by accessing service provided by Auth0.
Apart from the introduction home page, the main component of the Headr frontend is the Headr dashboard. The Headr dashboard is the admin panel where users can manage their sites, their posts, configurations and everything else. The frontend needs a login action first, in which it redirects the user to the login page of Auth0, gaining authorization of user information after the login. An
access_token is obtained during the login process so that the frontend can visit the Headr backend (the
apigateway) on the user’s behalf, without being denied of access.
And that is a coarse and brief introduction to the Headr architeture. There will be more components, but I guess what exists now will exists for at least a while. There will be more posts on details, too. Only when I am not that lazy. See you soon.