Shopware 5.3. Plugins erstellen mit den Shopware CLI Tools

Die Shopware CLI Tools ermöglichen es, das Erstellen von Plugins erheblich zu beschleunigen – egal ob es um Frontend-, Backend- oder API-Plugins (oder eine beliebige Kombination davon) geht. Im folgenden ein Beispiel für ein solches „Multi-Feature“-Plugin.

Shopware CLI Tools Installation

Wir erstellen mit den CLI Tools ein Plugin mit dem einfallsreichen Namen „MyTest01“, hierzu ist folgendes nötig:

  1. Die Shopware CLI Tools gehören nicht zum Shopware „Lieferumfang“ und müssen erst installiert werden, empfohlen wird hier der Download eines „Phar“-Releases.
  2. Man kann das Phar z.B. im Shopware-Hauptverzeichnis nach „bin/“ kopieren und der Einfachheit halber in „sw“ umbenennen.

Shopware CLI Tools Verwendung

Danach sind die Tools einsatzbereit. Hier ein Aufruf, welcher ein Plugin mit Frontend-, Backend- und API-Komponenten generiert:

./bin/sw plugin:create –haveApi –haveBackend –haveModels –haveFrontend –haveCommands –backendModel=MyTest01\\Models\\MyTest01Model MyTest01

Problem mit Backend Model CLI Generierung

Lässt man den Parameter „–backendModel“ beim Aufruf weg, so kann bzw, muss man das Model interaktiv in der CLI angeben:

Wichtig ist in beiden Fällen, den kompletten Pfad bzw. Namespace mit anzugeben! Allerdings hatte ich mit der interaktiven Methode keinen Erfolg, es wurden keine gültigen Dateinamen generiert, sondern die kompletten CLI-Kommandos inkl. Parameter wurden als Namen verwendet – was zu ungültigen Datei- und Klassenamen wie diesen führt:

class ‚plugin:create‘ –haveApi –haveBackend –haveModels –haveFrontend –haveCommands MyTest02 extends ShopwareCommand

Kann evtl. an noch fehlender Kompatiblität mit Shopware 5.3. liegen, ich habe bisher mit älteren Versionen nicht getestet – oder es hat sich aktuell ein Bug beim Verarbeiten der Parameter eingeschlichen.

Übergibt man das „backendModel“ allerdings als Parameter, klappt die Generierung soweit:

Nach dem Generieren sollte man die Plugin-Liste aktualisieren (und ggf. den Cache leeren) mit:

./bin/console sw:plugin:refresh
./bin/console sw:cache:clear

Probleme mit der API Generierung

Allerdings gibt es dann noch weitere kleine Probleme mit dem generierten Code, zumindest in der aktuellen Version 5.3 und falls man den Parameter „–haveApi“ bei der Generierung angibt.

Das erste Problem ist ein Fatal Error, der auftritt und ungefähr so lautet:

PHP Fatal error: Uncaught TypeError:
Argument 1 passed to MyTest01\\Subscriber\\ApiSubscriber::__construct() must be an instance of
Shopware\\Recovery\\Common\\DependencyInjection\\ContainerInterface, instance of
ShopwareProduction4a3b86fd9a3e955627adbda985118ae3e2bdc589ProjectContainer given

Sieht man sich die generierten Klassen an und vergleicht mit ähnlichen Klassen im Shopware Sourcecode selbst, findet man irgendwann das Problem: in der generierten Datei  „MyTest01\Subscriber\ApiSubscriber.php“ wird ein falsches Interface verwendet, man muss also folgendes ändern:

//use Shopware\Recovery\Common\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

Das letzte Problem tritt auf, wenn man versucht, die API URL „/api/mytest01model“ aufzurufen (nachdem man vorher im Backend einen API-Key generiert hat, den man dann für das Login als Passwort nutzen kann), gibt es ebenfalls einen „Fatal Error“ a lá:

PHP Fatal error: Uncaught Error: Class ‚Shopware\\Components\\Api\\Resource\\MyTest01Model‘ not found in
/var/www/html/engine/Shopware/Components/Api/Manager.php:55\nStack trace:\n#0
/var/www/html/custom/plugins/MyTest01/Controllers/Api/MyTest01Model.php(13):
Shopware\\Components\\Api\\Manager::getResource(‚MyTest01Model‘)\n#1 /var/www/html/engine/Library/Enlight/Class.php(74):
Shopware_Controllers_Api_MyTest01Model->init()\n#2 /var/www/html/engine/Library/Enlight/Controller/Action.php(101):

Hier muss man zwei Dinge anpassen:

in „MyTest01\Components\Api\Resource\MyTest01Model.php“ den Namespace ändern auf:

//namespace Shopware\Components\Api\Resource;
namespace MyTest01\Components\Api\Resource;

sowie in „Resources\services.xml“ den neuen Service definieren:

<service id=“shopware.api.mytest01model“ class=“MyTest01\Components\Api\Resource\MyTest01Model“>
</service>

Fall übrigens durch ein fehlerhaftes Modul auch das Backend nicht mehr funktioniert, kann man das betroffene Modul über die Kommandozeile deaktivieren mit:

./bin/console sw:plugin:uninstall MyTest01

Unser Testmodul sollte nun jedoch funktionieren und nicht nur per API aufrufbar sein, sondern auch im Backend auftauchen:

Happy coding! :)

Zierfisch oder Wal – lokale Software-Entwicklung mit Docker

Pünktlich zum 4. Geburtstag von Docker möchte ich ein kleines Fazit ziehen, ob und wie sich Docker-Container für die lokale Entwicklung im Agentur-Alltag mit sehr heterogenen Entwicklungs-Systemen (Windows Home / Pro, von 7 bis 10, Mac OS, Linux) der einzelnen Mitarbeiter bewähren können, welche Vorteile, aber auch Schwachstellen und Probleme es ggf. bei der täglichen Arbeit gibt und ob es sich lohnt, auf den blauen Wal zu setzen.

Die Anfänge

Vor knapp zwei Jahren fing ich an, mich erstmals mit Docker auseinanderzusetzen. Damals wie heute ging es dabei zentral darum, mit möglichst wenig Aufwand eine einheitliche, einfach aufzusetzende lokale Entwicklungsumgebung für verschiedene Software-Projekte zu haben, die idealerweise systemunabhängig zur Verfügung stehen sollte.

Lösungen mit MAMP, XAMP, LAMP usw. waren kaum beherrschbar, die Versionen der einzelnen Komponenten (Webserver, Datenbank, PHP-Umgebung, etc.) auf den Systemen der Mitarbeiter wucherten vor sich hin, unterschiedlichste Projekte stellten auch unterschiedlichste Anforderungen (ZendGuard Loader, ioncube Loader, Opcode-Caches, diverse PHP-Versionen, NodeJS, etc. etc.). Oder es wurde gar gemeinsam gleichzeitig auf nur einem Test-System entwickelt, die Mitarbeiter überschrieben sich „fröhlich“ gegenseitig ihre Änderungen. Gab es irgendwo einen Fehler im Code mussten alle warten, bis der Schuldige das Problem behoben hatte und das System wieder lief… kurzum: totale Anarchie :)

