LAMP Stack für Laravel auf WSL installieren

Für Windows existieren eine Vielzahl von LAMP Applikationen wie Laragon oder XAMPP. Allerdings sind einige nützliche Pakete, die man zu Laravel hinzufügen kann, nicht mit Windows kompatibel. Auch Funktionen wie der Scheduler oder Queue Workers können nur über Umwege getestet werden. Anstelle für jeden Entwickler bei jedem Projekt Server zu erstellen und echte Domains darauf umzuleiten, haben wir hier eine detaillierte Anleitung erstellt, mit der man auf dem Windows Subsystem for Linux eine vollwertige Entwicklungsumgebung erstellen kann.

Auch erstellt haben wir ein Shell Script, welches die komplette Verwaltung übernimmt:

  • Starten/Stoppen/Neustarten des LAMP Stacks
  • Anzeigen aller vHosts/Ordner
  • Erstellung neuer Projekte mit Option für Laravel
  • Erstellung einer neuen Datenbank mit Benutzer und Passwort und automatischem Ausfüllen der .env.
  • Erstellung von Self Signed Certificates

Updates

Vorbereitung in Windows

Damit wir innerhalb von Windows auf die im WSL gehosteten Seiten zugreifen können, müssen wir jedes Mal einen neuen Eintrag in der Host Datei hinterlegen. Damit das automatisch durch unser Script übernommen werden kann, verwenden wir gsudo. Um gsudo zu installieren, öffnen wir PowerShell als Administrator und verwenden diesen Befehl:

PowerShell -Command "Set-ExecutionPolicy RemoteSigned -scope Process; iwr -useb https://raw.githubusercontent.com/gerardog/gsudo/master/installgsudo.ps1 | iex"

Als nächstes loggen wir uns im Windows Store ein und installieren Ubuntu. Wir verwenden die neueste Version (Ubuntu 22.04 LTS).

Grundinstallation

Update: Einfaches Installationskript

Nachdem ich dieses Tutorial zum vierten mal in kurzer Zeit durcharbeite, habe ich das getan was jeder fauler Entwickler macht. 2 Stunden lang ein Shell Skript erstellt um alles automatisch zu machen. Natürlich sollte man den Rest des Blogeintrages weiter lesen um zu sehen was passiert.

wget https://www.admin-code.de/downloads/install-lamp.sh
bash install-lamp.sh

Ab hier muss man der Anleitung wieder folgen da wir uns aus Unix rausbewegen um das SSL Zertifikat zu hinterlegen.

Manuelle Installation

Nach erfolgreicher Installation von Ubuntu wechseln wir auf den root Benutzer.

sudo su

Damit wir ungestraft sudo verwenden können ohne ständig das Passwort eintippen zu müssen, fügen wir mit visudo am Ende der sudoers Datei eine Ausnahme für unseren Account hinzu (billinger mit dem eigenen Accountnamen austauschen).

billinger ALL=(ALL) NOPASSWD:ALL

PHP Repository hinzufügen:

add-apt-repository ppa:ondrej/php

Update, Upgrade und Installation von Apache, MySQL und PHP. Da bei älteren Laravel Versionen PHP 8 nicht funktioniert, installieren wir gleich PHP 7.4 und 8.1 gleichzeitig. Je nachdem welche Version später notwendig ist, kann diese einfach gewechselt werden.

apt-get update && apt-get upgrade -y 
apt install -y apache2 mysql-server unzip php8.1-{fpm,gd,mysql,curl,xml,zip,intl,mbstring,bz2,ldap,apcu,bcmath,gmp,imagick,igbinary,redis,smbclient,cli,common,opcache,readline,imagick,redis}  php7.4-{fpm,gd,mysql,curl,xml,zip,intl,mbstring,bz2,ldap,apcu,bcmath,gmp,imagick,igbinary,redis,smbclient,cli,common,opcache,readline,imagick,redis} 

