dashomu / R /github.md
jfulponi's picture
Upload 45 files
8bc9552
Dockerizando Shiny
================
## Introducción
Muchas veces, la versión estándar de los servidores gratuitos de Shiny
no alcanzan para nuestras hermosas creaciones en R. La limitación que
más me molestaba era la de las 5 instancias activas simultáneamente.
Para entornos de test quizá no estaba mal, pero una vez que la
aplicación llega a ámbitos más profesionales donde probablemente más
personas utilicen la app al mismo tiempo es más difícil.
En este breve tutorial llevaremos una apliación de Shiny a un
repositorio Docker y de allí mismo haremos un deploy a un servicio de
Google Cloud con más RAM y CPUs disponibles.
## Ingredientes
- Una aplicación Shiny, .Rmd, flexdashboard (como en este ejemplo),
etc.
- Docker y una cuenta Docker para subir la imagen a un repositorio.
- Cuenta en Google Cloud y las APIs básicas activas (el proceso es
bastante intuitivo).
## Instalando Docker
Para instalar Docker, se obtiene el archivo desde la página oficial y se
instala normalmente. En OSX, como es mi caso, después de descargar el
.dmg se instala de la siguiente manera desde la terminal:
``` bash
sudo hdiutil attach Docker.dmg
sudo /Volumes/Docker/Docker.app/Contents/MacOS/install
sudo hdiutil detach /Volumes/Docker
```
## Creando el Dockerfile
El archivo Dockerfile es un conjunto de líneas de texto que configuran
los paquetes que Docker va a utilizar para generar el entorno de nuestra
aplicación y es donde también le daremos “órdenes” para ejecutar la app.
En mi caso, creé una carpeta que se llama “Docker” con dos elementos:
una carpeta llamada “R” donde está todo lo relacionado a la aplicación
Shiny y el propio archivo “Dockerfile” (así, sin puntos, ni extensiones,
ni cosas raras).
En mi caso, el archivo Dockerfile contiene las siguientes líneas,
comentadas punto a punto para entender mejor:
``` bash
FROM rocker/shiny:latest # Se instala el entorno Shiny y R
RUN apt-get update -qq && apt-get -y --no-install-recommends install \ # Se instalan librerías necesarias
libgdal-dev \ # Ojo que probablemente necesites más o menos
libproj-dev \ # depende de los paquetes que uses
libgeos-dev \
default-libmysqlclient-dev \
libmysqlclient-dev \
libudunits2-dev \
netcdf-bin \
libxml2-dev \
libcairo2-dev \
libsqlite3-dev \
libpq-dev \
libssh2-1-dev \
unixodbc-dev \
libcurl4-openssl-dev \
libssl-dev
RUN apt-get install pandoc # Tenía problemas con Pandoc e instalarlo por separado lo solucionó
RUN apt-get update && \ # Actualizar paquetes y limpiar
apt-get upgrade -y && \
apt-get clean
# Se instalan los paquetes necesarios
# notar que ya estamos ejecutando COMANDOS DE R dentro del Docker
RUN R -e "install.packages(pkgs=c('shiny','tidyverse',
'flexdashboard', 'leaflet', 'sf', 'ggalluvial',
'plotly', 'rmarkdown'), repos='https://cran.rstudio.com/')"
RUN R -e "install.packages(pkgs=c('devtools'),
repos='https://cran.rstudio.com/')"
RUN R -e "devtools::install_github('rstudio/leaflet')"
RUN mkdir /root/app # Creamos la carpeta de la app
COPY R /root/shiny_save # Copia la carpeta "R" a la carpeta shiny_save
EXPOSE 3838 #Importantísimo: asignarle un puerto de comunicación a la app.
#Después no vamos a poder hacer nada si no se expone el puerto
# Se corre la app (en este caso, al ser un flexdashboard
# tengo que ejecutarlo con rmarkdown, si no sería shiny::runApp(...)).
# Como argumento extra le paso el puerto abierto.
CMD ["R", "-e", "rmarkdown::run('/root/shiny_save/dash_v2.Rmd',
shiny_args=list(host='0.0.0.0', port=3838))"]
```
## Construcción de la imagen
Una vez que tengamos el dockerfile y la carpeta con todos los archivos
que necesita la app para correr, pasamos a construir la imagen de Docker
propiamente dicha. Para esto, abrimos la consola y ejecutamos:
``` bash
docker login
```
Nos pedirá el usuario y la contraseña de Docker. La escribimos y luego:
``` bash
docker build -t nombreapp .
```
Ese comando construirá una imagen llamada “nombreapp” con los parámetros
del dockerfile (para eso el punto al final). Esta imagen contiene el
entorno R y todo el código que hemos usado, dentro de un sistema
Linux.
Es por esto que probablemente tarde bastante en ejecutarse la primera
vez (a mí me lleva aproximadamente 3 horas), obviamente dependiendo la
complejidad de la app. Si hacemos modificaciones al código de R de la
aplicación y pusheamos los cambios (ejecutando los mismos parámetros),
la construcción tarda muchísimo menos ya que la mayoría de las capas del
entorno son las mismas y no se modifican. Esto es algo que me encanta de
Docker. El trabajo se divide en “capas”, por lo que si corregís una
parte pequeña del código, el build y el push de la app tardan segundos.
``` bash
docker tag nombreapp usuario/nombreapp:latest
```
“Tagueamos” la app dentro del repositorio público que estamos por crear.
Es como decir que lo estamos “seleccionando” para subirlo. El
repositorio se llamará usuario/nombreapp. La última parte “:latest” es
un nombre que podemos poner para identificar a la última versión de la
app. Lo podemos ir modificando para cada versión.
``` bash
docker push usuario/nombreapp:latest
```
Se pushea la imagen al repositorio, tal como si fuera GitHub.
## Deploy en Google Cloud
Una vez creado el proyecto, procedemos a abrir la consola de comandos
dentro de la misma Web de Google. Lo primero que debemos hacer es
loguearnos con nuestras credenciales de Docker.
``` bash
docker login
```
Posteriormente, “traemos” la imagen del Docker a nuestro proyecto de
Google Cloud con los últimos cambios.
``` bash
docker pull usuario/nombreapp:latest
```
Luego, análogamente a lo que hacíamos en Docker, se “taguea” la imagen a
pushear, lo único es que esta vez no pusheamos al repo de Docker sino al
container de imágenes de Google Cloud. Reemplazá PROJECT_ID por el ID de
tu proyecto.
``` bash
docker tag usuario/nombreapp:latest gcr.io/PROJECT_ID/usuario/nombreapp:latest
```
Por último, pusheamos la imagen.
``` bash
docker push gcr.io/PROJECT_ID/usuario/nombreapp:latest
```
## Creación de la instancia del servicio
Ya casi terminamos. Luego de pusheada la imagen, en la pestaña de Google
Cloud Run clickeás “Crear Servicio”. Esto nos habilitará una máquina
virtual para hacer el deploy y que podamos compartir la app a un montón
de gente.
Donde dice “Url de la imagen” poné seleccionar y se van a desplegar las
opciones de imagen. Nos va a aparecer la imagen “nombreapp” con la
versión “latest”. Usamos esa y completamos los parámetros con lo que
necesite el proyecto.
**IMPORTANTE:** En la parte de más abajo donde dice “CONTENEDOR”,
completá dentro del campo “Puerto del contenedor” el puerto 3838 o el
que hayas elegido en la construcción del Dockerfile, ya que sin esto
Google va a enrutar mal el puerto generando errores.
Esperás unos segundos a que Google cree la instancia y listo! Ya está la
app deployada en Google Cloud Run. Cualquiera va a poder acceder a ella
a través de la URL que te muestra arriba del panel.