docker-R语言多版本安装,在python中使用R容器执行脚本

EN
EN
2025-02-02 / 0 评论 / 14 阅读 / 正在检测是否收录...

.env

# 新增R版本 4.2.1
R_NAME_421=r_4_2_1  # 容器/镜像 名称
R_VERSION_421=4.2.1 # 版本号
R_PORT_421=8780

# 新增R版本 4.3.0
R_NAME_430=r_4_3_0
R_VERSION_430=4.3.0
R_PORT_430=8781

# 新增R版本 4.4.2
R_NAME_442=r_4_4_2
R_VERSION_442=4.4.2
R_PORT_442=8782

docker-compose.yml

 # R 服务 4.2.1
  r_4_2_1:
    build:
      context: .
      dockerfile: ${R_DOCKERFILE}  # dockerfile公共文件
      args:
        FROM_IMAGE: rocker/r-ver:${R_VERSION_421}   # 指定镜像版本
        R_BIOC_MIRROR: ${R_BIOC_MIRROR}    # 公共 镜像源
        R_CRAN: ${R_CRAN}
    container_name: ${R_NAME_421}   # 指定版本名称
    image: ${R_NAME_421}-image      # 指定镜像名称
    ports:
      - "${R_PORT_421}:${R_PORT_421}"         # 指定端口
    volumes:
      - ./R:/workspace/R
      - ./static/matrix:/workspace/static/matrix
      - .env:/workspace/.env  # 挂载 .env 文件
      #- ./store/docker/r-library/${R_NAME_442}:/usr/local/lib/R/site-library  # 持久化 R 包的安装位置
    environment:
      - R_HOME=/usr/local/lib/R
    command: Rscript R/http/start.R # 启动API服务
    restart: unless-stopped
    networks:
      - backend

  # R 服务 4.3.0
  r_4_3_0:
    build:
      context: .
      dockerfile: ${R_DOCKERFILE}  # dockerfile公共文件
      args:
        FROM_IMAGE: rocker/r-ver:${R_VERSION_430}   # 指定镜像版本
        R_BIOC_MIRROR: ${R_BIOC_MIRROR}    # 公共 镜像源
        R_CRAN: ${R_CRAN}
    container_name: ${R_NAME_430}   # 指定版本名称
    image: ${R_NAME_430}-image      # 指定镜像名称
    ports:
      - "${R_PORT_430}:${R_PORT_430}"         # 指定端口
    volumes:
      - ./R:/workspace/R
      - ./static/matrix:/workspace/static/matrix
      - .env:/workspace/.env  # 挂载 .env 文件
      #- ./store/docker/r-library/${R_NAME_430}:/usr/local/lib/R/site-library  # 持久化 R 包的安装位置
    environment:
      - R_HOME=/usr/local/lib/R
    command: Rscript R/http/start.R # 启动API服务
    restart: unless-stopped
    networks:
      - backend

  # R 服务 4.4.2
  r_4_4_2:
    build:
      context: .
      dockerfile: ${R_DOCKERFILE}  # dockerfile公共文件
      args:
        FROM_IMAGE: rocker/r-ver:${R_VERSION_442}   # 指定镜像版本
        R_BIOC_MIRROR: ${R_BIOC_MIRROR}    # 公共 镜像源
        R_CRAN: ${R_CRAN}
    container_name: ${R_NAME_442}   # 指定版本名称
    image: ${R_NAME_442}-image      # 指定镜像名称
    ports:
      - "${R_PORT_442}:${R_PORT_442}"         # 指定端口
    volumes:
      - ./R:/workspace/R
      - ./static/matrix:/workspace/static/matrix
      - .env:/workspace/.env  # 挂载 .env 文件
      #- ./store/docker/r-library/${R_VERSION_442}:/usr/local/lib/R/site-library  # 持久化 R 包的安装位置
    environment:
      - R_HOME=/usr/local/lib/R
    command: Rscript R/http/start.R # 启动API服务
    restart: unless-stopped
    networks:
      - backend

dockerfile

ARG FROM_IMAGE=rocker/r-ver:4.4.1
FROM ${FROM_IMAGE}

