Learn by Directing AI
All materials

docker-compose.yml

ymldocker-compose.yml
# VERIFICATION: Run `docker compose up -d` and verify all services are healthy before use.
# Expected services: tenant-wifi-1, tenant-wifi-2, bms-doha, bms-alwakra, cctv-manager, access-control, grafana, loki, alloy
# Expected ports: grafana:3001, loki:3100, bms-doha:8080, bms-alwakra:8081, cctv-manager:9090, access-control:7070

services:
  # Tenant Wi-Fi simulation - Building 1 (Doha)
  tenant-wifi-1:
    image: alpine:3.19
    container_name: tenant-wifi-1
    command: >
      sh -c "apk add --no-cache nmap curl netcat-openbsd && tail -f /dev/null"
    networks:
      - default
    labels:
      zone: "tenant"
      building: "doha-1"
    restart: unless-stopped

  # Tenant Wi-Fi simulation - Building 2 (Al Wakra)
  tenant-wifi-2:
    image: alpine:3.19
    container_name: tenant-wifi-2
    command: >
      sh -c "apk add --no-cache nmap curl netcat-openbsd && tail -f /dev/null"
    networks:
      - default
    labels:
      zone: "tenant"
      building: "alwakra-1"
    restart: unless-stopped

  # Building Management System - Doha (modern, supports authentication)
  bms-doha:
    image: python:3.11-alpine
    container_name: bms-doha
    command: >
      sh -c "pip install flask && python -c \"
      from flask import Flask, jsonify, request
      app = Flask(__name__)
      @app.route('/status')
      def status():
          return jsonify({'building': 'Doha Tower 1', 'hvac': 'operational', 'fire_suppression': 'armed', 'elevators': 'running'})
      @app.route('/auth', methods=['POST'])
      def auth():
          token = request.headers.get('Authorization')
          if token == 'Bearer maskan-bms-2024':
              return jsonify({'authenticated': True})
          return jsonify({'authenticated': False}), 401
      @app.route('/control', methods=['POST'])
      def control():
          token = request.headers.get('Authorization')
          if token != 'Bearer maskan-bms-2024':
              return jsonify({'error': 'unauthorized'}), 401
          return jsonify({'result': 'command accepted'})
      app.run(host='0.0.0.0', port=8080)
      \""
    ports:
      - "8080:8080"
    networks:
      - default
    labels:
      zone: "building-systems"
      building: "doha-1"
    restart: unless-stopped

  # Building Management System - Al Wakra (LEGACY - no authentication)
  bms-alwakra:
    image: python:3.11-alpine
    container_name: bms-alwakra
    command: >
      sh -c "pip install flask && python -c \"
      from flask import Flask, jsonify, request
      app = Flask(__name__)
      @app.route('/status')
      def status():
          return jsonify({'building': 'Al Wakra Complex', 'hvac': 'operational', 'fire_suppression': 'armed', 'access_doors': '14 units'})
      @app.route('/control', methods=['POST'])
      def control():
          return jsonify({'result': 'command accepted', 'warning': 'no authentication configured'})
      app.run(host='0.0.0.0', port=8080)
      \""
    ports:
      - "8081:8080"
    networks:
      - default
    labels:
      zone: "building-systems"
      building: "alwakra-1"
      legacy: "true"
    restart: unless-stopped

  # CCTV Management System (with VPN simulation port)
  cctv-manager:
    image: python:3.11-alpine
    container_name: cctv-manager
    command: >
      sh -c "apk add --no-cache socat && pip install flask && (socat TCP-LISTEN:1194,fork,reuseaddr SYSTEM:'echo VPN-connected' &) && python -c \"
      from flask import Flask, jsonify
      app = Flask(__name__)
      @app.route('/cameras')
      def cameras():
          return jsonify({'cameras': 48, 'buildings': 12, 'nvrs': 6, 'remote_access': 'vpn_enabled'})
      @app.route('/feeds')
      def feeds():
          return jsonify({'live_feeds': 48, 'recording': True, 'retention_days': 30})
      app.run(host='0.0.0.0', port=9090)
      \""
    ports:
      - "9090:9090"
      - "1194:1194"
    networks:
      - default
    labels:
      zone: "surveillance"
      vendor_access: "third-party-vpn"
    restart: unless-stopped

  # Electronic Access Control System
  access-control:
    image: python:3.11-alpine
    container_name: access-control
    command: >
      sh -c "pip install flask && python -c \"
      from flask import Flask, jsonify, request
      app = Flask(__name__)
      @app.route('/doors')
      def doors():
          return jsonify({'total_doors': 156, 'buildings': 12, 'locked': 148, 'unlocked': 8})
      @app.route('/unlock', methods=['POST'])
      def unlock():
          token = request.headers.get('Authorization')
          if token != 'Bearer maskan-acl-2024':
              return jsonify({'error': 'unauthorized'}), 401
          return jsonify({'result': 'door unlocked'})
      app.run(host='0.0.0.0', port=7070)
      \""
    ports:
      - "7070:7070"
    networks:
      - default
    labels:
      zone: "access-control"
    restart: unless-stopped

  # Grafana - Monitoring Dashboard
  grafana:
    image: grafana/grafana:10.2.3
    container_name: grafana
    ports:
      - "3001:3000"
    environment:
      - GF_SECURITY_ADMIN_USER=admin
      - GF_SECURITY_ADMIN_PASSWORD=maskan2024
      - GF_AUTH_ANONYMOUS_ENABLED=true
      - GF_AUTH_ANONYMOUS_ORG_ROLE=Viewer
    volumes:
      - grafana-data:/var/lib/grafana
    networks:
      - default
    labels:
      zone: "management"
    restart: unless-stopped

  # Loki - Log Aggregation
  loki:
    image: grafana/loki:2.9.3
    container_name: loki
    ports:
      - "3100:3100"
    command: -config.file=/etc/loki/local-config.yaml
    networks:
      - default
    labels:
      zone: "management"
    restart: unless-stopped

  # Alloy - Log Collection
  alloy:
    image: grafana/alloy:v1.0.0
    container_name: alloy
    volumes:
      - ./alloy-config.yaml:/etc/alloy/config.alloy
      - /var/run/docker.sock:/var/run/docker.sock:ro
    command: run /etc/alloy/config.alloy
    networks:
      - default
    depends_on:
      - loki
    labels:
      zone: "management"
    restart: unless-stopped

volumes:
  grafana-data:

networks:
  default:
    driver: bridge
    name: maskan-default