R Shiny Apps in a Docker Container
R Shiny Containerized
One of the technologies that I am happy to have found out in the previous years has been Docker.
In a nutshell, it avoid the it works in my computer moment that we have all experienced.
With Docker, we can encapsulate our R Shiny applications and their dependencies, ensuring consistency and easy portability across various environments.
But I was wondering how to create my own containerized R Shiny App and the R-Stocks projec presented me the perfect opportunity.
Building R Shiny Apps in Docker
- Docker plays a pivotal role in enabling reproducible work by providing a consistent and isolated environment for software development and deployment.
- Dependency Management: Docker allows you to define and package all the dependencies required for your project within a container. By encapsulating the necessary libraries, frameworks, and tools, you ensure that the exact same environment is replicated across different systems, eliminating compatibility issues and ensuring consistent behavior.
- Portability: Docker containers are highly portable, enabling you to run the same software stack on different machines, operating systems, or cloud platforms. This eliminates platform-specific discrepancies and ensures that your work can be replicated regardless of the underlying infrastructure.
- Collaboration: With Docker, you can easily share your project as a self-contained container. This simplifies collaboration by allowing others to quickly spin up the same environment, work on the project, and reproduce your results without the need for complex setup or configuration.
Pre-Requisites
- Have Docker installed
- Have clear which functions/packages your code depends on
- Make sure that the FROM image that you are using contains a R version that supports all the necessary packages.
- For example yfR. as of today needs R with at least version 4.
- Better if you also know the specific versions of the packages that make your code work
R Shiny Apps for X86
Let’s start from something simpler, a regular X86 computer. And for this architecture flavour, I could easily find few ways to create our R Shiny App Docker images:
- Starting from R-Base
- Starting from rocker Shiny Image
- Starting from rocker Tideverse Image
We will use the build command, together with any of the following Dockerfiles:
docker build --no-cache --progress=plain -t r_stocks
#DOCKER_BUILDKIT=1 docker build --no-cache --progress=plain -t r_stocks:V1-amd64 --build-arg ARCH=arm64 .
From R-Base
FROM r-base:latest
LABEL maintainer "JAlcocerT <jalcocert@fossengineer.com>"
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
libssl-dev \
libssh2-1-dev \
&& rm -rf /var/lib/apt/lists/*
# Install remotes package
RUN R -e 'install.packages("remotes")'
RUN R -e 'install.packages(c("shiny", "plotly", "dplyr","viridis", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
# Install specific versions of Shiny and Plotly, along with their dependencies
# RUN R -e 'remotes::install_version("shiny", version = "1.6.0", dependencies = TRUE)'
# RUN R -e 'remotes::install_version("plotly", version = "4.9.3", dependencies = TRUE)'
# Copy app.R to the container
COPY app.R /srv/shiny-server/app.R
# Expose the required port
EXPOSE 3838
# Set the CMD to run the Shiny app
CMD ["R", "-e", "shiny::runApp('/srv/shiny-server/app.R', host = '0.0.0.0', port = 3838)"]
R Shiny App in Docker - with rocker/Shiny Image
FROM rocker/shiny:3.6.1
LABEL maintainer "JAlcocerT <jalcocert@fossengineer.com>"
WORKDIR /srv/shiny-serverRUN apt-get update \
&& apt-get install -y libsasl2-dev libssl-devRUN echo \
'options(repos=list(CRAN="https://cloud.r-project.org/"))' > \
".Rprofile"
RUN R -e 'install.packages(c("shiny", "plotly", "dplyr","viridis", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
ADD https://raw.githubusercontent.com/rocker-org/shiny/master/shiny-server.sh /usr/bin/
COPY ./ ./
EXPOSE 3838
RUN chmod a+w .
RUN chmod +x /usr/bin/shiny-server.sh
CMD /usr/bin/shiny-server.sh
R Shiny App in Docker - with rocker/Tideverse Image
Instead of using rocker/shiny:3.6.1, you can use rocker/tidyverse:3.6.1 and install the shiny package separately.
This will make your app available on port 3838 without the need for Shiny Server!
FROM rocker/tidyverse:4
LABEL maintainer "JAlcocerT <jalcocert@fossengineer.com>"
RUN R -e 'install.packages(c("shiny", "plotly","viridis", "dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
COPY app.R /app.R
EXPOSE 3838
CMD R -e 'shiny::runApp("app.R", port = 3838, host = "0.0.0.0")'
Running & Pushing the Docker Image
Once the build completes, you can run it with:
docker run --name r_stocksshiny -p 3838:3838 --restart unless-stopped -d r_stocks
And push it to DockerHub with (use your username):
docker tag r_stocks docker.io/fossengineer/r_stocks:v1-amd64
#docker login
docker push fossengineer/r_stocks:v1-amd64
R Shiny Apps for ARM86
Any of the tidyverse docker images is available for ARM at this moment. This is a good chance to learn how to install our packages, but starting from an R Base image with ARM.
I have found this one to be working for ARM86. And had to take use of webtops to discover that it was the libxml2-dev the one missing to allow the installation of the yfR package.
FROM r-base:latest
LABEL maintainer "JAlcocerT <jalcocert@fossengineer.com>"
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
libssl-dev \
libssh2-1-dev \
libxml2-dev \
&& rm -rf /var/lib/apt/lists/*
# Install remotes package
RUN R -e 'install.packages("remotes")'
RUN R -e 'install.packages(c("shiny", "plotly","viridis" ,"dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
# Install specific versions of Shiny and Plotly, along with their dependencies
# RUN R -e 'remotes::install_version("shiny", version = "1.6.0", dependencies = TRUE)'
# RUN R -e 'remotes::install_version("plotly", version = "4.9.3", dependencies = TRUE)'
# Copy app.R to the container
COPY app.R /srv/shiny-server/app.R
# Expose the required port
EXPOSE 3838
# Set the CMD to run the Shiny app
CMD ["R", "-e", "shiny::runApp('/srv/shiny-server/app.R', host = '0.0.0.0', port = 3838)"]
To build it, we can use:
#DOCKER_BUILDKIT=0 docker build -t my-image .
#DOCKER_BUILDKIT=1 docker build --no-cache --progress=plain -t r_stocks_arm64 .
DOCKER_BUILDKIT=1 docker build --no-cache --progress=plain -t r_stocks:V1-arm64 .
The learning path
It took me quite a while to get this Dockerfile right.
Specially to make it work in both ARM and x86.
sudo apt update
sudo apt dist-upgrade
sudo apt install r-base
R --version
R
R.Version()
quit()
Remember that you have few ways to install R Packages:
#install.packages('yfR') #ya esta en CRAN
install.packages('yfR', dependencies = TRUE)
sudo apt-get install libxml2-dev
R -e 'install.packages("remotes")'
R -e 'install.packages(c("shiny", "plotly", "dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
R -e 'install.packages("yfR", dependencies = TRUE)'
RStudio was my IDE companion
sudo apt install git
git clone https://github.com/JAlcocerT/R_Stocks ./R_Stocks
cd R_Stocks
####R install, not Rstudio
sudo apt install -y g++ gcc gfortran libreadline-dev libx11-dev libxt-dev \
libpng-dev libjpeg-dev libcairo2-dev xvfb \
libbz2-dev libzstd-dev liblzma-dev libtiff5 \
libssh-dev libgit2-dev libcurl4-openssl-dev \
libblas-dev liblapack-dev libopenblas-base \
zlib1g-dev openjdk-11-jdk \
texinfo texlive texlive-fonts-extra \
screen wget libpcre2-dev make
sudo apt install libedit2 libssl-dev libclang-dev libxkbcommon-x11-0 libsqlite3-0 libpq5 libc6
sudo apt install gdebi
Download and install RStudio:
#lsb_release -a #check whats your release and adapt the web link
sudo apt install wget
wget https://dailies.rstudio.com/rstudio/spotted-wakerobin/desktop/jammy/
sudo apt install ./rstudio*
- And this project from ploner helped me a lot
It turns out that there are different solutions: tidyverse, rbase and shiny images
Using rocker/tidyverse 📌
Instead of using rocker/shiny:3.6.1, you can use rocker/tidyverse:3.6.1 and install the shiny package separately.
This will make your app available on port 3838 without the need for Shiny Server stackoverflow.com. #https://hub.docker.com/r/rocker/tidyverse
FROM rocker/tidyverse:4
LABEL maintainer "Jesus Alcocer <jalcocert@fossengineer.com>"
RUN R -e 'install.packages(c("shiny", "plotly", "dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
COPY app.R /app.R
EXPOSE 3838
CMD R -e 'shiny::runApp("app.R", port = 3838, host = "0.0.0.0")'
Dockerfile build run time ~470s
Using rbase image 📌
Using r-base
docker build -t rstocks_rbase_arm .
docker run --name stocksshiny -p 3838:3838 --detach rstocks_rbase_arm
DOCKER_BUILDKIT=1 docker build --no-cache --progress=plain -t rstocks_rbase_arm .
FROM r-base:latest
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
libssl-dev \
libssh2-1-dev \
libxml2-dev \
&& rm -rf /var/lib/apt/lists/*
# Install remotes package
RUN R -e 'install.packages("remotes")'
RUN R -e 'install.packages(c("shiny", "plotly", "dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
# Install specific versions of Shiny and Plotly, along with their dependencies
# RUN R -e 'remotes::install_version("shiny", version = "1.6.0", dependencies = TRUE)'
# RUN R -e 'remotes::install_version("plotly", version = "4.9.3", dependencies = TRUE)'
# Copy app.R to the container
COPY app.R /srv/shiny-server/app.R
# Expose the required port
EXPOSE 3838
# Set the CMD to run the Shiny app
CMD ["R", "-e", "shiny::runApp('/srv/shiny-server/app.R', host = '0.0.0.0', port = 3838)"]
And finally this worked for x86 and ARM:
FROM r-base:latest
LABEL maintainer "Jesus Alcocer"
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
libssl-dev \
libssh2-1-dev \
libxml2-dev \
&& rm -rf /var/lib/apt/lists/*
# Install remotes package
RUN R -e 'install.packages("remotes")'
RUN R -e 'install.packages(c("shiny", "plotly", "dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
# Install specific versions of Shiny and Plotly, along with their dependencies
# RUN R -e 'remotes::install_version("shiny", version = "1.6.0", dependencies = TRUE)'
# RUN R -e 'remotes::install_version("plotly", version = "4.9.3", dependencies = TRUE)'
# Copy app.R to the container
COPY app.R /srv/shiny-server/app.R
# Expose the required port
EXPOSE 3838
# Set the CMD to run the Shiny app
CMD ["R", "-e", "shiny::runApp('/srv/shiny-server/app.R', host = '0.0.0.0', port = 3838)"]
Dockerfile build time using r-base ~1400s.
DOCKER_BUILDKIT=1 docker build --no-cache --progress=plain -t rstocks_rbase3 .
Thanks to r-bloggers post
FROM r-base:latest
LABEL maintainer="USER <user@example.com>"
RUN apt-get update && apt-get install -y --no-install-recommends \
sudo \
libcurl4-gnutls-dev \
libcairo2-dev \
libxt-dev \
libssl-dev \
libssh2-1-dev \
&& rm -rf /var/lib/apt/lists/*
# Install remotes package
RUN R -e 'install.packages("remotes")'
RUN R -e 'install.packages(c("shiny", "plotly", "dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
# Install specific versions of Shiny and Plotly, along with their dependencies
# RUN R -e 'remotes::install_version("shiny", version = "1.6.0", dependencies = TRUE)'
# RUN R -e 'remotes::install_version("plotly", version = "4.9.3", dependencies = TRUE)'
# Copy app.R to the container
COPY app.R /srv/shiny-server/app.R
# Expose the required port
EXPOSE 3838
# Set the CMD to run the Shiny app
CMD ["R", "-e", "shiny::runApp('/srv/shiny-server/app.R', host = '0.0.0.0', port = 3838)"]
Using Shiny as base 📌
FROM rocker/shiny:3.6.1
LABEL maintainer "Meinhard Ploner <dummy@host.com>"
WORKDIR /srv/shiny-serverRUN apt-get update \
&& apt-get install -y libsasl2-dev libssl-devRUN echo \
'options(repos=list(CRAN="https://cloud.r-project.org/"))' > \
".Rprofile"
RUN R -e 'install.packages(c("shiny", "plotly", "dplyr", "tidyr","lubridate","shinythemes","shinyWidgets","DT","bslib","priceR","quantmod"))'
RUN R -e 'install.packages("yfR", dependencies = TRUE)'
ADD https://raw.githubusercontent.com/rocker-org/shiny/master/shiny-server.sh /usr/bin/
COPY ./ ./
EXPOSE 3838
RUN chmod a+w .
RUN chmod +x /usr/bin/shiny-server.sh
CMD /usr/bin/shiny-server.sh
docker run --name stocksshiny -p 3838:3838 --detach fossengineer/rstocks_shiny
- Dockerfile build 500 segundos