如何解除LXC容器和应用生命周期之间的耦合

    使用LXC应用容器时,容器的生命周期和应用是耦合在一起的。即:

Lxc-execute -n foo bar 此时用容器foo 启动了一个应用bar,在容器启动时,应用就启动了。

Lxc-stop -n foo 此时停止了容器foo,根据lxc的规则,容器的应用也就停止了。

     通过以上两个例子可以看出应用和容器是紧耦合在一起的,要生一起生,要死一起死。可能  有很多人会不明白LXC怎么会有这么蛋疼的规定。其实深入了解一下LXC的实现就清楚了,LXC是基于cgroups实现的,LXC的应用容器并不是一个真正意义上完整的系统,只是一个进程组,然后系统以这个进程组进行资源和安全管理。因此,进程在,容器才在,不能只有容器没有进程。但是在实际应用中,我们可能有把应用和容器的生命周期耦合关系剥离开的需求,比如先启动容器,再部署应用;关闭应用,但是容器继续存在。

    那有没有解决办法呢?其实还是有的。从前面的分析,我们可以看出和容器真正耦合的是进程,而非应用。应用并不等价于进程。当我们按通常的做法,使用lxc-execute启动应用时,那应用就等价于进程,耦合在一起了,但是如果我们用lxc-execute启动一个进程,欺骗LXC,先将容器跑起来,然后再把我们的应用加入进去。LXC也确实有一个命令lxc-attach将一个进程在一个已经在运行的容器中启动。那结合这两点,好像已经可以解决这个问题了。不幸的是,lxc-attach是一个比较特殊的命令,使用它还需要额外内核补丁,因为改命令的实现中涉及到对进程的命令空间的切换,现在的内核对此支持还不完整。这就不好办了。

    那还有没有其他办法呢?也是有的,还不止一种。最直观的一种用LXC的系统容器,用lxc-start先启动一个操作系统的用户态实例,再在系统容器的启动应用。这样以来,应用和容器完全没有耦合了,因为现在应用和容器的关系,就跟你在原生系统使用应用一样了。但是这样也是有问题的。首先,不是所有的应用场景都需要这样大费周章去配一个系统容器,其次,这样也会有一定的额外开销。

    在前面所有的铺垫之后,来介绍一下作者自己想的一个不是办法的办法。思想跟使用lxc-attach差不多。用lxc-attach最大的问题在于,该命令的实现需要切换进程的命名空间、cgroup等,所以作者的方法就是不从外部把进程加进去,而是从内部把进程生出来。具体而言就是,利用容器里已有的进程fork一个子进程,然后在子进程中exec应用。由于cgroups和命名空间的特性,fork出来的子进程默认和父进程在同一个cgroup和命名空间,那对于LXC来说就相当于在同一个容器了。那用容器里哪个进程来fork,这个进程又怎么知道什么时候forkfork后又执行什么应用呢?方法仍然很简单,之前lxc-attach的方案中,不是用lxc-execute启动了一个进程么?那个进程在之前的方案中纯属多余,完全是为了起一个容器,其余时间都是在打酱油。由于这个进程可以是任何进程,所以在作者的方法里面,作者就自己写了一个程序,该程序跑起来以后,在容器里就充当一个代理的角色。容器外的进程通过进程间通信,通知它来完成应用的启动和关闭。

最后无图无真相,这里给出一个简单的示意图:

 

要自己动手写的就是proxy,和notifier。写好后,启动容器时,就用lxc-execute -n foo proxy,启动应用时,就用notifier通知proxy。

 

 

 

 

posted on 2012-06-20 17:17  lisperl  阅读(2587)  评论(0编辑  收藏  举报