Moodle auf STACKIT Cloud Foundry
Find a file
2025-07-18 10:50:39 +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-18 10:50:39 +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 ist als allgemeingültige Vorlage konzipiert und verwendet Platzhalter, die an Ihre Umgebung angepasst werden müssen.

Architektur

Die folgende Abbildung zeigt die Architektur der Moodle-Umgebung. CDN und Redis sind als optionale, aber für den Produktivbetrieb empfohlene Komponenten dargestellt.

graph TD
    A[Anwender] --> B(STACKIT DNS);
    B --> C(STACKIT CDN<br/>(Optional));
    C -.-> D{Cloud Foundry Router};
    B --> D;
    
    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<br/>(Optional))];
        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

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

    • Download: https://github.com/cloudfoundry/cli/releases
  • STACKIT CLI: Wird benötigt, um STACKIT-spezifische Services wie DNS, Object Storage und CDN zu verwalten.

    • Installationsanleitung: https://docs.stackit.cloud/stackit/de/automatisieren/cli/cli-installieren-200720436.html

1.2. Bei STACKIT Cloud Foundry anmelden

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

  • Login-Befehl:

    cf login -a https://api.cf.eu01.stackit.cloud --sso
    

1.3. Organisation und Space erstellen

cf create-org <DEINE_ORGANISATION>
cf create-space <DEIN_SPACE> -o <DEINE_ORGANISATION>
cf target -o <DEINE_ORGANISATION> -s <DEIN_SPACE>

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
tar -xzf moodle-5.0.1.tgz

# Redis-Plugin für Moodle installieren (Version passend zu Moodle 5.x)
wget https://moodle.org/plugins/download.php/30019/cachestore_redis_moodle44_2024051300.zip
unzip cachestore_redis_moodle44_2024051300.zip
mv redis moodle/cache/stores/

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

2.2. Buildpack- & System-Workarounds einrichten

# 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)

stackit object-storage bucket create --name <DEIN_S3_BUCKET_NAME>
stackit object-storage credential create --project-id <DEINE_PROJEKT_ID>

Notieren Sie sich den ausgegebenen accessKey und secretAccessKey.

3.2. CDN-Distribution erstellen (STACKIT CLI, Optional)

  1. CDN-Distribution erstellen:

    # Ersetzen Sie <DEINE_PROJEKT_ID>, <DEINE_INTERNE_CF_ROUTE> und <DEINE_MOODLE_DOMAIN>.
    stackit curl -X POST --body '{
      "name": "<DEIN_CDN_DISTRIBUTION_NAME>",
      "origin": {
        "path": "/",
        "hostname": "<DEINE_INTERNE_CF_ROUTE>",
        "port": 443
      },
      "originRequestHeaders": [
        {
          "name": "Host",
          "values": [
            "<DEINE_MOODLE_DOMAIN>"
          ]
        },
        {
          "name": "Cache-Control",
          "values": [
            "no-cache, no-store, must-revalidate"
          ]
        }
      ],
      "enabled": true
    }' /cdn/v1beta/projects/<DEINE_PROJEKT_ID>/distributions
    

    Notieren Sie sich die id der Distribution aus der Antwort.

  2. CDN-Standard-Domain auslesen:

    # Ersetzen Sie <DEINE_PROJEKT_ID> und <DISTRIBUTION_ID>.
    stackit curl -X GET /cdn/v1beta/projects/<DEINE_PROJEKT_ID>/distributions/<DISTRIBUTION_ID>
    

    Notieren Sie sich den Wert des domain-Feldes aus der Antwort. Dies ist Ihr CNAME-Ziel.

  3. Benutzerdefinierte Domain hinzufügen:

    # Ersetzen Sie <DEINE_PROJEKT_ID> und <DISTRIBUTION_ID>.
    stackit curl -X POST --body '{
      "domain": "<DEINE_MOODLE_DOMAIN>"
    }' /cdn/v1beta/projects/<DEINE_PROJEKT_ID>/distributions/<DISTRIBUTION_ID>/domains
    

