Deploying Apps
Deploying apps
Although ShinyProxy is ideal for hosting Shiny apps, it supports many other web applications, see the demos page for a number of examples. Regardless of the type of the app, the deployment process is the same:
Create or find a Docker image
In order to run apps on ShinyProxy, the app must be packaged into a Docker
image. For most apps, you’ll have to create your own image, containing the code
of your app. These images are created by writing a Dockerfile
. The
contents of the Dockerfile
depends on the technology used. In order to get
started, we provide demos for many different technologies. If you
just want to give ShinyProxy a try, you can use an existing Docker image
provided by the demos.
Give ShinyProxy access to the Docker image
In order for ShinyProxy to be able to use the Docker image, it must have access to the Docker image. The easiest way to get started, is to build your Docker image on the same machine as where you are running ShinyProxy. This way, ShinyProxy immediately has access to the image. Building an image is explained below or in the demo apps.
Once you start hosting more apps in ShinyProxy, it’s better to store your images in a Docker Registry. This allows you to centrally store your images and push images into the registry from different machines. You can use any Docker Registry with ShinyProxy, you’ll probably be already using a provider that offers a registry, such as:
- Harbor
- Docker Hub
- Quay.io
- GitHub Container Registry
- GitLab
- AWS ECR
- Azure ACR
- Google Artifact Registry
ShinyProxy will automatically pull the image from the registry if it’s not present on the server. Many registries require authentication, in this case the credentials must be added to ShinyProxy.
Configure ShinyProxy
In order for your app to show up in ShinyProxy, you must add it to the
ShinyProxy configuration. The configuration of ShinyProxy happens in a single
file application.yml
. The location of
this file depends on the way ShinyProxy is deployed. For each app you
must add an app spec to the proxy.specs
property. ShinyProxy
supports many configuration options, but only a few
properties are required for making your app work:
id
: the identifier of the app, this is used in the URL of the appcontainer-image
: name of the Docker image to useport
: the port on which the app is listening in the container
For example, to configure a Shiny app:
proxy:
specs:
- id: euler
container-image: openanalytics/shinyproxy-demo-minimal
Note: by default ShinyProxy assumes that the app is using port 3838
, which
is the port used by Shiny. For apps not using port 3838
, you’ll have
to add the port
property.
Restart ShinyProxy
Every time you make a change to the ShinyProxy configuration, you’ll have to
restart ShinyProxy. If you are manually running the JAR from a terminal, you can
stop it by pressing Ctrl+c
. If you installed ShinyProxy using
the deb or rpm
packages, you can use sudo systemctl restart shinyproxy
. Finally, when running
ShinyProxy in Docker,
you have to build a new image, stop the existing container and start a new
Docker container.
Note
The ShinyProxy Operator (supported on both pure Docker hosts and Kubernetes) allows to update the ShinyProxy config without downtime.Deploying a Shiny app
This section uses the steps explained in the previous step to deploy a Shiny app.
Create a Docker image
Docker images are built starting from a Dockerfile
. The Dockerfile
starts from a
preexisting image and builds up the image command by command.
In order to simplify writing Dockerfiles
for your Shiny apps, Open Analytics
made a template available in
the shinyproxy-shiny-demo-minimal
repository, that shows how an app can typically be prepared for deployment on
ShinyProxy. The app we will use in the example is named ’euler’ and allows to
compute Euler’s number using arbitrary precision. In the repository the R code
lives in the eler/app.R
file.
In order to make these very precise computations the app uses the
Rmpfr
package for multiple
precision computing
The following is a minimal version of the Dockerfile
for this app:
FROM openanalytics/r-ver:4.3.3
RUN echo "\noptions(shiny.port=3838, shiny.host='0.0.0.0')" >> /usr/local/lib/R/etc/Rprofile.site
# system libraries of general use
RUN apt-get update && apt-get install --no-install-recommends -y \
pandoc \
pandoc-citeproc \
libcairo2-dev \
libxt-dev \
&& rm -rf /var/lib/apt/lists/*
# system library dependency for the euler app
RUN apt-get update && apt-get install --no-install-recommends -y \
libmpfr-dev \
&& rm -rf /var/lib/apt/lists/*
# basic shiny functionality
RUN R -q -e "options(warn=2); install.packages(c('shiny'))"
# install dependencies of the euler app
RUN R -q -e "options(warn=2); install.packages('Rmpfr')"
# install R code
COPY euler /app
WORKDIR /app
EXPOSE 3838
CMD ["R", "-q", "-e", "shiny::runApp('/app')"]
Let’s walk through this Dockerfile
step by step:
- The first line of the
Dockerfile
simply indicates that the image starts from a pre-built imageopenanalytics/r-ver:4.3.3
which provides Ubuntu LTS with R version 4.3.3. This image is available on Docker hub. - The first
RUN
command configures R to ensure Shiny always uses port3838
, instead of choosing a random port. - Next, a number of additional system packages are installed from the Ubuntu
repository using
apt-get install
. The ones listed are of general use, but for your app additional system libraries may be required and can be installed in this way. For clarity we’ve included the installation of an extra system library in a separate block: the R packageRmpfr
requires the system librarylibmpfr-dev
to be available - Then, there is a
RUN
command to install the Shiny R package, that needs to be present for Shiny to be functional at all. - The next step is to install any R dependencies your specific app may
have - in this case the
Rmpfr
package for multiple-precision computing. - Once all dependencies are present, we can copy our euler app (existing of
the
app.R
file) into the image. EXPOSE 3838
instructs Docker to expose port 3838 to the outside world- The
CMD
statement, finally, instructs how to launch the Shiny app when the container is started.
In order to create an image from this Dockerfile
, it must be built. Navigate
into the directory that contains the Dockerfile
. In our example this is the
root folder of
our shinyproxy-shiny-demo-minimal
Git repository. Then, launch the following command:
sudo docker build -t shinyproxy-shiny-demo-minimal .
Give ShinyProxy access to the Docker image
For this example, we assume that you build the Docker image on the same machine as where you are running ShinyProxy. Therefore, the image is automatically available to ShinyProxy.
Configure ShinyProxy
In order to use the app, add the following configuration:
proxy:
specs:
- id: euler
container-image: shinyproxy-demo-minimal
Restart ShinyProxy
See the above section.
Updating an app
Once you have deployed an app, you might want to update the app. To update the
configuration (i.e. the application.yml
file) of an app, it’s always
required to restart ShinyProxy.
In order to update the container image of an app, a restart isn’t required if all of the following is true:
-
the
image-pull-policy
( for Docker and Kubernetes) is set toAlways
(this isn’t the default setting). For example:proxy: docker: image-pull-policy: Always kubernetes: image-pull-policy: Always
-
the updated image has the exact same name and tag (i.e. you must push the new version of the image using the exact same name and tag)
-
the app isn’t using pre-initialization or container-sharing
In all other cases it’s required to update the configuration of the app. For
example, if the image tag has changed, the container-image
property of the app
must be updated to use this new value.
In order to force an update of an app when using pre-initialization or container-sharing, you can add a dummy value to the config of the app. For example:
proxy:
# ...
specs:
- id: 01_hello
container-image: openanalytics/shinyproxy-demo
minimum-seats-available:
labels:
force-update: 123
Every time you update your image, you increase the force-update
value and
restart ShinyProxy. On startup, ShinyProxy notices that the configuration has
changed and starts replacing the pool of containers.