研一寒假Docker学习笔记2

来源:互联网 发布:python 查看字节 编辑:程序博客网 时间:2024/06/04 20:45

如果想把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数据库中的表的结构,看一下那些表都存着什么信息,都是干什么用的。


今日结语:加油吧!




0 0