โŒ

Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Learning Notes #68 โ€“ Buildpacks and Dockerfile

2 February 2025 at 09:32

  1. What is an OCI ?
  2. Does Docker Create OCI Images?
  3. What is a Buildpack ?
  4. Overview of Buildpack Process
  5. Builder: The Image That Executes the Build
    1. Components of a Builder Image
    2. Stack: The Combination of Build and Run Images
  6. Installation and Initial Setups
  7. Basic Build of an Image (Python Project)
    1. Building an image using buildpack
    2. Building an Image using Dockerfile
  8. Unique Benefits of Buildpacks
    1. No Need for a Dockerfile (Auto-Detection)
    2. Automatic Security Updates
    3. Standardized & Reproducible Builds
    4. Extensibility: Custom Buildpacks
  9. Generating SBOM in Buildpacks
    1. a) Using pack CLI to Generate SBOM
    2. b) Generate SBOM in Docker

Last few days, i was exploring on Buildpacks. I am amused at this tool features on reducing the developerโ€™s pain. In this blog i jot down my experience on Buildpacks.

Before going to try Buildpacks, we need to understand what is an OCI ?

What is an OCI ?

An OCI Image (Open Container Initiative Image) is a standard format for container images, defined by the Open Container Initiative (OCI) to ensure interoperability across different container runtimes (Docker, Podman, containerd, etc.).

It consists of,

  1. Manifest โ€“ Metadata describing the image (layers, config, etc.).
  2. Config JSON โ€“ Information about how the container should run (CMD, ENV, etc.).
  3. Filesystem Layers โ€“ The actual file system of the container.

OCI Image Specification ensures that container images built once can run on any OCI-compliant runtime.

Does Docker Create OCI Images?

Yes, Docker creates OCI-compliant images. Since Docker v1.10+, Docker has been aligned with the OCI Image Specification, and all Docker images are OCI-compliant by default.

  • When you build an image with docker build, it follows the OCI Image format.
  • When you push/pull images to registries like Docker Hub, they follow the OCI Image Specification.

However, Docker also supports its legacy Docker Image format, which existed before OCI was introduced. Most modern registries and runtimes (Kubernetes, Podman, containerd) support OCI images natively.

What is a Buildpack ?

A buildpack is a framework for transforming application source code into a runnable image by handling dependencies, compilation, and configuration. Buildpacks are widely used in cloud environments like Heroku, Cloud Foundry, and Kubernetes (via Cloud Native Buildpacks).

Overview of Buildpack Process

The buildpack process consists of two primary phases

  • Detection Phase: Determines if the buildpack should be applied based on the appโ€™s dependencies.
  • Build Phase: Executes the necessary steps to prepare the application for running in a container.

Buildpacks work with a lifecycle manager (e.g., Cloud Native Buildpacksโ€™ lifecycle) that orchestrates the execution of multiple buildpacks in an ordered sequence.

Builder: The Image That Executes the Build

A builder is an image that contains all necessary components to run a buildpack.

Components of a Builder Image

  1. Build Image โ€“ Used during the build phase (includes compilers, dependencies, etc.).
  2. Run Image โ€“ A minimal environment for running the final built application.
  3. Lifecycle โ€“ The core mechanism that executes buildpacks, orchestrates the process, and ensures reproducibility.

Stack: The Combination of Build and Run Images

  • Build Image + Run Image = Stack
  • Build Image: Base OS with tools required for building (e.g., Ubuntu, Alpine).
  • Run Image: Lightweight OS with only the runtime dependencies for execution.

Installation and Initial Setups

Basic Build of an Image (Python Project)

Project Source: https://github.com/syedjaferk/gh_action_docker_build_push_fastapi_app

Building an image using buildpack

Before running these commands, ensure you have Pack CLI (pack) installed.

a) Detect builder suggest

pack builder suggest

b) Build the image

pack build my-app --builder paketobuildpacks/builder:base

c) Run the image locally


docker run -p 8080:8080 my-python-app

Building an Image using Dockerfile

a) Dockerfile


FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .

RUN pip install -r requirements.txt

COPY ./random_id_generator ./random_id_generator
COPY app.py app.py

EXPOSE 8080

CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]

b) Build and Run


docker build -t my-python-app .
docker run -p 8080:8080 my-python-app

Unique Benefits of Buildpacks

No Need for a Dockerfile (Auto-Detection)

Buildpacks automatically detect the language and dependencies, removing the need for Dockerfile.


pack build my-python-app --builder paketobuildpacks/builder:base

It detects Python, installs dependencies, and builds the app into a container. ๐Ÿš€ Docker requires a Dockerfile, which developers must manually configure and maintain.

Automatic Security Updates

Buildpacks automatically patch base images for security vulnerabilities.

If thereโ€™s a CVE in the OS layer, Buildpacks update the base image without rebuilding the app.


pack rebase my-python-app

No need to rebuild! It replaces only the OS layers while keeping the app the same.

Standardized & Reproducible Builds

Ensures consistent images across environments (dev, CI/CD, production). Example: Running the same build locally and on Heroku/Cloud Run,


pack build my-app

Extensibility: Custom Buildpacks

Developers can create custom Buildpacks to add special dependencies.

Example: Adding ffmpeg to a Python buildpack,


pack buildpack package my-custom-python-buildpack --path .

Generating SBOM in Buildpacks

a) Using pack CLI to Generate SBOM

After building an image with pack, run,


pack sbom download my-python-app --output-dir ./sbom
  • This fetches the SBOM for your built image.
  • The SBOM is saved in the ./sbom/ directory.

โœ… Supported formats:

  • SPDX (sbom.spdx.json)
  • CycloneDX (sbom.cdx.json)

b) Generate SBOM in Docker


trivy image --format cyclonedx -o sbom.json my-python-app

Both are helpful in creating images. Its all about the tradeoffs.

Learning Notes #67 โ€“ Build and Push to a Registry (Docker Hub) with GH-Actions

28 January 2025 at 02:30

GitHub Actions is a powerful tool for automating workflows directly in your repository.In this blog, weโ€™ll explore how to efficiently set up GitHub Actions to handle Docker workflows with environments, secrets, and protection rules.

Why Use GitHub Actions for Docker?

My Code base is in Github and i want to tryout gh-actions to build and push images to docker hub seamlessly.

Setting Up GitHub Environments

GitHub Environments let you define settings specific to deployment stages. Hereโ€™s how to configure them:

1. Create an Environment

Go to your GitHub repository and navigate to Settings > Environments. Click New environment, name it (e.g., production), and save.

2. Add Secrets and Variables

Inside the environment settings, click Add secret to store sensitive information like DOCKER_USERNAME and DOCKER_TOKEN.

Use Variables for non-sensitive configuration, such as the Docker image name.

3. Optional: Set Protection Rules

Enforce rules like requiring manual approval before deployments. Restrict deployments to specific branches (e.g., main).

Sample Workflow for Building and Pushing Docker Images

Below is a GitHub Actions workflow for automating the build and push of a Docker image based on a minimal Flask app.

Workflow: .github/workflows/docker-build-push.yml


name: Build and Push Docker Image

on:
  push:
    branches:
      - main  # Trigger workflow on pushes to the `main` branch

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    environment: production  # Specify the environment to use

    steps:
      # Checkout the repository
      - name: Checkout code
        uses: actions/checkout@v3

      # Log in to Docker Hub using environment secrets
      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_TOKEN }}

      # Build the Docker image using an environment variable
      - name: Build Docker image
        env:
          DOCKER_IMAGE_NAME: ${{ vars.DOCKER_IMAGE_NAME }}
        run: |
          docker build -t ${{ secrets.DOCKER_USERNAME }}/$DOCKER_IMAGE_NAME:${{ github.run_id }} .

      # Push the Docker image to Docker Hub
      - name: Push Docker image
        env:
          DOCKER_IMAGE_NAME: ${{ vars.DOCKER_IMAGE_NAME }}
        run: |
          docker push ${{ secrets.DOCKER_USERNAME }}/$DOCKER_IMAGE_NAME:${{ github.run_id }}

To Actions on live: https://github.com/syedjaferk/gh_action_docker_build_push_fastapi_app/actions

SelfHost #2 | BugSink โ€“ An Error Tracking Tool

26 January 2025 at 16:41

I am regular follower of https://selfh.st/ , last week they showcased about BugSink. Bugsink is a tool to track errors in your applications that you can self-host. Itโ€™s easy to install and use, is compatible with the Sentry SDK, and is scalable and reliable.

When an application breaks, finding and fixing the root cause quickly is critical. Hosted error tracking tools often make you trade privacy for convenience, and they can be expensive. On the other hand, self-hosted solutions are an alternative, but they are often a pain to set up and maintain.

What Is Error Tracking?

When code is deployed in production, errors are inevitable. They can arise from a variety of reasons like bugs in the code, network failures, integration mismatches, or even unforeseen user behavior. To ensure smooth operation and user satisfaction, error tracking is essential.

Error tracking involves monitoring and recording errors in your application code, particularly in production environments. A good error tracker doesnโ€™t just log errors; it contextualizes them, offering insights that make troubleshooting straightforward.

Here are the key benefits of error tracking

  • Early Detection: Spot issues before they snowball into critical outages.
  • Context-Rich Reporting: Understand the โ€œwhat, when, and whyโ€ of an error.
  • Faster Debugging: Detailed stack traces make it easier to pinpoint root causes.

Effective error tracking tools allow developers to respond to errors proactively, minimizing user impact.

Why Bugsink?

Bugsink takes error tracking to a new level by prioritizing privacy, simplicity, and compatibility.

1. Built for Self-Hosting

Unlike many hosted error tracking tools that require sensitive data to be shared with third-party servers, Bugsink is self-hosted. This ensures you retain full control over your data, a critical aspect for privacy-conscious teams.

2. Easy to Set Up and Manage

Whether youโ€™re deploying it on your local server or in the cloud, the experience is smooth.

3. Resource Efficiency

Bugsink is designed to be lightweight and efficient. It doesnโ€™t demand hefty server resources, making it an ideal choice for startups, small teams, or resource-constrained environments.

4. Compatible with Sentry

If youโ€™ve used Sentry before, youโ€™ll feel right at home with Bugsink. It offers Sentry compatibility, allowing you to migrate effortlessly or use it alongside existing tools. This compatibility also means you can leverage existing SDKs and integrations.

5. Proactive Notifications

Bugsink ensures youโ€™re in the loop as soon as something goes wrong. Email notifications alert you the moment an error occurs, enabling swift action. This proactive approach reduces the mean time to resolution (MTTR) and keeps users happy.

Docs: https://www.bugsink.com/docs/

In this blog, i jot down my experience on using BugSink with Python.

1. Run using Docker

There are many ways proposed for BugSink installation, https://www.bugsink.com/docs/installation/. In this blog, i am trying using docker.


docker pull bugsink/bugsink:latest

docker run \
  -e SECRET_KEY=ab4xjs5wfnP2XrUwRJPtmk1sEnMcx9d2mta8vtbdZ4oOtvy5BJ \
  -e CREATE_SUPERUSER=admin:admin \
  -e PORT=8000 \
  -p 8000:8000 \
  bugsink/bugsink

2. Log In, Create a Team, Project

The Application will run at port 8000.

Login using admin/admin. Create a new team, by clicking the top right button.

Give a name to the team,

then create a project, under this team,

After creating a project, you will be able to see like below,

You will get an individual DSN , like http://9d0186dd7b854205bed8d60674f349ea@localhost:8000/1.

3. Attaching DSN to python app



import sentry_sdk

sentry_sdk.init(
    "http://d76bc0ccf4da4423b71d1fa80d6004a3@localhost:8000/1",

    send_default_pii=True,
    max_request_body_size="always",
    traces_sample_rate=0,
)

def divide(num1, num2):
    return num1/num2

divide(1, 0)


The above program, will throw an Zero Division Error, which will be reflected in BugSink application.

The best part is you will get the value of variables at that instance. In this example, you can see values of num1 and num2.

There are lot more awesome features out there https://www.bugsink.com/docs/.

Learning Notes #53 โ€“ The Expiration Time Can Be Unexpectedly Lost While Using Redis SET EX

