The pracpac
package enables developers to easily incorporate R packages into Docker images. What follows is a reproducible demonstration of select pracpac
use cases. Note that this vignette is by no means exhaustive, and the pattern of delivering an R package within a Docker image1 may prove useful in other scenarios as well.
The “pipeline” use case describes a scenario where a developer may want to use functions from a custom R package to perform processing on the input and/or output of a domain-specific tool. There are countless software packages that will not be implemented directly in R. Developers who want to leverage these tools in a reproducible context may choose to do so by using Docker. If the tool(s) in the workflow require(s) upstream or downstream processing that is best suited to R code, then the developer could write an R package and distribute everything together in Docker. Besides defining reproducible dependencies, Docker allows the developer to pass “instructions” for how the container should behave. These can include scripts to run when the container is launched.
To demonstrate this use case we use the hellow
R package source code that ships with pracpac
. We write a pipeline to use the isay()
function from hellow
to randomly select a flavor of “Hello”. The output is piped to a command-line tool called translate-shell
2 that translates the text to another language specified by the user (by default French). When the container runs, the pipeline is executed and outputs the translated results.
We will first move the example hellow
package to temporary location:
library(pracpac)
library(fs)
## specify the temp directory
<- tempdir()
tmp ## create a subdirectory of temp called "example"
dir_create(path = path(tmp, "example"))
## copy the example hellow package to the temp directory
dir_copy(path = system.file("hellow", package = "pracpac"), new_path = path(tmp, "example"))
The new directory includes the R package source contents of hellow
:
dir_tree(path(tmp, "example", "hellow"), recurse = TRUE)
├── DESCRIPTION
├── NAMESPACE
├── R
│ └── hello.R
└── man
└── isay.Rd
We can use use_docker(..., use_case="pipeline")
to create the template of files for building the Docker image:
use_docker(pkg_path = path(tmp, "example", "hellow"), use_case = "pipeline")
Using renv. Dockerfile will build from renv.lock in /tmp/RtmpsMexB6/example/hellow/docker.
Using template for the specified use case: pipeline
Writing dockerfile: /tmp/RtmpsMexB6/example/hellow/docker/Dockerfile
The directory will be created at /tmp/RtmpsMexB6/example/hellow/docker/assets
Assets for the specified use case (pipeline) will be copied there.
The specified use case (pipeline) includes the following asset: run.sh
The specified use case (pipeline) includes the following asset: pre.R
The specified use case (pipeline) includes the following asset: post.R
Building package hellow version 0.1.0 in /tmp/RtmpsMexB6/example/hellow/hellow_0.1.0.tar.gz
The directory now contains the docker/
subdirectory, which has another subdirectory called assets/
for the templated pipeline scripts:
dir_tree(path(tmp, "example", "hellow"), recurse = TRUE)
├── DESCRIPTION
├── NAMESPACE
├── R
│ └── hello.R
├── docker
│ ├── Dockerfile
│ ├── assets
│ │ ├── post.R
│ │ ├── pre.R
│ │ └── run.sh
│ ├── hellow_0.1.0.tar.gz
│ └── renv.lock
└── man
└── isay.Rd
The files need to be edited as follows:
Dockerfile
FROM rocker/r-ver:latest
## copy the renv.lock into the image
COPY renv.lock /renv.lock
## install renv
RUN Rscript -e 'install.packages(c("renv"))'
## set the renv path var to the renv lib
ENV RENV_PATHS_LIBRARY renv/library
## restore packages from renv.lock
RUN Rscript -e 'renv::restore(lockfile = "/renv.lock", repos = NULL)'
## copy in built R package
COPY hellow_0.1.0.tar.gz /hellow_0.1.0.tar.gz
## run script to install built R package from source
RUN Rscript -e 'install.packages("/hellow_0.1.0.tar.gz", type="source", repos=NULL)'
## COPY in the pre processing R script and run shell script
COPY assets/pre.R /pre.R
COPY assets/run.sh /run.sh
## add the translate-shell tool and dependencies
RUN apt-get update && apt-get install -y bsdmainutils translate-shell
## enter at run shell script
## allows for parameters passed to container at runtime
ENTRYPOINT ["bash", "/run.sh"]
run.sh
#!/bin/bash
## get tranlsation language from first argument
## default to :fr
TOLANG="${1:-:fr}"
HELLO=$(Rscript /pre.R)
trans "$HELLO" "$TOLANG"
pre.R
library(hellow)
isay()
Note that in this case the docker/assets/post.R
template is not necessary, so we can delete it:
file_delete(path(tmp, "example", "hellow", "docker", "assets", "post.R"))
With the template files created and edited as described above, we can build the image:
build_image(pkg_path = path(tmp, "example", "hellow"))
In this case, the image will be built with build_image()
default behavior of tagging with the package name and version:
system("docker images")
hellow 0.1.0 e1a9bc2ebbb5 15 seconds ago 959MB
hellow latest e1a9bc2ebbb5 15 seconds ago 959MB
Now we can run the container, either from a Docker client directly on the host or from within R:
system("docker run --rm hellow:latest")
You are peachy!
[1] "Hello"
[1] "Bonjour"
Translations of [1] "Hello"
[ English -> Français ]
[1] "Hello"
[1] "Bonjour", [1] "Salut"
system("docker run --rm hellow:latest :es")
You are groundbreaking!
[1] "What's up?"
[1] "¿Qué pasa?"
Translations of [1] "What's up?"
[ English -> Español ]
[1] "What's up?"
[1] "¿Qué pasa?", [1] "¿Qué hay de nuevo?"
Note that we discuss Docker terminology in the “Basic Usage” vignette: vignette("basic-usage", package = "pracpac")
↩︎