Damit der mysql Benutzer auch ein Home Verzeichnis hat und nicht bei jedem Neustart einen Fehler ausgibt:

usermod -d /var/lib/mysql/ mysql

Apache Konfiguration

Diverse notwendige Apache Module werden aktiviert und anschließend PHP FPM 8.1 umgestellt. Auf dem gleichen Weg kann auch PHP 7.4 aktiviert werden.

a2enmod rewrite ssl vhost_alias authz_groupfile headers cache expires actions alias proxy_fcgi proxy proxy_html proxy_http xml2enc mpm_event http2
a2enconf php8.1-fpm
echo "Protocols h2 http/1.1" >> /etc/apache2/apache2.conf

PHP Konfiguration

Optimierung der Konfiguration:

sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/pm.max_children =.*/pm.max_children = 240/" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/pm.start_servers =.*/pm.start_servers = 30/" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/pm.min_spare_servers =.*/pm.min_spare_servers = 10/" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/pm.max_spare_servers =.*/pm.max_spare_servers = 200/" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/;pm.max_requests =.*/pm.max_requests = 1000/" /etc/php/8.1/fpm/pool.d/www.conf
sed -i "s/allow_url_fopen =.*/allow_url_fopen = 1/" /etc/php/8.1/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/8.1/cli/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/8.1/cli/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/8.1/cli/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/8.1/cli/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/8.1/cli/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/8.1/cli/php.ini
sed -i "s/memory_limit = 128M/memory_limit = 1024M/" /etc/php/8.1/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/8.1/fpm/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/8.1/fpm/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/8.1/fpm/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/8.1/fpm/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/8.1/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/8.1/fpm/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=16/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/8.1/fpm/php.ini
sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/8.1/fpm/php.ini
sed -i "s|;emergency_restart_threshold.*|emergency_restart_threshold = 10|g" /etc/php/8.1/fpm/php-fpm.conf
sed -i "s|;emergency_restart_interval.*|emergency_restart_interval = 1m|g" /etc/php/8.1/fpm/php-fpm.conf
sed -i "s|;process_control_timeout.*|process_control_timeout = 10|g" /etc/php/8.1/fpm/php-fpm.conf
sed -i '$aapc.enable_cli=1' /etc/php/8.1/mods-available/apcu.ini
sed -i "s/rights=\"none\" pattern=\"PS\"/rights=\"read|write\" pattern=\"PS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"EPS\"/rights=\"read|write\" pattern=\"EPS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"PDF\"/rights=\"read|write\" pattern=\"PDF\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"XPS\"/rights=\"read|write\" pattern=\"XPS\"/" /etc/ImageMagick-6/policy.xml

sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/pm.max_children =.*/pm.max_children = 240/" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/pm.start_servers =.*/pm.start_servers = 30/" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/pm.min_spare_servers =.*/pm.min_spare_servers = 10/" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/pm.max_spare_servers =.*/pm.max_spare_servers = 200/" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/;pm.max_requests =.*/pm.max_requests = 1000/" /etc/php/7.4/fpm/pool.d/www.conf
sed -i "s/allow_url_fopen =.*/allow_url_fopen = 1/" /etc/php/7.4/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.4/cli/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/7.4/cli/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.4/cli/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.4/cli/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.4/cli/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.4/cli/php.ini
sed -i "s/memory_limit = 128M/memory_limit = 1024M/" /etc/php/7.4/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.4/fpm/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 3600/" /etc/php/7.4/fpm/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.4/fpm/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.4/fpm/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.4/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.4/fpm/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.4/fpm/php.ini
sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/7.4/fpm/php.ini
sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/7.4/fpm/php.ini
sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/7.4/fpm/php.ini
sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=16/" /etc/php/7.4/fpm/php.ini
sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/7.4/fpm/php.ini
sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/7.4/fpm/php.ini
sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/7.4/fpm/php.ini
sed -i "s|;emergency_restart_threshold.*|emergency_restart_threshold = 10|g" /etc/php/7.4/fpm/php-fpm.conf
sed -i "s|;emergency_restart_interval.*|emergency_restart_interval = 1m|g" /etc/php/7.4/fpm/php-fpm.conf
sed -i "s|;process_control_timeout.*|process_control_timeout = 10|g" /etc/php/7.4/fpm/php-fpm.conf
sed -i '$aapc.enable_cli=1' /etc/php/7.4/mods-available/apcu.ini
sed -i "s/rights=\"none\" pattern=\"PS\"/rights=\"read|write\" pattern=\"PS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"EPS\"/rights=\"read|write\" pattern=\"EPS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"PDF\"/rights=\"read|write\" pattern=\"PDF\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights=\"none\" pattern=\"XPS\"/rights=\"read|write\" pattern=\"XPS\"/" /etc/ImageMagick-6/policy.xml