12 January 2025 at 09:14

Redis, a high-performance in-memory key-value store, is widely used for caching, session management, and various other scenarios where fast data retrieval is essential. One of its key features is the ability to set expiration times for keys. However, when using the SET command with the EX option, developers might encounter unexpected behaviors where the expiration time is seemingly lost. Letโ€™s explore this issue in detail.

Understanding SET with EX

The Redis SET command with the EX option allows you to set a keyโ€™s value and specify its expiration time in seconds. For instance


SET key value EX 60

This command sets the key key to the value value and sets an expiration time of 60 seconds.

The Problem

In certain cases, the expiration time might be unexpectedly lost. This typically happens when subsequent operations overwrite the key without specifying a new expiration. For example,


SET key value1 EX 60
SET key value2

In the above sequence,

  1. The first SET command assigns a value to key and sets an expiration of 60 seconds.
  2. The second SET command overwrites the value of key but does not include an expiration time, resulting in the key persisting indefinitely.

This behavior can lead to subtle bugs, especially in applications that rely on key expiration for correctness or resource management.

Why Does This Happen?

The Redis SET command is designed to replace the entire state of a key, including its expiration. When you use SET without the EX, PX, or EXAT options, the expiration is removed, and the key becomes persistent. This behavior aligns with the principle that SET is a complete update operation.

When using Redis SET with EX, be mindful of operations that might overwrite keys without reapplying expiration. Understanding Redisโ€™s behavior and implementing robust patterns can save you from unexpected issues, ensuring your application remains efficient and reliable.

Learning Notes #6 Bloom Filters โ€“ A Probabilistic Data Structure

23 December 2024 at 14:24

I have came across reading Bloom Filters when i wanted to implement username check likewise in instagram. Today i came back to refresh on bloom filters and note it for my future self.

What is a Bloom Filter ?

A Bloom filter is a space-efficient, probabilistic data structure designed to test whether an element is part of a set. It can return two types of results

  • True: The element is probably in the set.
  • False: The element is definitely not in the set.

Notably, Bloom filters do not store the actual elements themselves, and there is a chance of false positives, but never false negatives.

If it says, the given word is not present then we can be 100% sure about it. This is the benefit we are getting out of Bloom Filters.

But setting up a bloom filter is not an easy task. You will soon get to know.

How Does a Bloom Filter Work?

A Bloom filter uses a bit array of size and independent hash functions. Hereโ€™s how it operates,

  1. Adding an Element
    • Compute the hash values for the element for each hash functions.
    • Map these hash values to positions in the bit array.
    • Set the corresponding bits to 1.
  2. Querying an Element
    • Compute the hash values for the element for each hash functions.
    • Check the corresponding bits in the bit array.
    • If all bits are 1, the element is probably in the set. If any bit is 0, the element is definitely not in the set.

As you can imagine, when we are continously adding element to array (considering the array size is smaller), then the percentage of false positives will increase. On the other hand choosing the correct numbers of hash functions also matters.

Setting Parameters

To effectively use a Bloom filter, itโ€™s important to set the parameters appropriately

  1. Bit Array Size (m):
    • The size of the bit array determines the capacity and accuracy of the filter.
    • A larger m reduces the false positive rate but requires more memory.
  2. Number of Hash Functions (k):
    • The number of hash functions affects the distribution of bits set to 1.
    • An optimal k minimizes the false positive rate for a given m and number of elements (n).
  3. Number of Elements (n):
    • Estimate the number of elements to be stored to configure m and k appropriately.

Someone derived a formula

Bit Array Size

The false positive rate represents the probability that a non-existing element is incorrectly identified as present in the Bloom filter. It depends on the size of the bit array (m), the number of hash functions (k), and the number of elements inserted (n). To achieve a desired false positive rate, we can calculate the optimal bit array size using the formula

Here, p denotes the desired false positive rate.

Optimal Number of Hash Functions

The optimal number of hash functions (k) is determined by the size of the bit array and the number of elements to be inserted. It can be calculated using the formula

This ensures an equal distribution of hash values across the bit array, minimizing collisions and maximizing the accuracy of the filter.

Probability of False Positives

The probability of false positives (P_fp) is influenced by the number of hash functions (k), the bit array size (m), and the number of elements inserted (n). It can be estimated using the formula.

Putting all together (Python Code)

Setting the fpr (false positive rate) to 0.1 %, letโ€™s calculate bit array size, no. of hash functions.


import math

# Expected number of items in the collection
n = 300_000

# Acceptable false-positive rate (0.01 = 1%)
fpr = 0.01

# Optimal size (number of elements in the bit array)
# m = -((n * ln(p)) / (ln(2)^2))
m = -(n * math.log(fpr)) / (math.log(2) ** 2)

# Optimal number of hash functions
# k = (m / n) * ln(2)
k = (m / n) * math.log(2)

print(f"Optimal Bloom filter size: {math.ceil(m)} bits")
print(f"Optimal number of hash functions: {math.ceil(k)}")

Practical Considerations

  • Hash Functions:
    • Choose independent and uniformly distributed hash functions to minimize collisions.
    • Common choices include MurmurHash and FNV.
  • Performance:
    • More hash functions increase computational cost but can reduce the false positive rate.
    • Balance the number of hash functions to achieve acceptable performance.
  • Capacity Planning:
    • Overestimating n leads to wasted space; underestimating increases the false positive rate.
    • Plan for future growth to maintain efficiency.

Online Calculator : https://hur.st/bloomfilter/?utm_source=parottasalna.com

References

  1. https://ayushgupta2959.medium.com/understanding-bloom-filters-part-4-storage-requirements-and-false-positive-probabilities-9ec003bf4af
  2. https://stackoverflow.com/questions/658439/how-many-hash-functions-does-my-bloom-filter-need
  3. https://systemdesign.one/bloom-filters-explained/
  4. https://llimllib.github.io/bloomfilter-tutorial/

Learning Notes #4 โ€“ Apache HTTP server benchmarking tool for Load Testing

21 December 2024 at 14:48

How i came across this Apache Bench aka AB tool ?

When i need to load test to check the rate limiting in my previous blog on Gatekeeper Cloud Pattern, i was searching a new tool to load test. Usually i prefer Locust, but this time i wanted to search new tool. Thatโ€™s how i came across ab .

Apache Bench

Apache Bench (ab) is a simple and powerful command-line tool for performance testing web servers. It helps developers measure the performance of HTTP services by simulating multiple requests and analyzing the results.

In this blog, weโ€™ll cover everything you need to get started, with examples for various use cases which i tried.

0. Output


Server Software:        Werkzeug/3.1.3
Server Hostname:        localhost
Server Port:            8090

Document Path:          /
Document Length:        16 bytes