ARG R_BIOC_MIRROR=https://mirrors.tuna.tsinghua.edu.cn/bioconductor
ARG R_CRAN=https://mirrors.tuna.tsinghua.edu.cn/CRAN/

# 设置镜像源为清华大学的 CRAN 镜像和 Bioconductor 镜像
RUN echo "options(BioC_mirror = '${R_BIOC_MIRROR}')" >> /usr/local/lib/R/etc/Rprofile.site
RUN echo "options(repos = c(CRAN = '${R_CRAN}'))" >> /usr/local/lib/R/etc/Rprofile.site

## 安装系统依赖包
RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|https://mirrors.tuna.tsinghua.edu.cn/ubuntu/|' /etc/apt/sources.list && \
    apt-get update && \
    apt-get install -y \
    libcurl4-openssl-dev \
    libssl-dev \
    libxml2-dev \
    libfontconfig1-dev \
    libharfbuzz-dev \
    libfribidi-dev \
    libpango1.0-dev \
    libx11-dev \
    libxt-dev \
    libjpeg-dev \
    libpng-dev \
    libtiff-dev \
    libbz2-dev \
    libsodium-dev \
    && rm -rf /var/lib/apt/lists/* \

# 安装中文字体
RUN apt-get update && apt-get install -y   fonts-wqy-zenhei   fonts-wqy-microhei   ttf-mscorefonts-installer fontconfig
# 查看系统中的中文字体:fc-list :lang=zh
# 报错:dpkg 进程被中断了; 修复:dpkg --configure -a

## 设定工作目录
WORKDIR /workspace

## 常用包安装脚本
COPY R/docker/install_packages.R /workspace/install_packages.R

# 验证文件是否被成功复制
RUN ls -l /workspace/install_packages.R

## 执行(耗时)
RUN Rscript /workspace/install_packages.R


## 设定工作目录
WORKDIR /workspace

#
## 保持容器在后台运行
CMD ["tail", "-f", "/dev/null"]

install_packages.R


# 安装常用的包

install.packages('pheatmap')
install.packages('BiocManager')

options(timeout = 600)

install_missing_packages <- function(packages) {
  if (!requireNamespace("BiocManager", quietly = TRUE)) {
     install.packages("BiocManager")
  }
  for (package in packages) {
    if (!requireNamespace(package, quietly = TRUE)) {
      cat(paste("开始安装", package, "...\n"))
      BiocManager::install(package, ask = FALSE)
    } else {
      cat(paste( packageVersion(package), package, "已安装.\n" ))
    }
  }
}

# 安装常用的包
packages <- c('GEOquery', 'ggplot2', 'readxl', 'readr', 'openxlsx', 'dplyr',
              'clusterProfiler', 'limma', 'DESeq2', 'ggtree', 'org.Hs.eg.db','org.Mm.eg.db','org.Rn.eg.db','GOplot','stringr',
              'enrichplot', 'treeio', 'WGCNA', 'igraph','plumber','jsonlite','dotenv','remotes')

# 安装缺失的 R 包
install_missing_packages(packages)

# 针对某个版本中的某些包进行检测安装
r_version <- getRversion()
cat(paste("检测R版本", r_version , "...\n"))
if(r_version == "4.2.1"){
    if (!requireNamespace("ggtree", quietly = TRUE)) {
      remotes::install_github("YuLab-SMU/ggtree")
    }
    if (!requireNamespace("clusterProfiler", quietly = TRUE)) {
      remotes::install_github("ctlab/fgsea")
      BiocManager::install("clusterProfiler")
    }
}

if(r_version == "4.3.0"){
    if (!requireNamespace("ggtree", quietly = TRUE) && !requireNamespace("limma", quietly = TRUE) && !requireNamespace("org.Hs.eg.db", quietly = TRUE) ) {
      options(BioC_mirror = "https://bioconductor.org")
      install_missing_packages(packages)
    }

    if (!requireNamespace("ggtree", quietly = TRUE)) {
      remotes::install_github("YuLab-SMU/ggtree")
    }
    if (!requireNamespace("clusterProfiler", quietly = TRUE)) {
      remotes::install_github("ctlab/fgsea")
      BiocManager::install("clusterProfiler")
    }
}

如何在Python中使用R容器,执行R脚本?

  1. 搭建好后可以通过接口的方式运行R脚本
  2. 也可以使用py-docker操作容器(线上可能需要挂载docker守护进程)

接口服务

http.R


if (!requireNamespace("plumber", quietly = TRUE)) {
   cat(paste("开始安装", "plumber", "...\n"))
   BiocManager::install("plumber", ask = FALSE)
}
if (!requireNamespace("jsonlite", quietly = TRUE)) {
   cat(paste("开始安装", "jsonlite", "...\n"))
   BiocManager::install("jsonlite", ask = FALSE)
}
if (!requireNamespace("dotenv", quietly = TRUE)) {
   cat(paste("开始安装", "dotenv", "...\n"))
   BiocManager::install("dotenv", ask = FALSE)
}

cat("当前工作目录",getwd(),"\n")

# 加载 dotenv 包
library(dotenv)
library(plumber)

# 读取 .env 文件
load_dot_env(".env")
#print(Sys.getenv())

# 获取 R 版本号
r_version <- getRversion()
cat("R版本:", as.character(r_version), "\n")

# 将 R 版本号转换为 .env 文件中的键名格式   例如:4.2.1 -> 421
r_version_key <- gsub("\\.", "", as.character(r_version))
# 构建端口号的键名
port_key <- paste0("R_PORT_", r_version_key)

# 从 .env 文件中获取端口号
port <- as.integer(Sys.getenv(port_key))
if (is.na(port)) {  # 如果端口号不存在,使用默认端口
  port <- 8006  # 默认端口
  warning("未找到当前R版本的端口配置(",port_key,") 使用默认端口:", port)
}else{
  cat("找到当前 R 版本的端口配置:", port, "\n")
}


# 创建 plumber API
pr <- plumb("R/http/app.R")
# 启动 plumber 服务
pr$run(host = "0.0.0.0", port = port)

app.R

library(jsonlite)


# 定义一个 API 端点,执行 R 脚本
#* 执行 R 脚本
#* @post /run_r_script
function(req, script = "", param = "") {

  # 调用执行脚本的函数
  result <- run_script(script, param)

  return(result)
}


#* 执行系统命令 post
#* @post /run_command
function(req, command = "") {
  if (command == "") {
    return(list(code =  unbox(0),msg = unbox("未提供命令") ))
  }

  # 执行系统命令
  result <- tryCatch({
    output <- system(command, intern = TRUE)
    list(code =  unbox(1), msg = unbox("命令执行成功"), data = output)
  }, error = function(e) {
    list(code =  unbox(0), msg = unbox(e$message) )
  })

  return(result)
}


# 执行 R 脚本的函数
run_script <- function(script_path, params) {

  setwd("/workspace")
  # 检查脚本文件是否存在
  if (!file.exists(script_path)) {
      return( list(
          code = unbox(0),
          msg = unbox("脚本文件不存在"),
          data = getwd()
        ))
  }

  # 将参数转换为 R 对象
  #params <- jsonlite::fromJSON(params)

  # 构造命令行
  command <- paste("Rscript", script_path, shQuote(params))

  # 在独立环境中执行脚本
  result <- tryCatch({
    #run_result <- source(script_path) # 执行结果
    run_result <- system(command, intern = TRUE)

    print(run_result)
    # 获取退出状态
    exit_status <- attr(run_result, "status")
    exit_status <- ifelse(is.null(exit_status), 0, exit_status)
    print(cat("退出状态:",exit_status,"\n"))
    if(exit_status == 1){
        # 将 run_result 转换为字符串
        result_string <- paste(run_result, collapse = "\n")
        #print(result_string)
         list(
          code = unbox(0),  # 使用 unbox() 将 code 转换为标量值
          msg = unbox(result_string),
          data = run_result,  # 捕获的输出
          output = params
        )
    }else{
        #run_result <- capture.output(source(script_path))
        list(
          code = unbox(1),  # 使用 unbox() 将 code 转换为标量值
          msg = unbox("脚本执行成功"),
          data = run_result,  # 捕获的输出
          output = params
        )
    }

  }, error = function(e) {
     print(e)
     list(
      code = unbox(0),
      msg = unbox("脚本执行失败"),
      data = NULL,
      output = unbox(e$message)
    )
  })

  return(result)
}
0

评论 (0)

取消