Vamos a montar un servidor donde poder subir todos nuestros proyectos. Podremos subir proyectos con cualquier lenguaje y además se subirán automáticamente al subir código, justo como en Vercel.
Existen herramientas que te permiten replicar funcionamientos similares como coolify. Pero vamos a construirlo nosotros.
Vamos a comprar nuestro VPS en https://contabo.com/en/, pero puedes usar Heztner, Digital Ocean o cualquier proveedor que prefieras.
Nuestro único requisito va a ser, instalar Ubuntu 20. Una vez tengamos la dirección IP y nuestra contraseña, podemos acceder por ssh a nuestro server
Vamos a generar unas claves ssh para nuestro servidor con el siguiente comando
ssh-keygen -t ed25519 -C "tucódigosecreto"
Es altamente recomendable crear un usuario nuevo en lugar de usar el root asi que vamos a hacerlo.
$ adduser afor
Después vamos a darle privilegios de sudo al usuario que acabamos de crear
// Damos privilegios de sudo
usermod -aG sudo afor
// Cambiamos a ese usuario
su - afor
// Lanzamos un comando sudo
sudo ls /
Como todo servidor, podemos asignar un DNS a nuestro nuevo vps. Para ello tenemos que comprar el dominio de nuestra elección y en la configuarción de DNS. Añadir un registro “A” con la dirección IP de nuestro servidor. Una vez se propague, nuestro dominio, resolverá a nuestro DNS.
Una vez tenemos la clave, la tenemos que registrar en el servidor
shh-copy-id afor@[IP]
al introducir nuestro phrase, podremos conectarnos otra vez.
Para deshabilitarlo tenemos que modificar la configuración de ssh.
sudo vim /etc/ssh/sshd_config
Yo uso vim, pero puedes usar el editor que quieras
Buscamos las siguientes lineas y las dejamos asi:
# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication no
...
PermitRootLogin no
...
UsePAM no
// Una vez hecho, reiniciamos el servicio de ssh
$> sudo systemctl restart ssh
// Y probamos a conectarnos otra vez (en otra ventana de terminal
// deja la conexión abierta por si acaso!)
Si hemos hecho todo bien, solo deberíamos poder acceder con el usuario y passphrase que hemos creado.
Vamos a utilizar un proyecto de react + vite como ejemplo. Ahora que tenemos configurado algunas cosas deberemos instalar lo necesario para levantar nuestros proyectos aunque sea en modo de desarrollo.
Una vez instalado podemos levantar el proyecto como pnpm dev --host
. Enfasis a este último parámetro, ya que necesitamos que el proyecto se pueda comunicar con el resto de la red.
Si. en el navegador accedemos a la dirección de nuestra ip (o dominio) y el puerto en cuestión, veremos cómo se visualiza.
Pero esto es el entorno de desarrollo, no es lo que deberíamos hacer. Ahora es cuando toca Dockerizar nuestra app.
services:
askfor:
image: ghrc.io/afordigital/askfor:prod #La imagen de tu app
ports:
- "5173:5173"
Los únicos puertos que nos interesan, son el 22 para el ssh, 80 para http y 443 para https por si habilitamos un dominio con SSL.
Para ello utilizaremos un firewall, Uncomplicated Firewall / UFW, que además está incluido en Ubuntu.
Primero vamos a deshabilitar todo el trafico entrante que le llegue al VPS y habilitar trafico saliente.
$> sudo ufw default deny incoming
$> sudo ufw default allow outgoing
Después tenemos que habilitar la conexión a OpenSSH, para poder seguir entrando a nuestro server. Esto es muy importante, porque nos podemos quedar bloqueados si no lo hacemos y habria que reiniciar el servidor y empezar de cero si no tienes copias de seguridad. Para ello introducimos el siguiente comando
$> sudo ufw allow OpenSSH
Una vez hecho esto, podemos listar las reglas que hemos añadido, habilitarlo y comprobar el estado del firewall
$> sudo ufw show added
$> sudo ufw enable
$> sudo ufw status
Al hacer esto, ningún puerto estará expuesto, salvo los que explicitamente indiquemos en un dockerfile o compose.yml. Pero esto no es deseable. Lo solucionaremos con un reverse proxy, que será lo único que estará expuesto a internet y se encargará de redirigir el tráfico. Se pueden utilizar opciones como Nginx, pero utilizaré Traefic.
services:
reverse-proxy:
image: traefik:v3.1
command:
- "--api.insecure=true"
- "--providers.docker"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
askfor:
image: ghcr.io/afordigital/askfor:prod
labels:
- "traefik.http.routers.askfor.rule=Host(`109.199.113.155`)"
Nota como no necesitamos declarar los puertos de nuestro proyecto, Traefic se encargará de ello
Después de levantar el contenedor con docker compose up
habilitaremos los puertos correspondientes
$> sudo ufw allow 80
$> sudo ufw allow 8080
Podemos hacer un pequeño load balancer si establecemos las réplicas en el yaml.
services:
reverse-proxy:
image: traefik:v3.1
command:
- "--api.insecure=true"
- "--providers.docker"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
askfor:
image: ghcr.io/afordigital/askfor:prod
labels:
- "traefik.http.routers.askfor.rule=Host(`109.199.113.155`)"
deploy:
mode: replicated
replicas: 3
Esto hará que las solicitudes se repartan entre la cantidad de replicas que creemos
Ya he escrito sobre como hacer CI/CD con Github Actions en otro post.
Pero recientemente he descubierto que hay algunas imagenes de docker que pueden encargarse de esto. Uno de ellos es watchtower. Monitorizará los contenedores que tengamos en nuestro servidor y si hay una nueva versión, la descargará y reiniciará el contenedor.
services:
watchtower:
image: containrrr/watchtower
command:
- "--rolling-restart"
- "--label-enable"
- "--interval"
- "30"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
reverse-proxy:
image: traefik:v3.1
command:
- "--api.insecure=true"
- "--providers.docker"
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
askfor:
image: ghcr.io/afordigital/askfor:prod
labels:
- "traefik.http.routers.askfor.rule=Host(`109.199.113.155`)"
- "com.centurylinklabs.watchtower.enable=tru
deploy:
mode: replicated
replicas: 3