Concurrency Level:      10
Time taken for tests:   2.050 seconds
Complete requests:      2000
Failed requests:        0
Total transferred:      378000 bytes
HTML transferred:       32000 bytes
Requests per second:    975.61 [#/sec] (mean)
Time per request:       10.250 [ms] (mean)
Time per request:       1.025 [ms] (mean, across all concurrent requests)
Transfer rate:          180.07 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       1
Processing:     2   10   2.3     10      37
Waiting:        2   10   2.2     10      37
Total:          3   10   2.3     10      37


It gives a detailed output on time taken, data transferred and other stress details.

1. Basic Load Testing

To send 100 requests to a server with 10 concurrent connections

ab -n 100 -c 10 http://example.com/

  • -n 100 : Total 100 requests.
  • -c 10 : Concurrent 10 requests at a time.
  • http://example.com/: The URL to test.

2. Testing with POST Requests

For testing POST requests, we can use the -p option to specify the data file and -T to set the content type.


ab -n 50 -c 5 -p post_data.txt -T "application/x-www-form-urlencoded" http://example.com/api
  • -p post_data.txt โ€“ File containing POST data.
  • -T "application/x-www-form-urlencoded" โ€“ Content type of the request.

3. Testing with Custom Headers

To add custom headers to your request

ab -n 100 -c 20 -H "Authorization: Bearer <token>" http://example.com/api

  • -H "Authorization: Bearer <token>" โ€“ Adds an Authorization header.

4. Benchmarking with Keep-Alive

By default, ab closes the connection after each request. Use -k to enable keep-alive

ab -n 500 -c 50 -k http://example.com/

  • -k Enables HTTP Keep-Alive.

5. Outputting Detailed Results

To write the results to a file for analysis


ab -n 200 -c 20 http://example.com/ > results.txt
  • > results.txt โ€“ Saves the results in a text file.

6. Configuring Timeout Values

To set a custom timeout,


ab -n 100 -c 10 -s 60 http://example.com/
  • -s 60: Sets a 60-second timeout for each request.

Apache Bench outputs key metrics such as

  • Requests per second: Average number of requests handled.
  • Time per request: Average time per request.
  • Transfer rate: Data transfer rate.

Getting started with Django Basics

11 December 2024 at 12:07

Below listed are the high level steps involved to create a basic Django application.

  1. install python
  2. use venv before installing django =>python -m venv tutorial-env
  3. activate the venv: tutorial-env\Scripts\activate
  4. install django in the venv=> python -m pip install django
  5. check version =>django-admin โ€“version
  6. Create a django project => django-admin startproject myApp
  7. To start the webserver =>python manage.py runserver
  8. From the myApp location, open cmd and type code .=> which will open vs code for this project from VSCode 1. init.py => when the proj receives a request it will understand that this is a package with the help of this init file 2.asgi & wsgi =>Both required during deployment 3.settings.py =>DB, Language, Timezone, Static, URL etc.,
  9. URLs.py => will contain the list of urls used for the project
  10. outside of myApp, db.sqlite3 will be used by default as a lite weight DB
  11. Manage.py =>Very important file
  12. Within the project myAPP, we can create multiple application. to create a new app => python manage.py startapp blog 1.migrations => DB related
    1. init => represent that it is a pkg
    2. admin => for admin purposes
    3. apps => app related config eg: name of the app etc.,
    4. models => contents 6.tests => used for testing the app 7.views 10.Register the app: from myApp->setting.py under Installed_Apps ->add the recently created app โ€˜blogโ€™ 11.Create the first View:(in general we will receive the request and send back the response) from blog->views.py 1.import HTTPResponse => from django.http import HttpRespose a. Create a python function which take request as a parameter and return the HttpResponse=>A static string output 2.under blog, create a python file by name โ€œurls.pyโ€ a.within that file add the urlpatterns list similar to myApp->urls.pyb.in this file, import path, and view from the project->from . import views c.to the urlpatterns list add and entry to the python function created under views.py path(โ€œโ€<โ€œโ€ represents home directory>,views.index,name=โ€indexโ€) 3.In myApp-> urls.py a. import path,include from django.urls b. under urlpatterns, add path(โ€œโ€,include(โ€œblog.urlsโ€)) โ€“> including the url from the blog->urls.py
      1. Time to test the changes. Go to the application url. it should show the content from views.py->index function
      2. Alternatively if we want to call the index with a seperate url a. from the myApp->urls.py-> in the urlpatterns.path -> instead of โ€œโ€, provide โ€œblogs/โ€ b. Test the same with both default application url and url/blogs

Postgres โ€“ Write-Ahead Logging (WAL) in PostgreSQL

16 November 2024 at 07:06

Write-Ahead Logging (WAL) is a fundamental feature of PostgreSQL, ensuring data integrity and facilitating critical functionalities like crash recovery, replication, and backup.

This series of experimentation explores WAL in detail, its importance, how it works, and provides examples to demonstrate its usage.

What is Write-Ahead Logging (WAL)?

WAL is a logging mechanism where changes to the database are first written to a log file before being applied to the actual data files. This ensures that in case of a crash or unexpected failure, the database can recover and replay these logs to restore its state.

Your question is right !

Why do we need a WAL, when we do a periodic backup ?

Write-Ahead Logging (WAL) is critical even when periodic backups are in place because it complements backups to provide data consistency, durability, and flexibility in the following scenarios.

1. Crash Recovery

  • Why Itโ€™s Important: Periodic backups only capture the database state at specific intervals. If a crash occurs after the latest backup, all changes made since that backup would be lost.
  • Role of WAL: WAL ensures that any committed transactions not yet written to data files (due to PostgreSQLโ€™s lazy-writing behavior) are recoverable. During recovery, PostgreSQL replays the WAL logs to restore the database to its last consistent state, bridging the gap between the last checkpoint and the crash.

Example:

  • Backup Taken: At 12:00 PM.
  • Crash Occurs: At 1:30 PM.
  • Without WAL: All changes after 12:00 PM are lost.
  • With WAL: All changes up to 1:30 PM are recovered.

2. Point-in-Time Recovery (PITR)

  • Why Itโ€™s Important: Periodic backups restore the database to the exact time of the backup. However, this may not be sufficient if you need to recover to a specific point, such as just before a mistake (e.g., accidental data deletion).
  • Role of WAL: WAL records every change, enabling you to replay transactions up to a specific time. This allows fine-grained recovery beyond what periodic backups can provide.

Example:

  • Backup Taken: At 12:00 AM.
  • Mistake Made: At 9:45 AM, an important table is accidentally dropped.
  • Without WAL: Restore only to 12:00 AM, losing 9 hours and 45 minutes of data.
  • With WAL: Restore to 9:44 AM, recovering all valid changes except the accidental drop.

3. Replication and High Availability

  • Why Itโ€™s Important: In a high-availability setup, replicas must stay synchronized with the primary database to handle failovers. Periodic backups cannot provide real-time synchronization.
  • Role of WAL: WAL enables streaming replication by transmitting logs to replicas, ensuring near real-time synchronization.

Example:

  • A primary database sends WAL logs to replicas as changes occur. If the primary fails, a replica can quickly take over without data loss.

4. Handling Incremental Changes

  • Why Itโ€™s Important: Periodic backups store complete snapshots of the database, which can be time-consuming and resource-intensive. They also do not capture intermediate changes.
  • Role of WAL: WAL allows incremental updates by recording only the changes made since the last backup or checkpoint. This is crucial for efficient data recovery and backup optimization.

5. Ensuring Data Durability

  • Why Itโ€™s Important: Even during normal operations, a database crash (e.g., power failure) can occur. Without WAL, transactions committed by users but not yet flushed to disk are lost.
  • Role of WAL: WAL ensures durability by logging all changes before acknowledging transaction commits. This guarantees that committed transactions are recoverable even if the system crashes before flushing the changes to data files.

6. Supporting Hot Backups

  • Why Itโ€™s Important: For large, active databases, taking a backup while the database is running can result in inconsistent snapshots.
  • Role of WAL: WAL ensures consistency by recording changes that occur during the backup process. When replayed, these logs synchronize the backup, ensuring it is valid and consistent.

7. Debugging and Auditing

  • Why Itโ€™s Important: Periodic backups are static snapshots and donโ€™t provide a record of what happened in the database between backups.
  • Role of WAL: WAL contains a sequential record of all database modifications, which can help in debugging issues or auditing transactions.
FeaturePeriodic BackupsWrite-Ahead Logging
Crash RecoveryLimited to the last backupEnsures full recovery to the crash point
Point-in-Time RecoveryRestores only to the backup timeAllows recovery to any specific point
ReplicationNot supportedEnables real-time replication
EfficiencyFull snapshotIncremental changes
DurabilityRelies on backup frequencyGuarantees transaction durability

In upcoming sessions, we will all experiment each one of the failure scenarios for understanding.

uv- A faster alternative to pip and pip-tools

26 April 2024 at 17:52

Introduction

If youโ€™re a Python developer, youโ€™re probably familiar with pip and pip-tools, the go-to tools for managing Python packages. However, did you know that thereโ€™s a faster alternative that can save you time and improve your workflow?

Meet UV

uv is a package installer used for installing packages in python in a faster way. Which is written on Rust ๐Ÿฆ€ย , makes a warping speed in installation of packages as compared to pip and pip-tools.

Also, It is Free and Open Source done by astral-sh. which has around 11.3k stars on GitHub makes a very trending alternative package manager forย python.

pip vsย uv

As per astral-sh, they claim uv makes as very faster on installation of python packages, as compared to poetryย , which is a another python package manager and pip-compile

Image courtesy: astral-sh ( Package Installation )

Also, we can able to create a virtual environment, at a warping speed as compared to python3 venv or virtualenv.

Image courtesy: astral-sh ( Virtual Environment )

My experience and Benchmarks

Nowadays, I am using uv as a package manager for doing my side projects. Which feels very good on developing python applications and it will be definitely useful on containerizing python applications usingย docker.

But now, uv has make my life easier with warping speed in installation on packages, suitable for building and deploying our containers as our need with many repitition and hassle-free in building docker containers.

Here is my comparison on pip and uv, Letโ€™s start withย pip

creating virtual environment withย pip

The above pic shows that it takes almost 3.84 or approximately 4 seconds to create a virtual environment in pythonย whereas,

creating Virtual environment usingย uv

uv takes just 0.01 seconds to create a virtual environment in python. Now we move on with installing packages such as fastapi and langchain at same time, which has more dependencies than ever workedย with.

pip install fastapi langchain
Installation of fastapi and langchain usingย pip

This takes around 22.5 seconds, which is fast today ๐Ÿ˜‚, Sometimes which makes it even slower during installation at crucial time. Now letโ€™s check withย uv.

Installation of langchain and fastapi usingย uv

uv, makes a warping installation of langchain and fastapi at same time within 0.12 seconds.ย ๐Ÿคฏ๐Ÿ’ฅ

Which makes me to use โ€˜uvโ€™ as my package manager for python while developing my projects at recentย times.

uv Installation andย usage

Firstly copy the command for installation usingย linux,

curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/uv/releases/download/0.1.38/uv-installer.sh | sh

on Windows,

powershell -c "irm https://github.com/astral-sh/uv/releases/download/0.1.38/uv-installer.ps1 | iex"

for mac users, go and download official binaries provided by astral-sh, givenย here.

Virtual Environment creation

for creating virtual environments for python, we canย use

uv venv <environment-name>

for <environment-name> give any name yourย wish.

Package Installation

for package Installationย , we have toย use

uv pip install <package-name>

Conclusion

Through this blog post, we have learned about uv package manager and how it is effective in making our python workflows faster and building our containers faster and ease of deploymentย .

To know about me, click on my github, Linkedin.


uv- A faster alternative to pip and pip-tools was originally published in Towards Dev on Medium, where people are continuing the conversation by highlighting and responding to this story.

Docker : Creating and uploading docker image to docker hub โ€“ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฎฟ เฎ…เฎคเฏˆ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎนเฎชเฏเฎชเฎฟเฎฒเฏ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฏเฎคเฎฒเฏ

By: Hariharan
25 September 2024 at 20:00

เฎšเฏ†เฎชเฏ 25, 2024

เฎจเฎพเฎฉเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎตเฎ•เฏเฎชเฏเฎชเฎฟเฎฒเฏ เฎ•เฎฑเฏเฎฑเฎตเฎฑเฏเฎฑเฏˆ เฎตเฏˆเฎคเฏเฎคเฏ เฎ’เฎฐเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎนเฎชเฏเฎชเฎฟเฎฒเฏ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฏเฎคเฎฒเฏ เฎตเฎฐเฏˆ เฎจเฎŸเฎจเฏเฎค เฎšเฏ†เฎฏเฎฒเฏเฎชเฎพเฎŸเฏเฎ•เฎณเฏˆ เฎ‡เฎจเฏเฎคเฎชเฏเฎชเฎคเฎฟเฎตเฎฟเฎฒเฏ เฎ•เฏเฎฑเฎฟเฎชเฏเฎชเฎฟเฎŸเฏเฎ•เฎฟเฎฑเฏ‡เฎฉเฏ.

เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎนเฎชเฏ เฎ•เฎฃเฎ•เฏเฎ•เฏˆ เฎคเฏเฎตเฎ•เฏเฎ•เฏเฎคเฎฒเฏ

https://app.docker.com/signup เฎ‡เฎฃเฏˆเฎชเฏเฎชเฏˆ เฎšเฏŠเฎŸเฏเฎ•เฏเฎ•เฎตเฏเฎฎเฏ เฎ…เฎคเฎฟเฎฒเฏ เฎ•เฏ‚เฎ•เฏเฎณเฏ เฎ•เฎฃเฎ•เฏเฎ•เฏˆ เฎตเฏˆเฎคเฏเฎคเฏ (เฎจเฏ€เฎ™เฏเฎ•เฎณเฏ เฎชเฎฟเฎฑ เฎ‰เฎณเฏเฎจเฏเฎดเฏˆเฎตเฏ เฎ…เฎฎเฏˆเฎชเฏเฎชเฏเฎ•เฎณเฏˆเฎฏเฏเฎฎเฏ เฎชเฎฏเฎฉเฏเฎชเฎŸเฏเฎคเฏเฎคเฎฟเฎ•เฏ เฎ•เฏŠเฎณเฏเฎณเฎฒเฎพเฎฎเฏ) เฎ‰เฎณเฏเฎจเฏเฎดเฏˆเฎฏเฎตเฏเฎฎเฏ.

เฎตเฏ†เฎฑเฏเฎฑเฎฟเฎ•เฎฐเฎฎเฎพเฎฉ เฎ‰เฎณเฏเฎจเฏเฎดเฏˆเฎตเฏเฎ•เฏเฎ•เฏ เฎชเฎฟเฎฑเฎ•เฏ https://hub.docker.com เฎ•เฏเฎ•เฏ Docker Hub Link เฎ เฎšเฏŠเฎŸเฏเฎ•เฏเฎ•เฏเฎตเฎคเฏเฎฎเฏ‚เฎฒเฎฎเฏ เฎšเฏ†เฎฒเฏเฎฒเฎตเฏเฎฎเฏ.

Click the Docker Hub Link

เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎนเฎชเฏเฎชเฎฟเฎฒเฏ เฎจเฎพเฎฎเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฎฎเฏ เฎšเฏ†เฎฏเฏเฎฏเฏเฎฎเฏ เฎฎเฏเฎฉเฏเฎฉเฎฐเฏ เฎ…เฎคเฎฉเฏˆ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑ เฎ’เฎฐเฏ เฎ•เฏ‹เฎชเฏเฎชเฏเฎฑเฏˆ เฎ’เฎฉเฏเฎฑเฏˆ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ• เฎตเฏ‡เฎฃเฏเฎŸเฏเฎฎเฏ.

เฎ•เฏ‹เฎชเฏเฎชเฏเฎฑเฏˆเฎ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฎฟเฎฏ เฎชเฎฟเฎฑเฎ•เฏ เฎจเฎพเฎฎเฏ เฎจเฎฎเฎคเฏ เฎ•เฎฃเฎฟเฎฉเฎฟเฎฏเฎฟเฎฒเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฎฟเฎฏ เฎชเฎฟเฎฉเฏเฎฉเฎฐเฏ เฎ…เฎคเฎฉเฏˆ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฎฟเฎ•เฏเฎ•เฏŠเฎณเฏเฎณเฎฒเฎพเฎฎเฏ.

เฎ•เฎฃเฎฟเฎฃเฎฟเฎฏเฎฟเฎฒเฏ เฎ’เฎฐเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฏเฎคเฎฒเฏ

เฎฎเฏเฎคเฎฒเฎฟเฎฒเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฏเฎฎเฏ เฎฎเฏเฎฉเฏเฎฉเฎฐเฏ เฎชเฎดเฏˆเฎฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎ•เฎฒเฎฉเฏเฎ•เฎณเฎฟเฎฉเฏ (Container) เฎ‡เฎฏเฎ•เฏเฎ•เฎคเฏเฎคเฏˆ เฎจเฎฟเฎฑเฏเฎคเฏเฎคเฎฟเฎตเฎฟเฎŸเฏเฎŸเฏ เฎšเฎฑเฏเฎฑเฏ เฎจเฎฟเฎฉเฏˆเฎตเฎคเฏเฎคเฎฟเฎฉเฏˆ เฎคเฎฏเฎพเฎฐเฏ เฎšเฏ†เฎฏเฏเฎคเฏ เฎ•เฏŠเฎณเฏเฎ•เฎฟเฎฑเฏ‡เฎฉเฏ (เฎจเฎฟเฎฉเฏˆเฎตเฎ• เฎชเฎฑเฏเฎฑเฎพเฎ•เฏเฎ•เฏเฎฑเฏˆ เฎ‡เฎฐเฏเฎชเฏเฎชเฎคเฎพเฎฒเฏ).

docker rm $(docker ps -aq)

เฎชเฎฟเฎฉเฏเฎฉเฎฐเฏ Dockerfile เฎŽเฎดเฏเฎค เฎคเฏเฎตเฎ™เฏเฎ• เฎตเฏ‡เฎฃเฏเฎŸเฎฟเฎฏเฎคเฏเฎคเฎพเฎฉเฏ

เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ• เฎจเฎฎเฎ•เฏเฎ•เฏ เฎคเฏ‡เฎตเฎฏเฎพเฎฉ เฎšเฎพเฎฐเฏเฎชเฏ เฎชเฎŸเฎ™เฏเฎ•เฎณเฏˆ เฎฎเฏเฎคเฎฒเฎฟเฎฒเฏ เฎชเฎคเฎฟเฎตเฎฟเฎฑเฎ•เฏเฎ•เฎฟ เฎ…เฎคเฎฉเฏˆ เฎคเฎฏเฎพเฎฐเฏเฎชเฎŸเฏเฎคเฏเฎคเฎฟเฎ•เฏเฎ•เฏŠเฎณเฏเฎตเฏ‹เฎฎเฏ.

เฎŽเฎฉเฏเฎฉเฏเฎŸเฎฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎฎเฏ เฎฎเฎฟเฎ•เฎตเฏเฎฎเฏ เฎšเฎฟเฎฑเฎฟเฎฏเฎคเฎพเฎ• เฎตเฏ‡เฎฃเฏเฎŸเฏเฎฎเฏ เฎŽเฎฉ เฎจเฎฟเฎฉเฏˆเฎชเฏเฎชเฎคเฎพเฎฒเฏ เฎจเฎพเฎฉเฏ python3-alpine เฎชเฎฏเฎฉเฏเฎชเฎŸเฏเฎคเฏเฎคเฏเฎ•เฎฟเฎฑเฏ‡เฎฉเฏ.

https://hub.docker.com/r/activestate/python3-alpine

เฎจเฎฟเฎฑเฏเฎตเฎฒเฏ เฎšเฎฐเฎฟเฎชเฎพเฎฐเฏเฎคเฏเฎคเฎฒเฏ

เฎฎเฏ‡เฎฑเฏเฎ•เฎฃเฏเฎŸ เฎ•เฎŸเฏเฎŸเฎณเฏˆเฎตเฎฐเฎฟเฎ•เฎณเฏˆ เฎชเฎฏเฎฉเฏเฎชเฎŸเฏเฎคเฏเฎคเฎฟ เฎจเฎพเฎฎเฏ เฎจเฎฎเฎคเฏ เฎจเฎฟเฎฑเฏเฎตเฎฒเฏˆ เฎšเฎฐเฎฟเฎชเฎพเฎฐเฏเฎ•เฏเฎ•เฎฒเฎพเฎฎเฏ.

เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎ•เฏ‹เฎชเฏเฎชเฏˆ เฎŽเฎดเฏเฎคเฏเฎคเฎฒเฏ เฎฎเฎฑเฏเฎฑเฏเฎฎเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฏเฎคเฎฒเฏ

# we are choosing the base image as python alpine
FROM activestate/python3-alpine:latest
# setting work directory
WORKDIR ./foss-event-aggregator
# Copying the workdirectory files to the container 
COPY ./foss-event-aggregator ./foss-event-aggregator
# Installing required dev-dependencies 
# RUN ["pip3","install","-r","./foss-event-aggregator/dev-requirements.txt"]
# Running PIP commands to update the dependencies for the
RUN ["apk","add","libxml2-dev","libxslt-dev","python-dev"]

RUN ["pip3","install","-r","./foss-event-aggregator/requirements.txt"]

CMD ["python","eventgator.py"]

เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎ•เฏ‹เฎชเฏเฎชเฏ เฎŽเฎดเฏเฎคเฏเฎฎเฏ เฎชเฏ‹เฎคเฏ เฎคเฏ‡เฎตเฏˆเฎฏเฎพเฎฉ เฎšเฎพเฎฐเฏเฎชเฏเฎ•เฎณเฏ เฎ…เฎฉเฏˆเฎคเฏเฎคเฏเฎฎเฏ เฎšเฎฐเฎฟเฎฏเฎพเฎ• เฎจเฎฟเฎฑเฏเฎตเฎชเฏเฎชเฎŸเฏเฎ•เฎฟเฎฑเฎคเฎพ เฎŽเฎฉเฏเฎชเฎคเฏˆ เฎšเฎฐเฎฟเฎชเฎพเฎฐเฏเฎ•เฏเฎ• เฎชเฎฟเฎดเฏˆเฎšเฏเฎšเฏ†เฎฏเฏเฎคเฎฟ เฎตเฎฐเฏเฎฎเฏเฎชเฏ‹เฎคเฏ เฎ…เฎคเฎฉเฏˆ เฎšเฎฐเฎฟเฎšเฏ†เฎฏเฏเฎฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎ•เฏ‹เฎชเฏเฎชเฏˆ เฎคเฏ‡เฎตเฏˆเฎชเฏเฎชเฎŸเฎฟ เฎฎเฎพเฎฑเฏเฎฑเฏเฎ•.

เฎตเฏ†เฎฑเฏเฎฑเฎฟเฎ•เฎฐเฎฎเฎพเฎ• foss-event-aggregator เฎŽเฎฉเฏเฎฎเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎฎเฏ เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฎชเฏเฎชเฎŸเฏเฎŸเฎคเฏ.

เฎ‰เฎฐเฏเฎตเฎพเฎ•เฏเฎ•เฎชเฏเฎชเฎŸเฏเฎŸ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎชเฎฐเฎฟเฎšเฏ‹เฎคเฎฟเฎคเฏเฎคเฎพเฎ•เฎฟเฎตเฎฟเฎŸเฏเฎŸเฎคเฏ เฎ‡เฎชเฏเฎชเฏŠเฎดเฏเฎคเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎนเฎชเฏเฎชเฏเฎ•เฏเฎ•เฏ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฎฒเฎพเฎฎเฏ.

เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎนเฎชเฏเฎชเฏเฎ•เฏเฎ•เฏ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฏเฎคเฎฒเฏ

เฎชเฎŸเฎคเฏเฎคเฏˆ เฎชเฎฐเฎฟเฎšเฏ‹เฎคเฎฟเฎคเฏเฎค เฎชเฎฟเฎฑเฎ•เฏ เฎ•เฏ‹เฎชเฏเฎชเฏเฎฑเฏˆ เฎชเฏ†เฎฏเฎฐเฎฟเฎฒเฏ เฎŸเฎพเฎ•เฏ เฎšเฏ†เฎฏเฏเฎฏเฎตเฏ‡เฎฃเฏเฎŸเฏเฎฎเฏ

docker image tag foss-event-aggregator:v1 itzmrevil/foss-events-aggregator:v1

เฎŸเฎพเฎ•เฏ เฎšเฏ†เฎฏเฏเฎฏเฎชเฎŸเฏเฎŸ เฎชเฎฟเฎฑเฎ•เฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฎฟเฎฒเฏ CLIเฎฒเฏ เฎ‰เฎณเฏเฎจเฏเฎดเฏˆเฎตเฏ เฎšเฏ†เฎฏเฏเฎคเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฎฟเฎฒเฏ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฎฎเฏ เฎšเฏ†เฎฏเฏเฎคเฎพเฎฒเฏ เฎฎเฎŸเฏเฎŸเฏเฎฎเฏ‡ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎเฎฑเฏเฎฑเฏเฎ•เฏเฎ•เฏŠเฎณเฏเฎณเฏเฎฎเฏ.

เฎŸเฎพเฎ•เฏเฎ•เฎฐเฎฟเฎฒเฏ เฎ‰เฎณเฏเฎจเฏเฎดเฏˆเฎฏ

docker login

เฎ•เฏŠเฎŸเฏเฎคเฏเฎคเฏ เฎŸเฏ†เฎฐเฏเฎฎเฎฟเฎฉเฎฒเฎฟเฎฒเฏ เฎตเฎฐเฏเฎฎเฏ เฎชเฎŸเฎฟเฎ•เฎณเฏˆ เฎชเฎฟเฎฉเฏเฎชเฎฑเฏเฎฑเฎตเฏเฎฎเฏ.

เฎ‰เฎณเฏเฎจเฏเฎดเฏˆเฎตเฏ เฎšเฏ†เฎฏเฏเฎค เฎชเฎฟเฎฉเฏเฎฉเฎฐเฏ

docker push itzmrevil/foss-events-aggregator:v1

เฎ•เฎŸเฏเฎŸเฎณเฏˆเฎฏเฏˆ เฎ•เฏŠเฎŸเฏเฎคเฏเฎคเฏ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎชเฎŸเฎคเฏเฎคเฏˆ เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฎตเฏเฎฎเฏ

เฎชเฎคเฎฟเฎตเฏ‡เฎฑเฏเฎฑเฎฎเฏ เฎšเฏ†เฎฏเฏเฎฏเฎชเฎŸเฏเฎŸเฎคเฏˆ เฎŸเฎพเฎ•เฏเฎ•เฎฐเฏ เฎนเฎชเฏเฎชเฎฟเฎฒเฏ เฎชเฎพเฎฐเฏเฎ•เฏเฎ• https://hub.docker.com/repository/docker/itzmrevil/foss-events-aggregator/general

เฎตเฏ†เฎฑเฏเฎฑเฎฟ ! เฎตเฏ†เฎฑเฏเฎฑเฎฟ !! เฎตเฏ†เฎฑเฏเฎฑเฎฟ !!!

Docker : Virtual Machines โ€“ เฎฎเฏ†เฎฏเฏเฎจเฎฟเฎ•เฎฐเฏ เฎ‡เฎฏเฎจเฏเฎคเฎฟเฎฐเฎ™เฏเฎ•เฎณเฏ

By: Hariharan
25 September 2024 at 16:54

เฎšเฏ†เฎชเฏ 25, 2024

เฎ’เฎฐเฏ เฎ•เฎฃเฎฟเฎฃเฎฟเฎฏเฎฟเฎฒเฏ เฎ’เฎฐเฏ เฎตเฎฒเฏˆเฎชเฏเฎชเฎฏเฎฉเฏเฎชเฎพเฎŸเฎฟเฎฉเฏˆ เฎ‡เฎฏเฎ™เฏเฎ•เฏเฎตเฎคเฎฑเฏเฎ•เฏ 4 เฎชเฎฏเฎฉเฏเฎชเฎพเฎŸเฏเฎ•เฎณเฏ เฎชเฎฏเฎฉเฏเฎชเฎŸเฏเฎคเฏเฎค เฎตเฏ‡เฎฃเฏเฎŸเฏเฎฎเฏ†เฎฉเฎฟเฎฒเฏ เฎ…เฎจเฏเฎค เฎชเฎฏเฎฉเฏเฎชเฎพเฎŸเฏ เฎ‡เฎฏเฎ•เฏเฎ•เฎคเฏเฎคเฎฟเฎฑเฏเฎ•เฎพเฎ• เฎšเฎพเฎฐเฏเฎจเฏเฎคเฎฟเฎฐเฏเฎ•เฏเฎ•เฏเฎฎเฏ เฎจเฏเฎฃเฏเฎšเฏ†เฎฏเฎฒเฎฟ(CPU), เฎจเฎฟเฎฉเฏˆเฎตเฎ•เฎฎเฏ(RAM), เฎšเฏ‡เฎฎเฎฟเฎชเฏเฎชเฎ• (Storage) เฎชเฏ‹เฎฉเฏเฎฑ เฎตเฎฉเฏเฎชเฏŠเฎฐเฏเฎณเฏ เฎคเฏ‡เฎตเฏˆเฎ•เฎณเฏˆ เฎชเฏ‚เฎฐเฏเฎคเฏเฎคเฎฟ เฎšเฏ†เฎฏเฏเฎฏ เฎตเฏ‡เฎฃเฏเฎŸเฏเฎฎเฏ.

เฎ‡เฎคเฏ‡ เฎคเฏ‡เฎตเฏˆเฎ•เฎณเฏˆ เฎšเฎฟเฎฒ เฎšเฎฎเฎฏเฎ™เฏเฎ•เฎณเฎฟเฎฒเฏ เฎชเฎฏเฎฉเฎฐเฏเฎ•เฎณเฎฟเฎฉเฏ (Users) เฎŽเฎฃเฏเฎฃเฎฟเฎ•เฏเฎ•เฏˆเฎ•เฏเฎ•เฏ เฎเฎฑเฏเฎฑเฎตเฎพเฎฑเฏเฎฎเฏ เฎชเฎฏเฎฉเฏเฎชเฎพเฎŸเฏเฎŸเฎฟเฎฉเฏ เฎ…เฎณเฎตเฏเฎ•เฎณเฏเฎ•เฏเฎ•เฏ (Usage) เฎเฎฑเฏเฎฑเฎตเฎพเฎฑเฏ เฎจเฎพเฎฎเฏ เฎ…เฎคเฎฟเฎ•เฎชเฏเฎชเฎŸเฏเฎคเฏเฎค (Scaling) เฎตเฏ‡เฎฃเฏเฎŸเฎฟเฎฏเฏเฎฎเฏเฎณเฏเฎณเฎคเฏ.

เฎ“เฎฐเฏ‡ เฎ•เฎฃเฎฟเฎฃเฎฟเฎฏเฎฟเฎฒเฏ เฎ…เฎคเฎฟเฎ•เฎณเฎตเฏ เฎชเฎฏเฎฉเฎฐเฏเฎ•เฎณเฎฟเฎฉเฏ เฎ…เฎฃเฏเฎ•เฎฒเฏเฎ•เฎณเฏˆ เฎ…เฎฉเฏเฎฎเฎคเฎฟเฎคเฏเฎคเฎพเฎฒเฏ เฎ…เฎคเฎฟเฎ•เฎชเฎฏเฎฉเฏเฎชเฎพเฎŸเฏเฎŸเฎฟเฎฉเฏ เฎ•เฎพเฎฐเฎฃเฎฎเฎพเฎ• เฎตเฎฒเฏˆเฎคเฎณเฎ™เฏเฎ•เฎณเฏ เฎฎเฏเฎŸเฎ™เฏเฎ•เฏเฎฎเฏ เฎ…เฎชเฎพเฎฏเฎฎเฏ เฎ‰เฎณเฏเฎณเฎคเฏ.เฎ‡เฎคเฎฉเฏˆ เฎคเฎตเฎฟเฎฐเฏเฎ•เฏเฎ• เฎคเฎฉเฎฟเฎคเฏเฎคเฎฉเฎฟ เฎ‡เฎฏเฎจเฏเฎคเฎฟเฎฐเฎ™เฏเฎ•เฎณเฏˆ เฎชเฎฏเฎฉเฏเฎชเฎŸเฏเฎคเฏเฎคเฏเฎฎเฏ เฎชเฏ‹เฎคเฏ เฎคเฏ‡เฎตเฏˆเฎ•เฏเฎ•เฏ เฎ…เฎคเฎฟเฎ•เฎฎเฎพเฎ• เฎตเฎฉเฏเฎชเฏŠเฎฐเฏเฎณเฏ เฎฎเฏ€เฎคเฎฎเฎฟเฎฐเฏเฎ•เฏเฎ•เฏเฎฎเฏ เฎ…เฎคเฏ เฎฎเฏเฎดเฏเฎตเฎคเฏเฎฎเฎพเฎ• เฎชเฎฏเฎฉเฏเฎชเฎŸเฏเฎคเฏเฎคเฎชเฏ เฎชเฎŸเฎพเฎฎเฎฒเฏเฎฎเฏ เฎ‡เฎฐเฏเฎ•เฏเฎ•เฏเฎฎเฏ (proper utilisation).

เฎŽเฎŸเฏเฎคเฏเฎคเฏเฎ•เฏเฎ•เฎพเฎŸเฏเฎŸเฎพเฎ• เฎ•เฏ€เฎดเฏเฎตเฎฐเฏเฎฎเฏ 4 เฎชเฎฏเฎฉเฏเฎชเฎพเฎŸเฏเฎ•เฎณเฏˆ

  • เฎ…เฎชเฏเฎชเฎพเฎšเฏเฎšเฎฟ เฎตเฎฒเฏˆ เฎšเฏ‡เฎตเฏˆเฎฏเฎ•เฎฎเฏ
  • เฎ•เฎฟเฎฐเฎพเฎชเฏ เฎ•เฎฟเฎ•เฏเฎตเฏ†เฎฒเฏ เฎŽเฎจเฏเฎคเฎฟเฎฐเฎฎเฏ
  • เฎชเฏ‹เฎธเฏเฎŸเฏเฎ•เฎฟเฎฑเฏ€เฎธเฏ เฎคเฎฐเฎตเฏเฎคเฎณ เฎ…เฎฎเฏˆเฎชเฏเฎชเฏ
  • เฎŽเฎ•เฏเฎธเฏเฎชเฎฟเฎฐเฎธเฏ เฎตเฎฒเฏˆเฎšเฏ เฎšเฏ‡เฎตเฏˆเฎฏเฎ•เฎฎเฏ

เฎ’เฎฐเฏ เฎ•เฎฃเฎฟเฎฃเฎฟเฎฏเฎฟเฎฒเฏ เฎ‡เฎฏเฎ•เฏเฎ•เฏเฎตเฎฑเฏเฎ•เฏ 4 GB (RAM), 2 Core (CPU) เฎฎเฎฑเฏเฎฑเฏเฎฎเฏ 250 GB (Storage) เฎคเฏ‡เฎตเฏˆเฎชเฏเฎชเฎŸเฏเฎฎเฏ เฎŽเฎฉ เฎตเฏˆเฎคเฏเฎคเฏเฎ•เฏเฎ•เฏŠเฎณเฏเฎตเฏ‹เฎฎเฏ.

เฎจเฎฎเฏเฎฎเฎฟเฎŸเฎฎเฏ 16 GB (RAM), 16 Core (CPU) เฎฎเฎฑเฏเฎฑเฏเฎฎเฏ 1000 GB เฎ•เฏŠเฎฃเฏเฎŸ เฎ•เฎฃเฎฟเฎฉเฎฟ เฎ‰เฎณเฏเฎณเฎคเฏ เฎ…เฎคเฎฟเฎฒเฏ เฎ‡เฎฐเฎฃเฏเฎŸเฏ เฎจเฎฟเฎฑเฏเฎตเฎฒเฏเฎ•เฎณเฏˆ เฎ…เฎฎเฏˆเฎคเฏเฎคเฏ เฎšเฏ‹เฎคเฎฃเฏˆ เฎšเฏ†เฎฏเฏเฎคเฏ เฎชเฎพเฎฐเฏเฎ•เฏเฎ• เฎฎเฏ†เฎฏเฏเฎจเฎฟเฎ•เฎฐเฏ เฎ‡เฎฏเฎจเฏเฎคเฎฟเฎฐเฎ™เฏเฎ•เฎณเฏ เฎ•เฎฐเฏเฎคเฏเฎคเฏเฎฐเฏ เฎตเฎดเฎฟเฎตเฎ•เฏˆ เฎšเฏ†เฎฏเฏเฎ•เฎฟเฎฑเฎคเฏ.

เฎ†เฎ•เฎตเฏ‡ เฎ’เฎฐเฏ เฎ•เฎฃเฎฟเฎฉเฎฟเฎฏเฎฟเฎฒเฏ เฎตเฎฉเฏเฎชเฏŠเฎฐเฏเฎณเฏ เฎ…เฎฎเฏˆเฎชเฏเฎชเฏเฎ•เฎณเฏˆ เฎคเฏ‡เฎตเฏˆเฎ•เฎณเฏˆเฎชเฏ เฎชเฏŠเฎฑเฏเฎคเฏเฎคเฏ เฎ’เฎฐเฏ เฎ•เฎฃเฎฟเฎฉเฎฟเฎฏเฏˆ เฎชเฎฒ เฎ•เฎฃเฎฟเฎฉเฎฟเฎ•เฎณเฎพเฎ• เฎฎเฎพเฎฑเฏเฎฑเฎฟ เฎšเฏ‹เฎคเฎฉเฏˆ เฎšเฏ†เฎฏเฏเฎคเฏ เฎชเฎฏเฎฉเฏเฎชเฎŸเฏเฎคเฏเฎคเฏเฎฎเฏเฎชเฏ‹เฎคเฏ เฎ…เฎจเฏเฎค เฎ•เฎฃเฎฟเฎฃเฎฟเฎ•เฎณเฏˆ เฎฎเฏ†เฎฏเฏเฎจเฎฟเฎ•เฎฐเฏ เฎ‡เฎฏเฎจเฏเฎคเฎฟเฎฐเฎ™เฏเฎ•เฎณเฏ เฎŽเฎฉเฎชเฏ เฎชเฏŠเฎฐเฏเฎณเฏ เฎ•เฏŠเฎณเฏเฎณเฎฒเฎพเฎฎเฏ.

Docker : Meet & Greet โ€“ เฎ…เฎฑเฎฟเฎฎเฏเฎ• เฎตเฎ•เฏเฎชเฏเฎชเฏ

By: Hariharan
16 September 2024 at 18:48

เฎšเฏ†เฎชเฏ 16, 2024

เฎจเฏ‡เฎฑเฏเฎฑเฏ (15-09-2024) เฎ•เฎพเฎžเฏเฎšเฎฟเฎฒเฎ•เฏ เฎšเฎจเฏเฎคเฎฟเฎชเฏเฎชเฏ เฎฎเฏเฎŸเฎฟเฎคเฏเฎคเฎตเฏเฎŸเฎฉเฏ docker เฎตเฎ•เฏเฎชเฏเฎชเฎฟเฎฒเฏ เฎ‡เฎฃเฏˆเฎจเฏเฎคเฏ‡เฎฉเฏ.

เฎตเฎ•เฏเฎชเฏเฎชเฎฟเฎฒเฏ เฎ‡เฎฃเฏˆเฎจเฏเฎคเฎตเฎฐเฏเฎ•เฎณเฏ เฎ…เฎฉเฏˆเฎตเฎฐเฏเฎฎเฏ เฎคเฎคเฏเฎคเฎฎเฏ เฎ…เฎฑเฎฟเฎฎเฏเฎ•เฎ™เฏเฎ•เฎณเฏˆ เฎตเฎดเฎ™เฏเฎ•เฎฟเฎฏ เฎชเฎฟเฎฑเฎ•เฏ เฎœเฎพเฎชเฎฐเฏ เฎ…เฎตเฎฐเฏเฎ•เฎณเฏ docker เฎฉเฏ เฎชเฎฃเฏเฎชเฏเฎ•เฏเฎ•เฏ‚เฎฑเฏเฎ•เฎณเฏˆ เฎตเฎฟเฎณเฎ•เฏเฎ•เฎฟเฎฉเฎพเฎฐเฏ.

เฎชเฎฟเฎฉเฏเฎฉเฎฐเฏ เฎตเฎฟเฎฉเฏเฎŸเฏ‹เฎธเฎฟเฎฒเฏ docker เฎ เฎŽเฎชเฏเฎชเฎŸเฎฟ เฎจเฎฟเฎฑเฏเฎตเฏเฎตเฎคเฏ เฎ•เฏเฎฑเฎฟเฎคเฏเฎคเฏ เฎตเฎฟเฎณเฎ•เฏเฎ•เฎฟเฎฉเฎพเฎฐเฏ.

เฎšเฎฟเฎฒเฎฐเฎฟเฎฉเฏ เฎ•เฎฃเฎฟเฎฉเฎฟเฎฏเฎฟเฎฒเฏ docker เฎจเฎฟเฎฑเฏเฎตเฏเฎคเฎฒเฎฟเฎฒเฏ เฎชเฎฟเฎดเฏˆเฎšเฏเฎšเฏ†เฎฏเฏเฎคเฎฟ เฎตเฎจเฏเฎคเฎคเฏˆเฎฏเฎŸเฏเฎคเฏเฎคเฏ เฎ…เฎคเฎฉเฏˆ เฎšเฎฐเฎฟเฎšเฏ†เฎฏเฏเฎคเฏ เฎตเฎ•เฏเฎชเฏเฎชเฏˆ เฎฎเฏเฎŸเฎฟเฎคเฏเฎคเฏเฎ•เฏŠเฎฃเฏเฎŸเฏ‹เฎฎเฏ.

HAProxy EP 9: Load Balancing with Weighted Round Robin

11 September 2024 at 14:39

Load balancing helps distribute client requests across multiple servers to ensure high availability, performance, and reliability. Weighted Round Robin Load Balancing is an extension of the round-robin algorithm, where each server is assigned a weight based on its capacity or performance capabilities. This approach ensures that more powerful servers handle more traffic, resulting in a more efficient distribution of the load.

What is Weighted Round Robin Load Balancing?

Weighted Round Robin Load Balancing assigns a weight to each server. The weight determines how many requests each server should handle relative to the others. Servers with higher weights receive more requests compared to those with lower weights. This method is useful when backend servers have different processing capabilities or resources.

Step-by-Step Implementation with Docker

Step 1: Create Dockerfiles for Each Flask Application

Weโ€™ll use the same three Flask applications (app1.py, app2.py, and app3.py) as in previous examples.

  • Flask App 1 (app1.py):

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Flask App 1!"

@app.route("/data")
def data():
    return "Data from Flask App 1!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)

  • Flask App 2 (app2.py):

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Flask App 2!"

@app.route("/data")
def data():
    return "Data from Flask App 2!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5002)

  • Flask App 3 (app3.py):

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Flask App 3!"

@app.route("/data")
def data():
    return "Data from Flask App 3!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5003)

Step 2: Create Dockerfiles for Each Flask Application

Create Dockerfiles for each of the Flask applications:

  • Dockerfile for Flask App 1 (Dockerfile.app1):

# Use the official Python image from Docker Hub
FROM python:3.9-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the application file into the container
COPY app1.py .

# Install Flask inside the container
RUN pip install Flask

# Expose the port the app runs on
EXPOSE 5001

# Run the application
CMD ["python", "app1.py"]

  • Dockerfile for Flask App 2 (Dockerfile.app2):

FROM python:3.9-slim
WORKDIR /app
COPY app2.py .
RUN pip install Flask
EXPOSE 5002
CMD ["python", "app2.py"]

  • Dockerfile for Flask App 3 (Dockerfile.app3):

FROM python:3.9-slim
WORKDIR /app
COPY app3.py .
RUN pip install Flask
EXPOSE 5003
CMD ["python", "app3.py"]

Step 3: Create the HAProxy Configuration File

Create an HAProxy configuration file (haproxy.cfg) to implement Weighted Round Robin Load Balancing


global
    log stdout format raw local0
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80
    default_backend servers

backend servers
    balance roundrobin
    server server1 app1:5001 weight 2 check
    server server2 app2:5002 weight 1 check
    server server3 app3:5003 weight 3 check

Explanation:

  • The balance roundrobin directive tells HAProxy to use the Round Robin load balancing algorithm.
  • The weight option for each server specifies the weight associated with each server:
    • server1 (App 1) has a weight of 2.
    • server2 (App 2) has a weight of 1.
    • server3 (App 3) has a weight of 3.
  • Requests will be distributed based on these weights: App 3 will receive the most requests, App 2 the least, and App 1 will be in between.

Step 4: Create a Dockerfile for HAProxy

Create a Dockerfile for HAProxy (Dockerfile.haproxy):


# Use the official HAProxy image from Docker Hub
FROM haproxy:latest

# Copy the custom HAProxy configuration file into the container
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

# Expose the port for HAProxy
EXPOSE 80

Step 5: Create a docker-compose.yml File

To manage all the containers together, create a docker-compose.yml file

version: '3'

services:
  app1:
    build:
      context: .
      dockerfile: Dockerfile.app1
    container_name: flask_app1
    ports:
      - "5001:5001"

  app2:
    build:
      context: .
      dockerfile: Dockerfile.app2
    container_name: flask_app2
    ports:
      - "5002:5002"

  app3:
    build:
      context: .
      dockerfile: Dockerfile.app3
    container_name: flask_app3
    ports:
      - "5003:5003"

  haproxy:
    build:
      context: .
      dockerfile: Dockerfile.haproxy
    container_name: haproxy
    ports:
      - "80:80"
    depends_on:
      - app1
      - app2
      - app3


Explanation:

  • The docker-compose.yml file defines the services (app1, app2, app3, and haproxy) and their respective configurations.
  • HAProxy depends on the three Flask applications to be up and running before it starts.

Step 6: Build and Run the Docker Containers

Run the following command to build and start all the containers


docker-compose up --build

This command builds Docker images for all three Flask apps and HAProxy, then starts them.

Step 7: Test the Load Balancer

Open your browser or use curl to make requests to the HAProxy server


curl http://localhost/
curl http://localhost/data

Observation:

  • With Weighted Round Robin Load Balancing, you should see that requests are distributed according to the weights specified in the HAProxy configuration.
  • For example, App 3 should receive three times more requests than App 2, and App 1 should receive twice as many as App 2.

Conclusion

By implementing Weighted Round Robin Load Balancing with HAProxy, you can distribute traffic more effectively according to the capacity or performance of each backend server. This approach helps optimize resource utilization and ensures a balanced load across servers.

HAProxy EP 8: Load Balancing with Random Load Balancing

11 September 2024 at 14:23

Load balancing distributes client requests across multiple servers to ensure high availability and reliability. One of the simplest load balancing algorithms is Random Load Balancing, which selects a backend server randomly for each client request.

Although this approach does not consider server load or other metrics, it can be effective for less critical applications or when the goal is to achieve simplicity.

What is Random Load Balancing?

Random Load Balancing assigns incoming requests to a randomly chosen server from the available pool of servers. This method is straightforward and ensures that requests are distributed in a non-deterministic manner, which may work well for environments with equally capable servers and minimal concerns about server load or state.

Step-by-Step Implementation with Docker

Step 1: Create Dockerfiles for Each Flask Application

Weโ€™ll use the same three Flask applications (app1.py, app2.py, and app3.py) as in previous examples.

Flask App 1 โ€“ (app.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Flask App 1!"

@app.route("/data")
def data():
    return "Data from Flask App 1!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)


Flask App 2 โ€“ (app.py)


from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Flask App 2!"

@app.route("/data")
def data():
    return "Data from Flask App 2!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5002)

Flask App 3 โ€“ (app.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def home():
    return "Hello from Flask App 3!"

@app.route("/data")
def data():
    return "Data from Flask App 3!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5003)


Step 2: Create Dockerfiles for Each Flask Application

Create Dockerfiles for each of the Flask applications:

  • Dockerfile for Flask App 1 (Dockerfile.app1):
# Use the official Python image from Docker Hub
FROM python:3.9-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the application file into the container
COPY app1.py .

# Install Flask inside the container
RUN pip install Flask

# Expose the port the app runs on
EXPOSE 5001

# Run the application
CMD ["python", "app1.py"]

  • Dockerfile for Flask App 2 (Dockerfile.app2):
FROM python:3.9-slim
WORKDIR /app
COPY app2.py .
RUN pip install Flask
EXPOSE 5002
CMD ["python", "app2.py"]


  • Dockerfile for Flask App 3 (Dockerfile.app3):

FROM python:3.9-slim
WORKDIR /app
COPY app3.py .
RUN pip install Flask
EXPOSE 5003
CMD ["python", "app3.py"]

Step 3: Create a Dockerfile for HAProxy

HAProxy Configuration file,


global
    log stdout format raw local0
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80
    default_backend servers

backend servers
    balance random
    random draw 2
    server server1 app1:5001 check
    server server2 app2:5002 check
    server server3 app3:5003 check

Explanation:

  • The balance random directive tells HAProxy to use the Random load balancing algorithm.
  • The random draw 2 setting makes HAProxy select 2 servers randomly and choose the one with the least number of connections. This adds a bit of load awareness to the random choice.
  • The server directives define the backend servers and their ports.

Step 4: Create a Dockerfile for HAProxy

Create a Dockerfile for HAProxy (Dockerfile.haproxy):

# Use the official HAProxy image from Docker Hub
FROM haproxy:latest

# Copy the custom HAProxy configuration file into the container
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

# Expose the port for HAProxy
EXPOSE 80


Step 5: Create a docker-compose.yml File

To manage all the containers together, create a docker-compose.yml file:


version: '3'

services:
  app1:
    build:
      context: .
      dockerfile: Dockerfile.app1
    container_name: flask_app1
    ports:
      - "5001:5001"

  app2:
    build:
      context: .
      dockerfile: Dockerfile.app2
    container_name: flask_app2
    ports:
      - "5002:5002"

  app3:
    build:
      context: .
      dockerfile: Dockerfile.app3
    container_name: flask_app3
    ports:
      - "5003:5003"

  haproxy:
    build:
      context: .
      dockerfile: Dockerfile.haproxy
    container_name: haproxy
    ports:
      - "80:80"
    depends_on:
      - app1
      - app2
      - app3

Explanation:

  • The docker-compose.yml file defines the services (app1, app2, app3, and haproxy) and their respective configurations.
  • HAProxy depends on the three Flask applications to be up and running before it starts.

Step 6: Build and Run the Docker Containers

Run the following command to build and start all the containers:


docker-compose up --build

This command builds Docker images for all three Flask apps and HAProxy, then starts them.

Step 7: Test the Load Balancer

Open your browser or use curl to make requests to the HAProxy server:

curl http://localhost/
curl http://localhost/data

Observation:

  • With Random Load Balancing, each request should randomly hit one of the three backend servers.
  • Since the selection is random, you may not see a predictable pattern; however, the requests should be evenly distributed across the servers over a large number of requests.

Conclusion

By implementing Random Load Balancing with HAProxy, weโ€™ve demonstrated a simple way to distribute traffic across multiple servers without relying on complex metrics or state information. While this approach may not be ideal for all use cases, it can be useful in scenarios where simplicity is more valuable than fine-tuned load distribution.

HAProxy EP 7: Load Balancing with Source IP Hash, URI โ€“ Consistent Hashing

11 September 2024 at 13:55

Load balancing helps distribute traffic across multiple servers, enhancing performance and reliability. One common strategy is Source IP Hash load balancing, which ensures that requests from the same client IP are consistently directed to the same server.

This method is particularly useful for applications requiring session persistence, such as shopping carts or user sessions. In this blog, weโ€™ll implement Source IP Hash load balancing using Flask and HAProxy, all within Docker containers.

What is Source IP Hash Load Balancing?

Source IP Hash Load Balancing is a technique that uses a hash function on the clientโ€™s IP address to determine which server should handle the request. This guarantees that a particular client will always be directed to the same backend server, ensuring session persistence and stateful behavior.

Consistent Hashing: https://parottasalna.com/2024/06/17/why-do-we-need-to-maintain-same-hash-in-load-balancer/

Step-by-Step Implementation with Docker

Step 1: Create Flask Application

Weโ€™ll create three separate Dockerfiles, one for each Flask app.

Flask App 1 (app1.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask App 1!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)


Flask App 2 (app2.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask App 2!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5002)


Flask App 3 (app3.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask App 3!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5003)

Each Flask app listens on a different port (5001, 5002, 5003).

Step 2: Dockerfiles for each flask application

Dockerfile for Flask App 1 (Dockerfile.app1)

# Use the official Python image from the Docker Hub
FROM python:3.9-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY app1.py .

# Install Flask inside the container
RUN pip install Flask

# Expose the port the app runs on
EXPOSE 5001

# Run the application
CMD ["python", "app1.py"]

Dockerfile for Flask App 2 (Dockerfile.app2)

FROM python:3.9-slim
WORKDIR /app
COPY app2.py .
RUN pip install Flask
EXPOSE 5002
CMD ["python", "app2.py"]

Dockerfile for Flask App 3 (Dockerfile.app3)

FROM python:3.9-slim
WORKDIR /app
COPY app3.py .
RUN pip install Flask
EXPOSE 5003
CMD ["python", "app3.py"]

Step 3: Create a configuration for HAProxy

global
    log stdout format raw local0
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80
    default_backend servers

backend servers
    balance source
    hash-type consistent
    server server1 app1:5001 check
    server server2 app2:5002 check
    server server3 app3:5003 check

Explanation:

  • The balance source directive tells HAProxy to use Source IP Hashing as the load balancing algorithm.
  • The hash-type consistent directive ensures consistent hashing, which is essential for minimizing disruption when backend servers are added or removed.
  • The server directives define the backend servers and their ports.

Step 4: Create a Dockerfile for HAProxy

Create a Dockerfile for HAProxy (Dockerfile.haproxy)

# Use the official HAProxy image from Docker Hub
FROM haproxy:latest

# Copy the custom HAProxy configuration file into the container
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

# Expose the port for HAProxy
EXPOSE 80

Step 5: Create a Dockercompose file

To manage all the containers together, create a docker-compose.yml file

version: '3'

services:
  app1:
    build:
      context: .
      dockerfile: Dockerfile.app1
    container_name: flask_app1
    ports:
      - "5001:5001"

  app2:
    build:
      context: .
      dockerfile: Dockerfile.app2
    container_name: flask_app2
    ports:
      - "5002:5002"

  app3:
    build:
      context: .
      dockerfile: Dockerfile.app3
    container_name: flask_app3
    ports:
      - "5003:5003"

  haproxy:
    build:
      context: .
      dockerfile: Dockerfile.haproxy
    container_name: haproxy
    ports:
      - "80:80"
    depends_on:
      - app1
      - app2
      - app3

Explanation:

  • The docker-compose.yml file defines four services: app1, app2, app3, and haproxy.
  • Each Flask app is built from its respective Dockerfile and runs on its port.
  • HAProxy is configured to wait (depends_on) for all three Flask apps to be up and running.

Step 6: Build and Run the Docker Containers

Run the following commands to build and start all the containers:

# Build and run the containers
docker-compose up --build

This command will build Docker images for all three Flask apps and HAProxy and start them up in the background.

Step 7: Test the Load Balancer

Open your browser or use a tool like curl to make requests to the HAProxy server:

curl http://localhost

Observation:

  • With Source IP Hash load balancing, each unique IP address (e.g., your local IP) should always be directed to the same backend server.
  • If you access the HAProxy from different IPs (e.g., using different devices or by simulating different client IPs), you will see that requests are consistently sent to the same server for each IP.

For the URI based hashing we just need to add,

global
    log stdout format raw local0
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80
    default_backend servers

backend servers
    balance uri
    hash-type consistent
    server server1 app1:5001 check
    server server2 app2:5002 check
    server server3 app3:5003 check


Explanation:

  • The balance uri directive tells HAProxy to use URI Hashing as the load balancing algorithm.
  • The hash-type consistent directive ensures consistent hashing to minimize disruption when backend servers are added or removed.
  • The server directives define the backend servers and their ports.

HAProxy Ep 6: Load Balancing With Least Connection

11 September 2024 at 13:32

Load balancing is crucial for distributing incoming network traffic across multiple servers, ensuring optimal resource utilization and improving application performance. One of the simplest and most popular load balancing algorithms is Round Robin. In this blog, weโ€™ll explore how to implement Least Connection load balancing using Flask as our backend application and HAProxy as our load balancer.

What is Least Connection Load Balancing?

Least Connection Load Balancing is a dynamic algorithm that distributes requests to the server with the fewest active connections at any given time. This method ensures that servers with lighter loads receive more requests, preventing any single server from becoming a bottleneck.

Step-by-Step Implementation with Docker

Step 1: Create Dockerfiles for Each Flask Application

Weโ€™ll create three separate Dockerfiles, one for each Flask app.

Flask App 1 (app1.py) โ€“ Introduced Slowness by adding sleep

from flask import Flask
import time

app = Flask(__name__)

@app.route("/")
def hello():
    time.sleep(5)
    return "Hello from Flask App 1!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)


Flask App 2 (app2.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask App 2!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5002)


Flask App 3 (app3.py) โ€“ Introduced Slowness by adding sleep.

from flask import Flask
import time

app = Flask(__name__)

@app.route("/")
def hello():
    time.sleep(5)
    return "Hello from Flask App 3!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5003)

Each Flask app listens on a different port (5001, 5002, 5003).

Step 2: Dockerfiles for each flask application

Dockerfile for Flask App 1 (Dockerfile.app1)

# Use the official Python image from the Docker Hub
FROM python:3.9-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY app1.py .

# Install Flask inside the container
RUN pip install Flask

# Expose the port the app runs on
EXPOSE 5001

# Run the application
CMD ["python", "app1.py"]

Dockerfile for Flask App 2 (Dockerfile.app2)

FROM python:3.9-slim
WORKDIR /app
COPY app2.py .
RUN pip install Flask
EXPOSE 5002
CMD ["python", "app2.py"]

Dockerfile for Flask App 3 (Dockerfile.app3)

FROM python:3.9-slim
WORKDIR /app
COPY app3.py .
RUN pip install Flask
EXPOSE 5003
CMD ["python", "app3.py"]

Step 3: Create a configuration for HAProxy

global
    log stdout format raw local0
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80
    default_backend servers

backend servers
    balance leastconn
    server server1 app1:5001 check
    server server2 app2:5002 check
    server server3 app3:5003 check

Explanation:

  • frontend http_front: Defines the entry point for incoming traffic. It listens on port 80.
  • backend servers: Specifies the servers HAProxy will distribute traffic evenly the three Flask apps (app1, app2, app3). The balance leastconn directive sets the Least Connection for load balancing.
  • server directives: Lists the backend servers with their IP addresses and ports. The check option allows HAProxy to monitor the health of each server.

Step 4: Create a Dockerfile for HAProxy

Create a Dockerfile for HAProxy (Dockerfile.haproxy)

# Use the official HAProxy image from Docker Hub
FROM haproxy:latest

# Copy the custom HAProxy configuration file into the container
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

# Expose the port for HAProxy
EXPOSE 80

Step 5: Create a Dockercompose file

To manage all the containers together, create a docker-compose.yml file

version: '3'

services:
  app1:
    build:
      context: .
      dockerfile: Dockerfile.app1
    container_name: flask_app1
    ports:
      - "5001:5001"

  app2:
    build:
      context: .
      dockerfile: Dockerfile.app2
    container_name: flask_app2
    ports:
      - "5002:5002"

  app3:
    build:
      context: .
      dockerfile: Dockerfile.app3
    container_name: flask_app3
    ports:
      - "5003:5003"

  haproxy:
    build:
      context: .
      dockerfile: Dockerfile.haproxy
    container_name: haproxy
    ports:
      - "80:80"
    depends_on:
      - app1
      - app2
      - app3

Explanation:

  • The docker-compose.yml file defines four services: app1, app2, app3, and haproxy.
  • Each Flask app is built from its respective Dockerfile and runs on its port.
  • HAProxy is configured to wait (depends_on) for all three Flask apps to be up and running.

Step 6: Build and Run the Docker Containers

Run the following commands to build and start all the containers:

# Build and run the containers
docker-compose up --build

This command will build Docker images for all three Flask apps and HAProxy and start them up in the background.

You should see the responses alternating between โ€œHello from Flask App 1!โ€, โ€œHello from Flask App 2!โ€, and โ€œHello from Flask App 3!โ€ as HAProxy uses the Round Robin algorithm to distribute requests.

Step 7: Test the Load Balancer

Open your browser or use a tool like curl to make requests to the HAProxy server:

curl http://localhost

You should see responses cycling between โ€œHello from Flask App 1!โ€, โ€œHello from Flask App 2!โ€, and โ€œHello from Flask App 3!โ€ according to the Least Connection strategy.

HAProxy EP 5: Load Balancing With Round Robin

11 September 2024 at 12:56

Load balancing is crucial for distributing incoming network traffic across multiple servers, ensuring optimal resource utilization and improving application performance. One of the simplest and most popular load balancing algorithms is Round Robin. In this blog, weโ€™ll explore how to implement Round Robin load balancing using Flask as our backend application and HAProxy as our load balancer.

What is Round Robin Load Balancing?

Round Robin load balancing works by distributing incoming requests sequentially across a group of servers.

For example, the first request goes to Server A, the second to Server B, the third to Server C, and so on. Once all servers have received a request, the cycle repeats. This algorithm is simple and works well when all servers have similar capabilities.

Step-by-Step Implementation with Docker

Step 1: Create Dockerfiles for Each Flask Application

Weโ€™ll create three separate Dockerfiles, one for each Flask app.

Flask App 1 (app1.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask App 1!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)


Flask App 2 (app2.py)

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask App 2!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5002)


Flask App 3 (app3.py)


from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello from Flask App 3!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5003)

Each Flask app listens on a different port (5001, 5002, 5003).

Step 2: Dockerfiles for each flask application

Dockerfile for Flask App 1 (Dockerfile.app1)


# Use the official Python image from the Docker Hub
FROM python:3.9-slim

# Set the working directory inside the container
WORKDIR /app

# Copy the current directory contents into the container at /app
COPY app1.py .

# Install Flask inside the container
RUN pip install Flask

# Expose the port the app runs on
EXPOSE 5001

# Run the application
CMD ["python", "app1.py"]

Dockerfile for Flask App 2 (Dockerfile.app2)


FROM python:3.9-slim
WORKDIR /app
COPY app2.py .
RUN pip install Flask
EXPOSE 5002
CMD ["python", "app2.py"]

Dockerfile for Flask App 3 (Dockerfile.app3)


FROM python:3.9-slim
WORKDIR /app
COPY app3.py .
RUN pip install Flask
EXPOSE 5003
CMD ["python", "app3.py"]

Step 3: Create a configuration for HAProxy

global
    log stdout format raw local0
    daemon

defaults
    log     global
    mode    http
    option  httplog
    option  dontlognull
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80
    default_backend servers

backend servers
    balance roundrobin
    server server1 app1:5001 check
    server server2 app2:5002 check
    server server3 app3:5003 check

Explanation:

  • frontend http_front: Defines the entry point for incoming traffic. It listens on port 80.
  • backend servers: Specifies the servers HAProxy will distribute traffic evenly the three Flask apps (app1, app2, app3). The balance roundrobin directive sets the Round Robin algorithm for load balancing.
  • server directives: Lists the backend servers with their IP addresses and ports. The check option allows HAProxy to monitor the health of each server.

Step 4: Create a Dockerfile for HAProxy

Create a Dockerfile for HAProxy (Dockerfile.haproxy)


# Use the official HAProxy image from Docker Hub
FROM haproxy:latest

# Copy the custom HAProxy configuration file into the container
COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

# Expose the port for HAProxy
EXPOSE 80

Step 5: Create a Dockercompose file

To manage all the containers together, create a docker-compose.yml file


version: '3'

services:
  app1:
    build:
      context: .
      dockerfile: Dockerfile.app1
    container_name: flask_app1
    ports:
      - "5001:5001"

  app2:
    build:
      context: .
      dockerfile: Dockerfile.app2
    container_name: flask_app2
    ports:
      - "5002:5002"

  app3:
    build:
      context: .
      dockerfile: Dockerfile.app3
    container_name: flask_app3
    ports:
      - "5003:5003"

  haproxy:
    build:
      context: .
      dockerfile: Dockerfile.haproxy
    container_name: haproxy
    ports:
      - "80:80"
    depends_on:
      - app1
      - app2
      - app3

Explanation:

  • The docker-compose.yml file defines four services: app1, app2, app3, and haproxy.
  • Each Flask app is built from its respective Dockerfile and runs on its port.
  • HAProxy is configured to wait (depends_on) for all three Flask apps to be up and running.

Step 6: Build and Run the Docker Containers

Run the following commands to build and start all the containers:


# Build and run the containers
docker-compose up --build

This command will build Docker images for all three Flask apps and HAProxy and start them up in the background.

You should see the responses alternating between โ€œHello from Flask App 1!โ€, โ€œHello from Flask App 2!โ€, and โ€œHello from Flask App 3!โ€ as HAProxy uses the Round Robin algorithm to distribute requests.

Step 7: Test the Load Balancer

Open your browser or use a tool like curl to make requests to the HAProxy server:


curl http://localhost

HAProxy EP 4: Understanding ACL โ€“ Access Control List

10 September 2024 at 23:46

Imagine you are managing a busy highway with multiple lanes, and you want to direct specific types of vehicles to particular lanes: trucks to one lane, cars to another, and motorcycles to yet another. In the world of web traffic, this is similar to what Access Control Lists (ACLs) in HAProxy doโ€”they help you direct incoming requests based on specific criteria.

Letโ€™s dive into what ACLs are in HAProxy, why they are essential, and how you can use them effectively with some practical examples.

What are ACLs in HAProxy?

Access Control Lists (ACLs) in HAProxy are rules or conditions that allow you to define patterns to match incoming requests. These rules help you make decisions about how to route or manage traffic within your infrastructure.

Think of ACLs as powerful filters or guards that analyze incoming HTTP requests based on headers, IP addresses, URL paths, or other attributes. By defining ACLs, you can control how requests are handledโ€”for example, sending specific traffic to different backends, applying security rules, or denying access under certain conditions.

Why Use ACLs in HAProxy?

Using ACLs offers several advantages:

  1. Granular Control Over Traffic: You can filter and route traffic based on very specific criteria, such as the content of HTTP headers, cookies, or request methods.
  2. Security: ACLs can block unwanted traffic, enforce security policies, and prevent malicious access.
  3. Performance Optimization: By directing traffic to specific servers optimized for certain types of content, ACLs can help balance the load and improve performance.
  4. Flexibility and Scalability: ACLs allow dynamic adaptation to changing traffic patterns or new requirements without significant changes to your infrastructure.

How ACLs Work in HAProxy

ACLs in HAProxy are defined in the configuration file (haproxy.cfg). The syntax is straightforward


acl <name> <criteria>
  • <name>: The name you give to your ACL rule, which you will use to reference it in further configuration.
  • <criteria>: The condition or match pattern, such as a path, header, method, or IP address.

It either returns True or False.

Examples of ACLs in HAProxy

Letโ€™s look at some practical examples to understand how ACLs work.

Example 1: Routing Traffic Based on URL Path

Suppose you have a web application that serves both static and dynamic content. You want to route all requests for static files (like images, CSS, and JavaScript) to a server optimized for static content, while all other requests should go to a dynamic content server.

Configuration:


frontend http_front
    bind *:80
    acl is_static path_beg /static
    use_backend static_backend if is_static
    default_backend dynamic_backend

backend static_backend
    server static1 127.0.0.1:5001 check

backend dynamic_backend
    server dynamic1 127.0.0.1:5002 check

  • ACL Definition: acl is_static path_beg /static : checks if the request URL starts with /static.
  • Usage: use_backend static_backend if is_static routes the traffic to the static_backend if the ACL is_static matches. All other requests are routed to the dynamic_backend.

Example 2: Blocking Traffic from Specific IP Addresses

Letโ€™s say you want to block traffic from a range of IP addresses that are known to be malicious.

Configurations

frontend http_front
    bind *:80
    acl block_ip src 192.168.1.0/24
    http-request deny if block_ip
    default_backend web_backend

backend web_backend
    server web1 127.0.0.1:5003 check


ACL Definition:acl block_ip src 192.168.1.0/24 defines an ACL that matches any source IP from the range 192.168.1.0/24.

Usage:http-request deny if block_ip denies the request if it matches the block_ip ACL.

Example 4: Redirecting Traffic Based on Request Method

You might want to redirect all POST requests to a different backend for further processing.

Configurations


frontend http_front
    bind *:80
    acl is_post_method method POST
    use_backend post_backend if is_post_method
    default_backend general_backend

backend post_backend
    server post1 127.0.0.1:5006 check

backend general_backend
    server general1 127.0.0.1:5007 check

Example 5: Redirect Traffic Based on User Agent

Imagine you want to serve a different version of your website to mobile users versus desktop users. You can achieve this by using ACLs that check the User-Agent header in the HTTP request.

Configuration:


frontend http_front
    bind *:80
    acl is_mobile_user_agent req.hdr(User-Agent) -i -m sub Mobile
    use_backend mobile_backend if is_mobile_user_agent
    default_backend desktop_backend

backend mobile_backend
    server mobile1 127.0.0.1:5008 check

backend desktop_backend
    server desktop1 127.0.0.1:5009 check

ACL Definition:acl is_mobile_user_agent req.hdr(User-Agent) -i -m sub Mobile checks if the User-Agent header contains the substring "Mobile" (case-insensitive).

Usage:use_backend mobile_backend if is_mobile_user_agent directs mobile users to mobile_backend and all other users to desktop_backend.

Example 6: Restrict Access to Admin Pages by IP Address

Letโ€™s say you want to allow access to the /admin page only from a specific IP address or range, such as your companyโ€™s internal network.


frontend http_front
    bind *:80
    acl is_admin_path path_beg /admin
    acl is_internal_network src 192.168.10.0/24
    http-request deny if is_admin_path !is_internal_network
    default_backend web_backend

backend web_backend
    server web1 127.0.0.1:5015 check

Example with a Flask Application

Letโ€™s see how you can use ACLs with a Flask application to enforce different rules.

Flask Application Setup

You have two Flask apps: app1.py for general requests and app2.py for special requests like form submissions.

app1.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return "Welcome to the main page!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5003)

app2.py:

from flask import Flask

app = Flask(__name__)

@app.route('/submit', methods=['POST'])
def submit_form():
    return "Form submitted successfully!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5004)


HAProxy Configuration with ACLs


frontend http_front
    bind *:80
    acl is_post_method method POST
    acl is_submit_path path_beg /submit
    use_backend post_backend if is_post_method is_submit_path
    default_backend general_backend

backend post_backend
    server app2 127.0.0.1:5004 check

backend general_backend
    server app1 127.0.0.1:5003 check

ACLs:

  • is_post_method checks for the POST method.
  • is_submit_path checks if the path starts with /submit.

Traffic Handling: The traffic is directed to post_backend if both the ACLs match, otherwise, it goes to general_backend.

HAProxy EP 3: Sarahโ€™s Adventure with L7 Load Balancing and HAProxy

10 September 2024 at 23:26

Meet Sarah, a backend developer at โ€œBrightApps,โ€ a fast-growing startup specializing in custom web applications. Recently, BrightApps launched a new service called โ€œFitGuru,โ€ a health and fitness platform that quickly gained traction. However, as the platformโ€™s user base started to grow, the team noticed performance issuesโ€”page loads were slow, and users began to complain.

Sarah knew that simply scaling up their backend servers might not solve the problem. What they needed was a smarter way to handle incoming traffic and distribute it across their servers. Thatโ€™s when she decided to dive into the world of Layer 7 (L7) load balancing with HAProxy.

Understanding L7 Load Balancing

Layer 7 load balancing operates at the Application Layer of the OSI model. Unlike Layer 4 (L4) load balancing, which only considers information from the Transport Layer (like IP addresses and ports), an L7 load balancer examines the actual content of the HTTP requests. This deeper inspection allows it to make more intelligent decisions on how to route traffic.

Hereโ€™s why Sarah chose L7 load balancing for โ€œFitGuruโ€:

  1. Content-Based Routing: Sarah could route requests to different servers based on the URL path, HTTP headers, cookies, or even specific parameters in the request. For example, requests for video content could be directed to a server optimized for handling media, while API requests could go to a server focused on data processing.
  2. SSL Termination: The L7 load balancer could handle the SSL encryption and decryption, relieving the backend servers from this CPU-intensive task.
  3. Advanced Health Checks: Sarah could set up health checks that simulate real user traffic to ensure backend servers are actually serving content correctly, not just responding to pings.
  4. Enhanced Security: With L7, she could filter out malicious traffic more effectively by inspecting request contents, blocking suspicious patterns, and protecting the app from common web attacks.

Step 1: Sarahโ€™s Plan with HAProxy as an HTTP Proxy

Sarah decided to configure HAProxy as an HTTP proxy. This way, it would operate at Layer 7 and provide advanced traffic management capabilities. She had a few objectives:

  • Route traffic based on the URL path to different servers.
  • Offload SSL termination to HAProxy.
  • Serve static files from specific backend servers and dynamic content from others.

Sarah started with a simple Flask application to test her configuration:

Flask Application Setup

Sarah created two basic Flask apps:

  1. Static Content Server (static_app.py):

from flask import Flask, send_from_directory

app = Flask(__name__)

@app.route('/static/<path:filename>')
def serve_static(filename):
    return send_from_directory('static', filename)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001)

