Moodle auf STACKIT Cloud Foundry
Find a file
2025-07-17 09:30:41 +00:00
.gitignore Bereinige manifest.yaml und füge es zu ignorierten Dateien 2025-07-07 11:55:00 +02:00
readme.md Update readme.md 2025-07-17 09:30:41 +00:00

Umfassende Anleitung: Moodle auf STACKIT Cloud Foundry

Diese Anleitung dokumentiert den gesamten Prozess, um eine Moodle-Instanz auf STACKIT Cloud Foundry zu deployen. Sie umfasst die Ersteinrichtung der CF-Umgebung, die Vorbereitung der Moodle-Anwendung inklusive aller notwendigen Workarounds für den Proxy- und CDN-Betrieb sowie die Konfiguration für eine externe Datenbank, S3-Dateispeicher, Redis-Caching und Autoskalierung.

Architektur

Die folgende Abbildung zeigt die Architektur der Moodle-Umgebung auf STACKIT als Mermaid-Diagramm.

graph TD
    A[Anwender] --> B(STACKIT DNS);
    B --> C(STACKIT CDN);
    C --> D{Cloud Foundry Router};
    
    subgraph Cloud Foundry Plattform
        D --> E1[Moodle Instanz 1];
        D --> E2[Moodle Instanz ...n];
    end
    
    subgraph Backing Services
        F[(MariaDB<br/>Datenbank)];
        G[(Redis<br/>Sessions & Locks)];
        H[(S3 Object Storage<br/>Dateien)];
    end

    E1 --> F;
    E1 --> G;
    E1 --> H;
    
    E2 --> F;
    E2 --> G;
    E2 --> H;

1. Voraussetzungen & initiales Setup

1.1. CLIs installieren

Für diese Anleitung werden zwei Command Line Interfaces (CLIs) benötigt:

  • Cloud Foundry CLI: Das primäre Werkzeug zur Interaktion mit der Cloud Foundry Plattform.

  • STACKIT CLI: Wird benötigt, um STACKIT-spezifische Services wie Object Storage zu verwalten.

1.2. Bei STACKIT Cloud Foundry anmelden

