Hoy día es una necesidad para los desarrolladores contar con un entorno de desarrollo y pruebas basado en Linux lo mas similar posible a un entorno de producción y lo suficientemente flexible para soportar una variedad de herramientas, no solo el stack Apache, PHP y MySQL.

También se hace necesario utilizar otros como Nginx, Python o NodeJS solo por nombrar algunos, para utilizar estas herramientas en un entrono de desarrollo una de las herramientas mas flexibles y populares es Docker.

En este articulo vamos a crear un contenedor con Nginx como servidor web y con soporte para Multi PHP que nos permitira trabajar con diferentes proyectos en el mismo contenedor y cada uno con una version de PHP diferente, para cada sitio pudiendo escoger para cada sitio una las siete versiones de PHP disponibles.

Versiones de PHP que instalaremos

Vamos a instalar las siguientes versiones de PHP en el mismo contenedor

  • PHP 5.4
  • PHP 5.5
  • PHP 5.6
  • PHP 7.0
  • PHP 7.1
  • PHP 7.2
  • PHP 7.3


Preparación de las Imágenes

Primera Imagen (PasoUno)

Lo primero va a ser cear un contenedor que corra CentOS e instalar un paquete, específicamente initscripts este paquete es importante para no tener problemas con el comando systemctl a la hora de controlar servicios:

docker run -it --privileged -e "container=docker" --cap-add SYS_ADMIN \
--tmpfs /run -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
--name PasoUno "centos:latest" "/bin/bash"

Si intentáramos ver iniciar, detener, reiniciar o simplemente ver el estado de un servicio como dbus nos saltara el siguiente mensaje:

[root@e8c87e7efdd4 /]# systemctl status dbus    
Failed to get D-Bus connection: Operation not permitted
[root@e8c87e7efdd4 /]# 

Para solucionar esto anterior en el siguiente paso instalamos initscripts y ademas asignamos una contraseña al usuario root debido a que la imagen original de centos en docker no tiene password debido a que directamente se ejecuta bash

Ahora en adelante necesitaremos que tenga contraseña para poder logearnos mediante init, por defecto le estoy colocando docker como password, ustedes pueden configurar el que quieran

#Actualizamos la imagen
yum -y update

#Instalamos el paquete necesario
yum install -y initscripts

#Asignamos una contraseña al root, en este caso docker
echo "docker" | passwd root --stdin

#Esto solo para que no de error al iniciar
touch /etc/sysconfig/network

#Salimos
exit

Convertimos el Contenedor en Imagen

Con docker ps -a podemos listar los contenedores, aqui veremos que ya nos aparece el contenedor llamado PasoUno que creamos anteriormente:

 
alvaro@acer:~$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                      PORTS               NAMES
e60ca0603d26        centos:latest       "/bin/bash"         2 minutes ago       Exited (0) 20 seconds ago                       PasoUno

En el comando anterior vemos que el contenedor con nombre PasoUno tiene un ID e60ca0603d26, por supuesto este id va a ser diferente en tu pc, copias el que aparezca a ti y ejecutas lo siguiente:

 
alvaro@acer:~$ docker commit e60ca0603d26 centos-base

En mi caso a la nueva imagen lo llame centos-base, ustedes pueden llamarlo como quieran.

Segunda Imagen (PasoDos)

En este paso crearemos un segundo contenedor a partir de la imagen centos-base, la diferencia con el primer contenedor es que esta vez el contenedor correrá /usr/sbin/init en lugar de /bin/bash , esta diferencia es fundamental porque los contenedores ya no tendrán el problema para iniciar servicios automáticamente.

docker run -it --privileged --hostname="server" -e "container=docker" --cap-add SYS_ADMIN \
--tmpfs /run -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-p 22:22 -p 80:80 -p 3306:3306 \
--name PasoDos "centos-base" "/usr/sbin/init"

Instalación del Stack Nginx+MySQL+ MutiPHP

Una vez que hayamos arrancado el nuevo contenedor en el paso anterior vamos a instalar unos cuantos paquetes y a configurarlo

#Instalamos este repositior
yum install -y http://rpms.remirepo.net/enterprise/remi-release-7.rpm
yum install -y epel-release

#Actualizamos el sistema
yum update -y

#Opcional: En lo personal instalo todos estas herramientas y 
#liberias porque siempre suelo utilizarlas
yum install -y wget nano git iputils bind-utils net-tools nmap mtr gcc gcc-c++ make autoconf glibc rcs pcre-devel openssl-devel expat-devel geoip-devel zlib-devel mlocate ncdu mytop

yum install yum-utils -y

#Instalamos Nginx, MariaDB y SSH
yum install -y httpd mariadb-server openssh-server nginx

