Desplegar contenedores de Docker en Clouding.io

Desplegar contenedores de Docker en Clouding.io

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:

  1. Dispondremos de un endpoint GET para obtener un listado de productos el cual:

  2. Si no esta cacheado en REDIS, los datos se obtendrán desde MYSQL.

  3. Si los datos estan cacheados, se tomarán de REDIS.

  4. Dispondremos de un endpoint POST para añadir un nuevo producto.

  5. 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

  1. Realiza una petición al Post para añadir unos 3 o 4 productos usando el body indicado.

  2. Realiza la petición al listado de productos y verás que la primera vez los datos vienen desde la base de datos MySQL.

  3. 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.

  4. Prueba añadiendo unos productos más.

  5. 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.

  6. Ahora ejecuta la petición para invalidar la cache.

  7. 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.

Did you find this article valuable?

Support Francisco Ugalde by becoming a sponsor. Any amount is appreciated!