Development for Docker. Local environment. Part 2 - Nginx + PHP + MySql + phpMyAdmin
For a better understanding of the following material, it is recommended that you first read the Previous Post.
Consider an example of developing a local environment consisting of a bunch of Nginx + PHP + MySql + phpMyAdmin. This bundle is very popular and can satisfy a number of standard needs of an ordinary developer.
As in the previous post, the emphasis will be shifted towards the docker-compose utility than the docker in its purest form.
So let's go!
Let's start with docker-compose.yml, which lies in a separate proxy folder:
The presented file describes the configuration for creating a single container with the name proxy based on the image: jwilder / nginx-proxy image and creating a network with the same name. The networks directive indicates which networks the container is connected to, in this example, this is our proxy network.
When creating a network, the driver: bridge directive could be omitted. A bridge driver is the default driver. This container will communicate over the network with other containers.
The jwilder / nginx-proxy image is basic and taken by the Docker Huba rather extensive and detailed description of its use is also presented there. The principle of operation of nginx-proxy is quite simple: it accesses information about running containers through a forwarded docker socket, analyzes the presence of an environment variable with the name VIRTUAL_HOST and redirects requests from the specified host to the container for which the given environment variable is set.
We start the proxy with the well-known docker-compose up -d command, we observe the following output:
This conclusion informs us that at the beginning the proxy_proxy network was created, and then the proxy_proxy_1 container was created. The network name is obtained from the name of the folder in which the docker-compose.yml file was located, I have a proxy and the network name of the same name.
If you enter the docker network ls command , we will see a list of docker networks in our system and one of them should be proxy_proxy.
The name of the container is built on the same principle as the folder name plus the name of the service and a number that allows containers with similar names not to be duplicated. Through the container_name directive, you can specify the name of the container explicitly, but I find this a rather useless function. This will be discussed in more detail in the following posts.
Create a second docker-compose.yml with the following content:
What is announced here?
Four services are listed: nginx, php, mysql and phpmyadmin. And two networks. One proxy network, called frontend, is declared as an external network and a new internal backend network. The driver for it is not specified, as I wrote earlier, the default driver of the bridge type will be used.
Everything should be clear here. We use the basic image with the docker hub. The environment variable is necessary for the proxy to work and tells it at what address the container should be accessible. The depends_on option indicates the dependence of this container on the php container. This means that the php container will be launched in advance, and after it the nginx container dependent on it will be launched. Next, we forward the configuration for our nginx. It will be a little lower and mount the folder with html. We also notice that the container has access to two networks at once. It must communicate both proxies from the frontend network and php from the backend network. In principle, it would be possible to shove all the containers and shove one frontend into one network, but I adhere that such a separation is more correct.
default.nginx is a config for nginx which is forwarded to the container. The key point here is the fastcgi_pass php directive : 9000 . It sets the address of the FastCGI server. The address can be specified as a domain name or IP address, and port.
php: 9000 - the name of the service is the address of the FastCGI server. Nginx accessing php will get the IP address of the container in which php is running. Port 9000 is a standard port, it is declared when creating the base container. This port is available for nginx over the network, but is not available on the host machine, since it was not forwarded.
It is unusual here that an image is not indicated. Instead, it builds its own image directly from the compose file. The context directive points to the folder in which the Dockerfile is located.
The Dockerfile states that the build uses the basic php image: 7.3.2-fpm, then the commands for installing php extensions are run. Next, composer is copied from another base image and the working directory for the project is set. I will consider assembly issues in more detail in other posts.
Also, the php.ini file and the html folder with our project are thrown inside the container.
Note that php is located on the backend network and, for example, the proxy can no longer access it.
The base mysql image is taken with the 5.7 tag, which is responsible for the mysql version. The ./docker/mysql/data folder is used to store database files (you don’t even need to create it, it is created at startup). And through environment variables, a password is set for the root user, also root.
The database is located on the backend network, which allows it to keep in touch with php. The standard image uses the standard port 3306. It is accessible via the docker network for php, but is not available on the host machine. If you forward for this port, you can connect to it, for example, from the same PHPSTORM. But if the phpmyadmin interface is enough for you, then you can not do this.
The official image of phpmyadmin. The environment variables use VIRTUAL_HOST to interact with the proxy, similar to nginx. PMA_USER and PMA_PASSWORD access to the database. And PMA_HOST the host base itself. But this is not localhost, as is usually the case, but mysql. Those. communication with the database is available by the name of its service, i.e. mysql. The phpmyadmin container can communicate with the database, because it has a connection to the frontend network.
We start the services with the usual command: docker-compose -d.
We see the following output:
We see that at the beginning the lesson2_backend network is created, then the php image is assembled, then images that are not already in the system (pull) can be downloaded and the described services are actually launched.
The final touch, so that everything works, this addition to the hosts or sites.local and phpmyadmin.local domains.
The contents of index.php may be as follows:
Consider an example of developing a local environment consisting of a bunch of Nginx + PHP + MySql + phpMyAdmin. This bundle is very popular and can satisfy a number of standard needs of an ordinary developer.
As in the previous post, the emphasis will be shifted towards the docker-compose utility than the docker in its purest form.
So let's go!
Let's start with docker-compose.yml, which lies in a separate proxy folder:
docker-compose.yml for nginx-proxy
version: '3.0'
services:
proxy:
image: jwilder/nginx-proxy
ports:
- 80:80
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- proxy
networks:
proxy:
driver: bridge
The presented file describes the configuration for creating a single container with the name proxy based on the image: jwilder / nginx-proxy image and creating a network with the same name. The networks directive indicates which networks the container is connected to, in this example, this is our proxy network.
When creating a network, the driver: bridge directive could be omitted. A bridge driver is the default driver. This container will communicate over the network with other containers.
The jwilder / nginx-proxy image is basic and taken by the Docker Huba rather extensive and detailed description of its use is also presented there. The principle of operation of nginx-proxy is quite simple: it accesses information about running containers through a forwarded docker socket, analyzes the presence of an environment variable with the name VIRTUAL_HOST and redirects requests from the specified host to the container for which the given environment variable is set.
We start the proxy with the well-known docker-compose up -d command, we observe the following output:
Creating network "proxy_proxy" with driver "bridge"
Creating proxy_proxy_1 ... done
This conclusion informs us that at the beginning the proxy_proxy network was created, and then the proxy_proxy_1 container was created. The network name is obtained from the name of the folder in which the docker-compose.yml file was located, I have a proxy and the network name of the same name.
If you enter the docker network ls command , we will see a list of docker networks in our system and one of them should be proxy_proxy.
The name of the container is built on the same principle as the folder name plus the name of the service and a number that allows containers with similar names not to be duplicated. Through the container_name directive, you can specify the name of the container explicitly, but I find this a rather useless function. This will be discussed in more detail in the following posts.
Create a second docker-compose.yml with the following content:
docker-compose.yml for other services
version: '3.0'
services:
nginx:
image: nginx
environment:
- VIRTUAL_HOST=site.local
depends_on:
- php
volumes:
- ./docker/nginx/conf.d/default.nginx:/etc/nginx/conf.d/default.conf
- ./html/:/var/www/html/
networks:
- frontend
- backend
php:
build:
context: ./docker/php
volumes:
- ./docker/php/php.ini:/usr/local/etc/php/php.ini
- ./html/:/var/www/html/
networks:
- backend
mysql:
image: mysql:5.7
volumes:
- ./docker/mysql/data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
networks:
- backend
phpmyadmin:
image: phpmyadmin/phpmyadmin:latest
environment:
- VIRTUAL_HOST=phpmyadmin.local
- PMA_HOST=mysql
- PMA_USER=root
- PMA_PASSWORD=root
networks:
- frontend
- backend
networks:
frontend:
external:
name: proxy_proxy
backend:
What is announced here?
Four services are listed: nginx, php, mysql and phpmyadmin. And two networks. One proxy network, called frontend, is declared as an external network and a new internal backend network. The driver for it is not specified, as I wrote earlier, the default driver of the bridge type will be used.
nginx
Everything should be clear here. We use the basic image with the docker hub. The environment variable is necessary for the proxy to work and tells it at what address the container should be accessible. The depends_on option indicates the dependence of this container on the php container. This means that the php container will be launched in advance, and after it the nginx container dependent on it will be launched. Next, we forward the configuration for our nginx. It will be a little lower and mount the folder with html. We also notice that the container has access to two networks at once. It must communicate both proxies from the frontend network and php from the backend network. In principle, it would be possible to shove all the containers and shove one frontend into one network, but I adhere that such a separation is more correct.
default.nginx
server {
listen 80;
server_name_in_redirect off;
access_log /var/log/nginx/host.access.log main;
root /var/www/html/;
location / {
try_files $uri /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass php:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~ /\.ht {
deny all;
}
}
default.nginx is a config for nginx which is forwarded to the container. The key point here is the fastcgi_pass php directive : 9000 . It sets the address of the FastCGI server. The address can be specified as a domain name or IP address, and port.
php: 9000 - the name of the service is the address of the FastCGI server. Nginx accessing php will get the IP address of the container in which php is running. Port 9000 is a standard port, it is declared when creating the base container. This port is available for nginx over the network, but is not available on the host machine, since it was not forwarded.
php
It is unusual here that an image is not indicated. Instead, it builds its own image directly from the compose file. The context directive points to the folder in which the Dockerfile is located.
Dockerfile
FROM php:7.3.2-fpm
RUN apt-get update && apt-get install -y \
libzip-dev \
zip \
&& docker-php-ext-configure zip --with-libzip \
&& docker-php-ext-install zip \
&& docker-php-ext-install mysqli
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /var/www/html
The Dockerfile states that the build uses the basic php image: 7.3.2-fpm, then the commands for installing php extensions are run. Next, composer is copied from another base image and the working directory for the project is set. I will consider assembly issues in more detail in other posts.
Also, the php.ini file and the html folder with our project are thrown inside the container.
Note that php is located on the backend network and, for example, the proxy can no longer access it.
mysql
The base mysql image is taken with the 5.7 tag, which is responsible for the mysql version. The ./docker/mysql/data folder is used to store database files (you don’t even need to create it, it is created at startup). And through environment variables, a password is set for the root user, also root.
The database is located on the backend network, which allows it to keep in touch with php. The standard image uses the standard port 3306. It is accessible via the docker network for php, but is not available on the host machine. If you forward for this port, you can connect to it, for example, from the same PHPSTORM. But if the phpmyadmin interface is enough for you, then you can not do this.
phpmyadmin
The official image of phpmyadmin. The environment variables use VIRTUAL_HOST to interact with the proxy, similar to nginx. PMA_USER and PMA_PASSWORD access to the database. And PMA_HOST the host base itself. But this is not localhost, as is usually the case, but mysql. Those. communication with the database is available by the name of its service, i.e. mysql. The phpmyadmin container can communicate with the database, because it has a connection to the frontend network.
We start the services with the usual command: docker-compose -d.
We see the following output:
Service launch
Creating network "lesson2_backend" with the default driver
Building php
Step 1/4 : FROM php:7.3.2-fpm
---> 9343626a0f09
Step 2/4 : RUN apt-get update && apt-get install -y libzip-dev zip && docker-php-ext-configure zip --with-libzip && docker-php-ext-install zip && docker-php-ext-install mysqli
---> Using cache
---> 5e4687b5381f
Step 3/4 : COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
---> Using cache
---> 81b9c665be08
Step 4/4 : WORKDIR /var/www/html
---> Using cache
---> 3fe8397e92e6
Successfully built 3fe8397e92e6
Successfully tagged lesson2_php:latest
Pulling mysql (mysql:5.7)...
5.7: Pulling from library/mysql
fc7181108d40: Already exists
787a24c80112: Already exists
a08cb039d3cd: Already exists
4f7d35eb5394: Already exists
5aa21f895d95: Already exists
a742e211b7a2: Already exists
0163805ad937: Already exists
62d0ebcbfc71: Pull complete
559856d01c93: Pull complete
c849d5f46e83: Pull complete
f114c210789a: Pull complete
Digest: sha256:c3594c6528b31c6222ba426d836600abd45f554d078ef661d3c882604c70ad0a
Status: Downloaded newer image for mysql:5.7
Creating lesson2_php_1 ... done
Creating lesson2_mysql_1 ... done
Creating lesson2_phpmyadmin_1 ... done
Creating lesson2_nginx_1 ... done
We see that at the beginning the lesson2_backend network is created, then the php image is assembled, then images that are not already in the system (pull) can be downloaded and the described services are actually launched.
The final touch, so that everything works, this addition to the hosts or sites.local and phpmyadmin.local domains.
The contents of index.php may be as follows:
index.php
Тут мы проверяем корректность подключения расширения php — mysqli, которое было добавлено при сборке Dockerfile.
И заметим, что для связи с контейнером используется название сервиса — mysql.
Структура всего проекта получилась следующей:
Структура проектаhabr/lesson2$ tree
.
├── docker
│ ├── mysql
│ │ └── data
│ ├── nginx
│ │ └── conf.d
│ │ └── default.nginx
│ └── php
│ ├── Dockerfile
│ └── php.ini
├── docker-compose.yml
├── html
│ └── index.php
└── proxy
└── docker-compose.yml