coding
coding

Reputation: 1237

Error at run shiny app in the background with system()?

I found two ways to run a shiny application in the background:

The first one

path_aux = "R -e \"shiny::runApp('inst/app.R', launch.browser = TRUE)\""
system(path_aux, wait = FALSE)

The issue

Seems like this alternative runs a different version of my shiny app, I mean, I have a variable called fileName that before run my app I run this line:

fileName <- "OpenTree"

But when I run

path_aux = "R -e \"shiny::runApp('inst/app.R', launch.browser = TRUE)\""
system(path_aux, wait = FALSE)

My variable fileName have another value, always the same, which I don't know where it is.

Second Alternative

I test with other alternative

rstudioapi::jobRunScript(path = "inst/shiny-run.R")```

shiny-run.R

shiny::runApp(appDir = "inst/app.R",port = 3522)

This alternative works fine

I would like to use the first one because maybe I want to have multiple windows running my app that is a package I just want to know where is this value "fileName" comes from. I don't know if system take a snapshot or something like that.

Thank you

Upvotes: 0

Views: 578

Answers (1)

r2evans
r2evans

Reputation: 160677

Since I recommended it in a comment, I'll formalize it a little here: Docker. In hindsight, this answer turned into much more of a tutorial/howto than intended. I hope it isn't daunting; the steps are actually quite straight-forward, where once you have docker available, you might only need to run something similar to:

$ docker run --rm -d -p 3838:3838                                    \
  -v c:/Users/r2/R/win-library/4.0/shiny/examples/:/srv/shiny-server \
  -v c:/Users/r2/shinylogs/:/var/log/shiny-server/                   \
  -v c:/Users/r2/R/otherlibrary/:/mylibrary/                         \
  rocker/shiny-verse:4.0.3

Very little of the below is R code, it is all in a shell/terminal. I am testing it on win10 using git-bash, but this should work on macos or linux with very little modification.

Table-of-Contents

  1. Install docker if not installed.
  2. Pull the shiny-server image.
  3. Start the shiny-server container (with app-dir mount).
  4. Browse to the running app. Update your apps, changes take effect immediately.
  5. Stop the server (when done for the day).

And then two sections on Logs and some ways to deal with Other Packages.

Content

  1. If not already available, make sure the docker is available:

    $ docker -v
    Docker version 20.10.2, build 2291f61
    

    If not found, then Get Docker is the best resource for installing it.

  2. Pull one of the available images. I'm demonstrating using rocker/shiny-verse which includes shiny and all of the tidyverse dependencies (image size is 1.95GB), but there is also rocker/shiny that is slightly smaller (1.56GB). (The docs are a bit more detailed at the second link.)

    I strongly recommend pulling a specific version instead of :latest (or no version), since you likely need to closely mimic the dev-environment on your laptop. This running container will not use your local R, it has its own. So it is possible that you only have R-3.6 installed on your host computer and use R-4.0.3 within the container. Use the same (major.minor) version you're testing/developing on.

    $ docker pull rocker/shiny-verse:4.0.3
    Using default tag: latest
    latest: Pulling from rocker/shiny-verse
    a4a2a29f9ba4: Already exists
    127c9761dcba: Already exists
    d13bf203e905: Already exists
    4039240d2e0b: Already exists
    fffc4b622efe: Pull complete
    c265253654a5: Pull complete
    e0161c6ad391: Pull complete
    8e7558fa9ec5: Pull complete
    Digest: sha256:ce760db38a4712a581aa6653cf3a6562ddea9b48d781aad4f9582b8056401317
    Status: Downloaded newer image for rocker/shiny-verse:4.0.3
    docker.io/rocker/shiny-verse:4.0.3
    
  3. Determine the (host) directory to "mount". I'm going to use the examples installed with R's shiny package, but you can use anything. There are two options:

    • Single App

      Mount: the app directory itself, e.g., c:/Users/r2/R/win-library/4.0/shiny/examples/01_hello/

      Use: http://localhost:3838/ will run that one app.

    • Directory of Multiple Apps

      Mount: a directory with other app-directories within it, e.g., c:/Users/r2/R/win-library/4.0/shiny/examples/

      Use: http://localhost:3838/ will show the directory of apps, and http://localhost:3838/01_hello/ will run the first app.

    In your case, you might choose something like /path/to/your/package/inst/ and opt for the single-app option from above.

    Per the docs at rocker/shiny, we'll mount this on /srv/shiny-server/.

    $ docker run --rm -d -p 3838:3838 \
      -v c:/Users/r2/R/win-library/4.0/shiny/examples/:/srv/shiny-server \
      rocker/shiny-verse:4.0.3
    ba4654d541ef39fb4882364446ece0b516e518baf946c21d1857565f05acd2c5
    

    (FYI: the -p 3838:3838 is needed to "expose" the server outside of the running shiny server "container". The first number is what is visible to your computer, the second number must remain 3838 and is what the server is actually running internally. If you don't include this, you won't see it.)

  4. Point your browser to http://localhost:3838 (or whatever port you assigned above) and you should see the app (if single-app) or the directory of apps.

  5. When you are done, kill (stop) the container:

    $ docker stop exciting_gagarin
    

Logs

There will come a time when you want/need to look at logs (e.g., warnings/errors). There are two types of logs:

  • server logs, accessible by running

    docker logs -f exciting_gagarin
    

    (Ctrl-C to stop the logs, server keeps running.)

  • app logs (per session). For this, you can jump into the running container and look at the logs, but these logs will be deleted when the container is stopped (because files inside docker containers are by default ephemeral) ... and this can be inconvenient. Instead, you can mount the app-logs to a local directory by including this volume-mount directive to the docker run command:

    -v c:/Users/r2/shinylogs/:/var/log/shiny-server/
    

    When an app is running and a warning/error occurs, you should see files like:

    $ ls -l c:/Users/r2/shinylogs/
    total 5
    -rw-r--r-- 1 r2 197121 112 Jan  8 08:38 shiny-server-shiny-20210108-133524-43293.log
    -rw-r--r-- 1 r2 197121 237 Jan  8 08:53 shiny-server-shiny-20210108-135551-35903.log
    -rw-r--r-- 1 r2 197121 271 Jan  8 08:54 shiny-server-shiny-20210108-135610-44445.log
    -rw-r--r-- 1 r2 197121 271 Jan  8 08:54 shiny-server-shiny-20210108-135612-40369.log
    -rw-r--r-- 1 r2 197121 145 Jan  8 08:57 shiny-server-shiny-20210108-140001-43857.log
    

Other Packages

It is possible that you have packages that are not included in rocker/shiny or rocker/shiny-verse. Fear not! Follow these instructions:

  1. Create a local (not in-container) directory where you will install these missing packages. I produce a separate folder because it is possible that the host OS is different from the in-container OS (debian), in which case packages may not be in the right format. I'll use c:/Users/r2/R/otherlibrary. I will not use this path in the host OS "R" instance.

  2. Add a volume-mount to your command that makes this available inside the container:

    -v c:/Users/r2/R/otherlibrary/:/mylibrary/
    

    If you have a container running, you'll need to docker stop it and then rerun it with this addition.

  3. In your app, add

    .libPaths( c("/mylibrary", .libPaths()) )
    

    Two things about this: (1) it is required for R to look in the alternate directory; and (2) this will do no harm if run on the host and "/mylibrary" does not exist. You can optionally include a conditional of if (dir.exists("/mylibrary"))... if you like.

  4. Install the additional CRAN packages.

    I see many apps that are built-in to install packages as needed, such as

    if (!require("ggrepel")) install.packages("ggrepel")
    

    This isn't wrong (and is the correct use of require versus library), but it isn't my preferred way of doing things.

    An alternative is to install these needed packages manually, which we can do by entering R within the container and install to the appropriate library-path.

    $ docker exec -it exciting_gagarin R
    R version 4.0.3 (2020-10-10) -- "Bunny-Wunnies Freak Out"
    Copyright (C) 2020 The R Foundation for Statistical Computing
    Platform: x86_64-pc-linux-gnu (64-bit)
    
    R is free software and comes with ABSOLUTELY NO WARRANTY.
    You are welcome to redistribute it under certain conditions.
    Type 'license()' or 'licence()' for distribution details.
    
    R is a collaborative project with many contributors.
    Type 'contributors()' for more information and
    'citation()' on how to cite R or R packages in publications.
    
    Type 'demo()' for some demos, 'help()' for on-line help, or
    'help.start()' for an HTML browser interface to help.
    Type 'q()' to quit R.
    
    > .libPaths( c("/mylibrary", .libPaths()) )
    > .libPaths()
    [1] "/mylibrary"                    "/usr/local/lib/R/site-library"
    [3] "/usr/local/lib/R/library"
    > install.packages("ggrepel")
    Installing package into ‘/mylibrary’
    (as ‘lib’ is unspecified)
    trying URL 'https://packagemanager.rstudio.com/all/__linux__/focal/latest/src/contrib/ggrepel_0.9.0.tar.gz'
    Content type 'binary/octet-stream' length 980180 bytes (957 KB)
    ==================================================
    downloaded 957 KB
    
    * installing *binary* package ‘ggrepel’ ...
    * DONE (ggrepel)
    
    The downloaded source packages are in
            ‘/tmp/RtmpgPG8YS/downloaded_packages’
    > q("no")
    
  5. Non-CRAN packages. I realize that you're working on a local package. Both of the referenced images contain the devtools package, so with an extra volume-mount, you can use devtools::install, devtools::load_all, or if you've built the source package already, you can install.packages("/mylibrary/mypackage.tar.gz").

More detail on why it might be necessary to have a different library of packages: compiled code. For instance, when I install ggrepel (one example) on windows, among the files are

ggrepel/libs/i386/ggrepel.dll
ggrepel/libs/x64/ggrepel.dll

which are compiled libraries specific to the Windows OS. When I install the same package on linux, I do not have those libraries, instead I see

ggrepel/libs/ggrepel.so

Why do I say linux? Because these rocker/shiny* images are running linux under the hood ... even if your host OS is windows or macos.

The file formats are incompatible, so renaming doesn't work. Many packages might work out-of-the-box (have not tested), but if you try to mount your host-OS R library path into the container and see errors, think of this discussion.

Notes on docker

(In case you are not familiar.)

  • In general, everything in a running docker container is ephemeral, meaning logs and saved files are gone when the container is stopped. The way around this is to use volume-mounts as we have done above. In this case, if the app saves a file to its own directory, then you will see it in the host OS under /path/to/mypackage/inst/....

  • Depending on your OS configuration, this is visible only on the local host, so either http://localhost:3838 or http://127.0.0.1:3838 (which are not always identical, e.g. windows sometimes). If you need it exposed to other computers on your network ... then I recommend you do a little research on this. Running servers in docker and exposing it to the rest of the network has risks and consequences.

Upvotes: 1

Related Questions