Use J. Wilders nginx-proxy in multiple docker-compose projects

There is an awesome project for Docker if you want to run e.g. multiple webserver containers on the same ports on one host machine, say Apache or Nginx on port 80: jwilder/nginx-proxy.

Nginx-Proxy for Docker

You have to expose the port 80 in the Dockerfile as usual, but you don’t explicitly map the port in your docker-compose.yml or when using „docker run …“. Instead, you let the nginx-proxy do the heavy work and forward the requests to the right container. Therefore, you add an environment variable for the proxy:

environment:
 VIRTUAL_HOST: myapp.dev.local

so that it knows which request to forward to which container.

If you want to start multiple docker-compose.yml files, you can’t just add the nginx-proxy container to all the docker-compose.yml files though. If you only had one docker-compose project with e.g. multiple webservers on port 80, you could just add one proxy container to your YAML:

nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - "80:80"
    volumes:
      - /var/run/docker.sock:/tmp/docker.sock:ro

The Problem

But if you have multiple projects, there would be conflicts with this approach since there can only be one container with any given name – and you do only want one nginx-proxy across projects after all! Unfortunately, docker (compose) does not allow existing containers (yet?) and throws an error if you try to start the same container multiple times.

If you want to share the proxy container for different projects, you should also use an external network in your docker-compose.yml files like so (see github.com/jwilder/nginx-proxy/issues/552):

networks:
  default:
    external:
      name: nginx-proxy

Be aware that if you do this, you have to manually create the network before you run „docker-compose up -d“:

docker network create nginx-proxy

The Solution

solution for using the proxy accross projects would be to check for the network and nginx-proxy container before each call to „docker-compose up -d“. One way to do this is with a Makefile, e.g. in your „make start“ or „make up“ commands, you could call a shell script which does those checks for you:

start:
 ./config/run-proxy.sh
 docker-compose start

up:
 ./config/run-proxy.sh
 docker-compose up -d

This way, the script would create the required network and/or the proxy container if either of them doesn’t exist yet. So all the running projects / containers can share the global proxy container in the global network.

The Details

So, here is an example docker-compose.yml and also an example bash script (run-proxy.sh):

#!/bin/bash
##########################################################################
# script to check if the jwilder proxy container is already running
# and if the ngnix-proxy network exists
# should be called before "docker-compose up -d"
##########################################################################

if [ ! "$(docker network ls | grep nginx-proxy)" ]; then
  echo "Creating nginx-proxy network ..."
  docker network create nginx-proxy
else
  echo "nginx-proxy network exists."
fi

if [ ! "$(docker ps | grep nginx-proxy)" ]; then
    if [ "$(docker ps -aq -f name=nginx-proxy)" ]; then
        # cleanup
        echo "Cleaning Nginx Proxy ..."
        docker rm nginx-proxy
    fi
    # run your container in our global network shared by different projects
    echo "Running Nginx Proxy in global nginx-proxy network ..."
    docker run -d --name nginx-proxy -p 80:80 --network=nginx-proxy -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy
else
  echo "Nginx Proxy already running."
fi

And, for reference – an example docker-compose.yml:

version: '2'
services:

  shopware:
    image: docker.myregistry.de/docker/php7-apache/image
    container_name: appswdemo
    environment:
     VIRTUAL_HOST: shopware.dev.local
     VIRTUAL_PORT: 80
     DB_HOST: db
     SHOPWARE_VERSION: 5.3
    volumes:
     - ./config/config.php:/var/www/html/config.php
     - ./src/pluginslocal:/var/www/html/engine/Shopware/Plugins/Local
     - ./src/plugins:/var/www/html/custom/plugins
     - ./src/customtheme:/var/www/html/themes/customtheme
    links:
    - db

  # data only container for persistence
  dbdata:
    container_name: dbdataswdemo
    image: mysql:5.6
    entrypoint: /bin/bash
  db:
    image: mysql:5.6
    container_name: dbswdemo
    environment:
        MYSQL_ROOT_PASSWORD: root
        MYSQL_DATABASE: shopware
        MYSQL_USER: shopware
        MYSQL_PASSWORD: shopware
        TERM: xterm
    volumes_from:
      - dbdata

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    environment:
      VIRTUAL_HOST: shopwaredb.dev.local
      VIRTUAL_PORT: 8080
      PMA_ARBITRARY: 1
      MYSQL_USER: shopware
      MYSQL_PASSWORD: shopware
      MYSQL_ROOT_PASSWORD: root
    links:
      - "db:db"

networks:
  default:
    external:
      name: nginx-proxy

As you can see, the web container („shopware“ in this example), which runs Apache and PHP 7 in this case, doesn’t map any explicit ports, it only tells the proxy its URL and „virtual port“, but there is no „ports:“ section in the YML file. Same goes for the „phpmyadmin“ container.

And finally, the relevant parts of the Makefile:

ARGS = $(filter-out $@,$(MAKECMDGOALS))
MAKEFLAGS += --silent
start:
 ./run-proxy.sh
 docker-compose start
