Cum să instalezi și să configurezi Laravel cu Docker Compose pe Ubuntu 22.04


Introducere

A containera o aplicație se referă la procesul de adaptare a unei aplicații și a componentelor sale astfel încât să poată fi rulată în medii ușoare cunoscute sub numele de containere. Astfel de medii sunt izolate și de unică folosință și pot fi folosite pentru dezvoltarea, testarea și implementarea aplicațiilor în producție.

În acest ghid, vom folosi Docker Compose pentru a containeriza o aplicație Laravel pentru dezvoltare. Când veți termina, veți avea o aplicație demo Laravel care rulează pe trei containere de servicii separate:

  • Un serviciu app care rulează PHP7.4-FPM;
  • Un serviciu db care rulează MySQL 8.0;
  • Un serviciu nginx care utilizează serviciul app pentru a interpreta codul PHP înainte de a livra aplicația Laravel utilizatorului final.

Pentru a permite un proces de dezvoltare mai fluid și a facilita depanarea aplicației, vom menține fișierele aplicației sincronizate folosind volume partajate. De asemenea, vom vedea cum să folosim comenzile docker compose exec pentru a rula Composer și Artisan pe containerul app.

Deplasați aplicațiile frontend din GitHub folosind Platforma de Aplicații Linux-Console.net. Lăsați Linux-Console.net să se ocupe de scalarea aplicației dumneavoastră.

Aspecte esențiale:

  • Containerizarea face dezvoltarea previzibilă: Rularea Laravel în Docker asigură medii consistente pe toate calculatoarele, eliminând problemele de tipul „funcționează pe calculatorul meu” și facilitând colaborarea.
  • Separarea serviciilor îmbunătățește mentenabilitatea: Împărțirea PHP, MySQL și Nginx în containere separate ajută la izolarea problemelor, simplifică actualizările și reflectă configurațiile reale de producție.
  • Variabilele de mediu sunt esențiale pentru configurarea flexibilă: Utilizarea unui fișier .env permite dezvoltatorilor să schimbe datele de autentificare ale bazei de date și alte setări fără a modifica codul aplicației.
  • Imaginile Docker personalizate îmbunătățesc controlul și securitatea: Crearea propriei imagini PHP asigură că instalați doar ceea ce are nevoie proiectul dvs., menținând mediul ușor și sigur.
  • Permisiunile fișierelor trebuie să corespundă utilizatorilor containerului: Setarea proprietății și a permisiunilor corecte pentru directoarele scriibile ale Laravel previne erorile la rulare și susține un dezvoltare locală fără probleme.
  • Folosirea fișierelor de suprascriere suportă mai multe medii: docker-compose.override.yml permite ajustări locale—cum ar fi reîncărcarea live—fără a modifica configurațiile de producție. Aceasta menține dezvoltarea și implementarea clar separate.
  • depanarea începe cu înțelegerea comportamentului containerului: Cunoașterea modului de a inspecta jurnalele, de a verifica serviciile în funcțiune și de a testa conectivitatea este esențială pentru rezolvarea rapidă a majorității problemelor în mediile containerizate.
  • O configurație bazată pe Docker se scalează odată cu fluxul tău de lucru: Odată construită, aceeași configurație poate alimenta mediile locale, de staging sau de producție, creând o bază stabilă și reutilizabilă pentru viitoarele proiecte Laravel.

Cerințe prealabile

  • Acces la o mașină locală Ubuntu sau un server de dezvoltare ca utilizator non-root cu privilegii sudo. Dacă folosești un server de la distanță, este recomandat să ai un firewall activ instalat. Pentru a configura aceste lucruri, te rugăm să consulți Ghidul nostru de Configurare Inițială a Serverului pentru Ubuntu.
  • Docker instalat pe serverul dvs., urmând Pașii 1 și 2 din Cum să instalați și să utilizați Docker pe Ubuntu.
  • Docker Compose instalat pe serverul dvs., urmând Pasul 1 din Cum să instalați și să utilizați Docker Compose pe Ubuntu.

Pasul 1 — Obținerea aplicației demo

Pentru a începe, vom prelua aplicația demo Laravel din depozitul său Github. Ne interesează branch-ul tutorial-01, care conține aplicația de bază Laravel pe care am creat-o în ghidul Cum să instalezi și să configurezi Laravel cu Nginx pe Ubuntu 22.04 (LEMP).

Pentru a obține codul aplicației care este compatibil cu acest tutorial, descărcați versiunea tutorial-1.0.1 în directorul dvs. de acasă cu:

cd ~

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

Vom avea nevoie de comanda unzip pentru a despacheta codul aplicației. În cazul în care nu ați instalat acest pachet anterior, faceți-o acum cu:

sudo apt update

sudo apt install unzip

Acum, dezarhivați conținutul aplicației și redenumiți directorul extras pentru un acces mai ușor:

unzip travellist.zip

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

Navigați către directorul travellist-demo:

cd travellist-demo

În pasul următor, vom crea un fișier de configurație .env pentru a configura aplicația.

Pasul 2 — Configurarea fișierului .env al aplicației

Fișierele de configurare Laravel se află într-un director numit config, în interiorul directorului rădăcină al aplicației. În plus, un fișier .env este folosit pentru a configura setările dependente de mediu, cum ar fi acreditările și orice informație care poate varia între implementări. Acest fișier nu este inclus în controlul versiunilor.

Atenție: Fișierul de configurare al mediului conține informații sensibile despre serverul dumneavoastră, inclusiv datele de autentificare la baza de date și cheile de securitate. Din acest motiv, nu ar trebui să partajați niciodată acest fișier public.

Valorile conținute în fișierul .env vor avea prioritate față de valorile setate în fișierele de configurare obișnuite aflate în directorul config. Fiecare instalare pe un mediu nou necesită un fișier de mediu personalizat pentru a defini aspecte precum setările de conectare la baza de date, opțiunile de depanare, URL-ul aplicației, printre alte elemente care pot varia în funcție de mediul în care rulează aplicația.

Acum vom crea un nou fișier .env pentru a personaliza opțiunile de configurare pentru mediul de dezvoltare pe care îl configurăm. Laravel vine cu un fișier exemplu .env pe care îl putem copia pentru a ne crea propriul fișier:

cp .env.example .env