Für jedes Projekt aber eigene VMs zu „basteln“ war ebenso zeitaufwändig wie hardware-hungrig… was also tun? Docker schien eine gute Lösung zu sein, mit Docker Toolbox war es auch für Windows und Mac OS verfügbar, es gab vorgefertigte Docker-Images für Apache, PHP, Mysql auf Docker Hub usw.

Allerdings war Docker Compose noch im Entstehen, grafische Tools zur Container-Administration waren Mangelware, einzelne Container wurden also auf der Kommandozeile über „docker run …“ gestartet. Für „richtige“ Entwickler natürlich ok, für Template- und Frontend-Kollegen eher „so naja“ … ;)
Der Einstieg war also eher holprig, erste Container waren der einfacheren Administration halber „all-in-one“, also entgegen der Docker-Prinzipien liefen Apache, mehrere PHP-Versionen über „phpbrew“, Mysql, NodeJs usw. in einem gemeinsamen Container und waren damit eigentlich schon fast ein „VM-Ersatz“.

Weiterlesen

Continuous Delivery Teil 2 – Ansible Tipps

Im ersten Teil dieser Blogserie haben wir den allgemeinen Entwicklungsworkflow bei shoptimax beleuchtet – in diesem Teil soll es um konkrete Tipps zum Deployment mit Ansible gehen.

Gitlab CI und Ansible kombinieren

Wie bereits beschrieben nutzen wir für automatisiertes Deployment unserer Projekte aus GIT heraus Gitlab CI. Je nach GIT- Branch (bei uns sind das normalerweise „develop“, „stage“ und „master“) löst Gitlab CI unterschiedliche Aktionen aus, welche in „.gitlab-ci.yml“-Dateien in den jeweiligen Projekten definiert sind.
In der Regel werden Änderungen in den „develop“-Branches direkt per Gitlab Runner auf interne Testserver deployed. Optional können hier (und/oder bei einem Git-Push in weitere Branches) Code-Style Prüfungen oder z.B. Codeception-Tests durchgeführt werden, die dann in einem Gitlab-internen Docker-Container automatisch gestartet werden.

Änderungen an „stage“- oder „master“-Branches hingegen werden in der Regel auf externe Server ausgespielt, sei es ein geteilter Staging-Server von shoptimax oder direkt ein Kunden-System. Hier kommt dann unser Ansible-Repository mit unterschiedlichen sog. „Playbooks“ ins Spiel.

Dazu wird im ersten Deployment-Step (welcher im Docker-Container abläuft) unser internes Ansible-GIT-Repository gecloned. Dieses wird am Ende des Durchlaufs in einem sog. Gitlab CI-Artefakt gespeichert, welches in weiteren Deployment-Steps dann ebenfalls zur Verfügung steht. Damit können nacheinander die unterschiedlichen Playbooks in den verschiedenen „Stages“ des Deployments aufgerufen werden. In einer „scripts“-Sektion der .gitlab-ci.yml wird also z.B. folgendes definiert:

- git clone --depth 1 --branch master git@repo:repo_group/ansible_deployment.git ansible

Für das zu erstellende Artefakt wird weiterhin festgelegt, welche Verzeichnisse darin gespeichert werden sollen und wie lange es auf dem Gitlab-Server vorgehalten wird, z.B.

artifacts:
    paths:
    - build
    - ansible
    - deploy_scripts
    expire_in: 5 days

Diese Artefakte können vor Ablauf dieser Zeitspanne übrigens auch über die Gitlab-Oberfläche per Browser heruntergeladen werden.

In der .gitlab-ci.yml wird also definiert, wo das Ansible-Verzeichnis liegen soll und an welchen Stellen im Prozess Ansible Playbooks aufgerufen werden:

variables:
  SHOP_BUILD_DIR: "/builds/oxid/$CI_PROJECT_NAME"
deploy_stage:
  stage: deploy
  environment: deploy_stage
  tags:
    - docker
  script:
- cd $SHOP_BUILD_DIR/ansible/
- ansible-playbook -s ./deploy.yml --extra-vars "target=customer1 custom_source_folder=/../temp shop_docroot=${SHOP_DOCROOT_STAGE} shop_deploy_dir=${SHOP_DEPLOY_DIR_STAGE} ..." -vv

Timestamp-Datei für unabhängige Playbooks

Um eine gemeinsame Referenz für die unterschiedlichen Playbooks zu haben, die ja meist in verschiedenen Build-„Stages“ ablaufen (z.B. lädt das erste Playbook ein *.tar.gz hoch und packt es auf dem Server in Verzeichnis X aus, das zweite Playbook legt dann Symlinks auf dieses Verzeichnis an usw.), wird ein gemeinsamer Timestamp verwendet (z.B. auch für die Verzeichnis-Namen der Deployments). Ein solcher Timestamp wird bereits beim Clone des Ansible-Repositories erzeugt und in einer txt-Datei gespeichert:

- date +%Y-%m-%d_%H-%M-%S > ansible/timestamp.txt

In den Playbooks wird jeweils geprüft, ob es diese Datei gibt, falls ja wird der Timestamp ausgelesen und über diesen Timestamp dann das zugehörige Verzeichnis gesucht / weiter verarbeitet.
Gitlab CI cloned das Ansible Repository in einen projektspezifischen Docker-Container und speichert die Dateien im Build-Artefakt, inkl. der Timestamp-Datei.
Dadurch bleibt der Timestamp während des gesamten Deployment-Prozesses über die verschiedenen, zeitlich nacheinander ablaufenden „Stages“ quasi als „Referenz“ vorhanden und jedes Playbook operiert so auf demselben Verzeichnis.

Projektspezifische Kommandos und Symlinks in JSON-Dateien

Mit Ansible ist es sehr einfach, JSON-Dateien einzulesen und abzuarbeiten. Wir verwenden z.B. JSON-Dateien, um während eines Deployments individuelle Symlinks je nach Kundenprojekt anzulegen oder um auf dem Zielserver projektspezifische Kommandos (Shell oder auch PHP) beim Deployment auszuführen.
Z.B. kann hier ein Shell-Script aufgerufen werden, das eine Datenbank-Sicherung durchführt oder ein PHP-Script, welches das „tmp“-Verzeichnis des Shops leert, Datenbank-Views neu erzeugt oder bestimmte Module automatisch de-/aktiviert usw.
Damit müssen wir keine Fallunterscheidungen oder zusätzliche Variablen und Tasks in Ansible selbst definieren, sondern können in das Kundenprojekt einfach entsprechende JSON-Dateien in das GIT-Repository einchecken. Diese können sogar z.B. pro Git-Branch oder Zielsystem unterschiedlich sein, da die Pfade bzw. Dateinamen der JSON-Dateien im jeweiligen Deployment-Step in der „.gitlab-ci.yml“-Datei als Kommandozeilen-Parameter an das Ansible Playbook übergeben werden können.

- ansible-playbook -s ./deploy.yml --extra-vars "target=customer1 shop_docroot=${SHOP_DOCROOT_STAGE} shop_deploy_dir=${SHOP_DEPLOY_DIR_STAGE} cust_htaccess_file=.htaccess_prelive cust_config_file=config.inc.prelive.php custom_commands_path=/../src/configs/commands.json custom_symlinks_path=/../src/configs/symlinks.json" -vv

So kann es z.B. eine „commands_live.json“ und eine „command_stage.json“ im Projekt geben, in welchen jeweils unterschiedliche Shell- oder PHP-Kommandos definiert sind.

