Voltar ao blog

Implantando Laravel, Nginx e MySQL com Docker Compose

Implantando Laravel, Nginx e MySQL com Docker Compose

Introdução

A Integração Contínua (CI) e a Implantação Contínua (CD) são alguns dos tópicos mais populares no desenvolvimento de software atualmente. Para alcançar o aspecto de CI/CD da arquitetura de software, os desenvolvedores fazem uso de contêineres. Contêineres são ambientes leves, virtualizados, portáteis e definidos por software. Nos contêineres, o software pode ser executado de forma isolada de outros softwares em execução na máquina host física. O foco deste tutorial é o uso da plataforma de contêiner Docker para implantar e executar aplicações web. O Docker ajuda a simplificar o processo de configuração de uma pilha de servidores web. Neste tutorial, usaremos a pilha LEMP para servir uma aplicação Laravel.

A pilha LEMP combina o Linux como sistema operacional, Nginx como servidor web, MySQL como banco de dados e a linguagem PHP para scripts e processamento dinâmico. Você pode seguir o nosso tutorial sobre como instalar e configurar uma pilha LEMP no Ubuntu. O Laravel é um dos principais frameworks PHP para o desenvolvimento de aplicações web.

O Docker fornece uma ferramenta chamada Docker Compose para definir o processo de configuração de um contêiner Docker. O Docker Compose permite que os desenvolvedores definam a infraestrutura de sua aplicação, serviços, volumes, redes e quaisquer dependências em um único arquivo chamado arquivo docker-compose. Ele pode gerenciar múltiplos contêineres Docker por meio de seus comandos, como docker container create, docker container run, etc.

Neste tutorial, você aprenderá como implantar uma aplicação web Laravel com Nginx e MySQL dentro de um contêiner. As configurações para toda a pilha serão definidas dentro de um arquivo docker-compose, bem como outros arquivos de configuração para PHP, MySQL e Nginx. Vamos começar!

Primeiro o Mais Importante

Passo 1: Baixar o Laravel e Instalar as Dependências

O primeiro passo é obter o código do Laravel de um repositório. Em cenários reais, você pode ter seu código Laravel em um repositório em algum lugar, ou seja, no GitHub, Bitbucket, Gitlab, etc. No entanto, para fins deste tutorial, clonaremos a versão mais recente do repositório oficial do Laravel no GitHub. O repositório vem com um arquivo composer, um gerenciador de dependências em nível de aplicação para PHP. Como queremos que tudo seja executado dentro do contêiner Docker, instalaremos as dependências usando a imagem do composer do Docker. Isso também nos ajudará a evitar a necessidade de instalar o composer globalmente na máquina host real. Em seguida, abra seu terminal.

Mude para o seu diretório home:

Insira o seguinte comando para clonar o repositório em um diretório chamado laravel-web. Você é livre para nomeá-lo como quiser. No momento da redação deste repositório, quando executamos este comando, ele baixa a versão 8 do Laravel. Quando você executar o comando, provavelmente encontrará uma nova versão:

Em seguida, mude para o diretório no qual você acabou de clonar o repositório:

Insira o seguinte comando para montar os diretórios necessários para sua aplicação Laravel usando a imagem do composer do Docker:

As flags -v e –rm no comando docker run criam um contêiner efêmero que será montado por vínculo ao diretório atual antes de ser removido. O comando copia o conteúdo de ~/laravel-web para dentro do contêiner e garante que a pasta vendor criada pelo composer seja copiada de volta para o diretório atual.

Agora, você precisa alterar a propriedade do diretório laravel-web para o usuário não-root. Isso permitirá trabalhar com o código da sua aplicação como um usuário não-root e executar processos dentro do container nas etapas subsequentes. Insira o seguinte comando para alterar a propriedade:

O código da sua aplicação agora está no lugar. O diretório pertence a um usuário não-root, então você pode prosseguir com a definição dos serviços da aplicação no arquivo docker-compose.

Passo 2: Criar o Arquivo Docker Compose

O Docker Compose simplifica o processo de construção e implantação de uma aplicação. Depois de definir as configurações e serviços, você pode implantar facilmente sua aplicação em qualquer máquina hospedeira que tenha o Docker e o Docker Compose instalados, sem se preocupar com as dependências da aplicação. O mais importante é que você pode fazer isso com apenas um comando do Docker Compose, como veremos no Passo 9.