Nach der Installation des CF CLI müssen Sie sich bei der STACKIT Cloud Foundry API anmelden.

  • API-Endpunkt: https://api.cf.eu01.stackit.cloud

  • Login-Befehl:

    cf login -a [https://api.cf.eu01.stackit.cloud](https://api.cf.eu01.stackit.cloud) --sso
    

1.3. Organisation und Space erstellen

Eine "Organisation" ist der höchste Mandant, ein "Space" eine Umgebung (z.B. Entwicklung, Produktion).

  1. Organisation erstellen:

    cf create-org DEINE_ORGANISATION
    
  2. Space erstellen:

    cf create-space produktion -o DEINE_ORGANISATION
    
  3. Ziel festlegen:

    cf target -o DEINE_ORGANISATION -s produktion
    

2. Moodle-Projekt lokal vorbereiten

2.1. Moodle und Plugins herunterladen

# Moodle 5.0.1 herunterladen und entpacken
wget [https://packaging.moodle.org/stable500/moodle-5.0.1.tgz](https://packaging.moodle.org/stable500/moodle-5.0.1.tgz)
tar -xzf moodle-5.0.1.tgz

# Redis-Plugin für Moodle installieren
wget [https://moodle.org/plugins/download.php/29037/cachestore_redis_moodle41_2022112800.zip](https://moodle.org/plugins/download.php/29037/cachestore_redis_moodle41_2022112800.zip)
unzip cachestore_redis_moodle41_2022112800.zip
mv redis moodle/cache/stores/

# S3 Object File System Plugin installieren
cd moodle
git clone [https://github.com/catalyst/moodle-tool_objectfs.git](https://github.com/catalyst/moodle-tool_objectfs.git) admin/tool/objectfs
cd ..

2.2. Buildpack- & System-Workarounds einrichten

Diese Schritte sind für das cloudfoundry/php-buildpack notwendig.

# Web-Verzeichnis festlegen und PHP-Version auf 8.2 erzwingen
echo '{"WEBDIR": ".", "PHP_VERSION": "{PHP_82_LATEST}"}' > moodle/.bp-config/options.json

# PHP-Erweiterungen und Moodle-Einstellungen konfigurieren
mkdir -p moodle/.bp-config/php/php.ini.d/
echo "extension=mysqli" > moodle/.bp-config/php/php.ini.d/mysqli.ini
echo "extension=redis" > moodle/.bp-config/php/php.ini.d/redis.ini
echo "max_input_vars = 5000" > moodle/.bp-config/php/php.ini.d/moodle.ini

# Symbolische Links für Log- und PID-Dateien anlegen
mkdir -p moodle/logs
ln -s /dev/stdout moodle/logs/rewrite.log
ln -s /dev/stdout moodle/logs/proc-man.log
mkdir -p moodle/httpd/logs
mkdir -p moodle/php/var/run
ln -s /tmp/httpd.pid moodle/httpd/logs/httpd.pid
ln -s /tmp/php-fpm.pid moodle/php/var/run/php-fpm.pid

3. Cloud Foundry & STACKIT Services erstellen

3.1. Object Storage Bucket erstellen (STACKIT CLI)

Verwenden Sie das STACKIT CLI, um einen Bucket zu erstellen.

# Erstellt einen neuen Bucket
stackit object-storage bucket create --name <BUCKET_NAME>

Anschließend benötigen Sie Zugangsdaten, um auf den Bucket zugreifen zu können.

# Erstellt neue Zugangsdaten für Ihr Projekt
# Sie müssen Ihre STACKIT Projekt-ID angeben.
stackit object-storage credential create --project-id <DEINE_PROJEKT_ID>

Notieren Sie sich den ausgegebenen accessKey und secretAccessKey. Sie werden im nächsten Schritt benötigt.

3.2. Cloud Foundry Services erstellen (CF CLI)

Diese Befehle legen die für Moodle notwendigen CF-Services an.

# Datenbank-Service
cf create-service <dein-mariadb-service> <plan> moodle-demo-mariadb

# Redis-Service (Optional)
cf create-service appcloud-redis7 redis-4.16.100-replica moodle-redis

# Autoscaler-Service
cf create-service autoscaler autoscaler-free-plan moodle-autoscaler

# User-Provided Service für die S3-Credentials aus dem vorherigen Schritt
cf create-user-provided-service moodle-s3-credentials -p '{
  "access_key": "DEIN_ZUVOR_GENERIERTER_ACCESS_KEY",
  "secret_key": "DEIN_ZUVOR_GENERIERTER_SECRET_KEY",
  "bucket": "DEIN_ZUVOR_GENERIERTES_BUCKET",
  "endpoint": "[https://s3.eu01.stackit.cloud](https://s3.eu01.stackit.cloud)"
}'

4. Konfigurationsdateien erstellen

a) Manifest-Dateien

Erstellen Sie zwei Manifest-Dateien, um einfach zwischen dem Betrieb mit und ohne CDN wechseln zu können.

manifest-cdn.yml (Produktion):

---
applications:
- name: moodle
  memory: 2G
  disk_quota: 2G
  buildpack: [https://github.com/cloudfoundry/php-buildpack.git](https://github.com/cloudfoundry/php-buildpack.git)
  path: moodle
  routes:
  - route: moodle.cf.demo.stackit.rocks
  - route: moodle.demo.stackit.rocks
  env:
    USE_CDN: 'true'
    WWW_ROOT: "[https://moodle.demo.stackit.rocks](https://moodle.demo.stackit.rocks)"
    BP_PHP_LOG_LEVEL: WARNING
  services:
  - moodle-demo-mariadb
  - moodle-redis
  - moodle-autoscaler
  - moodle-s3-credentials
  autoscaling:
    min_instances: 2
    max_instances: 4
    rules:
    - metric_type: cpu
      threshold: 50
      operator: ">"
      adjustment: "+1"
      breach_duration_secs: 60
      cool_down_secs: 60
    - metric_type: cpu
      threshold: 10
      operator: "<"
      adjustment: "-1"
      breach_duration_secs: 60
      cool_down_secs: 60

manifest-no-cdn.yml (Test):

---
applications:
- name: moodle
  memory: 2G
  disk_quota: 2G
  buildpack: [https://github.com/cloudfoundry/php-buildpack.git](https://github.com/cloudfoundry/php-buildpack.git)
  path: moodle
  routes:
  - route: moodle.cf.demo.stackit.rocks
  env:
    USE_CDN: 'false'
    WWW_ROOT: "[https://moodle.cf.demo.stackit.rocks](https://moodle.cf.demo.stackit.rocks)"
    BP_PHP_LOG_LEVEL: WARNING
  services:
  - moodle-demo-mariadb
  - moodle-redis
  - moodle-autoscaler
  - moodle-s3-credentials
  autoscaling:
    min_instances: 1
    max_instances: 4
    # ... deine Regeln

b) Dynamische config.php

Kopieren Sie moodle/config-dist.php zu moodle/config.php und ersetzen Sie den gesamten Inhalt mit dieser finalen, dynamischen Vorlage.

<?php  // Moodle configuration file

// =========================================================================
// UMWELTSPEZIFISCHE KONFIGURATION
// =========================================================================
$useCdn = (getenv('USE_CDN') === 'true');
$wwwRoot = getenv('WWW_ROOT');

unset($CFG);
global $CFG;
$CFG = new stdClass();

//=========================================================================
// 1. WEBSEITE, VERZEICHNISSE & PROXY EINSTELLUNGEN
//=========================================================================
$CFG->wwwroot        = $wwwRoot;
$CFG->dirroot        = __DIR__;
$CFG->dataroot       = '/tmp/moodledata';
$CFG->admin          = 'admin';
$CFG->directorypermissions = 0777;

// --- Proxy-Einstellungen ---
$CFG->sslproxy       = 1;
$CFG->slasharguments = false;
if ($useCdn) {
    // Diese Einstellungen sind NUR für den CDN-Betrieb notwendig, um den "reverseproxyabused"-Fehler zu vermeiden.
    $CFG->reverseproxy   = 1;
    $CFG->trustedproxies = '127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 185.124.192.185';
}

// --- Workaround: Manuelles Erstellen des dataroot-Verzeichnisses ---
if (!file_exists($CFG->dataroot)) {
    @mkdir($CFG->dataroot, $CFG->directorypermissions, true);
}

//=========================================================================
// 2. DATENBANK-SETUP
//=========================================================================
$CFG->dbtype    = 'mariadb';
$CFG->dblibrary = 'native';
$CFG->prefix    = 'mdl_';
$CFG->dboptions = array ('dbpersist' => 0, 'dbcollation' => 'utf8mb4_unicode_ci');

// --- Lese Service-Konfigurationen aus VCAP_SERVICES ---
$vcap_services_json = getenv('VCAP_SERVICES');
if ($vcap_services_json) {
    $vcap_services = json_decode($vcap_services_json, true);

    // Datenbank
    $db_service_key = null;
    if (isset($vcap_services['appcloud-mariadb1011'])) { $db_service_key = 'appcloud-mariadb1011'; }
    if ($db_service_key && isset($vcap_services[$db_service_key][0]['credentials'])) {
        $db_creds = $vcap_services[$db_service_key][0]['credentials'];
        $CFG->dbhost = $db_creds['host']; $CFG->dbname = $db_creds['name'];
        $CFG->dbuser = $db_creds['username']; $CFG->dbpass = $db_creds['password'];
        $CFG->dboptions['dbport'] = $db_creds['port'];
        if (isset($db_creds['cacrt']) && !empty($db_creds['cacrt'])) {
            $ca_cert_path = '/tmp/db-ca.crt';
            file_put_contents($ca_cert_path, $db_creds['cacrt']);
            $CFG->dboptions['ssl_ca'] = $ca_cert_path;
            $CFG->dboptions['ssl_verify_server_cert'] = true;
        }
    }

    // Redis (Optional, für bessere Performance)
    $redis_service_key = null;
    if (isset($vcap_services['appcloud-redis60'])) { $redis_service_key = 'appcloud-redis60'; }
    if ($redis_service_key && isset($vcap_services[$redis_service_key][0]['credentials'])) {
        $redis_creds = $vcap_services[$redis_service_key][0]['credentials'];
        $CFG->session_handler_class = '\cachestore_redis\session_handler';
        $CFG->session_redis_host = $redis_creds['host']; $CFG->session_redis_port = $redis_creds['port'];
        $CFG->session_redis_password = $redis_creds['password'];
        $CFG->session_redis_prefix = 'moodlesess_';
        $CFG->lock_factory = '\cachestore_redis\lock_factory';
    }

    // S3
    if (isset($vcap_services['user-provided'])) {
        foreach ($vcap_services['user-provided'] as $service) {
            if ($service['name'] === 'moodle-s3-credentials') {
                $s3_creds = $service['credentials'];
                $CFG->objectfs_s3_key = $s3_creds['access_key'];
                $CFG->objectfs_s3_secret = $s3_creds['secret_key'];
                $CFG->objectfs_s3_bucket = $s3_creds['bucket'];
                $CFG->objectfs_s3_endpoint = $s3_creds['endpoint'];
                break;
            }
        }
    }
}
// Fallback auf Datenbank-Sessions, falls Redis nicht konfiguriert ist
if (empty($CFG->session_handler_class)) {
    $CFG->session_handler_class = '\core\session\database';
}

//=========================================================================
// SESSION COOKIE HANDLING (bedingt durch CDN)
//=========================================================================
if ($useCdn) {
    $CFG->cookiepath = '/';
    $CFG->cookiedomain = 'moodle.demo.stackit.rocks';
    $CFG->cookiesecure = 1;
    $CFG->cookiehttponly = 1;
}

//=========================================================================
// DO NOT ADD ANYTHING BELOW THIS LINE
//=========================================================================
require_once(__DIR__ . '/lib/setup.php');

5. Deployment auf Cloud Foundry

Wählen Sie das gewünschte Manifest für Ihr Deployment aus.

Mit CDN (Produktion):

cf push -f manifest-cdn.yml

Hinweis: Der CDN-Fall hat sich als problematisch erwiesen. Wenn Fehler auftreten, liegt es wahrscheinlich an den Caching- oder Header-Einstellungen im CDN selbst, die hier nicht behandelt werden können.

Ohne CDN (Test / Direkter Zugriff):

cf push -f manifest-no-cdn.yml

Nach Abschluss des Deployments und dem Durchlaufen der Moodle-Web-Installation ist Ihre Instanz einsatzbereit.