Dockerizing Shiny Apps
Within the data science world, a commonly cited struggle is getting the results of an analysis embedded in an already existing business process – in other words, having the stakeholder use the data.
Often, it is easier to embed science into a process if the stakeholders have input into the process rather than simply working from flat results. Shiny Apps help Data Scientists to provide flexible sciences that allow for stakeholder input.
What is Docker?
Docker is an open source tool that allows Data Scientists to package applications with all dependencies, packages, and data into a self-sustainable container that can then be deployed to users as needed.
Why Dockerize a Shiny App?
One obvious question exists – if I have access to Shiny Server, why would I Dockerize my Shiny App? Well, you might not! Dockerizing an app is an alternative solution for those who do not have a dedicated Shiny Server, though still wish to deploy apps to stakeholders.
In addition to this, Dockerizing an app allows for the app to be reproducible by anybody. Because the container has all the package, data, and code dependencies necessary to run the app, it should be easily transferrable in the case of work handoff or collaborative coding.
How to Dockerize a Shiny App
The key to creating a Docker container is the Dockerfile. The Dockerfile is a set of instructions that tells Docker how to create the container. Below is an example Dockerfile that will Dockerize a Shiny App. The prerequisites for this Dockerfile example are:
- The app, and any relevant images, data, etc. are located in a folder named
app/folder must contain a
ui.Rfile that run the Shiny app
FROM rocker/shiny:3.5.1 RUN apt-get -y install libcurl4-gnutls-dev libxml2-dev libssl-dev; \ rm -r /srv/shiny-server; \ mkdir -p /var/lib/shiny-server/bookmarks/shiny; \ sed -i 's/3838/3838 0.0.0.0/' /etc/shiny-server/shiny-server.conf RUN R -e "install.packages(pkgs=c('shiny','tidyverse','shinydashboard'), repos='https://cran.rstudio.com/')" COPY /app /srv/shiny-server/ RUN chmod -R +rx /srv/shiny-server/ USER shiny EXPOSE 3838 CMD ["/usr/bin/shiny-server.sh"]
Now, let’s break down this Dockerfile to look at how it works.
This part tells Docker what source image to use. We are going to be using the shiny source image from rocker. You can check the documentation for this image and see the full code here.
RUN apt-get -y install libcurl4-gnutls-dev libxml2-dev libssl-dev; \ rm -r /srv/shiny-server; \ mkdir -p /var/lib/shiny-server/bookmarks/shiny; \ sed -i 's/3838/3838 0.0.0.0/' /etc/shiny-server/shiny-server.conf
This chunk will install some linux utilities that are necessary to run shiny server. It will also change some of the files from the rocker version of shiny so that we can deploy our app. Specifically, take a look at this line:
sed -i 's/3838/3838 0.0.0.0/' /etc/shiny-server/shiny-server.conf
Here, we are modifying the default shiny-server.conf file so that we open traffic to any IP (the 0 .0.0.0), and we are keeping the port of our app at the default of
3838. If we wanted to change port numbers, we would change only the second
3838 to whatever port number we wanted. For example, to change the port number to 1738, we would change the line to look like this:
sed -i 's/3838/1738 0.0.0.0/' /etc/shiny-server/shiny-server.conf
And now we would be deploying the app to port
1738 instead of
RUN R -e "install.packages(pkgs=c('shiny','tidyverse','shinydashboard'), repos='https://cran.rstudio.com/')"
This line installs whatever R packages we need into the container. Simply add packages within the
pkgs=c() argument that you want installed. This runs the command using R, so you can provide any additional function arguments that
install.packages() accepts. For full documentation, see here.
If you need an older version of a package, or simply want your container to always use a specific version, you can pass the full URL of the package version you want rather than the name of the package. For example, if we want
ggplot2 to be frozen at version
0.9.1, we could add this line to our Dockerfile:
RUN R -e "install.packages(pkgs='http://cran.r-project.org/src/contrib/Archive/ggplot2/ggplot2_0.9.1.tar.gz',repos=NULL,type='source')"
This will install the specific version of ggplot2 into our container, and make future-proofing the Shiny App easier.
COPY /app /srv/shiny-server/ RUN chmod -R +rx /srv/shiny-server/
This tells the Dockerfile to copy our
/app folder into the
/srv/shiny-server/ folder in our container. The end pathway will be
/srv/shiny-server/app. We then chmod our folder so that the files can be run by the container.
USER shiny EXPOSE 3838 CMD ["/usr/bin/shiny-server.sh"]
The last few lines here tell the container that we are declaring a
USER for the container, and that user is shiny. We then expose the app to our port number so that we can access the Shiny App via a URL. Lastly, we run the shiny-server shell script (provided by the
rocker/shiny:3.5.1 image) to kick off shiny server, running our app in the __
Deploying the App
Once your Dockerfile is complete, simply create a folder that has the Dockerfile and the
app/ folder in it.
Then, simply navigate to the folder containing the Dockerfile and run the command
docker build -t <NAME> <DOCKERFILE_PATH>
<NAME>, in this case, is the name you want to give your Docker image.
<DOCKERFILE_PATH> is the file path to the Dockerfile (if you are in the folder already, just enter .)
cd /path/to/dockerfile/ docker build -t example_shiny_docker .
This will begin the creation of the Docker image, using your Dockerfile as instructions.
Once the image is created, you can see deploy the image to the given port by using
docker run -p <PORT>:3838 <NAME>
<NAME> must match the name of the Docker image you created before, and
<PORT> should match the port # in your Dockerfile.
docker run -p 3838:3838 example_shiny_docker
Once this is entered, you are done! The app will now be accessible hosted from your server at the port # specified.