R Shiny Apps in a Docker Container

R Shiny Apps in a Docker Container

November 27, 2022

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:

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*

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
-->