Um z.B. eine JSON-Datei mit einem Array von Kommandos einzulesen, verwenden wir im Ansible-Playbook folgendes:

commands_path: "{{ custom_commands_path | default('/files/cmds.json') }}"
commands_json: "{{ lookup('file','{{ inventory_dir }}{{ commands_path }}') | from_json }}"

Die Variable „custom_commands_path“ (Pfad zur JSON-Datei) kann per Kommandozeilen-Parameter projekt-spezifisch übergeben werden, der aufgerufene Jinja2-Filter „from_json“ liest das JSON aus der Datei in eine Ansible-Variable ein.

Das erhaltene JSON kann dann in einer Schleife („with_items“, s.u.) abgearbeitet werden, die erkannten Kommandos werden dann ausgeführt. Hier ein Beispiel für eine solche JSON-Datei:

{
  "commands":[
    {"path":"/application/views/flow/", "ctype":"shell", "cmd":"ls -altr", "stage":"prerelease", "host":"master01" },
    {"path":"", "ctype":"php", "cmd":"oxid cache:clear" }
 ]
}

Zudem kann man definieren, ob man ein Kommando „prerelease“, also vor dem eigentlichen „Aktivieren“ des Deployments (z.B. ein Backup erstellen) oder aber erst hinterher (z.B. den Cache löschen) ausführen möchte. Die Variable „host“ schränkt die Aktion optional z.B. bei einem Multi-App-Server System auf einen bestimmten Host ein.

Hier ein Ansible-Task, welcher „post-release“ Shell-Kommandos ausführt:

- name: Additional Release Shell commands
  shell: "{{ item.cmd }}"
  args:
    chdir: "{{ release_dir }}/{{ release_subfolder }}{{ item.path }}"
  with_items: "{{ commands_json.commands }}"
  register: command_result
  when:
    - item.ctype != 'php'
    - ((item.stage is defined) and (item.stage != 'prerelease')) or (item.stage is not defined)
    - ((item.host is defined) and (item.host == ansible_nodename or item.host == '')) or (item.host is not defined)

Patchen von .htaccess-Dateien

Beim Release eines Live-Shops will man in manchen Fällen den Shop kurzzeitig für Kunden „sperren“, damit die Agentur und/oder der Shopbetreiber den Shop testen, Testbestellungen ausführen usw. können. Dazu verwendet man in der Regel einen Eintrag in der „.htaccess“-Datei im Shop-Verzeichnis, welche den Zugriff auf die Website / den Shop z.B. nur für bestimmte IPs erlaubt und andere Benutzer auf einen sog. Baustellen-/Offline-Seite weiterleitet oder einen Passwort-Schutz ergänzt.

Unsere Ansible-Playbooks können optional in einem ersten Deployment-Schritt den Inhalt einer frei definierbaren Datei in die .htaccess-Datei des Shops einfügen, welche z.B. eine bestimmte IP-Freigabe enthält. Dann kann, nach Freigabe bzw. Aktivierung des neuen Deployments über das Gitlab CI Backend die Agentur bzw. der Shopbetreiber den Shop testen, um im Erfolgsfall schliesslich ebenfalls über einen Button im Gitlab-CI Backend das letzte Playbook zu starten („publish“). Dadurch wird der zusätzliche Eintrag wieder aus der .htaccess-Datei entfernt. Im Gitlab CI Backend sieht das z.B. so aus:

Die vier Haken im Bild sind die Build-„Stages“, die in der „.gitlab-ci.yml“-Datei definiert sind.
In Stage 1 wird der Shop in einem internen Docker-Container „zusammengebaut“ und ein Artefakt inkl. Ansible-Playbooks erstellt, ab Stage 2 wird Ansible aufgerufen („deploy“ – „release“ – „publish“).
In Stage 2 wird das neue Release via Ansible auf den Server hochgeladen und ein Eintrag in die .htaccess-Datei eingefügt.
In Stage 3 wird der Shop-Symlink auf das neue Deployment-Verzeichnis gesetzt und in Stage 4 wird dann schliesslich der .htaccess-Eintrag wieder entfernt und der Shop ist damit wieder frei zugänglich.

Das Einfügen in die .htaccess-Datei sieht im Playbook wie folgt aus:

- name: Add custom content to .htaccess file
  blockinfile:
    dest: "{{ release_dir }}/{{ release_subfolder }}.htaccess"
    #insertbefore: BOF
    insertbefore: "#ANSIBLE_PLACEHOLDER#"
    block: |
          {{ htaccess_include }}
    marker: "# {mark} ANSIBLE BLOCK"
  when:
    - shop_htaccess_final.stat.exists == True
    - (custom_htaccess_include_rel_path is defined) and (htaccess_include != '')

Die Variable „htaccess_include“ enthält den Inhalt der zu inkludierenden Datei, sofern vorhanden. Der Pfad zur Include-Datei kann wieder auf der Kommandozeile übergeben werden:

- ansible-playbook -s ./deploy.yml --extra-vars "... custom_htaccess_include_rel_path=/../src/configs/htaccess.inc"

Entfernt wird der zusätzliche Inhalt am Ende wieder mit folgender Task-Definition:

- name: Remove included content from .htaccess file
  blockinfile:
    dest: "{{ release_dir }}/{{ release_subfolder }}.htaccess"
    marker: "# {mark} ANSIBLE BLOCK"
    content: ""
  when:
    - shop_htaccess_patched.stat.exists == True

