A todos nos encanta Docker, y tu también deberÃas.
Docker es una herramienta fantástica que nos permite crear contenedores con nuestras aplicaciones y ejecutarlos en cualquier entorno. Pero, ¿y si queremos desplegar nuestros contenedores en un VPS (Virtual Private Server, o Servidor privado virtual)? En este artÃculo, te mostraré cómo desplegar tus contenedores Docker en un VPS utilizando Github Actions.
Vamos a crear un flujo de trabajo sencillo que nos permitirá desplegar nuestras imágenes personalizadas en el registro de contenedores de Github, descargarlas desde nuestro VPS y ejecutarlas.
Esencial,ente, esto será como tener un pipeline simple de CI/CD.
Figura 1: Ejemplo de diagrama de flujo del pipeline CI/CD
Antes de empezar, necesitas tener lo siguiente:
Puedes crear un nuevo repositorio o utilizar uno existente. Para este ejemplo, utilizaré una aplicación sencilla de Node.js.
Utilizaremos el framework Hono. Puedes instalarlo ejecutando:
npm install hono @hono/node-server
// index.js
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Node.js!'))
console.log("Server started!")
serve(app)
// Respuesta de http://localhost:3000/
$> node index.js
Hello Node.js!
Aquà tienes la estructura de carpetas hasta ahora:
.
├── node_modules/
├── index.js
├── package-lock.json
└── package.json
En la raÃz de tu proyecto, crea un nuevo archivo llamado Dockerfile
y añade el siguiente contenido:
FROM node:18-alpine
WORKDIR /app
COPY package.json .
COPY index.js .
RUN npm install
CMD ["node", "index.js"]
Si estás utilizando un Mac M1 o similar, necesitarás añadir la bandera
--platform linux/amd64
al Dockerfile
FROM --platform linux/amd64 node:18-alpine
...
Este archivo creará una nueva imagen basada en la imagen node:18-alpine
, instalará las dependencias de tu aplicación y luego la ejecutará.
Ahora podemos construir la imagen ejecutando:
docker build -t vps_example .
Y ejecutarla con:
docker run -d -p 3000:3000 vps_example
Si vamos a http://localhost:3000/
deberÃamos ver el mensaje Hello Node.js!
. ¡Genial!
Figura 2: El servidor funciona según lo esperado
Necesitamos obtener algunos secretos para que esto funcione. Lo explicaré en un momento
GH_SECRET
SSH_USER
SSH_HOST
SSH_PRIVATE_KEY
WORK_DIR
Ahora necesitamos obtener una clave API de Github. Esto permitirá que el Flujo de Trabajo de Github Actions envÃe nuestra imagen al Registro de Contenedores de Github.
En Github, ve a Configuración
-> Configuración de desarrollador
-> Tokens de acceso personal
y haz clic en Generar nuevo token
. Selecciona el ámbito write:packages
y haz clic en Generar token
.
Aquà tienes un acceso directo a la página:
https://github.com/settings/tokens/new?scopes=write:packages,read:packages,delete:packages
Generaré un Token Clásico, este será nuestro GH_SECRET
.
Para el siguiente paso, necesitamos obtener la clave privada SSH de nuestro VPS. Tendrás que crear un nuevo usuario para las Github Actions si aún no tienes uno. Por supuesto, puedes usar el usuario que quieras.
El usuario será nuestro SSH_USER
.
La dirección IP de tu VPS será nuestro SSH_HOST
.
El directorio donde quieres desplegar tu aplicación será nuestro WORK_DIR
.
Inicia sesión en tu VPS. Luego, genera una clave ejecutando:
ssh-keygen -t rsa -b 4096
Copia el contenido de la clave pública y añádelo al archivo ~/.ssh/authorized_keys
del usuario que quieres utilizar.
cat <ruta/a/la/clave/pública> >> ~/.ssh/authorized_key
Ahora, copia el contenido de la clave privada a un nuevo archivo.
cat <ruta/a/la/clave/privada>
Esta será nuestra SSH_PRIVATE_KEY
.
Ten en cuenta que la clave pública será la que tenga la extensión
.pub
. La clave privada será la que no la tenga.
Ahora que tenemos todos los secretos, podemos configurarlos en nuestro repositorio de Github.
Ve a Seguridad
del repositorio -> Secretos y Variables
-> Acciones y haz clic en Nuevo secreto del repositorio
. Añade los secretos con los nombres mencionados anteriormente.
En tu repositorio de Github, crea una nueva carpeta llamada .github/workflows
y añade un nuevo archivo llamado docker-publish.yml
.
name: publish
on:
push:
branches: ['main']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.actor }}/<nombre-imagen>:latest # Cambia <nombre-imagen> por el nombre de tu imagen
jobs:
publish:
name: publish image
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Login
run: |
echo ${{ secrets.GH_SECRET }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and Publish
run: |
docker build . --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
deploy:
needs: publish
name: deploy image
runs-on: ubuntu-latest
steps:
- name: install ssh keys
run: |
install -m 600 -D /dev/null ~/.ssh/id_rsa
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
- name: connect and pull
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.WORK_DIR }} && docker compose pull && docker compose up -d && exit"
- name: cleanup
run: rm -rf ~/.ssh
El flujo de trabajo se activará en cada push a la rama master
. Construirá la imagen, la enviará al Registro de Contenedores de Github, y luego iniciará sesión en el VPS, descargará la imagen y la ejecutará.
Ya casi estamos listos, pero primero necesitamos generar la imagen y subirla al Registro de Contenedores de Github para que el flujo de trabajo pueda descargarla.
GH_SECRET
en tu terminal:export GH_SECRET=<GH_SECRET>
echo $GH_SECRET | docker login ghcr.io -u <nombre-usuario> --password-stdin
docker build . -t ghcr.io/<nombre-usuario>/<nombre-imagen>:latest && docker push ghcr.io/<nombre-usuario>/<nombre-imagen>:latest
La imagen se subirá al Registro de Contenedores de Github.
En tu servidor, tendrás que repetir los pasos 1 y 2 para que el servidor pueda descargar la imagen.
Crea un nuevo archivo llamado docker-compose.yml
en la misma ruta que el WORK_DIR
:
version: '3.7'
services: # Añade más servicios si es necesario
<nombre>:
container_name: <nombre> # Cambia esto por el nombre de tu contenedor
image: ghcr.io/{nombre-usuario}/<imagen>:latest
ports:
- 3000:3000
Después de crear el archivo, construye el contenedor ejecutando:
docker compose up -d
¡Enhorabuena! ¡Has desplegado con éxito tu contenedor Docker en tu VPS utilizando Github Actions!
Ahora, cada vez que hagas push a la rama main
, el flujo de trabajo se activará y desplegará la nueva imagen en tu VPS.
Aquà tienes un enlace al repositorio completo si quieres echarle un vistazo: