Laravel und Docker: Die perfekte Entwicklungsumgebung

aktualisiert am 26. Januar 2019, erstellt am 15. April 2018 von Dirk Helbert in Laravel
Laravel und Docker

Als Laravel Entwickler ist man immer darum bemüht eine optimale Entwicklungsumgebung zu haben. Laravel und Docker sind dabei zwei perfekte Partner für schnelle Entwicklung. Bisher habe ich VirtualBox oder Laravel Valet benutzt. Doch die beiden Möglichkeiten haben auch erhebliche Nachteile. Nachteile, die ich mit der Nutzung von Docker vermeiden möchte.

Ziel des Artikels ist eine einfache und reproduzierbare Entwicklungsumgebung mit Docker zu erstellen innerhalb von wenigen Minuten. Inspiriert wurde ich von Shane Osbourne und seinem Beitrag auf Medium.

Hol dir jetzt mein kostenloses E-Book
Geben Sie Ihre Email Adresse ein und klicken Sie auf den Knopf "Sofort Zugang erhalten".
Ich stimme zu, dass meine Daten beim Newsletter Provider Mailchimp gespeichert werden. ( Datenschutzerklärung und Datenschutz Mailchimp )
Mit der Anforderung des eBooks meldest du dich zu meinem kostenlosen E-Mail Newsletter mit relevanten Informationen zur Webentwicklung sowie meinen Büchern und Leistungen an.

Update 25.01.2019

In den letzten Wochen habe ich immer wieder Fragen zu dem Thema bekommen und einige hatten auch Probleme es zum laufen zu kriegen.

Daher will ich mir heute meinen Artikel und die Vorgehensweise selbst anschauen und ggf. den Artikel aktualisieren.

Ich habe in dem Zuge auch gleich noch den Code Highlighter ausgetauscht. Ich denke jetzt sind die Code Zeilen besser zu lesen.

Was ist Docker?

Docker ist eine Container Virtualisierung Software. Dabei werden einzelne Services in einem Container isoliert und unabhängig vom Betriebssystem ausgeführt. Im Vergleich zu einer Virtualisierung mit VirtualBox oder VMWare ist die Virtualisierung mit Docker sehr leichtgewichtig und spart Ressourcen auf dem Host System. Außerdem lassen sich Docker Container sehr schnell wieder von Grund auf neu initialisieren und es können sehr einfach komplette Produktivsysteme simuliert werden.

Warum nicht VirtualBox oder Laravel Valet?

Ich habe vor vielen Jahren mit VirtualBox angefangen und benutze es heute auf der Arbeit immer noch. Privat nutze ich seit einiger Zeit Laravel Valet, welches aber nur für Mac OS möglich ist. Viele Laravel Entwickler nutzen auch Laravel Homestead in Verbindung mit VirtualBox. VirtualBox benutzt mehr Systemressourcen als ein Docker Container. Bei Laravel Valet muss PHP, MySQL usw. auf dem Mac installiert sein und aktuell gehalten werden.

Gerade wenn man auch mal eine andere PHP Version testen will, dann ist eine Virtualisierung mit Docker einfach genial. Mehr über Docker erfährst du auf der What is Docker Seite

Vorbereitungen

Zuerst solltest du Docker für dein Betriebssystem installieren und zwar am besten die Docker Community Edition. Ich arbeite mit der Docker Version 18.03 und der Docker Compose Version 1.20.1.

docker -v
Docker version 18.09.0, build 4d60db4
docker-compose -v
docker-compose version 1.23.2, build 1110ad01

Ok, also Docker und Docker Compose sind jetzt schon in einer aktuelleren Version vorhanden. Das sollte allerdings bei diesem relativ einfachen Projekt auch keine große Rolle spielen.

Jetzt brauchst du noch ein Projektverzeichnis. Hier liegen dann später die beiden Verzeichnisse für die Docker Konfiguration und das Laravel Projekt. Ich nenne das hier einfach mal dockerprojekt.

mkdir dockerprojekt 
cd dockerprojekt 

Jetzt brauchst du noch eine aktuelle Laravel Version. Ich lade sie in dem Beispiel hier mit Curl runter. Du kannst auch einfach die Zip Datei von Github runterladen und dann entpacken. Wichtig ist, dass in dem dockerprojekt Verzeichnis ein laravel Verzeichnis liegt mit den entpackten Laravel Dateien.