Deschide acest fișier folosind nano sau editorul de text preferat:

nano .env

Fișierul actual .env din aplicația demo travellist conține setări pentru a folosi o bază de date MySQL locală, cu 127.0.0.1 ca gazdă a bazei de date. Trebuie să actualizăm variabila DB_HOST astfel încât să indice către serviciul de baze de date pe care îl vom crea în mediul nostru Docker. În acest ghid, vom numi serviciul nostru de baze de date db. Mergeți mai departe și înlocuiți valoarea listată a DB_HOST cu numele serviciului bazei de date:

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
...

Simțiți-vă liber să schimbați și numele bazei de date, numele de utilizator și parola, dacă doriți. Aceste variabile vor fi folosite într-un pas ulterior, când vom configura fișierul docker-compose.yml pentru a configura serviciile noastre.

Salvează fișierul când ai terminat de editat. Dacă ai folosit nano, poți face asta apăsând Ctrl+x, apoi Y și Enter pentru a confirma.

Pasul 3 – Configurarea Dockerfile-ului aplicației

Deși atât serviciile noastre MySQL, cât și Nginx vor fi bazate pe imagini implicite obținute de pe Docker Hub, avem totuși nevoie să construim o imagine personalizată pentru containerul aplicației. Vom crea un nou Dockerfile pentru aceasta.

Imaginea noastră travellist va fi bazată pe imaginea oficială PHP php:7.4-fpm de pe Docker Hub. Peste acest mediu de bază PHP-FPM, vom instala câteva module PHP suplimentare și instrumentul de gestionare a dependențelor Composer.

De asemenea, vom crea un nou utilizator de sistem; acest lucru este necesar pentru a executa comenzile artisan și composer în timp ce dezvoltăm aplicația. Setarea uid asigură că utilizatorul din interiorul containerului are același uid ca utilizatorul tău de pe sistemul gazdă, unde rulezi Docker. Astfel, orice fișiere create de aceste comenzi sunt redate pe gazdă cu permisiunile corecte. Aceasta înseamnă, de asemenea, că vei putea folosi editorul de cod preferat pe mașina gazdă pentru a dezvolta aplicația care rulează în interiorul containerelor.

Creează un nou Dockerfile cu:

nano Dockerfile

Copiați următorul conținut în Dockerfile-ul dvs.:

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

Nu uita să salvezi fișierul când termini.

Dockerfile-ul nostru începe prin definirea imaginii de bază pe care o folosim: php:7.4-fpm.

După instalarea pachetelor de sistem și a extensiilor PHP, instalăm Composer copiind executabilul composer din cea mai recentă imagine oficială în propria noastră imagine a aplicației.

Un nou utilizator de sistem este apoi creat și configurat folosind argumentele user și uid care au fost declarate la începutul Dockerfile-ului. Aceste valori vor fi injectate de Docker Compose în timpul construirii.

În cele din urmă, setăm directorul de lucru implicit ca /var/www și schimbăm utilizatorul către cel nou creat. Acest lucru va asigura că te conectezi ca utilizator obișnuit și că ești în directorul corect atunci când rulezi comenzile composer și artisan în containerul aplicației.

Pasul 4 — Configurarea fișierelor de configurare Nginx și a fișierelor de depanare a bazei de date

Atunci când creezi medii de dezvoltare cu Docker Compose, este adesea necesar să partajezi fișiere de configurare sau de inițializare cu containerele de servicii, pentru a configura sau a porni acele servicii. Această practică facilitează modificarea fișierelor de configurare pentru a ajusta fin mediul tău în timp ce dezvolți aplicația.

Acum vom configura un folder cu fișiere care vor fi folosite pentru a configura și inițializa containerele noastre de servicii.

Pentru a configura Nginx, vom împărtăși un fișier travellist.conf care va configura modul în care este servită aplicația. Creați folderul docker-compose/nginx cu:

mkdir -p docker-compose/nginx

Deschide un fișier nou numit travellist.conf în acel director:

nano docker-compose/nginx/travellist.conf

Copiați următoarea configurație Nginx în acel fișier:

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;
    }
}

Acest fișier va configura Nginx să asculte pe portul 80 și să folosească index.php ca pagină implicită de index. Va seta rădăcina documentului la /var/www/public și apoi va configura Nginx să folosească serviciul app pe portul 9000 pentru a procesa fișierele *.php.

Salvați și închideți fișierul când ați terminat de editat.

Pentru a configura baza de date MySQL, vom partaja un dump al bazei de date care va fi importat atunci când containerul este inițializat. Aceasta este o funcție oferită de imaginea MySQL 8.0 pe care o vom folosi pe acel container.

Creează un folder nou pentru fișierele tale de inițializare MySQL în interiorul folderului docker-compose:

mkdir docker-compose/mysql

Deschideți un fișier nou .sql:

nano docker-compose/mysql/init_db.sql

Următorul dump MySQL se bazează pe baza de date pe care am configurat-o în ghidul nostru Laravel pe LEMP. Va crea un tabel nou numit places. Apoi, va popula tabelul cu un set de locuri de exemplu.

Adaugă următorul cod în fișier:

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);

Tabelul places conține trei câmpuri: id, name și visited. Câmpul visited este un indicator folosit pentru a identifica locurile care încă sunt de vizitat. Te poți simți liber să schimbi locurile din exemplu sau să adaugi altele noi. Salvează și închide fișierul când ai terminat.

Am terminat de configurat Dockerfile-ul aplicației și fișierele de configurare ale serviciului. Următorul pas este să configurăm Docker Compose pentru a folosi aceste fișiere la crearea serviciilor noastre.

Pasul 5 — Crearea unui mediu cu mai multe containere folosind Docker Compose

Docker Compose îți permite să creezi medii cu mai multe containere pentru aplicații care rulează pe Docker. Folosește definiții de servicii pentru a construi medii complet personalizabile cu multiple containere care pot partaja rețele și volume de date. Acest lucru permite o integrare fără probleme între componentele aplicației.

Pentru a configura definițiile serviciilor noastre, vom crea un fișier nou numit docker-compose.yml. De obicei, acest fișier se află în rădăcina folderului aplicației și definește mediul containerizat, inclusiv imaginile de bază pe care le veți folosi pentru a construi containerele și modul în care serviciile voastre vor interacționa.

