Moodle auf STACKIT Cloud Foundry
Find a file
lukas.weberruss 64d5797692
Some checks are pending
Deploy Moodle to STACKIT Cloud Foundry / deploy (push) Waiting to run
Add terraform.tfvars
2025-07-23 08:42:39 +00:00
.github/workflows Erste Version für CI/CD Pipeline 2025-07-22 09:26:52 +00:00
.gitignore Bereinige manifest.yaml und füge es zu ignorierten Dateien 2025-07-07 11:55:00 +02:00
config.php.tpl Add config.php.tpl 2025-07-23 08:40:04 +00:00
main.tf Add main.tf 2025-07-23 08:38:19 +00:00
outputs.tf Add outputs.tf 2025-07-23 08:39:18 +00:00
prepare_source.sh Add prepare_source.sh 2025-07-23 08:41:35 +00:00
provider.tf Add provider.tf 2025-07-23 08:33:52 +00:00
readme.md Update readme.md 2025-07-22 08:27:14 +00:00
terraform.tfvars Add terraform.tfvars 2025-07-23 08:42:39 +00:00
variables.tf Add variables.tf 2025-07-23 08:34:46 +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
    subgraph "Nutzer & Netzwerk Edge"
        A[Anwender] --> B(STACKIT DNS);
        B --> C["STACKIT CDN<br/>(Optional)"];
    end
    
    subgraph "Zentrale Infrastruktur"
        D{Cloud Foundry Router};
    end

    C -.-> D;
    B --> D;
    
    subgraph "Cloud Foundry App-Instanzen"
        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: https://github.com/cloudfoundry/cli/releases
  • STACKIT CLI: https://docs.stackit.cloud/stackit/de/automatisieren/cli/cli-installieren-200720436.html

1.2. Bei STACKIT Cloud Foundry anmelden

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
wget https://packaging.moodle.org/stable500/moodle-5.0.1.tgz
tar -xzf moodle-5.0.1.tgz

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

Die Erstellung und Konfiguration des CDN erfolgt in mehreren Schritten.

  1. CDN-Distribution erstellen:

    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>" ] } ],
      "enabled": true
    }' /cdn/v1beta/projects/<DEINE_PROJEKT_ID>/distributions
    

    Notieren Sie sich die id der Distribution aus der Antwort.

  2. Benutzerdefinierte Domain hinzufügen:

    stackit curl -X POST --body '{
      "domain": "<DEINE_MOODLE_DOMAIN>"
    }' /cdn/v1beta/projects/<DEINE_PROJEKT_ID>/distributions/<DISTRIBUTION_ID>/domains
    
  3. Wichtige CDN-Einstellungen per Support-Ticket anfordern: Damit Moodle hinter dem CDN korrekt funktioniert, müssen spezielle Caching- und Cookie-Regeln gesetzt werden, die aktuell nicht als Self-Service verfügbar sind. Erstellen Sie ein Ticket im STACKIT Support-Portal.

    • Support-Portal: https://support.stackit.cloud/servicedesk/customer/portal/3

    • Anfrage-Vorlage:

      Hallo STACKIT-Team, für unsere CDN-Distribution mit der ID <DISTRIBUTION_ID> benötigen wir bitte die folgenden Einstellungen:

      1. "Strip Response Cookies" muss deaktiviert sein. Moodle benötigt dies, um Session-Cookies an den Browser zu senden.
      2. Edge Rule zum Umgehen des Caches: Bitte erstellen Sie eine Edge Rule, die das Caching für alle dynamischen Moodle-Inhalte deaktiviert:
        • Action: Bypass Cache
        • Conditions (Match Any):
          • Request URL (Match Wildcard): *<DEINE_MOODLE_DOMAIN>/*
          • Request URL (Match Wildcard): *<DEINE_MOODLE_DOMAIN>/*index.php*
          • Request URL (Match Wildcard): *<DEINE_MOODLE_DOMAIN>/*javascript.php*
          • Request URL (Match Wildcard): *<DEINE_MOODLE_DOMAIN>/*styles.php*
          • Request URL (Match Wildcard): *<DEINE_MOODLE_DOMAIN>/*yui_combo.php*

      Vielen Dank.

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

  1. Zone-ID ermitteln:

    stackit dns zone list
    
  2. CDN-Standard-Domain auslesen (nur für CDN-Setup):

    stackit curl -X GET /cdn/v1beta/projects/<DEINE_PROJEKT_ID>/distributions/<DISTRIBUTION_ID>
    

    Notieren Sie sich den Wert des domain-Feldes. Dies ist Ihr CNAME-Ziel für das CDN.

  3. CNAME-Eintrag erstellen:

    # Für den Betrieb MIT CDN
    stackit dns record-set create --zone-id <DEINE_ZONE_ID> --name <DEINE_MOODLE_DOMAIN> --type CNAME --ttl 300 --records "<DEIN_CDN_ZIEL_AUS_SCHRITT_2>."
    
    # 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.