feat(fog): add labs and reports

This commit is contained in:
maxbarsukov 2025-12-30 22:18:20 +03:00
parent 1d3aa14a60
commit 37f8b85021
26 changed files with 485 additions and 0 deletions

View File

@ -0,0 +1,4 @@
REDIS_PASSWORD=password
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DB=0

View File

@ -0,0 +1,68 @@
__pycache__/
*.py[codz]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
*.manifest
*.spec
pip-log.txt
pip-delete-this-directory.txt
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
instance/
.webassets-cache
.python-version
Pipfile.lock
poetry.lock
poetry.toml
__pypackages__/
*.rdb
*.aof
*.pid
.env
.envrc
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.idea/
.vscode/

View File

@ -0,0 +1,18 @@
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
FLASK_APP=app.py \
FLASK_RUN_HOST=0.0.0.0 \
FLASK_RUN_PORT=5000
WORKDIR /app
COPY app/requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ ./
EXPOSE 5000
CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"]

View File

@ -0,0 +1,29 @@
import os
from flask import Flask, jsonify
import redis
redis_client = redis.Redis(
host=os.getenv("REDIS_HOST", "redis"),
port=int(os.getenv("REDIS_PORT", "6379")),
db=int(os.getenv("REDIS_DB", "0")),
password=os.getenv("REDIS_PASSWORD"),
decode_responses=True,
)
app = Flask(__name__)
@app.route("/")
def index():
count = redis_client.incr("hits")
return jsonify(visit_count=count), 200
@app.route("/health")
def health():
try:
redis_client.ping()
return jsonify(status="ok"), 200
except redis.RedisError as exc:
return jsonify(status="failed", detail=str(exc)), 500
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

View File

@ -0,0 +1,2 @@
Flask==2.3.3
redis==5.0.1

View File

@ -0,0 +1,34 @@
services:
frontend:
build: .
image: lab1-frontend:latest
ports:
- "5002:5000"
env_file:
- .env
environment:
- REDIS_HOST=${REDIS_HOST:-redis}
- REDIS_PORT=${REDIS_PORT:-6379}
- REDIS_DB=${REDIS_DB:-0}
- REDIS_PASSWORD=${REDIS_PASSWORD}
depends_on:
- redis
deploy:
replicas: 1
restart_policy:
condition: any
redis:
image: redis:7-alpine
env_file:
- .env
command: ["redis-server", "--appendonly", "yes", "--requirepass", "${REDIS_PASSWORD}"]
volumes:
- redis-data:/data
deploy:
replicas: 1
restart_policy:
condition: any
volumes:
redis-data:

View File

@ -0,0 +1,7 @@
REDIS_PASSWORD=password
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DB=0
APP_PORT=5000
APP_NODE_PORT=31010
APP_REPLICAS=3

View File

@ -0,0 +1,68 @@
__pycache__/
*.py[codz]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
*.manifest
*.spec
pip-log.txt
pip-delete-this-directory.txt
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py.cover
.hypothesis/
.pytest_cache/
cover/
instance/
.webassets-cache
.python-version
Pipfile.lock
poetry.lock
poetry.toml
__pypackages__/
*.rdb
*.aof
*.pid
.env
.envrc
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
.idea/
.vscode/

View File

@ -0,0 +1,18 @@
FROM python:3.11-slim
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
FLASK_APP=app.py \
FLASK_RUN_HOST=0.0.0.0 \
FLASK_RUN_PORT=5000
WORKDIR /app
COPY app/requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ ./
EXPOSE 5000
CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"]

View File

@ -0,0 +1,29 @@
import os
from flask import Flask, jsonify
import redis
redis_client = redis.Redis(
host=os.getenv("REDIS_HOST", "redis"),
port=int(os.getenv("REDIS_PORT", "6379")),
db=int(os.getenv("REDIS_DB", "0")),
password=os.getenv("REDIS_PASSWORD"),
decode_responses=True,
)
app = Flask(__name__)
@app.route("/")
def index():
count = redis_client.incr("hits")
return jsonify(visit_count=count, message="Hello"), 200
@app.route("/health")
def health():
try:
redis_client.ping()
return jsonify(status="ok"), 200
except redis.RedisError as exc:
return jsonify(status="failed", detail=str(exc)), 500
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

View File

@ -0,0 +1,2 @@
Flask==2.3.3
redis==5.0.1

