R con Apptainer

R es uno de los entornos más utilizados para análisis estadístico, filogenética, transcriptómica, visualización de datos y bioinformática. En BEAGLE, R debe utilizarse dentro de contenedores con Apptainer, de la misma forma que cualquier otro software especializado.

La ventaja de usar R dentro de un contenedor es que podemos mantener una versión definida de:

sin modificar el sistema operativo del clúster.

En esta sección se muestran distintas estrategias para trabajar con R y mantener actualizado un contenedor.


Imagen base de R

Podemos partir de una imagen existente de R y convertirla a formato .sif.

Por ejemplo:

apptainer pull docker://r-base:4.6.1

Esto generará una imagen similar a:

r-base_4.6.1.sif

Podemos comprobar la versión de R:

apptainer exec r-base_4.6.1.sif R --version

También podemos revisar qué distribución Linux utiliza la imagen:

apptainer exec r-base_4.6.1.sif cat /etc/os-release

Por ejemplo, una imagen puede mostrar:

PRETTY_NAME="Debian GNU/Linux forky/sid"
NAME="Debian GNU/Linux"
VERSION_CODENAME=forky
ID=debian

Esto es importante porque las dependencias del sistema deben instalarse utilizando el gestor de paquetes correspondiente.

En Debian y Ubuntu se utiliza:

apt

Ejecutar R dentro del contenedor

Para abrir una sesión interactiva:

apptainer shell r-base_4.6.1.sif

Dentro del contenedor:

R

También podemos ejecutar directamente un script:

apptainer exec r-base_4.6.1.sif Rscript analisis.R

O ejecutar una instrucción sencilla:

apptainer exec r-base_4.6.1.sif \
    Rscript -e 'sessionInfo()'

¿Dónde instalar los paquetes de R?

Una imagen .sif está diseñada para ser inmutable.

Esto significa que normalmente no podemos hacer:

install.packages("ape")

y esperar que el paquete quede guardado permanentemente dentro del .sif.

Existen varias estrategias para mantener paquetes adicionales:

  1. montar una biblioteca externa con --bind
  2. utilizar un overlay persistente
  3. convertir temporalmente el .sif a un sandbox
  4. reconstruir el .sif después de instalar nuevos paquetes

Cada estrategia tiene ventajas diferentes.


Opción 1: biblioteca de R externa con --bind

Una estrategia sencilla consiste en guardar los paquetes de R fuera del contenedor.

Primero creamos una carpeta:

mkdir -p ~/R/library

Después la montamos dentro del contenedor:

apptainer exec \
    --bind ~/R/library:/R-library \
    r-base_4.6.1.sif \
    Rscript -e '
        .libPaths(c("/R-library", .libPaths()))
        install.packages(
            "ape",
            repos="https://cloud.r-project.org"
        )
    '

El paquete queda almacenado físicamente en:

~/R/library

y no dentro del .sif.

Para usarlo posteriormente:

apptainer exec \
    --bind ~/R/library:/R-library \
    r-base_4.6.1.sif \
    Rscript -e '
        .libPaths(c("/R-library", .libPaths()))
        library(ape)
        packageVersion("ape")
    '

Ventajas

  • no es necesario reconstruir la imagen
  • los paquetes se pueden actualizar rápidamente
  • cada proyecto puede tener su propia biblioteca
  • funciona bien para paquetes que solo dependen de R

Limitación

Algunos paquetes necesitan bibliotecas adicionales del sistema Linux.

Por ejemplo:

libcurl
libxml2
OpenSSL
fontconfig
freetype
harfbuzz

Estas dependencias no se pueden instalar simplemente dentro de una carpeta de paquetes de R.

En esos casos es necesario modificar la imagen base.


Opción 2: overlay persistente

Un overlay es una capa escribible que se coloca encima de una imagen .sif.

Podemos pensar en:

r-base_4.6.1.sif
        +
R-overlay.img
        =
contenedor modificable

La imagen original permanece sin cambios y todas las modificaciones se almacenan en el overlay.