Vom defini trei servicii diferite în fișierul nostru docker-compose.yml: app, db și nginx.

Serviciul app va construi o imagine numită travellist, bazată pe Dockerfile-ul pe care l-am creat anterior. Containerul definit de acest serviciu va rula un server php-fpm pentru a interpreta codul PHP și a trimite rezultatele înapoi către serviciul nginx, care va rula într-un container separat. Serviciul mysql definește un container care rulează un server MySQL 8.0. Serviciile noastre vor partaja o rețea bridge numită travellist.

Fișierele aplicației vor fi sincronizate atât pe serviciile app, cât și pe nginx prin bind mounts. Bind mounts sunt utile în mediile de dezvoltare deoarece permit o sincronizare bidirecțională performantă între mașina gazdă și containere.

Creează un nou fișier docker-compose.yml în directorul rădăcină al aplicației:

nano docker-compose.yml

Un fișier tipic docker-compose.yml începe cu o definiție a versiunii, urmată de un nod services, sub care sunt definite toate serviciile. Rețelele partajate sunt de obicei definite în partea de jos a acelui fișier.

Pentru a începe, copiază acest cod standard în fișierul tău docker-compose.yml:

version: "3.9"
services:


networks:
  travellist:
    driver: bridge

Acum vom edita nodul services pentru a include serviciile app, db și nginx.

Serviciul app

Serviciul app va configura un container numit travellist-app. Acesta construiește o nouă imagine Docker bazată pe un Dockerfile aflat în aceeași cale cu fișierul docker-compose.yml. Noua imagine va fi salvată local sub numele travellist.

Chiar dacă rădăcina documentului servită ca aplicație se află în containerul nginx, avem nevoie ca fișierele aplicației să fie undeva și în containerul app, astfel încât să putem executa sarcini din linia de comandă cu uneltele Laravel Artisan.

Copiați următoarea definiție de serviciu sub nodul services, în interiorul fișierului docker-compose.yml:

  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

Aceste setări fac următoarele:

  • build: Această configurație indică Docker Compose să construiască o imagine locală pentru serviciul app, folosind calea specificată (context) și Dockerfile-ul pentru instrucțiuni. Argumentele user și uid sunt injectate în Dockerfile pentru a personaliza comenzile de creare a utilizatorului la momentul construirii.
  • image: Numele care va fi utilizat pentru imaginea care este construită.
  • container_name: Setează numele containerului pentru acest serviciu.
  • restart: Reporniți întotdeauna, cu excepția cazului în care serviciul este oprit.
  • working_dir: Setează directorul implicit pentru acest serviciu ca /var/www.
  • volumes: Creează un volum partajat care va sincroniza conținutul din directorul curent către /var/www din interiorul containerului. Observați că acesta nu este rădăcina documentelor dvs., deoarece aceasta va fi în containerul nginx.
  • rețele: Configurează acest serviciu pentru a utiliza o rețea numită travellist.

Serviciul db

Serviciul db folosește o imagine MySQL 8.0 pre-construită de pe Docker Hub. Deoarece Docker Compose încarcă automat fișierele de variabile .env aflate în același director cu fișierul docker-compose.yml, putem prelua setările bazei noastre de date din fișierul Laravel .env pe care l-am creat într-un pas anterior.

Include următoarea definiție de serviciu în nodul tău services, imediat după serviciul app:

  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

Aceste setări fac următoarele:

  • image: Define imaginea Docker care trebuie folosită pentru acest container. În acest caz, folosim o imagine MySQL 8.0 de pe Docker Hub.
  • container_name: Setează numele containerului pentru acest serviciu: travellist-db.
  • restart: Repornește întotdeauna acest serviciu, cu excepția cazului în care este oprit în mod explicit.
  • environment: Define variabilele de mediu în noul container. Folosim valori obținute din fișierul Laravel .env pentru a configura serviciul nostru MySQL, care va crea automat o bază de date și un utilizator nou pe baza variabilelor de mediu furnizate.
  • volumes: Creează un volum pentru a partaja un dump de bază de date .sql care va fi folosit pentru a inițializa baza de date a aplicației. Imaginea MySQL va importa automat fișierele .sql plasate în directorul /docker-entrypoint-initdb.d din interiorul containerului.
  • rețele: Configurează acest serviciu pentru a utiliza o rețea numită travellist.

Serviciul nginx

Serviciul nginx folosește o imagine Nginx pre-construită pe baza Alpine, o distribuție Linux ușoară. Creează un container numit travellist-nginx și utilizează definiția ports pentru a crea o redirecționare de la portul 8000 de pe sistemul gazdă către portul 80 din interiorul containerului.

Include următoarea definiție de serviciu în nodul tău services, imediat după serviciul db:

  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

Aceste setări fac următoarele:

  • image: Define imaginea Docker care ar trebui folosită pentru acest container. În acest caz, folosim imaginea Alpine Nginx 1.17.
  • container_name: Setează numele containerului pentru acest serviciu: travellist-nginx.
  • restart: Reporniți întotdeauna acest serviciu, cu excepția cazului în care este oprit în mod explicit.
  • ports: Configurează o redirecționare a portului care va permite accesul extern prin portul 8000 către serverul web care rulează pe portul 80 din interiorul containerului.
  • volumes: Creează două volume partajate. Primul va sincroniza conținutul din directorul curent cu /var/www din interiorul containerului. În acest mod, atunci când faci modificări locale în fișierele aplicației, acestea vor fi reflectate rapid în aplicația servită de Nginx din interiorul containerului. Al doilea volum se va asigura că fișierul nostru de configurare Nginx, aflat la docker-compose/nginx/travellist.conf, este copiat în folderul de configurare Nginx al containerului.
  • rețele: Configurează acest serviciu să utilizeze o rețea numită travellist.

Fișier docker-compose.yml finalizat

Așa arată fișierul nostru final docker-compose.yml:

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

Asigură-te că salvezi fișierul când termini.

Pasul 6 — Rularea aplicației cu Docker Compose

Acum vom folosi comenzile docker compose pentru a construi imaginea aplicației și a rula serviciile pe care le-am specificat în configurația noastră.

Construiește imaginea app cu următoarea comandă:

docker compose build app

