En este post quiero mostrarte como desplegar contenedores de Docker en **Clouding.io. Para esto, quiero mostrarte como llevar a producción una aplicación de ejemplo con Symfony 4 y Redis**.
La aplicación es solo de ejemplo y se trata de un API Rest sencillo el cual nos permitirá mostrar cómo desplegar nuestra aplicación en dos instancias de .
¿Qué es Clouding.io?
Clouding.io es una empresa española que nos provee de una excelente plataforma de servicios de infraestructura para nuestras aplicaciones, lo que se traduce en: un proveedor de servicios en la nube que nos permite disponer de Cloud Servers ó los comúnmente llamados: VPS (Servidores privados virtuales).
¿Qué me gusta de Clouding.io?
Sus instancias son totalmente configurables en base a las necesidades de tu aplicación
Nos permite crecer y escalar fácilmente en el tiempo.
Su interfaz de configuración es super minimalista y sencilla, lo cual nos permite intuitivamente aprender a usarla rápidamente.
El rendimiento de sus instancias es increíble.
Me proveen de un servidor de nombres (DNS) gratuito. Por ende podrás gestionar tu dominio para que apunte a tus instancias fácilmente.
Desplegar contenedores de Docker en Clouding.io: API Rest con Symfony 4 y Redis
Para mostrar un poco el ecosistema entre una infraestructura con Clouding.io y nuestro software, tomaremos los siguientes requerimientos para demostrar un poco la interacción entre estos aspectos:
Dispondremos de un endpoint GET para obtener un listado de productos el cual:
Si no esta cacheado en REDIS, los datos se obtendrán desde MYSQL.
Si los datos estan cacheados, se tomarán de REDIS.
Dispondremos de un endpoint POST para añadir un nuevo producto.
Dispondremos de un endpoint POST para invalidar la cache de Redis y poder ver el funcionamiento entre MYSQL Y Redis de una forma clara.
El API Rest que usaré es a modo de ejemplo por lo que no incorpora ninguna seguridad mediante token (JWT ó OAuth2) para cada petición. Y por tanto se omiten también muchos de los requerimientos y buenas prácticas sugeridas. Acotación
En cuanto a la infraestructura que usaremos utilizando Clouding.io sera:
Una instancia (servidor) en donde desplegaremos 2 contenedores, un contenedor para PHP-FPM, otro para NGINX.
Una instancia (servidor) en donde desplegaremos 2 contenedores. Uno para MySQL y otro para Redis.
Ahora sí, manos a la obra y pongámonos a Desplegar contenedores de Docker en Clouding.io.
A partir de aquí, es necesario tener una cuenta en Clouding.io activada y con saldo suficiente para poder crear las instancias necesarias. Consideraciones iniciales
Lo primero que debemos hacer es crear la instancia en donde albergaremos el contenedor de nginx y el contenedor del API Rest en Symfony 4.
Para ello, vamos a acceder a nuestro panel de Clouding.io a la sección de “ Servidores “:
Hacemos click en el botón “ Haz click aquí para crear tu primer servidor “:
En la sección “ Seleccione un Nombre” le asignaremos el nombre a la instancia, en mi caso le pondré: API Rest Symfony 4
Si navegamos un poco más abajo, podremos elegir las especificaciones que tendrá nuestra instancia, para este ejemplo, usaré una instancia con las mínimas características posibles. Sólo aumentaré el tamaño del disco a 10GB de espacio.
Por ahora, no podemos activar la opción de red privada, pero una vez configuremos la segunda instancia, podremos hacerlo para poder comunicarnos entre ambas instancias. Activar Red Privada
Le damos al botón enviar y con esto se creará nuestra primera instancia de servidor.
Una vez terminado todo el proceso veremos nuestra instancia en la lista de servidores en donde podremos posteriormente ver información pertinente, asi como también podremos escalar ( redimensionar) las características de nuestra instancia en caso de ser necesario y a demás realizar una que otra configuración pertinente.
Podremos también apagar o reiniciar nuestra instancia en caso de que sea necesario.
Si hacemos click en el nombre de nuestro servidor, podremos acceder entonces a información importante, así como también podremos configurar mas a fondo nuestra instancia.
Como podemos observar, nos muestra detalles como la IP Pública de nuestra instancia, el nombre de HOST asignado, podrémos descargar nuestras llaves SSH para acceder a nuestra instancia remotamente por la terminal, entre otras cosas más com cambiar la contraseña del usuario root.
Tendremos también acceso a configuración de RED en el respectivo tab situado en la parte superior:
Aquí es donde más adelante debemos configurar la red privada para permitir la comunicación entre los distintos contenedores de cada instancia de servidor.
Adicionalmente, podremos indicar cuales puertos y protocolos estarán abiertos públicamente para su acceso desde el exterior.
Una vez hecho esto, nuestra lista de servidores quedará de la siguiente forma:
Configuración instancia para el API Rest en Symfony 4
Lo siguiente que haremos será configurar nuestros contenedores en la instancia creada para ello. Lo siguiente que haremos es hacer click sobre el nombre de la instancia “ Api Rest Symfony 4 “ en la lista de servidores.
Una vez allí, podremos obtener los datos para conectarnos vía SSH a la instancia del servidor:
Necesitaremos la IP pública, el usuario para linux y la contraseña (Debemos darle click al icono de “ ojo “ para ver la clave asignada). La clave puede ser cambiada por la que queramos. Acotación
Para conectarnos a la instancia basta con ejecutar el siguiente comando:
ssh root@185.166.215.90
Una vez ingresemos el comando, nos solicitará la contraseña de acceso.
Una vez conectados a la instancia, lo primero que haremos será crear el directorio en donde vamos a deplegar el código del API Rest en Symfony 4.
En mi caso desplegaré el código en /var/www/html/clouding.io
mkdir -p /var/www/html
Nos situamos en el directorio:
cd /var/www/html
Paso 1: Clonamos el Repositorio del API Rest
Ahora clonemos el repositorio del API Rest:
git clone https://github.com/franciscougalde-com/sf4-mysql-redis-example.git clouding.io
Nos situamos en el directorio:
cd clouding.io
Antes de seguir, quiero comentarte la estructura de directorios. Dentro del directorio veremos:
Application: Aquí es donde reside el código del API Rest en Symfony 4
Infrastructure: Aquí es donde esta toda la configuración requerida para levantar nuestros contenedores de Docker.
docker-compose-symfony.yml: Docker compose file para levantar los contenedores para el API (Nginx y PHP-FPM).
docker-compose-mysql-redis.yml: Docker compose file para levantar los contenedores de MySQL y Redis.
Paso 2: Verificar instalación de Docker en la instancia
Como hemos creado la instancia de servidor desde la opción que incluye Docker, ya debemos tener instalado lo requerido. Para verificar que todo está en orden ejecutamos:
docker --version
Efectivamente tenemos instalado Docker. Necesitamos ahora instalar docker-compose, para ello:
# Descargamos docker-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# Damos permisos de ejecución
sudo chmod +x /usr/local/bin/docker-compose
# Comprobamos versión
docker-compose --version
Paso 3: Crear contenedores para el API Rest
docker-compose -f docker-compose-symfony.yml build
Esto va a realizar el proceso de Build en donde se descargaran las imágenes de Docker requeridas y se instalarán los recursos esenciales para nuestra configuración requerida.
Una vez que este proceso finalice, procedemos a ejecutar:
docker-compose -f docker-compose-symfony.yml up -d
Para comprobar que todo va en orden, podemos visualizar los contenedores que están en ejecución:
docker container ls
#Otra alternativa es
docker ps
Podemos ver que nuestros dos contenedores están en ejecución correctamente.
docker exec -it sf4_redis_app bash
Y procedemos ahora así a instalar las dependencias del API Rest en Symfony:
composer install
Una vez finalizado esto, podremos verificar que tenemos acceso a nuestro API Rest utilizando Postman o desde cUrl. Para esto debemos realizar una petición **GET
al path `/check**, en mi caso, utilizando el IP de la instancia sería:
https://185.166.215.90/check`
Deberíamos obtener la respuesta: “ OK “
Por ahora tenemos casi listo nuestro API Rest. Vamos ahora a repetir los mismos pasos para la instancia de MySQL y Redis.
Para ello debemos buscar los datos de acceso (IP, usuario root y contraseña de acceso) de la instancia que hemos llamado “ Mysql + Redis “
Una vez dentro de ella mediante SSH, vamos a crear el mismo directorio que creamos anteriormente:
mkdir -p /var/www/html
Accedemos al directorio:
cd /var/www/html/
Ahora repetiremos el “ Paso 1” y “ Paso 2 “ y en este caso, nuestro paso 3 será:
docker-compose -f docker-compose-mysql-redis.yml build
Esto va a realizar el proceso de Build en donde se descargaran las imágenes de Docker requeridas y se instalarán los recursos esenciales para nuestra configuración requerida.
Una vez que este proceso finalice, procedemos a ejecutar:
docker-compose -f docker-compose-mysql-redis.yml up -d
Para comprobar que todo va en orden, podemos visualizar los contenedores que están en ejecución:
docker container ls
#Otra alternativa es
docker ps
Ahora vemos que se han levantados dos contenedores adicionales, el de MySQL y el de Redis, pero esta es en otra instancia de servidor.
Configurar acceso privado entre instancias
Debemos entrar desde nuestro panel de Clouding.io a cada instancia de servidor, haciendo click en el nombre y seleccionando la pestaña “ RED “.
Una vez alli, debemos habilitar la opción “ Red Privada “ tal como se muestra a continuación:
Configuración Adicional
Necesitamos ahora realizar algunos cambios para que nuestro API Rest funcione como debe ser.
Configuración MySQL
Procedemos a conectamos al contenedor de MySQL:
docker exec -it sf4_redis_mysql bash
Nos conectamos a MySQL:
mysql -uroot -proot
Ahora debemos cambiar algunos privilegios a nuestros usuarios debido a que estamos usando MySQL versión 8. Para ello ejecutamos las siguientes sentencias SQL:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
ALTER USER 'api'@'%' IDENTIFIED WITH mysql_native_password BY 'api';
Listo, podemos desconectarnos de MySQL, ejecutamos:
exit
Configuración API Rest
Necesitamos nuevamente acceder vía SSH a la instancia que hemos llamado: Api Rest Symfony 4 para configurar nuestro API Rest y poder indicarle en los parámetros de nuestro fichero de entornos (.env), cómo acceder a MySQL y Redis.
Una vez dentro, nos conectamos al contenedor donde reside nuestro API Rest:
docker exec -it sf4_redis_app bash
Editamos nuestro .env debido a que debemos cambiar los siguientes valores:
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=70652e6d1a682fac64b38a0d26d96fa6
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
#TRUSTED_HOSTS='^localhost|example\.com$'
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11"
# IMPORTANT: You MUST also configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=mysql://api:api@IP_PRIVADA_INSTANCIA:3306/api_db
###< doctrine/doctrine-bundle ###
###> snc/redis-bundle ###
# passwords that contain special characters (@, %, :, +) must be urlencoded
REDIS_URL=redis://IP_PRIVADA_INSTANCIA
###< snc/redis-bundle ###
En mi caso, mi fichero .env quedó de la siguiente forma:
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices/configuration.html#infrastructure-related-configuration
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=70652e6d1a682fac64b38a0d26d96fa6
#TRUSTED_PROXIES=127.0.0.1,127.0.0.2
#TRUSTED_HOSTS='^localhost|example\.com$'
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
# For an SQLite database, use: "sqlite:///%kernel.project_dir%/var/data.db"
# For a PostgreSQL database, use: "postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion=11"
# IMPORTANT: You MUST also configure your db driver and server_version in config/packages/doctrine.yaml
DATABASE_URL=mysql://api:api@ 10.20.10.5:3306/api_db
###< doctrine/doctrine-bundle ###
###> snc/redis-bundle ###
# passwords that contain special characters (@, %, :, +) must be urlencoded
REDIS_URL=redis://10.20.10.5
###< snc/redis-bundle ###
Migraciones de Doctrine
Necesitamos ahora ejecutar las migraciones de Doctrine en nuestro API Rest para crear la tabla necesaria de productos:
Estando conectados al contenedor “ sf4_redis_app “ ejecutamos ahora lo siguiente:
php bin/console doc:mig:mig
Nos mostrará un mensaje de advertencia y nos solicitará que confirmemos la ejecución del proceso, por lo que vamos a escribir “Y” para continuar.
Y con esto ya tendríamos desplegada nuestro API Rest en contenedores utilizando la plataforma Clouding.io
Hasta ahora, ¿Qué hemos logrado hacer?
Aprendimos a conocer como utilizar la plataforma de clouding.io
Aprendimos a crear dos instancias de servidor.
Vimos como instalar lo necesario para desplegar nuestros contenedores.
Hemos desplegado dos contenedores, uno con nginx y otro con php-fpm en nuestra instancia del API Rest.
Desplegamos dos contenedores, uno con MySQL y otro para Redis en nuestra instancia destinada para los datos.
Hemos aprendido cómo configurar las instancias para que puedan comunicarse localmente entre sí mediante la red privada.
Hemos configurado nuestro proyecto en Symfony 4 para que se conecte a los contenedores de MySQL y Redis. A su vez hemos hecho las migraciones respectivas.
¿Que sigue ahora?
Lo siguiente será probar nuestra API Rest en Symfony 4 con Redis para el cache y MySQL para albergar los datos.
Todo el código de la aplicación en Symfony 4 + Redis lo puedes descargar desde **mi repositorio**
¿Cómo probar el API Rest?
Para demostrar cómo funciona el despliegue de los contenedores en dos instancias separadas de Couding.io, he desarrollado un endpoint que permite consultar la lista de productos.
Si los productos no están en cache, se consultan de la base de datos MySQL y se cachean en Redis con un tiempo de expiración de una hora.
Para poder conocer si los datos vienen de la cache de Redis o de Mysql, la respuesta de la petición incluye en su cuerpo el parámetro “ source” indicando si los datos provienen de MySQL o Redis.
Adicionalmente incorporé el endpoint para añadir mas productos y poder ver el funcionamiento un poco más claro, ya que una vez cacheado los productos, podemos añadir unos cuantos más y no los veremos en la respuesta hasta tanto invalidemos el cache ó el cache expire (en una hora para nuestro caso).
Para probar el API Rest puedes hacer las siguientes peticiones:
Método: GET Url: https://ip_o_host_publico_de_instancia_symfony/check Este endpoint te permite verificar que tienes acceso al API Rest.
Método: GET Url: https://ip_o_host_publico_de_instancia_symfony/api/v1/products Nos va a listar los productos que tengamos en MySQL ó Redis
Método: POST Url: https://ip_o_host_publico_de_instancia_symfony/api/v1/products Nos permite añadir un producto a la Base de Datos. Body: Debes enviar un json en el cuerpo de la petición
#Body para añadir un producto nuevo
{
"name":"Product 1",
"price":1256,
"stock":111
}
Método: POST Url: https://ip_o_host_publico_de_instancia_symfony/api/v1/products/cache-invalidate Nos permite invalidar la cache de redis para poder demostrar su funcionamiento.
Pila de Pruebas para demostrar el funcionamiento
Realiza una petición al Post para añadir unos 3 o 4 productos usando el body indicado.
Realiza la petición al listado de productos y verás que la primera vez los datos vienen desde la base de datos MySQL.
Nuevamente ejecuta la misma petición del listado de productos y verás que provienen de Redis puesto que en la anterior petición fueron cacheados.
Prueba añadiendo unos productos más.
Intenta nuevamente obtener la lista de productos, verás que si no ha pasado 1 hora desde que la lista inicial ha sido cacheada, los productos que añadas luego, no aparecerán en la petición del listado de productos.
Ahora ejecuta la petición para invalidar la cache.
Prueba nuevamente obtener el listado de productos, verás que ahora vendrán nuevamente desde MySQL y aparecerán los últimos productos que añadiste y que no habían sido cacheados.
Recuerda que si tienes alguna sugerencia o pregunta, no dudes en dejar tus comentarios al final del post.
Si te gustó este post, ayúdame a que pueda servirle a muchas más personas, compartiendo mis contenidos en tus redes sociales.
Espero que este post haya sido de gran ayuda para ti, y como siempre, cualquier inquietud o duda que tengas, puedes contactarme por cualquiera de las vías disponibles, o dejando tus comentarios al final de este post. También puedes sugerir que temas o post te gustaría leer a futuro.