Configurar deploy automático desde GitHub a cPanel - Caso ejemplo

En este caso ejemplo trataremos un escenario frecuente en el desarrollo web y de software, por un lado, el uso de un repositorio git remoto en el que mantenemos nuestro código, en este caso por uso extendido, GitHub, que utilizaremos como control de versiones, y desde el que automáticamente aplicaremos los cambios en nuestra web o app en producción en el servicio de hosting con cPanel, incluido cualquier acción que requiera (limpiar cache, sesiones, copiar ficheros...) e incluso en última instancia, haciendo que el deploy se ejecute en cada commit que realicemos sobre nuestro repositorio en GitHub.

Antes de empezar, ten en cuenta que partimos de la base en la que tienes el repositorio creado en GitHub y conocimientos básicos del uso de git y también del manejo básico de la terminal sería idóneo, de todas formas cada comando será explicado para que los entiendas y así evitar cualquier error derivado de su ejecución.

Preparando el terreno

Para nuestro ejemplo vamos a hacer una puesta en producción y estrategía de deploy de una aplicación desarrollada en Laravel, de esa forma cubriremos varios puntos avanzados a tener en cuenta, pero el proceso es similar para otras webs o frameworks, y las herramientas que proveemos se adaptan a cada situación.

Habilitando el acceso SSH

Al ser una opción avanzada no requerida de forma común, por defecto el acceso SSH está desactivado, lo puedes activar de forma sencilla desde el área de clientes.

Creando una clave segura para SSH

La necesitaremos para autenticar las peticiones desde nuestro sistema a GitHub, entendemos que se trata de un repositorio privado, si fuese público no sería necesario para clonar y tomar los cambios, de todas formas te recomendamos que igualmente lleves a cabo este paso.

Accede vía SSH a tu cuenta, una vez dentro vamos a comprobar si ya tienes una clave pública lista para usar, ejecutatamos:

cat ~/.ssh/id_rsa.pub

cat nos permite leer un fichero, a continuación le pasamos la ruta donde debería estar la clave pública, ~/.ssh/id_rsa.pub.

Si el comando devuelve un contenido del tipo ssh-rsa AAAAB3Nza..., significa que ya tenemos una clave creada, copiamos este texto, ya que lo usaremos en el siguiente paso.

Si en cambio recibes un error No such file or directory, debemos proceder a crear nuestro nuevo par de claves pública y privada.

Ejecutamos:

ssh-keygen -t rsa -b 4096

Nos solicitará un nombre para la clave, dejamos el campo vacío para usar el nombre defecto, a continuación se solicitará una passphrase, es una contraseña adicional a la clave que generamos, en este caso dejaremos igualmente el campo vacío, para el escenario planteado no es necesario, hacemos enter dos veces para confirmar.

Esperamos confirmación y listo, ya tenemos nuestro par de claves generado, volvemos al comando inicial para imprimir por pantalla nuestra clave pública y poder copiarla.

cat ~/.ssh/id_rsa.pub

Configurando nuestra SSH key en GitHub

Accedemos a nuestra cuenta de GitHub, sección Settings, subsección SSH and GPG Keys, y click en New SSH Key.

Introducimos un nombre identificativo para nuestra clave, y en el campo Key, introducimos la clave pública que hemos copiado en el paso anterior.

Ya con la clave añadida, volvemos a la terminal para comprobar que efectivamente cPanel y GitHub se conectan sin errores, para ello ejecutamos un intento de conexión a GitHub vía SSH:

ssh -T git@github.com

Al ser la primera vez verás un mensaje pidiendo confirmación, responde con yes.

Are you sure you want to continue connecting (yes/no)?

Si todo fue bien, recibirás una respuesta afirmativa por parte del servidor de GitHub.

Hi user! You've successfully authenticated, but GitHub does not provide shell access.

Si recibes un error, comprueba que has copiado correctamente la clave pública, y que la definida en GitHub coincide con la que tienes en tu servicio de hosting, desde el propio cPanel, sección SSH Access, Manage SSH Keys, también puedes ver y gestionar las claves de acceso SSH.

Definiendo las tareas durante la aplicación de cambios o deploy

Toda puesta en producción suele requerir ejecutar comandos y acciones, puede ser el actualizar nuestras dependencias, mover o copiar ficheros, ejecutar tareas del propio framework, entre otras.

