How’s that done?
::write_bib(names(sessionInfo()$otherPkgs), file="bibliography.bib") knitr
Robert W. Walker
March 3, 2023
Last updated: 2023-04-06 03:04:17
Timezone: America/Los_Angeles
When it comes to shiny applications, hosting is a perennial concern. The free alternative shinyapps.io
works great but limits the number of user applications that can be deployed. One can operate a server and serve the shiny applications for one’s self. To be honest, this solution is probably superior but is also costly insofar as system administration consumes time. There is an in between that is worth exploring.
We could link to the apps and deploy them in the cloud. The most common methods for accomplishing this particular task involve Docker containers and using Docker to run the shiny applications. I want to explore this alternative in what follows.
docker
is a tool for setting up a working environment that can house a shiny server. Colin Fay has a nice post introducing docker. There is also a very nice article on rocker
by Eddelbuettel and Boettiger.
One can. The free shiny server can be installed and configured and the process is fairly straightforward. I am coming to believe that it is simply an easier administration task to let containers handle this.
There are a few steps to this.
shiny-server.conf
I created a shiny-server.conf
file to configure the shiny server inside the container. The contents of the file are below.
# Define the user we should use when spawning R Shiny processes
run_as shiny;
# Define a top-level server which will listen on a port
server {
# Instruct this server to listen on port 3838.
listen 3838;
# Define the location available at the base URL
location / {
# Run this location in 'site_dir' mode, which hosts the entire directory
# tree at '/srv/shiny-server'
site_dir /srv/shiny-server;
# Define where we should put the log files for this location
log_dir /var/log/shiny-server;
# Should we list the contents of a (non-Shiny-App) directory when the user
# visits the corresponding URL?
directory_index on;
}
}
shiny-server.sh
This file invokes the shiny server.
#!/bin/sh
# Make sure the directory for individual app logs exists
mkdir -p /var/log/shiny-server
chown shiny.shiny /var/log/shiny-server
exec shiny-server >> /var/log/shiny-server.log 2>&1
Dockerfile
The dockerfile needs instructions for building R and being able to execute the necessary components. Here is the Dockerfile.
# get shiny server plus tidyverse packages image
FROM rocker/shiny-verse:latest
# system libraries of general use
RUN apt-get update && apt-get install -y \
sudo \
pandoc \
pandoc-citeproc \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
libssl-dev \
libssh2-1-dev
# install R packages required
# (change it depending on the packages you need)
RUN R -e "install.packages(c('shinydashboard','DT','hrbrthemes','lubridate'), repos='http://cran.rstudio.com/')"
# Copy configuration files into the Docker image
COPY shiny-server.conf /etc/shiny-server/shiny-server.conf
COPY /app /srv/shiny-server/
RUN rm /srv/shiny-server/index.html
# Make the ShinyApp available at port 3838
EXPOSE 3838
# Copy further configuration files into the Docker image
COPY shiny-server.sh /usr/bin/shiny-server.sh
RUN ["chmod", "+x", "/usr/bin/shiny-server.sh"]
CMD ["/usr/bin/shiny-server.sh"]
app.R
The last thing that I require is a functional app. In this case, my app is three panels applied to archigos
and an .rda version of the data exists in the container. Here is the total file.
#
# This is a Shiny web application. You can run the application by clicking
# the 'Run App' button above.
#
# Find out more about building applications with Shiny here:
#
# http://shiny.rstudio.com/
#
library(shiny)
library(bslib)
library(shinydashboard)
library(tidyverse)
library(magrittr)
library(utf8)
library(shiny)
library(DT)
library(lubridate)
library(hrbrthemes)
# load the data
load("data/archigos.rda")
# create the set of countries
Country.Select <- Archigos %$% table(idacr) %>% data.frame() %>% mutate(Country = idacr) %>% select(Country)
# plot for durations
Plot.Durations <- function(data, state) {
data %>% ggplot(., aes(x=tenureY)) + geom_histogram() + theme_ipsum_rc() + labs(x="Durations", title=paste0("Durations: ",state))
}
# plot for chronology
Plot.Chronology <- function(data, state) {
data %>% arrange(eindate) %>%
ggplot(., aes(x=fct_reorder(leader, eindate), color=leader)) + geom_errorbar(aes(ymin=eindate, ymax=eoutdate)) + coord_flip() + labs(x="", title=paste0("Leader Chronology: ",state)) + theme_ipsum_rc() + scale_color_viridis_d(option = "E") + guides(color=FALSE)
}
header <- dashboardHeader(title = "Archigos")
sidebar <- dashboardSidebar(
sidebarMenu(selectInput(inputId = "Country", label="Country:", choices = Country.Select$Country, selected="AFG"))
)
body <- dashboardBody(
tabsetPanel(
tabItem(tabName = "dashb1",
title="Chronology",
# Boxes need to be put in a row (or column)
fluidRow(box(plotOutput("plotDur"), width=12))
),
tabItem(tabName = "dashb2",
title="Durations",
fluidRow(box(plotOutput("plotChr"), width=12))
)),
fluidRow(DTOutput("plotDT"))
)
ui <- dashboardPage(skin = "purple", header, sidebar, body)
server <- function(input, output) {
dataset <- reactive({
Archigos %>%
filter(idacr==input$Country) %>%
arrange(desc(eoutdate))
})
output$plotDT <- renderDT({ dataset()}, options = list(scrollX = TRUE)
)
output$plotDur <- renderPlot({
Plot.Chronology(dataset(), input$country)
})
output$plotChr <- renderPlot({
Plot.Durations(dataset(), input$country)
})
}
# Run the application
shinyApp(ui = ui, server = server)
At the terminal, here I need to build the docker image.
sudo docker build image -t pieratio/archigos
The first one is to go to docker hub. I have an account that has a single container currently there called archigos
.
Next we wander off to cloud.google.com and enable containers for use in Cloud Run
. I then set up the project called archigos
attached to port 3838 and set it to run. Now I have a running shiny app in the cloud on google to tie to this post!
Deploying this container is tricky because of the overall size of the project. I have to limit the number of instances and kick up the amount of usable RAM to make it work. The margins of this page do not render this super well. It is better seen at the host site for that repository.