Wir verwenden einen Marker („#ANSIBLE_PLACEHOLDER#„) in den .htaccess-Dateien, vor welchem die Datei dann inkludiert wird. Details dazu finden sich in der Doku zum Ansible „blockinfile“-Modul.

Das war es auch schon wieder mit unserem kleinen Ansible-Exkurs, in der nächsten und letzten Folge dieser Deployment-Serie schauen wir dann Gitlab CI genauer unter die Haube!

Update PHPMailer für ältere OXID Versionen

Hintergrund

Zum Ende des vorangegangenen Jahres wurde eine Sicherheitslücke der PHPMailer-Bibliothek bekannt gegeben. Da diese auch im OXID eShop eingesetzt wird, wurde in einem ausführlichen Beitrag seitens OXID auch eine kurze Anleitung zum Update des PHPMailers auf Version 5.2.21 veröffentlicht.
Es wird darauf hingewiesen, dass durch das OXID eShop framework die Basisinstallation trotz dieser Sicherheitslücke absolut sicher ist. Einzig durch externe Frontend-Module, welche die PHPMailer- Bibliothek direkt ansprechen, besteht ein eventuelles Sicherheitsrisiko. Um diese Möglichkeit komplett auszuschließen, wird mitgeteilt, dass die Klassen class.phpmailer.php und class.smtp.php durch die aktuellste Version ausgetauscht werden sollen.
(Quelle: https://oxidforge.org/en/phpmailer-5-2-21-remote-code-execution-oxid-eshop-is-safe.html)

Update für ältere OXID eShop Versionen (< 4.9.8)

Bei älteren Installationen bis zur Version 4.9.7 reicht der Austausch der beiden Klassen jedoch nicht aus. Für den Fall, dass der Mailversand über SMTP erfolgt, sind zusätzliche Anpassungen am Code notwendig. Bis zur Version 4.9.8 wurde in den OXID Core-Klassen oxEmail sowie auch oxDb jeweils nur die PHPMailer-Klasse eingebunden.

oxemail.php:

require oxRegistry::getConfig()->getConfigParam(’sCoreDir‘) . „/phpmailer/class.phpmailer.php“;

oxdb.php:

include_once getShopBasePath() . ‚core/phpmailer/class.phpmailer.php‘;

Bei älteren Versionen des PHPMailers ist dies auch ausreichend, da die Klasse SMTP bei Aufruf der Funktion SmtpSend() in der PHPMailer-Klasse inkludiert wird:
require_once $this->PluginDir . ‚class.smtp.php‘;
In der aktuellsten PHPMailer-Bibliothek werden jedoch die einzelnen Klassen (darunter auch SMTP) nicht mehr innerhalb von class.phpmailer.php‘, sondern über eine eigene autoloader-Klasse initialisiert (Quelle: https://github.com/PHPMailer/PHPMailer/wiki/Tutorial).
Um die Funktionalität von PHPMailer bei älteren OXID eShops komplett zu gewährleisten, ist daher eine weitere Anpassung notwendig. Zusätzlich zur beschriebenen Aktualisierung, muss in den beiden OXID Core-Klassen noch class.smtp.php eingebunden werden. Der folgende Code zeigt, wie die aktualisierte Klasse SMTP in OXID, Version 4.9.7, verwendet werden kann. Für weit ältere Installationen ist es nötig, sich an der schon bestehenden Einbindung der PHPMailer-Klasse zu orientieren und diese für class.smtp.php zu adaptieren.

Klasse oxEmail (v4.9.7):

require oxRegistry::getConfig()->getConfigParam(’sCoreDir‘) . „/phpmailer/class.phpmailer.php“;
require oxRegistry::getConfig()->getConfigParam(’sCoreDir‘) . „/phpmailer/class.smtp.php“;

Klasse oxDb (v4.9.7):

include_once getShopBasePath() . ‚core/phpmailer/class.phpmailer.php‘;
include_once getShopBasePath() . ‚core/phpmailer/class.smtp.php‘;

 

Continuous Delivery mit Docker, Gitlab CI und Ansible

Im letzten Jahr haben wir bei shoptimax den Entwicklungs- und Deployment-Workflow stark optimiert, um Projekte möglichst effektiv umsetzen, automatisiert testen und Fehler vermeiden zu können, die z.B. bei manuellen Uploads oder Änderungen direkt auf dem Server auftreten können. Mit einem passenden Continuous Delivery Workflow kann man erreichen, dass Änderungen zeitnah und zuverlässig ausgespielt werden können und dass auf einem Server immer ein genau definierter Zustand herrscht. Nicht zuletzt werden dadurch auch die Entwickler entlastet, da sie sich nicht mehr um das Hochladen von Änderungen, Abgleichen unterschiedlicher Datei-Versionen usw. kümmern müssen und sie durch automatisierte Tests und Validierungen frühzeitig Feedback bekommen, sollte es Probleme im Zusammenspiel der Software geben.

Alle neuen Projekte werden seit einigen Monaten bereits mit automatisiertem Deployment und nach dem Grundsatz „Continuous Delivery“ umgesetzt, ältere bzw. größere laufende Projekte stellen wir nach und nach um.

Entwicklungs-Workflow

Grundsätzlich gilt: jeder Entwickler sollte primär in seiner lokalen Umgebung entwickeln können, wozu Docker eingesetzt wird. Der Entwickler arbeitet, testet und debugged lokal auf seinem eigenen Rechner, „committed“ und „pusht“ seine Arbeit anschliessend in unsere GIT-Versionsverwaltung, für welche wir einen internen Gitlab-Server nutzen. Im Docker-Container der Entwickler sind zudem Tools wie Blackfire.io oder XHProf sowie Xdebug verfübar.

Sobald mit GIT in den sogenannten „develop“-Branch gepusht wird, übernimmt unser Gitlab-Server das automatische Deployment auf ein internes Test-System, auf welchem dann Projektleiter oder andere Beteiligte (Grafiker usw.) den aktuellen Stand begutachten können.

Pusht ein Entwickler in den sog. „stage“-Branch, wird der Stand, nach optionalem Durchlaufen von Tests usw., auf einen externen Entwicklungsserver gespielt, z.B. auf einen Kunden-Staging-Server.

Ein Release-Manager kann schliesslich noch in den sog. „master“-Branch pushen, dieser wird dann automatisch auf den Live-Server ausgerollt, allerdings wird hier ein Feature von Gitlab CI genutzt, welches es ermöglicht, neue Deployments über die Gitlab-Oberfläche manuell per Button zu „aktivieren“ – dazu reicht ein simples

when:
– manual

in der Datei „.gitlab-ci.yml“.

So kann der genaue Zeitpunkt bestimmt werden, wann der neue Stand online geht und es können vorher z.B. noch Kontrollen durchgeführt werden. Der vorherige Stand wird auf dem Server belassen, so ist ein blitzschnelles „Rollback“ über die Gitlab-Oberfläche oder die Shell möglich, sollte es doch einmal Probleme mit der neuen Version geben.

Build und Testing

In allen drei Fällen (develop, stage und master Branch) können optional vorab in einem automatisch von Gitlab gestarteten Docker-Container z.B. Akzeptanz-Tests usw. mit Codeception durchgeführt oder Coding-Standards mittels PHP Codesniffer und ähnlicher Tools geprüft werden etc.
Ausserdem können mit einem auf „ioly“ bzw. „OXID Modulconnector“ basierenden Installer im Shop benötigte Module aus anderen GIT-Repositories oder auch von Drittanbietern automatisch installiert und aktiviert werden.
Schlägt einer der automatisierten Tests fehl, wird das Deployment gestoppt. Sind die Tests erfolgreich, wird der komplette, getestete Shop im Docker-Container als *.tar.gz gepackt.

Ausrollen mit Ansible

Im nächsten Step wird innerhalb des Docker-Containers unser Ansible-Deployment Repository aus GIT geclont.

Ansible ist eine Server-Orchestrierungs/Konfigurations-Software, womit man von einem mit Ansible ausgestatteten Rechner beliebig viele „entfernte“ Rechner/Server administrieren kann. Dazu ist auf den Remote-Rechnern keinerlei Client- oder Server-Komponente nötig, es muss lediglich ein Public Key hinterlegt werden. Die Ansible Befehle definiert man in sog. „Playbooks“, die einfach im YAML-Format geschrieben werden können.

Unser Ansible-Playbook kann nun den kompletten Shop auf beliebige Server hochladen/verteilen und diverse zusätzliche Tasks ausführen, z.B. Symlinks anlegen (die in einer JSON-Datei definiert werden können) sowie Shell- oder PHP-Befehle ausführen (ebenfalls in JSON beliebig definierbar, z.B. Tmp-Verzeichnis leeren, Datenbank-Views neu erzeugen, Module aktivieren usw.).
Abschliessend kann von Ansible automatisch oder auch manuell per Button der Symlink auf das Haupt-Shopverzeichnis geändert und so der neue Stand live geschaltet werden. Wie schon erwähnt ist auch ein schnelles Rollback kein Problem, da der vorherige Stand (sowie einige ältere Stände) noch auf dem Server vorgehalten werden.

In weiteren Blog-Beiträgen werden wir sowohl Gitlabl CI näher vorstellen als auch einige Ansible-Features näher beleuchten. Teil 2 zum Thema „Ansible“ jetzt hier lesen!

Devops Camp Compact 2016

Am 7. und 8. Oktober 2016 fand das diesjährige „Devops Camp Compact“ in Nürnberg statt. „Compact“ war allerdings relativ, zwar ist die Herbstausgabe des Barcamps nach wie vor auf einen Session-Tag konzentriert (im Frühjahr findet das Devops Camp jeweils zwei volle Tage statt), allerdings war die Zahl der Anmeldungen mit gut 100 Teilnehmern (und damit ausverkauft) diesmal ähnlich hoch wie beim „großen Bruder“ und auch Location, Ambiente und Qualität der Sessions waren wieder einmal aussergewöhnlich.

Los ging es am Freitag Abend in noch überschaubarem Teilnehmer-Kreis in den Räumlichkeiten der netlogix GmbH im Nürnberger Norden bei Pizza, kalten Getränken und viel Tech-Talk und Networking.

Samstags war dann Startschuss ab 9 Uhr, nach kurzer Anmeldung und Empfang des obligatorischen T-Shirts erst einmal mit üppigem Frühstück inklusive feinstem Kaffee vom eigens engagierten Barista los – sehr lecker! Da konnte bei der anschliessenden Session-Planung nicht mehr viel schiefgehen. Eine kurze Umfrage brachte zutage, dass relativ viele Devopscamp- bzw. Barcamp-Neulinge anwesend waren, ich denke die meisten davon wird man von nun an öfters auf Barcamps antreffen :)

Nach einer blitzschnellen, sozusagen kompakten Vorstellungsrunde ging es an die Session-Planung, die im Vorfeld eingereichten Vorschläge wurden von weiteren Spontan-Sessions ergänzt und so waren die über vier Räume verteilten Slots auch relativ schnell belegt.
Hier der Überblick über die Sessions: https://openspacer.org/60-devops-community/127-devops-camp-compact-2016/timetable/75

Pünktlich um 11 Uhr verteilten sich die Teilnehmer dann auf besagte Räume, für mich persönlich startete der Wissens-Teil des Tags mit der Session „Unifying DevOps Model“, einem sehr interessanten Ansatz, alle nötigen Bestandteile eines neuen „Deployment-Projektes“, von diversen Konfigurationen über Jenkins Jobdefinitionen bis hin zu verlinkter Dokumentation und Email-Versand mittels einfacher Groovy-Definitionen und -Templates automatisch zu generieren. Infos und Beispiel-Repositories gibt es auf Github.

Weiter ging es mit „Kubernetes – join the hype!“, einer knackigen Einführung in Installation und erste Verwendung von Kubernetes für die Container-Verwaltung – allerdings ist das vorgestellte „kubeadm“ Tool aktuell noch in der Beta-Phase und bisher nur für Ubuntu 16.04 und CentOS 7 verfügbar.

Nach einer Mittagspause mit Leckereien vom Grill, diversen Salaten und feinen Nachspeisen ging es ab 14 Uhr in die zweite Runde und ich durfte zusammen mit Stefan Peter „spr2“ Roos etwas zu „Continuous Integration mit Gitlab CI“ erzählen. Unser Fazit: nicht immer eine Alternative zu Jenkins und v.a. sinnvoll, wenn man eh Gitlab nutzt, aber sehr einfach mittels
einer „.gitlab-ci.yml“-Datei zu konfigurieren und mit diversen neuen Features in jedem neuen Release auf jeden Fall auf einem sehr guten Weg und für projektbezogenes Deployment unbedingt mindestens einen Blick wert!

Nebenbei bemerkt – die Teilnehmer hatten natürlich auch diesmal wieder die Qual der Wahl – bei vier gleichzeitigen Slots hat man eigentlich immer das Problem, dass mindestens zwei Sessions gleichzeitig stattfinden, die einen persönlich interessieren.
Ich entschied mich als nächstes für die Session „Docker Security“, die allerdings nichts wirklich neues bot … evtl. wäre „Laßt uns über Kommunikation & Organisation reden“ spannender gewesen … oder auch „NixOs“ … oder die aktuellen OwnCloud-News von Felix… :)

Da ich mich gerade speziell mit dem Vergleich Jenkins / Gitlab CI beschäftige klang für mich als nächstes die Session zum Thema „Jenkins Pipelines“ am spannendsten, wobei ich natürlich auch gerne in „Docker Webserver Infrastruktur“ von Kollege Tobias „Tabsl“ Merkl gegangen wäre… die Einführung in die Jenkins Pipelines war jedoch sehr informativ, die Slides dazu finden sich unter https://github.com/StephenKing/dvocc16-jenkins-pipeline.

Als Session-Abschluss gab es für mich nochmals etwas zum Thema „Docker“, diesmal ging es um eine Diskussion der aktuellen „Docker-Plattformen“ – mein Fazit dazu: aktuell ist der Markt sehr aktiv, schnelllebig und unübersichtlich, man hat die Wahl zwischen diversen „Meta“-Frameworks, z.B. Rancher, Kubernetes, Docker Cloud, Mesos, Marathon, DC/OS, Terraform, etc. etc. In der Diskussion ging es v.a. um DC/OS im Zusammenspiel mit Marathon und Chronos.

Vor dem Ausklang des Tages auf der Abend-Party gab es nach einer kleinen Feedback-Runde, die natürlich sehr positiv ausfiel, noch ein leckeres Abendessen in Form von Wraps, belegten Brötchen usw. Alles in allem wieder einmal eine mehr als gelungene Veranstaltung, an dieser Stelle nochmal ein herzlicher Dank an alle Beteiligten und Sponsoren sowie an netlogix für die tollen Räumlichkeiten!

Bis zum Frühjahr beim DevOps Camp 2017!

OXID EE MySQL Master/Slave Setup

oxid-news

Hintergrund

Der OXID eShop unterstützt in der Enterprise Edition (EE) MySQL Master/Slave Datenbank-Setups, was für sog. „Read-/Write-Splitting“ genutzt werden kann. Dadurch ist es möglich, Datenbank-Abfragen auf mehrere Datenbank-Server zu verteilen, um damit im Idealfall an Datenbank-Performance zu gewinnen und Shops gerade zu Lastzeiten schneller zu machen.
Read-/Write-Splitting kann ebenso helfen, das Shop-Frontend performant zu halten, wenn z.B. gerade Massen-Updates über ERP- oder PIM-Schnittstellen im Shop-Backend laufen, welche mit vielen Insert-Statements nicht selten Datenbank-Locks auslösen, die sich dann auch auf das Frontend auswirken können. Im schlimmsten Fall ist der Shop zeitweise nicht mehr aufrufbar, weil gleichzeitig zu den laufenden Insert- und Update-Statements aus einer gelockten Datenbank-Tabelle gelesen werden soll.

Master/Slave Setup in OXID

Im OXID eShop ist die Konfiguration der Master-/Slave-Verteilung sehr einfach, hier genügt es, in der allgemeinen Shop-Konfigurationsdatei „config.inc.php“ die einzelnen Server einzutragen.

Hierzu gibt es zwei Variablen, die gesetzt werden müssen:

$this->aSlaveHosts = array('localhost', '192.168.0.10:3306', '192.168.0.11:3307');
$this->iMasterSlaveBalance = 0;

Im Array „aSlaveHosts“ können beliebig viele IP-Adressen (mit optionalem Port) eingetragen werden. Auch der Master wird hier als erster Wert hinterlegt.
Der zweite Parameter, „iMasterSlaveBalance“ legt fest, wie die Verteilung der Abfragen zwischen Master und Slave ist.
Ein Wert von „0“ bedeutet hier, dass alle Abfragen auf den Slaves stattfinden und auf den Master nur schreibend zugegriffen wird.

Für Shops mit regelmässig laufenden Artikeldaten-Updates über Schnittstellen zu Warenwirtschaftssystemen usw. sollte diese Einstellung verwendet werden, damit sichergestellt ist, dass auch bei Massen-Updates die Lesezugriffe aus dem Shop-Frontend nicht in Mitleidenschaft gezogen werden.

Das Setup der Datenbankserver an sich geht über den Umfang dieses Artikels hinaus, hierzu finden sich z.B. sehr gute Infos unter https://www.thomas-krenn.com/de/wiki/MySQL_Replikation

Tipps und Tricks für das MySQL Master/Slave Setup

Der OXID EE Shop selbst ist bereits sehr gut für die Master-/Slave-Aufteilung optimiert, sobald Slaves konfiguriert sind, hält sich der Shop strikt an die definierte Aufteilung und führt z.B. Lesezugriffe immer auf den Slaves aus.

Für eigene Erweiterungen des Shops sollte man sich hier am OXID Standard orientieren, hier ist es v.a. wichtig, für Lese- oder Schreibzugriffe die richtigen Methoden des OXID Datenbank-Adapters zu verwenden.

Prinzipiell gilt: für Lesezugriffe sollte man immer die „select()“-Methode der „oxDb“-Klasse nutzen, da nur diese wirklich standardmässig dezidiert auf die Slaves zugreift.

$oRs = oxDb::getDb()->select("SELECT * FROM oxarticles WHERE oxartnum = '12345'"); // liest vom Slave

Man kann allerdings als dritten Parameter „false“ übergeben, um dennoch vom Master zu lesen – Standard ist jedoch „true“ und damit „Slave:

public function select($sSql, $aParams = false, $blType = true)

Auch die Methoden „getOne()“, „getArray()“, „getAssoc()“, „getRow()“ und „getAll()“ selektieren standardmässig von den Slaves, können aber ebenfalls per Parameter auf den Master „umgebogen“ werden bei Bedarf.

Nutzt man hingegen die Methoden „execute()“ oder „query()“, geht das SQL-Statement ausnahmslos an den Master-Server!

$oRs = oxDb::getDb()->query("SELECT * FROM oxarticles WHERE oxartnum = '12345'"); // liest vom Master

Problematisch sind bei Master-/Slave-Setups oft SQL-Fehler, welche zu Fehlermeldungen auf Datenbank-Ebene führen. MySQL ist hier meist recht rigoros und beendet die Synchronisation zwischen Master und Slaves. Diese muss dann vom System-Administrator wieder manuell aktiviert werden.

Man sollte das System also erst auf Herz und Nieren testen und die Synchronisation im Testbetrieb beobachten, um zu entscheiden, ob es hier kritische Stellen im Shop gibt und regelmässige SQL-Fehler auftauchen.

Handelt es sich um temporäre, „unwichtigere“ Daten (z.B. Session-Einträge, Captcha-Codes o.ä.), kann man ggf. in Erwägung ziehen, die betroffenen Tabellen aus der Synchronisation herauszunehmen bzw. MySQL-Fehler für diese Tabellen bei der Synchronisation zu ignorieren, siehe z.B. https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html#option_mysqld_slave-skip-errors
Ausserdem kann helfen, die Slaves explizit als „read_only“ zu konfigurieren, damit es keine „versehentlichen“ Schreibversuche auf die Slaves gibt.

MySQL Master/Slave in OXID – Fazit

Mit der Option für Master-/Slave-Setups bietet OXID gerade für große Shops mit regelmässigen, umfangreichen Datenänderungen über Drittsysteme einen erheblichen Performance-Hebel.
Weiss man um die technischen Hintergründe des OXID-Datenbankadapters und kennt die möglichen Fehlerquellen auf Seiten der MySQL-Datenbank-Synchronisation, spricht nichts dagegen, dieses Feature im produktiven Einsatz zu nutzen.
Hat man dann noch einen technisch versierten Hoster an seiner Seite, kann eigentlich nichts mehr schiefgehen :)