Neste passo, você definirá um arquivo Docker Compose com configurações para o servidor web, banco de dados e serviços de aplicação necessários para implantar um aplicativo Laravel.

Os arquivos do Docker Compose são YAML arquivos salvos com a extensão .yml. Observe que a indentação correta é necessária para um arquivo Docker Compose válido. Insira o seguinte comando para criar e abrir o arquivo com o nano para edição:

Em seguida, você definirá três serviços neste arquivo: app, webserver e db. A seção db define as credenciais do banco de dados para sua aplicação, portanto, certifique-se de escolher uma mysql_root_password forte e substituí-la nessa seção. Copie e cole o seguinte código:

Abaixo, seguem as explicações das definições dos serviços do código acima:

  • app: Define a aplicação Laravel e executa uma imagem Docker personalizada, cloudsigma.com/php, que definiremos no Passo 4. Ele também define o working_dir no container para /var/www/html.
  • webserver: Ele baixa a imagem nginx:alpine do Docker e expõe as portas 80 e 443.
  • db: Ele baixa a imagem imagem mysql:5.7.32 do Docker e define algumas variáveis de ambiente. Elas incluem um banco de dados chamado laravel_web para a aplicação e a senha root para o banco de dados. Você pode renomear o banco de dados com um nome de sua escolha. Lembre-se de substituir a propriedade MYSQL_ROOT_PASSWORD por uma senha forte. Este serviço também mapeia a porta 3306 no host para a porta 3306 no container.

A propriedade container_name em cada serviço define um nome para o contêiner correspondente ao serviço. Se você não definir a propriedade, o Docker escolhe um nome aleatório para cada contêiner.

A propriedade networks define uma rede bridge chamada app-network que facilita a comunicação entre contêineres. Uma rede bridge é controlada por uma ponte de software que apenas permite a comunicação entre contêineres na mesma ponte de rede. O controlador de software da ponte instala drivers que impedem que contêineres em redes bridge diferentes se comuniquem diretamente entre si. Isso garante um alto nível de segurança, pois apenas serviços relacionados podem se comunicar diretamente. Você pode optar por definir múltiplos serviços e redes conectando-se a funções relacionadas.

Passo 3: Como Persistir Dados

Seu aplicativo web lidará com dados e os servirá aos seus usuários. Nesta etapa, guiaremos você na definição de volumes e bind mounts para as definições de seus serviços para persistir os dados do aplicativo. O Docker oferece recursos incríveis, como bind mounts e volumes para persistir dados e salvar arquivos de configuração do aplicativo. Nós os usaremos na configuração do nosso aplicativo Laravel com o Docker.

Os volumes são preferidos por vários motivos, incluindo a oferta de backups e a persistência de dados além do ciclo de vida de um contêiner. Os Bind Mounts geralmente referenciam um diretório real na máquina host. Quando você cria um volume, um novo diretório é criado dentro do diretório de armazenamento do Docker, que é gerenciado pelo Docker. Quando você cria um bind mount, um arquivo ou diretório dentro da máquina host é montado em um contêiner (referenciado por seu caminho absoluto). Isso é crucial para o nosso aplicativo web porque, quando você faz alterações no código na máquina host, elas ficam imediatamente disponíveis para o contêiner.

Tenha cuidado ao usar bind mounts. Os processos em execução dentro do contêiner Docker podem fazer alterações no sistema de arquivos do host e afetar processos não-Docker em execução no sistema host. Embora os mounts do Docker sejam um recurso poderoso, esteja ciente dessas implicações de segurança.

Dito isso, vamos ver como podemos usar esses dois recursos em nossa configuração. Primeiro, definiremos um volume para persistir o banco de dados MySQL. No arquivo Docker Compose que criamos, sob o serviço db, adicione uma propriedade volumes conforme destacado abaixo:

Conforme definido, o volume dbdata persistirá o conteúdo de /var/lib/mysql. Ele facilita backups e permite reinicializações do serviço sem perder os dados. Em seguida, você deve adicionar a definição de volumes no final do arquivo Docker Compose para torná-la disponível entre os serviços. Insira o seguinte trecho de código na parte inferior do arquivo:

