Website-Suche

Wie man Laravel mit Docker Compose auf Ubuntu 22.04 installiert und einrichtet


Einführung

Eine Anwendung zu containerisieren bezieht sich auf den Prozess, eine Anwendung und ihre Komponenten so anzupassen, dass sie in leichten Umgebungen, die als Container bekannt sind, ausgeführt werden kann. Solche Umgebungen sind isoliert und austauschbar und können für die Entwicklung, das Testen und die Bereitstellung von Anwendungen in der Produktion genutzt werden.

In diesem Leitfaden verwenden wir Docker Compose, um eine Laravel-Anwendung für die Entwicklung zu containerisieren. Nach Abschluss haben Sie eine Demo-Laravel-Anwendung, die auf drei separaten Service-Containern läuft:

  • Ein app-Dienst, der PHP7.4-FPM ausführt;
  • Ein db-Dienst, der MySQL 8.0 ausführt;
  • Ein nginx-Dienst, der den app-Dienst verwendet, um PHP-Code zu parsen, bevor die Laravel-Anwendung dem Endbenutzer bereitgestellt wird.

Um einen reibungslosen Entwicklungsprozess zu ermöglichen und das Debuggen von Anwendungen zu erleichtern, werden wir die Anwendungsdateien mithilfe gemeinsamer Volumes synchron halten. Außerdem werden wir sehen, wie man docker compose exec-Befehle verwendet, um Composer und Artisan im app-Container auszuführen.

Stellen Sie Ihre Frontend-Anwendungen von GitHub mit der App-Plattform von Linux-Console.net bereit. Lassen Sie Linux-Console.net die Skalierung Ihrer App übernehmen.

Wichtige Erkenntnisse:

  • Containerisierung macht die Entwicklung vorhersehbar: Das Ausführen von Laravel in Docker sorgt für konsistente Umgebungen auf verschiedenen Rechnern, beseitigt das Problem "Es funktioniert auf meinem Computer" und erleichtert die Zusammenarbeit.
  • Die Trennung von Diensten verbessert die Wartbarkeit: Das Aufteilen von PHP, MySQL und Nginx in separate Container hilft, Probleme zu isolieren, vereinfacht Updates und spiegelt reale Produktionsumgebungen wider.
  • Umgebungsvariablen sind zentral für eine flexible Konfiguration: Die Verwendung einer .env-Datei ermöglicht es Entwicklern, Datenbankanmeldeinformationen und andere Einstellungen zu ändern, ohne den Anwendungscode zu berühren.
  • Benutzerdefinierte Docker-Images verbessern Kontrolle und Sicherheit: Wenn Sie Ihr eigenes PHP-Image erstellen, stellen Sie sicher, dass Sie nur das installieren, was Ihr Projekt benötigt, und halten die Umgebung leichtgewichtig und sicher.
  • Dateiberechtigungen müssen mit den Container-Benutzern übereinstimmen: Das Festlegen der richtigen Eigentumsrechte und Berechtigungen für Laravels beschreibbare Verzeichnisse verhindert Laufzeitfehler und unterstützt eine reibungslose lokale Entwicklung.
  • Die Verwendung von Override-Dateien unterstützt mehrere Umgebungen: docker-compose.override.yml ermöglicht lokale Anpassungen – wie Live-Reloading – ohne die Produktionskonfigurationen zu verändern. Dies hält Entwicklung und Deployment sauber getrennt.
  • Fehlerbehebung beginnt mit dem Verständnis des Containerverhaltens: Zu wissen, wie man Protokolle einsehen, laufende Dienste überprüfen und die Konnektivität testen kann, ist entscheidend, um die meisten Probleme in containerisierten Umgebungen schnell zu lösen.
  • Ein Docker-basiertes Setup passt sich Ihrem Workflow an: Sobald es erstellt ist, kann dieselbe Konfiguration lokale, Staging- oder Produktionsumgebungen unterstützen und schafft eine stabile, wiederverwendbare Grundlage für zukünftige Laravel-Projekte.

Voraussetzungen

  • Zugang zu einer lokalen Ubuntu-Maschine oder einem Entwicklungsserver als Nicht-Root-Benutzer mit Sudo-Rechten. Wenn Sie einen Remote-Server verwenden, wird empfohlen, eine aktive Firewall zu installieren. Um diese einzurichten, lesen Sie bitte unseren Leitfaden zur Ersteinrichtung eines Servers für Ubuntu.
  • Docker auf Ihrem Server installiert, nach den Schritten 1 und 2 von 'So installieren und verwenden Sie Docker auf Ubuntu'.
  • Docker Compose auf Ihrem Server installiert, gemäß Schritt 1 von Wie man Docker Compose auf Ubuntu installiert und verwendet.

Schritt 1 — Die Demoanwendung erhalten

Um loszulegen, werden wir die Demo-Laravel-Anwendung aus ihrem Github-Repository abrufen. Wir interessieren uns für den tutorial-01-Branch, der die grundlegende Laravel-Anwendung enthält, die wir im Leitfaden "So installieren und konfigurieren Sie Laravel mit Nginx auf Ubuntu 22.04 (LEMP)" erstellt haben.

Um den Anwendungscode zu erhalten, der mit diesem Tutorial kompatibel ist, laden Sie die Version tutorial-1.0.1 in Ihr Home-Verzeichnis herunter mit:

cd ~

curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zip

Wir benötigen den Befehl unzip, um den Anwendungscode zu entpacken. Falls Sie dieses Paket bisher nicht installiert haben, tun Sie dies jetzt mit:

sudo apt update

sudo apt install unzip

Entpacken Sie nun den Inhalt der Anwendung und benennen Sie das entpackte Verzeichnis für einen einfacheren Zugriff um:

unzip travellist.zip

mv travellist-laravel-demo-tutorial-1.0.1 travellist-demo

Navigieren Sie zum Verzeichnis travellist-demo:

cd travellist-demo

Im nächsten Schritt erstellen wir eine .env-Konfigurationsdatei, um die Anwendung einzurichten.

Schritt 2 — Einrichten der .env-Datei der Anwendung

Die Laravel-Konfigurationsdateien befinden sich in einem Verzeichnis namens config im Stammverzeichnis der Anwendung. Zusätzlich wird eine .env-Datei verwendet, um umgebungsabhängige Konfigurationen festzulegen, wie Zugangsdaten und alle Informationen, die zwischen den Deployments variieren könnten. Diese Datei ist nicht in der Versionskontrolle enthalten.

Warnung: Die Konfigurationsdatei der Umgebung enthält sensible Informationen über Ihren Server, einschließlich Datenbankzugangsdaten und Sicherheitsschlüsseln. Aus diesem Grund sollten Sie diese Datei niemals öffentlich teilen.

Die in der .env-Datei enthaltenen Werte haben Vorrang vor den Werten, die in den regulären Konfigurationsdateien im config-Verzeichnis festgelegt sind. Jede Installation in einer neuen Umgebung erfordert eine maßgeschneiderte Umgebungsdatei, um Dinge wie Datenbankverbindungseinstellungen, Debug-Optionen, die Anwendungs-URL und andere Punkte zu definieren, die je nach Umgebung, in der die Anwendung läuft, variieren können.

Wir werden nun eine neue .env-Datei erstellen, um die Konfigurationsoptionen für die Entwicklungsumgebung, die wir einrichten, anzupassen. Laravel wird mit einer Beispiel-.env-Datei geliefert, die wir kopieren können, um unsere eigene zu erstellen:

cp .env.example .env

Öffnen Sie diese Datei mit nano oder dem Texteditor Ihrer Wahl:

nano .env

Die aktuelle .env-Datei der travellist-Demoanwendung enthält Einstellungen zur Verwendung einer lokalen MySQL-Datenbank, mit 127.0.0.1 als Datenbankhost. Wir müssen die Variable DB_HOST aktualisieren, damit sie auf den Datenbankdienst zeigt, den wir in unserer Docker-Umgebung erstellen werden. In diesem Leitfaden nennen wir unseren Datenbankdienst db. Ersetzen Sie den angegebenen Wert von DB_HOST durch den Namen des Datenbankdienstes:

APP_NAME=Travellist
APP_ENV=dev
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:8000

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=travellist
DB_USERNAME=travellist_user
DB_PASSWORD=password
...

Du kannst auch gerne den Datenbanknamen, Benutzernamen und das Passwort ändern, wenn du möchtest. Diese Variablen werden in einem späteren Schritt verwendet, wenn wir die docker-compose.yml-Datei einrichten, um unsere Dienste zu konfigurieren.

Speichere die Datei, wenn du mit dem Bearbeiten fertig bist. Wenn du nano verwendet hast, kannst du das tun, indem du Ctrl+x drückst, dann Y und Enter, um zu bestätigen.

Schritt 3 – Einrichten der Dockerfile der Anwendung