Hoffentlich hat dieser Artikel dazu beigetragen, die technischen Hintergründe in Bezug auf OXID EE und das Mysql Master-/Slave-Setup zu erhellen!

OXID EE MySQL Master/Slave Setup

oxid-news

Hintergrund

Der OXID eShop unterstützt in der Enterprise Edition (EE) MySQL Master/Slave Datenbank-Setups, was für sog. „Read-/Write-Splitting“ genutzt werden kann. Dadurch ist es möglich, Datenbank-Abfragen auf mehrere Datenbank-Server zu verteilen, um damit im Idealfall an Datenbank-Performance zu gewinnen und Shops gerade zu Lastzeiten schneller zu machen.
Read-/Write-Splitting kann ebenso helfen, das Shop-Frontend performant zu halten, wenn z.B. gerade Massen-Updates über ERP- oder PIM-Schnittstellen im Shop-Backend laufen, welche mit vielen Insert-Statements nicht selten Datenbank-Locks auslösen, die sich dann auch auf das Frontend auswirken können. Im schlimmsten Fall ist der Shop zeitweise nicht mehr aufrufbar, weil gleichzeitig zu den laufenden Insert- und Update-Statements aus einer gelockten Datenbank-Tabelle gelesen werden soll.

Master/Slave Setup in OXID

