Published 12 jan. 2025

Get Your Apps Cloud Ready

A Step-by-Step Migration Guide

#kubernetes
#app
Cover image for Get Your Apps Cloud Ready

Cloud environments like Kubernetes demand specific application requirements. Let's explore how to transform traditional applications into cloud-ready workloads.

The Naive Approach

So, you’ve decided to move your applications to the cloud. Great! You create a simple Dockerfile, build and image and deploy it to Kubernetes, done! That wasn’t so hard, why do we need a guide for this?

Well, unfortunately it’s not that simple. Most traditional applications make all kind of assumptions about their runtime that don’t hold up in cloud-native environments like Kubernetes. Let’s do this properly, one step at a time.

Building an Image

The first step is to create a container image which contains your application and its dependencies. Notice how I’m not saying “docker image” here? In 2025 there are many ways to build a container image, such as ko, Bazel or jib.

One thing to keep in mind is that your application ideally should have a single process. When a container crashes, Kubernetes needs to know unambiguously whether to restart it - with multiple processes, failure scenarios become complex and unpredictable.

Hidden Restarts

We had an incident once where a NoSQL database was randomly unresponsive every couple of minutes. It turned out that the database was being run with a process supervisor inside the container.

When the database process had issues, instead of failing cleanly and generating proper Kubernetes events, the supervisor would silently restart it. This masked the real problem and made debugging much harder – we had to dig through internal container logs just to understand what was happening.

Stateless Design

Most applications need to store some kind of state, whether it’s media files, user sessions, or database entries. In Kubernetes, pods are ephemeral and can be rescheduled across nodes at any time. It’s best to externalize state - user sessions in Redis, files in object storage, and databases as separate services.

Stateless Architecture

Stateless Architecture

It is possible to run stateful applications in Kubernetes, but it requires additional complexity and comes with a cost. Keeping your applications mostly stateless makes scaling, updates, and recovery much easier.

Configuration Management

The Twelve-Factor App methodology teaches us to store configuration in the environment, not in the code. This separation is crucial for cloud-native applications. Your app should be able to run in any environment without code changes or rebuilds - whether that’s local development, staging, or production.

Our recommendation is to support all strategies, whether it’s command-line arguments, environment variables, or configuration files. Ideally by using a configuration library that can read from multiple sources.

Application Lifecycle

How does your application start up and shut down? Many applications need initialization tasks like database migrations or cache warming. A common strategy is to use init containers to handle these tasks before your main application starts.

Pod lifecycle (simplified)

Pod lifecycle (simplified)

As mentioned before, your pods can be rescheduled at any time. Make sure your application can handle this gracefully - for example, by completing in-flight requests, closing database connections, and freeing up resources. Your application should listen for SIGTERM signals and initiate a clean shutdown when received.

Health Checks and Probes

Your application should be able to tell Kubernetes whether it’s healthy and ready to serve traffic. These are called readiness and liveness probes. Readiness probes exist to prevent traffic from being sent to a pod that is not ready yet, while liveness probes are used to restart a pod that is in a broken state.

Implementing these can be as simple as exposing two HTTP endpoints (e.g. /livez and /readyz) that return a 200 status code when the application is ready and/or healthy. When implemented properly both will help to prevent (or at least decrease) downtime.

Putting It All Together

Moving applications to Kubernetes requires careful consideration of cloud-native principles. Each step in this guide addresses specific challenges:

  • Container building focuses on simplicity and predictability
  • Stateless design enables scalability and resilience
  • Configuration management ensures flexibility across environments
  • Lifecycle management handles the dynamic nature of cloud environments
  • Health checks maintain reliability

Need help with your cloud journey? Let’s connect and discuss how we can assist with your application modernization.

Stay up to date