Crear un overlay

Por ejemplo, para crear un overlay de 10 GB:

apptainer overlay create \
    --size 10240 \
    R-overlay.img

Después podemos entrar al contenedor usando el overlay:

apptainer shell \
    --fakeroot \
    --overlay R-overlay.img \
    r-base_4.6.1.sif

Dentro del contenedor podemos instalar paquetes:

install.packages(
    "ape",
    repos="https://cloud.r-project.org"
)

Al salir:

exit

los cambios permanecen dentro de:

R-overlay.img

Para volver a utilizar el mismo entorno:

apptainer exec \
    --overlay R-overlay.img \
    r-base_4.6.1.sif \
    Rscript analisis.R

Ventajas

  • no se modifica el .sif
  • los cambios son persistentes
  • permite probar paquetes antes de reconstruir una imagen
  • útil durante desarrollo

Limitaciones

El entorno queda dividido en dos archivos:

r-base_4.6.1.sif
R-overlay.img

Por lo tanto, si queremos mover el entorno a otro servidor debemos copiar ambos archivos.


Opción 3: usar un sandbox

Un sandbox convierte el contenedor en una carpeta editable.

Partimos de:

r-base_4.6.1.sif

y creamos:

apptainer build \
    --fakeroot \
    --sandbox \
    r-base_4.6.1_sandbox \
    r-base_4.6.1.sif

Ahora podemos entrar con:

apptainer shell \
    --fakeroot \
    --writable \
    --pwd / \
    r-base_4.6.1_sandbox

Dentro podemos instalar paquetes del sistema:

apt update
apt install -y libxml2-dev

y también paquetes de R:

R
install.packages(
    "xml2",
    repos="https://cloud.r-project.org"
)

Cuando terminemos:

exit

podemos reconstruir una nueva imagen:

apptainer build \
    --fakeroot \
    r-base_4.6.1.sif \
    r-base_4.6.1_sandbox

El resultado vuelve a ser un único archivo:

r-base_4.6.1.sif

Paquetes de R y dependencias del sistema

No todos los paquetes de R están formados únicamente por código R.

Muchos paquetes contienen código:

  • C
  • C++
  • Fortran

o necesitan bibliotecas externas proporcionadas por Linux.

Por ejemplo, al instalar:

install.packages("tidyverse")

R también instala numerosas dependencias:

curl
openssl
xml2
systemfonts
textshaping
ragg
readr
readxl
rvest
ggplot2
dplyr
tidyr
...

Algunas de ellas necesitan bibliotecas del sistema operativo.

Ejemplo: tidyverse

Una instalación de tidyverse puede fallar aunque R funcione correctamente.

Por ejemplo:

ERROR: configuration failed for package 'curl'
ERROR: configuration failed for package 'xml2'
ERROR: configuration failed for package 'openssl'

Esto normalmente indica que faltan bibliotecas de desarrollo del sistema.

En Debian pueden instalarse dependencias como:

apt install -y \
    libcurl4-openssl-dev \
    libuv1-dev \
    libxml2-dev \
    libssl-dev \
    libfontconfig1-dev \
    libfreetype-dev \
    libharfbuzz-dev \
    libfribidi-dev \
    libpng-dev \
    libtiff-dev \
    libjpeg-dev

Después podemos volver a instalar:

install.packages(
    "tidyverse",
    repos="https://cloud.r-project.org"
)

¿Por qué ocurre esto?

R descarga paquetes desde CRAN, pero algunas extensiones deben compilarse contra bibliotecas del sistema.

Por ejemplo:

Paquete de R Dependencia del sistema
curl libcurl4-openssl-dev
xml2 libxml2-dev
openssl libssl-dev
systemfonts libfontconfig1-dev, libfreetype-dev
textshaping libharfbuzz-dev, libfribidi-dev
ragg bibliotecas gráficas como PNG, TIFF y JPEG

Por esta razón, para mantener una imagen de R completa es útil poder instalar tanto paquetes de R como bibliotecas del sistema.