Im OXID eShop ist die Konfiguration der Master-/Slave-Verteilung sehr einfach, hier genügt es, in der allgemeinen Shop-Konfigurationsdatei „config.inc.php“ die einzelnen Server einzutragen.

Hierzu gibt es zwei Variablen, die gesetzt werden müssen:

$this->aSlaveHosts = array('localhost', '192.168.0.10:3306', '192.168.0.11:3307');
$this->iMasterSlaveBalance = 0;

Im Array „aSlaveHosts“ können beliebig viele IP-Adressen (mit optionalem Port) eingetragen werden. Auch der Master wird hier als erster Wert hinterlegt.
Der zweite Parameter, „iMasterSlaveBalance“ legt fest, wie die Verteilung der Abfragen zwischen Master und Slave ist.
Ein Wert von „0“ bedeutet hier, dass alle Abfragen auf den Slaves stattfinden und auf den Master nur schreibend zugegriffen wird.

Für Shops mit regelmässig laufenden Artikeldaten-Updates über Schnittstellen zu Warenwirtschaftssystemen usw. sollte diese Einstellung verwendet werden, damit sichergestellt ist, dass auch bei Massen-Updates die Lesezugriffe aus dem Shop-Frontend nicht in Mitleidenschaft gezogen werden.

Das Setup der Datenbankserver an sich geht über den Umfang dieses Artikels hinaus, hierzu finden sich z.B. sehr gute Infos unter https://www.thomas-krenn.com/de/wiki/MySQL_Replikation