#Con esto instalaremos PHP 5.4, 5.5, 5.6, 7.0, 7.1, 7.2 y 7.3 
#ademas de todas las librerías necesarias mas PHP-FPM para
#cada versión de PHP anterior
yum install -y php54 php55 php56 php70 php71 php72 php73 \
php54-php-fpm php55-php-fpm php56-php-fpm php70-php-fpm php71-php-fpm php72-php-fpm php73-php-fpm \
php54-php-mysql php54-php-mcrypt php54-php-mbstring php54-php-cli php54-php-fpm php54-php-gd php54-php-json php54-php-ioncube-loader php54-php-intl php54-php-pdo php54-php-pgsql php54-php-soap php54-php-xml php54-php-xmlrpc \
php55-php-mysql php55-php-mcrypt php55-php-mbstring php55-php-cli php55-php-fpm php55-php-gd php55-php-json php55-php-ioncube-loader php55-php-intl php55-php-pdo php55-php-pgsql php55-php-soap php55-php-xml php55-php-xmlrpc \
php56-php-mysql php56-php-mcrypt php56-php-mbstring php56-php-cli php56-php-fpm php56-php-gd php56-php-json php56-php-ioncube-loader php56-php-intl php56-php-pdo php56-php-pgsql php56-php-soap php56-php-xml php56-php-xmlrpc \
php70-php-mysql php70-php-mcrypt php70-php-mbstring php70-php-cli php70-php-fpm php70-php-gd php70-php-json php70-php-ioncube-loader php70-php-intl php70-php-pdo php70-php-pgsql php70-php-soap php70-php-xml php70-php-xmlrpc \
php71-php-mysql php71-php-mcrypt php71-php-mbstring php71-php-cli php71-php-fpm php71-php-gd php71-php-json php71-php-ioncube-loader php71-php-intl php71-php-pdo php71-php-pgsql php71-php-soap php71-php-xml php71-php-xmlrpc \
php72-php-mysql php72-php-mcrypt php72-php-mbstring php72-php-cli php72-php-fpm php72-php-gd php72-php-json php72-php-ioncube-loader php72-php-intl php72-php-pdo php72-php-pgsql php72-php-soap php72-php-xml php72-php-xmlrpc \
php73-php-mysql php73-php-mcrypt php73-php-mbstring php73-php-cli php73-php-fpm php73-php-gd php73-php-json php73-php-ioncube-loader php73-php-intl php73-php-pdo php73-php-pgsql php73-php-soap php73-php-xml php73-php-xmlrpc \

#Detenemos estos servicios
systemctl stop php54-php-fpm
systemctl stop php55-php-fpm
systemctl stop php56-php-fpm
systemctl stop php70-php-fpm
systemctl stop php71-php-fpm
systemctl stop php72-php-fpm
systemctl stop php73-php-fpm

#A cada version de PHP-FPM le asignamos un
#puerto diferente mediante los siguientes comandos
sed -i 's/:9000/:9054/' /opt/remi/php54/root/etc/php-fpm.d/www.conf
sed -i 's/:9000/:9055/' /opt/remi/php55/root/etc/php-fpm.d/www.conf
sed -i 's/:9000/:9056/' /etc/opt/remi/php56/php-fpm.d/www.conf
sed -i 's/:9000/:9070/' /etc/opt/remi/php70/php-fpm.d/www.conf
sed -i 's/:9000/:9071/' /etc/opt/remi/php71/php-fpm.d/www.conf
sed -i 's/:9000/:9072/' /etc/opt/remi/php72/php-fpm.d/www.conf
sed -i 's/:9000/:9073/' /etc/opt/remi/php73/php-fpm.d/www.conf

#Cambimos el usuario, nginx en lugar de apache
sed -i 's/user = apache/user = nginx/' /opt/remi/php54/root/etc/php-fpm.d/www.conf
sed -i 's/group = apache/group = nginx/' /opt/remi/php54/root/etc/php-fpm.d/www.conf
sed -i 's/user = apache/user = nginx/' /opt/remi/php55/root/etc/php-fpm.d/www.conf
sed -i 's/group = apache/group = nginx/' /opt/remi/php55/root/etc/php-fpm.d/www.conf
sed -i 's/user = apache/user = nginx/' /etc/opt/remi/php56/php-fpm.d/www.conf
sed -i 's/group = apache/group = nginx/' /etc/opt/remi/php56/php-fpm.d/www.conf
sed -i 's/user = apache/user = nginx/' /etc/opt/remi/php70/php-fpm.d/www.conf
sed -i 's/group = apache/group = nginx/' /etc/opt/remi/php70/php-fpm.d/www.conf
sed -i 's/user = apache/user = nginx/' /etc/opt/remi/php71/php-fpm.d/www.conf
sed -i 's/group = apache/group = nginx/' /etc/opt/remi/php71/php-fpm.d/www.conf
sed -i 's/user = apache/user = nginx/' /etc/opt/remi/php72/php-fpm.d/www.conf
sed -i 's/group = apache/group = nginx/' /etc/opt/remi/php72/php-fpm.d/www.conf
sed -i 's/user = apache/user = nginx/' /etc/opt/remi/php73/php-fpm.d/www.conf
sed -i 's/group = apache/group = nginx/' /etc/opt/remi/php73/php-fpm.d/www.conf

