Skip to main content

Overview

This example shows how to containerize and deploy a web service using Buildr. You’ll learn how to expose ports, configure replicas for scaling, and make your service publicly accessible.

Complete Example

Here’s a complete HTTP server that’s automatically containerized and deployed:
#!/usr/bin/python

from six.moves import SimpleHTTPServer, socketserver
import socket
from metaparticle_pkg.containerize import Containerize

OK = 200

port = 8080


class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        self.send_response(OK)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        self.wfile.write("Hello Metaparticle [{}] @ {}\n".format(self.path, socket.gethostname()).encode('UTF-8'))
        print("request for {}".format(self.path))

    def do_HEAD(self):
        self.send_response(OK)
        self.send_header("Content-type", "text/plain")
        self.end_headers()


@Containerize(
    package={'name': 'web', 'repository': 'docker.io/brendanburns'},
    runtime={'ports': [8080], 'executor': 'metaparticle', 'replicas': 3, 'public': True}
)
def main():
    Handler = MyHandler
    httpd = socketserver.TCPServer(("", port), Handler)
    httpd.serve_forever()


if __name__ == '__main__':
    main()

Step-by-Step Breakdown

1

Import dependencies

from six.moves import SimpleHTTPServer, socketserver
import socket
from metaparticle_pkg.containerize import Containerize
  • SimpleHTTPServer: Basic HTTP server implementation
  • socketserver: TCP server framework
  • socket: For getting hostname information
  • Containerize: Buildr decorator for containerization
2

Create HTTP request handler

class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        self.send_response(OK)
        self.send_header("Content-type", "text/plain")
        self.end_headers()
        self.wfile.write("Hello Metaparticle [{}] @ {}\n".format(
            self.path, socket.gethostname()).encode('UTF-8'))
        print("request for {}".format(self.path))
Custom handler that:
  • Responds to GET requests with a 200 OK status
  • Returns the requested path and hostname
  • Shows which container instance handled the request
The hostname in the response helps you see load balancing across replicas in action!
3

Configure containerization with scaling

@Containerize(
    package={'name': 'web', 'repository': 'docker.io/brendanburns'},
    runtime={
        'ports': [8080], 
        'executor': 'metaparticle', 
        'replicas': 3, 
        'public': True
    }
)
def main():
    Handler = MyHandler
    httpd = socketserver.TCPServer(("", port), Handler)
    httpd.serve_forever()
This configuration does several important things:Package Configuration:
  • name: 'web' - The container image name
  • repository: 'docker.io/brendanburns' - Docker Hub repository
Runtime Configuration:
  • ports: [8080] - Exposes port 8080 for HTTP traffic
  • executor: 'metaparticle' - Uses Metaparticle runtime for orchestration
  • replicas: 3 - Runs 3 instances of the service for load balancing
  • public: True - Makes the service accessible from outside the cluster
4

Start the server

if __name__ == '__main__':
    main()
When executed, Buildr will:
  1. Build a Docker image containing your web service
  2. Deploy 3 replicas to the Metaparticle runtime
  3. Configure load balancing across all replicas
  4. Expose the service publicly on port 8080

Configuration Options Explained

Package Options

OptionTypeDescriptionExample
namestringService/image name'web'
repositorystringDocker registry repository'docker.io/yourusername'

Runtime Options

OptionTypeDescriptionDefault
portsarrayPorts to expose[]
executorstringRuntime executor (docker, metaparticle)'docker'
replicasnumberNumber of instances to run1
publicbooleanMake service publicly accessiblefalse
The metaparticle executor provides orchestration features like replicas and load balancing. Use docker executor for simple local testing.

Running the Example

1

Save the code

Save the example to web.py
2

Create requirements.txt

metaparticle-pkg
six
3

Run the service

python web.py
Buildr will build, deploy, and start your service with 3 replicas.
4

Test the service

curl http://localhost:8080/hello
You’ll see responses from different container instances:
Hello Metaparticle [/hello] @ web-abc123
Hello Metaparticle [/hello] @ web-def456
Hello Metaparticle [/hello] @ web-ghi789

Scaling Your Service

Buildr makes it easy to scale your web service by adjusting the replicas configuration:
# Single instance for development
runtime={'replicas': 1, 'public': False}

# 3 replicas for production
runtime={'replicas': 3, 'public': True}

# High availability with 10 replicas
runtime={'replicas': 10, 'public': True}
Start with fewer replicas during development and increase them in production based on your traffic patterns and performance requirements.

Load Balancing

When you set replicas > 1, Buildr automatically:
  1. Distributes traffic across all container instances
  2. Health checks each replica to ensure availability
  3. Routes requests to healthy instances only
  4. Balances load using round-robin or other algorithms

Public vs Private Services

Public Service (public: True)

  • Accessible from the internet
  • Gets an external IP or load balancer
  • Suitable for customer-facing applications

Private Service (public: False)

  • Only accessible within the cluster
  • No external exposure
  • Suitable for internal microservices
# Public-facing API
@Containerize(
    package={'name': 'api', 'repository': 'myrepo'},
    runtime={'ports': [80], 'public': True, 'replicas': 5}
)

# Internal service
@Containerize(
    package={'name': 'worker', 'repository': 'myrepo'},
    runtime={'public': False, 'replicas': 2}
)

Best Practices

1

Use appropriate replica counts

  • Development: 1 replica
  • Staging: 2-3 replicas
  • Production: 3+ replicas (odd numbers for quorum-based systems)
2

Configure health checks

Implement health check endpoints:
def do_GET(self):
    if self.path == '/health':
        self.send_response(200)
        self.end_headers()
        return
    # ... normal request handling
3

Use environment-specific repositories

import os
repo = os.getenv('DOCKER_REPO', 'docker.io/myuser')

Build docs developers (and LLMs) love