Tipps und Tricks für das MySQL Master/Slave Setup

Der OXID EE Shop selbst ist bereits sehr gut für die Master-/Slave-Aufteilung optimiert, sobald Slaves konfiguriert sind, hält sich der Shop strikt an die definierte Aufteilung und führt z.B. Lesezugriffe immer auf den Slaves aus.

Für eigene Erweiterungen des Shops sollte man sich hier am OXID Standard orientieren, hier ist es v.a. wichtig, für Lese- oder Schreibzugriffe die richtigen Methoden des OXID Datenbank-Adapters zu verwenden.

Prinzipiell gilt: für Lesezugriffe sollte man immer die „select()“-Methode der „oxDb“-Klasse nutzen, da nur diese wirklich standardmässig dezidiert auf die Slaves zugreift.

$oRs = oxDb::getDb()->select("SELECT * FROM oxarticles WHERE oxartnum = '12345'"); // liest vom Slave

Man kann allerdings als dritten Parameter „false“ übergeben, um dennoch vom Master zu lesen – Standard ist jedoch „true“ und damit „Slave:

public function select($sSql, $aParams = false, $blType = true)

Auch die Methoden „getOne()“, „getArray()“, „getAssoc()“, „getRow()“ und „getAll()“ selektieren standardmässig von den Slaves, können aber ebenfalls per Parameter auf den Master „umgebogen“ werden bei Bedarf.

Nutzt man hingegen die Methoden „execute()“ oder „query()“, geht das SQL-Statement ausnahmslos an den Master-Server!

$oRs = oxDb::getDb()->query("SELECT * FROM oxarticles WHERE oxartnum = '12345'"); // liest vom Master

Problematisch sind bei Master-/Slave-Setups oft SQL-Fehler, welche zu Fehlermeldungen auf Datenbank-Ebene führen. MySQL ist hier meist recht rigoros und beendet die Synchronisation zwischen Master und Slaves. Diese muss dann vom System-Administrator wieder manuell aktiviert werden.

Man sollte das System also erst auf Herz und Nieren testen und die Synchronisation im Testbetrieb beobachten, um zu entscheiden, ob es hier kritische Stellen im Shop gibt und regelmässige SQL-Fehler auftauchen.

Handelt es sich um temporäre, „unwichtigere“ Daten (z.B. Session-Einträge, Captcha-Codes o.ä.), kann man ggf. in Erwägung ziehen, die betroffenen Tabellen aus der Synchronisation herauszunehmen bzw. MySQL-Fehler für diese Tabellen bei der Synchronisation zu ignorieren, siehe z.B. https://dev.mysql.com/doc/refman/5.7/en/replication-options-slave.html#option_mysqld_slave-skip-errors
Ausserdem kann helfen, die Slaves explizit als „read_only“ zu konfigurieren, damit es keine „versehentlichen“ Schreibversuche auf die Slaves gibt.

MySQL Master/Slave in OXID – Fazit

Mit der Option für Master-/Slave-Setups bietet OXID gerade für große Shops mit regelmässigen, umfangreichen Datenänderungen über Drittsysteme einen erheblichen Performance-Hebel.
Weiss man um die technischen Hintergründe des OXID-Datenbankadapters und kennt die möglichen Fehlerquellen auf Seiten der MySQL-Datenbank-Synchronisation, spricht nichts dagegen, dieses Feature im produktiven Einsatz zu nutzen.
Hat man dann noch einen technisch versierten Hoster an seiner Seite, kann eigentlich nichts mehr schiefgehen :)

Hoffentlich hat dieser Artikel dazu beigetragen, die technischen Hintergründe in Bezug auf OXID EE und das Mysql Master-/Slave-Setup zu erhellen!

 

Shopware als eines der ersten Shopsysteme mit dem gestern erschienenen PHP 7 kompatibel

Schöppingen, 04.12.2015 – Gestern ist mit dem Major Release 7.0 die neueste, unter Entwicklern lang erwartete Version der beliebten Skriptsprache PHP erschienen. Shopware ist als eines der ersten Shopsysteme, die auf der Sprache aufbauen, mit PHP 7 kompatibel – und das bereits seit der im September vorgestellten Shopware 5.1. Davon profitieren nicht nur Entwickler, sondern vor allem auch die Nutzer des Shopsystems.

Shopware und PHP7

„Der größte Vorteil der Kompatibilität mit PHP 7 ist die Performance-Steigerung. Unsere Messungen haben ergeben, dass Shopware nun in vielen Bereichen doppelt so schnell ist wie vorher. So wird beispielsweise der Theme Cache unter PHP 7 in nur circa zwei Sekunden erstellt, während dieser Prozess unter der Vorgängerversion PHP 5.6 noch etwa fünf Sekunden dauerte. Bei Tests zu anderen Prozessen ergaben sich immerhin noch Verbesserungen von mindestens 25 bis 30 Prozent“, sagt Stefan Hamann, Vorstand und Leiter der Entwicklung der shopware AG.

In der neuen Version der Skriptsprache stecken laut einschlägiger Meldungen knapp 10.000 Änderungen gegenüber der vor mehr als einem Jahrzehnt herausgebrachten Version PHP 5. Mehrfach war seit dieser Version vergeblich versucht worden, eine Version 6 herauszubringen, die jedoch, um Verwirrungen zu vermeiden, einfach übersprungen worden sei. Nun sei PHP 7 laut Herstellerangaben 14-mal schneller als PHP 5. Außerdem habe man nun eine konsistente 64-Bit-Unterstützung für Windows und eine stark überarbeitete Zend-Engine, um nur einige Verbesserungen zu nennen. Alles in allem sind die Änderungen beim neuen PHP 7 genug, um auf heise.de von „einer neuen Zeitrechnung für die Skriptsprache“ zu sprechen.

Und zwar eine Zeitrechnung, die auch bei Shopware eine große Rolle spielt: „Als einer der Innovationstreiber im deutschen eCommerce ist es uns wichtig, bei der Entwicklung von Shopware immer auf die neuesten Technologien zu setzen. Das gibt unseren Kunden ein großes Maß an Sicherheit und erlaubt uns, das System immer schnell den neuesten Standards anpassen zu können“, begründet Stefan Hamann.

Business Intelligence – Big Data durch Analyse für E-Commerce nutzbar machen

clouds-978965_1920

Die Schlagworte Business Intelligence und Big Data machen in letzter Zeit wieder verstärkt die Runde, auch wenn Erkenntnisse aus der systematischen, elektronischen Analyse von Daten bereits seit Anfang der 1990-er Jahre zur Optimierung operativer und strategischer Unternehmensentscheidungen genutzt werden. Grund dafür ist in erster Linie die fortschreitende Digitalisierung von Gesellschaft und Wirtschaft, die Geschäftsdaten in nahezu unendlich großen Mengen inzwischen ständig verfügbar macht.  Kunden von Online-Shops hinterlassen auf ihren Wegen durch das Internet an verschiedensten Stellen wichtige Informationen, die richtig verknüpft und analysiert für Unternehmen große Chancen bieten. Auch für shoptimax als Lösungsanbieter im E-Commerce rücken Big Data Analysen und Business Intelligence im allgemeinen immer mehr in den Vordergrund, da sie als Basis für optimierte Online-Vertriebs-Strategien von höchstem Wert sind. Dank ihnen können Geschäftsabläufe und Kundenbeziehungen profitabler gemacht, Kosten gesenkt und Risiken minimiert werden.