Erweiterung der php.ini mit einer custom.ini und Erstellung von symbolischen Links:

#### /etc/php/custom.ini

max_execution_time = 2400
max_input_time = 900
post_max_size = 800M
memory_limit = 4048M
upload_max_filesize = 800M
max_input_vars = 100000
max_file_uploads = 5000
realpath_cache_size = 4M
date.timezone = 'Europe/Berlin'
display_errors = On
error_log = /var/log/php-error.log
error_reporting = E_ALL
phar.readonly = 0

opcache.enable=1
opcache.enable_cli=0
opcache.memory_consumption=512
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=32531
opcache.save_comments=1
opcache.fast_shutdown=0
opcache.max_file_size=0
opcache.validate_timestamps=1
opcache.revalidate_freq=2
ln -s /etc/php/custom.ini /etc/php/7.4/fpm/conf.d/custom.ini
ln -s /etc/php/custom.ini /etc/php/8.1/fpm/conf.d/custom.ini
ln -s /etc/php/custom.ini /etc/php/7.4/cli/conf.d/custom.ini
ln -s /etc/php/custom.ini /etc/php/8.1/cli/conf.d/custom.ini

Composer Installation

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
mv composer.phar /usr/local/bin/composer
echo "export COMPOSER_ALLOW_SUPERUSER=1" >> ~/.bashrc

NPM Installation

curl -fsSL https://raw.githubusercontent.com/tj/n/master/bin/n | bash -s lts
npm install -g n

Mit dem Paket „n“ kann man einfach die NPM und Node Versionen installieren und ändern.

lamp Script hinterlegen

visudo ausführen und in der Zeile Details secure_path= /opt/lamp hinzufügen

mkdir /opt/lamp
cd /opt/lamp
wget https://admin-code.de/downloads/lamp
chmod +x lamp
echo "export PATH='$PATH:/opt/lamp'" >> ~/.bashrc
source ~/.bashrc

Rechte in /var/www anpassen

(billinger mit dem eigenen Accountnamen austauschen)

chown -R www-data:www-data /var/www
chmod 00755 /var
chmod 00755 /var/www
find /var/www -type d -exec chmod 00755 {} \;
find /var/www -type f -exec chmod 00644 {} \;
usermod -a -G www-data billinger

Mögliche Script Schalter

Ausführen kann man das Script mit dem Befehl „lamp“. Danach kommen die weiteren Keywords. Die Befehle „start“, „stop“, „restart“, „status“ beziehen sich auf die Services. Mit „list“ kann man kontrollieren, welche Konfigurationen in Apache vorhanden sind und welche Ordner in /var/www stehen. Mit „add Projektname“ und „remove Projektname“ können Projekte hinzugefügt und gelöscht werden (beim Löschen wird nur die Apache Konfiguration gelöscht. Die Daten bleiben weiterhin vorhanden).

Die Hilfe kann über „lamp help“ angezeigt werden.

Lamp Script anpassen