Estrategia recomendada para mantener una imagen de R

Para pruebas rápidas puede utilizarse:

bind

o:

overlay

pero cuando queremos mantener una imagen R que pueda distribuirse y utilizarse directamente en BEAGLE, es más conveniente generar nuevamente el .sif.

El flujo recomendado es:

r-base_4.6.1.sif
        |
        v
sandbox temporal
        |
        +--> apt install ...
        |
        +--> install.packages(...)
        |
        v
nuevo r-base_4.6.1.sif

Esto puede automatizarse con el script:

update_r_sif.sh

Script update_r_sif.sh

El script permite actualizar una imagen existente utilizando fakeroot.

Puede instalar:

  • paquetes del sistema Debian con --apt
  • paquetes CRAN con --r
  • paquetes Bioconductor con --bioc

y después reconstruye automáticamente el .sif.

Descargar update_r_sif.sh

Mostrar ayuda

./update_r_sif.sh --help

La sintaxis general es:

./update_r_sif.sh \
    -i IMAGEN.sif \
    [opciones]

Las opciones principales son:

-i, --image FILE
    Imagen SIF que se desea actualizar.

--apt PKG...
    Instala paquetes del sistema Debian.

--r PKG...
    Instala paquetes CRAN.

--bioc PKG...
    Instala paquetes Bioconductor.

-h, --help
    Muestra la ayuda.

Instalar un paquete sencillo de R

Por ejemplo:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --r ape

El script:

  1. convierte temporalmente el .sif a sandbox
  2. instala ape
  3. comprueba que el paquete quedó instalado
  4. construye un nuevo .sif
  5. conserva la imagen anterior como respaldo
  6. deja la nueva imagen con el mismo nombre

Por ejemplo:

r-base_4.6.1_20260922_101500.sif
r-base_4.6.1.sif

La imagen:

r-base_4.6.1.sif

siempre corresponde a la versión más reciente.


Volver a actualizar la misma imagen

Después podemos utilizar nuevamente exactamente el mismo archivo.

Por ejemplo:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --r ape phytools

Posteriormente:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --bioc DESeq2

Cada actualización parte del .sif más reciente.

El proceso puede verse como:

R base
  |
  +-- tidyverse
  |
  +-- ape
  |
  +-- phytools
  |
  +-- DESeq2
  |
  v
r-base_4.6.1.sif

Ejemplo: instalar tidyverse

tidyverse es un buen ejemplo porque necesita tanto paquetes de R como bibliotecas adicionales del sistema Linux.

No basta necesariamente con ejecutar:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --r tidyverse

Si faltan dependencias del sistema, la instalación puede detenerse.

Por esta razón podemos instalar las dependencias Debian y tidyverse en una sola actualización:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --apt \
        libcurl4-openssl-dev \
        libuv1-dev \
        libxml2-dev \
        libssl-dev \
        libfontconfig1-dev \
        libfreetype-dev \
        libharfbuzz-dev \
        libfribidi-dev \
        libpng-dev \
        libtiff-dev \
        libjpeg-dev \
    --r tidyverse

El script primero instala las bibliotecas del sistema:

Debian / apt

y posteriormente ejecuta:

install.packages("tidyverse")

Finalmente reconstruye:

r-base_4.6.1.sif

Ejemplo: instalar terra

terra es un paquete de R utilizado para trabajar con datos espaciales y ráster. A diferencia de paquetes formados únicamente por código R, terra depende de varias bibliotecas geoespaciales instaladas en el sistema Linux.

Si intentamos instalar directamente:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --r terra

la instalación puede fallar si la imagen no contiene las bibliotecas necesarias.

Entre las dependencias del sistema más importantes se encuentran:

GDAL
GEOS
PROJ
SQLite

En una imagen basada en Debian, estas dependencias pueden instalarse con:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --apt \
        libgdal-dev \
        libgeos-dev \
        libproj-dev \
        libsqlite3-dev \
    --r terra

El script primero instala las bibliotecas del sistema mediante:

apt

y posteriormente ejecuta:

install.packages("terra")

Durante la instalación, terra detecta las bibliotecas geoespaciales disponibles dentro del contenedor y compila el paquete contra ellas.

Finalmente, el script reconstruye la imagen:

r-base_4.6.1.sif

que ahora contiene tanto las dependencias del sistema como el paquete terra.

Podemos comprobar la instalación con:

apptainer exec \
    r-base_4.6.1.sif \
    Rscript -e '
        library(terra)
        packageVersion("terra")
    '

También podemos revisar qué versiones de las bibliotecas geoespaciales fueron detectadas:

apptainer exec \
    r-base_4.6.1.sif \
    Rscript -e '
        library(terra)
        terra::gdal()
        terra::projInfo()
    '

Este ejemplo muestra por qué algunos paquetes de R no pueden instalarse únicamente con install.packages(): parte de sus dependencias pertenece al sistema operativo y debe estar disponible dentro de la imagen Apptainer antes de compilar el paquete.


Instalar varios paquetes de R

Podemos instalar varios paquetes en la misma actualización:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --r \
        ape \
        phytools \
        geiger \
        tidyverse

Instalar paquetes Bioconductor

El script también puede utilizar BiocManager.

Por ejemplo:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --bioc \
        DESeq2 \
        edgeR \
        Biostrings

También podemos combinar CRAN y Bioconductor:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --r \
        tidyverse \
        ggplot2 \
    --bioc \
        DESeq2 \
        Biostrings

Instalar solamente dependencias del sistema

También podemos actualizar únicamente el sistema base del contenedor:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --apt \
        libxml2-dev \
        libssl-dev \
        libcurl4-openssl-dev

Después podemos volver a utilizar la imagen actualizada para instalar paquetes de R:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --r xml2 curl openssl

Sin embargo, cuando conocemos todas las dependencias, es más eficiente instalarlas en una sola ejecución:

./update_r_sif.sh \
    -i r-base_4.6.1.sif \
    --apt \
        libxml2-dev \
        libssl-dev \
        libcurl4-openssl-dev \
    --r \
        xml2 \
        curl \
        openssl

Así el .sif solamente necesita reconstruirse una vez.


fakeroot

El script utiliza:

--fakeroot

en lugar de sudo.

Esto permite realizar operaciones que normalmente requieren privilegios de administrador dentro del contenedor, sin ser root real en el sistema anfitrión.

Podemos comprobar que fakeroot funciona:

apptainer exec \
    --fakeroot \
    r-base_4.6.1.sif \
    id

El resultado debería mostrar algo similar a:

uid=0(root) gid=0(root)

Esto no significa que tengamos acceso root al servidor.

Los privilegios existen únicamente dentro del espacio de usuario utilizado por Apptainer.


Verificar paquetes instalados

Podemos comprobar que un paquete existe:

apptainer exec \
    r-base_4.6.1.sif \
    Rscript -e '
        library(tidyverse)
        packageVersion("tidyverse")
    '

También podemos listar todos los paquetes instalados:

apptainer exec \
    r-base_4.6.1.sif \
    Rscript -e '
        x <- installed.packages()[, c("Package", "Version")]
        print(x, row.names=FALSE)
    '

Recomendación práctica

Para trabajar con R pueden utilizarse distintas estrategias:

Estrategia Uso recomendado
Biblioteca externa con --bind paquetes de R por proyecto
Overlay pruebas y desarrollo
Sandbox manual modificar y depurar una imagen
update_r_sif.sh mantener una imagen R reutilizable

Para una imagen destinada a utilizarse repetidamente en BEAGLE, la opción recomendada es:

update_r_sif.sh

porque permite mantener en un único .sif:

Debian
  |
  +-- R
  |
  +-- bibliotecas del sistema
  |
  +-- paquetes CRAN
  |
  +-- paquetes Bioconductor
  |
  v
r-base_4.6.1.sif

De esta forma podemos actualizar progresivamente el entorno sin modificar el sistema operativo de BEAGLE y conservar una imagen portable y reproducible.