Configurando uma aplicação Laravel com Docker Compose para desenvolvimento local usando RabbitMq, Mariadb e Redis

Sejam bem vindos ao oitavo post do blog.
Iremos configurar e executar uma aplicação Laravel (PHP)
Repositório:
https://github.com/brunocaramelo/docker-compose-php-application-example

DependênciaDescriçãoVersão
PHPUsado para Backend7.4.11
NginxUsado como Web server1.19.3
MariadbUsado como Banco de dados SQL10.5.6-MariaDB
RedisUsado com Cache Storage6.0.8
RabbitMqUsado para mensageria3.8.9

Premissas

Ter as seguintes dependências previamente instaladas e configuradas:

  • Docker
  • Docker Compose

Composição de Serviços

Especificações de serviços da aplicação.

ServiçoDescriçãoDependências de serviços
phpDomínio da aplicação backend da API exposta com o PHP FPM.mysql, redis, rabbitmq
worker-phpDomínio da aplicação para execução de rotinas.mysql
worker-queueDomínio da aplicação para consumo da mensageria.mysql, rabbitmq
webRecurso responsável pelo Web server da aplicação com montagem para configuração.php
rabbitmqRecurso responsável pela Mensageria e prover a interface gráfica.
mysqlRecurso responsável pela base de dados SQL persistida em disco local.
Exemplo arquivo docker-compose.yml

Entendo a configuração do serviço: php

php: # Nome do serviço
    build: # Inicio da sessão build
      context: .  # Nivel onde o build deve ser executado
      dockerfile: ./docker/php7-fpm/Dockerfile # Dockerfile escolhido (padrão arquivo com nome Dockerfile, no mesmo nivel do arquivo docker-compose)
    image: repo-registry-local/libraryapitest-app-example:v4.0 # imagem que sera usada como base do build. pode ser usado tambem para que seja enviado ao registry, como este exemplo
    container_name: php-library-example # nome do container ao subir o serviço
    depends_on: # depencia de outros serviços, ira iniciar apenas quando a lista abaixo estiver de pé
      - redis
      - mysql
      - rabbitmq
    command: bash -c "php artisan migrate && php-fpm" # sobreposicao do atributo CMD do Dockerfile
    env_file: # vairaveis de ambiente vindas de um arquivo
      - docker/docker-compose-env/application.env
    environment: # variaveis de ambiente explicitas, tambem pode ser usado para sobreescrever variaveis
      APP_NAME: 'Sobrepondo - Library API'
    links: # Quando listado, o container passa e ver os serviços abaixo
      - mysql
      - redis
      - rabbitmq
    volumes: # Area destinada a montagem externa
     - ./application/:/app:rw
     - /app/vendor

Topologia da aplicação

Iniciando a aplicação

agora que temos um entendimento básico sobre os recursos da aplicação, iremos iniciá-la.

docker-compose up -d

O comando acima executa faz com que todos os serviços contido em nosso docker-compose sejam iniciados, tendo o seguinte resultado.

Starting redis-library ... done
Starting mysql-library ... done
Starting rabbitmq                   ... done
Starting php-library-example-worker ... done
Starting php-library-example              ... done
Starting php-library-worker-queue-example ... done
Starting nginx-library                    ... done

Também é possível discriminar os serviços que queremos iniciar como o comando abaixo.

docker-compose up rabbitmq mysql -d

Ou executar o build de todos os serviços ou de um determinado servico conforme podemos ver abaixo.

docker-compose build
docker-compose build php

Verificando Serviços

Podemos verificar se os serviços iniciaram corretamente com o seguinte comando :

docker ps

Com o seguinte resultado:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2f0d029513c3 nginx:1.19-alpine "/docker-entrypoint.…" 3 days ago Up 18 minutes 0.0.0.0:80->80/tcp nginx-library
90a5a7a0d335 repo-registry-local/libraryapitest-app-example:v4.0 "docker-php-entrypoi…" 3 days ago Up 18 minutes 9000/tcp php-library-example
9630371843b4 libraryapiotra:php-worker-example "docker-php-entrypoi…" 3 days ago Up 18 minutes 9000/tcp php-library-example-worker
71861ab99b26 libraryapiotra:php-worker-queue "docker-php-entrypoi…" 3 days ago Up 18 minutes 9000/tcp php-library-worker-queue-example
9fcb27caaceb mariadb:latest "docker-entrypoint.s…" 13 days ago Up 19 minutes 0.0.0.0:3306->3306/tcp mysql-library
40f05238ace7 rabbitmq:3-management-alpine "docker-entrypoint.s…" 13 days ago Up 18 minutes 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 15691-15692/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp rabbitmq
e85dbe64c2e6 redis:alpine "docker-entrypoint.s…" 13 days ago Up 18 minutes 6379/tcp redis-library

Visualizando documentação da API (Swagger)

Ao inciar nossa aplicação, podemos ver a documentação da API. a partir do seguinte endereço:


http://localhost/api/documentation

Entendendo as interdependências

Iremos utilizar algumas rotas para entender melhor a participação de diferentes serviços entre si, para isso iremos ver 3 casos de uso:
– VER DETALHE DE LIVRO
– ENVIAR MENSAGEM AO MESSAGE BROKER
– PROCESSAR MENSAGEM DO MESSAGE BROKER

DETALHAMENTO DOS CASOS DE USO

1 – VER DETALHE DE LIVRO

Este recurso tem como finalidade apresentar o detalhamento de um livro, utilizando o recurso de Cache para evitarmos o uso de uma base de dados SQL por exemplo, durante o tempo em que o Cache existir, ou que se tenha uma alteração no dado, momentos em que o índice é invalidado.