Para se conectar a um banco de dados MySQL, você deve fornecer credenciais. Para fazer isso, defina um bind mount adicionando o seguinte trecho de código destacado ao serviço db sob a propriedade volumes:

O código vincula ~/laravel-web/mysql/my.cnf a /etc/mysql/my.cnf no contêiner. O arquivo vinculado é o arquivo de configuração do MySQL que criaremos no Passo 7.

O contêiner precisa usar o servidor Nginx para servir o código do seu aplicativo. Portanto, definiremos dois bind mounts (um para o arquivo de configuração do Nginx e outro para o código do aplicativo) sob o serviço webserver para essa finalidade. Adicione o seguinte trecho de código para a definição de volumes sob o serviço webserver:

Esta linha – ./:/var/www/html vincula o código da aplicação no diretório ~/laravel-web ao diretório /var/www/html dentro do container. Para a segunda montagem de vínculo, um arquivo de configuração para o Nginx será criado em ~/laravel-web/nginx/conf.d/. Ele será montado em /etc/nginx/conf.d/ dentro do container. Portanto, você pode atualizar o arquivo de configuração na máquina hospedeira conforme necessário. Criaremos o arquivo de configuração do Nginx no Passo 6.

Para que as alterações no código sejam refletidas automaticamente no container, vinculamos o código da aplicação ao container por meio de montagem de vínculo. Isso acelera o processo de implantação. Portanto, adicione o seguinte trecho de código destacado ao serviço app:

A segunda linha vincula um arquivo de configuração do php, que criaremos no Passo 5 dentro do arquivo ~/laravel-web/php/laravel.ini para /usr/local/etc/php/conf.d/laravel.ini dentro do container.

Seu arquivo Docker Compose completo deve agora se parecer com isto:

Se tudo parecer correto, pressione Ctrl + O para salvar o arquivo. Em seguida, pressione Ctrl + X para sair do editor. Neste ponto, você deve ser capaz de construir uma imagem Docker personalizada para sua aplicação com o arquivo Docker Compose.

Passo 4: Criar o Dockerfile

O Dockerfile inclui instruções que o Docker pode usar para construir imagens Docker personalizadas. Ele também pode instalar o software necessário e definir as configurações necessárias para sua aplicação. Eles especificam o ambiente dentro de um container que hospedará o código da sua aplicação. Você pode enviar as imagens que criar para o docker hub para compartilhamento ou colocá-las em outros registros privados.

Criaremos um Dockerfile que especificará as instruções para construir a imagem da aplicação Laravel. Use o nano para criar o Dockerfile no diretório ~/laravel-web:

No editor aberto, adicione o seguinte código:

O Dockerfile primeiro cria uma imagem baseada na php:7.4-fpm imagem Docker. Esta é uma imagem baseada no Debian com a implementação PHP FastCGI (PHP-FPM instalada. Para que o Laravel funcione corretamente, ele requer que outras extensões php como mcrypt, pdo_mysql, mbstring e imagick estejam disponíveis, as quais o script instala. Em seguida, ele instala o composer gerenciador de pacotes php. O contêiner o usará para instalar as dependências php do Laravel.

Você pode usar a diretiva RUN para definir comandos como instalar, atualizar e definir configurações dentro do contêiner. Ela também atribui permissões de usuário. A diretiva WORKDIR especifica o diretório de trabalho, /var/www/html neste caso. O script executa o comando CHOWN para atribuir as permissões do diretório /var/www/html ao usuário www-data.

Antes de finalmente construir a imagem, uma porta deve ser exposta para permitir o acesso à aplicação em execução dentro do contêiner. O comando EXPOSE expõe uma porta, 9000, para o servidor php-fpm. O comando final a ser executado é a diretiva CMD. Ela executa o php-fpm para iniciar o servidor.

Agora você pode pressionar Ctrl + O para salvar o arquivo. Em seguida, pressione Ctrl + X para sair do editor.

Passo 5: Configurar o PHP

Neste passo, vamos configurar o serviço php para processar as requisições recebidas do Nginx. Você criará um arquivo laravel.ini dentro do diretório php. Este arquivo conterá as configurações do PHP. Este é o arquivo que você montou via bind para /usr/local/etc/php/conf.d/laravel.ini no contêiner no Passo 3. As configurações neste arquivo substituem o arquivo php.ini padrão normalmente lido pelo PHP quando ele inicia. Insira o seguinte comando para criar o diretório php:

Crie e abra o arquivo laravel.ini dentro do diretório php inserindo o seguinte comando:

O arquivo php.ini padrão tem um limite de upload definido para 2M. Como exemplo, mostraremos como ajustar e definir as configurações do php alterando o valor do limite de upload permitido, caso você queira fazer upload de arquivos maiores. Insira as seguintes linhas de código dentro do arquivo:

Isso define o limite de upload e você poderá fazer upload de arquivos com um tamanho total de no máximo 80MB. Você pode adicionar outras configurações do php dentro do arquivo laravel.ini para substituir as configurações padrão do php. Agora, salve e feche o arquivo.

Passo 6: Configurar o Nginx

Neste passo, vamos configurar o Nginx para usar o serviço php que definimos anteriormente. Ele usará o PHP-FPM como o servidor FastCGI para servir conteúdo dinâmico. O servidor FastCGI é um software que permite que programas interativos façam interface com um servidor web.

Como definimos no arquivo docker-compose no Passo 3, criaremos o arquivo de configuração do Nginx app.conf dentro do diretório ~/laravel-web/nginx/conf.d/. Primeiro, insira o seguinte comando para criar o diretório:

Em seguida, crie e abra o arquivo app.conf usando o nano inserindo o seguinte comando:

Adicione o seguinte código de configuração do Nginx ao arquivo:

O Nginx lê arquivos de configuração chamados blocos de servidor (server blocks) para saber qual diretório deve ser servido a um visitante do site com base na URL. Para saber mais, leia sobre a configuração de blocos de servidor em nosso tutorial sobre como instalar o Nginx no Ubuntu 18.04. As diretivas definidas servem aos seguintes propósitos:

  • listen – Define a porta na qual o servidor escutará as requisições recebidas, que geralmente é a porta 80.
  • error_log & access_log – Define os arquivos para gravação dos logs da aplicação.
  • root – Define o caminho raiz da web (webroot), o diretório que servirá qualquer requisição feita ao servidor a partir da internet.

In the location block for php, the fastcgi_pass directive specifies that the app service is listening on a TCP socket on port 9000 (which was defined in the Dockerfile). This directs the PHP-FPM server to listen over the network and not on a Unix socket. While a Unix socket may have a slight advantage in speed over a TCP socket, it lacks a network protocol hence skipping the network stack.

Um socket Unix seria mais apropriado para cenários onde os hosts estão localizados em uma única máquina. No entanto, se você tiver serviços rodando em hosts diferentes, um socket TCP tem a vantagem de conectar serviços que estão distribuídos. No nosso caso, o container da aplicação está rodando em um host diferente do container do nosso servidor web. Assim, um socket TCP é o mais apropriado para a nossa configuração.

Agora você pode pressionar Ctrl + O para salvar o arquivo e, em seguida, pressionar Ctrl + X para sair do editor. As alterações feitas no diretório nginx/conf.d/ serão refletidas automaticamente no container do servidor web graças ao bind mount que você adicionou no arquivo Docker Compose no Passo 3.

Passo 7: Configurar o MySQL

Depois de configurarmos o Nginx para funcionar com o PHP, podemos agora configurar o MySQL para armazenar e servir dados dinâmicos para o PHP. Nós já havíamos configurado o arquivo Docker Compose para instalar as extensões necessárias para a comunicação entre PHP e MySQL. Criaremos o arquivo de configuração do MySQL my.cnf dentro da pasta mysql, que será montado via bind mount em /etc/mysql/my.cnf, conforme definimos na seção do serviço db do Docker Compose no Passo 3.

As configurações e alterações do MySQL podem ser feitas no arquivo my.cnf sempre que você desejar. Elas devem ser refletidas dentro do container imediatamente. Primeiro, crie o diretório inserindo o seguinte comando:

Em seguida, crie e abra o nano inserindo o seguinte comando:

Insira o seguinte trecho de código para habilitar o log de consultas e especificar o local do arquivo de log de consultas:

Ao definir a propriedade general_log como 1, você permite logs gerais. A propriedade general_log_file especifica o local do arquivo de logs. Pressione Ctrl + O para salvar o arquivo e, em seguida, pressione Ctrl + X para sair do editor.

Passo 8: Definir as Variáveis de Ambiente do Laravel

Até este ponto, todos os serviços e configurações estão concluídos. Assim, poderíamos implantar nossos containers. No entanto, há uma etapa importante que deve ser realizada antes que nossa aplicação web seja realmente utilizável – as variáveis de ambiente. O framework Laravel espera um arquivo chamado .env que ele usa para definir seu ambiente. Por padrão, o Laravel vem com o .env.example que você pode copiar para .env e, em seguida, modificar as variáveis com seus dados reais. Insira o seguinte comando para copiar o arquivo:

Depois de copiado, abra o arquivo usando o nano para modificar:

Aqui está uma captura de tela de como o arquivo deve se parecer:

screenshot of what the file

No arquivo, o próximo passo é modificar as variáveis sob o bloco DB_CONNECTION conforme você definiu nas configurações anteriores que fizemos até agora. Atualize da seguinte forma:

  • DB_HOST é o db container de banco de dados.
  • DB_DATABASE é o laravel_web.
  • DB_USERNAME é o nome de usuário para o banco de dados. Escolha um nome de sua preferência, mas para fins deste tutorial, vamos usar laraveldocker.
  • DB_PASSWORD é uma senha forte que o seu usuário acima usará para fazer login no banco de dados, portanto, escolha uma senha forte. No Passo 10, criaremos este usuário com a senha que você escolher aqui.

Com os valores atualizados, seu DB_CONNECTION deve se parecer com isto:

DB_CONNECTION

Save and close the file.

Passo 9: Executar os Containers Docker

Nesta etapa, todos os seus serviços e configurações estão definidos no arquivo Docker Compose. Apenas um comando é necessário para iniciar todos os containers, criar os volumes, conectar as redes e configurar e compilar sua aplicação. Insira o seguinte comando no seu terminal:

Quando você executa o comando docker-compose up pela primeira vez, ele baixa todas as imagens Docker necessárias. Se você estiver configurando a infraestrutura no seu computador local, isso pode levar algum tempo para ser concluído. Assim que as imagens forem baixadas, o Compose cria os contêineres. A flag -d instrui o Docker a executar os contêineres em segundo plano. Se o processo for concluído com sucesso, você deverá ver algo assim no seu terminal:

terminal

Insira o seguinte comando no seu terminal para listar todos os contêineres em execução:

Deve exibir algo como a captura de tela abaixo, com detalhes sobre os contêineres app, webserver e db:

screenshot details

  • CONTAINER ID – Um identificador exclusivo para cada contêiner.
  • NAMES – O nome do serviço associado a cada contêiner, conforme definido no arquivo Docker Compose. (Você pode usar o ID ou o nome do contêiner para acessá-lo).
  • IMAGE – O nome da imagem para cada contêiner.
  • STATUS – Exibe informações sobre o estado do contêiner (pode estar parado, em execução ou reiniciando).
  • PORTS – Mostra as portas que um contêiner está expondo.

O Docker Compose fornece um comando chamado exec que você pode usar para executar comandos de terminal ou acessar a linha de comando dentro de um contêiner. Primeiro, queremos executar alguns comandos dentro do contêiner app, que é o contêiner que executa o aplicativo Laravel.

O Docker fornece um comando para acessar a linha de comando de um contêiner. Sua sintaxe é a seguinte: docker-compose exec container_name bash. Para acessar a linha de comando do contêiner app, insira o seguinte comando:

Uma vez na linha de comando do contêiner, você pode executar alguns comandos de configuração do Laravel Artisan. Insira o seguinte comando para gerar a chave do laravel e salvá-la no arquivo .env:

Com a chave de ambiente definida, você pode executar o seguinte comando para armazenar as configurações em cache:

As configurações são armazenadas no arquivo /var/www/html/bootstrap/cache/config.php dentro do contêiner. Você pode pressionar Ctrl + D para sair do terminal do contêiner.

Para confirmar que o aplicativo Laravel foi implantado e está em execução, visite o IP público do seu servidor no seu navegador (http://your_server_public_ip). Você deverá ver a página de boas-vindas de uma nova instalação do Laravel:

laravel screenshot

Passo 10: Configurar um usuário MySQL

Neste passo, criaremos um usuário de banco de dados para o banco de dados MySQL laravel_web que especificamos no arquivo docker-compose. Quando você executou o comando de criação do contêiner no Step 9, o MySQL foi instalado, mas apenas criou uma conta administrativa root padrão, que por acaso tem privilégios ilimitados no banco de dados. Para evitar o uso do usuário root, criaremos um usuário dedicado, laraveldocker, para usar no aplicativo. Este é o usuário que você especificou nas variáveis de ambiente no Step 8. Acesse a linha de comando dentro do terminal inserindo o seguinte comando:

Uma vez dentro do contêiner, faça login no MySQL inserindo o seguinte comando:

No prompt da senha, insira a senha que você definiu no serviço db no arquivo docker-compose no Step 2.

Depois de fazer login no prompt do MySQL, verifique se você consegue ver o banco de dados especificado no arquivo docker-compose inserindo o seguinte comando SQL:

Você deve ver o banco de dados laravel_web, ou qualquer nome que tenha especificado para a sua configuração:

Laravel

Em seguida, criamos um usuário e uma senha para o banco de dados laravel_web. Devem ser os mesmos detalhes que você especificou no arquivo .env no Step 8. Insira o seguinte comando para criar o usuário e a senha, e conceder todos os privilégios a este usuário:

Para que as alterações entrem em vigor imediatamente, insira o seguinte comando para recarregar os privilégios:

Isso conclui a configuração do usuário do MySQL. Saia do prompt do MySQL digitando exit e pressionando enter. Por fim, saia do contêiner db pressionando Ctrl + D.

Passo 11: Testar a Comunicação entre o Código da Aplicação Laravel e o Banco de Dados MySQL

Até este passo, tudo funcionou bem. No entanto, queremos confirmar que o código Laravel no contêiner app consegue se comunicar com o banco de dados MySQL no contêiner db. Primeiro, acesse o terminal do contêiner app inserindo o seguinte comando:

O próximo passo é executar o comando de migração do Laravel que cria as tabelas:

Você deve ver o processo de migração no seu terminal enquanto o Laravel cria as tabelas padrão:

migration process

A seguir, testaremos se conseguimos acessar o banco de dados a partir do Laravel. O Laravel vem com o Tinker por padrão, o que permite interagir com toda a aplicação a partir da linha de comando, incluindo acessar o banco de dados, executar tarefas, ORM Eloquent, e muito mais. Podemos usar o Tinker para visualizar os dados na tabela de migrações. Insira o seguinte comando para acessar o Tinker:

Uma vez no prompt do Tinker, você pode listar as tabelas criadas pelo comando migrate inserindo o seguinte:

A captura de tela abaixo mostra a saída, que são as tabelas atualmente no banco de dados laravel_web:

laravel_web database

Você pode recuperar os dados de uma tabela especificando o nome da tabela. Por exemplo, você pode recuperar os dados da tabela migrations inserindo o seguinte comando:

O comando produz a seguinte saída:

comman output

A partir da saída acima, sua aplicação Laravel está bem configurada e pode se comunicar com o banco de dados. Você pode experimentar mais comandos, como criar modelos, executar tarefas e muito mais. Você pode sair do prompt do Tinker pressionando Ctrl + D.

Conclusão

Neste tutorial, você implantou uma aplicação Laravel com a pilha LEMP dentro de um contêiner Docker. Você testou a aplicação acessando a interface web, bem como conectando-se ao banco de dados através do Laravel Tinker. Você pôde experimentar o poder do Docker Compose. Ele permite criar um grupo de contêineres Docker definidos em um único arquivo, que podem ser executados com apenas um comando.

Se você quiser se familiarizar ainda mais com contêineres, dê uma olhada no nosso tutorial que mostra como limpar recursos do Docker – imagens, contêineres e volumes e no nosso visão geral detalhada da ferramenta Kubernetes.

Você também pode visitar nosso blog para saber mais sobre Docker e Integração Contínua e Implantação Contínua.

Boa computação!

author

Pranay Kapgate

Autor · CloudSigma

Preslav Dobrev é um designer criativo na CloudSigma, focado na construção de uma identidade empresarial consistente por meio de canais de marketing tradicionais e inovadores. Ele é hábil em combinar a visão artística com o marketing estratégico para criar narrativas de marca impactantes.

Comentários

Nenhum comentário ainda. Seja o primeiro.