Как сохранить рабочее пространство R в нескольких инструкциях CMD в Dockerfile? - PullRequest
1 голос
/ 28 мая 2020

Я создаю контейнер docker для приема входных данных от клиента, а затем отправляю сценарий R, который выполняет анализ данных и выводит три графика в виде PDF-файлов в рабочий каталог. Я столкнулся с проблемой необходимости двух операторов CMD в файле Dockerfile, что не разрешено движком Docker. Мне нужно, чтобы чтение данных пользователя происходило во время выполнения, чтобы набор данных мог быть изменен в зависимости от пользователя. Затем необходимо получить исходный код сценария R после того, как данные пользователя были считаны в виде объекта таблицы в рабочем пространстве R. Что происходит, данные читаются нормально, но сообщение об ошибке указывает на то, что вторая строка CMD для источника сценария R не может найти данные, которые только что были прочитаны. Я понимаю, что это потому, что каждая строка файла Docker выполняется отдельно при сборке, но я не знаю, как это исправить. Я исследовал объемы, использование супервизора и возможность использования нескольких контейнеров. Я также мог бы создать сценарий Python для динамического программирования моего сценария R, но для этого могут потребоваться две строки CMD. Мне еще предстоит найти конкретный пример c, достаточный для моей ситуации. Как бы кто-нибудь из вас решил это?

Вот мой сценарий R для создания 3 графиков из фрейма данных 'x':

library(iq)
norm_data <- iq::preprocess(x, median_normalization = FALSE, pdf_out = NULL)
protein_list <- iq::create_protein_list(norm_data)

# basic protein plot
pdf(file = "Protein P00366.pdf")
iq::plot_protein(protein_list$P00366, main = "Protein P00366", split = NULL)
dev.off()

protein_table <- iq::create_protein_table(protein_list)

#MaxLFQ plot
pdf(file = "MaxLFQ quantification of P00366.pdf")
iq::plot_protein(rbind(protein_list$P00366, 
                       MaxLFQ = iq::maxLFQ(protein_list$P00366)$estimate), 
                 main = "MaxLFQ quantification of P00366", 
                 col = c(rep("gray", nrow(protein_list$P00366)), "green"), 
                 split = NULL)  
dev.off()

# ground truth
MaxLFQ_estimate <- iq::maxLFQ(protein_list$P12799)$estimate

ground_truth <-  log2(rep(c(200, 125.99, 79.37, 50, 4, 2.52, 1.59, 1), each = 3))
ground_truth <- ground_truth - mean(ground_truth) + mean(MaxLFQ_estimate)

#ground truth plot
pdf(file = "P12799 MaxLFQ versus groundtruth.pdf")
iq::plot_protein(rbind(MaxLFQ = MaxLFQ_estimate,
                       Groundtruth = ground_truth), 
                 main = "P12799 - MaxLFQ versus groundtruth",  
                 split = 0.75, 
                 col = c("green", "gold"))  
dev.off()

Вот мой Dockerfile:

FROM r-base:latest

ENV SCRIPT=""
ENV DATA=""                            

RUN R -e "install.packages('iq', repos='http://cran.rstudio.com/')"

WORKDIR /home/iq/

CMD R -e "x <- read.table(\"$DATA\", header = TRUE, sep = ",", fill = TRUE)" \
   && R -e "source('$SCRIPT')"

В папке с путем / home / iq у меня есть сценарий под названием iqTest.R, Dockerfile и data.csv. Я собираю и запускаю свой контейнер с помощью следующих команд:

$ cd iq
$ docker build -t my_image .
$ docker run -it -v /home/user/iq:/home/iq --env DATA=data.csv --env SCRIPT=iqTest.R my_image:latest

После попытки запуска я получаю следующее сообщение об ошибке из рабочего пространства R:

> source('iqTest.R')
Error in iq::preprocess(x, median_normalization = FALSE, pdf_out = NULL) : 
  object 'x' not found
Calls: source -> withVisible -> eval -> eval -> <Anonymous>

Изменить: I недавно обнаружил, что проблема больше связана с R: вы можете использовать ; для соединения нескольких строк кода R в одной строке, что решает проблему с несколькими операторами CMD. Пусть это послужит примером для всех, кто подключает R и Docker. Например, новый файл Dockerfile с точкой с запятой для связывания строк:

FROM r-base:latest

ENV SCRIPT=""
ENV DATA="" 

RUN mkdir /home/analysis/

RUN R -e "install.packages('iq', repos='http://cran.rstudio.com/')"

WORKDIR /home/analysis/

CMD R -e "x = read.csv(\"$DATA\", header = TRUE, sep = ",", fill = TRUE); source('$SCRIPT')"

1 Ответ

0 голосов
/ 02 июня 2020

Если вы хотите настроить путь к данным через переменную среды, я бы рекомендовал получить доступ к этой переменной в вашем скрипте с помощью Sys.getenv(). Это также позволяет вам использовать Rscript вместо R -e "source....

Вот что у меня сработало:

script.R

cat(Sys.getenv('SCRIPT'), '\n');
cat(Sys.getenv('DATA'), '\n')

Dockerfile

FROM r-base:latest

ENV SCRIPT="script.R"
ENV DATA="data.csv"

WORKDIR /workspace

CMD R -q -e "source('$SCRIPT')"
# alternative: CMD Rscript $SCRIPT

Использование

daniel@nuest /tmp/stackoverflow []$ docker build --tag stackoverflow .
Sending build context to Docker daemon  4.608kB
Step 1/5 : FROM r-base:latest
 ---> 46edce0e80af
Step 2/5 : ENV SCRIPT="script.R"
 ---> Using cache
 ---> 8f26d34d9c0a
Step 3/5 : ENV DATA="data.csv"
 ---> Using cache
 ---> 16c83c16a4c8
Step 4/5 : WORKDIR /workspace
 ---> Running in fce8619af30b
Removing intermediate container fce8619af30b
 ---> a8278f609d9a
Step 5/5 : CMD R -q -e "source('$SCRIPT')"
 ---> Running in 765bafeb8681
Removing intermediate container 765bafeb8681
 ---> ff7d7b09dffb
Successfully built ff7d7b09dffb
Successfully tagged stackoverflow:latest
daniel@nuest /tmp/stackoverflow []$ docker run --rm -it -v $(pwd):/workspace stackoverflow
> source('script.R')
script.R 
data.csv 
> 
> 

В качестве альтернативы вы можете попробовать передать путь к данным в качестве аргумента свой файл сценария см. https://swcarpentry.github.io/r-novice-inflammation/05-cmdline/

В качестве дополнения: для воспроизводимости лучше закрепить конкретную версию c R, чем использовать :latest.

...