curl -L https://github.com/laravel/laravel/archive/master.zip | tar xz  
mv laravel-master laravel 

Laravel und Docker konfigurieren

Docker Konfigurations Verzeichnis anlegen

Hier werden später alle Docker Konfigurationen abgelegt. Ich trenne die Konfiguration immer von dem eigentlichen Projekt. Das ist dann aufgeräumter.

mkdir docker
cd docker 

Docker Compose Konfiguration

Zuerst erstelle ich eine docker-compose.yml Datei und öffne sie mit einem geeigneten Editor. Hier musst du aufpassen, dass keine Tabs in der Konfiguration vorkommen. Sonst gibt es beim Start einen Fehler. Ich benutze für solche Sachen gerne Visual Studio Code (vorher Atom) du kannst aber auch vi, oder jeden anderen Editor benutzen.

code docker-compose.yml

Am Anfang steht immer die Konfigurations Versionsnummer. In dem Fall ist es Version 3 Version 3.7. Es kommt auf die Docker Version an, die du benutzt. Einige Konfigurationseinstellungen können während der Versionssprünge weggefallen sein. Eine Aufstellung der Versionsnummern findest du auf der Docker Seite Compose file versions and upgrading. Wenn du bereits eine aktuelle Version hast, dann kannst du meinem Beispiel einfach weiter folgen ohne Anpassungen vorzunehmen.

In der ersten Zeile steht also version: "3.7". Direkt danach folgen die Services. In unserem Fall sind das app für die Laravel Anwendung, web für den Web Server nginx und database für die MySQL Datenbank.

app Service

app:
  build:
    context: ./
    dockerfile: app.dockerfile
  working_dir: /var/www
  volumes:
    - ./../laravel:/var/www
  environment:
    - "DB_PORT=3306"
    - "DB_HOST=database"

In dem app Service wird die eigentliche Laravel Anwendung als Container konfiguriert. Wichtig ist hier dockerfile: app.dockerfile, das wir gleich noch erstellen werden. Unter volumes wird der Pfad von dem lokalen Verzeichnis ../laravel auf den Docker Container Pfad /var/www gemappt. Zusätzlich wird der Anwendung noch die Datenbank Informationen übergeben.

web Service

web:
 build:
   context: ./
   dockerfile: web.dockerfile
 working_dir: /var/www
 volumes:
   - ./../laravel:/var/www
 ports:
   - "8080:80"

Der web Service enthält den Webserver nginx. Dieser wird über das web.dockerfile konfiguriert. Das machen wir dann im nächsten Schritt. Der Port 8080 wird dann auf den Port 80 gemappt. Damit lässt sich unsere Anwendung dann später mit 127.0.0.1:8080 aufrufen.

database Service

database:
  image: mysql:5.6
  volumes:
    - ./db:/var/lib/mysql
  environment:
    - "MYSQL_DATABASE=homestead"
    - "MYSQL_USER=homestead"
    - "MYSQL_PASSWORD=secret"
    - "MYSQL_ROOT_PASSWORD=secret"
  ports:
    - "33061:3306"

Der letzte Service ist der database Service. image ist hier für das Docker Image und in dem Fall nutzen wir MySQL 5.6. Du kannst auch MySQL 5.7 dort eintragen und es funktioniert direkt. Auch eine spätere Änderung der Version ist schnell möglich. Auch hier mappen wir wieder den Port.

Damit die Datenbank beim nächsten Neustart des Datenbank Containers nicht wieder neu initialisiert wird, erstelle ich noch das Verzeichnis db. Hier werden dann die Datenbank Dateien hin gelinkt.

Hier die gesamte docker-compose.yml:

version: "3.7"

services:
  app:
    build:
      context: ./
      dockerfile: app.dockerfile
    working_dir: /var/www
    volumes:
      - ./../laravel:/var/www
    environment:
      - "DB_PORT=3306"
      - "DB_HOST=database"
  web:
    build:
      context: ./
      dockerfile: web.dockerfile
    working_dir: /var/www
    volumes:
      - ./../laravel:/var/www
    ports:
      - "8080:80"

  database:
    image: mysql:5.6
    volumes:
      - ./db:/var/lib/mysql
    environment:
      - "MYSQL_DATABASE=homestead"
      - "MYSQL_USER=homestead"
      - "MYSQL_PASSWORD=secret"
      - "MYSQL_ROOT_PASSWORD=secret"
    ports:
      - "33061:3306"

app Service konfigurieren

