Log for self-building docker-compose.yml
for docker-compose.yml
“Hello World” project.
Before you begin
- You need a basic understanding of Docker and Docker compose.
- Knowledge of nginx, configuring virtual host for nginx.
- The article will setup virtual with
phpinfo()
function first. Then change the root path to the Laravel project to run your application at a basic level.
Let’s start with installing without Docker ..
1 2 3 | Khoan nói về các ứng dụng Laravel phức tạp, ở đây, tôi chỉ đề cập ở mức độ cài đặt Laravel project trang chủ và có thể migrate bảng user có sẵn. |
Start installing apps from not using Docker. LAMP (Linux, Apache2, MySQL, PHP) , LEMP (Linux, Nginx, MySQL, PHP) are the two classic “search” phrases. It is a set of tools that help you install a regular web application to work.
Basically, every web application to run you need at least the following 3 parts:
- Web server: Nginx or Apache2 are popular choices
- Programming language: of course
, you must tell the web server what programming language you use.
- Database: Where to save data for the application
LAMP, LEMP are all 3 parts above (Linux, I will talk later). Within the scope of this article, I will use LEMP.
Once LEMP is installed, we need to configure a virtual host to run the application with the domain. First of all, let’s just display the PHP information via phpinfo()
, then transfer the configuration to the Laravel project.
Step 1: You need to change nginx configuration
You need to have nginx point to the host containing the index.php
file. It basically looks like this:
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 | server { listen 80; server_name localhost; # Cái này quan trọng nè, root của project root /var/www/html; index index.php index.html index.htm; error_log /var/log/nginx/laravel.error.log; access_log /var/log/nginx/laravel.access.log; location / { try_files $uri $uri/ /index.php?$query_string; } # PHP-FPM Configuration Nginx location ~ .php$ { try_files $uri = 404; fastcgi_split_path_info ^(.+.php)(/.+)$; fastcgi_pass localhost:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param REQUEST_URI $request_uri; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_path_info; } } |
Step 2: Add the index.php
file
There is no need to explain much. From the above configuration, we can see, you need to have an index.php
file in /var/www/html
‘re done.
1 2 3 4 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">echo</span> <span class="token function">phpinfo</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token delimiter important">?></span></span> |
So when you go to http: // localhost , the PHP information is displayed.
The configuration switch for the Laravel application, just change the root path, I include it in part 2.
Thinking docker LEMP
Docker turns HelloWorld first
To docker the parts as in section 1, we need 3 images corresponding to the 3 parts in LEMP above: nginx, mysql and php-fpm.
Start from the easy first. The database I use MySQl, simply pulls the image of mysql back, does not need to install anything.
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token key atrule">services</span> <span class="token punctuation">:</span> <span class="token key atrule">mysql</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> mysql <span class="token punctuation">:</span> <span class="token number">5.7</span> <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 string">'laravel'</span> <span class="token key atrule">MYSQL_USER</span> <span class="token punctuation">:</span> <span class="token string">'mingnv'</span> <span class="token key atrule">MYSQL_PASSWORD</span> <span class="token punctuation">:</span> <span class="token string">'password'</span> <span class="token key atrule">MYSQL_ROOT_PASSWORD</span> <span class="token punctuation">:</span> <span class="token string">'password'</span> <span class="token comment"># Volumes này đang sử dụng theo dạng biến đó</span> <span class="token comment"># Nó tương đương khai báo VOLUME trong Dockerfile, sinh volume dạng mã băm</span> <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> db_data <span class="token punctuation">:</span> /var/lib/mysql |
- The game is easy, so there is a database to store data, next we will pull the PHP image via
php-fpm
image
1 2 3 4 | <span class="token key atrule">services</span> <span class="token punctuation">:</span> <span class="token key atrule">php-fpm</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> php <span class="token punctuation">:</span> 7.2 <span class="token punctuation">-</span> fpm |
- Similar to the database. But wait.
php-fpm
cannot be alone. It needs to talk to the web server, combined to handle requests from the user …. To be continue !!! - Web server: this part is the most important part of the web application. Here I use
nginx
so I will pull the nginx image. But as I said above, the web server cannot work alone. It needs a configuration file, points to your host, gets the code and works. Therefore you need a Dockerfile file to build.
1 2 3 4 5 6 | <span class="token keyword">FROM</span> nginx <span class="token punctuation">:</span> latest <span class="token comment"># Change Nginx config here...</span> <span class="token keyword">RUN</span> rm /etc/nginx/conf.d/default.conf <span class="token keyword">COPY</span> ./default.conf /etc/nginx/conf.d/ |
The default config will not point to the host, so we will use the conf file above to replace nginx’s default.conf
. Remember to replace the php-fpm bằng
correct host contianer
Now that you have the application up and running, docker-compose.yml
will look like this
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 | <span class="token comment"># Sử dụng version 3.3 cho nó mới</span> <span class="token key atrule">version</span> <span class="token punctuation">:</span> <span class="token string">'3.3'</span> <span class="token key atrule">services</span> <span class="token punctuation">:</span> <span class="token key atrule">nginx</span> <span class="token punctuation">:</span> <span class="token key atrule">build</span> <span class="token punctuation">:</span> ./nginx <span class="token key atrule">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token string">"8080:80"</span> <span class="token comment"># Thêm depends_on để nó start php-fpm trước khi start nginx, nếu không sẽ bị báo lỗi host php-fpm không tìm thấy</span> <span class="token key atrule">depends_on</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> php <span class="token punctuation">-</span> fpm <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token comment"># Logging</span> <span class="token punctuation">-</span> ./nginx/log <span class="token punctuation">:</span> /var/log/nginx/ <span class="token comment"># Volume đến code ứng dụng</span> <span class="token punctuation">-</span> ./nginx/index.php <span class="token punctuation">:</span> /var/www/html/index.php <span class="token key atrule">mysql</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> mysql <span class="token punctuation">:</span> <span class="token number">5.7</span> <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 string">'laravel'</span> <span class="token key atrule">MYSQL_USER</span> <span class="token punctuation">:</span> <span class="token string">'mingnv'</span> <span class="token key atrule">MYSQL_PASSWORD</span> <span class="token punctuation">:</span> <span class="token string">'password'</span> <span class="token key atrule">MYSQL_ROOT_PASSWORD</span> <span class="token punctuation">:</span> <span class="token string">'password'</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">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> 8081 <span class="token punctuation">:</span> <span class="token number">8080</span> <span class="token key atrule">php-fpm</span> <span class="token punctuation">:</span> <span class="token key atrule">image</span> <span class="token punctuation">:</span> php <span class="token punctuation">:</span> 7.2 <span class="token punctuation">-</span> fpm |
Perform docker-compose up
to run. Hmm, an error occurred.
I was really confused by the day-to-day error and it took me 2 days to fix it. The reason is that it cannot find the index.php
file while running php-fpm
. After consulting a few documents, I understood, although nginx
and php-fpm
have talked to each other, its folders and files are not shared. So if you only volumes
file index.php
for nginx
, when manipulation of php-fpm
not find this file.
1 2 3 | Tất cả các services làm việc với source code (nginx, php-fpm và sau này xuất hiện thêm bạn workspace) đều cần volumes chung với nhau và volumes đến source code folder. |
And the final result file docker-compose.yml
will be as follows:
Once the volume has been set for php-fpm
and nginx
, the application is up.
Level up for any Laravel apps!
In step 2.1, we have gone to phpinfo()
. In the next step, we will replace this page with the Laravel application.
Simple work, adjust the root path of the application is done.
https://github.com/minhnv2306/laravel-docker/commit/35c4920de3ec51a91c5bcc59b4014e743181b6b3
That’s right, Laravel application is up. You have tried connecting them to the database.
1 2 | docker exec php-fpm php artisan migrate |
The result is a notification missing mysql-extension
. So we can see that the php-fpm
image has only the most basic php extensions, not yet supporting mysql-extension
. Our next job is to update this extension
1 2 | Để cài thêm các extension cho php trong Docker, bạn sử dụng docker-php-ext-install |
1 2 3 4 5 6 7 8 | <span class="token keyword">FROM</span> php <span class="token punctuation">:</span> 7.2 <span class="token punctuation">-</span> fpm <span class="token keyword">RUN</span> apt <span class="token punctuation">-</span> get update && apt <span class="token punctuation">-</span> get install <span class="token punctuation">-</span> y vim && docker <span class="token punctuation">-</span> php <span class="token punctuation">-</span> ext <span class="token punctuation">-</span> install mysqli pdo_mysql <span class="token keyword">EXPOSE</span> 9000 |
And this time, it has gone well .
We need more than that …
Our application is already beautiful, at a glance. Do you remember I mentioned Linux and said the following section will mention.
In parts 1,2, my application has been installed composer
, npm
already installed on the device, the new application can work. In other words, I’ve run these commands before from my local machine:
1 2 3 4 5 | composer <span class="token function">install</span> <span class="token function">npm</span> <span class="token function">install</span> <span class="token function">npm</span> run dev |
But that is not quite right with Docker, independent of your environment. We need a place called workspace
to work with the source code, such as installing git, viewing the source code, installing copmoser, npm, and similar source management tools. I seem to mention the operating system, right?
Yes, we will separate them into a separate container called “workspace” , the workplace with source code, install the application.
That is just the part you need to add. Besides, you can add as “data” container just to store data for the application, “data_test” container to store data for the application when running the test.
You can refer to the services here
- nginx
- application (for storing project source code)
- php-fpm
- workspace (for working around with the all project)
- mysql
- mysql_test (for running integration test)
- mongodb
- redis
- data (for storing mysql, mongo, redis data)
- data_test (for storing mysql, mongo, redis data while running test)
- logs (for storing some system logs)
Separate variables into configuration …
For added danger, add the .env
variable when running docker-compose
. Another benefit is that it helps you maintain and change parameters without having to edit them directly in docker-composer.yml
.
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 | <span class="token key atrule">version</span> <span class="token punctuation">:</span> <span class="token string">'3'</span> <span class="token punctuation">...</span> <span class="token key atrule">services</span> <span class="token punctuation">:</span> <span class="token comment">### NGINX Server #########################################</span> <span class="token key atrule">nginx</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> ./nginx <span class="token key atrule">args</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> CHANGE_SOURCE=$ <span class="token punctuation">{</span> CHANGE_SOURCE <span class="token punctuation">}</span> <span class="token punctuation">-</span> PHP_UPSTREAM_CONTAINER=$ <span class="token punctuation">{</span> NGINX_PHP_UPSTREAM_CONTAINER <span class="token punctuation">}</span> <span class="token punctuation">-</span> PHP_UPSTREAM_PORT=$ <span class="token punctuation">{</span> NGINX_PHP_UPSTREAM_PORT <span class="token punctuation">}</span> <span class="token punctuation">-</span> http_proxy <span class="token punctuation">-</span> https_proxy <span class="token punctuation">-</span> no_proxy <span class="token key atrule">volumes</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> $ <span class="token punctuation">{</span> APP_CODE_PATH_HOST <span class="token punctuation">}</span> <span class="token punctuation">:</span> $ <span class="token punctuation">{</span> APP_CODE_PATH_CONTAINER <span class="token punctuation">}</span> $ <span class="token punctuation">{</span> APP_CODE_CONTAINER_FLAG <span class="token punctuation">}</span> <span class="token punctuation">-</span> $ <span class="token punctuation">{</span> NGINX_HOST_LOG_PATH <span class="token punctuation">}</span> <span class="token punctuation">:</span> /var/log/nginx <span class="token punctuation">-</span> $ <span class="token punctuation">{</span> NGINX_SITES_PATH <span class="token punctuation">}</span> <span class="token punctuation">:</span> /etc/nginx/sites <span class="token punctuation">-</span> available <span class="token punctuation">-</span> $ <span class="token punctuation">{</span> NGINX_SSL_PATH <span class="token punctuation">}</span> <span class="token punctuation">:</span> /etc/nginx/ssl <span class="token key atrule">ports</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token string">"${NGINX_HOST_HTTP_PORT}:80"</span> <span class="token punctuation">-</span> <span class="token string">"${NGINX_HOST_HTTPS_PORT}:443"</span> <span class="token punctuation">-</span> <span class="token string">"${VARNISH_BACKEND_PORT}:81"</span> <span class="token key atrule">depends_on</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> php <span class="token punctuation">-</span> fpm <span class="token key atrule">networks</span> <span class="token punctuation">:</span> <span class="token punctuation">-</span> frontend <span class="token punctuation">-</span> backend <span class="token punctuation">...</span> |
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token punctuation">...</span> <span class="token comment">### NGINX #################################################</span> NGINX_HOST_HTTP_PORT=80 NGINX_HOST_HTTPS_PORT=443 NGINX_HOST_LOG_PATH=./logs/nginx/ NGINX_SITES_PATH=./nginx/sites/ NGINX_PHP_UPSTREAM_CONTAINER=php <span class="token punctuation">-</span> fpm NGINX_PHP_UPSTREAM_PORT=9000 NGINX_SSL_PATH=./nginx/ssl/ <span class="token punctuation">...</span> |
In the end, I concluded
- You should separate the application into separate services for easy management, which is also part of the best practice for dockers
- All services that work with source code (nginx, php-fpm, workspace …) need volumes together and volumes to the source code folder.
- You need to have basic services to run the application (php-fpm, nginx, database). Besides is a service to work with the source code, install the source code (composer, npm) with the name workspace .
1 2 3 4 5 6 7 8 9 10 | docker-compose <span class="token function">exec</span> workspace composer <span class="token function">install</span> docker-compose <span class="token function">exec</span> workspace php artisan key:generate docker-compose <span class="token function">exec</span> workspace php artisan migrate docker-compose <span class="token function">exec</span> workspace php artisan db:seed docker-compose <span class="token function">exec</span> workspace yarn docker-compose <span class="token function">exec</span> workspace yarn <span class="token function">watch</span> <span class="token comment"># For testing</span> docker-compose <span class="token function">exec</span> workspace php artisan db:seed --class <span class="token operator">=</span> TestDataSeeder |
- Separate parameters in
docker-composer.yml
into configuration file for easy management and change.