This app served static content from a folder named static.

  1. Dynamic Content Server (dynamic_app.py):

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Welcome to FitGuru!"

@app.route('/api/data')
def api_data():
    return {"data": "Some dynamic data"}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5002)

This app handled dynamic requests like API endpoints and the home page.

Step 2: Configuring HAProxy for HTTP Proxy

Sarah then moved on to configure HAProxy. She created an HAProxy configuration file (haproxy.cfg) to route traffic based on URL paths


global
    log stdout format raw local0
    maxconn 4096

defaults
    mode http
    log global
    option httplog
    option dontlognull
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

frontend http_front
    bind *:80

    acl is_static path_beg /static
    use_backend static_backend if is_static
    default_backend dynamic_backend

backend static_backend
    balance roundrobin
    server static1 127.0.0.1:5001 check

backend dynamic_backend
    balance roundrobin
    server dynamic1 127.0.0.1:5002 check

Explanation of the Configuration

  1. Frontend Configuration (http_front):
    • The frontend listens on ports 80 (HTTP).
    • An ACL (is_static) is defined to identify requests for static content based on the URL path prefix /static.
    • Requests that match the is_static ACL are routed to the static_backend. All other requests are routed to the dynamic_backend.
  2. Backend Configuration:
    • The static_backend handles static content requests and uses a round-robin strategy to distribute traffic between the servers (in this case, just static1).
    • The dynamic_backend handles all other requests in a similar manner.

