Plumber + Shiny + Docker
Page content
Why?
- Shiny will scale really well as the data is not duplicated in memory for each user
- The data is housed in one location (plumber API)
- The underlying data can be VERY large, which may not be suitable for a shiny app, but is ok for an R process to solve.
- The API inner workings can be updated without redeploying the shiny application.
The docker-compose.yml
version: '3.7'
services:
shinyapptest:
build: ./shiny
container_name: testshiny
image: testshiny
restart: unless-stopped
networks:
- backend
ports:
- 80:3838
dockerizedapi:
build: ./plumber
container_name: dockerizedapi
image: dockerizedapi
restart: unless-stopped
networks:
- backend
networks:
backend:
external:
name: backend
- There are two services:
shinyapptest
: Is the shiny app that calls the Plumber API. This app is available to the world in port 80.dockerizedapi
" The Plumber api that is only available toshinyapptest
The Dockerfiles
Plumber
FROM rocker/r-base
RUN apt-get update -qq && apt-get install -y \
git-core \
libssl-dev \
libcurl4-gnutls-dev
RUN R -e 'install.packages("remotes")'
RUN R -e 'remotes::install_cran(c("glue", "plumber", "magrittr"))'
COPY testShinyPlumber_*.tar.gz /app.tar.gz
RUN R -e 'remotes::install_local("/app.tar.gz")'
EXPOSE 3098
CMD R -e "testShinyPlumber::run_api()"
Shiny
FROM rocker/r-base
RUN apt-get update -qq && apt-get install -y \
git-core \
libssl-dev \
libcurl4-gnutls-dev
RUN R -e 'install.packages("remotes")'
RUN R -e 'remotes::install_cran(c("shiny", "plumber"))'
COPY testShinyPlumber_*.tar.gz /app.tar.gz
RUN R -e 'remotes::install_local("/app.tar.gz")'
EXPOSE 3838
CMD R -e "testShinyPlumber::run_app()"
The R Package testShinyPlumber
Notice that both docker cotainers call testShinyPlumber
.
The Plumber API
This is saved as plumber.R
inside the ./inst
folder
library(plumber)
#* @apiTitle Plumber Example API
#* Echo back the input
#* @param mean
#* @param sd
#* @get /rnorm
function(mean = "0", sd = "sd") {
rnorm(n = 1000, mean = as.numeric(mean), sd = as.numeric(sd))
}
The R Fuctions
run_api.R
#' Run Test API
#'
#' @return
#' @export
#'
#' @examples
#' \dontrun{
#' testShinyPlumber::run_api()
#' }
run_api <- function(){
pr <- plumber::plumb(file = system.file("plumber.R", package="testShinyPlumber"))
pr$run(host='0.0.0.0', port = 3098, swagger = FALSE)
}
get_draws.R
#' Get Draws
#'
#' @param mean
#' @param sd
#' @param host
#'
#' @return
#' @export
#'
#' @examples
#' \dontrun{
#' draws <- testShinyPlumber::get_draws(mean = 9, sd = 1)
#' }
get_draws <- function(mean, sd, host="localhost"){
draws <- httr::GET(url = glue::glue('http://{host}:3098/rnorm'),
query = list(mean = mean,
sd = sd)) %>%
httr::content(., encode='json') %>%
unlist()
return(draws)
}
run_app.R
#' Run the Shiny Application
#'
#' @export
#' @importFrom shiny runApp
run_app <- function() {
shiny::runApp(system.file("app", package = "testShinyPlumber"), port = 3838, host = "0.0.0.0")
}
Get Things Up and Running
docker-compose build
docker-compose up #-d if you want to run it as a service
Final Thoughts
- I should comeback to this someday and add more details. For now, here is a public repo with all the code.