diff --git a/renv.Rmd b/renv.Rmd index f95d36ec..1aec3007 100644 --- a/renv.Rmd +++ b/renv.Rmd @@ -66,7 +66,7 @@ would restore the versions as listed in `renv.lock`, and is the go-to command to By default, development dependencies (like those specified under `Suggests:` in the `DESCRIPTION` file), are not included in `renv.lock`. They can be however included via ```{r, echo = TRUE, eval = FALSE} -renv::snaphot(dev = TRUE) +renv::snapshot(dev = TRUE) ``` Note that, given the default behavior with respect to development dependencies, `renv` might report out-of-sync dependencies upon session startup. Make sure to use diff --git a/version-stable-r-development.Rmd b/version-stable-r-development.Rmd index 8bfeecde..742c125c 100644 --- a/version-stable-r-development.Rmd +++ b/version-stable-r-development.Rmd @@ -6,7 +6,7 @@ knitr::opts_chunk$set(echo = TRUE, collapse = TRUE, eval = FALSE) ``` In the context of productive solutions, it is essential to have full control -over the code-base and environment to ensure reproducibility and stability of +over the codebase and environment to ensure reproducibility and stability of the setup. In the case of R-based projects, this implies fixing and aligning the version of R as well as package and system dependencies. In order to achieve well-managed release pipelines, a key aspect is to guarantee full equivalence of @@ -18,31 +18,39 @@ environment based on containerized solutions leveraging the of multiple dockerized development flavors, to match various target production environments or projects. +> _The instructions in this chapter are for R >= 4.0.0. +Images for R <= 3.6.3 are defined in +[rocker-org/rocker-versioned](https://github.com/rocker-org/rocker-versioned), +but are no longer actively maintained._ + ## Version-stable deployments When deploying R applications (e.g. a Shiny app) using Docker containers, it is -important to control versioning of R and packages for the sake of reproducibilty +important to control versioning of R and packages for the sake of reproducibility and stability of the deployments. For this reason, -[version-stable](https://github.com/rocker-org/rocker-versioned) images are +[version-stable](https://github.com/rocker-org/rocker-versioned2) images are provided as part of the [Rocker project](https://www.rocker-project.org/) and used as a basis for deploying productive applications. Each version-stable Rocker image has an associated _tag_ for all non-latest R -versions (e.g. `rocker/r-ver:3.6.1`). Besides being specific to the +versions (e.g. `rocker/r-ver:4.4.1`). Besides being specific to the corresponding version of R, each tag fixes the version of contributed packages -(by using as package repository the MRAN snapshot of the last day CRAN +(by using as package repository the CRAN snapshot of the last day CRAN distributed that R version as latest release). See -[VERSIONS.md](https://github.com/rocker-org/rocker-versioned/blob/master/VERSIONS.md) +[wiki/Versions](https://github.com/rocker-org/rocker-versioned2/wiki/Versions). +If that R version is the latest, the CRAN date will not be set and the latest packages will always be installed. + The `Dockerfile` of a deployed application then defines a given version-stable image tag to start `FROM`, e.g. ```dockerfile -FROM rocker/r-ver:3.6.1 +FROM rocker/r-ver:4.4.1 ``` See -[SmaRP/Dockerfile](https://github.com/miraisolutions/SmaRP/blob/master/Dockerfile) +[SmaRP/Dockerfile](https://github.com/miraisolutions/SmaRP/blob/feature/upgrade-latest-r/Dockerfile) for an example. + ## Align local development and deployment environments @@ -50,31 +58,31 @@ for an example. When developing and testing an app locally, it is important to ensure the environment is aligned with the target deployment environment. This might imply using e.g. multiple R and package versions for the local development of -different applications, which clash with the typical setup (especially on +different applications. This is not possible with the typical setup (especially on Linux systems), where only one R version (the latest release) exists. The idea is then to rely on the same version-stable rocker containers used for the deployments, using a containerized versioned RStudio instance for the local development. This is available through Rocker's [versioned stack](https://www.rocker-project.org/images/#the-versioned-stack), so we could -use e.g. `rocker/rstudio:3.6.1`. +use e.g. `rocker/rstudio:4.4.1`. Note that the same version-stable instance of RStudio can be used across all different projects for which such version is relevant. For this reason, a -sensible choice is to rely on `rocker/verse`, which adds tidyverse and -devtools to the stack, as well as properly setting up R Markdown system -dependencies TinyTeX and `pandoc`, sparing the effort of the tedious extra -install. See the specific section below about 'TinyTeX considerations'. +sensible choice is to rely on `rocker/verse` images, which add tidyverse and +devtools to the stack. They also include R Markdown system +dependencies TinyTeX and pandoc, sparing the effort of the tedious extra +install. ### Running versioned RStudio instances -Assume we want to run a containerized versioned instance of RStudio for R 3.6.1, +Assume we want to run a containerized versioned instance of RStudio for R 4.4.1, possibly alongside instances for other versions of R. -First of all, we need to get the image from docker-hub +First of all, we need to get the image from docker-hub: ```{bash pull} -docker pull rocker/verse:3.6.1 +docker pull rocker/verse:4.4.1 ``` We then want to have a running instance on `localhost` (`127.0.0.1`), with the @@ -83,38 +91,43 @@ following setup: - No authentication required (local setup). - Enable root by setting the environment variable `ROOT` to `TRUE`, so that e.g. `sudo apt-get` can be used in RStudio. -- Use a version-specific port, e.g. `3500` for R 3.5.0, `3610` for R 3.6.1 and -so on, so that we can use `localhost` for concurrent R version instances. +- Use a version-specific port, e.g. `4000` for R 4.0.0, `4410` for R 4.4.1 and +so on, so that we can use `localhost` for concurrent R version instances. +We bind the port to localhost (`127.0.0.1:4410`), so it is only accessible locally +(see [the Rocker reference](https://rocker-project.org/images/versioned/rstudio.html#disable_auth)). - The development code of all relevant projects should live outside the -container and be shared with it (and possibly many of them), e.g. under -`~/RStudioProjects` on the host machine and `/home/rstudio/RStudioProjects` in +container and be shared with it (and possibly multiple other containers), e.g. under +`~/workspace` on the host machine and `/home/rstudio/workspace` in the container. - For this to work w/o [permission - issues](https://github.com/rocker-org/rocker/wiki/Sharing-files-with-host-machine#avoiding-permission-changes-when-sharing-volumes), - the container user (`rstudio`) must match the UID of the host user (`$UID`). -- In order for the RStudio setting to persist if the container is recreated + issues](https://rocker-project.org/images/versioned/rstudio.html#userid-and-groupid), + the container user (`rstudio`) must match the UID of the host user (`$UID`). + This has the effect of setting the ownership of `~/workspace` on the host machine to `$UID` if it is not already owned by that user. +- In order for the RStudio settings to persist if the container is recreated (e.g. after pulling a new `rocker` image), we also use a shared volume (like -`~/.rstudio-docker/3.6.1`) for the `home/rstudio/.rstudio` directory, which is -version-specific in case of multiple R versions +`~/.rstudio-config/4.4.1`) for the `/home/rstudio/.config/rstudio` directory, which is +version-specific in case of multiple R versions. - If we want to use Meld via the [compareWith](https://github.com/miraisolutions/compareWith/) addins, we need to - - map the `DISPLAY` environment variable and volume `/tmp/.X11-unix` - - add `DISPLAY` to `Renviron` - - install Meld - - install `dbus-x11` + - map the `DISPLAY` environment variable and volume `/tmp/.X11-unix`, + - add `DISPLAY` to `Renviron`, + - install Meld, + - install `dbus-x11`. - Use a version-specific name for the container running the RStudio instance, -e.g. `rstudio_3.6.1`. +e.g. `rstudio_4.4.1`. ```{bash run} -R_VER=3.6.1 -SHARED_DIR=RStudioProjects +R_VER=4.4.1 +SHARED_DIR=workspace +mkdir -p $HOME/.rstudio-config/$R_VER docker run -d --restart=always \ -p 127.0.0.1:$(echo $R_VER | sed 's/[.]//g')0:8787 \ -e DISABLE_AUTH=true \ - -e ROOT=TRUE \ + -e ROOT=true \ -e USERID=$UID \ + -e GROUPID=$GID \ -v $HOME/$SHARED_DIR:/home/rstudio/$SHARED_DIR \ - -v $HOME/.rstudio-docker/$R_VER:/home/rstudio/.rstudio \ + -v $HOME/.rstudio-config/$R_VER:/home/rstudio/.config/rstudio \ -e DISPLAY=$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix:ro \ --name rstudio_$R_VER \ @@ -126,9 +139,9 @@ docker exec rstudio_$R_VER bash -c \ docker exec rstudio_$R_VER bash -c \ 'apt-get update && apt-get install -y --no-install-recommends meld dbus-x11' ``` -The running RStudio can then be accessed by visiting `http://localhost:3610/`. +If you are using `R_VER=4.4.1`, the running RStudio can then be accessed by visiting `http://localhost:4410/`. -You may find convenient to define a shell function as follows +You may find convenient to define a shell function for these steps: ```{bash run_rstudio_ver-def} run_rstudio_ver() { @@ -137,109 +150,81 @@ run_rstudio_ver() { local RVER_IMAGE=${3:-"verse"} local BASE_IMAGE=rocker/$RVER_IMAGE:$R_VER local PORT=$(echo $R_VER | sed 's/[.]//g')0 - local CONTANER_NAME=rstudio_$R_VER + local CONTAINER_NAME=rstudio_$R_VER echo "Containerized version-stable RStudio for R "$R_VER\ "based on image "$BASE_IMAGE\ "with shared volume "$SHARED_DIR docker pull $BASE_IMAGE && + mkdir -p $HOME/.rstudio-config/$R_VER && docker run -d --restart=always \ -p 127.0.0.1:$PORT:8787 \ -e DISABLE_AUTH=true \ - -e ROOT=TRUE \ + -e ROOT=true \ -e USERID=$UID \ + -e GROUPID=$GID \ -v $HOME/$SHARED_DIR:/home/rstudio/$SHARED_DIR \ - -v $HOME/.rstudio-docker/$R_VER:/home/rstudio/.rstudio \ + -v $HOME/.rstudio-config/$R_VER:/home/rstudio/.config/rstudio \ -e DISPLAY=$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix:ro \ - --name $CONTANER_NAME \ + --name $CONTAINER_NAME \ $BASE_IMAGE && # R and RStudio are not getting the DISPLAY environment variable - docker exec $CONTANER_NAME bash -c \ + docker exec $CONTAINER_NAME bash -c \ 'echo "DISPLAY=${DISPLAY}" >> /usr/local/lib/R/etc/Renviron' && # Install Meld - docker exec $CONTANER_NAME bash -c \ + docker exec $CONTAINER_NAME bash -c \ 'apt-get update && apt-get install -y --no-install-recommends meld dbus-x11' && - echo "RStudio running in container "$CONTANER_NAME" on port "$PORT && + echo "RStudio running in container "$CONTAINER_NAME" on port "$PORT && echo "visit http://localhost:"$PORT } ``` -which you can re-use as compact command for any R version as follows +which you can re-use as compact command for any R version: ```{bash run_rstudio_ver-use} -run_rstudio_ver 3.6.1 RStudioProjects +run_rstudio_ver 4.4.1 workspace ``` Note that `--restart=always` specifies that the container should stay up and restart itself after stopping, e.g. upon machine reboot or docker upgrade, so that it is always available. Still, you can explicitly stop the running container with ```{bash stop} -docker stop rstudio_3.6.1 +docker stop rstudio_4.4.1 ``` Alternatively, you can omit `--restart=always` and explicitly start the container whenever needed with ```{bash start} -docker start rstudio_3.6.1 +docker start rstudio_4.4.1 ``` -Note that `start`/`stop` operations do not affect the persistence of files -created outside the shared location, including global RStudio options such as -dark theme, diagnostic, etc. (set via _Tools > Global Options..._). On the other -hand, these files and settings do ~~not~~ (see above) persist removing the -container (`docker rm`, see below) . - +Note that `start`/`stop` operations do not affect the persistence of any files +created in rstudio while the container is running. +However if the container is _removed_, files created outside of mounted volumes +do **not** persist (`docker rm`, see below). +This is why we use a mounted volume for the `~/.config/rstudio` directory. -### TinyTeX considerations +### Using `podman` instead of `docker` -#### `pdfcrop` - -Older `rocker/verse` images might not include `pdfcrop`, which is required for -the default and desirable cropping of PDF figures with R Markdown (see -[rocker-org/rocker-versioned#146](https://github.com/rocker-org/rocker-versioned/issues/146)). -Make sure `pdfcrop` is installed by running at R console -```{r pdfcrop} -tinytex::tlmgr_install("pdfcrop") +`podman` can be used instead of `docker` to run the above commands. In that case, the active user in the container will be _root_, +which is then mapped to the user that invoked the podman command when writing files to the shared volume. +Because of this, RStudio does not set the desired home directory as the initial working directory. To correct this, add ``` -(see [R Markdown: The Definitive -Guide](https://bookdown.org/yihui/rmarkdown/pdf-document.html#figure-options-1)) - -#### Align TinyTeX to current remote repo - -**NOTE** - This should never be needed with recent `rocker/verse` images, where -a version-stable Tex Live repository is used for the TinyTeX install (see -[rocker-org/rocker-versioned#169](https://github.com/rocker-org/rocker-versioned/issues/169)). - -If you are using LaTeX and start seeing errors like -``` -Remote repository is newer than local (2018 < 2019) -``` -it means that you have to re-install TinyTeX. This happens e.g. with -`rocker/verse:3.6.1`, since it was build at the end of 2018 but the current -Tex Live repo is 2019. You can fix this via a **user-specific** re-installation of -TinyTeX for R. **NOTE** however that this will uninstall the system-level -TinyTeX pre-installed in `rocker/verse`. - -First, make sure `/home/rstudio/bin` is part of the `PATH` environment variable. -Check this by running -```{bash check-path} -docker exec --user rstudio rstudio_3.6.1 R --slave -e 'Sys.getenv("PATH")' -``` -If you don't see `/home/rstudio/bin`, you can make sure it is part of the `PATH` for R via -```{bash set-path} -docker exec --user rstudio rstudio_3.6.1 sh -c 'echo "PATH=$HOME/bin:\${PATH}" >> $HOME/.Renviron' -# check again -docker exec --user rstudio rstudio_3.6.1 R --slave -e 'Sys.getenv("PATH")' +echo '{ "initial_working_directory": "/home/rstudio" }' > $HOME/.rstudio-config/$R_VER/rstudio-prefs.json && ``` +After `mkdir -p $HOME/.rstudio-config/$R_VER` in the `run_rstudio_ver` function. +If you run into issues, the discussion in this rocker [pull request](https://github.com/rocker-org/rocker-versioned2/pull/636) +about rootless container support may help. -Then, from the running RStudio, run -```{r reinstall-tinytex} -tinytex::reinstall_tinytex() -``` +### Best-supported R versions +This tutorial uses images based on the [rocker-versioned2](https://github.com/rocker-org/rocker-versioned2) repository. +We recommend using the latest patch version for each minor version - e.g. 4.0.5 for 4.0.x, +as these seem to be the most regularly updated images +(see e.g. [rocker/verse on docker hub](https://hub.docker.com/r/rocker/verse/tags?page=&page_size=&ordering=&name=4.0.)). ### Cleanup ```{bash cleanup} -docker rm $(docker stop rstudio_3.6.1) +docker rm $(docker stop rstudio_4.4.1) ``` @@ -249,3 +234,4 @@ docker rm $(docker stop rstudio_3.6.1) - [Shared Volumes](https://www.rocker-project.org/use/shared_volumes/) - [Rocker Wiki](https://github.com/rocker-org/rocker/wiki) - [Sharing files with host machine](https://github.com/rocker-org/rocker/wiki/Sharing-files-with-host-machine) +- [Rocker reference for verse and other images](https://rocker-project.org/images/versioned/rstudio.html) (in particular [how to use](https://rocker-project.org/images/versioned/rstudio.html#how-to-use)) \ No newline at end of file