View File

@ -0,0 +1,25 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: python-app
spec:
replicas: ${APP_REPLICAS}
selector:
matchLabels:
app: python-app
template:
metadata:
labels:
app: python-app
spec:
containers:
- name: python-app
image: ${APP_IMAGE}
imagePullPolicy: Never
ports:
- containerPort: ${APP_PORT}
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: app-secrets

View File

@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: python-app-service
spec:
type: NodePort
selector:
app: python-app
ports:
- port: ${APP_PORT}
targetPort: ${APP_PORT}
nodePort: ${APP_NODE_PORT}

View File

@ -0,0 +1,16 @@
apiVersion: v1
kind: Secret
metadata:
name: app-secrets
type: Opaque
stringData:
REDIS_PASSWORD: password
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
data:
REDIS_HOST: redis
REDIS_PORT: "${REDIS_PORT}"
REDIS_DB: "0"

View File

@ -0,0 +1,23 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
envFrom:
- secretRef:
name: app-secrets
command: ["sh", "-c", "redis-server --appendonly yes --requirepass $REDIS_PASSWORD"]
ports:
- containerPort: ${REDIS_PORT}

View File

@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: redis
spec:
type: ClusterIP
selector:
app: redis
ports:
- port: ${REDIS_PORT}
targetPort: ${REDIS_PORT}

View File

@ -0,0 +1,26 @@
import paho.mqtt.client as mqtt
import os
import json
BROKER = os.getenv("MQTT_BROKER", "cloud-broker")
PORT = int(os.getenv("MQTT_PORT", 1883))
TOPIC = os.getenv("MQTT_TOPIC", "cloud/alerts")
def on_connect(client, userdata, flags, reason_code, properties):
print(f"Connected to Cloud Broker with result code {reason_code}")
client.subscribe(TOPIC)
def on_message(client, userdata, msg):
try:
data = json.loads(msg.payload.decode())
print(f"ALERT RECEIVED: Temp={data.get('temp')}°C at {data.get('ts')}")
except:
print(f"Received raw message: {msg.payload.decode()}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.on_connect = on_connect
client.on_message = on_message
print(f"Connecting to {BROKER}...")
client.connect(BROKER, PORT, 60)
client.loop_forever()

View File

@ -0,0 +1,61 @@
services:
edge-broker:
image: eclipse-mosquitto:2
container_name: edge-broker
ports:
- "1883:1883"
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
networks:
- factory-net
sensor:
build: ./sensor
container_name: sensor
environment:
- MQTT_BROKER=edge-broker
- MQTT_TOPIC=factory/temp
depends_on:
- edge-broker
networks:
- factory-net
fog-node:
image: nodered/node-red
container_name: fog-node
ports:
- "1880:1880"
environment:
- TZ=Europe/Moscow
volumes:
- node-red-data:/data
networks:
- factory-net
cloud-broker:
image: eclipse-mosquitto:2
container_name: cloud-broker
ports:
- "1884:1883"
volumes:
- ./mosquitto.conf:/mosquitto/config/mosquitto.conf
networks:
- factory-net
cloud-dashboard:
build: ./cloud_dashboard
container_name: cloud-dashboard
environment:
- MQTT_BROKER=cloud-broker
- MQTT_TOPIC=cloud/alerts
depends_on:
- cloud-broker
networks:
- factory-net
networks:
factory-net:
driver: bridge
volumes:
node-red-data:

View File

@ -0,0 +1,2 @@
listener 1883
allow_anonymous true

View File

@ -0,0 +1,30 @@
import time
import random
import json
import paho.mqtt.client as mqtt
import os
BROKER = os.getenv("MQTT_BROKER", "edge-broker")
PORT = int(os.getenv("MQTT_PORT", 1883))
TOPIC = os.getenv("MQTT_TOPIC", "factory/temp")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
def connect():
while True:
try:
client.connect(BROKER, PORT, 60)
print(f"Connected to {BROKER}:{PORT}")
return
except Exception as e:
print(f"Connection failed: {e}. Retrying in 5s...")
time.sleep(5)
connect()
while True:
temp = random.randint(20, 100)
payload = json.dumps({"temp": temp, "ts": time.time()})
client.publish(TOPIC, payload)
print(f"Sent: {payload} to {TOPIC}")
time.sleep(1)