Obwohl sowohl unsere MySQL- als auch unsere Nginx-Dienste auf Standard-Images aus dem Docker Hub basieren werden, müssen wir trotzdem ein eigenes Image für den Anwendungs-Container erstellen. Dafür werden wir ein neues Dockerfile erstellen.

Unser Travellist-Image wird auf dem offiziellen PHP-Image php:7.4-fpm von Docker Hub basieren. Auf dieser grundlegenden PHP-FPM-Umgebung werden wir einige zusätzliche PHP-Module und das Abhängigkeitsverwaltungstool Composer installieren.

Wir werden auch einen neuen Systembenutzer erstellen; dies ist notwendig, um artisan und composer-Befehle während der Anwendungsentwicklung auszuführen. Die Einstellung uid stellt sicher, dass der Benutzer im Container dieselbe UID wie Ihr Systembenutzer auf Ihrem Host-Rechner, auf dem Docker läuft, hat. Auf diese Weise werden alle von diesen Befehlen erstellten Dateien mit den richtigen Berechtigungen auf dem Host repliziert. Das bedeutet auch, dass Sie Ihren bevorzugten Code-Editor auf dem Host-Rechner verwenden können, um die Anwendung zu entwickeln, die innerhalb der Container läuft.

Erstelle eine neue Dockerfile mit:

nano Dockerfile

Kopieren Sie die folgenden Inhalte in Ihre Dockerfile:

FROM php:7.4-fpm

# Arguments defined in docker-compose.yml
ARG user
ARG uid

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

# Get latest Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
    chown -R $user:$user /home/$user

# Set working directory
WORKDIR /var/www

USER $user

Vergiss nicht, die Datei zu speichern, wenn du fertig bist.

Unser Dockerfile beginnt damit, dass wir das Basis-Image definieren, das wir verwenden: php:7.4-fpm.

Nach der Installation von Systempaketen und PHP-Erweiterungen installieren wir Composer, indem wir die composer-Executable aus dem neuesten offiziellen Image in unser eigenes Anwendungs-Image kopieren.

Ein neuer Systembenutzer wird dann erstellt und eingerichtet, wobei die am Anfang des Dockerfiles deklarierten Argumente user und uid verwendet werden. Diese Werte werden während des Build-Vorgangs von Docker Compose eingefügt.

Schließlich setzen wir das Standardarbeitsverzeichnis auf /var/www und wechseln zu dem neu erstellten Benutzer. Dadurch wird sichergestellt, dass Sie sich als regulärer Benutzer anmelden und sich im richtigen Verzeichnis befinden, wenn Sie composer- und artisan-Befehle im Anwendungscontainer ausführen.

Schritt 4 — Einrichten der Nginx-Konfiguration und der Datenbank-Dump-Dateien

Beim Erstellen von Entwicklungsumgebungen mit Docker Compose ist es oft notwendig, Konfigurations- oder Initialisierungsdateien mit Service-Containern zu teilen, um diese Dienste einzurichten oder zu starten. Diese Vorgehensweise erleichtert es, Änderungen an Konfigurationsdateien vorzunehmen, um Ihre Umgebung während der Entwicklung der Anwendung zu optimieren.

Wir werden nun einen Ordner mit Dateien einrichten, die zur Konfiguration und Initialisierung unserer Service-Container verwendet werden.

Um Nginx einzurichten, teilen wir eine travellist.conf-Datei, die konfiguriert, wie die Anwendung bereitgestellt wird. Erstellen Sie den Ordner docker-compose/nginx mit:

mkdir -p docker-compose/nginx

Öffnen Sie eine neue Datei mit dem Namen travellist.conf in diesem Verzeichnis:

nano docker-compose/nginx/travellist.conf

Kopiere die folgende Nginx-Konfiguration in diese Datei:

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        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;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

Diese Datei konfiguriert Nginx so, dass es auf Port 80 hört und index.php als Standardindexseite verwendet. Sie setzt das Dokumentenverzeichnis auf /var/www/public und konfiguriert dann Nginx so, dass es den app-Dienst auf Port 9000 verwendet, um *.php-Dateien zu verarbeiten.

Speichern Sie die Datei und schließen Sie sie, wenn Sie mit der Bearbeitung fertig sind.

Um die MySQL-Datenbank einzurichten, werden wir einen Datenbank-Dump bereitstellen, der beim Starten des Containers importiert wird. Dies ist eine Funktion, die das MySQL 8.0-Image bietet, das wir in diesem Container verwenden werden.

Erstellen Sie einen neuen Ordner für Ihre MySQL-Initialisierungsdateien innerhalb des docker-compose-Ordners:

mkdir docker-compose/mysql

Öffnen Sie eine neue .sql-Datei:

nano docker-compose/mysql/init_db.sql

Der folgende MySQL-Dump basiert auf der Datenbank, die wir in unserem Laravel-auf-LEMP-Guide eingerichtet haben. Er wird eine neue Tabelle mit dem Namen places erstellen. Anschließend wird die Tabelle mit einer Reihe von Beispielorten gefüllt.

Fügen Sie den folgenden Code in die Datei ein:

DROP TABLE IF EXISTS `places`;

CREATE TABLE `places` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `visited` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `places` (name, visited) VALUES ('Berlin',0),('Budapest',0),('Cincinnati',1),('Denver',0),('Helsinki',0),('Lisbon',0),('Moscow',1),('Nairobi',0),('Oslo',1),('Rio',0),('Tokyo',0);

Die places-Tabelle enthält drei Felder: id, name und visited. Das visited-Feld ist ein Kennzeichen, das verwendet wird, um die Orte zu identifizieren, die noch besucht werden müssen. Du kannst die Beispielorte gerne ändern oder neue hinzufügen. Speichere die Datei und schließe sie, wenn du fertig bist.

Wir haben die Einrichtung der Dockerfile der Anwendung und der Konfigurationsdateien für die Dienste abgeschlossen. Als Nächstes richten wir Docker Compose ein, um diese Dateien beim Erstellen unserer Dienste zu verwenden.

Schritt 5 — Erstellen einer Multi-Container-Umgebung mit Docker Compose

Docker Compose ermöglicht es Ihnen, Multi-Container-Umgebungen für Anwendungen, die auf Docker laufen, zu erstellen. Es verwendet Servicedefinitionen, um vollständig anpassbare Umgebungen mit mehreren Containern zu erstellen, die Netzwerke und Datenträger gemeinsam nutzen können. Dies ermöglicht eine nahtlose Integration zwischen den Anwendungskomponenten.

Um unsere Dienstdefinitionen einzurichten, erstellen wir eine neue Datei mit dem Namen docker-compose.yml. Üblicherweise befindet sich diese Datei im Stammverzeichnis des Anwendungsordners und definiert Ihre containerisierte Umgebung, einschließlich der Basis-Images, die Sie zum Erstellen Ihrer Container verwenden werden, und wie Ihre Dienste miteinander interagieren.

Wir werden drei verschiedene Dienste in unserer docker-compose.yml-Datei definieren: app, db und nginx.

Der app-Dienst erstellt ein Image namens travellist, basierend auf dem Dockerfile, das wir zuvor erstellt haben. Der von diesem Dienst definierte Container wird einen php-fpm-Server ausführen, um PHP-Code zu verarbeiten und die Ergebnisse an den nginx-Dienst zurückzusenden, der in einem separaten Container läuft. Der mysql-Dienst definiert einen Container, der einen MySQL-8.0-Server ausführt. Unsere Dienste werden ein Bridge-Netzwerk mit dem Namen travellist teilen.

Die Anwendungsdateien werden auf beiden Diensten app und nginx über Bind-Mounts synchronisiert. Bind-Mounts sind in Entwicklungsumgebungen nützlich, da sie eine leistungsstarke bidirektionale Synchronisation zwischen Host-Rechner und Containern ermöglichen.

Erstellen Sie eine neue docker-compose.yml-Datei im Stammverzeichnis des Anwendungsordners:

nano docker-compose.yml

Eine typische docker-compose.yml-Datei beginnt mit einer Versionsdefinition, gefolgt von einem services-Knoten, unter dem alle Dienste definiert werden. Gemeinsame Netzwerke werden normalerweise am Ende dieser Datei definiert.

Um zu beginnen, kopieren Sie diesen Boilerplate-Code in Ihre docker-compose.yml-Datei:

version: "3.9"
services:


networks:
  travellist:
    driver: bridge

Wir werden nun den services-Knoten bearbeiten, um die app-, db- und nginx-Dienste einzuschließen.

Der app-Dienst

Der app-Dienst richtet einen Container mit dem Namen travellist-app ein. Er erstellt ein neues Docker-Image basierend auf einer Dockerfile, die sich im gleichen Verzeichnis wie die docker-compose.yml-Datei befindet. Das neue Image wird lokal unter dem Namen travellist gespeichert.

Obwohl sich das als Anwendung bereitgestellte Dokumenten-Root im nginx-Container befindet, benötigen wir die Anwendungsdateien auch irgendwo im app-Container, damit wir Befehlszeilenaufgaben mit dem Laravel Artisan-Tool ausführen können.

