Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/47 update docker based version stable r #48

Merged
merged 15 commits into from
Aug 14, 2024
2 changes: 1 addition & 1 deletion renv.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
172 changes: 78 additions & 94 deletions version-stable-r-development.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -18,63 +18,71 @@ 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._
riccardoporreca marked this conversation as resolved.
Show resolved Hide resolved


## 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.
<!-- NOTE: Not yet merged into master, to be replaced with https://github.com/miraisolutions/SmaRP/blob/master/Dockerfile -->


## Align local development and deployment environments

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
riccardoporreca marked this conversation as resolved.
Show resolved Hide resolved

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
Expand All @@ -83,38 +91,42 @@ 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
riccardoporreca marked this conversation as resolved.
Show resolved Hide resolved
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
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.
riccardoporreca marked this conversation as resolved.
Show resolved Hide resolved
- 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 \
spoltier marked this conversation as resolved.
Show resolved Hide resolved
-e USERID=$UID \
-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 \
Expand All @@ -126,9 +138,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() {
Expand All @@ -137,109 +149,80 @@ 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 USERID=$UID \
-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)
```


Expand All @@ -249,3 +232,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))