Această comandă ar putea dura câteva minute pentru a se finaliza. Vei vedea un rezultat asemănător cu acesta:

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

Când compilarea este terminată, poți rula mediul în modul background cu:

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

Acest lucru va rula containerele tale în fundal. Pentru a afișa informații despre starea serviciilor tale active, rulează:

docker compose ps

Vei vedea un rezultat ca acesta:

      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

Mediul tău este acum activ și funcțional.

Acum puteți verifica dacă fiecare serviciu containerizat răspunde corect folosind comenzi simple curl.

  1. Pentru a testa serviciul web Nginx:

    curl -I http://localhost:8000

    Rezultat așteptat:

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

    Acest lucru confirmă că containerul Nginx rulează și oferă răspunsuri din aplicația ta Laravel.

  2. Pentru a testa PHP-FPM prin Nginx:

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

    Dacă aplicația rulează corect, această comandă returnează HTML-ul paginii de bun venit care include cuvântul Berlin.

Trebuie să executăm încă câteva comenzi pentru a finaliza configurarea aplicației. Poți folosi comanda docker compose exec pentru a executa comenzi în containerele serviciilor, cum ar fi ls -l pentru a afișa informații detaliate despre fișierele din directorul aplicației:

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

Acum vom rula composer install pentru a instala dependențele aplicației:

docker compose exec app rm -rf vendor composer.lock

docker compose exec app composer install

Vei vedea un rezultat ca acesta:

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.

Ultimul lucru pe care trebuie să-l facem înainte de a testa aplicația este să generăm o cheie unică pentru aplicație folosind instrumentul de linie de comandă artisan din Laravel. Această cheie este folosită pentru a cripta sesiunile utilizatorilor și alte date sensibile:

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

Acum mergi la browserul tău și accesează numele de domeniu sau adresa IP a serverului tău pe portul 8000:

http://server_domain_or_IP:8000

Notă: Dacă rulați această demonstrație pe calculatorul dumneavoastră, folosiți http://localhost:8000 pentru a accesa aplicația din browserul dumneavoastră.

Vei vedea o pagină ca aceasta:

De asemenea, poți testa aplicația Laravel din interiorul containerului aplicației fără a folosi un browser:

docker compose exec app curl http://nginx

Dacă totul este configurat corect, ar trebui să vedeți rezultatul HTML al paginii de bun venit implicite din Laravel.

<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>

Poți folosi comanda logs pentru a verifica jurnalele generate de serviciile tale:

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"

De asemenea, poți folosi următoarea comandă curl pentru a declanșa înregistrări în jurnal:

curl http://localhost:8000

Apoi rulează din nou:

docker compose logs nginx

Ar trebui să vedeți acum o intrare corespunzătoare în jurnalul de acces pentru cererea dvs.

Dacă doriți să puneți în pauză mediul Docker Compose păstrând starea tuturor serviciilor sale, rulați:

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

Apoi vă puteți relua serviciile cu:

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

Pentru a închide mediul dvs. Docker Compose și a elimina toate containerele, rețelele și volumele acestuia, rulați:

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

Pentru o prezentare generală a tuturor comenzilor Docker Compose, vă rugăm să consultați referința comenzilor Docker Compose.

Fixarea permisiunilor fișierelor și directoarelor în Laravel

Când rulezi Laravel în containere Docker, una dintre cele mai frecvente probleme cu care se confruntă dezvoltatorii este legată de permisiunile fișierelor și directoarelor. Laravel necesită ca anumite directoare să fie scriibile de procesul PHP-FPM din interiorul containerului app. Dacă aceste directoare nu sunt scriibile, aplicația poate eșua în a înregistra erori, a salva fișierele de configurare în cache sau a stoca corect datele de sesiune.

S-ar putea să întâlniți mesaje precum:

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

sau

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

Aceste erori indică faptul că utilizatorul care rulează PHP în container nu are permisiunea de a scrie în directoarele necesare.

Următorii pași descriu cum să diagnosticați și să rezolvați aceste probleme atât în mediile locale, cât și în cele de producție.

Ajustați permisiunile directorului inscripționabil

Laravel necesită ca două directoare să fie scrisibile de către utilizatorul serverului web:

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

Dacă vedeți erori de permisiune, le puteți remedia ajustând proprietatea și permisiunile din interiorul containerului:

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

Dacă Dockerfile-ul tău folosește un alt nume de utilizator (definit prin argumentul de construire user), înlocuiește sammy cu acel nume de utilizator.

Puteți verifica proprietatea folosind comanda următoare:

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

Rezultatul ar trebui să arate că utilizatorul și grupul corespund procesului PHP-FPM din interiorul containerului.

Dacă vrei să ajustezi permisiunile de pe mașina gazdă, rulează:

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

sudo chmod -R 775 storage bootstrap/cache

Acest lucru asigură că ambele directoare Laravel sunt scriitoare atât de către container, cât și de către utilizatorul dvs. gazdă.

Setați drepturile de proprietate asupra directorului în timpul construirii imaginii

Pentru a preveni reapariția problemelor de permisiuni după fiecare reconstruire, puteți seta proprietatea directorului în timpul procesului de construire a imaginii Docker. Deschideți Dockerfile și adăugați următoarea linie aproape de sfârșit, înainte de a trece la utilizatorul non-root:

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

Această comandă asigură că toate fișierele și directoarele din /var/www sunt deținute de utilizatorul pe care l-ați definit în Dockerfile-ul dumneavoastră.

Pentru o abordare mai țintită și sigură, poți ajusta doar directoarele în care Laravel trebuie să scrie:

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

După ce faceți această modificare, reconstruiți imaginea și reporniți containerele:

docker compose build app

docker compose up -d

Data viitoare când containerul va porni, va avea deja permisiunile corecte.

Verifică consistența ID-ului utilizatorului între gazdă și container

Atunci când folosiți bind mounts, Docker partajează fișiere între gazdă și container. Dacă UID-ul numeric al utilizatorului de pe gazdă diferă de UID-ul definit în container, pot apărea conflicte de proprietate a fișierelor. Acest lucru este obișnuit pe sistemele în care ID-urile utilizatorilor variază între mașini.

Pentru a verifica valorile UID, rulează:

id -u $(whoami)

docker compose exec app id -u

Dacă aceste două numere nu corespund, actualizează fișierul tău docker-compose.yml pentru a transmite UID-ul tău real procesului de construire:

