Welcome to the eighth post of the blog. We will set up and run a Laravel application (PHP) Repository: https://github.com/brunocaramelo/docker-compose-php-application-example
Dependency | Description | Version |
---|---|---|
PHP | Used as Backend | 7.4.11 |
Nginx | Used as Webserver | 1.19.3 |
MariaDB | Used as SQL database | 10.5.6-MariaDB |
Redis | Used as Cache Storage | 6.0.8 |
RabbitMq | Used as message broker | 3.8.9 |
Assumptions
Have the following dependencies previously installed and configured:
- Docker
- Docker Compose
Composition of Services
Specifications of application services.
Service | Description | Service dependencies |
---|---|---|
php | Domain of the API backend application exposed with PHP FPM. | mysql, redis, rabbitmq |
worker-php | Domain of the application for execution of routines. | mysql |
worker-queue | Domain of the application for consumption of the message broker. | mysql, rabbitmq |
web | Resource responsible for the application’s web server with assembly for configuration. | php |
rabbitmq | Resource responsible for messaging and providing the graphical interface. | |
mysql | Resource responsible for the SQL database persisted in a host local disk |

Source: https://github.com/brunocaramelo/docker-compose-php-application-example/blob/main/docker-compose.yml
Understanding the service configuration: php
php: # Service name build: # Start build session context: . # Level where the build should be executed dockerfile: ./docker/php7-fpm/Dockerfile # Dockerfile chosen (default file with name Dockerfile, on the same level as the docker-compose file) image: repo-registry-local/libraryapitest-app-example:v4.0 # image that will be used as a base for the build. can also be used to be sent to the registry, like this example container_name: php-library-example # container name when uploading the service depends_on: # depends on other services, will start only when the list below is up - redis - mysql - rabbitmq command: bash -c "php artisan migrate && php-fpm" # Dockerfile CMD attribute overlap env_file: # environment variables coming from a file - docker/docker-compose-env/application.env environment: # explicit environment variables, can also be used to overwrite variables APP_NAME: 'Overlaying - Library API links: # When listed, the container passes and see the services below - mysql - redis - rabbitmq volumes: # External mounting area - ./application/:/app:rw - /app/vendor
Application topology

Starting the application
now that we have a basic understanding of the app’s features, let’s get started.
docker-compose up -d
The above command executes all the services contained in our docker-compose to start, with the following result.
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
It is also possible to break down the services we want to start with the command below.
docker-compose up rabbitmq mysql -d
Or perform the build of all services or a particular service as we can see below.
docker-compose build docker-compose build php
Checking Services
We can verify that the services started correctly with the following command :
docker ps
With the following result:
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
Viewing API (Swagger) documentation
When starting our application, we can see the API documentation from the following address:
http://localhost/api/documentation

Understanding interdependencies
We will use some routes to better understand the participation of different services among themselves, for this, we will see 3 use cases:
– SEE BOOK DETAIL
– SEND MESSAGE TO MESSAGE BROKER
– PROCESS MESSAGE BROKER
DETAILS OF USE CASES
1 – SEE BOOK DETAIL
The purpose of this resource is to present the detailing of a book, using the Cache resource to avoid the use of an SQL database for example, during the time that Cache exists, or that there is a change in the data, moments in which the index is invalidated.
UML route representation: GET book/{bookId}

Application representation for the route: GET book/{bookId}

When you click on Try it out >> fill in the Book Id field >> click on Execute.

In this route we used a Cache for consultation, using the Driver that sends to Redis, as we can see below, three books were viewed: book 13, 15, and 16, and we have the value of the index generated in our Redis instant.

2 – SEND MESSAGE TO MESSAGE BROKER
The purpose of this feature is to send a message to our Message Broker so that it can be processed at another time asynchronously.
UML route representation: POST /author/process/message

Route application representation: POST /author/process/message
After clicking Try it out >> fill in the message field >> click on Execute.

We will receive a message that our message will be processed. this message only and generated, if our Message Broker received and accepted our message.
Accessing The Message Broker:
http://localhost:15672

Login: admin
Password: admin
Checking Messages received on:
http://localhost:15672/#/queues

We can notice that we have 4 messages waiting for processing. in the queue called test, which was configured in our Laravel application.
When clicking on the Messages menu:

{"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"}
Example of a payload message sent by our application.
3 – PROCESS MESSAGE BROKER
The purpose of this feature is to process a message from our Message Broker, we can see its processing as well as its completion in our queue.
UML representation of queue processing

Representation of the application on queue processing
worker-queue: build: context: . dockerfile: ./docker/php7-fpm/Dockerfile image: libraryapiotra:php-worker-queue container_name: php-library-worker-queue-example depends_on: - mysql - rabbitmq command: sh -c "php /app/artisan queue:work --verbose --tries=7 --timeout=90" env_file: - docker/docker-compose-env/application.env environment: APP_NAME: 'Queue Worker - Library API' links: - mysql - redis - rabbitmq volumes: - ./application/:/app:rw
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
As we can see above. We saw that when our worker service verified that there were messages to process he executed them,
we saw his output, as he is using /dev/stderr to write logs:
We can see it with the following command:
docker-compose logs worker-queue
Checking messages after processing in the application

We have seen the operation of a mini-application and the communication between its components.
We close here.
I hope I was clear in my explanation.
Thanks for your attention, see you next time!