3.3. DNS-Einträge konfigurieren (STACKIT CLI)

  1. Zone-ID ermitteln:

    stackit dns zone list
    

    Notieren Sie sich die ID Ihrer Domain-Zone.

  2. CNAME-Eintrag erstellen:

    # Für den Betrieb MIT CDN
    # <DEIN_CDN_ENDPOINT> ist die Standard-Domain, die Sie in Schritt 3.2 ausgelesen haben.
    stackit dns record-set create --zone-id <DEINE_ZONE_ID> --name <DEINE_MOODLE_DOMAIN> --type CNAME --ttl 300 --records "<DEIN_CDN_ENDPOINT>."
    
    # Für den Betrieb OHNE CDN (falls benötigt)
    stackit dns record-set create --zone-id <DEINE_ZONE_ID> --name <DEINE_INTERNE_CF_ROUTE> --type CNAME --ttl 300 --records "console.apps.01.cf.eu01.stackit.cloud."
    

3.4. Cloud Foundry Services erstellen (CF CLI)

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

# Redis-Service (Optional)
cf create-service appcloud-redis7 redis-4.16.100-replica <DEINE_REDIS_INSTANZ>

# Autoscaler-Service
cf create-service autoscaler autoscaler-free-plan <DEIN_AUTOSCALER_INSTANZ>

# User-Provided Service für die S3-Credentials
cf create-user-provided-service <DEINE_S3_CUPS_INSTANZ> -p '{
  "access_key": "<DEIN_ZUVOR_GENERIERTER_ACCESS_KEY>",
  "secret_key": "<DEIN_ZUVOR_GENERIERTER_SECRET_KEY>",
  "bucket": "<DEIN_S3_BUCKET_NAME>",
  "endpoint": "https://object.storage.eu01.onstackit.cloud/"
}'

4. Konfigurationsdateien erstellen

a) Manifest-Dateien

manifest-cdn.yml (Produktion):

---
applications:
- name: moodle
  memory: 2G
  disk_quota: 2G
  buildpack: https://github.com/cloudfoundry/php-buildpack.git
  path: moodle
  routes:
  - route: <DEINE_INTERNE_CF_ROUTE>
  - route: <DEINE_MOODLE_DOMAIN>
  env:
    USE_CDN: 'true'
    WWW_ROOT: "https://<DEINE_MOODLE_DOMAIN>"
    BP_PHP_LOG_LEVEL: WARNING
  services:
  - <DEINE_MARIADB_INSTANZ>
  - <DEINE_REDIS_INSTANZ>
  - <DEIN_AUTOSCALER_INSTANZ>
  - <DEINE_S3_CUPS_INSTANZ>
  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
  path: moodle
  routes:
  - route: <DEINE_INTERNE_CF_ROUTE>
  env:
    USE_CDN: 'false'
    WWW_ROOT: "https://<DEINE_INTERNE_CF_ROUTE>"
    BP_PHP_LOG_LEVEL: WARNING
  services:
  - <DEINE_MARIADB_INSTANZ>
  - <DEINE_REDIS_INSTANZ>
  - <DEIN_AUTOSCALER_INSTANZ>
  - <DEINE_S3_CUPS_INSTANZ>
  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 Inhalt mit dieser Vorlage.

<?php  // Moodle configuration file

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

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

$CFG->wwwroot        = $wwwRoot;
$CFG->dirroot        = __DIR__;
$CFG->dataroot       = '/tmp/moodledata';
$CFG->admin          = 'admin';
$CFG->directorypermissions = 0777;

$CFG->sslproxy       = 1;
$CFG->slasharguments = false;
if ($useCdn) {
    $CFG->reverseproxy   = 1;
    $CFG->trustedproxies = '127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, <IP_DES_CDN_KNOTENS>';
}

if (!file_exists($CFG->dataroot)) {
    @mkdir($CFG->dataroot, $CFG->directorypermissions, true);
}

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

$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
    $redis_service_key = null;
    if (isset($vcap_services['appcloud-redis7'])) { $redis_service_key = 'appcloud-redis7'; }
    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'] === '<DEINE_S3_CUPS_INSTANZ>') {
                $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;
            }
        }
    }
}
if (empty($CFG->session_handler_class)) {
    $CFG->session_handler_class = '\core\session\database';
}

if ($useCdn) {
    $CFG->cookiepath = '/';
    $CFG->cookiedomain = '<DEINE_MOODLE_DOMAIN>';
    $CFG->cookiesecure = 1;
    $CFG->cookiehttponly = 1;
}

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

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.