#Iniciamos los servicios
systemctl start php56-php-fpm
systemctl start php55-php-fpm
systemctl start php56-php-fpm
systemctl start php70-php-fpm
systemctl start php71-php-fpm
systemctl start php72-php-fpm
systemctl start php73-php-fpm



#Los iniciamos
systemctl start nginx
systemctl start mariadb

#Configuramos para que todos arranque
#desde el inicio de la ejecucion
#del contenedor
systemctl enable nginx
systemctl enable mariadb
systemctl enable php54-php-fpm
systemctl enable php55-php-fpm
systemctl enable php56-php-fpm
systemctl enable php70-php-fpm
systemctl enable php71-php-fpm
systemctl enable php72-php-fpm
systemctl enable php73-php-fpm

#Opcional: Por ultimo en mi  caso instalo composer
yum install -y composer

Seleccionado una version de PHP

Cuando configuremos un nuevo dominio en Nginx para cambiar la versión de PHP solo debemos colocar alguna de las siguientes lineas:

            
#PHP 5.4
fastcgi_pass 127.0.0.1:9054;

#PHP 5.5
fastcgi_pass 127.0.0.1:9055;

#PHP 5.6
fastcgi_pass 127.0.0.1:9056;

#PHP 7.0
fastcgi_pass 127.0.0.1:9070;

#PHP 7.1
fastcgi_pass 127.0.0.1:9071;

#PHP 7.2
fastcgi_pass 127.0.0.1:9072;

#PHP 7.3 
fastcgi_pass 127.0.0.1:9073;

Configuración de ejemplo por defecto para un sitio en Nginx

A modo de ejemplo guardamos un archivo de configuración con PHP 5.6 pre configurado,  luego al momento de dar de alta un nuevo dominio lo tomaremos como ejemplo.

cat > /etc/nginx/conf.d/example.lan.conf << EOF
server {

        listen 80;
        listen [::]:80;

        server_name example.lan;

        root /var/www/example.lan;
        index index.php index.html index.htm index.nginx-debian.html;

        location / {
                try_files \$uri \$uri/ =404;
        }

        location ~ \.php$ {
            try_files \$uri =404;

            #PHP 5.4
            #fastcgi_pass 127.0.0.1:9054;

            #PHP 5.5
            #fastcgi_pass 127.0.0.1:9055;

            #PHP 5.6
            fastcgi_pass 127.0.0.1:9056;

            #PHP 7.0
            #fastcgi_pass 127.0.0.1:9070;

            #PHP 7.1
            #fastcgi_pass 127.0.0.1:9071;

            #PHP 7.2
            #fastcgi_pass 127.0.0.1:9072;

            #PHP 7.3
            #fastcgi_pass 127.0.0.1:9073;

            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  \$document_root\$fastcgi_script_name;
            include        fastcgi_params;
            fastcgi_buffer_size 128k;
            fastcgi_buffers 256 4k;
            fastcgi_busy_buffers_size 256k;
            fastcgi_temp_file_write_size 256k;

            #php.ini
            fastcgi_param PHP_VALUE "
                        memory_limit=512M;
                        upload_max_filesize=5M;
                        error_reporting=E_ALL;
            ";
        }

         location ~ /\.ht {
                 deny all;
        }
}
EOF

Convertimos el Contenedor en Imagen

 alvaro@acer:~$ docker ps -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                       PORTS               NAMES
bb0b488138bc        centos-base         "/usr/sbin/init"    11 minutes ago      Exited (130) 2 seconds ago                       PasoDos
e60ca0603d26        centos:latest       "/bin/bash"         2 minutes ago       Exited (0) 20 minutes ago                        PasoUno

#Convertimos el contenedor llammado PasoDos en Imagen llamandolo por ejemplo nginx-multiphp
alvaro@acer:~$ docker commit bb0b488138bc nginx-multiphp
sha256:1c745640f601924fc21df4c80fdad19bb93f67fcb4cca80ad2c1d4264da11966

Limpiamos los contenedores que ya no necesitamos

#Borramos el contenedor PasoUno
alvaro@acer:~$ docker rm PasoUno
PasoUno

#Borramos el contenedor PasoDos
alvaro@acer:~$ docker rm PasoDos
PasoDos

Revisamos las imágenes