build:
  args:
    user: sammy
    uid: 1000

Apoi reconstruiește imaginea:

docker compose build app

docker compose up -d

Aceasta asigură că utilizatorul containerului se potrivește cu utilizatorul gazdă, prevenind neconcordanțele de permisiuni pentru fișierele montate.

Remediați permisiunile directorului cache Composer

Composer stochează dependențele descărcate și fișierele cache în directorul de acasă al utilizatorului din interiorul containerului (/home/sammy/.composer). Dacă acest director nu este scriibil, este posibil să vedeți avertismente de genul:

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

Poți remedia acest lucru creând directorul de cache și atribuindu-i proprietarul corect:

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

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

Acest lucru asigură că Composer poate stoca dependențele și fișierele cache fără erori de permisiune, ceea ce accelerează și rulările viitoare ale comenzii composer install.

Practici recomandate pentru mediile de producție

În producție, gestionarea permisiunilor ar trebui realizată în timpul construirii imaginii, și nu la rulare. Acest lucru îmbunătățește consistența și securitatea.

Urmărește aceste practici pentru o configurație pregătită pentru producție:

  • Evită montările legate pentru codul aplicației: În schimb, copiază fișierele aplicației în imagine în timpul construirii. Montările legate ar trebui să fie rezervate pentru dezvoltare.

  • Setați permisiunile în timpul construirii: Adăugați comenzile RUN chown și chmod în Dockerfile-ul dvs. pentru a asigura proprietatea și drepturile de acces corecte înainte de implementare.

  • Folosește utilizatorul cu cele mai mici privilegii: Asigură-te că procesul PHP rulează sub un utilizator non-root (configurația actuală cu $user deja face acest lucru).

  • Restricționați permisiunile pentru directoarele nedeschisibile la scriere: Majoritatea fișierelor aplicației ar trebui să fie doar pentru citire. Puteți face acest lucru prin:

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

    Această configurare permite Laravel să scrie doar acolo unde este necesar, protejând în același timp celelalte fișiere ale aplicației de modificare.

Urmând acești pași, containerele tale Laravel vor avea permisiuni de fișiere stabile și previzibile atât în mediile locale, cât și în cele de producție. Acest lucru reduce erorile recurente și asigură că Laravel poate înregistra date, stoca fișiere în cache și scrie sesiuni în mod fiabil.

Înțelegerea fișierului docker-compose.override.yml pentru mediile locale și de producție

Docker Compose îți permite să definești aplicații cu mai multe containere folosind un singur fișier de configurare. În majoritatea cazurilor, această configurație este stocată într-un fișier numit docker-compose.yml. Cu toate acestea, gestionarea mai multor medii, cum ar fi dezvoltarea locală și producția, necesită adesea ajustări specifice mediului. Aici devine util fișierul docker-compose.override.yml.

În mod implicit, Docker Compose caută automat un fișier numit docker-compose.override.yml în același director cu fișierul de bază Compose. Când este găsit, acesta combină configurațiile din ambele fișiere atunci când rulezi orice comandă docker compose. Această abordare permite dezvoltatorilor să mențină o configurație de bază curată și reutilizabilă, în timp ce personalizează aspecte specifice ale setup-ului pentru utilizare locală sau în producție.

Rolul fișierului docker-compose.override.yml

Fișierul docker-compose.override.yml este o extensie a configurației de bază Compose. Acesta modifică sau adaugă proprietăți care se aplică doar unui anumit mediu, fără a necesita modificări ale fișierului principal docker-compose.yml. Fișierul de override este deosebit de util în fluxurile de lucru de dezvoltare locală, unde dezvoltatorii trebuie adesea să monteze codul sursă, să activeze unelte de depanare sau să modifice variabile de mediu pentru testare.

Când execuți comanda docker compose up, Docker combină automat conținutul fișierelor docker-compose.yml și docker-compose.override.yml. Dacă un serviciu, volum sau configurație este definit în ambele fișiere, valorile din fișierul de suprascriere au prioritate.

Pentru a folosi un fișier de suprascriere diferit, de exemplu, unul destinat producției, poți specifica fișierul manual folosind flag-ul -f:

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

Această abordare îți permite să menții configurații separate pentru diferite medii, folosind în același timp o structură de comandă unică și coerentă.

Structurarea pentru dezvoltarea locală

Mediile de dezvoltare locale necesită de obicei modificări frecvente, reîncărcare live a codului și înregistrare detaliată. Fișierul docker-compose.override.yml face posibilă introducerea acestor funcționalități fără a afecta alte medii.

Un fișier tipic de suprascriere locală poate include:

  • Montări legate pentru actualizări live de cod
  • Directive de construire locală în loc de utilizarea imaginilor preconstruite
  • Variabile de mediu specifice dezvoltării
  • Servicii de depanare și testare

De exemplu:

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

În acest exemplu:

  • Serviciul app se construiește direct dintr-un Dockerfile local (Dockerfile.dev), ceea ce facilitează testarea modificărilor fără a reconstrui imaginile printr-un registru.
  • Codul sursă este montat folosind montări bind, permițând containerului să reflecte modificările fișierelor locale în timp real.
  • Variabilele de mediu precum LOG_LEVEL=debug sunt setate pentru a oferi ieșiri mai detaliate în timpul dezvoltării.
  • Baza de date folosește acreditive simple care sunt potrivite pentru testarea locală, dar nu ar trebui utilizate în producție.

Această configurație oferă un mediu de dezvoltare mai rapid și mai flexibil, păstrând în același timp configurația de bază neschimbată.

Pregătirea pentru producție

Mediile de producție necesită stabilitate, reproductibilitate și securitate. Containerele ar trebui să ruleze din imagini preconstruite, mai degrabă decât să fie construite local, iar informațiile sensibile trebuie gestionate în mod sigur. Spre deosebire de fișierul de suprascriere aplicat automat folosit pentru dezvoltarea locală, suprascrierile pentru producție trebuie specificate în mod explicit.

Un fișier de suprascriere pentru producție (docker-compose.prod.yml) este de obicei conceput pentru a înlocui pașii de construire locali cu referințe la imagini, pentru a folosi volume denumite în locul montărilor directe și pentru a defini proprietăți legate de implementare, cum ar fi limitele de resurse și scalarea.

Exemplu:

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:

În această configurație:

  • Serviciul app face referire la o imagine preconstruită (myapp:1.2.3) stocată într-un registru, în loc să fie construită local.

  • Montările legate sunt înlocuite cu volume denumite pentru a asigura stocarea datelor într-un mod constant și izolat.

  • Datele de autentificare sensibile sunt stocate în siguranță folosind secrete Docker.

  • Secțiunea deploy definește comportamentul de scalare și limitele de resurse pentru o stabilitate mai bună sub sarcină.

  • Configurația trebuie aplicată în mod explicit în timpul implementării:

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

Această structură asigură că implementările de producție sunt consistente, controlate și lipsite de artefacte provenite din dezvoltarea locală.

Compararea suprascrierilor locale și de producție

Aspectul configurațieiLocal (docker-compose.override.yml)Producție (docker-compose.prod.yml)
Aplicat automatDaNu (trebuie să folosiți -f)
Gestionarea codului sursăMontări legate pentru actualizări în timp realImagini preconfigurate din registru
Variabile de mediuValori orientate către dezvoltatoriValori sigure pentru producție
Exploatare forestierăVerbose (debug)Concis (info sau eroare)
Gestionarea secretelorValori simple sau .envSecrete Docker sau secrete gestionate
ScalareRecipient individualMai multe replici
Optimizarea performanțeiNu este necesarLimite de resurse definite
Unelte de depanareActivatDezactivat
Politica de repornireManual sau deloc
restart: unless-stopped

Utilizarea mai multor fișiere Docker Compose oferă o modalitate clară de a separa configurațiile specifice mediului de setările de bază. Fișierul de bază docker-compose.yml definește structura comună a aplicației, în timp ce docker-compose.override.yml o ajustează pentru dezvoltarea locală. Atunci când se face implementarea în producție, un fișier de override explicit asigură o configurare predictibilă și sigură.

Acest model permite echipelor să mențină un flux de lucru constant. Dezvoltatorii pot porni aplicația local folosind:

docker compose up

și să implementați în producție utilizând:

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

Ambele medii împărtășesc aceeași configurație de bază, menținând în același timp setări independente potrivite pentru cerințele lor specifice. Această separare îmbunătățește mentenabilitatea, reduce erorile de configurare și menține alinierea dintre mediile locale și cele de producție.

Depanarea mediului dumneavoastră

Această secțiune oferă pași de depanare pentru problemele comune pe care le puteți întâmpina atunci când rulați aplicația cu Docker Compose.

502 Gateway Greșit (Nginx afișează răspunsuri 502)

Simptome:

  • Browserul afișează 502 Bad Gateway când accesează http://localhost:8000.
  • Jurnalele Nginx conțin linii precum recv() failed (111: Connection refused) sau connect() failed (111: Connection refused) while connecting to upstream.

Cauze probabile:

  • PHP-FPM (serviciul app) nu rulează sau nu ascultă pe socket/portul așteptat.
  • Nginx fastcgi_pass indică gazda/portul greșit.
  • Numele containerului nu se potrivește sau există probleme de rețea între nginx și app.
  • app s-a blocat la pornire (de exemplu, din cauza extensiilor lipsă sau a erorilor de configurare).

Cum să confirmați:

  1. Inspectează jurnalele Nginx:

    docker compose logs nginx | tail -n 100

    Caută mesaje 502 sau erori de conexiune upstream.

  2. Verifică starea și jurnalele containerului app:

    docker compose ps app
    
    docker compose logs app --tail=200
  3. Confirmă că PHP-FPM ascultă în interiorul containerului app:

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

    Așteptat: fie un ascultător TCP pe portul 9000, fie un socket Unix folosit de nginx (mai puțin frecvent cu acest compose).

  4. Confirmați că nginx poate rezolva și accesa app pe rețeaua compose:

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

    Dacă ping eșuează sau wget expiră, există o problemă de rețea sau o nepotrivire a numelui serviciului.

Remedieri comune:

  • Dacă PHP-FPM nu rulează, verificați jurnalul app pentru erori PHP fatale sau eșecuri la pornire. Remediați eroarea de bază (adesea o extensie lipsă sau o problemă de permisiuni) și reporniți:

    docker compose restart app
    
    docker compose logs app --tail=100
  • Dacă configurația Nginx folosește un upstream diferit (de exemplu, fastcgi_pass 127.0.0.1:9000;), actualizați docker-compose/nginx/travellist.conf la fastcgi_pass app:9000; (potrivind numele serviciului din compose) și reîncărcați Nginx:

    # After editing config in host
    
    docker compose exec nginx nginx -s reload
    
    docker compose logs nginx --tail=50
  • Dacă PHP-FPM ascultă pe un socket Unix (de exemplu, /run/php/php7.4-fpm.sock), dar Nginx se așteaptă la TCP sau invers, standardizați pe TCP app:9000 pentru configurațiile Compose sau montați corect socket-ul între containere (recomandat: folosiți TCP în Compose cu mai multe containere).

Extensii PHP lipsă (erori la rularea Composer sau Artisan, sau eșecuri la execuție)

Simptome:

  • Comenzile Composer sau Laravel eșuează cu mesaje de tipul Class "PDO" not found, ext-pdo is missing sau Call to undefined function json_encode.
  • Erori PHP în jurnalele app care fac referire la clase sau funcții nedefinite.

Cum să confirmați:

  1. Inspectați lista modulelor PHP din containerul app:

    docker compose exec app php -m | sort

    Caută numele extensiilor de care ai nevoie (pentru extensiile tipice Laravel: pdo_mysql, mbstring, bcmath, openssl, tokenizer, xml, ctype, json, gd).

  2. Verificați textul de eroare al composer, care de obicei menționează extensiile necesare:

    docker compose exec app composer install

Remedieri:

  • Adaugă extensiile lipsă în Dockerfile-ul tău și reconstruiește imaginea. Exemplu (pentru pdo_mysql și gd):

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

    Apoi reconstruiește și repornește:

    docker compose build app
    
    docker compose up -d app
    
    docker compose logs app --tail=100
  • Dacă o extensie necesită biblioteci de sistem (de exemplu, gd are nevoie de libpng-dev), adaugă liniile corespunzătoare apt-get install înainte de docker-php-ext-install.

  • Dacă ai nevoie de o extensie PHP care nu este inclusă implicit, ia în considerare instalarea acesteia prin PECL și activarea ei în php.ini.

