Docker for PHP

Today Docker became an important part of the development process. Docker creates an isolated environment in containers within the operating system. This allows to get a transferable environment - everything in the container can be saved, copied and deployed to another system.

We'll prepare our own local docker environment for PHP development with components:

PHP 7.3
Nginx
MySQL 8
Redis

We'll follow important docker rule - one service per container. Also, we'll mount docker volumes to keep code and data on our host machine and use it in containers, so our containers will work for "read-only" purpose and can be stopped, deleted or replaced without losing data.

Install docker

First, we need to install Docker https://docs.docker.com/install/linux/docker-ce/ubuntu/ and docker compose https://docs.docker.com/compose/install/.

Create folders structure

Create somewhere a directory that will be the root for our build and create the following directories:

www - projects files
mysql - mysql data
logs - different logs
nginx -  nginx conf files
images - docker images

Create a test project

To test our build, create a config file for nginx in hosts/test_dev.conf with content:

server {
    index index.php;
    server_name hello.dev;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/hello.dev;

    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;
    }
}

And two PHP files:

www/test.dev/index.php - to check PHP and Nginx.

<?php
   phpinfo();

www/test.dev/db.php - to check MySql connection from PHP.

<?php
    try {
        $dsn = 'mysql:host=mysql;dbname=information_schema;charset=utf8;port=3306';
        $pdo = new PDO($dsn, 'root', 'secret');
        echo "Connection to the MySQL database established successfully";
    } catch (PDOException $e) {
        echo $e->getMessage();
    }

Docker file for PHP

We'll use default images except for PHP because it doesn't include the necessary modules. Create dockerfile images/php/Dockerfile with the following content:

# Use official image of PHP
FROM php:7.3-fpm
# If you have any question, here a contancs
MAINTAINER codingwar.com <demyanovs@gmail.com>

# Install additional modules to PHP image
# Note that for each RUN command creates a new layer in the image, so it's recommended to merge the commands.
RUN apt-get update && apt-get install -y \
        curl \
        wget \
        git \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
        libzip-dev \
    && docker-php-ext-install -j$(nproc) zip iconv mbstring mysqli pdo_mysql zip \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd

# Our php.ini to redefine config values
ADD php.ini /usr/local/etc/php/conf.d/40-custom.ini

# PHP working dir
WORKDIR /var/www

# Run the container
CMD ["php-fpm"]

Docker compose

Now create docker-compose file in the root of our project. In this file, we describe which containers will run, their settings, how they will interact with each other, and so on.

version: '2'
# Our services (containers)
services:
    nginx:
        image: nginx:latest
        # Route ports
        ports:
            - "8080:80"
            - "1443:443"
        # Mount directoris - host_dir:container_dir
        volumes:
            - ./nginx:/etc/nginx/conf.d
            - ./www:/var/www
            - ./logs:/var/log/nginx
        # Nginx need to communicate with php container
        links:
            - php
    php:
        # Build PHP image
        build: ./images/php
        # PHP need to communicate with mysql
        links:
            - mysql
        # Mount dir with our code
        volumes:
            - ./www:/var/www
    mysql:
        image: mysql:8
        ports:
            - "13306:3306"
        # Mount dir to keep mysql data
        volumes:
            - ./mysql:/var/lib/mysql
        # Set root password for Mysql
        environment:
            MYSQL_ROOT_PASSWORD: secret
    redis:
        image: redis:alpine
        volumes:
            - ./redis:/data
        links:
            - php

Run docker-compose

All is left is to execute the docker-compose up -d command from the root of our build and wait a bit. The first launch will take longer because docker needs to download and build images.

Now our containers are running. Let's check it out. Open the browser and go to localhost:8080.

docker phpinfo

Great! Check MySQL connection, run localhost:8080/db.php.

docker mysql

You can also open a site by domain, to do this, add to /etc/hosts the line http://hello.dev/

Connection refused

In new versions of PHP and MySQL you may see error running this script:

SQLSTATE[HY000] [2002] Connection refused

Just connect to a MySQL container and run commands:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'secret';
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'secret';
ALTER USER 'default'@'%' IDENTIFIED WITH mysql_native_password BY 'secret';

To connect to Mysql Container

Find container id by running docker container ls and run docker exec -it CONTAINER_ID /bin/bash to connect to it. Then connect to MySQL from command line mysql -uroot -p.

Conclusion

We have four isolated containers which can communicate with each other. Also, we mounted volumes, so can edit code on our host machine and all changes will reflect in containers. You can easily add more hosts, services, and containers if you need.

All samples are available on github.

Комментарии