docker部署shiny app详细教程

不了解docker的可以自己搜,这里就不赘述了(本文已经很啰嗦了😅)。我是初学者,第一次打包可能有很多写的不对的,欢迎大家评论里批评指正。项目已上传github

为什么

用docker部署R脚本或者shiny app越来越受到R用户的推崇,因为R有两点尤其被诟病(特别是被我周围的python用户们😅):

1.  在安包时经常遇到报错,而且在天朝不用代理,下载速度还很慢,R和Rstudio的代理还得在环境里再设置一遍,否则他不走系统的代理;

2.  版本一更新,R包载入挂了,或者是某函数挂了,得手动安旧版本才能运行(我的部长特别喜欢更新服务器内核,一更新R就升级了,然后有些老软件就挂了😂);python里有virtualenv这个文明利器,可以隔离不同python版本环境,R我目前还没找到可以替代的。

用docker部署R程序就可以很好地解决上述问题,使R脚本的稳定性和可用性大大提高,交付直接给他docker包,只要是会用docker的都能够运行,且和你得到相同的结果,以后就不用再听他们发牢骚了😆。

怎么做

本文主要介绍shiny app的docker部署,操作环境是CentOS7,shiny是一个网页服务,用docker打包特别方便部署。注意本文是讲如何将app整个打包到docker里,如果只想部署shiny-sever可以直接使用现成的镜像。

一、安装docker 

docker的安装可以查看官方文档,不同系统在文档里都能找到。

运行

sudo systemctl start docker

权限

docker命令需要使用sudo权限,如果想给无sudo权限用户使用,请把他加入docker组:

sudo usermod -aG docker  username

使用代理

如果想要使用代理下载镜像需要创建如下文件:

sudo mkdir -p /etc/systemd/system/docker.service.d
sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf

然后往文件添加代理以下信息:

[Service]   
    Environment="HTTP_PROXY=http://代理ip:代理端口/" "NO_PROXY=localhost,127.0.0.1,.cn"

保存后重启docker

sudo systemctl daemon-reload
sudo systemctl restart docker

查看docker使用的代理

systemctl show --property=Environment docker

开机启动

sudo systemctl daemon-reload
sudo systemctl enable docker.service


二、创建Dockerfile 

1、创建文件夹,把shiny app拷贝进去

app需要注意的是路径名称,比如我把关键函数写成了R脚本“tool.R”在app.R里要写成source("./tool.R"),否则会提示找不到。


2、创建Dockerfile

大多数情况下我们都需要基于别人建好的镜像来构建我们自己的镜像,Dockerfile以FROM开头,用来描述我们的镜像是依赖于何种镜像构建的。R语言有许多种可用镜像,比如官方的shiny-Docker镜像rocker,基于Debian系统,它允许我们使用docker版的shiny来运行我们主机上的应用。不过我们现在的目的是要把shiny app整个打包到docker里,所以我们把它的Dockerfile拷贝下来进行修改。

①  FROM

rocker的R镜像rocker/r-ver,从3.1.0至今版本均有维护。R控制台中输入如下命令可查看自己的R版本:

R.Version()$version.string

我的R版本是3.6.0,所以Dockerfile开头可以改成:

FROM rocker/r-ver:3.6.0


②  RUN

RUN语句是模仿命令行的语句,我们在服务器上安装shiny所运行的命令都在这里操作一遍。

Dockerfile文件里的RUN命令,主要是用来更新系统的包,安装一些必须的命令和shiny server,以及安装R的包,如shiny、rmarkdown之类。我们需要修改和补充的主要是安装代码,补充上你运行app需要安装依赖*和包,我还用到了bioconductor的包,所以需要补它的安装命令 。

* 比如我用到了pdftools包,需要安装依赖 sudo apt-get install -y libmagick++-dev 
   其实blast和参考序列也可以在这里安装下载和构建索引,但是因为参考序列文件太大了,下载需要时间长,所以就直接拷贝了。


③  COPY

copy命令用来拷贝主机(host)上的文件或文件夹到容器(container),待拷贝的文件或文件夹需要和Dockerfile存放在同一目录下。

