Zum Inhalt

Monte Carlo

Übersicht

In diesem Projekt wird eine verteilte Berechnung verwendet, um eine Näherung für die Zahl Pi zu finden. Der Manager-Container sammelt Näherungswerte von Worker-Containern, die diese Berechnungen parallel mithilfe eines Monte-Carlo-Algorithmus durchführen. Der Durchschnitt dieser Werte ergibt die Näherung von Pi.

1. Manager-Server (Flask-Webserver)

Der Manager fungiert als zentraler Server und empfängt die von den Workern berechneten Pi-Schätzungen. Er speichert die Ergebnisse und bietet eine API an, über die der Durchschnitt aller eingegangenen Werte abgefragt werden kann.

manager.py

    from flask import Flask, request, jsonify

    app = Flask(__name__)
    results = []

    @app.route('/submit', methods=['POST'])
    def submit_result():
        data = request.json
        result = data.get('pi_estimate')
        if result:
            results.append(result)
            return jsonify({"message": "Result received"}), 200
        else:
            return jsonify({"message": "Invalid data"}), 400

    @app.route('/average', methods=['GET'])
    def get_average():
        if len(results) == 0:
            return jsonify({"average": None, "message": "No results yet"}), 200
        average = sum(results) / len(results)
        return jsonify({"average": average}), 200

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

2. Worker-Skript mit Monte-Carlo-Algorithmus

Jeder Worker führt eine Monte-Carlo-Berechnung durch, um eine Näherung für Pi zu berechnen, und sendet das Ergebnis an den Manager.

worker.py

import random
import requests
import json
import time

def monte_carlo_pi(n):
    inside_circle = 0
    for _ in range(n):
        x, y = random.random(), random.random()
        if x**2 + y**2 <= 1:
            inside_circle += 1
    return (4 * inside_circle) / n

def send_result_to_manager(pi_estimate):
    url = 'http://manager:5000/submit'
    data = {'pi_estimate': pi_estimate}
    headers = {'Content-Type': 'application/json'}
    response = requests.post(url, data=json.dumps(data), headers=headers)
    if response.status_code == 200:
        print("Result sent successfully.")
    else:
        print("Error sending result.")

if __name__ == '__main__':
    n = 1000000  # Number of points for Pi approximation
    while True:
        pi_estimate = monte_carlo_pi(n)
        send_result_to_manager(pi_estimate)
        time.sleep(5)  # Wait 5 seconds before repeating

3. Docker-Setup

Dockerfile für den Manager

# Manager Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY manager.py /app/
RUN pip install flask
CMD ["python", "manager.py"]

Dockerfile für den Worker

# Worker Dockerfile
FROM python:3.9-slim
WORKDIR /app
COPY worker.py /app/
RUN pip install requests
CMD ["python", "worker.py"]

4. Docker Compose für den Swarm

Mit Docker Compose wird das gesamte Setup konfiguriert. Hier wird ein Overlay-Netzwerk definiert, das die Kommunikation zwischen Manager und Workern ermöglicht.

Docker Compose Datei: docker-compose.yml

services:
  manager:
    build:
      context: ./manager
      dockerfile: Dockerfile
    ports:
      - "5000:5000"
    deploy:
      replicas: 1
    networks:
      - pi_network

  worker:
    build:
      context: ./worker
      dockerfile: Dockerfile
    deploy:
      replicas: 2
    networks:
      - pi_network

networks:
  pi_network:
    driver: overlay

5. Schritte zur Ausführung

  1. Erstelle die Dockerfiles für den Manager und Worker.

  2. Baue die Images:

docker-compose build
  1. Initialisiere den Swarm und starte die Services:
docker swarm init --advertise-addr <Manager-IP>
docker stack deploy -c docker-compose.yml pi_stack

Die Worker senden nun ihre berechneten Pi-Werte an den Manager, und der Durchschnitt der Ergebnisse kann über den /average-Endpunkt abgefragt werden.

6. Kommunikation und Netzwerk

Docker Swarm nutzt ein Overlay-Netzwerk, um die Kommunikation zwischen Containern zu vereinfachen. Der Manager und die Worker können sich so über ihre Servicenamen finden, ohne eine explizite IP-Adresse festzulegen.