Hi everyone, welcome back to my Docker and CICD series.
Wishing everyone a warm and peaceful Christmas
A few days ago, when the spirit of blogging was going up, my eyes went on strike again after a period of too much computer time, today the “window of the soul” seems to be good again, I slowly wrote a blog again. for everyone
Whatever you do, remember to stay healthy everyone, when Tet comes, you have to have health to work to get money to give to your teacher
In the previous article I showed you the Dockerize NodeJS application with mysql, redis, … along with the setup for dev and production environments.
In this article we will play “hardcore” by Dockerize Chat application realtime with Laravel, VueJS, Laravel Echo, SocketIO, Redis along with the setup of Laravel Horizon and Laravel Schedule Task.
Sorry for the name of this article I left a bit long, the purpose to read through the title you will be clearer and for those who search from google, it will be easier to find offline.
Money Setup
As usual, you need to install Docker and Docker-compose, if you have not already done so, see in my first post.
Setup
You can clone source code here (branch master )
In this article we will only care about the docker-laravel-realtime-chat-app folder.
Application overview
This application was made and deployed to run production here, you can go and try it out
Overview:
- The application has chat rooms for users to join
- Each chat room will have 1 shared chat box, all users in the room can message here
- Along with that is the list of users in the chat room, click on any one user to private message with that person
- Every 1 minute will automatically appear the welcome message of Bot (using Laravel Schedule Task )
- Detect user typing, or viewing messages, …
- You can choose the expressive messages like Facebook Messager: Love, Haha, Wow, …..
Shake the brain before use
Before we go into the Dockerfile configuration, we will first “shake the brain” to analyze what services this project has, what Dockerfile you need.
- Similarly all Dockerize Laravel applications , to run the project’s minimum basic service we will use 2 app (php-fpm there) and service webserver (using nginx)
- Having a database to store messages, a list of users, a chat room, … -> requires a db service
- Having queue job -> need a redis service
- Having realtime with Laravel Echo, socketio -> requires a laravel-echo-server service
- At the same time we also need to add a service adminer to administer the MySQL database for visualization (this is similar to the phpmyadmin type)
)
So we all have 6 services corresponding to 6 containers that need to be initialized to run the application.
In this article, we will write Dockerfile for 2 services, app and laravel-echo-server , the rest of the services we use are always provided.
Build Docker image
In this article because we have quite a lot of services, so the entire setup + data section of Mysql, redis we are stored in a folder called .docker.
At the root project (folder docker-laravel-realtime-chat-app , in this article I said always the project root because the folder name is too long ), create a folder named .docker .
Configure Dockerfile for Laravel Echo Server
First we will proceed to configure Dockerfile and build image for laravel-echo-server service .
In the .docker folder, create a folder called laravel-echo-server
This service is rightly named above as Laravel Echo Server to handle the broadcast from Laravel to the browser
Basically this service is quite simple, written in nodejs, this service will almost no change after building, we will be less active on this service.
To run the service written in NodeJS in the Docker environment, we will use PM2.
In the laravel-echo-server folder, create a file called laravel-echo-server.json with the following content: (This file is a configuration for Laravel Echo Server)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | <span class="token punctuation">{</span> <span class="token property">"authHost"</span> <span class="token operator">:</span> <span class="token string">"http://localhost"</span> <span class="token punctuation">,</span> <span class="token property">"authEndpoint"</span> <span class="token operator">:</span> <span class="token string">"/broadcasting/auth"</span> <span class="token punctuation">,</span> <span class="token property">"clients"</span> <span class="token operator">:</span> <span class="token punctuation">[</span> <span class="token punctuation">]</span> <span class="token punctuation">,</span> <span class="token property">"database"</span> <span class="token operator">:</span> <span class="token string">"redis"</span> <span class="token punctuation">,</span> <span class="token property">"databaseConfig"</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"redis"</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token property">"sqlite"</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"databasePath"</span> <span class="token operator">:</span> <span class="token string">"/database/laravel-echo-server.sqlite"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token property">"devMode"</span> <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> <span class="token property">"host"</span> <span class="token operator">:</span> <span class="token null">null</span> <span class="token punctuation">,</span> <span class="token property">"port"</span> <span class="token operator">:</span> <span class="token string">"6001"</span> <span class="token punctuation">,</span> <span class="token property">"protocol"</span> <span class="token operator">:</span> <span class="token string">"http"</span> <span class="token punctuation">,</span> <span class="token property">"socketio"</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token property">"secureOptions"</span> <span class="token operator">:</span> <span class="token number">67108864</span> <span class="token punctuation">,</span> <span class="token property">"sslCertPath"</span> <span class="token operator">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token property">"sslKeyPath"</span> <span class="token operator">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token property">"sslCertChainPath"</span> <span class="token operator">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token property">"sslPassphrase"</span> <span class="token operator">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token property">"subscribers"</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"http"</span> <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">,</span> <span class="token property">"redis"</span> <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token property">"apiOriginAllow"</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token property">"allowCors"</span> <span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">,</span> <span class="token property">"allowOrigin"</span> <span class="token operator">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token property">"allowMethods"</span> <span class="token operator">:</span> <span class="token string">""</span> <span class="token punctuation">,</span> <span class="token property">"allowHeaders"</span> <span class="token operator">:</span> <span class="token string">""</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Next, because we use PM2 to run this service, we will create a configuration file for PM2, still in the laravel-echo-server folder, create echo.json file with the following content:
1 2 3 4 5 6 | <span class="token punctuation">{</span> <span class="token property">"name"</span> <span class="token operator">:</span> <span class="token string">"laravel-echo-server-6001"</span> <span class="token punctuation">,</span> <span class="token property">"script"</span> <span class="token operator">:</span> <span class="token string">"laravel-echo-server"</span> <span class="token punctuation">,</span> <span class="token property">"args"</span> <span class="token operator">:</span> <span class="token string">"start"</span> <span class="token punctuation">}</span> |
Finally, we create the Dockerfile file to configure the image we need to build (still in the laravel-echo-server directory, guys ):
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">FROM</span> node <span class="token punctuation">:</span> 13 <span class="token punctuation">-</span> alpine <span class="token keyword">WORKDIR</span> /app <span class="token keyword">COPY</span> echo.json /app/echo.json <span class="token keyword">COPY</span> laravel <span class="token punctuation">-</span> echo <span class="token punctuation">-</span> server.json /app/laravel <span class="token punctuation">-</span> echo <span class="token punctuation">-</span> server.json <span class="token keyword">RUN</span> npm install <span class="token punctuation">-</span> g pm2 laravel <span class="token punctuation">-</span> echo <span class="token punctuation">-</span> server <span class="token keyword">EXPOSE</span> 6001 <span class="token keyword">CMD</span> <span class="token punctuation">[</span> <span class="token string">"pm2-runtime"</span> <span class="token punctuation">,</span> <span class="token string">"echo.json"</span> <span class="token punctuation">]</span> |
After configuring the configuration directory for our laravel echo server , it will look like this:
It’s okay, but in this article we will not build the image immediately with the command docker build … as usual, but we will build with docker-compose later (we’ll see in a little later. )
Configuration for service app
Next is that we will configure the service app, the section below will have a lot of things related to Linux, I will explain into the remaining important things what you wonder then search google continue to understand more.
Continue to shake the brain before use
As at the beginning of the article I have introduced in this article we will have Laravel Horizon and Laravel Schedule Task. So in the service app we will run the process (process as follows):
- PHP-FPM to run PHP code (this process is required)
- Crontab for Laravel Schedule Task (this process is optional)
- Laravel Horizon (this process is also optional)
Above I have 2 optional processes, ie we can use it or not, the application can still run without those 2 processes, later we will specify the configuration later.
And in Linux to run and manage many processes, I will use a fairly common tool called supervisor offline.
We will create configuration files corresponding to the above processes, then the supervisor will read and start those processes.
Supervisor configuration
In the .docker folder you create yourself a file named supervisord.conf , with the following content:
1 2 3 4 5 6 | <span class="token punctuation">[</span> supervisord <span class="token punctuation">]</span> nodaemon <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">;</span> chạy ở foreground <span class="token punctuation">[</span> <span class="token keyword">include</span> <span class="token punctuation">]</span> files <span class="token operator">=</span> <span class="token operator">/</span> etc <span class="token operator">/</span> supervisor <span class="token punctuation">.</span> d <span class="token operator">/</span> <span class="token operator">*</span> <span class="token punctuation">.</span> conf <span class="token punctuation">;</span> load toàn bộ các file cấu hình cho các process ở đường dẫn này |
Next, in the .docker folder, create a folder called supervisor.d , this folder will contain the image files for the processes mentioned above.
In the supervisor.d folder, create the php-fpm.conf file with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [program:php-fpm] process_name=%(program_name)s command=php-fpm -F ; chạy ở foreground user=root ; chạy với user root autostart=true autorestart=true numprocs=1 ; muốn chạy 1 lúc nhiều process này thì ta thay đổi numprocs (>=1) redirect_stderr=false stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 |
Still in the supervisor.d folder, create a file called horizon.conf.default with the following content:
1 2 3 4 5 6 7 8 9 10 | [program:horizon] process_name=%(program_name)s command=php /var/www/html/artisan horizon ; command này cũng sẽ chạy ở foreground user=www-data autostart=true autorestart=true redirect_stderr=true stdout_logfile=/var/www/html/horizon.log stopwaitsecs=3600 |
The configuration is similar to the php-fpm process, except that this process will be run under the user named www-data , this user will be made available from php-fpm . The reason is because we do not need the root privileges to run this command, because later the entire code when building the image will be set to the www-data user (to limit the rights of the user running the project, only for the user). rights sufficient to run)
Next, it is still in the supervisor.d folder, we create 2 files cron.conf.default , and worker.conf.default with the following contents (I quickly explain it no further)
File cron.conf.default
1 2 3 4 5 6 7 8 9 10 11 12 13 | [program:cron-job] process_name=%(program_name)s command=crond -f user=root ; crond should be started as root at all times autostart=true autorestart=true numprocs=1 redirect_stderr=false stdout_logfile=/dev/stdout stdout_logfile_maxbytes=0 stderr_logfile=/dev/stderr stderr_logfile_maxbytes=0 |
worker.conf.default
1 2 3 4 5 6 7 8 9 | [program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/html/artisan queue:work --sleep=3 --tries=3 --daemon user=www-data autostart=true autorestart=true numprocs=2 redirect_stderr=true |
After configuration is complete, our .docker folder will look like this:
You may wonder:
- Why is the name of the php-fpm process only without the default suffix? As I said above, the php-fpm process is required for our PHP code to run so we always need the process, while the remaining processes are optional (optional), without them the application of I still run
- Where is the worker.conf.default file coming from? do you need to use it or not Because Laravel Horizon has automatically created workers for us, if you run Horizon, you won’t need worker.conf.default anymore, but if you don’t run Horizon, we need this file for Queue Job to have Can run
Ok that configuration is okay. We move to Dockerfile
Configure Dockerfile
At the project root (folder docker-laravel-realtime-chat-app ) you create the Dockerfile file with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <span class="token comment"># Set master image</span> <span class="token keyword">FROM</span> php <span class="token punctuation">:</span> 7.2 <span class="token punctuation">-</span> fpm <span class="token punctuation">-</span> alpine <span class="token keyword">LABEL</span> maintainer= <span class="token string">"Mai Trung Duc ( <a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="137e727a6761667d747766702227222353747e727a7f3d707c7e">[email protected]</a> )"</span> <span class="token comment"># Set working directory</span> <span class="token keyword">WORKDIR</span> /var/www/html <span class="token comment"># Install Additional dependencies</span> <span class="token keyword">RUN</span> apk update && apk add <span class="token punctuation">-</span> <span class="token punctuation">-</span> no <span class="token punctuation">-</span> cache build <span class="token punctuation">-</span> base shadow supervisor php7 <span class="token punctuation">-</span> common php7 <span class="token punctuation">-</span> pdo php7 <span class="token punctuation">-</span> pdo_mysql php7 <span class="token punctuation">-</span> mysqli php7 <span class="token punctuation">-</span> mcrypt php7 <span class="token punctuation">-</span> mbstring php7 <span class="token punctuation">-</span> xml php7 <span class="token punctuation">-</span> openssl php7 <span class="token punctuation">-</span> json php7 <span class="token punctuation">-</span> phar php7 <span class="token punctuation">-</span> zip php7 <span class="token punctuation">-</span> gd php7 <span class="token punctuation">-</span> dom php7 <span class="token punctuation">-</span> session php7 <span class="token punctuation">-</span> zlib <span class="token comment"># Add and Enable PHP-PDO Extenstions for PHP connect Mysql</span> <span class="token keyword">RUN</span> docker <span class="token punctuation">-</span> php <span class="token punctuation">-</span> ext <span class="token punctuation">-</span> install pdo pdo_mysql <span class="token keyword">RUN</span> docker <span class="token punctuation">-</span> php <span class="token punctuation">-</span> ext <span class="token punctuation">-</span> enable pdo_mysql <span class="token comment"># This extension required for Laravel Horizon</span> <span class="token keyword">RUN</span> docker <span class="token punctuation">-</span> php <span class="token punctuation">-</span> ext <span class="token punctuation">-</span> install pcntl <span class="token comment"># Remove Cache</span> <span class="token keyword">RUN</span> rm <span class="token punctuation">-</span> rf /var/cache/apk/* <span class="token keyword">COPY</span> .docker/supervisord.conf /etc/supervisord.conf <span class="token keyword">COPY</span> .docker/supervisor.d /etc/supervisor.d <span class="token comment"># Use the default production configuration for PHP-FPM ($PHP_INI_DIR variable already set by the default image)</span> <span class="token keyword">RUN</span> mv <span class="token string">"$PHP_INI_DIR/php.ini-production"</span> <span class="token string">"$PHP_INI_DIR/php.ini"</span> <span class="token comment"># Add UID </span> <span class="token string">'1000'</span> to www <span class="token punctuation">-</span> data <span class="token keyword">RUN</span> usermod <span class="token punctuation">-</span> u 1000 www <span class="token punctuation">-</span> data <span class="token comment"># Copy existing application directory</span> <span class="token keyword">COPY</span> . . <span class="token comment"># Chang app directory permission</span> <span class="token keyword">RUN</span> chown <span class="token punctuation">-</span> R www <span class="token punctuation">-</span> data <span class="token punctuation">:</span> www <span class="token punctuation">-</span> data . <span class="token keyword">ENV</span> ENABLE_CRONTAB 1 <span class="token keyword">ENV</span> ENABLE_HORIZON 1 <span class="token keyword">ENTRYPOINT</span> <span class="token punctuation">[</span> <span class="token string">"sh"</span> <span class="token punctuation">,</span> <span class="token string">"/var/www/html/.docker/docker-entrypoint.sh"</span> <span class="token punctuation">]</span> <span class="token keyword">CMD</span> supervisord <span class="token punctuation">-</span> n <span class="token punctuation">-</span> c /etc/supervisord.conf |
Explain some places that may have questions:
- We started from a built-in image of PHP-FPM I got here
- Below we have created two environment variables ENABLE_CRONTAB and ENABLE_HORIZON to specify that we will use the process crontab and horizon later (as in the previous section, I said that these optional processes can be used or not, here we use both )
- Next we have ENTRYPOINT , here are the uploads so we can optionally use processes like: crontab, horizon or worker we talked about. When building the image, the docker-entrypoint.sh file will be run, including some setups (this file will be created at the bottom)
- Finally, we start the supervisor with CMD
The remaining places in the Dockerfile file you can see in the previous articles I have explained offline
Entrypoint configuration
As I said above, we will use a file called docker-entrypoint.sh to set the option to use processes like crontab, horizon or worker, depending on the environment variable we set.
In the .docker folder you create yourself a file named docker-entrypoint.sh with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <span class="token shebang important">#!/bin/sh</span> <span class="token keyword">set</span> -e <span class="token comment"># Enable Laravel schedule</span> <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span> <span class="token string">" <span class="token variable">${ENABLE_CRONTAB:-0}</span> "</span> <span class="token operator">=</span> <span class="token string">"1"</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token function">mv</span> -f /etc/supervisor.d/cron.conf.default /etc/supervisor.d/cron.conf <span class="token keyword">echo</span> <span class="token string">"* * * * * php /var/www/html/artisan schedule:run >> /dev/null 2>&1"</span> <span class="token operator">>></span> /etc/crontabs/www-data <span class="token keyword">fi</span> <span class="token comment"># Enable Laravel queue workers</span> <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span> <span class="token string">" <span class="token variable">${ENABLE_WORKER:-0}</span> "</span> <span class="token operator">=</span> <span class="token string">"1"</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token function">mv</span> -f /etc/supervisor.d/worker.conf.default /etc/supervisor.d/worker.conf <span class="token keyword">else</span> <span class="token function">rm</span> -f /etc/supervisor.d/worker.conf.default <span class="token keyword">fi</span> <span class="token comment"># Enable Laravel horizon</span> <span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span> <span class="token string">" <span class="token variable">${ENABLE_HORIZON:-0}</span> "</span> <span class="token operator">=</span> <span class="token string">"1"</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">;</span> <span class="token keyword">then</span> <span class="token function">mv</span> -f /etc/supervisor.d/horizon.conf.default /etc/supervisor.d/horizon.conf <span class="token keyword">else</span> <span class="token function">rm</span> -f /etc/supervisor.d/horizon.conf.default <span class="token keyword">fi</span> <span class="token function">exec</span> <span class="token string">" <span class="token variable"><a class="__cf_email__" href="/cdn-cgi/l/email-protection" data-cfemail="ae8aee">[email protected]</a></span> "</span> |
Looking at the file above is already clear, right? Depending on the environment variable we set, we can choose to use a certain process or not.
If used, rename the file of the process to remove the default suffix, and if not use, we delete the configuration file
Configure .dockerignore
Before building the image we will configure .dockerignore to tell Docker “to skip some files, don’t copy them inside the image when building”.
At the root project (folder docker-laravel-realtime-chat-app ) you create .dockerignore file with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | bootstrap/cache/* storage/app/* storage/framework/cache/* storage/framework/sessions/* storage/framework/views/* storage/logs/* node_modules .git .idea .gitignore .gitattributes .sass-cache *.env* *.config.js .dockerignore Dockerfile docker-compose.yml .docker/* !.docker/supervisor* !.docker/php !.docker/docker-entrypoint.sh .vscode .idea README.md .editorconfig vendor # ignore frontend built files public/js/app.js public/js/manifest.js public/js/vendor.js public/css/app.css public/mix-manifest.json ./supervisord* horizon.log |
This is quite similar to the content of gitignore, isn’t it
Note: although using folders like node_modules we don’t have, we still ignore them because later when building VueJS will need node_modules and in the future we can build this project many more times, so let’s add it first.
Ok, everything is fine for the service app , but here we will not build the image right away but we will build with docker-compose later.
Nginx configuration
Similarly in post Dockerize Laravel application we will need to set up Nginx webserver.
In the .docker folder, create nginx.conf file with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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/html/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; fastcgi_hide_header X-Powered-By; } location / { try_files $uri $uri/ /index.php?$query_string; gzip_static on; } location /socket.io { proxy_pass http://laravel_echo_server:6001; proxy_redirect off; proxy_http_version 1.1; proxy_cache_bypass $http_upgrade; proxy_set_header Host $host; proxy_set_header Connection 'upgrade'; proxy_set_header Upgrade $http_upgrade; proxy_set_header X-Real-Ip $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } } |
Notice above when with the PHP code, we will redirect the request into the service app so that the service app has PHP-FPM working, and with socket.io , we redirect the job to the laravel-echo-server service.
Create a folder to mount the volume
Similar to the Dockerize NodeJS article with Mongo and Redis in this article, we will create folders to save data from MySQL and Redis so that when our project restarts, all data will remain intact.
In the .docker folder you create yourself 1 folder named data , inside the data folder you create yourself 2 folders named db (save data for MySQL) and redis okay
At this point, our .docker folder will look like this:
Run the application
Configure docker-compose
To run this application, we will create a docker-compose file with the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | <span class="token key atrule">version</span> <span class="token punctuation">:</span> <span class="token string">'3.4'</span> <span class="token key atrule">services</span> <span class="token punctuation">:</span> <span class="token comment">#PHP Service</span> <span class="token key atrule">app</span> <span class="token punctuation">:</span> <span class="token key atrule">build</span> <span class="token punctuation">:</span> <span class="token key atrule">context</span> <span class="token punctuation">:</span> . <span class="token key atrule">dockerfile</span> <span class="token punctuation">:</span> Dockerfile <span class="token key atrule">restart</span> <span class="token punctuation">:</span> unless <span class="token punctuation">-</span> stopped <span class="token key atrule">working_dir</span> <span class="token punctuation">:</span> /var/www/html <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> ./ <span class="token punctuation">:</span> /var/www/html <span class="token key atrule">depends_on</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> db <span class="token punctuation">-</span> redis <span class="token punctuation">-</span> laravel_echo_server <span class="token comment">#Nginx Service</span> <span class="token key atrule">webserver</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> nginx <span class="token punctuation">:</span> alpine <span class="token key atrule">restart</span> <span class="token punctuation">:</span> unless <span class="token punctuation">-</span> stopped <span class="token key atrule">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token string">"${APP_PORT}:80"</span> <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> ./ <span class="token punctuation">:</span> /var/www/html <span class="token punctuation">-</span> .docker/nginx.conf <span class="token punctuation">:</span> /etc/nginx/conf.d/default.conf <span class="token comment"># DB UI management service</span> <span class="token key atrule">adminer</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> adminer <span class="token key atrule">restart</span> <span class="token punctuation">:</span> unless <span class="token punctuation">-</span> stopped <span class="token key atrule">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> $ <span class="token punctuation">{</span> ADMINER_PORT <span class="token punctuation">}</span> <span class="token punctuation">:</span> <span class="token number">8080</span> <span class="token key atrule">depends_on</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> db <span class="token comment">#MySQL Service</span> <span class="token key atrule">db</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> mysql <span class="token punctuation">:</span> 5.7.22 <span class="token key atrule">restart</span> <span class="token punctuation">:</span> unless <span class="token punctuation">-</span> stopped <span class="token key atrule">environment</span> <span class="token punctuation">:</span> <span class="token key atrule">MYSQL_DATABASE</span> <span class="token punctuation">:</span> $ <span class="token punctuation">{</span> DB_DATABASE <span class="token punctuation">}</span> <span class="token key atrule">MYSQL_USER</span> <span class="token punctuation">:</span> $ <span class="token punctuation">{</span> DB_USERNAME <span class="token punctuation">}</span> <span class="token key atrule">MYSQL_PASSWORD</span> <span class="token punctuation">:</span> $ <span class="token punctuation">{</span> DB_PASSWORD <span class="token punctuation">}</span> <span class="token key atrule">MYSQL_ROOT_PASSWORD</span> <span class="token punctuation">:</span> rootpass <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> .docker/data/db <span class="token punctuation">:</span> /var/lib/mysql/ <span class="token key atrule">laravel_echo_server</span> <span class="token punctuation">:</span> <span class="token key atrule">build</span> <span class="token punctuation">:</span> <span class="token key atrule">context</span> <span class="token punctuation">:</span> .docker/laravel <span class="token punctuation">-</span> echo <span class="token punctuation">-</span> server <span class="token key atrule">dockerfile</span> <span class="token punctuation">:</span> Dockerfile <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> .env <span class="token punctuation">:</span> /app/.env <span class="token key atrule">restart</span> <span class="token punctuation">:</span> unless <span class="token punctuation">-</span> stopped <span class="token key atrule">redis</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> redis <span class="token punctuation">:</span> 5 <span class="token punctuation">-</span> alpine <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> .docker/data/redis <span class="token punctuation">:</span> /data <span class="token key atrule">restart</span> <span class="token punctuation">:</span> unless <span class="token punctuation">-</span> stopped |
The content of the file above is similar to the previous article, right? There are some places I explain so if any of you have any questions:
- In the service db, the environment information will be taken from variables in the .env file (we will create it shortly).
- Similarly in the laravel-echo-server service , we will not use the environment to set an environment variable that Laravel Echo Server will read directly from the .env file (this section is specified in the github of the Laravel echo server )
- In the service webserver and adminer in the mapping port, we have to leave the values of two environment variables later to be defined in the .env file.
- As I said in the service app and laravel-echo-server will be built using docker-compose , you can see we have a build field and specify the context (the context is in which path) plus what is Dockerfile’s name . When running the app, docker-compose will find and build each service for us
Create .env file
Next we will create a .env file. You create a file named .env and copy the content from the file .env.example to offline.
Then you open the file .env and fix some places as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | DB_CONNECTION=mysql DB_HOST=db DB_PORT=3306 DB_DATABASE=laravel DB_USERNAME=laraveluser DB_PASSWORD=laraveluserpass BROADCAST_DRIVER=redis CACHE_DRIVER=redis QUEUE_CONNECTION=redis SESSION_DRIVER=redis SESSION_LIFETIME=120 REDIS_HOST=redis REDIS_PASSWORD=null REDIS_PORT=6379 .... LARAVEL_ECHO_SERVER_REDIS_HOST=redis LARAVEL_ECHO_SERVER_REDIS_PORT=6379 LARAVEL_ECHO_SERVER_AUTH_HOST=http://webserver:80 LARAVEL_ECHO_SERVER_DEBUG=true APP_PORT=4000 MIX_FRONTEND_PORT=4000 ADMINER_PORT=8080 |
Explain:
- Above we have the configuration parameters connected to the database, you notice DB_HOST must match the name of the service we defined in docker-compose.yml . Same for the Redis configuration section
Moment of truth
Yes, so after a 1-year long configuration post, it’s time for us to test this project to see if it’s hard to get any results.
You run the following command to launch the project:
1 2 | docker-compose up -d --build |
After the above command is successful, open the browser at localhost: 4000 .
And ….. MUD blank, see nothing
The problem here is that we have not run composer install or npm install so the service app (the Laravel ** part ** fails)
So the problem is: how can you run composer or npm while the service app environment doesn’t have Composer or NodeJS?
Install dependencies (composer install, npm install, …)
As in Dockerize VueJS, I mentioned using temporary containers to install dependencies instead of installing directly into images.
This will help to reduce the size of the image, because the installation of this dependencies does not occur often, but we usually only do it once in the beginning of the setup project.
You run the following command in turn:
1 2 3 4 5 6 7 8 9 10 | docker run --rm -v $(pwd):/app -w /app composer install --ignore-platform-reqs --no-autoloader --no-dev --no-interaction --no-progress --no-suggest --no-scripts --prefer-dist docker run --rm -v $(pwd):/app -w /app composer dump-autoload --classmap-authoritative --no-dev --optimize docker run --rm -v $(pwd):/app -w /app node npm install --production docker run --rm -v $(pwd):/app -w /app node npm run prod |
Explain:
- First we create a temporary container from the image compose (with pre-installed composer) and we run composer install with some options behind (search google for more about these options)
- Next we create a temporary container still from the image composer and run dump-autoload to load the PHP library classes
- Next we create a container from the image node and run npm install we have the –production option “to install only things in the dependencies section of the package.json file
- Finally, we build the VueJS code (if you want to watch it, change it to run watch).
Moment of truth (part 2)
Once completed we return to the browser and F5:
I have seen the app launch and report that there is no key yet.
Before running the command to create the key, please note: all php artisan commands … from now on we will always run as docker-compose exec app php artisan …. (note the first part). Why? Because when running outside, Laravel will rely on the context of the external environment to run, so commands like migrate will fail. So to sync and for you to know, we always run with docker-compose exec … okay
We run the following command to create the key and create a DB and seed as well
1 2 3 4 | docker-compose exec app php artisan key:generate docker-compose exec app php artisan migrate --seed |
Then we reloaded the browser and …… BUOM
Was successful
You try to create 2 accounts and chat with each other to see how. This part I let you take a selfie
To dabble in the database administration section, access localhost: 8080
To access the Horizon section you access the address localhost: 4000 / horizon okay
Since we have mounted the volume from the root environment into the container, you can directly modify the code and see the changes. Note if you fix the VueJS code, remember to run npm
End
Phewuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu …….. The song is filled with the sky, billions of new knowledge, blurred vision, and whirling mind.
Dockerize is a chat application that spray nasal blood
This song is also quite long, but I do not want to split into 2 tracks because it will be a bit in the middle. The Laravel code I have made available for you and we only focus on the Docker.
In this article there is a lot of knowledge related to Linux, I can not explain it little by little for you, but you can copy and paste and search google will have all the information offline.
There will also be parts of the Linux knowledge that you see as “even if the search does not understand”, then we “accept” to use and gradually absorb it. In the past, when I first dockedize Laravel, some parts about my supervisor were almost blind, who told me what to do, now I’m getting better. . Personally, I see through these projects our Linux submission will increase significantly
Through this article hope you have understood how to Dockerize a project Laravel full options , full of things like DB, Redis, Queue Job, Cronjob, … in what would be like offline. From there apply and apply into practice accordingly. At the same time we also note that even though we use many things MySQL, Redis, Nginx, Adminer, PHP, Composer NodeJS … but all are run in Docker containers, our original environment is still “virgin”.
If there is a problem, please leave a comment for me.
The entire source code to the final step of this article I put here (branch complete-tutorial okay)
Thank you for watching. See you in the following post ^^