Eșecuri de conectare la baza de date (Laravel nu se poate conecta la MySQL)

Simptome:

  • Laravel afișează erori de conectare la baza de date (SQLSTATE[HY000] [2002] Conexiune refuzată sau Acces refuzat pentru utilizator).
  • Migrațiile eșuează sau aplicația afișează o pagină goală pentru paginile bazate pe date.

Cum să confirmați:

  1. Verificați jurnalele containerului DB pentru erori de inițializare:

    docker compose logs db --tail=200
  2. Verificați dacă containerul DB este pornit:

    docker compose ps db
  3. Confirmă că acreditările și gazda sunt consistente între .env și docker-compose.yml. În .env din Laravel, DB_HOST ar trebui să fie numele serviciului din compose (db), nu 127.0.0.1 sau localhost:

    DB_HOST=db
    DB_PORT=3306
    DB_DATABASE=travellist
    DB_USERNAME=travellist_user
    DB_PASSWORD=password
  4. Test din interiorul containerului db:

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

    Acum, testează din containerul app pentru a te asigura de conectivitatea rețelei:

    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();"'

Cauze și soluții comune:

  • Gazdă greșită în .env: actualizați DB_HOST=db, recreați containerele sau reîncărcați variabilele de mediu:

    docker compose down
    
    docker compose up -d
    
    docker compose exec app php artisan migrate
  • Baza de date nu este pregătită când aplicația pornește: folosește un mecanism de reîncercare în aplicația ta sau adaugă un healthcheck și începe procesarea comenzilor. De asemenea, poți aștepta manual:

    docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping
    
    # expected: mysqld is alive
  • Eșec la inițializarea SQL: verificați fișierele din ./docker-compose/mysql, o eroare de sintaxă sau de permisiune poate opri inițializarea bazei de date. Inspectați jurnalele bazei de date pentru erori de import.

  • Privilegii/credite incorecte: confirmați că baza de date și utilizatorul există. Refaceți baza de date sau rulați SQL pentru a acorda privilegii.

  • Utilizarea portului la nivel de gazdă: dacă ați încercat curl telnet://localhost:3306 și serviciul db nu expune un port al gazdei, acea conexiune va fi refuzată din motive de design. Testați din interiorul containerelor sau expuneți portul în docker-compose.yml (ports: - "3306:3306") pentru depanare doar locală.

Probleme de permisiuni și ale sistemului de fișiere (stocare, cache, fișiere jurnal)

Sintome:

  • Laravel aruncă excepții legate de scrierea în cache sau în jurnale: Fluxul sau fișierul "/var/www/storage/logs/laravel.log" nu a putut fi deschis.
  • Fișierele create de procesele containerului sunt deținute de root și nu pot fi modificate de utilizatorul de pe gazdă.

Cum să confirmați:

  1. Verificați permisiunile fișierului de pe gazdă:

    ls -l
    
    ls -l storage bootstrap/cache
    
    docker compose exec app id -u   # user id inside container
  2. Verificați jurnalele containerului pentru erori de permisiuni.

Remedieri:

  • Asigură-te că Dockerfile setează utilizatorul și UID-ul care corespund utilizatorului tău de pe gazdă (Dockerfile-ul tău creează deja un utilizator cu ARG uid). Confirmă că uid din argumentele de build din docker-compose.yml corespunde UID-ului tău de pe gazdă (de obicei 1000).

  • Remediați proprietatea de la gazdă sau din interiorul containerului:

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

    sau de la gazdă:

    sudo chown -R $(id -u):$(id -g) storage bootstrap/cache
  • Pentru dezvoltare, montările legate pot provoca neconcordanțe de permisiuni; este mai bine să rulezi comenzile din container pentru a ajusta proprietatea după ce pornești containerele.

Erori legate de compozitor sau furnizor

Simptome:

  • composer install eșuează în interiorul app sau lipsa folderului vendor cauzează eroarea de clasă negăsită.
  • composer raportează epuizarea memoriei în container.

Cum să confirmați:

  • Rulează composer în interiorul containerului app și capturează rezultatul:

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

Remedieri:

  • Dacă limitele de memorie pentru composer sunt depășite, permiteți mai multă memorie sau setați variabila de mediu a composer:

    docker compose exec app php -d memory_limit=-1 /usr/bin/composer install
  • Asigurați-vă că executabilul Composer este prezent (Dockerfile-ul dvs. copiază composer). Dacă nu, reconstruiți imaginea cu stratul composer.

  • Dacă vendor lipsește din cauza suprapunerii de montare bind, asigură-te că montarea ta bind nu ascunde folderul vendor creat în imagine. Cea mai bună practică este să rulezi composer install după ce containerele sunt pornite, astfel încât vendor să fie creat în folderul montat, sau să adaugi vendor în container prin construirea imaginii pentru producție.

Aplicația returnează o pagină goală sau erori 500

Simptome:

  • Browser-ul afișează un răspuns gol sau HTTP 500.
  • Jurnalele Nginx arată 200, dar pagina este goală; jurnalele app arată excepții fatale.

Cum să confirmați:

  • Inspectează jurnalele aplicației:

    docker compose exec app tail -n 200 storage/logs/laravel.log
    
    docker compose logs app --tail=200
  • Verifică mediul: APP_DEBUG=true în .env va face ca Laravel să afișeze erori detaliate în dezvoltare (nu activați în producție).

Remedieri:

  • Remediați excepția de bază afișată în laravel.log.

  • Asigură-te că .env există și că APP_KEY este setat:

    docker compose exec app php artisan key:generate
  • Executați migrațiile, goliți memoria cache:

    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

Eșecuri la construirea containerelor (erori în timpul docker compose build)

Simptome:

  • docker compose build generează erori la pașii apt sau build.

Cum să confirmați:

  • Rulați din nou build-ul cu jurnale:

    docker compose build app