up:
 ./run-proxy.sh
 docker-compose up -d
#############################
# Argument fix workaround
#############################
%:
 @:

nginx-proxy would now forward all requests to shopware.dev.local to the PHP / Apache container on port 80 and also shopwaredb.dev.local to the PhpMyAdmin container on Port 8080, and you could start more containers on port 80 and 8080 (PhpMyAdmin) without any port conflicts on your host!

Tools und Tipps vom Developercamp 2017

Nach dem Camp ist auf dem Camp – erst das Devops Camp letztes Wochenende, nun das Developer Camp im Rahmen der Webweek Nürnberg.

Auch auf dem DevCamp gibt es natürlich zahlreiche interessante Sessions mit vielen Tools und Tipps für den Entwickler-Alltag. Hier eine selektive Auswahl aus Sessions, die ich bisher besucht habe – viel Spass! 🙂

Webassembly

React Native

Big Data

Ramda.js / Functional programming

NPM

Monolith zu Microservice

REST-Backend

Ansible

Documentation

Deep Learning

Sonstiges

DevOps Camp 2017 Tool-Tipps

Das DevOps Camp 2017 vom 12.-14. Mai 2017 bot wieder zahlreiche interessante Vorträge, Diskussionen und Leute, tolles Essen und super Atmosphäre. Ein komplettes Recap folgt später, hier schonmal eine kleine Link-Sammlung für Tipps und Tools, die ich beim DevOps-Camp (wieder-)entdeckt habe bzw. die ich mir nun endlich einmal ansehen muss 🙂

Hier werde ich sicher noch einige ergänzen, schaut gelegentlich mal vorbei 🙂

 

 

OXID Artikel-Importer

Bei OXID Exchange finden Sie den rent-a-hero Artikelimporter für OXID eShops der Version 4.x / 5.x (CE, PE und EE).
Dabei handelt es sich um ein Modul zum Importieren / Aktualisieren von Artikeln, Bildern und damit zusammenhängenden Daten (Artikeltexte, SEO Daten, Attribute, Auswahllisten, Crossselling, usw.) mittels CSV-Dateien.

Neben den Feldern der Tabelle oxarticles können auch andere Tabellen befüllt werden. Aktuell können Langtext (oxartextends), SEO-Keywords und Description (oxseo), Attribute (oxattribute, oxobject2attribute), Auswahllisten (oxselectlist, oxobject2selectlist), Hersteller und Lieferanten (oxvendor und oxmanufacturers) automatisch angelegt und zugeordnet werden. Es werden auch Auswahllisten neu angelegt, falls noch nicht vorhanden.
Weiterhin ist es möglich, die Artikel in eine beliebige Kategorie zu importieren. Ausserdem können aus Bildern, die per FTP  hochgeladen werden automatisch alle relevanten Shop-Bilder generiert werden. Das Modul kann damit auch zum Skalieren der Shopbilder eingesetzt werden.
Generell kann der Import beliebig oft wiederholt werden, die bereits vorhandenen Daten werden dann überschrieben/aktualisiert. Der Import kann auch per Cronjob automatisiert werden.

NEU in Version 2.3.0.: es können nun auch externe Bilder per URLs heruntergeladen und importiert werden! Verfügbar bis OXID 4.10 bzw. 5.3.!

Der Preis beträgt 199 EUR (plus MwSt.) für die CE-Version und 249 EUR (plus MwSt.) für die kostenpflichtigen PE/EE Versionen. Sie können auch direkt bei uns per Rechnung bestellen, kontaktieren Sie uns einfach!

OXID|Json – REST Schnittstelle für den OXID eShop mit AngularJS Frontend auf Github

Seit einigen Wochen auf Github verfügbar: OXID|Json!

OXID|Json bietet ein CRUD-Interface (Create, Read, Update, Delete)
für den OXID eShop an und enthält ein mit AngularJS erstelltes
Frontend, mit dem die wichtigsten Funktionen getestet werden können.

Das Frontend erlaubt die “Inline”-Bearbeitung aller Daten, die Daten
können sortiert, gefiltert und modifiziert werden. Durch die Verwendung
des Responsive-Frameworks “Twitter Bootstrap” ist die Oberfläche auch mit mobilen Endgeräten nutzbar.

Die REST-Schnittstelle basiert auf Tonic und ist nach einem verschlüsselten Login mit Shop-Zugangsdaten je nach Rechten read-only oder mit Vollzugriff nutzbar.

Define associative arrays in Smarty

Here is a quick and dirty way to define an associative array in Smarty 🙂

[{ assign var='myArray' value='/[\s,:]+/'|preg_split:"Blue:blue.png,Black:black.png,Red:deep_red.png"}]
[{section loop=$myArray name=item step=2}]
[{assign var="midx" value=$smarty.section.item.index+1}]
Key: [{$myArray[item]}] - Value: [{ $myArray[$midx] }]<br>
[{/section }]

PHP Tipps – Arbeiten mit SAP IDoc Dateien