Estas tareas se pueden automatizar por medio del fichero .cpanel.yml, es un fichero en formato YAML con el que podemos definir cualquier acción necesaria y que será procesado al hacer un deploy desde la interfaz de gestión de repositorios de cPanel o desde la terminal por medio de la API.

Vamos a realizar este paso antes de clonar el repositorio en cPanel ya que necesitamos incluir este fichero en nuestro repositorio en GitHub para que esté disponible en cada aplicación de cambios que llevemos a cabo.

Para ello crearemos un fichero llamado .cpanel.yml en el directorio raíz de nuestro repositorio, su contenido sería:

---
deployment:
  tasks:
    - chmod 755 ~/repositories
    - chmod 755 ~/repositories/laravel
    - chmod 755 ~/repositories/laravel/public
    - /opt/cpanel/composer/bin/composer update --optimize-autoloader --no-dev
    - /usr/local/bin/php artisan migrate --force
    - /usr/local/bin/php artisan config:cache
    - /usr/local/bin/php artisan view:cache
    - /usr/local/bin/php artisan route:cache

Su formato es bastante sencillo, un comando por línea definido dentro de las tareas a ejecutar, los tres primeros comandos con chmod están destinados a corregir los permisos de los directorios para que el servidor web pueda acceder sin errores (más tarde verás que vamos a alojar nuestro repositorio en /home/usuario/repositories/laravel), tras esto usamos composer para instalar/actualizar nuestras dependencias y finalmente cuatro comandos específicos de Laravel, primero para aplicar posibles cambios en la base de datos (definidos en las migraciones de Laravel) y tras esto limpiar y cachear nuestra configuración, vistas y rutas para un mejor rendimiento.

En el comando relacionado con composer y con php hemos indicado la ruta absoluta a los binarios para que la tarea los localice sin problemas, si no sabes cuál es la ruta absoluta a algún programa basta con ejecutar which seguido del alias y nos devolverá dicha ruta:

which php
-> /usr/local/bin/php

Clonando el respositorio desde GitHub

Aunque podríamos clonar directamente el repositorio vía SSH (recuerda que tienes control absoluto sobre git, no habría limitaciones para hacer uso de este comando como harías en cualquier otra máquina), lo vamos a configurar usando la interfaz de gestión de Git de cPanel para beneficiarnos de los mecanismos de deploy que veremos más adelante.

Copia la URL de clonado desde tu repostorio en GitHub, seleccionando el tipo SSH.

Nos dirigimos a cPanel, sección Git™ Version Control, y hacemos click en Create para crear un nuevo repositorio.

En Clone URL, introducimos la dirección de nuestro repositorio previamente clonada desde GitHub, Repository Path será la ruta donde alojaremos este repositorio, en nuestro caso hemos elegido repositories/laravel, de esa forma tendremos una mejor organización si hay varios dominios en el servidor, Repository Name sería un nombre identificativo del repositorio.

Click en Create y esperamos que se complete el clonado.

Si navegamos a la ruta repositories/laravel, ya sea vía FTP, administrador de ficheros de cPanel, o mejor aún, vía SSH, veremos que nuestro repositorio de GitHub ya se encuentra clonado correctamente.

Preparando el acceso web a Laravel

Como puedes ver tenemos nuestro repositorio en un subdirectorio de la raíz de la cuenta, en el caso del framework Laravel, necesitamos un paso adicional, que el acceso al dominio se realice sobre el directorio public del propio framework, lo que significa que el servidor web debe apuntar a:

/home/user/repositories/laravel/public

Si fuese un dominio adicional no tendría mayor complicación, desde cPanel podemos modificar la ruta inicial del dominio para que sea el subdirectorio que queramos, pero en este caso ejemplo, estamos trabajando sobre el dominio principal de la cuenta, por lo que procedemos de otra forma.

Para ello vamos a prescindir de public_html, lo eliminamos con el siguiente comando.

rm -rf ~/public_html

Y vamos a crear un enlace simbólico (enlace directo) con el mismo nombre, public_html, pero que internamente resuelva sobre /home/user/repositories/laravel/public, esto lo podemos llevar a cabo usando el comando ln -s.

ln -s ~/ruta_laravel/public public_html

Siguiendo con el ejemplo, debemos ejecutar:

ln -s ~/repositories/laravel/public public_html

Ejecutamos un listado de directorios para ver que efectivamente está creado el enlace simbólico.

