Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
It doesn’t matter if you’ve created the world’s best R Shiny application if you can’t share it with others. Reproducibility and portability are two major key points in software development, and they basically stand for the idea that the code running on your machine should be easily reproduced on another machine.
That’s where Docker comes in. In the context of R Shiny, Docker allows you to create and package an application that’s ready to be shipped and recreated on another operating system. Think of it as an entire virtual machine running Shiny but without the unnecessary stuff.
Today you’ll learn why Docker is important for R Shiny applications, and how to get started using R Shiny Docker by coding an app from scratch and Dockerizing it. Let’s dig in!
Are your data science pipelines reproducible? Read our guide to clean and function-oriented pipelines with R targets.
Table of contents:
- Why R Shiny Docker – The Benefits
- Writing the R Shiny Application from Scratch
- R Shiny Docker – How to Dockerize your Shiny App
- Summing up R Shiny Docker
Why R Shiny Docker – The Benefits
Docker is a platform for developing, shipping, and running applications in isolated environments, or containers, which is something you know if you’ve read our previous blog post on getting started with R and Docker.
It can provide a lot of benefits for you in the context of R and R Shiny, such as:
- Consistency: You want your code running consistently across different environments, such as development, testing, and production. Docker containers essentially encapsulate programming environments, so you can rest assured the code and dependencies will remain the same across all of them.
- Reproducibility: Docker images are highly flexible, meaning they allow you to specify the R version needed, R dependencies, and other OS-related dependencies. This ensures you (or anyone else) won’t run into any issues when sharing your code. It’s a write-once-run-anywhere system.
- Portability: The containers you create can be run on any system that supports Docker, such as servers, other laptops, home NAS solutions, and even Raspberry PI.
- Scalability: Docker makes it easy to scale applications, R Shiny included. You can create multiple containers with identical configurations and scale your app horizontally. This is a good way of future-proofing your infrastructure as your app’s workload increases.
There are many other, excellent reasons to use Docker, but we find these ones to be the most relevant in the context of R and R Shiny.
As for the Docker installation, we’ve covered it in a previous article, and it boils down to a couple of mouse clicks once you download the installation file from the web.
Linux users might have to do a couple of extra steps, but it’s all well-documented online.
Writing the R Shiny Application from Scratch
The focus of today’s article isn’t R Shiny, so the app we’ll write will be relatively simple.
We’ll leverage the well-known Gapminder dataset to visualize the average life expectancy over time, and also the average GDP per capita. The user will be able to select the continent, and the app will automatically update when a change is detected.
In case you need a refresher on data visualization in R:
If you have some experience with R Shiny, the dashboard code should read like plain English. If not, try tweaking a couple of things to see what happens.
The only thing that should look new is a call to options()
function at the top of app.R
. We’re using this function to explicitly define the host and port on which the app will run.
You generally want to take maximum control over the deployment process, so that’s why we highly recommend you do the same as we did
Anyhow, here’s the full source code for app.R
:
library(shiny) library(dplyr) library(ggplot2) library(gapminder) # Specify the application port options(shiny.host = "0.0.0.0") options(shiny.port = 8180) ui <- fluidPage( sidebarLayout( sidebarPanel( tags$h4("Gapminder Dashboard"), tags$hr(), selectInput(inputId = "inContinent", label = "Continent", choices = unique(gapminder$continent), selected = "Europe") ), mainPanel( plotOutput(outputId = "outChartLifeExp"), plotOutput(outputId = "outChartGDP") ) ) ) server <- function(input, output, session) { # Filter data and store as reactive value data <- reactive({ gapminder %>% filter(continent == input$inContinent) %>% group_by(year) %>% summarise( AvgLifeExp = round(mean(lifeExp)), AvgGdpPercap = round(mean(gdpPercap), digits = 2) ) }) # Common properties for charts chart_theme <- ggplot2::theme( plot.title = element_text(hjust = 0.5, size = 20, face = "bold"), axis.title.x = element_text(size = 15), axis.title.y = element_text(size = 15), axis.text.x = element_text(size = 12), axis.text.y = element_text(size = 12) ) # Render Life Exp chart output$outChartLifeExp <- renderPlot({ ggplot(data(), aes(x = year, y = AvgLifeExp)) + geom_col(fill = "#0099f9") + geom_text(aes(label = AvgLifeExp), vjust = 2, size = 6, color = "#ffffff") + labs(title = paste("Average life expectancy in", input$inContinent)) + theme_classic() + chart_theme }) # Render GDP chart output$outChartGDP <- renderPlot({ ggplot(data(), aes(x = year, y = AvgGdpPercap)) + geom_line(color = "#f96000", size = 2) + geom_point(color = "#f96000", size = 5) + geom_label( aes(label = AvgGdpPercap), nudge_x = 0.25, nudge_y = 0.25 ) + labs(title = paste("Average GDP per capita in", input$inContinent)) + theme_classic() + chart_theme }) } shinyApp(ui = ui, server = server)
This is what the app looks like when you run it:
As you would expect, the chart contents update when you change the dropdown menu value:
To conclude, this R Shiny app is nothing fancy, but will serve us just fine for simple deployment purposes. Let’s go over that next.
R Shiny Docker – How to Dockerize your Shiny App
In this section, you’ll see how to write a simple Dockerfile
for an R Shiny application, how to create a Docker image from Dockerfile
, and also how to create a container from the previously created image.
Writing the Dockerfile
Think of Dockerfile
as an instruction file responsible for telling Docker how to build an image. In a nutshell, you pick a base image (typically Linux with R language installed), and then specify instructions for installing additional dependencies, copying project files, and running R scripts.
It’s a simple file when you get used to the syntax, but uses a couple of distinct keywords you have to learn:
FROM
: A starting point for everyDockerfile
. Used to tell Docker from which base image you’ll build your own. Arocker/shiny
image already has R and Shiny installed, so that’s the one we’ll use.RUN
: Runs a shell command. Useful for creating files and folders, installing dependencies, and more.COPY
: Copies contents from your local machine to the Docker container. You first specify the path to the local file, followed by the file on the container.EXPOSE
: Exposes a port of a Docker container so you can access it from your local machine.CMD
: Command that will be used every time you launch the container. We’ll use it to launch the R Shiny app.
You’ll find the contents of our Dockerfile
below. Create your file first (no extensions), and copy the following snippet:
# Base R Shiny image FROM rocker/shiny # Make a directory in the container RUN mkdir /home/shiny-app # Install R dependencies RUN R -e "install.packages(c('dplyr', 'ggplot2', 'gapminder'))" # Copy the Shiny app code COPY app.R /home/shiny-app/app.R # Expose the application port EXPOSE 8180 # Run the R Shiny app CMD Rscript /home/shiny-app/app.R
To summarize, we’re using the latest rocker/shiny
image since it already has R programming language and Shiny frameworks installed – less work for us to do. Then, we’re creating a directory to store our app, install R package dependencies, copy the app.R
file, exposing the port on which the Shiny app will run (the one hardcoded in app.R
), and then finally using Rscrit
to launch the app.
The next step is to create our custom image from this Dockerfile
.
Creating the Image and Running the Container
Note: At the time of writing this article (September 2023), the rocker/shiny
base image isn’t supported on Apple Silicon. If you’re using an M-series Mac, you’ll have to append the --platform linux/x86_64
tag when building the image.
To build an image from a Dockerfile
, run the following command (make sure you navigate to the source code folder first):
docker build -t shiny-docker-demo .
This will create a new image called shiny-docker-demo
.
If you’re using an Apple Silicon Mac like us, run this command instead:
docker build --platform linux/x86_64 -t shiny-docker-demo .
This is the Terminal output you will see:
Downloading the base image and R dependencies will take some time, and you’ll see something similar once finished:
Your Docker image is now created, which means the only thing left to do is to run it inside a container.
To do so, run the following command (works on M1/M2 Macs):
docker run -p 8180:8180 shiny-docker-demo
Here’s what you’ll see in the Terminal:
The R Shiny application is now running inside the Docker container on port 8180. You can access it from your local machine since we’ve exposed that port in the Dockerfile
.
Here’s what the app looks like:
As before, you can change the value in the dropdown menu and the charts will update automatically:
And that’s it – you’ve successfully Dockerized an R Shiny application!
Monitoring Dockerized R Shiny Apps
The Docker Desktop application can do all sorts of powerful things. For example, you can click on the container that’s running your R Shiny application to inspect the runtime logs and even the file system.
You can see that the directory structure was created as per the Dockerfile
instructions:
Further, you can monitor the resource usage under the Stats tab:
This tab might need some time to populate the chart values, but you get the point – there’s a lot of tooling and monitoring built into the Docker Desktop application, so make sure to spend some time investigating.
Summing up R Shiny Docker
Congratulations – you now have a fully working R Shiny application running in a Docker container! Writing Dockerfiles is definitely something to get used to, but it will feel like a walk in the park after a couple of weeks.
Today you’ve successfully Dockerized one Shiny application, but what if you want to run two or more Shiny apps at once in separate containers? Make sure to stay tuned to Appsilon Blog so you don’t miss out on advanced deployment techniques.
What’s your preferred way of sharing R Shiny applications? Do you use Docker or something else? Let us know in the comment section below.
Yes, R programming can significantly improve your business workflows – Here are 5 ways how.
The post appeared first on appsilon.com/blog/.
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.