Wie Daten nutzbar machen?

Da es sich bei einem Großteil der Daten jedoch um unstrukturierte Daten oder Daten mit ungeeigneter Strukturierung aus verschiedenen Quellen handelt, ist für deren Auswertung der Einsatz entsprechender Lösungen, die alle Daten zusammenführen und analysierbar machen, manchmal unabdingbar. Mit welchen Mitteln das funktionieren und was man aus den Daten ableiten kann, zeigten Daniel Wrigley von der SHI GmbH und Andreas Leichtle von Hortonworks Ende Oktober gemeinsam in einem Webinar zum Thema „Das Geheimnis der Customer Journey? Suche, Relevanz, Umsatz“, an dem Mitarbeiter von shoptimax teilgenommen haben.

Das Geheimnis der Customer Journey

SHI ist Partner von Hortonworks und realisiert Lösungen, mit denen große Mengen an Daten optimal nutzbar gemacht werden können. Daniel Wrigley, Consultant für Search- und Big-Data-Technologien bei der SHI GmbH zeigte im Webinar anhand einer Fallstudie, wie Daten aus verschiedenen Quellen optimal zur Analyse der Customer Journey genutzt und daraus schließlich Handlungsoptionen abgeleitet werden können.

Das Fallbeispiel: ein E-Commerce Elektronikverkäufer beobachtet auf seiner Seite steigenden Traffic, der Umsatz aber bleibt gleich und die Conversion sinkt sogar. Der CEO des Elektronikverkäufers fragt nun einen Data-Scientist bzw. –Analyst, was die Ursachen für diese Situation sind und was man tun kann.

Hierzu sieht sich der Data-Analyst verschiedene Daten an. Diese Daten fallen zum Beispiel nicht nur in ERP- bzw. Warenwirtschafts-Systemen, sondern unter anderem auch durch Marketingmaßnahmen, beispielsweise Bannerschaltungen, sowie die interne Shop-Suche an (selbstverständlich ist dies nur ein kleiner Teil – weitere Daten können beispielsweise auch aus Log-Files, Social Media Monitoring und zahlreichen weiteren Quellen stammen).

Interessant für den Analysten sind im Zusammenhang mit möglichen Daten aus dem Warenwirtschaftssystem beispielsweise die Fragen „Was sind meine Topseller?“, „Sind meine Topseller profitabel?“, „Was beeinflusst meinen Profit (z.B. hohe Retourenquote bei einem Topseller)?“ und „Gibt es Kundenfeedback (z.B. zu dem häufig retournierten Topseller in eine negative Richtung)?“, aus denen sich schon erste mögliche Maßnahmen ableiten lassen können.

Aus den Daten zu produktspezifischen Marketingmaßnahmen bzw. Marketingkampagnen lassen sich zudem Fragen wie „Wie viele Kampagnen gibt es zu einem bestimmten Produkt (also z.B. zu dem nicht profitablen Top-Seller-Produkt)?“ und „Wie kann dieses Budget besser allokiert werden?“ beantworten.

Zuletzt gilt es noch die Shop-Suche zu betrachten, um die Frage zu beantworten, wie Nutzer eigentlich zu den Produkten gelangen, also welche Suchterme am häufigsten eingegeben werden, wann was genau gesucht wird und welche Suchanfragen zu null Treffern führen. Insgesamt lässt sich daraus die Frage beantworten, wie die Shop-Suche bzw. die Seite insgesamt optimiert werden kann.

Zusätzlich interessant für die Analyse können die aus den Logs indexierten Geo-Daten sein, die dem Shop-Betreiber zeigen, woher die User geografisch stammen und wo man dementsprechend das Marketingbudget am besten einsetzen könnte.

Die Technik hinter der Analyse

Allerdings liefern die genannten Quellen wie Warenwirtschaftssystem, Marketingmaßnahen und Shop-Suche die Daten in unterschiedlicher Art und Form. Sie können jedoch zusammengeführt, durchsuchbar und analysierbar gemacht werden, indem man sie in einer Datenbank abspeichert und indexiert – also in einem bestimmten Format auf einem Suchserver ablegt.

Eine Option an dieser Stelle einzusteigen bietet die Hortonworks Data Platform (HDP), welche auf Apache Hadoop basiert und unter anderem Hadoop Distributed File System (HDFS), MapReduce, Pig, Hive, HBase und Zookeeper beinhaltet. Die Plattform dient zur Analyse, Speicherung und Bearbeitung großer Datenmengen.

Mit Apache Hadoop können Daten aus verschiedenen Kanälen, auf denen der Shop-Kunde unterwegs ist, so zusammengeführt werden, dass sie eine 360 Grad Kundenansicht ermöglichen. Dank dieser Technologie muss nicht überlegt werden, wie Daten zu strukturieren sind, bevor man sie abspeichert, sondern die Daten können erst einmal verteilt abgespeichert und dann je nach Analysefragestellung zentral verarbeitet und strukturiert werden.

Um die angefallenen Daten analysieren zu können, ist schließlich eine Suchkomponente, wie beispielsweise Apache Solr, zu ergänzen. Solr ist für Analysen optimal geeignet, da es umfangreiche Such- und Strukturierungsmöglichkeiten bietet. Interaktiv visualisiert werden können die Daten dann beispielsweise noch mittels Banana, das Dashboards aus den in Solr gespeicherten Daten erstellen kann.

Einsatz von Hadoop zwingend?

Selbstverständlich kann für die oben beschriebenen Zwecke auch eine andere Datenbanklösung als die von Hortonworks und SHI vorgeschlagene verwendet werden, wie beispielsweise die dokumentenbasierte Datenbank MongoDB oder der verteilte Key-Value-Store Riak. Die Entscheidung für eine bestimmte Datenverarbeitungs-Infrastruktur muss je nach Anforderungen individuell entschieden werden. Es sind auch Lösungen denkbar, die ausschließlich auf Apache Solr als integrative Storage-Einheit setzen. Eine Rolle bei der Auswahl einzelner Schritte spielt nicht nur das Volumen und die Verteilung der Daten, sondern auch die Geschwindigkeit, mit der die Daten erzeugt werden und analysiert werden sollen, die Vielfalt der Datenformate bzw. Datentypen (strukturierte aus CRM oder ERP, semistrukturierte Daten aus Logs und unstrukturierte Daten aus Kundenrezensionen oder Tweets) und ob bzw. wie unterschiedliche Daten miteinander verknüpft werden sollen.

Individuelle Business-Intelligence-Lösungen

Das von SHI und Hortonworks vorgestellte Fallbeispiel ist natürlich nur eine Möglichkeit, gesammelte Daten für Entscheidungen zur Optimierung bestimmter Entscheidungen und Prozesse einzusetzen. Ein Patentrezept für das Vorgehen bei solchen Analysen gibt es generell nicht und die Lösungen sind je nach den Bedürfnissen und den Fragestellungen, die zu beantworten sind, immer individuell anzupassen.

shoptimax wird sich im Interesse seiner Kunden über aktuelle und laufende Business-Intelligence-Trends auf dem Laufenden halten. Sollten Sie bereits auf der Suche nach einer entsprechenden Business-Intelligence-Lösung sein, sprechen Sie uns an!