Jetzt kommt der app Service. Hier installieren wir PHP, den MySQL Client, Imagick, Composer, Git und Zip. Man kann die RUN Befehle auch zusammenfassen. Ich habe sie hier einzeln aufgeführt, damit es übersichtlicher ist.

code app.dockerfile
FROM php:7.1.3-fpm 
RUN apt-get update 
RUN apt-get install -y libmcrypt-dev 
RUN apt-get install -y mysql-client 
RUN apt-get install -y libmagickwand-dev --no-install-recommends 
RUN pecl install imagick 
RUN docker-php-ext-enable imagick 
RUN docker-php-ext-install mcrypt pdo_mysql 
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"  
RUN php composer-setup.php 
RUN php -r "unlink('composer-setup.php');" 
RUN mv composer.phar /usr/local/bin/composer 
RUN apt-get install -y git 
RUN apt-get update
RUN apt-get install -y zlib1g-dev 
RUN docker-php-ext-install zip 

Ich habe den folgenden RUN Befehl entfernt:

RUN php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"

Das hat natürlich einmal funktioniert, als ich den Artikel geschrieben habe. Aber der Hash ändert sich natürlich mit jeder Version. Daher lasse ich den Check weg ob die Datei intakt ist oder nicht.

web Service konfigurieren

In meinem Beispiel benutze ich nginx als Webserver. Dieser lässt sich auch als Docker Container sehr einfach konfigurieren.

code web.dockerfile
FROM nginx:1.10
ADD vhost.conf /etc/nginx/conf.d/default.conf 

Hier wird nginx 1.10 installiert und unsere vhost.conf als default.conf für nginx verwendet.

code vhost.conf
server {
    listen 80;
    index index.php index.html;
    root /var/www/public;
    
    location / {
        try_files $uri /index.php?$args;
    }
    
    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
} 

Endlich starten

Nachdem jetzt alles konfiguriert ist, können wir unsere Composition starten:

docker-compose up 

Beim allerersten Start dauert das jetzt ein wenig. Es müssen einige Pakete aus dem Internet geladen werden. Das aktuelle Konsolen Fenster bleibt auch geöffnet. Hier erscheinen auch Log Daten der einzelnen Container.

Mögliche Fehler:

ERROR: for web Cannot start service web: driver failed programming external connectivity on endpoint docker_web_1 (21edf08f68044607be77fc7f96bc4e2fa8972c38154b1954ce8e962fddc0ddf2): Error starting userland proxy: Bind for 0.0.0.0:8080 failed: port is already allocated
ERROR: Encountered errors while bringing up the project.

Wenn dieser Fehler auftritt, dann ist der Port schon in Benutzung. Dann änderst du einfach in der docker-compose.yml im Abschnitt web den Port 8080 auf 8800 oder irgendeinen der frei ist.

Also öffne am besten eine neue Konsole und geht wieder in das dockerprojekt/docker Verzeichnis.

docker-compose exec app mv .env.example .env
docker-compose exec app composer update
docker-compose exec app php artisan key:generate 

docker-compose exec führt auf dem Container app den Befehl mv .env.example .env aus. Das gilt auch für die beiden anderen Befehle. Hier wird die mitgelieferte Laravel Beispiel Konfiguration aktiviert, Composer aktualisiert alle benötigten Pakete und der Key wird generiert.

Mögliche Fehler:

Wenn die docker-compose exec Befehle eine Fehlermeldung zurückgeben, dann hat das meistens damit was zu tun, dass der laravel Ordner nicht korrekt in den Container gelinkt ist.

Daher hier die Verzeichnisstruktur:

dockerproject
  -- docker
    -- app.dockerfile
    -- db
    -- docker-compose.yml
    -- vhost.conf
    -- web.dockerfile
  -- laravel
    -- ... hier liegt dann das Laravel Projekt
  

Der erste Aufruf im Browser

Du kannst jetzt im Browser http://localhost:8080 (oder den geänderten Port) aufrufen und wooohoooooo. Wir sehen unsere Laravel Installation. Ist das nicht genial einfach?

Die kannst jetzt die Laravel Dateien bearbeiten und sie werden im Container automatisch aktualisiert, weil sie gelinkt sind.

Ein paar Meter extra

Da jetzt alles funktioniert machen wir noch die Probe ob auch die Datenbankverbindung einwandfrei läuft. Das sind die sprichwörtlichen extra Meter bzw. der Zusatzaufwand, wenn man es nüchtern aussprechen will.