rocker-shiny是用挂载的方式来运行app,本身没有打包app;我们现在要打包自己的app就要把他从主机拷到容器,比如我把待打包的所有文件存在同一目录的名为app的目录下,则在Dockerfile里要补充:

COPY /app /srv/shiny-server/


④  EXPOSE

指定shiny容器的端口,我们可以采用shiny服务器默认的3838端口。

⑤  CMD

CMD命令是每次我们运行这个容器都会执行的命令,在这里我们直接用rocker的git里提供的shiny-server.sh,不需要修改Dockerfile这部分命令。


3、配置shiny server

装过shiny server的都知道,需要配置shiny-server.conf,这个文件主要是用来指定shiny服务器的运行用户(run_as shiny,服务器端口,还有我们app的存放路径和日志路径,假如新增一个app就要在这里设置。我们可以直接使用默认的,不用自己配置。

如果需要特殊配置的,比如添加新用户,添加新app之类的,配置一份文件,然后用COPY命令替换原始的配置。比如要新增一个app叫sanger,访问localhost:3838/sanger/, 则可以在server的花括号里添加:

  location /sanger {
    run_as 用户名;
    app_dir app存放路径;
    log_dir log存放路径;
  }

最终创建镜像前,文件夹的结构如下:

directory/
---  Dockerfile
---  shiny-server.sh
---  app/
             ---   app.R

注意事项

如果软件用到的配置文件比较大,可以采取挂载的形式,让容器可以访问主机上的路径。
但是需要注意,shiny在运行的时候是以“shiny”这个用户运行的,而docker则是以root运行的,所以Dockerfile里要写好开权限的命令,保证:

1)app里用到其他软件,比如该app会调用blastn,需要开可执行权限给“shiny”用户。

2)app如果会输出文件,需保证输出的文件夹满足:

容器内:需要是shiny用户能够访问和写入的文件夹,不要开在/home下,也不要开在/home/shiny下
主机内:如果挂载了输出文件夹,那么主机的挂载文件夹需要开可写入权限

或者创建一个新用户,在shiny-server.conf配置运行权限,可以但是不建议直接用root运行shiny。

* blastn是用来比对DNA序列的软件

三、创建镜像

我们cd到存放Dockerfile的目录,然后运行如下创建命令,app名称可以随便取

docker build -t APP名称 .

如果构建失败重新运行上述命令,他会用缓存,从失败的那一步重新运行,不想使用缓存可以在build后加参数--no-cache

docker build --no-cache -t APP名称 .

*  如果在国外创建,可以用rocker里的下载安装命令,如果人在墙内,建议把默认的Debian stretch源换成中科大的,R和bioconductor用的是清华的镜像(参考本项目的Dockerfile),下载速度比源代码有了明显提高。shiny-sever源目前还没有找到国内的镜像,个人感觉早上下载速度快,实在下不动就用拷贝的吧。

四、在本地运行镜像

创建成功后,可以用如下命令在本地运行镜像,然后访问localhost:端口号就能看到我们的app,下述命令冒号前面是主机端口号,后面的3838是配置在Dockerfile的端口号

docker run -p 端口号:3838 APP名称

如果需要挂载文件,比如挂载shiny-server的log,可以用-v命令,同样,冒号前面是主机路径,后面是容器内路径,shiny-server的log路径配置在shiny-server.conf文件里:

docker run -p 端口号:3838 -v ./log:/var/log/shiny-server APP名称

如果app有错误,可以先用docker ps查找容器的id,然后用exec进入容器调试,用ctrlD可以退出容器

docker ps
docker exec -it  容器ID /bin/bash

五、 分享或部署

到此为止,我们创建了镜像,并且能在本机成功运行容器,你可以通过分享Dockerfile和软件包来分享镜像,比如上传docker hub。也可以将项目打包:

docker save -o ~/APP名称.tar APP名称

然后在其他服务器上解压和运行,就能快速地部署服务器了

docker load -i APP名称.tar
docker run -p 端口号:3838 APP名称

评论

此博客中的热门博文

R包编写详细教程

Hadley Wickham的R语言编写规范

RMarkdown中文报错的问题【解决】