ls -l ~

Y obtendremos una salida donde veremos que efectivamente, está correctamente creado:

Todos estos pasos podríamos incluirlos en el propio fichero .cpanel.yml, al ser una acción a realizar una única vez, hemos preferido hacerlo manualmente y también para que entiendas mejor la configuración que se realiza.

Si accedemos vía navegador a tudominio.com, veremos un error 500 o página en blanco, aunque el acceso web ya carga sobre el repositorio, en el caso de Laravel hemos clonado los ficheros base de nuestra app, pero no se han instalado las dependencias, esto es algo que se hace siempre durante el deploy, usando composer.

Podríamos navegar al directorio y ejecutar composer install, pero recuerda que hemos definido todas las acciones necesarias en nuestro fichero .cpanel.yml, por lo que vamos a hacer un deploy para que se ejecuten estas tareas.

Accedemos de nuevo a Git™ Version Control, accedemos a Manage sobre nuestro repositorio y vamos a la pestaña Pull or deploy, ejecutamos Update from Remote para tomar cambios desde nuestro repositorio en GitHub y en cuanto finalice, ejecutamos Deploy HEAD commit para que se ejecuten las tareas definidas.

Si todo fue bien, recibiremos confirmación en la zona lateral derecha.

Accedemos de nuevo a nuestro dominio y ahora si, vemos que nuestra app en Laravel ya está funcionando.

Definiendo el flujo de trabajo

Ya tendrías todo el proceso configurado, desde este momento, podrás trabajar sobre tu repositorio en GitHub y cuando estés decidido a aplicar los cambios y ponerlos en producción, simplemente tendrías que ir a cPanel, hacer un Update from Remote para tomar los cambios y a continuación Deploy HEAD Commit para ejecutar las tareas.

Estas mismas acciones las podemos realizar desde nuestra terminal, primero haciendo un git pull para tomar los cambios desde GitHub y después usando la API de cPanel para ejecutar el deploy.

# Navegamos al repositorio
cd ~/repositories/laravel

# Tomamos los cambios desde GitHub
git pull

# Usamos uapi para ejecutar las tareas del fichero .cpanel.yml
uapi VersionControlDeployment create repository_root='/home/user/repositories/laravel'

Desgranando el último comando, uapi es la interfaz de acceso a la API de cPanel vía local, VersionControlDeployment create le está diciendo que queremos ejecutar el deploy al igual que haríamos usando la interfaz gráfica de cPanel, y finalmente repository_root debe ser la ruta de nuestro repositorio.

Este comando nos devolverá una salida similar a la siguiente:

apiversion: 3
func: create
module: VersionControlDeployment
result:
  data:
    deploy_id: 6
    log_path: /home/user/.cpanel/logs/vc_1644663792.81236_git_deploy.log
    repository_root: /home/user/repositories/laravel
    sse_url: /sse/UserTasks/00000000_620793f0c68e52/vc_1644663792.81236_git_deploy.log
    task_id: 00000000/620793f0c68e52
    timestamps:
      queued: '1644663792.82822'
  errors: ~
  messages: ~
  metadata: {}

  status: 1
  warnings: ~

Lo más importante es "status" donde 1 significa que se ejecutó correctamente, y log_path, que sería el registro del deploy que podemos abrir para comprobar si las tareas del .cpanel.yml se ejecutaron sin errores.

Si vamos a hacer un deploy de nuestra web de forma frecuente, quizás nos interese crear un alias, un comando con un nombre acortado que ejecute estas dos acciones en una única vez.

Vamos a editar el fichero .bashrc que tienes en la raíz de la cuenta, y justo al final añadimos:

# User specific aliases and functions

alias deploy="cd ~/repositories/laravel; git pull ; uapi VersionControlDeployment create repository_root='/home/user/repositories/laravel'"

Estamos creando un alias que se llama deploy, este alias define entre comillas dobles 3 comandos (están separados por ;), el primero nos posiciona en el directorio de nuestro repositorio, el segundo usa git pull para tomar los cambios desde GitHub y el tercero es la ejecución de la API para hacer el deploy (recuerda cambiar las rutas según sea tu caso).

Recuerda salir de la sesión abierta y volver a entrar para que el alias comience a funcionar, también puedes ejecutar source ~/.bashrc para que se apliquen los cambios en la sesión abierta.

