Blog
Observability
March 9, 2024
This is the second part of the kubernetes(k8s) series where we will focus on deploying three applications inside a k8s cluster namely a Postgres database, a Go API server, and a Nuxt frontend single-page-application (SPA). Along the way, we will learn about typical k8s objects including Deployment, Service, PersistentVolume, Secret, but also related stuff like port-forwarding, authorization, building container images, and many others. Just like the first part, this post is also going to be elaborate containing many details because k8s is a behemoth, and I think it is a good idea to look a little bit under the hood to get a better understanding of how all the pieces work together. What we will do is to simply break it down into smaller parts, and you will see that using a handful of yaml files were enough to easily deploy applications on k8s. Circling back to the applications, we are going to deploy three items: A frontend written in Nuxt 3 that you will see and interact with A database using Postgres that will store our data A backend api server written in Go The frontend will be a simple interface where it will list the latest ten UUID items which are stored from a database. We will add an ‘add’ button where a new random UUID is generated and stored into the database. Once that is done, it will do another api call to retrieve the latest UUIDs.Deploy Applications in Kubernetes
September 23, 2023
Why, and when do you use a universally unique identifier (UUID) as a primary key in a database? They are 32 hexadecimal characters consisting of 16 octets, thus 16*8 = 128 bits. It has an 8-4-4-4-12 format length separated by dashes where each section corresponds to a specific way of encoding. They look something like this Figure 1: An example of a UUIDUUID For Database Primary Key
May 1, 2023
What are UUID?
c50ab83b-b632-419d-91b9-626895e705ec
How do you guarantee …in such a way that they are an atomic operation — both happens or nothing at all. To see how this problem is difficult to solve, let us consider the following sketch. We have a mail delivery at the fork of two roads and a copy needs to be sent to each of the queue and the database. While sending the messages is easy, what is hard is how can we confirm if the messages reached their destinations. Both queue and database cannot see each other. Sending the message to the database is easy because we can wrap in a transaction. If there are any problems, the transaction will be rolled back.
Figure 1: How can the sender guarantee both are deliveredTransactional Outbox Pattern
April 26, 2023
Prologue
mail_deliveries
table; and
Kubernetes… everyone’s favourite container orchestration. Love or hate it, it is fast becoming
the de-facto solution for managing containers at scale. This blog post will go through step by
step on how we can create a kubernetes (k8s) cluster on a local machine from the first command
to all nodes being ready in about 12 minutes. This post assumes the readers to have basic knowledge
of k8s. If you haven’t heard of k8s, you should read this. Before we jump into the guide, I will outline the tools and the kind of k8s cluster we are
going to create. We know that creating a k8s cluster is complex. The plan is to create a series of blog posts to create a production-grade k8s cluster. The first
step is to create an HA cluster. This is important because we do not want to have any single point
of failure in our architecture. At the end of this post, we will deploy a vanilla nginx web server
to demonstrate that we can successfully access its pod with the correct port number. Second part of the series is going to be deploying more complex applications. A popular architecture
for a web application is having a single-page application (SPA) frontend talking to an api backend
on a specific port. We will see how we can configure both k8s and our applications to achieve this.
We will throw in observability which includes prometheus, jaeger, and loki as well as
grafana for visualisation. The final part is going to be about ingress controller, so we can access our application in local
machine from the internet. We will need a domain name for this, and we will configure the DNS to
point to our k8s cluster and see how our virtual IP (floating IP) from This guide on the first part does not come out of vacuum. I heavily referred to this video
https://youtu.be/SueeqeioyKY and highly available (HA) section of k8s at
https://github.com/kubernetes/kubeadm/blob/main/docs/ha-considerations.md#options-for-software-load-balancing.
Full list of reference is at https://github.com/gmhafiz/k8s-ha#reference. Why is k8s short for kubernetes? There are 8 letters in between of ‘k’ and ’s’ 😎
If you do not want to read the rest, tldr:Highly Available Kubernetes Cluster
December 11, 2022
kubeadm
has made the process
easier but there are still steps that feel like a maze. Do you not need docker
with k8s
anymore? What are the various container runtimes? Why are there so many choices for pod
networking? What to do with ‘cgroup driver’? This blog post intends to go through each step and be a
complete guide into creating a highly available kubernetes cluster.keepalived
plays into this.
To make our cluster more production-grade, we also look into backup and restore etcd
database.
git clone https://github.com/gmhafiz/k8s-ha
cd k8s-ha
make install
make up
make cluster
We look over how normal and abnormal usage of these libraries and ORM helps protect (or not!) against
SQL injection. Remember that we accept various query parameters when listing a resource, ‘users`
records in particular. The result is limited to a maximum of 30 records. We are going to try to list
all records in one resource by formulating sneaking a malicious query parameter in the request. We know that This malicious URL is not the only way of trying to inject SQL queries. Others include using
The post is incomplete without the addition of this comic:
(from https://xkcd.com/327/) There are two things we want to see how these libraries and ORMs behave. The first being if
malicious sql gets injected, and second how do they behave and the output that gets returned.Golang Database Library Orm Example Sql Injection
December 8, 2022
http GET http://localhost:3080/api/sqlx/user?favourite_colour=blue;select%20*%20FROM%20users;,--;
favourite_colour
is a valid and accepted query parameter. We are going to see if
appending the url starting with an apostrophe, then a semicolon ;
, followed by a select all
(select * FROM users
), closing off with another semicolon, and an sql comment (--
) will work.
select%20*%20FROM%20users;
select everything in users
table. %20
is a space
AND 1=1
which means the query is always true, and the dreaded ';DROP DATABASE users--
which
mean adding a second SQL query to drop users
table. You can try out with different urls by running
the examples in the provided example/rest.http file.
Typically, to make a transaction, we first start by calling Following the pattern from the Go blog at
https://go.dev/doc/database/execute-transactions,
we If you carefully analyse the pattern above, you will notice that a rollback will happen after a commit.
Fortunately, if that transaction is already done, Go will ignore the rollback and does not make
another round trip to the database - While knowing the pattern above is important, we want to know a couple of things when it comes to
creating a transaction across multiple SQL queries. It would be nice if we can just reuse existing methods because why should we repeat ourselves if
the operations are the same, but we needed a transaction over them. Understanding isolation level is essential when deciding which level we want our transaction to run
on. Isolation is the ‘I’ in ACID. There are four isolation
levels in SQL. In reality, there are only three in postgres as Read Uncommitted behaves like
Read Committed. You can read more about isolation level
in further details. The default in postgres is read committed.
This means that a In this post, we will see how these libraries and ORMs fare with creating and using a transaction as
well as changing an isolation level.Golang Database Library Orm Example Transaction
August 15, 2022
BeginTx()
method
in database/sql
package which returns a Tx
transaction struct.defer
the rollback operation immediately after starting a transaction and ends with a commit.
This ensures that a rollback is always called even if any error or panic happens in the function.Rollback()
will be ignored if the transaction has been
committed as seen from the source code,// rollback aborts the transaction and optionally forces the pool to discard
// the connection.
func (tx *Tx) rollback(discardConn bool) error {
if !atomic.CompareAndSwapInt32(&tx.done, 0, 1) {
return ErrTxDone
}
...
SELECT
query will only see data committed before the query began. In other
words, it will not see any in-progress uncommitted changes.
In this post, we will look at selecting records based on a column with multiple conditions. To keep
it simple, we will select users whose last names are ‘Donovan’ and ‘Campbell’. In this post, we will look at how deleting a single record in a table works for these various
libraries and ORMs. In general, this operation is simple because we only care about deleting and checking for an error.Golang Database Library Orm Example - Where IN
July 13, 2022
Often when you are listing a resource, a client may want to perform various filter operations and
sorting. They may want to retrieve all addresses in a specific postcode, or sort the users by
last name followed by first name. A common way of making this request is by passing them using query
parameters in a GET operation in the URL request. For example, For pagination, we can either define In our pagination examples, we simply order by In short, we will look at these three common list operationsGolang Database Library Orm Example Dynamic List
April 23, 2022
GET /api/user?email=john@example.com
only returns user record with john@example.com
as its email. GET /api/users?sort=first_name,asc
sorts users by first name in ascending order. We
can add multiple columns to sort by adding more sort
key: GET /api/users?sort=first_name,asc&sort=email,desc
.offset
and limit
or use the convenience page
key:
GET /api/users?page=2
. Please note that there is a huge penalty LIMIT
and OFFSET
when you are
retrieving records where the offset is in the millions. See Why You Shouldn’t Use OFFSET and LIMIT For Your Pagination.
ORDER BY
also must be added in all LIMIT
and OFFSET
.ID
. This is important to constraint the result rows
into a unique order https://www.postgresql.org/docs/15/queries-limit.html.
Read more >
In this post, we will look at listing users with all their addresses. Both Golang Database Library Orm Example - Many to Many
January 2, 2022
users
and addresses
tables are joined with a pivot table, user_addresses
, which holds the foreign key of each of
users
and address
table primary key.