En este punto nos quedaron 2 imagenes, un CentOS 7 recién instalado y uno con Nginx + MultiPHP y MySQL,la imagen llamada centos-base podemos borrarla si queremos, yo prefiero dejarla porque me sirve para otros propósitos, en ella ya esta solucionado el tema de reiniciar servicios.

alvaro@acer:~$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx-multiphp      latest              1c745640f601        3 minutes ago       1.07GB
centos-base         latest              6d3b3ee2b754        18 minutes ago      333MB

Si quisiéramos borrar simplemente escribimos docker rmi centos-base

Trabajando con el nuevo contenedor

Ya tenemos listas la imagen que necesitamos, ahora creamos un nuevo contenedorcon el siguente comando, al contenedor lo voy a llamar MultiPHP y lo voy a constuir apartir de la imagen nginx-multiphp

docker run -it --privileged --hostname="server" -e "container=docker" --cap-add SYS_ADMIN \
--tmpfs /run -v /sys/fs/cgroup:/sys/fs/cgroup:ro \
-p 22:22 -p 80:80 -p 3306:3306 \
-v '/home/alvaro/www/:/var/www' \
--name MultiPHP "nginx-multiphp" "/usr/sbin/init"

Si ustedes colocaron un nombre diferente al momento de crear la imagen deberán colocar ese nombre, luego deberán modificar la linea -v ‘/home/alvaro/www/:/var/www’ y cambiar por la ruta que a ustedes les sirva, la primera ruta es la ubicacion de la carepta en nuestro disco duro, la segunda es en donde la vamos a montar dentro del contenedor.

Cuando lo corran aparecerá lo siguiente, recuerden que el usuario es root y la contraseña es docker o la que ustedes le hayan configurado

[ OK ] Reached target Network is Online.
[ OK ] Started OpenSSH server daemon.
[ OK ] Started The nginx HTTP and reverse proxy server.
[ OK ] Started The PHP FastCGI Process Manager.
[ OK ] Started The PHP FastCGI Process Manager.
[ OK ] Started The PHP FastCGI Process Manager.
[ OK ] Started The PHP FastCGI Process Manager.
[ OK ] Started The PHP FastCGI Process Manager.
[ OK ] Started The PHP FastCGI Process Manager.
[ OK ] Started The PHP FastCGI Process Manager.
[ OK ] Started MariaDB database server.
[ OK ] Reached target Multi-User System.
Starting Update UTMP about System Runlevel Changes...
[ OK ] Started Update UTMP about System Runlevel Changes.

CentOS Linux 7 (Core)
Kernel 4.9.0-8-amd64 on an x86_64
server login:

Dando de alta un nuevo dominio

Para dar de alta un nuevo dominio hacemos

cp /etc/nginx/conf.d/example.lan.conf /etc/nginx/conf.d/ejemplo.com.conf
nano /etc/nginx/conf.d/ejemplo.com.conf

Dentro del fichero cambiamos las lineas

server_name example.lan;

root /var/www/example.lan;

Y las dejamos como

server_name ejemplo.com;

root /var/www/ejemplo.com;

Guardamos y salimos del fichero y creamos la ruta

mkdir /var/www/ejemplo.com;
mkdir /var/www/ejemplo.com;

Reiniciamos Nginx

service nginx restart

Ejecutamos

echo "<?php phoinfo(); ?>;" > /var/www/ejemplo.com/index.php

Luego si modificamos el archivo host de nuestra pc apuntando a 127.0.0.1 podremos probar que la pagina funcione

127.0.0.1 ejemplo.com

Cambiando la versión de PHP

Para cambiar la version de PHP de un dominio modificamos

nano /etc/nginx/conf.d/ejemplo.com.conf

Vamos a la parte que aparece las versiones de PHP y descomentamos la version que nos interese y comentamos la que no.

            #PHP 5.4
            #fastcgi_pass 127.0.0.1:9054;

            #PHP 5.5
            #fastcgi_pass 127.0.0.1:9055;

            #PHP 5.6
            fastcgi_pass 127.0.0.1:9056;

            #PHP 7.0
            #fastcgi_pass 127.0.0.1:9070;

            #PHP 7.1
            #fastcgi_pass 127.0.0.1:9071;

            #PHP 7.2
            #fastcgi_pass 127.0.0.1:9072;

            #PHP 7.3
            #fastcgi_pass 127.0.0.1:9073;

Guardamos salimos y reinciamos Nginx

service nginx restart

Saliendo e ingresando nuevamente

De los contenedores CentOS normalmente salimos con exit porque usualmente ejecutan bash pero aquí estamos ejecutando init, por eso el comando para terminar el contenedor es halt

Un error muy comun al usar docker es cada vez que lo iniciamos hacerlo con docker run, esto nos comera el espacio en el disco duro porque estara creando un conenedor nuevo en lugar de usar el auq ya tengamos, la mejor manera de trabajar es con

docker start MultiPHP && docker attach MultiPHP

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *