如果想把OJ和docker结合起来,我觉得无论是把编译运行放在容器里面,还是只把运行放在容器里面,
首先都得用程序来把容器启动起来,运行结束反馈结果后,容器关闭。
先不论这个相应的镜像怎么创建,以及不论容器怎么从服务器中取程序或者可执行文件,首先,我觉得
我首先得解决用程序来生成一个容器,当然是任意一个镜像的容器。
这里我选择的程序是C程序,因为docker暂时只是和linux相处比较融洽。第一版程序如下:
#include <stdio.h> #include <stdlib.h> #include<unistd.h> #include<sys/types.h> int main(int argc, char **argv){ pid_t pid; printf("PID before fork(): %d\n" , getpid()); pid = fork(); pid_t npid = getpid(); if(pid < 0){ perror("fork error!\n"); } else if(pid == 0){ int i = execlp("docker", "docker", "images", NULL); printf("return num is : %d\n I am child process, PID is %d\n", i, npid); } else{ printf("213\n"); } return 0; }
这段代码的大概意思就是,fock出来一个子进程,通过函数execlp(我把这个函数叫做换魂函数,哈哈)去
执行docker images命令。我的想法是如果能够执行docker images命令,那么就肯定能够执行其他docker相关
命令,当然包括创建容器之类的命令。
执行结果如下:
执行成功,对照一下真正的docker images命令:
-----------------------------------------------------------------------------------------------------(分割线)(下面是思考过程)
第一部分的实验结束了,那么接下来就是真正的工作了,那就是通过代码调用命令来生成容器。
我的想法是这样的:假设学生前台的代码已经写完了,然后点击提交,那么首先把代码保存到数
据库中;之后,服务器中部署好的代码从数据库中取出学生提交的程序进行编译(这里假设编译过程
不在docker中进行);如果编译不通过,直接返回给学生客户端(浏览器),提示出错;如果编译通
过,那么就通过代码调用镜像(假设镜像已经构建成功)生成容器,然后把编译好的可执行文件放在
容器中运行;容器将运行结果反馈出来;服务器开始对反馈出来的结果进行对比,然后将最终结果返
回给学生。
经过上面的思路分析,我觉得应该把编译过程也放在容器中,但是又在想如果把编译过程放在容
器中,那么编译启动容器,然后如果不成功,那么就会造成额外的浪费(我也不知道是浪费什么)。
只是感觉,这个想法已经是后话。
那么假设镜像已经构建好了,那么镜像肯定有自己的名字,这个名字是固定的。那么生成的容器
肯定也必须要有一个对应的名字,虽然说容器运行完程序后就会关闭,但是还是要记录一下。这个容
器的名字什么的,暂时不需要考虑。
说了这么多,那么经过了上面的实验,现在需要做的是什么?要做的有以下几个部分。
1、尝试用代码启动容器。(会在下面给出实验过程结果)
2、容器调用编译好的文件。(实验的时候先用普通文件代替)(会在下面给出实验过程结果)
3、制作可以运行编译后文件的镜像。(❤重点,暂时完全不知道怎么弄,明天再看)
4、如果3不能够进行下去,那么就试着将编译过程写进镜像中。(❤同上)
----------------------------------------------------------------------------------------------(分割线)
尝试用代码启动容器
只是改一下上面的调用函数(excelp)的参数。
修改后的代码和运行结果如下:
int i = execlp("docker", "docker", "run", "-ti", "--name", "test", "a5", "/bin/bash", NULL);可以看到只是改变了函数的参数,这个命令写出来就是:
$ docker run -ti --name test a5 /bin/bash
命令解释:docker run 就是新建并启动一个容器;-ti 参数其实是 -t 和 -i 的缩写,t代表docker分配
一个伪终端给新建的容器,i表示让标准输入打开;--name test 表示新建的容器名字叫做test;a5是镜像
名字,这里只是用了镜像的id的前面两个字符(可区分就可以),从上面截图可以看到a5是一个Ubuntu
系统,也就是这个容器就是一个Ubuntu;后面的/bin/bash相当于运行此容器中应用。
运行结果如下:
从截图可以看到运行成功,新建的容器id是003ca....。因为是Ubuntu,所以可以用linux的命令
ls,mkdir等,当用到gedit时,因为在a5镜像中没有安装此应用,所以无法执行。
这个截图就是退出容器,然后使用docker ps -a 就能查看到所有处于stop状态的容器。可以看
到,这个容器就是刚才的那个容器,名字是test,id是003c.....,退出时间是12秒前。
----------------------------------------------------------------------------------------------(分割线)
上面解决了程序调用启动容器,至于关闭删除容器暂时先不想,然后下面是容器获得编译好的
可执行文件,暂时用普通文件代替。
对于这部分我的想法是用docker数据卷的功能。关于这部分,在《docker技术入门与实践》这
本书上有这么一段话:用户在使用docker 的过程中,往往需要能查看容器内应用产生的数据,或者
需要把容器内的数据进行备份,甚至多个容器之间进行数据的共享,这必然涉及容器的数据管理操
作。
容器中管理数据主要有两种方式,一种是数据卷,一种是数据卷容器。我要用的是数据卷。
按照上面分析的过程,首先将学生的代码进行编译,将编译后的可执行文件放在某个目录下,然
后在执行的时候将这个目录挂载在容器上,这样容器在启动后就能够看到这个这个编译后的可执行文
件。
现在假设容器中已经将运行环境安装好了,那么接下来就有两种方法去执行这个可执行文件了。
第一种:启动容器后直接执行,通过命令行调用执行这个可执行文件。
第二种:在容器中写好一段程序,启动程序后通过这段程序来调用执行用户的可执行文件。
这两种方法,第二种我觉得我能做一下,至于第一种,还没有细想。这个下次再想,现在呢,就实验
一下挂载数据卷的那个功能。
首先设想这么一个场景,用户的代码已经编译通过,并且保存在服务器的一个目录下了,然后服
务器程序现在就应该去执行这个可执行文件了。首先通过命令行新建启动一个容器,同时把编译文件
所在目录挂载在容器上。然后后面的执行过程等张文把带有执行环境的镜像搞定后就ok了。
以下是实验过程:
修改的代码部分为:
没有改代码,改的话也只是把execlp函数的参数改变一下而已,没有什么意思。
对应的docker命令是下面这个:
$ docker run -ti --name test -P -v /home/wmn/dockertest : /home : ro -v /home a5
命令解释:docker run 就是新建并启动一个容器;-ti 参数其实是 -t 和 -i 的缩写,t代表docker分配
一个伪终端给新建的容器,i表示让标准输入打开;--name test 表示新建的容器名字叫做test;-P的意思
就是指定容器与外部的通信端口,大写P就是默认,如果是小写p则需要指定;-v就是挂载或者创建一个
数据卷,这块有两个-v,第一个是将主机的目录/home/wmn/dockertest挂载到容器的/home目录下,or的
意思是挂载的这个目录是read only ;第二个是在home目录下创建一个数据卷;a5是镜像名字,这里只
是用了镜像的id的前面两个字符(可区分就可以),从上面截图可以看到a5是一个Ubuntu系统,也就是
这个容器就是一个Ubuntu;后面的/bin/bash相当于运行此容器中应用。
运行结果截图:
可以看到,原主机目录下的dockertest目录下有两个目录,一个是JudgeResult目录,一个是compiled目
录,经过挂载后,在容器test里面可以看到home目录下也有了这两个目录,而且目录下面的内容也是可以看到
的。然后尝试创建一个新的目录,提示,挂载的这个目录是只读的,不能被写。
对于命令中的第二个-v参数还没有弄明白,创建的数据卷并没有体现出来,明天解决这个问题吧。
---------------------------------------------------------------------------------------------------------------(分割线)
总结:
那么基本的实验已经完成了,对于上面这个实验,假设挂载的目录下就存在已经编译好的可执行文件,和
程序的输入文件和输出对比文件。然后假设容器里面已经有一个程序,执行那个程序就可以运行这个可执行文件,
并进行输入和输出校验(根据挂载的目录下的文件进行输入和校验)(这里需要把原来的OJ代码读透彻),执行
结束后,将结果返回。
现在看来,这两个实验简直是简单。没有什么技术含量。
================================================================(结束)
接下来的工作计划:
1、制作一个可以执行代码的镜像(其实我发现,用来测试的这个Ubuntu镜像就可以执行C程序的可执行文件);
2、编译过程就不放在容器中进行了;
3、开始透彻分析OJ源码的执行过程,部署主机的目录和镜像中的目录(这块跟张文商讨一下吧);
4、分析OJ数据库中的表的结构,看一下那些表都存着什么信息,都是干什么用的。
今日结语:加油吧!