SAP IDoc (Intermediate Document) wird von SAP intern verwendet, um z.B. Bestelldaten abzubilden. In der Regel werden IDoc-Daten mittels Message Mapping in andere Formate hin- und herkonvertiert, z.B. in EDIFACT.
Eine generelle Definition von IDoc findet sich z.B. auf www.php-deluxe.de:

Ein IDoc besteht nur aus Textzeichen und besteht aus einem Kontrollsatz, den Datensätzen und Statussätzen.

Der Kontrollsatz besteht aus Sender, Empfänger, Nachrichtentyp und
IDoc-Typ. Der IDoc-Typ beschreibt die Struktur der Datensätze, der
Nachrichtentyp entspricht einem in dem IDoc-Typ definierten, konkreten
Geschäftsvorfall (z.B. Bestellung, Lieferung).

In den Datensätzen sind die zu übertragenden Informationen enthalten, der Aufbau entspricht grundsätzlich einer Datenbanktabelle mit einer durch den IDoc-Typ definierten Struktur und den dazugehörigen Datensegmenten.

In den Statussätzen sind im IDoc-Format Verwaltungsinformationen wie
Bearbeitungsbeginn, Übermittlungszeit, Fehlermeldungen und ähnliches
abgelegt.

Prinzipiell können IDocs auch im XML-Format verwendet werden, dennoch ist das „klassische“ IDoc Format immer noch weit verbreitet, nicht zuletzt wegen des geringeren Datenvolumens durch die „schlankere“ Struktur.
Dieses „klassische“ IDoc-Format ist gekennzeichnet durch „feste Satzlängen“, d.h. eine Zeile im IDoc ist genau spezifiziert, was die Position der Datensätze (Offsets) und die „Zeilenlänge“ angeht. So kann z.B. eine Zeile im Dokument (Beispiel aus der „ORDERS05“ Struktur) folgendermaßen definiert sein:

Segment E1EDK01:

Segmentdefinition E2EDK01005
freigegeben seit Release 45B , Segmentlänge : 0357

  1. ACTION : Aktionscode, der die gesamte
    EDI-Nachricht betrifft.
    interner Datentyp : CHAR
    interne Länge : 000003 Stellen
    Position in Segment : 001, Offset : 0063. externe Länge : 000003
  2. KZABS : Kennzeichen für
    Auftragsbestätigungspflicht
    interner Datentyp : CHAR
    interne Länge : 000001 Stellen
    Position in Segment : 002, Offset : 0066. externe Länge : 000001
  3. CURCY : Währunginterner Datentyp : CHAR
    interne Länge : 000003 Stellen
    Position in Segment : 003, Offset : 0067. externe Länge : 000003
  4. usw.

Der Parameter „ACTION“ ist also vom Typ „CHAR“, steht in der Zeile, die mit „E1EDK01“ beginnt, an Offset 63 und hat eine Länge von 3 Zeichen, danach kommt der Parameter „KZABS“, dieser steht an Stelle/Offset 66, ist ebenfalls ein „CHAR“ und hat eine Länge von einem Zeichen usw.

Dies würde nun folgendermaßen aussehen:

E1EDK01005                       ABC                           1

wobei sich rechts ggf. noch weitere Werte anschliessen würden.

Zudem hat jede „Zeile“ im Normalfall eine feste Zeichenlänge, unser Beispiel-Segment muss insgesamt 1063 Zeichen haben – stehen am Ende keine Werte, muss die Zeile mit Leerzeichen „aufgefüllt“ werden.

Für einen „IDoc Writer“ in PHP bräuchte man also vor allem eine Möglichkeit, Zeichenketten von vorgegebener (maximaler) Länge an bestimmte Stellen (Offsets) einer Zeile zu schreiben sowie die Zeile mit Leerzeichen bis zum Ende aufzufüllen. Ist eine Zeichenkette / ein Wert länger als an dem vorgegebenen Offset „Platz“ ist, muss die Zeichenkette gegebenenfalls abgeschnitten werden, um nicht versehentlich den Wert am nächsten Offset zu überschreiben. Zeilen werden im Regelfall nur mit „Line Feed“ beendet (in PHP entspricht dies „\n“):

 

$idocWriter =  new smxIdocWriter();
$idocWriter->write(array("E1EDKA1","AG", "0815", "Stefan Moises", "Some company", "Street 45a"), array(0,63,66,100,135,240), 1063);
$idocWriter->toFile("/tmp/test_idoc.dat");

 

Ein kleiner Tipp am Rande: werden die IDoc-Dateien nach Erstellung per FTP transportiert, sollten diese im Binär-Modus versendet werden, da FTP im ASCII-Modus die Eigenart hat, am Zeilenende zusätzlich zum „Line Feed“ auch ein „Carriage Return“ (in PHP „\r“) einzufügen – zumindest macht dies der „ftp_put()“ Befehl in PHP, wenn man ihm FTP_ASCII statt FTP_BINARY als Transfer-Typ übergibt).

Viel Spass mit IDoc! 🙂 Hier gibt es eine einfache IDoc-Klasse zum Download: smxIdocWriter.php_.txt.