Ok, also erweitern wir unsere Laravel Anwendung mal um die Authentifizierung.

docker-compose exec app php artisan make:auth
docker-compose exec app php artisan migrate 

Mögliche Fehler:

In dem Kommentar von Martin bin ich wieder auf einen Fehler gestossen, den ich vor einiger Zeit schon gesehen habe:

Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table users add unique users_email_unique(email))

Ich dachte der Fehler wäre längst behoben und spielt keine Rolle mehr. Aber auch ich sehe ihn jetzt während ich meine Vorgehensweise noch mal durchgehe.

Lösung: (nur wenn der Fehler bei dir auftritt

Füge folgenden Code in laravel/app/Providers/AppServiceProvider.php ein

use Illuminate\Support\Facades\Schema; 

...

public function boot() {
    Schema::defaultStringLength(191);
}
use Illuminate\Support\Facades\Schema; 

...

public function boot() {
    Schema::defaultStringLength(191);
}

Wenn du den oben genannten Fehler hattest, dann wurde die Tabelle users schon angelegt. Daher muss die Migration jetzt noch mal frisch (fresh) eingespielt werden.

docker-compose exec app php artisan migrate:fresh

Hier geht es weiter, wenn du den Fehler „Specified key was too long“ nicht hattest.

Danach erstellen wir mit artisan tinker einen neuen Benutzer.
dann tinker

docker-compose exec app php artisan tinker 
$user = new App\User;
$user->name = "Dirk";
$user->email = "info@example.com";
$user->password = bcrypt('123456');
$user->save(); 

Jetzt kannst du einmal die Seite localhost:8080 (oder den gewählten Port) im Browser neu laden. Oben rechts dann auf Login klicken und dich mit den eben erstellten Zugangsdaten einloggen. Wenn du eingeloggt bist, hat alles geklappt. Perfekt.

In der Realität benutzt du natürlich nicht 123456 als Passwort. Jetzt denk nicht: „Ja, das ist mit klar“. Wenn es wirklich allen klar wäre, dann hätte es den Promi Hacker Anfang 2019 nicht gegeben.

Wie du siehst, kann man in sehr kurzer Zeit eine Laravel Entwicklungsumgebung mit Docker erstellen. Vor allem ist diese Umgebung reproduzierbar. Mit ein paar Anpassungen kann man das auch für eine WordPress Entwicklungsumgebung machen. Hier sind keine Grenzen gesetzt. Einfacher geht es kaum.

Wenn dir der Beitrag gefallen hat, dann würde ich mich über einen Kommentar freuen.

Laravel Version : 5.7.22

3 Kommentare zu “Laravel und Docker: Die perfekte Entwicklungsumgebung”

  • Sorry – Funktioniert nicht! Sobald ich die exec Befehle eingeben möchte kommen Fehler. Die composer befehle gehen so auch nicht – habe ich durch „RUN curl -sS https://getcomposer.org/installer | php — –install-dir=/usr/local/bin –filename=composer“ ersetz, aber langsam verliere ich echt die nerven…

  • Hallo Dirk,

    Ich bin auf Win10 unterwegs und habe Docker for Windows installiert. Ich konnte bisher die Container (Linux-Container) aufrufen, scheitere dann aber an folgendem Befehl:

    docker-compose exec app mv .env.example .env

    Fehlermeldung: mv: cannot stat ‚.env.example‘: No such file or directory

    Ich habe aber noch eine Frage zur Ordnerstruktur. Ich habe diese wie folgt erstellt:

    projectName
    -laravel
    -app.dockerfile
    -docker-compose.yml
    -vhost.conf
    -web.dockerfile

    So habe ich es aus deinem Tutorial heraus gelesen.

    Vielen Dank für das Tutorial!

    Gruß
    Nahne

  • Vielen Dank, bei mir läuft es jetzt einwandfrei.
    Bin auf ein zwei Sachen gestoßen. Deshalb hier für andere ggf. eine Hilfestellung:

    Beim installieren von composer in der app.dockerfile meinte docker der Installer sei corrupt. Habe die Zeile # auskommentiert, dann funktioniert es. Hier muss ich später nochmal tiefer einsteigen.

    Und beim erstellen der User Table meinte Laravel
    „SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`))“
    -> das hatte mir geholfen: https://laravel-news.com/laravel-5-4-key-too-long-error

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.