Remedieri:

  • Citește rezultatul pasului care a eșuat. Probleme tipice:

    • Rețea/DNS în mediul de construcție: încearcă din nou sau asigură-te că Docker poate accesa internetul.
    • Pachete lipsă sau sursă apt greșită: adăugați apt-get update înainte de instalări și curățați listele apt după aceea.
    • Permisiuni asupra fișierelor copiate în imagine: asigurați-vă că rutele COPY sunt corecte.

Exemplu de fragment Dockerfile pentru instalări robuste:

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/*

Probleme de rețea sau DNS între containere

Simptome:

  • Un container nu poate ajunge la altul folosind numele serviciului (de exemplu, app nu poate ajunge la db prin db:3306).
  • ping după numele serviciului eșuează.

Cum să confirmați:

docker compose exec app ping -c 3 db

docker compose exec app getent hosts db

docker network inspect travellist

Remedieri:

  • Confirmă că serviciile partajează aceeași rețea în docker-compose.yml (ambele ar trebui să facă referire la travellist).

  • Dacă ați schimbat numele serviciilor sau al rețelelor, actualizați corespunzător DB_HOST și upstream-urile Nginx.

  • Recreează rețeaua (aceasta distruge containerele efemere, deci fii atent):

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

Comenzi de diagnostic utile

Executați acestea pentru inspecție țintită:

  • Afișați containerele și porturile active:

    docker compose ps
  • Jurnale în timp real pentru un serviciu:

    docker compose logs -f --tail=200 nginx
  • Executați un shell într-un container:

    docker compose exec app sh
    
    docker compose exec db bash
  • Inspectează variabilele de mediu ale containerului:

    docker compose exec app env | sort
    
    docker compose exec db env | grep MYSQL
  • Verifică lista proceselor din container:

    docker compose exec app ps aux
    
    docker compose exec app ss -ltnp
  • Verifică starea MySQL:

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

Când să expui porturile gazdei pentru depanare

Implicit, serviciul db în această configurație nu este legat de porturile gazdă. Expune porturile doar pentru depanarea locală. Pentru a expune temporar MySQL pe gazdă, adaugă în docker-compose.yml sub db:

ports:
  - "3306:3306"

Apoi repornește:

docker compose up -d db

După depanare, eliminați maparea ports pentru a evita expunerea bazei de date la rețea.

Întrebări frecvente

1. Am nevoie de Composer instalat pe gazdă?

Nu. Poți rula Composer complet în interiorul containerului aplicației, ceea ce este abordarea recomandată pentru acest flux de lucru Compose. Dockerfile-ul din tutorial include Composer, așa că rulează comenzi precum docker compose exec app composer install sau docker compose run --rm app composer require vendor/package. Rularea Composer în container asigură utilizarea versiunii și extensiilor PHP corecte și evită crearea fișierelor vendor cu proprietate nepotrivită pe gazdă.

2. Pot rula migrațiile Laravel din interiorul containerului?

Da. Execută migrațiile din containerul aplicației astfel încât să folosească același runtime și aceeași rețea ca aplicația ta: docker compose exec app php artisan migrate. Pentru sarcini unice sau în CI poți folosi docker compose run --rm app php artisan migrate --force. Înainte de a rula migrațiile, asigură-te că containerul bazei de date este sănătos și acceptă conexiuni, de exemplu cu docker compose exec db mysqladmin -u$ {DB_USERNAME} -p$ {DB_PASSWORD} ping sau prin reîncercare până când răspunde.

3. Cum pot instala extensii PHP suplimentare?

Adaugă pachetele de sistem necesare și comenzile pentru extensii în Dockerfile, apoi reconstruiește imaginea. De exemplu, instalează dependențele și construiește extensiile comune cu ceva de genul:

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

Dacă ai nevoie de o extensie PECL, folosește pecl install și docker-php-ext-enable. După ce editezi Dockerfile-ul, rulează docker compose build app, iar apoi docker compose up -d app pentru a aplica modificările.

4. Cum expun containerul Nginx pentru HTTPS?

Adaugă un bloc de server HTTPS în configurația Nginx, montează certificatul și cheia în container și publică portul 443 în docker-compose. Iată un exemplu cu modificări minimale în docker-compose.yml:

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

Apoi adaugă un bloc de server SSL care face referire la /etc/nginx/certs/fullchain.pem și /etc/nginx/certs/privkey.pem. Pentru producție, folosește un proxy invers sau un manager de certificate precum Traefik sau un balancer de sarcină extern pentru a obține și reînnoi certificatele în siguranță, în loc să stochezi cheile private în stiva aplicației.

5. Cum îmi reconstruiesc Laravel după modificările din cod?

Depinde de ceea ce s-a schimbat. Dacă folosești bind mounts pentru directorul proiectului, modificările la PHP, template-uri, rute și controlere se reflectă imediat; rulează comenzi de curățare a cache-ului Laravel dacă este nevoie, de exemplu:

docker compose exec app php artisan config:clear

docker compose exec app php artisan route:clear

docker compose exec app php artisan view:clear

Dacă ați schimbat dependențele Composer sau Dockerfile-ul (noi extensii sau pachete de sistem), reinstalați sau reconstruiți: docker compose exec app composer install pentru schimbări de dependențe și docker compose build app && docker compose up -d pentru schimbări în Dockerfile. Repornirea serviciului afectat este necesară când modificați configurația PHP-FPM sau Nginx: docker compose restart app sau docker compose restart nginx.

Concluzie

În acest ghid, am configurat un mediu Docker cu trei containere folosind Docker Compose pentru a defini infrastructura noastră într-un fișier YAML.

De acum înainte, poți lucra la aplicația ta Laravel fără a fi nevoie să instalezi și să configurezi un server web local pentru dezvoltare și testare. Mai mult, vei lucra cu un mediu temporar care poate fi ușor replicat și distribuit, ceea ce poate fi util atât în timpul dezvoltării aplicației tale, cât și atunci când te muți către un mediu de producție.

Pentru mai multe tutoriale Laravel, consultați următoarele articole:

  • Găzduiește-ți aplicația Laravel pe Linux-Console.net
  • Cum să instalezi și să configurezi Laravel cu Nginx pe Ubuntu (Stack LEMP)
  • Cum să creezi resurse API Laravel Eloquent pentru a transforma modelele în JSON
  • Cum să inițializezi o nouă aplicație Laravel cu Docker Compose

Mulțumim că ați învățat împreună cu comunitatea Linux-Console.net.