Como Instalar e Configurar o Laravel com Docker Compose no Ubuntu 22.04
Introdução
Containerizar uma aplicação refere-se ao processo de adaptar uma aplicação e seus componentes para poder executá-la em ambientes leves conhecidos como containers. Esses ambientes são isolados e descartáveis, e podem ser utilizados para desenvolver, testar e implantar aplicações em produção.
Neste guia, usaremos o Docker Compose para containerizar uma aplicação Laravel para desenvolvimento. Quando você terminar, terá uma aplicação Laravel de demonstração rodando em três contêineres de serviço separados:
- Um serviço
approdando PHP7.4-FPM; - Um serviço
dbexecutando MySQL 8.0; - Um serviço
nginxque utiliza o serviçoapppara processar código PHP antes de servir a aplicação Laravel ao usuário final.
Para permitir um processo de desenvolvimento mais ágil e facilitar a depuração da aplicação, manteremos os arquivos da aplicação sincronizados usando volumes compartilhados. Também veremos como usar os comandos docker compose exec para executar o Composer e o Artisan no contêiner app.
Implante suas aplicações frontend do GitHub usando a Plataforma de Aplicativos do Linux-Console.net. Deixe o Linux-Console.net se preocupar com a escalabilidade do seu app.
Principais Pontos:
- A conteinerização torna o desenvolvimento previsível: Executar Laravel no Docker garante ambientes consistentes em diferentes máquinas, eliminando problemas do tipo “funciona no meu computador” e facilitando a colaboração.
- A separação de serviços melhora a manutenibilidade: Dividir PHP, MySQL e Nginx em contêineres separados ajuda a isolar problemas, simplifica atualizações e reflete configurações de produção reais.
- Variáveis de ambiente são fundamentais para uma configuração flexível: Usar um arquivo
.envpermite que os desenvolvedores alterem credenciais de banco de dados e outras configurações sem mexer no código da aplicação. - Imagens Docker personalizadas melhoram o controle e a segurança: Criar sua própria imagem PHP garante que você instale apenas o que seu projeto precisa, mantendo o ambiente leve e seguro.
- As permissões de arquivos devem corresponder aos usuários do contêiner: Configurar a propriedade e as permissões corretas para os diretórios graváveis do Laravel evita erros em tempo de execução e garante um desenvolvimento local tranquilo.
- Usar arquivos de substituição suporta múltiplos ambientes:
docker-compose.override.ymlpermite ajustes locais—como recarga ao vivo—sem alterar as configurações de produção. Isso mantém o desenvolvimento e a implantação claramente separados. - A solução de problemas começa com a compreensão do comportamento do contêiner: Saber como inspecionar logs, verificar os serviços em execução e testar a conectividade é essencial para resolver a maioria dos problemas rapidamente em ambientes conteinerizados.
- Uma configuração baseada em Docker escala conforme seu fluxo de trabalho: Uma vez construída, a mesma configuração pode alimentar ambientes locais, de testes ou de produção, criando uma base estável e reutilizável para futuros projetos Laravel.
Pré-requisitos
- Acesso a uma máquina local Ubuntu ou servidor de desenvolvimento como um usuário não-root com privilégios de sudo. Se você estiver usando um servidor remoto, é recomendável ter um firewall ativo instalado. Para configurá-los, consulte nosso Guia de Configuração Inicial de Servidor para Ubuntu.
- Docker instalado no seu servidor, seguindo os Passos 1 e 2 de Como Instalar e Usar Docker no Ubuntu.
- Docker Compose instalado no seu servidor, seguindo o Passo 1 de Como Instalar e Usar o Docker Compose no Ubuntu.
Passo 1 — Obtendo o Aplicativo Demo
Para começar, vamos buscar a aplicação de demonstração do Laravel em seu repositório no Github. Estamos interessados no branch tutorial-01, que contém a aplicação básica do Laravel que criamos no guia Como Instalar e Configurar o Laravel com Nginx no Ubuntu 22.04 (LEMP).
Para obter o código do aplicativo que é compatível com este tutorial, faça o download da versão tutorial-1.0.1 para o seu diretório home com:
cd ~
curl -L https://github.com/do-community/travellist-laravel-demo/archive/tutorial-1.0.1.zip -o travellist.zipVamos precisar do comando unzip para descompactar o código da aplicação. Caso você não tenha instalado esse pacote antes, faça isso agora com:
sudo apt update
sudo apt install unzipAgora, descompacte o conteúdo do aplicativo e renomeie o diretório descompactado para facilitar o acesso:
unzip travellist.zip
mv travellist-laravel-demo-tutorial-1.0.1 travellist-demoNavegue até o diretório travellist-demo:
cd travellist-demoNo próximo passo, vamos criar um arquivo de configuração .env para configurar a aplicação.
Passo 2 — Configurando o arquivo .env do aplicativo
Os arquivos de configuração do Laravel estão localizados em um diretório chamado config, dentro do diretório raiz da aplicação. Além disso, um arquivo .env é usado para configurar informações dependentes do ambiente, como credenciais e qualquer informação que possa variar entre implantações. Este arquivo não é incluído no controle de versão.
Aviso: O arquivo de configuração do ambiente contém informações sensíveis sobre o seu servidor, incluindo credenciais do banco de dados e chaves de segurança. Por esse motivo, você nunca deve compartilhar este arquivo publicamente.
Os valores contidos no arquivo .env terão prioridade sobre os valores definidos nos arquivos de configuração regulares localizados no diretório config. Cada instalação em um novo ambiente requer um arquivo de ambiente personalizado para definir itens como configurações de conexão com o banco de dados, opções de depuração, URL da aplicação, entre outros itens que podem variar dependendo do ambiente em que a aplicação está sendo executada.
Agora vamos criar um novo arquivo .env para personalizar as opções de configuração do ambiente de desenvolvimento que estamos configurando. O Laravel vem com um arquivo de exemplo .env que podemos copiar para criar o nosso próprio:
cp .env.example .envAbra este arquivo usando nano ou o editor de texto de sua preferência:
nano .envO arquivo .env atual da aplicação de demonstração travellist contém configurações para usar um banco de dados MySQL local, com 127.0.0.1 como host do banco de dados. Precisamos atualizar a variável DB_HOST para que ela aponte para o serviço de banco de dados que criaremos em nosso ambiente Docker. Neste guia, chamaremos nosso serviço de banco de dados de db. Vá em frente e substitua o valor listado de DB_HOST pelo nome do serviço de banco de dados:
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
...Sinta-se à vontade para também alterar o nome do banco de dados, o nome de usuário e a senha, se desejar. Essas variáveis serão usadas em uma etapa posterior, onde configuraremos o arquivo docker-compose.yml para configurar nossos serviços.
Salve o arquivo quando terminar de editar. Se você usou nano, pode fazer isso pressionando Ctrl+x, depois Y e Enter para confirmar.
Passo 3 – Configurando o Dockerfile da Aplicação
Embora nossos serviços MySQL e Nginx sejam baseados em imagens padrão obtidas do Docker Hub, ainda precisamos construir uma imagem personalizada para o container da aplicação. Vamos criar um novo Dockerfile para isso.
Nossa imagem travellist será baseada na imagem oficial do PHP php:7.4-fpm do Docker Hub. Sobre esse ambiente básico do PHP-FPM, vamos instalar alguns módulos PHP adicionais e a ferramenta de gerenciamento de dependências Composer.
Também criaremos um novo usuário do sistema; isso é necessário para executar os comandos artisan e composer enquanto desenvolve a aplicação. A configuração uid garante que o usuário dentro do contêiner tenha o mesmo uid que o seu usuário do sistema na máquina host, onde você está executando o Docker. Dessa forma, quaisquer arquivos criados por esses comandos são replicados no host com as permissões corretas. Isso também significa que você poderá usar o editor de código de sua preferência na máquina host para desenvolver a aplicação que está rodando dentro dos contêineres.
Crie um novo Dockerfile com:
nano DockerfileCopie o seguinte conteúdo para o seu 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
Não se esqueça de salvar o arquivo quando terminar.
Nosso Dockerfile começa definindo a imagem base que estamos usando: php:7.4-fpm.
Após instalar os pacotes do sistema e as extensões PHP, instalamos o Composer copiando o executável composer da sua imagem oficial mais recente para a nossa própria imagem de aplicação.
Um novo usuário do sistema é então criado e configurado usando os argumentos user e uid que foram declarados no início do Dockerfile. Esses valores serão injetados pelo Docker Compose durante a construção.
Finalmente, configuramos o diretório de trabalho padrão como /var/www e mudamos para o usuário recém-criado. Isso garantirá que você esteja se conectando como um usuário comum e que esteja no diretório correto ao executar os comandos composer e artisan no contêiner da aplicação.
Etapa 4 — Configurando a Configuração do Nginx e os Arquivos de Dump do Banco de Dados
Ao criar ambientes de desenvolvimento com Docker Compose, muitas vezes é necessário compartilhar arquivos de configuração ou de inicialização com os containers de serviço, a fim de configurar ou iniciar esses serviços. Essa prática facilita a realização de alterações nos arquivos de configuração para ajustar seu ambiente enquanto você desenvolve a aplicação.
Agora vamos configurar uma pasta com arquivos que serão usados para configurar e inicializar nossos contêineres de serviço.
Para configurar o Nginx, vamos compartilhar um arquivo travellist.conf que configurará como a aplicação será servida. Crie a pasta docker-compose/nginx com:
mkdir -p docker-compose/nginxAbra um novo arquivo chamado travellist.conf dentro desse diretório:
nano docker-compose/nginx/travellist.confCopie a seguinte configuração do Nginx para esse arquivo:
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;
}
}Este arquivo irá configurar o Nginx para escutar na porta 80 e usar index.php como página inicial padrão. Ele definirá o diretório raiz para /var/www/public e, em seguida, configurará o Nginx para usar o serviço app na porta 9000 para processar arquivos *.php.
Salve e feche o arquivo quando terminar de editar.
Para configurar o banco de dados MySQL, iremos compartilhar um dump do banco de dados que será importado quando o contêiner for inicializado. Esta é uma funcionalidade fornecida pela imagem MySQL 8.0 que usaremos nesse contêiner.
Crie uma nova pasta para seus arquivos de inicialização do MySQL dentro da pasta docker-compose:
mkdir docker-compose/mysqlAbra um novo arquivo .sql:
nano docker-compose/mysql/init_db.sqlO seguinte dump do MySQL é baseado no banco de dados que configuramos em nosso guia Laravel no LEMP. Ele criará uma nova tabela chamada places. Em seguida, irá popular a tabela com um conjunto de lugares de exemplo.
Adicione o seguinte código ao arquivo:
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);A tabela places contém três campos: id, name e visited. O campo visited é uma marcação usada para identificar os lugares que ainda estão a visitar. Fique à vontade para alterar os lugares de exemplo ou incluir novos. Salve e feche o arquivo quando terminar.
Terminamos de configurar o Dockerfile da aplicação e os arquivos de configuração dos serviços. Em seguida, vamos configurar o Docker Compose para usar esses arquivos ao criar nossos serviços.
Passo 5 — Criando um Ambiente Multi-Container com Docker Compose
O Docker Compose permite criar ambientes de múltiplos contêineres para aplicações que rodam no Docker. Ele utiliza definições de serviço para construir ambientes totalmente personalizáveis com múltiplos contêineres que podem compartilhar redes e volumes de dados. Isso permite uma integração perfeita entre os componentes da aplicação.
Para configurar nossas definições de serviço, criaremos um novo arquivo chamado docker-compose.yml. Normalmente, este arquivo está localizado na raiz da pasta da aplicação e define seu ambiente conteinerizado, incluindo as imagens base que você usará para construir seus containers e como seus serviços irão interagir.
Vamos definir três serviços diferentes em nosso arquivo docker-compose.yml: app, db e nginx.
O serviço app irá construir uma imagem chamada travellist, baseada no Dockerfile que criamos anteriormente. O contêiner definido por este serviço executará um servidor php-fpm para interpretar o código PHP e enviar os resultados de volta para o serviço nginx, que estará rodando em um contêiner separado. O serviço mysql define um contêiner executando um servidor MySQL 8.0. Nossos serviços irão compartilhar uma rede do tipo bridge chamada travellist.
Os arquivos da aplicação serão sincronizados em ambos os serviços app e nginx através de bind mounts. Bind mounts são úteis em ambientes de desenvolvimento porque permitem uma sincronização bidirecional eficiente entre a máquina host e os containers.
Crie um novo arquivo docker-compose.yml na raiz da pasta da aplicação:
nano docker-compose.ymlUm arquivo típico docker-compose.yml começa com uma definição de versão, seguida por um nó services, sob o qual todos os serviços são definidos. Redes compartilhadas geralmente são definidas na parte inferior desse arquivo.
Para começar, copie este código base para o seu arquivo docker-compose.yml:
version: "3.9"
services:
networks:
travellist:
driver: bridgeAgora vamos editar o nó services para incluir os serviços app, db e nginx.
O Serviço app
O serviço app configurará um contêiner chamado travellist-app. Ele cria uma nova imagem Docker com base em um Dockerfile localizado no mesmo caminho do arquivo docker-compose.yml. A nova imagem será salva localmente com o nome travellist.
Apesar de a raiz do documento que está sendo servida como a aplicação estar localizada no contêiner nginx, precisamos que os arquivos da aplicação também estejam em algum lugar dentro do contêiner app, para que possamos executar tarefas de linha de comando com a ferramenta Laravel Artisan.
Copie a seguinte definição de serviço sob o seu nó services, dentro do arquivo 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:
- travellistEstas configurações fazem o seguinte:
build: Esta configuração informa ao Docker Compose para construir uma imagem local para o serviçoapp, usando o caminho especificado (contexto) e o Dockerfile para instruções. Os argumentosusereuidsão inseridos no Dockerfile para personalizar os comandos de criação de usuário no momento da construção.imagem: O nome que será usado para a imagem que está sendo criada.container_name: Define o nome do contêiner para este serviço.restart: Sempre reinicie, a menos que o serviço esteja parado.working_dir: Define o diretório padrão para este serviço como/var/www.volumes: Cria um volume compartilhado que irá sincronizar o conteúdo do diretório atual com/var/wwwdentro do container. Observe que este não é o seu diretório raiz de documentos, já que ele estará no containernginx.networks: Configura este serviço para usar uma rede chamadatravellist.
O serviço db
O serviço db utiliza uma imagem pré-construída do MySQL 8.0 do Docker Hub. Como o Docker Compose carrega automaticamente os arquivos de variáveis .env localizados no mesmo diretório do arquivo docker-compose.yml, podemos obter nossas configurações de banco de dados do arquivo .env do Laravel que criamos em uma etapa anterior.
Inclua a seguinte definição de serviço no seu nó services, logo após o serviço 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:
- travellistEstas configurações fazem o seguinte:
image: Define a imagem do Docker que deve ser usada para este contêiner. Neste caso, estamos usando uma imagem MySQL 8.0 do Docker Hub.container_name: Define o nome do contêiner para este serviço:travellist-db.restart: Sempre reinicie este serviço, a menos que ele seja explicitamente parado.environment: Define variáveis de ambiente no novo contêiner. Estamos usando valores obtidos do arquivo.envdo Laravel para configurar nosso serviço MySQL, que criará automaticamente um novo banco de dados e usuário com base nas variáveis de ambiente fornecidas.volumes: Cria um volume para compartilhar um dump de banco de dados.sqlque será usado para inicializar o banco de dados da aplicação. A imagem do MySQL importará automaticamente os arquivos.sqlcolocados no diretório/docker-entrypoint-initdb.ddentro do container.networks: Configura este serviço para usar uma rede chamadatravellist.
O serviço nginx
O serviço nginx utiliza uma imagem Nginx pré-construída baseada no Alpine, uma distribuição Linux leve. Ele cria um contêiner chamado travellist-nginx e utiliza a definição ports para criar um redirecionamento da porta 8000 no sistema hospedeiro para a porta 80 dentro do contêiner.
Inclua a seguinte definição de serviço no seu nó services, logo após o serviço 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:
- travellistEstas configurações fazem o seguinte:
image: Define a imagem Docker que deve ser usada para este contêiner. Neste caso, estamos usando a imagem Alpine Nginx 1.17.container_name: Define o nome do container para este serviço: travellist-nginx.restart: Sempre reinicie este serviço, a menos que ele seja explicitamente parado.ports: Configura uma redireção de porta que permitirá o acesso externo pela porta8000ao servidor web que está rodando na porta80dentro do contêiner.volumes: Cria dois volumes compartilhados. O primeiro irá sincronizar os conteúdos do diretório atual com/var/wwwdentro do contêiner. Dessa forma, quando você fizer alterações locais nos arquivos da aplicação, elas serão rapidamente refletidas na aplicação servida pelo Nginx dentro do contêiner. O segundo volume garantirá que nosso arquivo de configuração do Nginx, localizado emdocker-compose/nginx/travellist.conf, seja copiado para a pasta de configuração do Nginx do contêiner.networks: Configura este serviço para usar uma rede chamadatravellist.
Arquivo docker-compose.yml concluído
É assim que nosso arquivo docker-compose.yml final fica:
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: bridgeCertifique-se de salvar o arquivo quando terminar.
Passo 6 — Executando a Aplicação com Docker Compose
Agora usaremos os comandos docker compose para construir a imagem da aplicação e executar os serviços que especificamos em nossa configuração.
Construa a imagem app com o seguinte comando:
docker compose build appEste comando pode levar alguns minutos para ser concluído. Você verá uma saída semelhante a esta:
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:latestQuando a build estiver concluída, você pode executar o ambiente em modo de segundo plano com:
docker compose up -dCreating travellist-db ... done
Creating travellist-app ... done
Creating travellist-nginx ... doneIsso fará com que seus contêineres sejam executados em segundo plano. Para mostrar informações sobre o estado dos seus serviços ativos, execute:
docker compose psVocê verá uma saída assim:
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/tcpSeu ambiente está agora ativo e funcionando.
Agora você pode verificar se cada serviço em contêiner está respondendo corretamente usando comandos simples de curl.
Para testar o serviço web Nginx:
curl -I http://localhost:8000Saída esperada:
HTTP/1.1 200 OK Server: nginx/1.17.10 Content-Type: text/html; charset=UTF-8Isso confirma que o contêiner Nginx está em execução e respondendo a solicitações da sua aplicação Laravel.
Para testar o PHP-FPM através do Nginx:
curl -s http://localhost:8000 | grep "Berlin"Se o aplicativo estiver funcionando corretamente, este comando retorna o HTML da página de boas-vindas que inclui a palavra Berlin.
Ainda precisamos executar alguns comandos para finalizar a configuração do aplicativo. Você pode usar o comando docker compose exec para executar comandos nos contêineres do serviço, como um ls -l para mostrar informações detalhadas sobre os arquivos no diretório do aplicativo:
docker compose exec app ls -ltotal 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.jsAgora vamos executar composer install para instalar as dependências da aplicação:
docker compose exec app rm -rf vendor composer.lock
docker compose exec app composer installVocê verá uma saída assim:
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.A última coisa que precisamos fazer antes de testar o aplicativo é gerar uma chave única do aplicativo com a ferramenta de linha de comando artisan do Laravel. Essa chave é usada para criptografar sessões de usuários e outros dados sensíveis:
docker compose exec app php artisan key:generateApplication key set successfully.Agora vá para o seu navegador e acesse o nome de domínio ou o endereço IP do seu servidor na porta 8000:
http://server_domain_or_IP:8000Nota: Caso você esteja executando esta demonstração em sua máquina local, use http://localhost:8000 para acessar o aplicativo pelo seu navegador.
Você verá uma página assim:
Você também pode testar a aplicação Laravel de dentro do contêiner do app sem usar um navegador:
docker compose exec app curl http://nginxSe tudo estiver configurado corretamente, você deverá ver a saída HTML da página de boas-vindas padrão do 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>Você pode usar o comando logs para verificar os registros gerados pelos seus serviços:
docker compose logs nginxAttaching 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"Você também pode usar o seguinte comando curl para acionar entradas de log:
curl http://localhost:8000Então execute novamente:
docker compose logs nginxVocê agora deve ver uma entrada correspondente no registro de acesso para sua solicitação.
Se você quiser pausar seu ambiente Docker Compose mantendo o estado de todos os seus serviços, execute:
docker compose pausePausing travellist-db ... done
Pausing travellist-nginx ... done
Pausing travellist-app ... doneVocê pode então retomar seus serviços com:
docker compose unpauseUnpausing travellist-app ... done
Unpausing travellist-nginx ... done
Unpausing travellist-db ... donePara desligar seu ambiente Docker Compose e remover todos os seus containers, redes e volumes, execute:
docker compose downStopping 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_travellistPara uma visão geral de todos os comandos do Docker Compose, consulte a referência de linha de comando do Docker Compose.
Corrigindo Permissões de Arquivos e Diretórios no Laravel
Ao executar o Laravel dentro de contêineres Docker, um dos problemas mais comuns que os desenvolvedores enfrentam está relacionado às permissões de arquivos e diretórios. O Laravel exige que certos diretórios sejam graváveis pelo processo PHP-FPM dentro do contêiner app. Se esses diretórios não forem graváveis, a aplicação pode falhar ao registrar erros, armazenar arquivos de configuração em cache ou salvar dados de sessão corretamente.
Você pode encontrar mensagens como:
The stream or file "/var/www/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission deniedou
file_put_contents(/var/www/bootstrap/cache/config.php): Failed to open stream: Permission deniedEsses erros indicam que o usuário que está executando o PHP dentro do contêiner não tem permissão para escrever nos diretórios necessários.
Os passos a seguir descrevem como diagnosticar e corrigir esses problemas tanto em ambientes locais quanto de produção.
Ajustar permissões do diretório gravável
O Laravel requer que dois diretórios sejam graváveis pelo usuário do servidor web:
/var/www/storage/var/www/bootstrap/cache
Se você vir erros de permissão, pode corrigi-los ajustando a propriedade e as permissões de dentro do contêiner:
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/cacheSe o seu Dockerfile usar um nome de usuário diferente (definido através do argumento de build user), substitua sammy por esse nome de usuário.
Você pode verificar a propriedade com o seguinte comando:
docker compose exec app ls -ld /var/www/storage /var/www/bootstrap/cacheA saída deve mostrar que o usuário e o grupo correspondem ao processo PHP-FPM dentro do contêiner.
Se você quiser ajustar as permissões a partir da máquina host, execute:
sudo chown -R $(id -u):$(id -g) storage bootstrap/cache
sudo chmod -R 775 storage bootstrap/cacheIsso garante que ambos os diretórios do Laravel possam ser gravados pelo contêiner e pelo usuário do seu host.
Definir a Propriedade do Diretório Durante a Construção da Imagem
Para evitar que problemas de permissão ocorram após cada reconstrução, você pode definir a propriedade do diretório durante o processo de construção da imagem Docker. Abra seu Dockerfile e adicione a seguinte linha perto do final, antes de mudar para o usuário não-root:
RUN chown -R $user:www-data /var/wwwEste comando garante que todos os arquivos e diretórios sob /var/www sejam de propriedade do usuário que você definiu em seu Dockerfile.
Para uma abordagem mais direcionada e segura, você pode ajustar apenas os diretórios que o Laravel precisa escrever:
RUN chown -R $user:www-data /var/www/storage /var/www/bootstrap/cacheApós fazer essa alteração, reconstrua a imagem e reinicie os contêineres:
docker compose build app
docker compose up -dNa próxima vez que o contêiner iniciar, ele já terá as permissões corretas.
Verificar a Consistência do ID de Usuário Entre o Host e o Contêiner
Ao usar bind mounts, o Docker compartilha arquivos entre seu host e o contêiner. Se o UID numérico do usuário do host for diferente do UID definido no contêiner, podem ocorrer conflitos de propriedade dos arquivos. Isso é comum em sistemas onde os IDs de usuário variam entre máquinas.
Para verificar os valores UID, execute:
id -u $(whoami)
docker compose exec app id -uSe esses dois números não coincidirem, atualize seu arquivo docker-compose.yml para passar seu UID real para o processo de build:
build:
args:
user: sammy
uid: 1000Então reconstrua a imagem:
docker compose build app
docker compose up -dIsso garante que o usuário do contêiner corresponda ao seu usuário do host, evitando incompatibilidades de permissão em arquivos montados por vínculo.
Corrigir as permissões do diretório de cache do Composer
O Composer armazena dependências baixadas e arquivos de cache no diretório home do usuário dentro do contêiner (/home/sammy/.composer). Se esse diretório não for gravável, você pode ver avisos como:
Cannot create cache directory /home/sammy/.composer/cacheVocê pode resolver isso criando o diretório de cache e atribuindo a propriedade correta:
docker compose exec app mkdir -p /home/sammy/.composer/cache
docker compose exec app chown -R sammy:sammy /home/sammy/.composerIsso garante que o Composer possa armazenar dependências e arquivos em cache sem erros de permissão, o que também acelera futuras execuções de composer install.
Práticas Recomendadas para Ambientes de Produção
Em produção, o gerenciamento de permissões deve ser feito durante a construção da imagem, em vez de em tempo de execução. Isso melhora a consistência e a segurança.
Siga estas práticas para uma configuração pronta para produção:
Evite bind mounts para código de aplicação: Em vez disso, copie os arquivos da aplicação para a imagem durante a construção. Bind mounts devem ser reservados para desenvolvimento.
Definir permissões durante a construção: Adicione os comandos
RUN chownechmodao seu Dockerfile para garantir a propriedade e os direitos de acesso corretos antes do deployment.Use o usuário com menos privilégios: Garanta que o processo PHP seja executado sob um usuário que não seja root (a configuração atual com
$userjá faz isso).Restringir permissões em diretórios não graváveis: A maioria dos arquivos de aplicativos deve ser somente leitura. Você pode aplicar isso com:
chmod -R 755 /var/www chmod -R 775 /var/www/storage /var/www/bootstrap/cacheEssa configuração permite que o Laravel escreva apenas onde é necessário, enquanto protege outros arquivos da aplicação contra modificações.
Seguindo estes passos, seus containers Laravel terão permissões de arquivos estáveis e previsíveis, tanto em ambientes locais quanto de produção. Isso reduz erros recorrentes e garante que o Laravel possa registrar dados, armazenar arquivos em cache e escrever sessões de forma confiável.
Entendendo docker-compose.override.yml para Ambientes Locais e de Produção
O Docker Compose permite que você defina aplicativos com múltiplos contêineres usando um único arquivo de configuração. Na maioria dos casos, essa configuração é armazenada em um arquivo chamado docker-compose.yml. No entanto, gerenciar múltiplos ambientes, como desenvolvimento local e produção, muitas vezes requer ajustes específicos para cada ambiente. É aí que o arquivo docker-compose.override.yml se torna útil.
Por padrão, o Docker Compose procura automaticamente um arquivo chamado docker-compose.override.yml no mesmo diretório do arquivo base do Compose. Quando encontrado, ele mescla as configurações de ambos os arquivos ao executar qualquer comando docker compose. Essa abordagem permite que os desenvolvedores mantenham uma configuração base limpa e reutilizável, enquanto personalizam aspectos específicos do ambiente para uso local ou em produção.
O Papel do docker-compose.override.yml
O arquivo docker-compose.override.yml é uma extensão da configuração base do Compose. Ele modifica ou adiciona propriedades que se aplicam apenas a um ambiente específico, sem a necessidade de alterar o arquivo principal docker-compose.yml. O arquivo de sobrescrição é especialmente útil em fluxos de trabalho de desenvolvimento local, onde os desenvolvedores frequentemente precisam montar o código-fonte, habilitar ferramentas de depuração ou modificar variáveis de ambiente para testes.
Quando você executa o comando docker compose up, o Docker mescla automaticamente o conteúdo de docker-compose.yml e docker-compose.override.yml. Se um serviço, volume ou configuração estiver definido em ambos os arquivos, os valores no arquivo de sobreposição têm precedência.
Para usar um arquivo de substituição diferente, por exemplo, um destinado à produção, você pode especificar o arquivo manualmente usando a flag -f:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -dEssa abordagem permite que você mantenha configurações separadas para diferentes ambientes enquanto utiliza uma única estrutura de comando consistente.
Estruturando para o Desenvolvimento Local
Ambientes de desenvolvimento local geralmente exigem mudanças frequentes, recarregamento de código em tempo real e registros detalhados. O arquivo docker-compose.override.yml torna possível introduzir essas funcionalidades sem afetar outros ambientes.
Um arquivo de substituição local típico pode incluir:
- Montagens bind para atualizações de código em tempo real
- Diretivas de build locais em vez de usar imagens pré-construídas
- Variáveis de ambiente específicas de desenvolvimento
- Serviços de depuração e testes
Por exemplo:
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=postgresNeste exemplo:
- O serviço
appé construído diretamente a partir de um Dockerfile local (Dockerfile.dev), facilitando o teste de alterações sem precisar reconstruir as imagens por meio de um registro. - O código-fonte é montado usando bind mounts, permitindo que o container reflita alterações nos arquivos locais em tempo real.
- Variáveis de ambiente, como
LOG_LEVEL=debug, são configuradas para fornecer uma saída mais detalhada durante o desenvolvimento. - O banco de dados utiliza credenciais simples que são adequadas para testes locais, mas não devem ser usadas em produção.
Esta configuração oferece um ambiente de desenvolvimento mais rápido e flexível, mantendo a configuração base inalterada.
Preparando para Produção
Ambientes de produção exigem estabilidade, reprodutibilidade e segurança. Contêineres devem ser executados a partir de imagens pré-construídas ao invés de serem construídos localmente, e informações sensíveis devem ser gerenciadas de forma segura. Diferentemente do arquivo de substituição aplicado automaticamente usado para desenvolvimento local, as substituições de produção devem ser especificadas explicitamente.
Um arquivo de substituição de produção (docker-compose.prod.yml) geralmente é projetado para substituir etapas de construção locais por referências de imagem, usar volumes nomeados em vez de montagens de ligação e definir propriedades relacionadas à implantação, como limites de recursos e escalonamento.
Exemplo:
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:Nesta configuração:
O serviço
appreferencia uma imagem pré-construída (myapp:1.2.3) armazenada em um registro, em vez de construir localmente.As montagens vinculadas são substituídas por volumes nomeados para garantir o armazenamento de dados consistente e isolado.
Credenciais sensíveis são armazenadas com segurança usando segredos do Docker.
A seção
deploydefine o comportamento de escalonamento e os limites de recursos para melhor estabilidade sob carga.A configuração deve ser aplicada explicitamente durante a implantação:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Essa estrutura garante que as implantações de produção sejam consistentes, controladas e livres de artefatos de desenvolvimento local.
Comparando Substituições Locais e de Produção
| Aspecto de Configuração | Local (docker-compose.override.yml) | Produção (docker-compose.prod.yml) |
|---|---|---|
| Aplicado automaticamente | Sim | Não (é necessário usar -f) |
| Manipulação de código-fonte | Montagens vinculadas para atualizações ao vivo | Imagens pré-construídas do registro |
| Variáveis de ambiente | Valores voltados para desenvolvedores | Valores seguros para produção |
| Registro | Verbose (debug) | Conciso (info ou error) |
| Gestão de segredos | Valores simples ou .env | Segredos do Docker ou segredos gerenciados |
| Escalonamento | Recipiente único | Múltiplas réplicas |
| Otimização de desempenho | Não obrigatório | Limites de recursos definidos |
| Ferramentas de depuração | Habilitado | Desativado |
| Política de reinício | Manual ou nenhum | |
Usar múltiplos arquivos Docker Compose fornece uma forma clara de separar as configurações específicas do ambiente da configuração base. O docker-compose.yml base define a estrutura comum da aplicação, enquanto o docker-compose.override.yml a ajusta para o desenvolvimento local. Ao implantar em produção, um arquivo de override explícito garante uma configuração previsível e segura.
Esse padrão permite que as equipes mantenham um fluxo de trabalho consistente. Os desenvolvedores podem iniciar a aplicação localmente usando:
docker compose upe implantar em produção usando:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -dAmbos os ambientes compartilham a mesma configuração central, enquanto mantêm configurações independentes adequadas aos seus requisitos específicos. Essa separação melhora a manutenção, reduz erros de configuração e mantém os ambientes local e de produção alinhados.
Solução de Problemas do Seu Ambiente
Esta seção fornece etapas de solução de problemas para problemas comuns que você pode encontrar ao executar seu aplicativo com o Docker Compose.
502 Gateway Inválido (Nginx mostra respostas 502)
Sintomas:
- O navegador mostra 502 Bad Gateway ao acessar
http://localhost:8000. - Os logs do Nginx contêm linhas como
recv() failed (111: Connection refused)ouconnect() failed (111: Connection refused) while connecting to upstream.
Causas prováveis:
- PHP-FPM (o serviço
app) não está em execução ou não está ouvindo no socket/porta esperada. - O
fastcgi_passdo Nginx aponta para o host/porta errados. - Nome do contêiner incompatível ou problemas de rede entre
nginxeapp. apptravou ao iniciar (por exemplo, devido a extensões ausentes ou erros de configuração).
Como confirmar:
Inspecionar os logs do Nginx:
docker compose logs nginx | tail -n 100Procure por mensagens
502ou erros de conexão com o upstream.Verifique o status e os logs do contêiner
app:docker compose ps app docker compose logs app --tail=200Confirme se o PHP-FPM está ouvindo dentro do contêiner
app:docker compose exec app ss -ltnp | grep :9000 || docker compose exec app ss -lxp | grep php-fpmEsperado: ou um listener TCP na porta
9000ou um socket Unix usado pelonginx(menos comum com este compose).Confirme se o
nginxconsegue resolver e alcançar oappna rede do compose:docker compose exec nginx ping -c 2 app docker compose exec nginx wget -qO- --timeout=2 http://app:9000/ || trueSe o
pingfalhar ou owgetexpirar, há um problema de rede ou uma incompatibilidade de nome de serviço.
Correções comuns:
Se o PHP-FPM não estiver em execução, verifique os logs do
appem busca de erros fatais do PHP ou falhas na inicialização. Corrija o erro subjacente (frequentemente uma extensão ausente ou problema de permissão) e reinicie:docker compose restart app docker compose logs app --tail=100Se a configuração do Nginx usar um upstream diferente (por exemplo,
fastcgi_pass 127.0.0.1:9000;), atualizedocker-compose/nginx/travellist.confparafastcgi_pass app:9000;(correspondendo ao nome do serviço do seu compose) e recarregue o Nginx:# After editing config in host docker compose exec nginx nginx -s reload docker compose logs nginx --tail=50Se o PHP-FPM estiver ouvindo em um socket Unix (por exemplo,
/run/php/php7.4-fpm.sock) mas o Nginx esperar TCP ou vice-versa, padronize para TCPapp:9000em configurações do Compose ou monte o socket corretamente entre os contêineres (recomendado: use TCP em Compose com múltiplos contêineres).
Extensões PHP faltando (erros ao executar o Composer ou Artisan, ou falhas em tempo de execução)
Sintomas:
- Comandos do Composer ou do Laravel falham com mensagens como
Classe "PDO" não encontrada,ext-pdo está ausenteouChamada para função indefinida json_encode. - Erros de PHP nos logs de
appreferenciando classes ou funções indefinidas.
Como confirmar:
Inspecione a lista de módulos PHP do contêiner
app:docker compose exec app php -m | sortProcure pelos nomes das extensões que você precisa (para extensões típicas do Laravel:
pdo_mysql,mbstring,bcmath,openssl,tokenizer,xml,ctype,json,gd).Verifique o texto de erro do composer, que geralmente indica as extensões necessárias:
docker compose exec app composer install
Correções:
Adicione as extensões que estão faltando em seu Dockerfile e reconstrua a imagem. Exemplo (para
pdo_mysqlegd):RUN apt-get update && apt-get install -y libpng-dev libxml2-dev \ && docker-php-ext-install pdo_mysql mbstring xml gdEntão reconstrua e reinicie:
docker compose build app docker compose up -d app docker compose logs app --tail=100Se uma extensão requer bibliotecas do sistema (por exemplo,
gdprecisa delibpng-dev), adicione as linhas correspondentes deapt-get installantes dedocker-php-ext-install.Se você precisar de uma extensão PHP que não seja nativa, considere instalá-la via PECL e habilitá-la no
php.ini.
Falhas na conexão com o banco de dados (Laravel não consegue conectar ao MySQL)
Sintomas:
- O Laravel exibe erros de conexão com o banco de dados (
SQLSTATE[HY000] [2002] Conexão recusadaouAcesso negado para o usuário). - As migrações falham ou o aplicativo mostra uma página em branco para páginas baseadas em dados.
Como confirmar:
Verifique os logs do contêiner do banco de dados para erros de inicialização:
docker compose logs db --tail=200Verifique se o contêiner do DB está ativo:
docker compose ps dbConfirme se as credenciais e o host são consistentes entre
.envedocker-compose.yml. No.envdo Laravel,DB_HOSTdeve ser o nome do serviço do compose (db), não127.0.0.1oulocalhost:DB_HOST=db DB_PORT=3306 DB_DATABASE=travellist DB_USERNAME=travellist_user DB_PASSWORD=passwordTeste de dentro do contêiner
db:docker compose exec db mysql -u"${DB_USERNAME}" -p"${DB_PASSWORD}" -e "SHOW DATABASES;"Ou, teste a partir do contêiner
apppara garantir a conectividade de rede: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();"'
Causas comuns e soluções:
Host incorreto em
.env: atualizeDB_HOST=db, recrie os containers ou recarregue os ambientes:docker compose down docker compose up -d docker compose exec app php artisan migrateBanco de dados não está pronto quando o app inicia: use um mecanismo de tentativas no seu app ou adicione um healthcheck e comece a sincronizar. Você também pode esperar manualmente:
docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping # expected: mysqld is aliveFalha na inicialização do SQL: verifique os arquivos em
./docker-compose/mysql, um erro de sintaxe ou de permissão pode interromper a inicialização do banco de dados. Verifique os logs do banco para erros de importação.Privilégios/credenciais incorretas: confirme se o banco de dados e o usuário existem. Recrie o BD ou execute SQL para conceder privilégios.
Uso de porta no nível do host: se você tentou
curl telnet://localhost:3306e o serviçodbnão expõe uma porta no host, essa conexão será recusada por design. Teste de dentro dos contêineres, ou exponha a porta nodocker-compose.yml(ports: - "3306:3306") para depuração apenas local.
Problemas de permissão e de sistema de arquivos (armazenamento, cache, arquivos de log)
Sintomas:
- O Laravel lança exceções relacionadas à escrita de cache ou logs:
O fluxo ou arquivo "/var/www/storage/logs/laravel.log" não pôde ser aberto. - Arquivos criados por processos de contêiner são de propriedade do
roote não podem ser modificados pelo usuário do seu host.
Como confirmar:
Verifique as permissões do arquivo no host:
ls -l ls -l storage bootstrap/cache docker compose exec app id -u # user id inside containerInspecione os logs do contêiner em busca de erros de permissão.
Correções:
Certifique-se de que o Dockerfile define o usuário e o UID que correspondem ao seu usuário do host (seu Dockerfile já cria um usuário com
ARG uid). Confirme se ouidnos argumentos de build dodocker-compose.ymlcorresponde ao UID do seu host (geralmente1000).Corrigir a propriedade a partir do host ou dentro do contêiner:
docker compose exec app chown -R sammy:sammy /var/www/storage /var/www/bootstrap/cacheou do anfitrião:
sudo chown -R $(id -u):$(id -g) storage bootstrap/cachePara desenvolvimento, montagens bind podem causar incompatibilidades de permissão; prefira executar comandos no contêiner para ajustar a propriedade após iniciar os contêineres.
Erros relacionados ao compositor ou fornecedor
Sintomas:
composer installfalha dentro deappou a ausência da pastavendorcausa erro de classe não encontrada.composerrelata esgotamento de memória no contêiner.
Como confirmar:
Execute o composer dentro do contêiner
appe capture a saída:docker compose exec app composer install --no-interaction --prefer-dist
Correções:
Se os limites de memória do
composerforem atingidos, permita mais memória ou defina a variável de ambiente do composer:docker compose exec app php -d memory_limit=-1 /usr/bin/composer installCertifique-se de que o executável do Composer está presente (seu Dockerfile copia o Composer). Caso não esteja, reconstrua a imagem com a camada do Composer.
Se o
vendorestiver ausente devido a um overlay de bind mount, certifique-se de que seu bind mount não está escondendo a pastavendorcriada na imagem. A melhor prática é executarcomposer installapós os containers estarem rodando, para que ovendorseja criado na pasta montada, ou adicionar ovendorao container via build da imagem para produção.
O aplicativo retorna página em branco ou erros 500
Sintomas:
- O navegador exibe uma resposta em branco ou HTTP 500.
- Os logs do Nginx mostram 200, mas a página está vazia; os logs do
appmostram exceções fatais.
Como confirmar:
Inspecionar logs do aplicativo:
docker compose exec app tail -n 200 storage/logs/laravel.log docker compose logs app --tail=200Verifique o ambiente:
APP_DEBUG=trueno.envfará o Laravel mostrar erros detalhados no desenvolvimento (não ative em produção).
Correções:
Corrija a exceção subjacente mostrada em
laravel.log.Certifique-se de que
.envexiste e queAPP_KEYestá definido:docker compose exec app php artisan key:generateExecutar migrações, limpar 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
Falhas na construção do container (erros durante docker compose build)
Sintomas:
- O
docker compose buildapresenta erros durante o apt ou nas etapas de build.
Como confirmar:
Reexecutar build com logs:
docker compose build app
Correções:
Leia a saída da etapa com falha. Problemas típicos:
- Rede/DNS no ambiente de build: tente novamente ou garanta que o Docker possa acessar a internet.
- Pacotes ausentes ou fonte do apt incorreta: adicione
apt-get updateantes das instalações e limpe as listas do apt depois. - Permissões em arquivos copiados para a imagem: garanta os caminhos corretos do
COPY.
Exemplo de trecho de Dockerfile para instalações robustas:
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/*Problemas de rede ou DNS entre contêineres
Sintomas:
- Um contêiner não consegue acessar outro pelo nome do serviço (por exemplo,
appnão consegue acessardbusandodb:3306). pingpelo nome do serviço falha.
Como confirmar:
docker compose exec app ping -c 3 db
docker compose exec app getent hosts db
docker network inspect travellistCorreções:
Confirme se os serviços compartilham a mesma rede em
docker-compose.yml(ambos devem referenciartravellist).Se você alterou os nomes dos serviços ou das redes, atualize
DB_HOSTe os upstreams do Nginx de acordo.Recriar rede (isso destrói contêineres efêmeros, então tenha cuidado):
docker compose down docker network prune # optional and destructive docker compose up -d
Comandos de diagnóstico úteis
Execute estes para inspeção direcionada:
Mostrar contêineres em execução e portas:
docker compose psExibir logs de um serviço:
docker compose logs -f --tail=200 nginxExecute um shell dentro de um contêiner:
docker compose exec app sh docker compose exec db bashInspecionar variáveis de ambiente do contêiner:
docker compose exec app env | sort docker compose exec db env | grep MYSQLVerificar lista de processos do contêiner:
docker compose exec app ps aux docker compose exec app ss -ltnpVerificar a saúde do MySQL:
docker compose exec db mysqladmin -u"${DB_USERNAME}" -p"${DB_PASSWORD}" ping
Quando expor portas do host para depuração
Por padrão, o serviço db nesta configuração não está vinculado às portas do host. Exponha portas apenas para depuração local. Para expor temporariamente o MySQL no host, adicione ao docker-compose.yml em db:
ports:
- "3306:3306"Então reinicie:
docker compose up -d dbApós a depuração, remova o mapeamento ports para evitar expor o banco de dados à rede.
Perguntas Frequentes
1. Eu preciso ter o Composer instalado no host?
Não. Você pode executar o Composer inteiramente dentro do contêiner do aplicativo, que é a abordagem recomendada para este fluxo de trabalho do Compose. O Dockerfile no tutorial inclui o Composer, então execute comandos como docker compose exec app composer install ou docker compose run --rm app composer require vendor/package. Executar o Composer no contêiner garante que a versão correta do PHP e as extensões sejam usadas, e evita gerar arquivos de vendor com propriedades incompatíveis no host.
2. Posso executar migrações do Laravel de dentro do contêiner?
Sim. Execute as migrações a partir do contêiner do aplicativo para que elas usem o mesmo tempo de execução e rede que sua aplicação: docker compose exec app php artisan migrate. Para tarefas únicas ou de CI, você pode usar docker compose run --rm app php artisan migrate --force. Antes de executar as migrações, certifique-se de que o contêiner do banco de dados está saudável e aceitando conexões, por exemplo, com docker compose exec db mysqladmin -u$ {DB_USERNAME} -p$ {DB_PASSWORD} ping ou tentando novamente até que haja resposta.
3. Como faço para instalar extensões PHP adicionais?
Adicione os pacotes de sistema necessários e os comandos de extensão ao Dockerfile, e então reconstrua a imagem. Por exemplo, instale dependências e construa extensões comuns com algo como:
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 gdSe você precisar de uma extensão PECL, use pecl install e docker-php-ext-enable. Depois de editar o Dockerfile, execute docker compose build app e então docker compose up -d app para aplicar as alterações.
4. Como faço para expor o contêiner Nginx para HTTPS?
Adicione um bloco de servidor HTTPS à sua configuração do Nginx, monte seu certificado e chave no contêiner e publique a porta 443 no docker-compose. Aqui está um exemplo com alterações mínimas em docker-compose.yml:
services:
nginx:
ports:
- "8000:80"
- "443:443"
volumes:
- ./docker-compose/nginx:/etc/nginx/conf.d
- ./certs:/etc/nginx/certs:roEm seguida, adicione um bloco de servidor SSL que faça referência a /etc/nginx/certs/fullchain.pem e /etc/nginx/certs/privkey.pem. Para produção, use um proxy reverso ou um gerenciador de certificados, como Traefik ou um balanceador de carga externo, para obter e renovar certificados de forma segura, em vez de armazenar chaves privadas na pilha do aplicativo.
5. Como faço para reconstruir o Laravel após alterações no código?
Depende do que mudou. Se você estiver usando bind mounts para o diretório do projeto, as alterações em PHP, templates, rotas e controladores são refletidas imediatamente; execute os comandos de limpeza de cache do Laravel, se necessário, por exemplo:
docker compose exec app php artisan config:clear
docker compose exec app php artisan route:clear
docker compose exec app php artisan view:clearSe você alterou dependências do Composer ou o Dockerfile (novas extensões ou pacotes do sistema), reinstale ou reconstrua: docker compose exec app composer install para alterações de dependências, e docker compose build app && docker compose up -d para alterações no Dockerfile. Reinicie o serviço afetado quando você alterar a configuração do PHP-FPM ou Nginx: docker compose restart app ou docker compose restart nginx.
Conclusão
Neste guia, configuramos um ambiente Docker com três containers usando o Docker Compose para definir nossa infraestrutura em um arquivo YAML.
A partir deste ponto, você pode trabalhar em sua aplicação Laravel sem precisar instalar e configurar um servidor web local para desenvolvimento e testes. Além disso, você estará trabalhando com um ambiente descartável que pode ser facilmente replicado e distribuído, o que pode ser útil durante o desenvolvimento da sua aplicação e também na transição para um ambiente de produção.
Para mais tutoriais de Laravel, confira os seguintes artigos:
- Hospede seu app Laravel no Linux-Console.net
- Como Instalar e Configurar o Laravel com Nginx no Ubuntu (Stack LEMP)
- Como Criar Recursos de API Eloquent no Laravel para Converter Models em JSON
- Como Iniciar uma Nova Aplicação Laravel com Docker Compose
Obrigado por aprender com a comunidade Linux-Console.net.