Step 3: Deploying HAProxy with Docker

Sarah decided to use Docker to deploy HAProxy quickly:

Dockerfile for HAProxy:


FROM haproxy:2.4

COPY haproxy.cfg /usr/local/etc/haproxy/haproxy.cfg

Build and Run:


docker build -t haproxy-http .
docker run -d -p 80:80 -p 443:443 haproxy-http


This command runs HAProxy in a Docker container, listening on ports 80.

Step 4: Testing the Setup

Now, it was time to test!

  1. Static Content Test:
    • Sarah visited http://localhost:5000/static/logo.png. The L7 load balancer identified the /static path and routed the request to static_backend.
  2. Dynamic Content Test:
    • Visiting http://localhost:5000 or http://localhost:5000/api/data confirmed that requests were routed to the dynamic_backend as expected.

The Result: A Smoother Experience for โ€œFitGuruโ€

With L7 load balancing in place, โ€œFitGuruโ€ was now more responsive and could efficiently handle the incoming traffic surge:

  • Optimized Performance: Static content requests were efficiently served from servers dedicated to that purpose, while dynamic content was processed by more capable machines.
  • Enhanced Security: SSL termination was handled by HAProxy, and the backend servers were freed from CPU-intensive encryption tasks.
  • Flexible Traffic Management: Sarah could now easily add or modify rules to adapt to changing traffic patterns or requirements.

By implementing Layer 7 load balancing with HAProxy, Sarah provided โ€œFitGuruโ€ with a robust and scalable solution that ensured a seamless user experience, even during peak times. Now, she could confidently tackle the growing demands of their expanding user base, knowing the platform was built to handle whatever traffic came its way.

Layer 7 load balancing was more than just a tool; it was a strategy that allowed Sarah to understand, control, and optimize traffic in a way that best suited their applicationโ€™s unique needs. And with HAProxy, she had all the flexibility and power she needed to keep โ€œFitGuruโ€ running smoothly.

โŒ
โŒ