Kopieren Sie die folgende Service-Definition unter Ihrem services-Knoten in der docker-compose.yml-Datei:

  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

Diese Einstellungen bewirken Folgendes:

  • build: Diese Konfiguration weist Docker Compose an, ein lokales Image für den app-Dienst zu erstellen, wobei der angegebene Pfad (Context) und das Dockerfile als Anweisungen verwendet werden. Die Argumente user und uid werden in das Dockerfile eingefügt, um die Benutzererstellungsbefehle zur Build-Zeit anzupassen.
  • image: Der Name, der für das zu erstellende Image verwendet wird.
  • container_name: Legt den Containernamen für diesen Dienst fest.
  • restart: Immer neu starten, außer der Dienst ist gestoppt.
  • working_dir: Legt das Standardverzeichnis für diesen Dienst auf /var/www fest.
  • volumes: Erstellt ein geteiltes Volume, das Inhalte vom aktuellen Verzeichnis mit /var/www im Container synchronisiert. Beachten Sie, dass dies nicht Ihr Dokumentenstamm ist, da dieser im nginx-Container gespeichert wird.
  • networks: Richtet diesen Dienst so ein, dass er ein Netzwerk mit dem Namen travellist verwendet.

Der db-Dienst

Der db-Dienst verwendet ein vorgefertigtes MySQL 8.0-Image von Docker Hub. Da Docker Compose automatisch .env-Variablendateien lädt, die sich im selben Verzeichnis wie die docker-compose.yml-Datei befinden, können wir unsere Datenbankeinstellungen aus der Laravel-.env-Datei beziehen, die wir in einem vorherigen Schritt erstellt haben.

Fügen Sie die folgende Service-Definition in Ihren services-Knoten direkt nach dem app-Service ein:

  db:
    image: mysql:8.0
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

Diese Einstellungen bewirken Folgendes:

  • image: Definiert das Docker-Image, das für diesen Container verwendet werden soll. In diesem Fall verwenden wir ein MySQL-8.0-Image von Docker Hub.
  • container_name: Legt den Containernamen für diesen Dienst fest: travellist-db.
  • restart: Starten Sie diesen Dienst immer neu, es sei denn, er wurde ausdrücklich gestoppt.
  • environment: Definiert Umgebungsvariablen im neuen Container. Wir verwenden Werte, die aus der Laravel .env-Datei stammen, um unseren MySQL-Dienst einzurichten, der automatisch eine neue Datenbank und einen neuen Benutzer basierend auf den bereitgestellten Umgebungsvariablen erstellt.
  • volumes: Erstellt ein Volume, um ein .sql-Datenbank-Dump zu teilen, das zur Initialisierung der Anwendungsdatenbank verwendet wird. Das MySQL-Image importiert automatisch .sql-Dateien, die im Verzeichnis /docker-entrypoint-initdb.d im Container abgelegt werden.
  • networks: Richtet diesen Dienst so ein, dass er ein Netzwerk mit dem Namen travellist verwendet.

Der nginx-Dienst

Der nginx-Dienst verwendet ein vorgefertigtes Nginx-Image auf Basis von Alpine, einer leichtgewichtigen Linux-Distribution. Er erstellt einen Container mit dem Namen travellist-nginx und verwendet die ports-Definition, um eine Weiterleitung von Port 8000 auf dem Host-System zu Port 80 im Container einzurichten.

Fügen Sie die folgende Service-Definition in Ihren services-Knoten direkt nach dem db-Service ein:

  nginx:
    image: nginx:1.17-alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d
    networks:
      - travellist

Diese Einstellungen bewirken Folgendes:

  • image: Definiert das Docker-Image, das für diesen Container verwendet werden soll. In diesem Fall verwenden wir das Alpine Nginx 1.17-Image.
  • container_name: Legt den Container-Namen für diesen Dienst fest: travellist-nginx.
  • restart: Starten Sie diesen Dienst immer neu, es sei denn, er wurde ausdrücklich gestoppt.
  • ports: Richtet eine Portweiterleitung ein, die den externen Zugriff über Port 8000 auf den Webserver ermöglicht, der im Container auf Port 80 läuft.
  • volumes: Erstellt zwei geteilte Volumes. Das erste synchronisiert Inhalte vom aktuellen Verzeichnis nach /var/www innerhalb des Containers. Auf diese Weise werden Änderungen an den Anwendungsdateien lokal schnell in der von Nginx im Container bereitgestellten Anwendung angezeigt. Das zweite Volume stellt sicher, dass unsere Nginx-Konfigurationsdatei, die sich unter docker-compose/nginx/travellist.conf befindet, in den Nginx-Konfigurationsordner des Containers kopiert wird.
  • networks: Richtet diesen Dienst so ein, dass er ein Netzwerk namens travellist verwendet.

Fertige docker-compose.yml-Datei

So sieht unsere fertige docker-compose.yml-Datei aus:

version: "3.9"
services:
  app:
    build:
      args:
        user: sammy
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    image: travellist
    container_name: travellist-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./:/var/www
    networks:
      - travellist

  db:
    image: mysql:8.0
    container_name: travellist-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - ./docker-compose/mysql:/docker-entrypoint-initdb.d
    networks:
      - travellist

  nginx:
    image: nginx:1.17-alpine
    container_name: travellist-nginx
    restart: unless-stopped
    ports:
      - 8000:80
    volumes:
      - ./:/var/www
      - ./docker-compose/nginx:/etc/nginx/conf.d/
    networks:
      - travellist

networks:
  travellist:
    driver: bridge

Stelle sicher, dass du die Datei speicherst, wenn du fertig bist.

Schritt 6 — Die Anwendung mit Docker Compose ausführen

Wir werden nun docker compose-Befehle verwenden, um das Anwendungsimage zu erstellen und die Dienste auszuführen, die wir in unserer Einrichtung angegeben haben.

Erstellen Sie das app-Image mit folgendem Befehl:

docker compose build app

Dieser Befehl kann einige Minuten dauern. Sie werden eine Ausgabe ähnlich der folgenden sehen:

Building app
Sending build context to Docker daemon  377.3kB
Step 1/11 : FROM php:7.4-fpm
 ---> 8c08d993542f
Step 2/11 : ARG user
 ---> e3ce3af04d87
Step 3/11 : ARG uid
 ---> 30cb921ef7df
Step 4/11 : RUN apt-get update && apt-get install -y     git     curl     libpng-dev     libonig-dev     libxml2-dev     zip     unzip
. . .
 ---> b6dbc7a02e95
