背景
小师妹突然找到我,问我是否知道怎么做singularity或者docker镜像。细问之下了解到,她的文章在投,因为涉及她自己写的一个工具以及数据分析,编辑要求她提供镜像。原因在上一篇推文其实提到过,总的来说,只提供分析源码和数据本身并不能保证分析的可重复性。因为还有依赖的库以及软件版本改变带来的隐忧。
也许,容器技术在生信领域已经成为其中一种标准了。
这篇推文主要介绍一下2种生成docker镜像的方法。
镜像封装
打包镜像的方法有2种:
以某个基础镜像,比如centos7,为基础,运行docker容器,在容器内将所有需要安装的安装完毕后,以该容器为基础生成一个新的镜像。
我自己的习惯是,先在容器内将需要安装的软件安装一遍,测试所需要安装的工具怎么装,以及是否有需要额外安装的依赖包等等,然后写成Dockerfile再生成新镜像。
对于需要安装的软件包,我们可以直接下载到本地,再使用某些方式放到容器内(后边会细说),也可以联网下载。
方式1:基于容器
先获取1个基础镜像并启动为容器。
docker run -it --name sxydemo --hostname sxy -v $PWD :/docker centos:7
docker run 其实本质上是docker pull(拉取镜像)+ docker start(启动容器)。拉取镜像是从
Docker-Hub
上。镜像相关信息也可以从上边获取。
-it,其实是-i 和 -t 的组合,一般一起使用,就是开启了一个伪终端,让你可以在终端输入命令进行交互
Name, shorthand
Default
Description
--interactive , -i
Keep STDIN open even if not attached
--tty , -t
Allocate a pseudo-TTY
-v 挂载卷。这里我的当前路径就是放了很多需要安装软件的路径。在容器中,对应该卷路径为根目录下的docker目录。
--name 给容器命名,可选。这个参数不加对我们后续没有影响
--hostname 加了以后,容器内部的hostname就设定为你指定的这个,不加则为容器id。这个参数不加对我们后续没有影响
docker run后我们就进入了容器了。我们来看看挂载的情况:
docker目录下的东西跟我们本地的被挂载路径的东西是一模一样的。因为本质上,这俩就是同一个卷。通过这种方式,你在容器内分析的数据就可以存储到服务器上了。
你可以自己尝试在容器内创建一个空文件,在容器外部也会看到的。
因为我们拉取的是一个linux系统的基础镜像,因此在进入容器以后,我们就跟在一个linux的终端上体验没区别了。安装软件也完全一样的。并且原先你习惯用conda来安装软件,在docker内也是一样一样的。
来装2个软件试试。下载安装的方式以perl为例。使用已经下载好的安装包进行安装则以samtools为例。但其实二者没什么太大区别,完全使用前一种方法的话,无需挂载路径。而后者需要将能访问到软件包的路径挂载到容器内(注意,可以是软件包所在路径,也可以是其上级目录)。仅此而已。
perl
perl5.16.3源码地址:https://www.cpan.org/src/5.0/perl-5.16.3.tar.bz2
在正式开始之前,先装几个要用到的软件。
perl的编译需要gcc和make(或者gmake)
yum -y install bzip2 gcc make less
在这个镜像里面没有预装wget,我们用curl来下载。
mkdir /src && cd /src curl -OL https://www.cpan.org/src/5.0/perl-5.16.3.tar.bz2 tar -jxvf perl-5.16.3.tar.bz2cd perl-5.16.3# -d use defaults for all answers.否则一项项地确认好麻烦。 ./Configure -d make make install
plink
解压zip需要unzip工具:
yum install -y unzip
为了避免出现读写权限问题,将/docker下的plink拷贝到/src下再进行后续操作.
cp /docker/plink_linux_x86_64.zip /src unzip plink_linux_x86_64.zip -d ./plink ls plink# LICENSE plink prettify toy.map toy.ped
因为这里只有2个可执行文件,我将plink和prettify直接拷贝到其中一个PATH路径下。
查看:
echo $PATH /usr/local /sbin:/usr/local /bin:/usr/sbin:/usr/bin:/sbin:/bin
拷贝:
cp ./plink/{plink,prettify} /usr/local /bin
其他的软件安装方法都是跟linux下一样的(因为是基于centos7镜像),不再赘述了。
将容器打包成镜像:
docker commit sxydemo sxydemo:20201126
sxydemo是docker的名称,你也可以用容器id。二者是一样的。
docker images|head -2
我们使用新打包好的镜像生成容器,然后简单验证一下:
docker run -it --rm -u $(id -u):$(id -g) sxydemo:20201126
另外,我们挂载的容器内路径还在,但是内容是空的(因为我们这次没挂载了)。而拷贝到容器内的目录下的文件还会在。为了使得我们最后打包的镜像体积小一点,其实在安装后无用的文件我们都应该清除。
另外,我们可以看到,这次因为我使用了
-u
命令进入容器,因此此时,对于docker容器内部,我是一个普通的用户身份。
--rm
:当我退出容器的时候,便将容器销毁。这个命令在我们频繁使用docker进行数据分析的时候很有必要加上。
方式2:基于dockerfile
将上述过程,整理成dockerfile的形式,打包镜像。
FROM centos:7 ADD . /src RUN yum install -y bzip2 gcc less unzip RUN cd /src && curl -OL https://www.cpan.org/src/5.0/perl-5.16.3.tar.bz2 && \ tar -jxvf perl-5.16.3.tar.bz2 && \ cd perl-5.16.3 && \ ./Configure -d -e && \ make && make install && \ rm -rf /src/perl-5.16.3* RUN cd /src && unzip plink_linux_x86_64.zip -d ./plink && \ cp ./plink/{plink,prettify} /usr/local /bin && \ rm -rf /src/plink* CMD ["/bin/bash" ]
将上述文件名字命名为
Dockerfile
,
docker build
的时候会自动去找指定路径下的这个名称的文件,然后根据描述去构建镜像。你也可以更改名字,只要在
docker build
的时候用
--file
或者
-f
指定一下就好了。
docker build -t sxydemo:20201126 .
这里多说一句,为什么两次RUN我都需要
cd /src
,上一次进入这个路径以后,不是应该就在这个路径下了吗?但是对于Dockerfile打包镜像的时候,每一行命令都是基于上一个打包好的基础上作为一个新的docker启动的,因此每次都是回到某个设定好的最初进入的路径(比如你在linux下每次登陆进去都是进入你的home目录),可以使用
WORKDIR
进行设置进入docker后的路径是哪里。
另外,Docker镜像是一层一层的,容器只是在镜像最上边加了一层读写层。而每一层需要删除的东西得在这一层进行删除(比如第2个和第3个RUN命令),在下一层删除是没有意义的,它其实仍然存在,只是对你不可见了而已,这样镜像打包后大小并不会改变。
同样,打包完自行检查一下软件等是不是都正确能使用了。
docker run -it --rm -u $(id -u):$(id -g) sxydemo:20201126
如果需要将这个镜像分享给别人,可以使用下边命令打包成tar文件:
docker save -o /YOUR/PATH/docker_tar/sxydemo_20201126.tar sxydemo:20201126
参考资料