Representação UML da rota: GET book/{bookId}

Representação da aplicação para a rota: GET book/{bookId}

Ao clicar em Try it out >> preencher o campo Id do Livro >> clicar em Execute.

Nesta rota utilizamos um Cache para consulta executada ,utilizando o Driver que envia para o Redis, conforme podemos ver abaixo, foram visualizados três livros: livro 13, 15 e 16, e temos o valor do índice gerado em nossa instancia Redis.

2 – ENVIAR MENSAGEM AO MESSAGE BROKER

Este recurso tem como finalidade enviar uma mensagem a nosso Message Broker, para que seja processado em outro momento de forma assíncrona.

Representação UML da rota: POST /author/process/message

Representação da aplicação para a rota: POST /author/process/message

Após clicar em Try it out >> preencher o campo de mensagem >> clicar em Execute.

Receberemos uma mensagem que nossa messagem sera processada. esta mensagem apenas e gerada, caso nosso Message Broker recebeu e aceitou nossa mensagem.

Acessando O Message Broker:
http://localhost:15672

Login: admin
Password: admin

Verificando Mensagens recebidas em:
http://localhost:15672/#/queues

Podemos notar que temos 4 mensagens aguardando processamento. na fila chamada test, que foi configurada em nossa aplicação Laravel.

Ao Clicar no Menu Messages:

Podemos ver maiores detalhes da mensagem, e como o Laravel envia ela para que seja processada por padrão.
{"displayName":"App\\Jobs\\ProcessExperimentalJob","job":"Illuminate\\Queue\\CallQueuedHandler@call","maxTries":null,"timeout":null,"timeoutAt":null,"data":{"commandName":"App\\Jobs\\ProcessExperimentalJob","command":"O:31:\"App\\Jobs\\ProcessExperimentalJob\":8:{s:40:\"\u0000App\\Jobs\\ProcessExperimentalJob\u0000message\";s:87:\"Place the message here for the processing queue received at: 2021-01-30 22:51:54:507117\";s:6:\"\u0000*\u0000job\";N;s:10:\"connection\";N;s:5:\"queue\";N;s:15:\"chainConnection\";N;s:10:\"chainQueue\";N;s:5:\"delay\";N;s:7:\"chained\";a:0:{}}"},"id":"bLVXYvR1U9wzTXtGJNQEiLl2wkx8Zqtl"}

Exemplo de payload de uma mensagem enviada pela nossa aplicação.

3 – PROCESSAR MENSAGEM DO MESSAGE BROKER

Este recurso tem como finalidade processar uma mensagem vinda do nosso Message Broker, poderemos ver seu processamento, assim como sua conclusão em nossa fila.

Representação UML do processamento de filas

Representação da aplicação sobre processamento de filas

Trecho da notação do nosso container responsavel pelo processamento de filas de nossa aplicação

Execução do serviço:

php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30a841676.51726260] Processing: App\Jobs\ProcessExperimentalJob
php-library-worker-queue-example | {"message":"Place the message here for the processing queue received at: 2021-01-30 22:51:54:507117 Processed on: 2021-01-31 00:47:38:247462","context":[],"level":200,"level_name":"INFO","channel":"local","datetime":{"date":"2021-01-31 00:47:38.249261","timezone_type":3,"timezone":"UTC"},"extra":[]}
php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30a841676.51726260] Processed:  App\Jobs\ProcessExperimentalJob
php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30a913a25.80490108] Processing: App\Jobs\ProcessExperimentalJob
php-library-worker-queue-example | {"message":"Place the message here for the processing queue received at: 2021-01-30 22:51:54:579488 Processed at: 2021-01-31 00:47:38:251189","context":[],"level":200,"level_name":"INFO","channel":"local","datetime":{"date":"2021-01-31 00:47:38.251229","timezone_type":3,"timezone":"UTC"},"extra":[]}
php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30a913a25.80490108] Processed:  App\Jobs\ProcessExperimentalJob
php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30abadad8.37346333] Processing: App\Jobs\ProcessExperimentalJob
php-library-worker-queue-example | {"message":"Place the message here for the processing queue received at: 2021-01-30 22:51:54:753117 Processed at: 2021-01-31 00:47:38:253215","context":[],"level":200,"level_name":"INFO","channel":"local","datetime":{"date":"2021-01-31 00:47:38.253247","timezone_type":3,"timezone":"UTC"},"extra":[]}
php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30abadad8.37346333] Processed:  App\Jobs\ProcessExperimentalJob
php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30b0eb9b5.50128892] Processing: App\Jobs\ProcessExperimentalJob
php-library-worker-queue-example | {"message":"Place the message here for the processing queue received at: 2021-01-30 22:51:55:052662 Processed at: 2021-01-31 00:47:38:254837","context":[],"level":200,"level_name":"INFO","channel":"local","datetime":{"date":"2021-01-31 00:47:38.254869","timezone_type":3,"timezone":"UTC"},"extra":[]}
php-library-worker-queue-example | [2021-01-31 00:47:38][6015e30b0eb9b5.50128892] Processed:  App\Jobs\ProcessExperimentalJob

Conforme podemos ver acima. Vimos que quando o nosso serviço worker-queue verificou que existiam mensagens para processar ele as executou,

vimos sua saída, pois ele esta usando o /dev/stderr para escrever logs:
Podemos ver com o seguinte comando:

docker-compose logs worker-queue

Verificando mensagens após o processamento na aplicação

Vimos o funcionamento de uma mini aplicação e a comunicação entre seus componentes.

Encerramos por aqui.
Espero ter sido claro na explicação.
Obrigado pela atenção, até a próxima!

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *