Containers#

Containers are mini–Virtual Machines with the purpose of packaging up a single piece of software in a highly reproducible, reliable way.

A container allows anyone to package up software into a miniature virtual machine that can be executed by a container orchestrator. Hence, the name “container” (as it contains some software).

Containers, by design, are intended to house only a single piece of software as a matter of standard practice. So you don’t load it up with several applications and then boot it up like a VM would. Containers follow the single role of responsibility pattern. You install one piece of software, and when a container is started, it runs that software.

Software can run in the container completely isolated from the world with no input or output. Containers allow for exporting things like network ports and/or filesystem volumes. This allows data to be written in/out of the container freely.

XBE Container Usage#

Xsolla Backend is a micro-service-based architecture. This means that each component is housed in its own container and executes independently of one another. The _services repos represent each component. They are web server applications that are packaged up into a container to be run as a whole to make up the entire Xsolla Backend platform.

For example, the Identity component, which consists of the account_services web server, has its own container, as does social_services, session_services, telemetry_services, and so on. Even the databases powering Xsolla Backend run in their own containers, such as mongodb and redis.

You may be asking yourself the question: If I have 30 containers all running their own web server, how do I as a consumer of those servers access them? If I want to use the account_services and then the session_services container, how do I access them?

Do I need to know each container’s port numbers for their web servers? How do I map that by the client? Will it require managing n URLs for n services? Also, what happens when I use up all the container’s resources? How do I expand capacity for my containers?

That sounds awful! HELP! Enter Kubernetes >>


Kubernetes (k8s)#

Kubernetes is as container orchestrator that provides built-in service discovery, load balancing, and auto-scaling technology. Its job is to solve these problems.

First, Kubernetes solves the scaling problem by organizing multiple containers of the exact same type (container image) into groups called Deployments. Organizing containers into Deployments allows for starting with one container of a given image, and then seamlessly adding more copies of the same container as usage increases, and you require more capacity. This is huge - as it makes scaling the web server elastic in a very dynamic way.

Deployments also have the benefit of seamless upgrades and downgrades.

Instead of tearing down the server, updating the software in place, and then starting it back up, as was required in Bare-Metal Hosting and Virtual Machine-based environments, with Kubernetes you can deploy out a new version of a container alongside the current one. Then, when the new container shows signs of life, you can take the old container down.

Incoming traffic can then be seamlessly routed to the correct container and load balanced across all copies of the container.

Ingress#

In one example, we may have a bunch of containers. Each container exposes its internal web server and has a port assigned to it - as any good VM would. But how do I actually route traffic to it?

Ingress. Kubernetes offers what is called an Ingress Controller. The Ingress Controller’s purpose is to coordinate incoming traffic and automatically route that traffic to the appropriate Deployment/Container.

There are many ways this routing can happen. Routing could occur through raw port numbers, essentially directly exposing a port number mapped to an individual container. Or, routing can use HTTP to intelligently map URLs of a domain name.

In our case, we use the HTTP method.

Route Mapping#

When we deploy Xsolla Backend, we define a set of Ingress rules that tell the Ingress Controller how to route HTTP requests to each container using URL patterns.

So for example, to route to the account_services containers we configure the following URL patterns:

/users
/roles
/organizations
/usersecrets

and so on.

The Ingress Controller will receive the incoming HTTP request, analyze the path, and then look for matching rules like the ones above. When a matching rule is found, it then routes the request to the Deployment/Container matching the rule.

The net effect then is automatic service discovery and routing all-in-one. We can define one domain and be able to seamlessly route every request to every container without having to worry about port assignments, locations, etc., across the entire Deployment.

Route Naming#

We adopted a more natural, human-readable, pathing scheme that is designed to be more intuitive to end-users adopting our framework.

The hard work of how pathing is managed is done internally, and the Client SDK provides direct access to our unified API.

Load Balancing#

As the number of containers scale up for a given Deployment, traffic can be load balanced between each serving container. The way in which that traffic is load balanced can also be configured so that we can control how load balancing is done.

For example, when we have a user session, we can ensure that subsequent requests by the same client the load balancer will route traffic to the same container each time so that we can benefit from caching.


Bring Your Own Container#

BYOC is the terminology for when a customer has a customer container they want to deploy alongside our built-in collection of services.

We’ve designed our deployment tools (helm_charts in Gitlab) to be flexible and dynamic.

Any container can be deployed as part of the whole platform. Ingress Rules can be easily defined, so that traffic can be directed to that new container the same way all other traffic is routed.

By doing this, we make the entire development process easy for developers to define all new REST API routes that their custom container will handle.