Damit PHP-FPM korrekt gestartet, beendet und neu gestartet werden kann müssen die entsprechenden Zeilen in /opt/lamp/lamp angepasst werden. Die entsprechenden Versionen sollten auskommentiert werden.

Script ausführen

Da es ein paar Funktionen gibt die ohne https nicht funktionieren, erstellen wir für alle neuen Projekte automatisch Zertifikate. Da es sich hier allerdings um lokale Domainnamen handelt, können hier keine „echten“ Zertifikate wie von Let’s Encrypt verwendet werden. Eine Certificate Authority kann die Validität unserer Entwicklungsseiten nicht verifizieren. Deswegen werden wir unsere eigene Certificate Authority erstellen.

Wir verwenden hierfür dev-certificates von Ben Morel (https://github.com/BenMorel).

Unser lamp Script prüft automatisch, ob das Zertifikat schon existiert und erstellt es gegebenenfalls neu. Damit der Browser dieses auch verwendet, muss es hinterlegt werden.

Nachdem das Zertifikat hinterlegt wurde muss der Browser komplett geschlossen werden, am besten einmal den PC Neustarten.

Bei der Erstellung eines neuen Projektes kann zwischen Laravel und nicht Laravel entschieden werden. Ebenfalls ist es möglich, ein zuvor gepulltes Projekt in die vHosts mit einzufügen. Dabei sollte man die Frage ob Laravel installiert werden soll, ablehnen. Eine neue Datenbank kann trotzdem automatisch generiert und in die .env geschrieben werden.

Ebenfalls ist es möglich, „normale“ Projekte mit oder ohne Datenbank zu erstellen. Diese bekommen dann, um zu verifizieren dass die Installation funktioniert hat, eine Standard HTML Seite.

Unter den allgemeinen Informationen im Script kann die Variable TLD mit einer gewünschten TLD definiert werden. Moderne Browser werden beim aufrufen wie my-project.local direkt die Suchmaschine verwenden. Wer nicht jedes erste Mal https:// schreiben möchte, kann hier eine reguläre TLD wie .de oder .com verwenden. Besser wären allerdings die neuen TLDs wie .fun, .xyz, oder thematisch passend .test.

Autostart LAMP Stack

Da Apache und MySQL nicht Teil unseres Windows Betriebssystems sind, steht uns der LAMP Stack erst zur Verfügung, wenn Ubuntu gestartet wird und wir dort die Services entweder automatisch oder manuell über „lamp start“ ausführen. Damit dies automatisiert passiert erstellen wir uns ein kleines Script im Autostart Ordner.

Als nächstes öffnen wir mit WIN + R den Ausführen Dialog und öffnen den Autostart Ordner von Windows.

shell:startup

In diesem Ordner erstellen wir nun eine neue Batch Datei in der wir unsere WSL Befehle hinterlegen. Die Batch Datei wird bei jedem Systemstart automatisch ausgeführt.

@echo off 
wsl sudo bash /opt/lamp/lamp start

Visual Studio Code mit WSL Verbinden

Sobald WSL auf dem Gerät installiert ist, sollte sich Visual Studio Code automatisch beim Start melden und das Plugin „Remote – WSL“ vorschlagen, nun dieses installieren und in der .bashrc den Pfad für Visual Studio hinterlegen. Danach kann in WSL mit dem Befehl „code /path/to/open“ das gewünschte Verzeichnis geöffnet werden.

export PATH=$PATH:"/mnt/c/Users/BILLINGER/AppData/Local/Programs/Microsoft VS Code/bin"

Abschließend nochmal das .bashrc File mit source .bashrc laden.

Sollten Sie noch Fragen haben oder eine Beratung wünschen, können Sie gerne mit uns Kontakt aufnehmen oder unsere Webseite besuchen.

Gerne können Sie hier auch weitere Artikel zum Thema Laravel lesen. Andere Sysadmin Blogartikel finden Sie auf unserem Hauptblog.