Cómo Correr Shiny en Docker: Una guía Paso a Paso
R Shiny es una de las formas más eficientes de crear aplicaciones de datos interactivas. Y cuando llega el momento de entregar tus paneles analíticos avanzados a los usuarios, es importante garantizar entornos reproducibles y estabilidad. Esto evitará sorpresas desagradables y retrasos al final del proceso, cuando tanto tú como tus usuarios pensaban que todo estaba listo.
La necesidad de un camino fluido desde el desarrollo hasta los despliegues en producción no es exclusiva de Shiny, sino que se aplica a muchas tecnologías en diferentes industrias. Docker es una excelente manera de lograr esto, tanto es así que Docker (y los contenedores en general) se han convertido en un estándar de la industria para la entrega de aplicaciones.
En esta publicación de blog, aprenderás cómo comenzar a ejecutar una aplicación Shiny desde un contenedor y luego usar Faucet para desplegar tu aplicación Shiny en producción.
¿Por qué usar Shiny en Docker?
Docker es la herramienta más popular para construir y ejecutar contenedores. Aloja el registro más popular de imágenes de contenedores, DockerHub, y ha sido adaptado para todas las plataformas populares.
El uso de contenedores puede ofrecer muchos beneficios, entre los que se incluyen, pero no se limitan a:
- Estabilidad: Ejecutar aplicaciones que duren años siempre será un desafío. Sin embargo, hacerlo sin un entorno estable puede hacer que esto sea casi imposible. Los contenedores proporcionan un entorno estable del cual tu aplicación puede depender en gran medida. Si ejecutas un contenedor hoy, es concebible que siga funcionando durante un par de años.
- Portabilidad: Los contenedores te permiten desplegar tu aplicación en muchos entornos. Puedes desplegar en servicios administrados como Google Cloud Run, AWS ECS o Fargate, y Azure Container Apps. También puedes ejecutar tu contenedor directamente en una máquina virtual, lo que te dará mayor control sobre los recursos. Todos estos funcionan muy bien con contenedores.
- Reproducibilidad: Al utilizar contenedores, tu equipo puede optimizar el proceso de desarrollo. Si el contenedor funciona en un equipo, puedes esperar que funcione en otro.
Puedes comenzar con Docker visitando: docker.com para obtener instrucciones específicas de instalación para cada plataforma.
Creando nuestra aplicación Shiny
Para este ejemplo, crearemos una aplicación Shiny muy sencilla. Utilizaremos un único archivo app.R en lugar de separarlo en múltiples archivos para mayor simplicidad, sin embargo, el ejemplo será válido en ambos casos.
Este será el código que incluiremos en el archivo app.R:
library(shiny)
library(bslib)
ui <- page_sidebar(
title = "Hello Shiny!",
sidebar = sidebar(
sliderInput(
inputId = "bins",
label = "Number of bins:",
min = 1,
max = 50,
value = 30
)
),
plotOutput(outputId = "distPlot")
)
server <- function(input, output) {
output$distPlot <- renderPlot({
x <- faithful$waiting
bins <- seq(min(x), max(x), length.out = input$bins + 1)
hist(x, breaks = bins, col = "#007bc2", border = "white",
xlab = "Waiting time to next eruption (in mins)",
main = "Histogram of waiting times")
})
}
shinyApp(ui = ui, server = server)
Podemos verificar que la aplicación funciona ejecutándola en nuestra consola de R con shiny::runApp().
Escribiendo el Dockerfile
En esta sección, aprenderás a escribir un Dockerfile para tu aplicación Shiny. El Dockerfile contiene la receta para crear tu imagen. Un Dockerfile típicamente incluye una imagen base y un conjunto de instrucciones.
Este es el Dockerfile que utilizaremos para nuestra aplicación Shiny mínima:
# Base image for our image
FROM ixpantia/faucet:r4.4
# Install the packages we need
RUN Rscript -e "install.packages(c('shiny', 'bslib'))"
# Copy the file app.R from our computer to the image
COPY app.R app.R
# We are done!
Este Dockerfile se puede resumir en lo siguiente:
- Imagen base: Este Dockerfile utiliza ixpantia/faucet:r4.4 como imagen base. Faucet es un programa que se encarga de ejecutar la aplicación, registrar las solicitudes e incluso crear réplicas.
- Dependencias: Luego instalamos shiny y bslib (nuestras dos dependencias), que necesitamos para ejecutar nuestra aplicación.
- Copiar nuestro código: A continuación, copiamos nuestro código dentro de la imagen; en este caso, solo copiamos nuestro app.R.
Antes de continuar, se recomienda guardar este archivo con el nombre Dockerfile en el mismo directorio donde se encuentra la app.R.
¿Por qué utilizar ixpantia/faucet
?
La imagen de Docker ixpantia/faucet
viene con Faucet pre configurado para ejecutarse en el directorio de trabajo predeterminado. Esto te permite crear archivos Docker mínimos sin mucha configuración adicional.
Faucet ofrece varios beneficios integrados, como réplicas de la aplicación, registro de solicitudes, un enrutador multi-aplicaciones y soporte para APIs de Plumber. Esto significa que puedes usar el mismo flujo de trabajo para la mayor parte de la infraestructura que necesitas desplegar con R, lo que proporciona un gran impulso a la productividad de tu equipo.
ixpantia/faucet
también es compatible con arm64 y amd64, lo que significa que Faucet se ejecuta de forma nativa en servidores ARM, PCs y portátiles, mientras que otras opciones pueden estar disponibles sólo para sistemas x86 o requerir capas de emulación que afectan al rendimiento.
Para aprender más sobre faucet puedes visitar GitHub o leer la documentación oficial.
Construcción y Ejecución de nuestra Imagen Docker
Ahora que tenemos un Dockerfile, necesitamos construirlo en una imagen. Asegúrate de que tu directorio de trabajo sea el mismo en el que se encuentran el Dockerfile y app.R. Luego, puedes ejecutar el siguiente comando:
docker build -t shiny-app .
Esto iniciará el proceso de construcción con la etiqueta shiny-app.
Una vez que la imagen esté construida, podemos ejecutar la aplicación. Algo importante a tener en cuenta es que necesitaremos exportar un puerto al equipo del usuario. Podemos hacerlo utilizando la bandera -p.
Para ejecutar el contenedor, podemos ejecutar el siguiente comando:
docker run -p 3838:3838 shiny-app
¡Ahora podemos acceder a localhost:3838
y ver nuestra aplicación en funcionamiento!
Si observas la salida de tu terminal, es posible que veas registros detallados sobre lo que está ocurriendo en la aplicación.
Ajustes para producción
Si estás buscando llevar tu aplicación a producción, puede que te interese modificar algunas opciones para asegurarte de que la aplicación funcione como deseas.
Configurar el número de réplicas
Si utilizas el contenedor en un sistema multinúcleo, probablemente notes que se inician varios workers. Estos workers son réplicas de nuestra aplicación, lo que te permite escalarla al servir conexiones concurrentes en diferentes núcleos/procesos de tu contenedor. Puedes ajustar esto configurando la variable de entorno FAUCET_WORKERS
en tu Dockerfile. Si no se establece, el número de workers será igual al número de núcleos lógicos en tu contenedor.
# Base image for our image
FROM ixpantia/faucet:r4.4
# Install the packages we need
RUN Rscript -e "install.packages(c('shiny', 'bslib'))"
# Copy the file app.R from our computer to the image
COPY app.R app.R
# Set four workers
ENV FAUCET_WORKERS=4
# We are done!
Apagado controlado (Graceful shutdown)
Si planeas desplegar tu aplicación en un servicio como Google Cloud Run, AWS ECS o Fargate, el apagado controlado hará que el proceso de despliegue continuo sea más fluido. El apagado controlado permite que la aplicación y sus réplicas no se detengan hasta que todas las conexiones o solicitudes se hayan completado. Podemos añadir el apagado controlado configurando la variable de entorno FAUCET_SHUTDOWN
en tu Dockerfile. Esto asegura que las actualizaciones o despliegues no interrumpan las solicitudes en curso.
# Base image for our image
FROM ixpantia/faucet:r4.4
# Install the packages we need
RUN Rscript -e "install.packages(c('shiny', 'bslib'))"
# Copy the file app.R from our computer to the image
COPY app.R app.R
# Enable graceful shutdown
ENV FAUCET_SHUTDOWN=graceful
# We are done!
Conclusión
En conclusión, utilizar Docker para contenerizar tu aplicación R Shiny ofrece beneficios significativos, como estabilidad, portabilidad y reproducibilidad. Siguiendo los pasos descritos en esta guía, puedes asegurarte de que tu aplicación Shiny funcione en un entorno consistente, tanto para el desarrollo como para la producción.
Si deseas llevar tu infraestructura R al siguiente nivel, ¡Contáctanos y comienza tu camino con ixpantia!