Step 5/11 : RUN apt-get clean && rm -rf /var/lib/apt/lists/*
 ---> 10ef9dde45ad
. . .
Step 6/11 : RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd
. . .
 ---> 920e4f09ec75
Step 7/11 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
 ---> dbbcd44e44af
Step 8/11 : RUN useradd -G www-data,root -u $uid -d /home/$user $user
 ---> db98e899a69a
Step 9/11 : RUN mkdir -p /home/$user/.composer &&     chown -R $user:$user /home/$user
 ---> 5119e26ebfea
Step 10/11 : WORKDIR /var/www
 ---> 699c491611c0
Step 11/11 : USER $user
 ---> cf250fe8f1af
Successfully built cf250fe8f1af
Successfully tagged travellist:latest

Wenn der Build abgeschlossen ist, können Sie die Umgebung im Hintergrundmodus ausführen mit:

docker compose up -d
Creating travellist-db    ... done
Creating travellist-app   ... done
Creating travellist-nginx ... done

Dies wird Ihre Container im Hintergrund ausführen. Um Informationen über den Status Ihrer aktiven Dienste anzuzeigen, führen Sie aus:

docker compose ps

Sie werden eine Ausgabe wie diese sehen:

      Name                    Command              State                  Ports                
-----------------------------------------------------------------------------------------------
travellist-app     docker-php-entrypoint php-fpm   Up      9000/tcp                            
travellist-db      docker-entrypoint.sh mysqld     Up      3306/tcp, 33060/tcp                 
travellist-nginx   nginx -g daemon off;            Up      0.0.0.0:8000->80/tcp,:::8000->80/tcp

Ihre Umgebung ist jetzt einsatzbereit.

Sie können nun überprüfen, ob jeder containerisierte Dienst korrekt reagiert, indem Sie einfache curl-Befehle verwenden.

  1. Um den Nginx-Webdienst zu testen:

    curl -I http://localhost:8000

    Erwartete Ausgabe:

    HTTP/1.1 200 OK
    Server: nginx/1.17.10
    Content-Type: text/html; charset=UTF-8

    Dies bestätigt, dass der Nginx-Container läuft und Antworten von Ihrer Laravel-Anwendung liefert.

  2. Um PHP-FPM über Nginx zu testen:

    curl -s http://localhost:8000 | grep "Berlin"

    Wenn die Anwendung korrekt läuft, gibt dieser Befehl die HTML der Willkommensseite zurück, die das Wort Berlin enthält.

Wir müssen noch ein paar Befehle ausführen, um die Einrichtung der Anwendung abzuschließen. Sie können den Befehl docker compose exec verwenden, um Befehle in den Service-Containern auszuführen, zum Beispiel ls -l, um detaillierte Informationen über die Dateien im Anwendungsverzeichnis anzuzeigen:

docker compose exec app ls -l
total 256
-rw-r--r-- 1 sammy sammy    737 Apr 18 14:21 Dockerfile
-rw-r--r-- 1 sammy sammy    101 Jan  7  2020 README.md
drwxr-xr-x 6 sammy sammy   4096 Jan  7  2020 app
-rwxr-xr-x 1 sammy sammy   1686 Jan  7  2020 artisan
drwxr-xr-x 3 sammy sammy   4096 Jan  7  2020 bootstrap
-rw-r--r-- 1 sammy sammy   1501 Jan  7  2020 composer.json
-rw-r--r-- 1 sammy sammy 179071 Jan  7  2020 composer.lock
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 config
drwxr-xr-x 5 sammy sammy   4096 Jan  7  2020 database
drwxr-xr-x 4 sammy sammy   4096 Apr 18 14:22 docker-compose
-rw-r--r-- 1 sammy sammy   1017 Apr 18 14:29 docker-compose.yml
-rw-r--r-- 1 sammy sammy   1013 Jan  7  2020 package.json
-rw-r--r-- 1 sammy sammy   1405 Jan  7  2020 phpunit.xml
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 public
-rw-r--r-- 1 sammy sammy    273 Jan  7  2020 readme.md
drwxr-xr-x 6 sammy sammy   4096 Jan  7  2020 resources
drwxr-xr-x 2 sammy sammy   4096 Jan  7  2020 routes
-rw-r--r-- 1 sammy sammy    563 Jan  7  2020 server.php
drwxr-xr-x 5 sammy sammy   4096 Jan  7  2020 storage
drwxr-xr-x 4 sammy sammy   4096 Jan  7  2020 tests
-rw-r--r-- 1 sammy sammy    538 Jan  7  2020 webpack.mix.js

Wir werden nun composer install ausführen, um die Anwendungsabhängigkeiten zu installieren:

docker compose exec app rm -rf vendor composer.lock

docker compose exec app composer install

Sie werden eine Ausgabe wie diese sehen:

No composer.lock file present. Updating dependencies to latest instead of installing from lock file. See https://getcomposer.org/install for more information.
. . .
Lock file operations: 89 installs, 0 updates, 0 removals
  - Locking doctrine/inflector (2.0.4)
  - Locking doctrine/instantiator (1.4.1)
  - Locking doctrine/lexer (1.2.3)
  - Locking dragonmantank/cron-expression (v2.3.1)
  - Locking egulias/email-validator (2.1.25)
  - Locking facade/flare-client-php (1.9.1)
  - Locking facade/ignition (1.18.1)
  - Locking facade/ignition-contracts (1.0.2)
  - Locking fideloper/proxy (4.4.1)
  - Locking filp/whoops (2.14.5)
. . .
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 89 installs, 0 updates, 0 removals
  - Downloading doctrine/inflector (2.0.4)
  - Downloading doctrine/lexer (1.2.3)
  - Downloading dragonmantank/cron-expression (v2.3.1)
  - Downloading symfony/polyfill-php80 (v1.25.0)
  - Downloading symfony/polyfill-php72 (v1.25.0)
  - Downloading symfony/polyfill-mbstring (v1.25.0)
  - Downloading symfony/var-dumper (v4.4.39)
  - Downloading symfony/deprecation-contracts (v2.5.1)
. . .
Generating optimized autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover --ansi
Discovered Package: facade/ignition
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Discovered Package: nesbot/carbon
Discovered Package: nunomaduro/collision
Package manifest generated successfully.

Das Letzte, was wir tun müssen, bevor wir die Anwendung testen, ist, einen eindeutigen Anwendungsschlüssel mit dem artisan Laravel Kommandozeilen-Tool zu erstellen. Dieser Schlüssel wird verwendet, um Benutzersitzungen und andere sensible Daten zu verschlüsseln:

docker compose exec app php artisan key:generate
Application key set successfully.

Gehe nun zu deinem Browser und rufe den Domainnamen oder die IP-Adresse deines Servers auf Port 8000 auf:

http://server_domain_or_IP:8000

Hinweis: Falls Sie diese Demo auf Ihrem lokalen Rechner ausführen, verwenden Sie http://localhost:8000, um die Anwendung über Ihren Browser zu erreichen.

Du wirst eine Seite wie diese sehen:

Sie können die Laravel-Anwendung auch von innerhalb des App-Containers testen, ohne einen Browser zu verwenden:

docker compose exec app curl http://nginx

Wenn alles korrekt konfiguriert ist, sollten Sie die HTML-Ausgabe der Standard-Willkommensseite von Laravel sehen.

<html>
<head>
    <title>Travel List</title>
</head>

<body>
    <h1>My Travel Bucket List</h1>
    <h2>Places I'd Like to Visit</h2>
    <ul>
        <li>Berlin</li>
        <li>Budapest</li>
        <li>Denver</li>
        <li>Helsinki</li>
        <li>Lisbon</li>
        <li>Nairobi</li>
        <li>Rio</li>
        <li>Tokyo</li>
    </ul>

    <h2>Places I've Already Been To</h2>
    <ul>
        <li>Cincinnati</li>
        <li>Moscow</li>
        <li>Oslo</li>
    </ul>
</body>
</html>

Sie können den Befehl logs verwenden, um die von Ihren Diensten erzeugten Protokolle zu überprüfen:

docker compose logs nginx
Attaching to travellist-nginx
. . .
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:49:16 +0000] "GET / HTTP/1.1" 200 627 "-" "curl/7.82.0"
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:51:27 +0000] "GET / HTTP/1.1" 200 627 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"
travellist-nginx | 172.24.9.1 - - [18/Apr/2022:14:51:27 +0000] "GET /favicon.ico HTTP/1.1" 200 0 "http://localhost:8000/" "Mozilla/5.0 (X11; Linux x86_64; rv:89.0) Gecko/20100101 Firefox/89.0"

Sie können auch den folgenden curl-Befehl verwenden, um Logeinträge auszulösen:

curl http://localhost:8000

Dann erneut ausführen:

docker compose logs nginx

Sie sollten jetzt einen entsprechenden Zugriffseintrag für Ihre Anfrage sehen.

Wenn Sie Ihre Docker-Compose-Umgebung pausieren möchten, während Sie den Zustand aller Dienste beibehalten, führen Sie Folgendes aus:

docker compose pause
Pausing travellist-db    ... done
Pausing travellist-nginx ... done
Pausing travellist-app   ... done

Sie können Ihre Dienste dann wieder aufnehmen mit:

docker compose unpause
Unpausing travellist-app   ... done
Unpausing travellist-nginx ... done
Unpausing travellist-db    ... done

Um Ihre Docker-Compose-Umgebung herunterzufahren und alle dazugehörigen Container, Netzwerke und Volumes zu entfernen, führen Sie aus:

docker compose down
Stopping travellist-nginx ... done
Stopping travellist-db    ... done
Stopping travellist-app   ... done
Removing travellist-nginx ... done
Removing travellist-db    ... done
Removing travellist-app   ... done
Removing network travellist-laravel-demo_travellist

Für eine Übersicht aller Docker Compose-Befehle besuchen Sie bitte die Docker Compose-Befehlsreferenz.

Laravel-Datei- und Verzeichnisberechtigungen beheben

Beim Ausführen von Laravel innerhalb von Docker-Containern ist eines der häufigsten Probleme, auf das Entwickler stoßen, mit Datei- und Verzeichnisberechtigungen verbunden. Laravel erfordert, dass bestimmte Verzeichnisse vom PHP-FPM-Prozess innerhalb des app-Containers beschreibbar sind. Wenn diese Verzeichnisse nicht beschreibbar sind, kann die Anwendung Fehler möglicherweise nicht protokollieren, Konfigurationsdateien nicht cachen oder Sitzungsdaten nicht korrekt speichern.

Sie könnten auf Nachrichten wie diese stoßen:

The stream or file "/var/www/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied

oder

file_put_contents(/var/www/bootstrap/cache/config.php): Failed to open stream: Permission denied

Diese Fehler weisen darauf hin, dass der Benutzer, der PHP innerhalb des Containers ausführt, keine Berechtigung hat, in die erforderlichen Verzeichnisse zu schreiben.

Die folgenden Schritte beschreiben, wie man diese Probleme sowohl in lokalen als auch in Produktionsumgebungen diagnostiziert und behebt.

Beschreibbare Verzeichnisberechtigungen anpassen

Laravel erfordert, dass zwei Verzeichnisse vom Webserver-Benutzer beschreibbar sind:

  • /var/www/storage
  • /var/www/bootstrap/cache

Wenn Sie Berechtigungsfehler sehen, können Sie diese beheben, indem Sie Eigentum und Berechtigungen innerhalb des Containers anpassen:

docker compose exec app chown -R sammy:sammy /var/www/storage /var/www/bootstrap/cache

docker compose exec app chmod -R 775 /var/www/storage /var/www/bootstrap/cache

Wenn Ihr Dockerfile einen anderen Benutzernamen verwendet (definiert durch das Build-Argument user), ersetzen Sie sammy durch diesen Benutzernamen.

Sie können die Eigentümerschaft mit folgendem Befehl überprüfen:

docker compose exec app ls -ld /var/www/storage /var/www/bootstrap/cache

Die Ausgabe sollte zeigen, dass der Benutzer und die Gruppe mit dem PHP-FPM-Prozess innerhalb des Containers übereinstimmen.

Wenn Sie stattdessen die Berechtigungen vom Host-Rechner aus anpassen möchten, führen Sie aus:

sudo chown -R $(id -u):$(id -g) storage bootstrap/cache

sudo chmod -R 775 storage bootstrap/cache

Dies stellt sicher, dass beide Laravel-Verzeichnisse sowohl vom Container als auch von Ihrem Host-Benutzer beschrieben werden können.

Verzeichnisbesitz während des Image-Builds festlegen

Um zu verhindern, dass Berechtigungsprobleme nach jedem Neuaufbau erneut auftreten, können Sie während des Docker-Image-Erstellungsprozesses die Besitzrechte des Verzeichnisses festlegen. Öffnen Sie Ihre Dockerfile und fügen Sie die folgende Zeile am Ende hinzu, bevor Sie zum Benutzer ohne Root-Rechte wechseln:

RUN chown -R $user:www-data /var/www

Dieser Befehl stellt sicher, dass alle Dateien und Verzeichnisse unter /var/www dem Benutzer gehören, den Sie in Ihrem Dockerfile definiert haben.

Für einen gezielteren und sichereren Ansatz können Sie nur die Verzeichnisse anpassen, in die Laravel schreiben muss:

RUN chown -R $user:www-data /var/www/storage /var/www/bootstrap/cache

Nachdem Sie diese Änderung vorgenommen haben, erstellen Sie das Image neu und starten die Container neu:

docker compose build app

docker compose up -d

Beim nächsten Start des Containers wird es bereits die richtigen Berechtigungen haben.

Überprüfen Sie die Konsistenz der Benutzer-ID zwischen Host und Container

Bei der Verwendung von Bind-Mounts teilt Docker Dateien zwischen Ihrem Host und dem Container. Wenn die numerische UID des Host-Benutzers von der im Container definierten UID abweicht, kann es zu Konflikten bei der Dateibesitzerschaft kommen. Dies ist häufig auf Systemen der Fall, bei denen die Benutzer-IDs zwischen den Maschinen variieren.

Um die UID-Werte zu überprüfen, führen Sie aus:

id -u $(whoami)

docker compose exec app id -u

Wenn diese beiden Zahlen nicht übereinstimmen, aktualisieren Sie Ihre docker-compose.yml-Datei, um Ihre aktuelle UID an den Build-Prozess zu übergeben:

build:
  args:
    user: sammy
    uid: 1000

Dann das Image neu erstellen:

docker compose build app

docker compose up -d

Dies stellt sicher, dass der Containerbenutzer mit Ihrem Host-Benutzer übereinstimmt, wodurch Berechtigungsfehler bei bind-gemounteten Dateien vermieden werden.

Berechtigungen des Composer-Cache-Verzeichnisses reparieren

Composer speichert heruntergeladene Abhängigkeiten und Cache-Dateien im Home-Verzeichnis des Benutzers innerhalb des Containers (/home/sammy/.composer). Wenn dieses Verzeichnis nicht beschreibbar ist, können Sie Warnungen wie die folgenden sehen:

Cannot create cache directory /home/sammy/.composer/cache

Sie können dies beheben, indem Sie das Cache-Verzeichnis erstellen und die korrekten Besitzrechte zuweisen:

docker compose exec app mkdir -p /home/sammy/.composer/cache

docker compose exec app chown -R sammy:sammy /home/sammy/.composer

Dies stellt sicher, dass Composer Abhängigkeiten und Cache-Dateien ohne Berechtigungsfehler speichern kann, was auch zukünftige composer install-Durchläufe beschleunigt.

Empfohlene Vorgehensweisen für Produktionsumgebungen

In der Produktion sollte die Berechtigungsverwaltung während des Image-Builds und nicht zur Laufzeit erfolgen. Dies verbessert die Konsistenz und Sicherheit.

Befolgen Sie diese Praktiken für eine produktionsbereite Konfiguration:

  • Vermeiden Sie Bind-Mounts für den Anwendungscode: Stattdessen sollten die Anwendungsdateien während des Builds in das Image kopiert werden. Bind-Mounts sollten nur für die Entwicklung verwendet werden.

  • Berechtigungen während des Builds festlegen: Fügen Sie die RUN chown- und chmod-Befehle in Ihr Dockerfile ein, um vor der Bereitstellung die korrekten Eigentumsrechte und Zugriffsrechte sicherzustellen.

  • Verwenden Sie den Benutzer mit den geringsten Rechten: Stellen Sie sicher, dass der PHP-Prozess unter einem Nicht-Root-Benutzer läuft (die aktuelle Einrichtung mit $user macht dies bereits).

  • Berechtigungen für nicht beschreibbare Verzeichnisse einschränken: Die meisten Anwendungsdateien sollten schreibgeschützt sein. Sie können dies erzwingen mit:

    chmod -R 755 /var/www
    
    chmod -R 775 /var/www/storage /var/www/bootstrap/cache

    Dieses Setup ermöglicht es Laravel, nur dort zu schreiben, wo es nötig ist, und schützt gleichzeitig andere Anwendungsdateien vor Änderungen.

Wenn Sie diese Schritte befolgen, werden Ihre Laravel-Container in sowohl lokalen als auch Produktionsumgebungen stabile, vorhersehbare Dateiberechtigungen haben. Dies reduziert wiederkehrende Fehler und stellt sicher, dass Laravel Daten protokollieren, Dateien cachen und Sitzungen zuverlässig schreiben kann.

Verstehen von docker-compose.override.yml für lokale und Produktionsumgebungen

Docker Compose ermöglicht es Ihnen, Multi-Container-Anwendungen mit einer einzigen Konfigurationsdatei zu definieren. In den meisten Fällen wird diese Konfiguration in einer Datei namens docker-compose.yml gespeichert. Die Verwaltung mehrerer Umgebungen, wie z. B. der lokalen Entwicklung und der Produktion, erfordert jedoch oft umgebungsspezifische Anpassungen. Hierbei wird die Datei docker-compose.override.yml nützlich.

Standardmäßig sucht Docker Compose automatisch nach einer Datei mit dem Namen docker-compose.override.yml im selben Verzeichnis wie die Basis-Compose-Datei. Wenn sie gefunden wird, werden die Konfigurationen beider Dateien zusammengeführt, wenn Sie einen beliebigen docker compose-Befehl ausführen. Dieser Ansatz ermöglicht es Entwicklern, eine saubere, wiederverwendbare Basis-Konfiguration zu pflegen und gleichzeitig bestimmte Aspekte der Einrichtung für lokale oder Produktionsumgebungen anzupassen.

Die Rolle von docker-compose.override.yml

Die docker-compose.override.yml-Datei ist eine Erweiterung der Basis-Compose-Konfiguration. Sie ändert oder fügt Eigenschaften hinzu, die nur für eine bestimmte Umgebung gelten, ohne dass Änderungen an der primären docker-compose.yml-Datei erforderlich sind. Die Override-Datei ist besonders nützlich in lokalen Entwicklungs-Workflows, in denen Entwickler oft Quellcode einbinden, Debugging-Tools aktivieren oder Umgebungsvariablen für Tests ändern müssen.

Wenn Sie den Befehl docker compose up ausführen, kombiniert Docker automatisch die Inhalte von docker-compose.yml und docker-compose.override.yml. Wenn ein Dienst, Volume oder eine Konfiguration in beiden Dateien definiert ist, haben die Werte in der Override-Datei Vorrang.

Um eine andere Override-Datei zu verwenden, zum Beispiel eine für die Produktion, können Sie die Datei manuell mit dem -f-Flag angeben:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Dieser Ansatz ermöglicht es Ihnen, separate Konfigurationen für verschiedene Umgebungen beizubehalten, während Sie eine einheitliche Befehlsstruktur verwenden.

Strukturierung für lokale Entwicklung

Lokale Entwicklungsumgebungen erfordern in der Regel häufige Änderungen, Live-Code-Neuladungen und ausführliche Protokollierung. Die Datei docker-compose.override.yml ermöglicht es, diese Funktionen einzuführen, ohne andere Umgebungen zu beeinträchtigen.

Eine typische lokale Überschreibungsdatei kann Folgendes enthalten:

  • Bind-Mounts für Live-Code-Updates
  • Lokale Build-Anweisungen anstelle der Verwendung vorgefertigter Images
  • Entwicklungsspezifische Umgebungsvariablen
  • Debugging- und Testdienste

Zum Beispiel:

version: "3.9"

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile.dev
    volumes:
      - ./src:/app
      - ./logs:/app/logs
    environment:
      - DATABASE_URL=postgres://postgres:postgres@db:5432/app_db
      - LOG_LEVEL=debug
    ports:
      - "8000:8000"
    depends_on:
      - db

  db:
    environment:
      - POSTGRES_PASSWORD=postgres

In diesem Beispiel:

  • Der app-Dienst wird direkt aus einer lokalen Dockerfile (Dockerfile.dev) erstellt, was es einfach macht, Änderungen zu testen, ohne Images über ein Registry neu bauen zu müssen.
  • Der Quellcode wird mithilfe von Bind-Mounts eingebunden, wodurch der Container lokale Dateiänderungen in Echtzeit widerspiegelt.
  • Umgebungsvariablen wie LOG_LEVEL=debug werden gesetzt, um während der Entwicklung detailliertere Ausgaben zu erhalten.
  • Die Datenbank verwendet einfache Zugangsdaten, die für lokale Tests geeignet sind, aber nicht in der Produktion verwendet werden sollten.

Diese Konfiguration bietet eine schnellere und flexiblere Entwicklungsumgebung, während die Basiskonfiguration unverändert bleibt.

Vorbereitung auf die Produktion

Produktionsumgebungen erfordern Stabilität, Reproduzierbarkeit und Sicherheit. Container sollten aus vorgefertigten Images ausgeführt werden, anstatt lokal erstellt zu werden, und sensible Informationen sollten sicher verwaltet werden. Im Gegensatz zu der automatisch angewendeten Override-Datei, die für die lokale Entwicklung verwendet wird, müssen Produktions-Overrides ausdrücklich angegeben werden.

Eine Produktionsüberschreibungsdatei (docker-compose.prod.yml) ist in der Regel dafür vorgesehen, lokale Build-Schritte durch Image-Referenzen zu ersetzen, benannte Volumes anstelle von Bind-Mounts zu verwenden und bereitstellungsbezogene Eigenschaften wie Ressourcengrenzen und Skalierung zu definieren.

Beispiel:

version: "3.9"

services:
  app:
    image: registry.example.com/myapp:1.2.3
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - LOG_LEVEL=info
    volumes:
      - app-data:/var/lib/app
    ports:
      - "8000:8000"
    restart: unless-stopped
    deploy:
      replicas: 3
      resources:
        limits:
          cpus: "0.5"
          memory: 512M

  db:
    image: postgres:15
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password

secrets:
  db_password:
    external: true

volumes:
  app-data:
  db-data:

In dieser Konfiguration:

  • Der app-Dienst verweist auf ein vorgefertigtes Image (myapp:1.2.3), das in einem Registry gespeichert ist, anstatt es lokal zu erstellen.

  • Bind-Mounts werden durch benannte Volumes ersetzt, um eine konsistente und isolierte Datenspeicherung zu gewährleisten.

  • Sensible Zugangsdaten werden sicher mithilfe von Docker-Geheimnissen gespeichert.

  • Der deploy-Abschnitt definiert das Skalierungsverhalten und die Ressourcengrenzen für eine bessere Stabilität unter Last.

  • Die Konfiguration muss während der Bereitstellung ausdrücklich angewendet werden:

    docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Diese Struktur stellt sicher, dass Produktionsbereitstellungen konsistent, kontrolliert und frei von lokalen Entwicklungsartefakten sind.

Vergleich von lokalen und produktiven Überschreibungen

KonfigurationsaspektLokal (docker-compose.override.yml)Produktion (docker-compose.prod.yml)
Automatisch angewendetJaNein (muss -f verwenden)
QuellcodeverwaltungBind-Mounts für Live-UpdatesVorgefertigte Images aus dem Register
UmgebungsvariablenEntwicklerorientierte WerteProduktionssichere Werte
ProtokollierungAusführlich (debug)Kompakt (info oder error)
GeheimnisverwaltungEinfache Werte oder .envDocker-Geheimnisse oder verwaltete Geheimnisse
SkalierungEinzelcontainerMehrere Replikate
LeistungsoptimierungNicht erforderlichRessourcenlimits definiert
Debugging-ToolsAktiviertDeaktiviert
Neustart-RichtlinieManuell oder keine
restart: unless-stopped

Die Verwendung mehrerer Docker-Compose-Dateien bietet eine saubere Möglichkeit, um umgebungsspezifische Konfigurationen von der Basisinstallation zu trennen. Die Basis-docker-compose.yml definiert die gemeinsame Anwendungsstruktur, während docker-compose.override.yml diese für die lokale Entwicklung anpasst. Beim Einsatz in der Produktion sorgt eine explizite Override-Datei für eine vorhersehbare und sichere Konfiguration.

Dieses Muster ermöglicht es Teams, einen konsistenten Arbeitsablauf beizubehalten. Entwickler können die Anwendung lokal starten mit:

docker compose up

und in die Produktion bereitstellen mit:

docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Beide Umgebungen teilen die gleiche Kernkonfiguration, während sie unabhängige Einstellungen beibehalten, die für ihre spezifischen Anforderungen geeignet sind. Diese Trennung verbessert die Wartbarkeit, reduziert Konfigurationsfehler und hält lokale und Produktionsumgebungen im Einklang.

Fehlerbehebung in Ihrer Umgebung

Dieser Abschnitt bietet Schritte zur Fehlerbehebung für häufige Probleme, die beim Ausführen Ihrer App mit Docker Compose auftreten können.

502 Ungültiges Gateway (Nginx zeigt 502-Antworten an)

Symptome:

  • Der Browser zeigt 502 Bad Gateway an, wenn http://localhost:8000 aufgerufen wird.
  • Die Nginx-Protokolle enthalten Zeilen wie recv() failed (111: Connection refused) oder connect() failed (111: Connection refused) while connecting to upstream.

Wahrscheinliche Ursachen:

  • PHP-FPM (der app-Dienst) läuft nicht oder hört nicht auf dem erwarteten Socket/Port.
  • Nginx fastcgi_pass zeigt auf den falschen Host/Port.
  • Container-Name stimmt nicht überein oder Netzwerkprobleme zwischen nginx und app.
  • app ist beim Start abgestürzt (z. B. aufgrund fehlender Erweiterungen oder Konfigurationsfehler).

Wie man bestätigt:

  1. Nginx-Protokolle überprüfen:

    docker compose logs nginx | tail -n 100

    Suche nach 502-Meldungen oder Fehlern bei der Verbindung zum Upstream.

  2. Überprüfen Sie den Status und die Protokolle des app-Containers:

    docker compose ps app
    
    docker compose logs app --tail=200
  3. Bestätigen Sie, dass PHP-FPM im app-Container lauscht:

    docker compose exec app ss -ltnp | grep :9000 || docker compose exec app ss -lxp | grep php-fpm

    Erwartet: entweder ein TCP-Listener auf Port 9000 oder ein von nginx verwendeter Unix-Socket (bei diesem Compose weniger üblich).

  4. Überprüfen Sie, ob nginx app im Compose-Netzwerk auflösen und erreichen kann:

    docker compose exec nginx ping -c 2 app
    
    docker compose exec nginx wget -qO- --timeout=2 http://app:9000/ || true

    Wenn ping fehlschlägt oder wget zeitüberschreitet, besteht ein Netzwerkproblem oder eine Diskrepanz beim Servicenamen.

Häufige Fehlerbehebungen:

  • Wenn PHP-FPM nicht läuft, überprüfen Sie die app-Protokolle auf schwerwiegende PHP-Fehler oder Startprobleme. Beheben Sie den zugrunde liegenden Fehler (oft eine fehlende Erweiterung oder ein Berechtigungsproblem) und starten Sie neu:

    docker compose restart app
    
    docker compose logs app --tail=100
  • Wenn die Nginx-Konfiguration einen anderen Upstream verwendet (z. B. fastcgi_pass 127.0.0.1:9000;), aktualisieren Sie docker-compose/nginx/travellist.conf auf fastcgi_pass app:9000; (entsprechend dem Namen Ihres Compose-Dienstes) und laden Sie Nginx neu:

    # After editing config in host
    
    docker compose exec nginx nginx -s reload
    
    docker compose logs nginx --tail=50
  • Wenn PHP-FPM auf einem Unix-Socket hört (z. B. /run/php/php7.4-fpm.sock), Nginx jedoch TCP erwartet oder umgekehrt, standardisieren Sie auf TCP app:9000 für Compose-Setups oder mounten Sie den Socket korrekt zwischen den Containern (empfohlen: TCP in Multi-Container-Compose verwenden).

Fehlende PHP-Erweiterungen (Fehler beim Ausführen von Composer oder Artisan oder Laufzeitfehler)

Symptome:

  • Composer- oder Laravel-Befehle schlagen fehl mit Meldungen wie Class "PDO" not found, ext-pdo is missing oder Call to undefined function json_encode.
  • PHP-Fehler in den app-Logs, die auf nicht definierte Klassen oder Funktionen verweisen.

Wie man bestätigt:

  1. Überprüfen Sie die Liste der PHP-Module im app-Container:

    docker compose exec app php -m | sort

    Suche nach den Erweiterungsnamen, die du benötigst (für typische Laravel-Erweiterungen: pdo_mysql, mbstring, bcmath, openssl, tokenizer, xml, ctype, json, gd).

  2. Überprüfen Sie die Composer-Fehlermeldung, die normalerweise die erforderlichen Erweiterungen nennt:

    docker compose exec app composer install

Fehlerbehebungen:

  • Fügen Sie fehlende Erweiterungen in Ihrem Dockerfile hinzu und bauen Sie das Image neu. Beispiel (für pdo_mysql und gd):

    RUN apt-get update && apt-get install -y libpng-dev libxml2-dev \
        && docker-php-ext-install pdo_mysql mbstring xml gd

    Dann neu bauen und neu starten:

    docker compose build app
    
    docker compose up -d app
    
    docker compose logs app --tail=100
  • Wenn eine Erweiterung Systembibliotheken benötigt (zum Beispiel benötigt gd libpng-dev), fügen Sie die entsprechenden apt-get install-Zeilen vor docker-php-ext-install hinzu.

  • Wenn Sie eine PHP-Erweiterung benötigen, die nicht eingebaut ist, ziehen Sie in Betracht, sie über PECL zu installieren und in php.ini zu aktivieren.

Datenbankverbindungsfehler (Laravel kann keine Verbindung zu MySQL herstellen)

Symptome:

  • Laravel zeigt Datenbankverbindungsfehler (SQLSTATE[HY000] [2002] Verbindung verweigert oder Zugriff für Benutzer verweigert).
  • Migrationen schlagen fehl, oder die Anwendung zeigt eine leere Seite für datengesteuerte Seiten an.

Wie man bestätigt:

  1. Prüfen Sie die DB-Container-Protokolle auf Initialisierungsfehler:

    docker compose logs db --tail=200
  2. Überprüfen, ob der DB-Container läuft:

    docker compose ps db
  3. Überprüfen Sie, ob die Anmeldedaten und der Host zwischen .env und docker-compose.yml übereinstimmen. In der Laravel-.env sollte DB_HOST der Name des Compose-Dienstes (db) und nicht 127.0.0.1 oder localhost sein:

    DB_HOST=db
    DB_PORT=3306
    DB_DATABASE=travellist
    DB_USERNAME=travellist_user
    DB_PASSWORD=password
  4. Test aus dem db-Container:

    docker compose exec db mysql -u"${DB_USERNAME}" -p"${DB_PASSWORD}" -e "SHOW DATABASES;"

    Oder testen Sie vom app-Container aus, um die Netzwerkverbindung sicherzustellen:

    docker compose exec app sh -c 'which mysql >/dev/null 2>&1 || echo "no mysql client"; mysql -h db -u"${DB_USERNAME}" -p"${DB_PASSWORD}" -e "SELECT VERSION();"'

Häufige Ursachen und Lösungen:

  • Falscher Host in .env: aktualisiere DB_HOST=db, erstelle die Container neu oder lade die Umgebungen neu:

    docker compose down
    
    docker compose up -d
    
    docker compose exec app php artisan migrate
  • Datenbank beim Start der App nicht bereit: Verwenden Sie einen Wiederholungsmechanismus in Ihrer App oder fügen Sie einen Healthcheck hinzu und starten Sie die Bestellung. Sie können auch manuell warten:

    docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping
    
    # expected: mysqld is alive
  • Initialisierungs-SQL schlägt fehl: Überprüfen Sie die Dateien unter ./docker-compose/mysql, ein Syntax- oder Berechtigungsfehler kann die DB-Initialisierung abbrechen. Prüfen Sie die DB-Protokolle auf Importfehler.

  • Privilegien/falsche Anmeldeinformationen: Stellen Sie sicher, dass die Datenbank und der Benutzer existieren. Erstellen Sie die Datenbank neu oder führen Sie SQL aus, um die Berechtigungen zu vergeben.

  • Host-Ebene Portnutzung: Wenn Sie versucht haben, curl telnet://localhost:3306 auszuführen und der db-Dienst keinen Host-Port freigibt, wird diese Verbindung absichtlich abgelehnt. Testen Sie dies innerhalb von Containern oder geben Sie den Port in docker-compose.yml frei (ports: - "3306:3306") für lokales Debugging.

Berechtigungs- und Dateisystemprobleme (Speicher, Cache, Protokolldateien)

Symptome:

  • Laravel wirft Ausnahmen im Zusammenhang mit dem Schreiben von Cache oder Logs: Der Stream oder die Datei "/var/www/storage/logs/laravel.log" konnte nicht geöffnet werden.
  • Von Container-Prozessen erstellte Dateien gehören root und sind für Ihren Host-Benutzer nicht beschreibbar.

Wie man bestätigt:

  1. Überprüfen Sie die Dateiberechtigungen vom Host aus:

    ls -l
    
    ls -l storage bootstrap/cache
    
    docker compose exec app id -u   # user id inside container
  2. Überprüfen Sie die Containerprotokolle auf Berechtigungsfehler.

Behobene Fehler:

  • Stellen Sie sicher, dass das Dockerfile den Benutzer und die UID festlegt, die mit Ihrem Host-Benutzer übereinstimmen (Ihr Dockerfile erstellt bereits einen Benutzer mit ARG uid). Überprüfen Sie, dass die uid in den Build-Argumenten der docker-compose.yml mit Ihrer Host-UID übereinstimmt (normalerweise 1000).

  • Eigentum vom Host oder innerhalb des Containers korrigieren:

    docker compose exec app chown -R sammy:sammy /var/www/storage /var/www/bootstrap/cache

    oder vom Host:

    sudo chown -R $(id -u):$(id -g) storage bootstrap/cache
  • Für die Entwicklung können Bind-Mounts zu Berechtigungsproblemen führen; es ist besser, Container-Befehle auszuführen, um die Besitzrechte nach dem Start der Container anzupassen.

Komponisten- oder lieferantenbezogene Fehler

Symptome:

  • composer install schlägt innerhalb von app fehl oder ein fehlender vendor-Ordner verursacht, dass die Klasse nicht gefunden wird.
  • composer meldet Speicherauslastung im Container.

Wie man bestätigt:

  • Führen Sie Composer innerhalb des app-Containers aus und erfassen Sie die Ausgabe:

    docker compose exec app composer install --no-interaction --prefer-dist

Fehlerbehebungen:

  • Wenn die Speicherlimits von composer erreicht werden, erlauben Sie mehr Speicher oder setzen Sie die Composer-Umgebungsvariable:

    docker compose exec app php -d memory_limit=-1 /usr/bin/composer install
  • Stellen Sie sicher, dass die Composer-Datei vorhanden ist (Ihr Dockerfile kopiert Composer). Falls nicht, bauen Sie das Image mit der Composer-Schicht neu.

  • Wenn vendor aufgrund eines Bind-Mount-Overlays fehlt, stellen Sie sicher, dass Ihr Bind-Mount den in dem Image erstellten vendor-Ordner nicht verdeckt. Die beste Vorgehensweise ist, composer install auszuführen, nachdem die Container gestartet wurden, damit vendor im gemounteten Ordner erstellt wird, oder vendor für die Produktion über den Image-Build zum Container hinzuzufügen.

Die Anwendung gibt eine leere Seite oder 500-Fehler zurück

Symptome:

  • Der Browser zeigt eine leere Antwort oder HTTP 500 an.
  • Nginx-Protokolle zeigen 200, aber die Seite ist leer; app-Protokolle zeigen fatale Ausnahmen.

Wie man bestätigt:

  • Anwendungsprotokolle prüfen:

    docker compose exec app tail -n 200 storage/logs/laravel.log
    
    docker compose logs app --tail=200
  • Überprüfen Sie die Umgebung: APP_DEBUG=true in .env bewirkt, dass Laravel detaillierte Fehler in der Entwicklung anzeigt (nicht in der Produktion aktivieren).

Behobene Fehler:

  • Behebe die zugrunde liegende Ausnahme, die in laravel.log angezeigt wird.

  • Stellen Sie sicher, dass .env vorhanden ist und APP_KEY gesetzt ist:

    docker compose exec app php artisan key:generate
  • Migrationen ausführen, Cache leeren:

    docker compose exec app php artisan migrate --force
    
    docker compose exec app php artisan config:cache
    
    docker compose exec app php artisan route:cache
    
    docker compose exec app php artisan view:cache

Fehler beim Erstellen von Containern (Fehler während docker compose build)

Symptome:

  • docker compose build schlägt mit apt- oder Build-Schritten fehl.

Wie man bestätigt:

  • Baustatus mit Protokollen erneut ausführen:

    docker compose build app

Behobene Fehler:

  • Lies die Ausgabe des fehlgeschlagenen Schritts. Typische Probleme:

    • Netzwerk/DNS in der Build-Umgebung: erneut versuchen oder sicherstellen, dass Docker auf das Internet zugreifen kann.
    • Fehlende Pakete oder falsche apt-Quelle: Führen Sie apt-get update vor der Installation aus und bereinigen Sie anschließend die apt-Listen.
    • Berechtigungen für in das Image kopierte Dateien: Stellen Sie die korrekten COPY-Pfade sicher.

Beispielhafter Dockerfile-Ausschnitt für robuste Installationen:

RUN apt-get update && apt-get install -y --no-install-recommends \
    libpng-dev libxml2-dev zip unzip \
  && docker-php-ext-install pdo_mysql mbstring xml gd \
  && apt-get clean && rm -rf /var/lib/apt/lists/*

Netzwerk- oder DNS-Probleme zwischen Containern

Symptome:

  • Ein Container kann einen anderen nicht über den Servicenamen erreichen (z. B. kann app db nicht über db:3306 erreichen).
  • ping nach Dienstnamen schlägt fehl.

Wie man bestätigt:

docker compose exec app ping -c 3 db

docker compose exec app getent hosts db

docker network inspect travellist

Behobene Fehler:

  • Stellen Sie sicher, dass die Dienste dasselbe Netzwerk in docker-compose.yml teilen (beide sollten auf travellist verweisen).

  • Wenn Sie die Servicenamen oder Netzwerknamen geändert haben, aktualisieren Sie entsprechend DB_HOST und Nginx-Upstreams.

  • Netzwerk neu erstellen (dies zerstört temporäre Container, also sei vorsichtig):

    docker compose down
    
    docker network prune   # optional and destructive
    
    docker compose up -d

Nützliche Diagnosebefehle

Führen Sie diese zur gezielten Inspektion aus:

  • Laufende Container und Ports anzeigen:

    docker compose ps
  • Tail-Protokolle für einen Dienst:

    docker compose logs -f --tail=200 nginx
  • Führe eine Shell innerhalb eines Containers aus:

    docker compose exec app sh
    
    docker compose exec db bash
  • Container-Umgebungsvariablen prüfen:

    docker compose exec app env | sort
    
    docker compose exec db env | grep MYSQL
  • Überprüfen Sie die Prozessliste des Containers:

    docker compose exec app ps aux
    
    docker compose exec app ss -ltnp
  • Überprüfen Sie den Zustand von MySQL:

    docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping

Wann Host-Ports zum Debuggen freigegeben werden sollten

Standardmäßig ist der db-Dienst in dieser Konfiguration nicht an Host-Ports gebunden. Öffnen Sie Ports nur für das lokale Debugging. Um MySQL vorübergehend auf dem Host verfügbar zu machen, fügen Sie unter db Folgendes zu docker-compose.yml hinzu:

ports:
  - "3306:3306"

Dann neu starten:

docker compose up -d db

Nach dem Debuggen entfernen Sie die Ports-Zuordnung, um zu vermeiden, dass die Datenbank im Netzwerk sichtbar wird.

Häufig gestellte Fragen

1. Muss Composer auf dem Host installiert sein?

Nein. Sie können Composer vollständig innerhalb des App-Containers ausführen, was der empfohlene Ansatz für diesen Compose-Workflow ist. Das Dockerfile im Tutorial enthält Composer, daher können Sie Befehle wie docker compose exec app composer install oder docker compose run --rm app composer require vendor/package ausführen. Die Ausführung von Composer im Container stellt sicher, dass die richtige PHP-Version und die erforderlichen Erweiterungen verwendet werden und verhindert, dass Vendor-Dateien auf dem Host mit falschem Besitz erstellt werden.

2. Kann ich Laravel-Migrationen von innerhalb des Containers ausführen?

Ja. Führen Sie Migrationen aus dem App-Container aus, damit sie dieselbe Laufzeit und dasselbe Netzwerk wie Ihre Anwendung verwenden: docker compose exec app php artisan migrate. Für einmalige Aufgaben oder CI-Aufgaben können Sie docker compose run --rm app php artisan migrate --force verwenden. Bevor Sie Migrationen ausführen, stellen Sie sicher, dass der Datenbank-Container gesund ist und Verbindungen akzeptiert, zum Beispiel mit docker compose exec db mysqladmin -u$ {DB_USERNAME} -p$ {DB_PASSWORD} ping oder indem Sie so lange neu versuchen, bis er reagiert.

3. Wie installiere ich zusätzliche PHP-Erweiterungen?

Fügen Sie die erforderlichen Systempakete und Erweiterungsbefehle in die Dockerfile ein und bauen Sie dann das Image neu. Installieren Sie zum Beispiel Abhängigkeiten und erstellen Sie gängige Erweiterungen mit etwas wie:

RUN apt-get update && apt-get install -y libpng-dev libjpeg-dev \
  && docker-php-ext-configure gd --with-jpeg \
  && docker-php-ext-install pdo_mysql mbstring gd

Wenn Sie eine PECL-Erweiterung benötigen, verwenden Sie pecl install und docker-php-ext-enable. Nachdem Sie die Dockerfile bearbeitet haben, führen Sie docker compose build app und dann docker compose up -d app aus, um die Änderungen anzuwenden.

4. Wie mache ich den Nginx-Container für HTTPS zugänglich?

Fügen Sie einen HTTPS-Serverblock zu Ihrer Nginx-Konfiguration hinzu, binden Sie Ihr Zertifikat und Ihren Schlüssel in den Container ein und veröffentlichen Sie Port 443 in docker-compose. Hier ist ein Beispiel mit minimalen Änderungen in docker-compose.yml:

services:
  nginx:
    ports:
      - "8000:80"
      - "443:443"
    volumes:
      - ./docker-compose/nginx:/etc/nginx/conf.d
      - ./certs:/etc/nginx/certs:ro

Fügen Sie dann einen SSL-Server-Block hinzu, der auf /etc/nginx/certs/fullchain.pem und /etc/nginx/certs/privkey.pem verweist. Für die Produktion sollten Sie einen Reverse-Proxy oder einen Zertifikat-Manager wie Traefik oder einen externen Load Balancer verwenden, um Zertifikate sicher zu erhalten und zu erneuern, anstatt private Schlüssel im Anwendungs-Stack zu speichern.

5. Wie baue ich Laravel nach Codeänderungen neu auf?

Es hängt davon ab, was sich geändert hat. Wenn du Bind-Mounts für das Projektverzeichnis verwendest, werden Änderungen an PHP, Templates, Routen und Controllern sofort übernommen; führe bei Bedarf Laravel-Cache-Clears aus, zum Beispiel:

docker compose exec app php artisan config:clear

docker compose exec app php artisan route:clear

docker compose exec app php artisan view:clear

Wenn Sie Composer-Abhängigkeiten oder die Dockerfile (neue Erweiterungen oder Systempakete) geändert haben, installieren oder bauen Sie neu: docker compose exec app composer install bei Abhängigkeitsänderungen und docker compose build app && docker compose up -d bei Änderungen an der Dockerfile. Starten Sie den betroffenen Dienst neu, wenn Sie die PHP-FPM- oder Nginx-Konfiguration ändern: docker compose restart app oder docker compose restart nginx.

Schlussfolgerung

In diesem Leitfaden haben wir eine Docker-Umgebung mit drei Containern eingerichtet und Docker Compose verwendet, um unsere Infrastruktur in einer YAML-Datei zu definieren.

Ab diesem Punkt können Sie an Ihrer Laravel-Anwendung arbeiten, ohne einen lokalen Webserver für die Entwicklung und Tests installieren und einrichten zu müssen. Darüber hinaus arbeiten Sie mit einer temporären Umgebung, die leicht repliziert und verteilt werden kann, was sowohl bei der Entwicklung Ihrer Anwendung als auch beim Übergang zu einer Produktionsumgebung hilfreich sein kann.

Für weitere Laravel-Tutorials sehen Sie sich die folgenden Artikel an:

  • Hosten Sie Ihre Laravel-App auf Linux-Console.net
  • Anleitung zur Installation und Konfiguration von Laravel mit Nginx auf Ubuntu (LEMP-Stack)
  • So erstellen Sie Laravel Eloquent API-Ressourcen, um Modelle in JSON umzuwandeln
  • Wie man eine neue Laravel-Anwendung mit Docker Compose startet

Danke, dass Sie mit der Linux-Console.net Community lernen.