Como correr tu propio LLM en AWS
knitr::opts_chunk$set(eval = FALSE)
Tengo una suscripción a ChatGPT que me parece un gran producto, pero info de trabajo que no puedo subir. En este post cuento como poner un LLM en una instancia EC2 para correrlo en forma local. La solución usa contendores con Ollama y OpenWebUI. Esta solución permite correr cualquier modelo open source disponible en Ollama como Deepseek, Mixtral y Dolphin3.
EC2
AWS tiene varios tipos de instancia con GPU. Las mejores opciones son: g4dn.2xlarge
($0.526) y g5.2xlarge
($1.212). Las dos son bastante caras así que hay que acordarse de apagarlas
cuando no las usas!
Para lanzar las instancias, hay que definir la configuración de la conectividad y el almacenamiento. Para el almacenamiento aumenté a 100 GB el tamaño del volúmen para poder correr modelos un poco más grandes.
Para conectividad, precisamos acceso ssh
(opcional) y acceso web a través del puerto (¿3000? ¿80?). Otra cosa que podría probar el el SSM.
Una vez lanzada la instancia, configuramos los drivers para poder usar la GPU. Hay que registrar el repositorio e instalar los drivers. Demora un poco.
# 1. Add NVIDIA's official drivers PPA
sudo add-apt-repository ppa:graphics-drivers/ppa -y
sudo apt update
# 2. Install the driver (525 or newer)
sudo apt install -y nvidia-driver-525
# 3. Reboot to activate the driver
sudo reboot
Si está todo OK, el comando nvidia-smi
nos da info sobre la GPU.
nvidia-smi
TODO: paste output. Este comando nos dice el modelo de GPU y cuanto RAM tiene disponible.
Ollama
Ollama permite correr modelos open source usando contenedores de Docker. Para esto, precisamos instalar Docker (doh!), y el toolkit de nvidia para contenedores.
Instalar Docker
sudo apt update
sudo apt install docker.io
NVIDIA Container toolkit
Esto sirve para que los contenedores de Docker puedan usar la GPU. Para probar si anda, corremos el comando nvidia-smi
adentro de un contenedor. Este comando usa la imagen nvidia/cuda
. ubuntu/ubuntu
también funciona.
docker run --rm --gpus all nvidia/cuda:12.0.0-base-ubuntu20.04 nvidia-smi
Instalar Ollama
curl -fsSL https://ollama.com/install.sh | sh
Prendemos Docker y creamos un contenedor basado en la imagen ollama/ollama
:
sudo docker run -d --gpus=all --restart unless-stopped -v ollama:/root/.ollama -p 11343:11343 --name ollama ollama/ollama
El contenedor tiene un volumen para persistir datos (los modelos que bajemos, etc.). La opción --restart unless-stopped
hace que el contenedor se reinicie si apagamos la instancia y la volvemos a prender. También recibimos conexiones por el puerto 11343.
El daemon de Docker corre como root, por lo que precisamos usar sudo para usar la cli. Para solucionar esto, agregamos nuestro usuario al grupo docker sudo usermod -aG docker ubuntu
.
Una vez creado el contenedor, corremos el comando ollama run dentro del contenedor. Este comando se baja dolphin-mixtral del repositorio de ollama (si no lo tenemos disponible en una carga anterior), lo carga en la memoria y nos da una consola interactiva para interactuar con el modelo.
docker exec -it ollama ollama run dolphin-mixtral
Tenemos todo funcionando bien, pero la interfaz por línea de comandos es incómoda.
Enter OpenWebUI
OpenWebUI es una app de React que nos permite interactuar con ollama a través de un navegador web, permite tener múltiples usuarios y guarda un historial de conversaciones. Para configurarlo, tenemos que crear un contenedor en base a la imagen oficial.
Hay que configurarlo para que se conecte con ollama
a través de la API. Este es el comando:
sudo docker run -d \
-v $(pwd)/openwebui:/app/backend/data \
-p 3000:8080 \
-e OLLAMA_BASE_URLS="http://172.31.39.200:11434" \
--name openwebui \
ghcr.io/open-webui/open-webui:main
Hay que acordarse de configurar la red paradejar pasar tráfico por el puerto 3000. Además, Ollama tiene que aceptar conexiones desde el contenedor que está corriendo openwebui. Para eso, seteamos la opción OLLAMA_HOST
cuando arrancamos el contenedor con ollama:
docker run -d \
--gpus=all \
--restart unless-stopped \
-v ollama:/root/.ollama \
-e OLLAMA_HOST="0.0.0.0"
-p 11434:11434 \
--name ollama \
ollama/ollama
Docker Compose
Para no tener que iniciar los contenedores manualmente desde la línea de comandos, docker compose nos permite especificar todos los parámetros necesarios en un archivo:
Con este archivo corremos docker compose up -d
y la app queda configurada.
restart: always
. Esta opción reinicia los contenedores cada vez que prendemos la máquina. Esto nos permite apagar y prender la instancia sin tener que volver a correr los contendores.
services:
ollama:
image: ollama/ollama
restart: always
ports:
- "11434:11434"
environment:
- "OLLAMA_HOST=0.0.0.0"
volumes:
- ollama:/root/.ollama
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
openwebui:
image: ghcr.io/open-webui/open-webui:main
restart: always
environment:
- "OLLAMA_BASE_URLS=http://ollama:11434"
ports:
- "3000:8080"
volumes:
- openwebui:/app/backend/data/
Otra mejora para AWS
También hice un script que prende la instancia cada vez que la uso:
Optimizando
Uno de los costos más altos en mi cuenta de AWS es de EBS storage, así que para bajar estos costos voy a configurar un snapshot de esta instancia y crear el volúmen cada vez que la prendo. Esto hace el tiempo de inicio un poquito más alto pero el costo es bastante menor. EBS sale ~$0.08 GB/mes y un snapshot (~$0.05 GB/mes). Además, salvando la snapshot en S3 pagamos solo por GB usado, mientras que el volumen cuesta por toda la capacidad del volúmen.
Una opción aún más barata es respaldar todo como un objeto en S3.
Otra cosa que noto es que la instancia demora mucho en parar.
Cambiando el modelo
Estoy leyendo que Dolphin3 puede tener mejor performance que el modelo que estaba usando. Para usarlo, nos conectamos el contenedor de ollama y borramos dolphin-mixtral:
# ollama ls
NAME ID SIZE MODIFIED
dolphin-mixtral:latest 4f76c28c0414 26 GB 2 weeks ago
# ollama rm dolphin-mixtral:latest
deleted 'dolphin-mixtral:latest'
Bajamos dolphin3:
docker ps