Ahora si ejecutamos el comando deploy que hemos creado, en un solo comando haremos todos los pasos para poner en producción los cambios desde nuestro repositorio y ejecutar las tareas necesarias.

Deploy automático en cada commit sobre nuestro repositorio en GitHub

Si solo aplicas cambios finales desde tu repositorio local al que tienes en GitHub, puedes considerar que cada commit sobre GitHub debería ser puesto en producción, y por lo tanto buscarías automatizar el paso anterior.

Para este fin podemos usar los webhooks de GitHub, es una configuración que nos permite recibir notificaciones en una dirección específica, en este caso queremos que cuando se produzca un evento push sobre nuestro repositorio, se notifique una URL de nuestro control que será la que ejecute los comandos para hacer el deploy que antes hacíamos manualmente.

Como primer paso vamos a crear un subdominio, de esa forma tendremos una vía de notificaciones independiente a nuestra web principal, en nuestro ejemplo hemos creado deploy.dominio.com.

Dentro del directorio del dominio vamos a crear un fichero llamado index.php con el siguiente código:

<?php

shell_exec("cd ~/repositories/laravel ; git pull");
shell_exec("/usr/bin/uapi VersionControlDeployment create repository_root='/home/user/repositories/laravel'");

Como puedes ver lo único que estamos haciendo es lo mismo que hicimos antes con nuestro alias, o con la ejecución independiente de cada comando, estamos ejecutando los comandos necesarios desde php usando la función shell_exec, primero haciendo git pull y tras eso el deploy usando uapi para la ejecución del deploy como hicimos en pasos anteriores.

Si accedemos a deploy.domino.com el proceso de deploy se ejecutará por el mero hecho de acceder a dicha dirección.

A continuación accedemos a nuestro repositorio en GitHub, opción Settings, Webhooks y configuramos nuestra notificación automática.

Ahora en adelante, cada vez que hagas push sobre tu repo, GitHub notificará a la URL que hemos definido y esta ejecutará el deploy para poner en producción los cambios.

Depurar errores de deploy

Puede suceder que algún comando no esté funcionando, o tengamos un error que no preveíamos, para ello podemos ver los registros de cada deploy en el directorio .cpanel/logs, los registros siguen el formato "NUMERO_git_deploy.log".

De esa forma podrás revisar la ejecución de cada comando desglosado paso a paso:

164466267.976022855
$ chmod 755 ~/repositories

Task completed with exit code 0.

1644662676.980349011
$ chmod 755 ~/repositories/laravel

Task completed with exit code 0.

1644662676.984084793
$ chmod 755 public

Task completed with exit code 0.

1644662676.987835992
$ /opt/cpanel/composer/bin/composer update --optimize-autoloader --no-dev
Loading composer repositories with package information
Updating dependencies
Nothing to modify in lock file
Installing dependencies from lock file
Nothing to install, update or remove

$ /usr/local/bin/php artisan config:cache
Configuration cache cleared!
Configuration cached successfully!

Task completed with exit code 0.

1644663799.347817739
$ /usr/local/bin/php artisan view:cache
Compiled views cleared!
Blade templates cached successfully!

Task completed with exit code 0.

1644663799.534180019
$ /usr/local/bin/php artisan route:cache
Route cache cleared!
Routes cached successfully!

Task completed with exit code 0.

1644663799.71281
Build completed with exit code 0

La flexibilidad del entorno es la clave, tu decides como trabajar

Hemos partido de un escenario ejemplo que nosotros mismos usamos en nuestro día a día, pero la realidad es que la flexibilidad de las herramientas que te hemos mostrado te permiten configurar tu flujo de trabajo para que se adapte a las necesidades tanto si eres un único desarrollador, como si sois un equipo.

Por ejemplo, en vez de tener un repositorio en producción, podrías hacer que el repositorio solo actúe de vía de entrada para los cambios, y que el proceso de deploy copie dichos cambios a otra ruta dentro de la cuenta usando los comandos rsync (sincronizar) o cp (copiar), también podrías prescindir de GitHub y trabajar directamente aplicando cambios sobre tu repositorio creado en cPanel.

Al final del día se trata de desarrollar y trabajar de la forma que resulte más ágil en cada caso y proyecto específico, de ahí que la flexibilidad del entorno y las herramientas, además del conocimiento acerca de estas marque un punto de inflexión en los procesos de los desarrolladores y administradores.