I. Introduction
Over the years, Docker has become a frequently used solution for quick deployment of applications by simplifying the running and deploying of applications in container
(containers). When we use LEMP
, for example, with PHP
, Nginx
, Mysql
and Laravel framework
, Docker can simplify the setup process.
Docker Compose further simplifies the development process by allowing developers to define their infrastructure, including application services
, networks
and networks
( networks
) volums
) in a single file. Docker Compose provides an effective alternative to running multiple docker container creation and docker container running commands.
In this tutorial, you will build a Laravel web application, with Nginx
being the web server and MySQL
as the database, all inside the Docker containers. You will define the entire configuration in the docker editor file, along with the configuration files for PHP, MySQL and Nginx.
II. Application building
1. Download Laravel and install the Dependencies
In this step, we install Laravel, you can go to Laravel’s doc to review how to install here
First, go to your working directory (here I choose /var/www
) and then proceed to clone the Laravel code with the command
1 2 3 | cd <span class="token operator">~</span> git clone https <span class="token punctuation">:</span> <span class="token comment">//github.com/laravel/laravel.git laravel-app</span> |
Go to the newly created laravel-app directory
1 2 | docker run <span class="token operator">--</span> rm <span class="token operator">-</span> v $ <span class="token punctuation">(</span> pwd <span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token operator">/</span> app composer install |
Next, use the Docker composer image to mount the folders you need in the project and avoid having to install Composer globally.
Using -v and -rm with the docker run creates containers, these containers will be connected to your current directory before being deleted. This will copy your laravel-app folder into the container and make sure that the vendor directory created inside is a copy of your root directory.
Finally, we grant permissions on the project directory with non-root user:
1 2 | sudo chown <span class="token operator">-</span> R <span class="token variable">$USER</span> <span class="token punctuation">:</span> <span class="token variable">$USER</span> <span class="token operator">~</span> <span class="token operator">/</span> laravel <span class="token operator">-</span> app |
2. Create Docker Compose File
Building your applications with Docker Compose will help you to simplify the infrastructure setup. To establish Laravel application, we will file docker-compose
to determine web server, database and application service.
In the laravel-app
directory, we create the docker-composer.yml
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 64 65 | version <span class="token punctuation">:</span> <span class="token single-quoted-string string">'3'</span> services <span class="token punctuation">:</span> <span class="token shell-comment comment">#PHP Service</span> app <span class="token punctuation">:</span> build <span class="token punctuation">:</span> context <span class="token punctuation">:</span> <span class="token punctuation">.</span> dockerfile <span class="token punctuation">:</span> Dockerfile image <span class="token punctuation">:</span> digitalocean <span class="token punctuation">.</span> com <span class="token operator">/</span> php container_name <span class="token punctuation">:</span> app restart <span class="token punctuation">:</span> unless <span class="token operator">-</span> stopped tty <span class="token punctuation">:</span> <span class="token boolean">true</span> environment <span class="token punctuation">:</span> <span class="token constant">SERVICE_NAME</span> <span class="token punctuation">:</span> app <span class="token constant">SERVICE_TAGS</span> <span class="token punctuation">:</span> dev working_dir <span class="token punctuation">:</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www volumes <span class="token punctuation">:</span> <span class="token operator">-</span> <span class="token punctuation">.</span> <span class="token operator">/</span> <span class="token punctuation">:</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www <span class="token operator">-</span> <span class="token punctuation">.</span> <span class="token operator">/</span> php <span class="token operator">/</span> local <span class="token punctuation">.</span> ini <span class="token punctuation">:</span> <span class="token operator">/</span> usr <span class="token operator">/</span> local <span class="token operator">/</span> etc <span class="token operator">/</span> php <span class="token operator">/</span> conf <span class="token punctuation">.</span> d <span class="token operator">/</span> local <span class="token punctuation">.</span> ini networks <span class="token punctuation">:</span> <span class="token operator">-</span> app <span class="token operator">-</span> network <span class="token shell-comment comment">#Nginx Service</span> webserver <span class="token punctuation">:</span> image <span class="token punctuation">:</span> nginx <span class="token punctuation">:</span> alpine container_name <span class="token punctuation">:</span> webserver restart <span class="token punctuation">:</span> unless <span class="token operator">-</span> stopped tty <span class="token punctuation">:</span> <span class="token boolean">true</span> ports <span class="token punctuation">:</span> <span class="token operator">-</span> <span class="token double-quoted-string string">"80:80"</span> <span class="token operator">-</span> <span class="token double-quoted-string string">"443:443"</span> volumes <span class="token punctuation">:</span> <span class="token operator">-</span> <span class="token punctuation">.</span> <span class="token operator">/</span> <span class="token punctuation">:</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www <span class="token operator">-</span> <span class="token punctuation">.</span> <span class="token operator">/</span> nginx <span class="token operator">/</span> conf <span class="token punctuation">.</span> d <span class="token operator">/</span> <span class="token punctuation">:</span> <span class="token operator">/</span> etc <span class="token operator">/</span> nginx <span class="token operator">/</span> conf <span class="token punctuation">.</span> d <span class="token operator">/</span> networks <span class="token punctuation">:</span> <span class="token operator">-</span> app <span class="token operator">-</span> network <span class="token shell-comment comment">#MySQL Service</span> db <span class="token punctuation">:</span> image <span class="token punctuation">:</span> mysql <span class="token punctuation">:</span> <span class="token number">5.7</span> <span class="token number">.22</span> container_name <span class="token punctuation">:</span> db restart <span class="token punctuation">:</span> unless <span class="token operator">-</span> stopped tty <span class="token punctuation">:</span> <span class="token boolean">true</span> ports <span class="token punctuation">:</span> <span class="token operator">-</span> <span class="token double-quoted-string string">"3306:3306"</span> environment <span class="token punctuation">:</span> <span class="token constant">MYSQL_DATABASE</span> <span class="token punctuation">:</span> laravel <span class="token constant">MYSQL_ROOT_PASSWORD</span> <span class="token punctuation">:</span> your_mysql_root_password <span class="token constant">SERVICE_TAGS</span> <span class="token punctuation">:</span> dev <span class="token constant">SERVICE_NAME</span> <span class="token punctuation">:</span> mysql volumes <span class="token punctuation">:</span> <span class="token operator">-</span> dbdata <span class="token punctuation">:</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> lib <span class="token operator">/</span> mysql <span class="token operator">/</span> <span class="token operator">-</span> <span class="token punctuation">.</span> <span class="token operator">/</span> mysql <span class="token operator">/</span> my <span class="token punctuation">.</span> cnf <span class="token punctuation">:</span> <span class="token operator">/</span> etc <span class="token operator">/</span> mysql <span class="token operator">/</span> my <span class="token punctuation">.</span> cnf networks <span class="token punctuation">:</span> <span class="token operator">-</span> app <span class="token operator">-</span> network <span class="token shell-comment comment">#Docker Networks</span> networks <span class="token punctuation">:</span> app <span class="token operator">-</span> network <span class="token punctuation">:</span> driver <span class="token punctuation">:</span> bridge <span class="token shell-comment comment">#Volumes</span> volumes <span class="token punctuation">:</span> dbdata <span class="token punctuation">:</span> driver <span class="token punctuation">:</span> local |
Explain file docker-compose.yml
1 bit, here we identify there are 3 services: app , webserver and database
- app : This definition contains the Laravel application and runs a custom Docker image : digitalocean.com/php , as shown in Step 4, it sets working_dir in the container to / var / www.
- webserver : The definition of this service will pull the nginx: alpine image from docker and run on ports 80 and 443
- db : The definition of the service is pulled from image mysql: 5.7.22 from Docker and has identified several environment variables including the
database
set for your application named laravel (this is the name database), along with theroot
password for the database, you can give another name to the database and set the password you want. This service will map port 3306 on the host to port 3306 on the container. - container_name : To determine the name of the container, corresponding to the name of the service, if you do not specify a name, Docker will assign a name to each container.
- app-network : To create a connection between containers, services will be connected via app-network.
- dbdata : is a volume with the contents of the / var / lib / mysql folder inside the container. This allows you to stop and start the db service without losing data, at the end of the
docker-compose.yml
file we also see dbdata, with this definition we can use this volume through many services.
3. Create Dockerfile
You create a file called Dockerfile
and stored in ~ / laravel-app , the content of the file is 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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | <span class="token constant">FROM</span> php <span class="token punctuation">:</span> <span class="token number">7.2</span> <span class="token operator">-</span> fpm <span class="token shell-comment comment"># Copy composer.lock and composer.json</span> <span class="token constant">COPY</span> composer <span class="token punctuation">.</span> lock composer <span class="token punctuation">.</span> json <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www <span class="token operator">/</span> <span class="token shell-comment comment"># Set working directory</span> <span class="token constant">WORKDIR</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www <span class="token shell-comment comment"># Install dependencies</span> <span class="token constant">RUN</span> apt <span class="token operator">-</span> get update <span class="token operator">&&</span> apt <span class="token operator">-</span> get install <span class="token operator">-</span> y build <span class="token operator">-</span> essential <span class="token keyword">default</span> <span class="token operator">-</span> mysql <span class="token operator">-</span> client libpng <span class="token operator">-</span> dev libjpeg62 <span class="token operator">-</span> turbo <span class="token operator">-</span> dev libfreetype6 <span class="token operator">-</span> dev locales zip jpegoptim optipng pngquant gifsicle vim unzip git curl <span class="token shell-comment comment"># Clear cache</span> <span class="token constant">RUN</span> apt <span class="token operator">-</span> get clean <span class="token operator">&&</span> rm <span class="token operator">-</span> rf <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> lib <span class="token operator">/</span> apt <span class="token operator">/</span> lists <span class="token operator">/</span> <span class="token operator">*</span> <span class="token shell-comment comment"># Install extensions</span> <span class="token constant">RUN</span> docker <span class="token operator">-</span> php <span class="token operator">-</span> ext <span class="token operator">-</span> install pdo_mysql mbstring zip exif pcntl <span class="token constant">RUN</span> docker <span class="token operator">-</span> php <span class="token operator">-</span> ext <span class="token operator">-</span> configure gd <span class="token operator">--</span> with <span class="token operator">-</span> gd <span class="token operator">--</span> with <span class="token operator">-</span> freetype <span class="token operator">-</span> dir <span class="token operator">=</span> <span class="token operator">/</span> usr <span class="token operator">/</span> <span class="token keyword">include</span> <span class="token operator">/</span> <span class="token operator">--</span> with <span class="token operator">-</span> jpeg <span class="token operator">-</span> dir <span class="token operator">=</span> <span class="token operator">/</span> usr <span class="token operator">/</span> <span class="token keyword">include</span> <span class="token operator">/</span> <span class="token operator">--</span> with <span class="token operator">-</span> png <span class="token operator">-</span> dir <span class="token operator">=</span> <span class="token operator">/</span> usr <span class="token operator">/</span> <span class="token keyword">include</span> <span class="token operator">/</span> <span class="token constant">RUN</span> docker <span class="token operator">-</span> php <span class="token operator">-</span> ext <span class="token operator">-</span> install gd <span class="token shell-comment comment"># Install composer</span> <span class="token constant">RUN</span> curl <span class="token operator">-</span> sS https <span class="token punctuation">:</span> <span class="token comment">//getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer</span> <span class="token shell-comment comment"># Add user for laravel application</span> <span class="token constant">RUN</span> groupadd <span class="token operator">-</span> g <span class="token number">1000</span> www <span class="token constant">RUN</span> useradd <span class="token operator">-</span> u <span class="token number">1000</span> <span class="token operator">-</span> ms <span class="token operator">/</span> bin <span class="token operator">/</span> bash <span class="token operator">-</span> g www www <span class="token shell-comment comment"># Copy existing application directory contents</span> <span class="token constant">COPY</span> <span class="token punctuation">.</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www <span class="token shell-comment comment"># Copy existing application directory permissions</span> <span class="token constant">COPY</span> <span class="token operator">--</span> chown <span class="token operator">=</span> www <span class="token punctuation">:</span> www <span class="token punctuation">.</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www <span class="token shell-comment comment"># Change current user to www</span> <span class="token constant">USER</span> www <span class="token shell-comment comment"># Expose port 9000 and start php-fpm server</span> <span class="token constant">EXPOSE</span> <span class="token number">9000</span> <span class="token constant">CMD</span> <span class="token punctuation">[</span> <span class="token double-quoted-string string">"php-fpm"</span> <span class="token punctuation">]</span> |
Explain the Dockerfile file a bit:
- First, Dockerfile creates an image on the top of a Docker image, php: 7.2-fpm , this image is pre-installed with php-fpm.
- RUN to perform the update, install and configuration within a container, including specifying the
user
and thewww
group - WORKDIR points to the working directory for the container application.
- Creating a dedicated user and group with limited permissions will minimize the inherent vulnerability when running Docker containers, because the default is root . Instead of running this container as root, we created the user www , who has read / write access to
/var/www
via the COPY command and the – chown to copy the permissions of the application directory. - Finally, the EXPOSE command displays a port in the container, 9000, for the
php-fpm
server. CMD specifies the command to run when the container is created. Here, CMD specifies “php-fpm”, which will start the server.
4. Configure PHP
Now that you define the infrastructure via docker-compose.yml, you can configure the PHP service to handle requests from Nginx.
To configure PHP, you will create a local.ini file inside the ~ / laravel-app / php directory. This is the file you associated with /usr/local/etc/php/conf.d/local.ini inside the container in section 2. Creating this file will allow you to override the default php.ini
file that PHP Read when it starts booting:
5. Configuring Nginx
To configure Nginx, create an app.conf file inside the ~ / laravel-app / nginx / conf.d / directory , 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 | server <span class="token punctuation">{</span> listen <span class="token number">80</span> <span class="token punctuation">;</span> index index <span class="token punctuation">.</span> php index <span class="token punctuation">.</span> html <span class="token punctuation">;</span> error_log <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> log <span class="token operator">/</span> nginx <span class="token operator">/</span> error <span class="token punctuation">.</span> log <span class="token punctuation">;</span> access_log <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> log <span class="token operator">/</span> nginx <span class="token operator">/</span> access <span class="token punctuation">.</span> log <span class="token punctuation">;</span> root <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> www <span class="token operator">/</span> <span class="token keyword">public</span> <span class="token punctuation">;</span> location <span class="token operator">~</span> <span class="token punctuation">.</span> php$ <span class="token punctuation">{</span> try_files <span class="token variable">$uri</span> <span class="token operator">=</span> <span class="token number">404</span> <span class="token punctuation">;</span> fastcgi_split_path_info <span class="token operator">^</span> <span class="token punctuation">(</span> <span class="token punctuation">.</span> <span class="token operator">+</span> <span class="token punctuation">.</span> php <span class="token punctuation">)</span> <span class="token punctuation">(</span> <span class="token operator">/</span> <span class="token punctuation">.</span> <span class="token operator">+</span> <span class="token punctuation">)</span> $ <span class="token punctuation">;</span> fastcgi_pass app <span class="token punctuation">:</span> <span class="token number">9000</span> <span class="token punctuation">;</span> fastcgi_index index <span class="token punctuation">.</span> php <span class="token punctuation">;</span> <span class="token keyword">include</span> fastcgi_params <span class="token punctuation">;</span> fastcgi_param <span class="token constant">SCRIPT_FILENAME</span> <span class="token variable">$document_root</span> <span class="token variable">$fastcgi_script_name</span> <span class="token punctuation">;</span> fastcgi_param <span class="token constant">PATH_INFO</span> <span class="token variable">$fastcgi_path_info</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> location <span class="token operator">/</span> <span class="token punctuation">{</span> try_files <span class="token variable">$uri</span> <span class="token variable">$uri</span> <span class="token operator">/</span> <span class="token operator">/</span> index <span class="token punctuation">.</span> php <span class="token operator">?</span> <span class="token variable">$query_string</span> <span class="token punctuation">;</span> gzip_static on <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
6. Configure MySQL
Create my.cnf file inside ~ / laravel-app / mysql directory with content
1 2 3 4 | <span class="token punctuation">[</span> mysqld <span class="token punctuation">]</span> general_log <span class="token operator">=</span> <span class="token number">1</span> general_log_file <span class="token operator">=</span> <span class="token operator">/</span> <span class="token keyword">var</span> <span class="token operator">/</span> lib <span class="token operator">/</span> mysql <span class="token operator">/</span> general <span class="token punctuation">.</span> log |
7. Run the containers and correct the environment configuration
Now that you have defined all your services in docker-compose and created configuration files for these services, you can start the containers. However, as the final step, we will create the file. env from .env.example to define the environment in the Laravel application.
1 2 | cp <span class="token punctuation">.</span> env <span class="token punctuation">.</span> example <span class="token punctuation">.</span> env |
We will configure in detail when we start running the container.
For all services defined in the docker-compose file, you only need to issue a single command to start all containers, create volumes and set up and connect networks:
1 2 | docker <span class="token operator">-</span> compose up <span class="token operator">-</span> d |
When you run docker-compose for the first time, it will download all the necessary Docker images. When the image is downloaded and stored in your local machine, Compose will create containers. -d
flag to say that running containers is underground.
You can modify the .env configuration in the container with the example docker-compose exec
command to open the .env file we use:
1 2 | docker <span class="token operator">-</span> compose exec app nano <span class="token punctuation">.</span> env |
Also you run more
1 2 3 4 | docker <span class="token operator">-</span> compose exec app php artisan key <span class="token punctuation">:</span> generate docker <span class="token operator">-</span> compose exec app php artisan config <span class="token punctuation">:</span> cache |
The results you will see are as follows:
Good luck !