A Practical Guide to Building Multi-Service Applications with Helm
A Quick Recap and Moving Forward
In a previous chapter, we built a simple Todo application that uses SQLite for storage. While SQLite is easy to set up, it's not what will introduce you to managing real-world applications with different components. In this chapter, we will be concerned with more challenging tasks:
- Adapt our application code to use PostgreSQL instead of SQLite to introduce a database dependency.
- Build and push the updated application image to our Harbor registry.
- Create a Helm chart that deploys both our application and a PostgreSQL database as a dependency.
- Deploy the application using our new Helm chart.
- Verify the deployment and run Helm tests.
- Discuss best practices throughout the process.
Our new application will support the same functionality as before (creating and listing tasks):
GET /tasks—> Returns all tasks as JSON.POST /tasks—> Creates a new task.
We will introduce a third endpoint for health checks:
GET /health—> Returns a simple health status for Kubernetes probes
Let's move on to the implementation. Start by creating a new directory for our updated application:
mkdir -p $HOME/todowithdb-app/app
This is the new application code that:
- Uses
psycopg2to connect to PostgreSQL - Reads database connection details from environment variables
- Includes a health check endpoint
- Initializes the database schema on startup
- Handles database connections simply (no pooling for code brevity)
- Runs on port 5000 of the container.
cat << 'EOF' > $HOME/todowithdb-app/app/app.py
import os
from flask import Flask, jsonify, request
import psycopg2
from psycopg2.extras import RealDictCursor
app = Flask(__name__)
# Database configuration from environment variables
# These will be injected by Kubernetes from our Helm values
DB_HOST = os.environ.get("DB_HOST", "localhost")
DB_PORT = int(os.environ.get("DB_PORT", "5432"))
DB_NAME = os.environ.get("DB_NAME", "todo_db")
DB_USER = os.environ.get("DB_USER", "todo_user")
DB_PASSWORD = os.environ.get("DB_PASSWORD", "todo_password")
def get_db_connection():
"""
Create a new PostgreSQL connection.
In production, you might use a connection pool or PgBouncer,
but this simple approach works well for learning.
"""
conn = psycopg2.connect(
host=DB_HOST,
port=DB_PORT,
dbname=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
)
return conn
def init_db():
"""
Initialize the database schema.
Creates the tasks table if it doesn't exist.
In production, use a migration tool like Alembic.
"""
conn = get_db_connection()
try:
with conn.cursor() as cursor:
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS tasks (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL
);
"""
)
conn.commit()
finally:
conn.close()
@app.route("/health", methods=["GET"])
def health_check():
"""Simple health check endpoint for Kubernetes probes."""
return jsonify({"status": "healthy"})
@app.route("/tasks", methods=["GET"])
def get_tasks():
"""Return all tasks as JSON."""
connHelm in Practice
Designing, Deploying, and Operating Kubernetes Applications at ScaleEnroll now to unlock current content and receive all future updates for free. Your purchase supports the author and fuels the creation of more exciting content. Act fast, as the price will rise as the course nears completion!
Hurry! This limited time offer ends in:
To redeem this offer, copy the coupon code below and apply it at checkout:
