• DockerInfo
  • >
  • Docker头条
  • >
  • 看过《黑客帝国》和《明日边缘》,可你知道Docker容器和它们的关系吗?

看过《黑客帝国》和《明日边缘》,可你知道Docker容器和它们的关系吗?

尼奥:为什么那些程序会被删除?

先知:或许发生故障了,或许有更好的程序替代它,这种事天天发生,一旦这种事情发生了,程序就会躲在母体里或是选择回到万物之源。

20160720094351

“重启”是一个具有哲学意义的话题,比如《黑客帝国》中特工史密斯可以随时在一个身体上重启自己;《明日边缘》中阿汤哥饰演的男主角在一次次的重启中不断进步,最终战胜了大Boss;程序员的段子中,“你重启下试试”非常经典。虽然重启不能解决所有的问题,但还是能解决大部分问题的,因为重启能清除应用的当前状态,包括任何不在正常状态的代码。对于Docker,我们可以将restart作为一种自我修复的机制。

如果把Docker容器看做生命,那必然是“向死而生”的一生:它们可能会随时被停止或删除。默认情况下,所有运行过程中产生的数据也将被清除。但就像《黑客帝国》中的特工史密斯一样,容器也可以立马以初始的状态被重启。无疑从这个角度来讲,暂时的任务是容器最常用的场景。但实际上,容器也可以完美运行web服务,还可以通过volume实现数据库和持久化数据存储。MongoDB,MySQL和Postgres都是Docker Hub上最流行的Docker镜像。

一个容器的“传记”
Containers Life Story

都有哪些因素影响了容器的平均寿命呢?我们可以通过容器的整个存在过程进行深入研究。在Docker Engine记录了容器整个生命周期的事件,还有其它的重要信息,都保存在了/var/log/docker.log 中。我们也可以通过“docker event“命令行大体看下某个容器。这个命令会向Docker Engine查询某个时间段内容器内发生的重要事件。比如我们先启动一个容器:

20160720094408

然后,运行“docker event”看看哪些事件会被记录下来:

20160720094417

哇!能在命令行看到容器启动过程幕后发生了什么!

  • 首先,由于在本地未能找到镜像,Docker client通过Remote API从Docker Hub ull下镜像;
  • 接下来,创建容器,添加stdout/stderr到终端;
  • 然后,新容器被加入到默认的bridge网络,被engine启动;
  • 最终,容器完成自己的使命后,就会被删除。在此之前,Docker Engine会将其移出默认的bridge网络中。
容器已死,容器万岁!
The container isdead, long live the container!

虽然这个容器已经“死了”,我们还是能用“docker ps -a”命令捕捉到它生前的“音容笑貌”。

20160720094426

看起来这个容器去得很平静,因为Exit(0)并不是每个容器都能达到的境界。

而且虽然容器停止了,我们还是能找到它的“遗产”,因为所有容器的数据存储只有在“docker rm”执行后才会被正式销毁。“docker export”命令可以将容器的文件系统存储到一个tar包中。然后可以用“docker import”将其导入到同主机上的另一个容器中,或一个新的容器中。

20160720094434

请注意“docker export”导出的数据中不包含容器的历史。实际上,当tar包被导入到一个镜像中,结果镜像会被压缩为一层。

20160720094443

如果你在运行短期的前台操作容器,这些数据堆积会额外占用很多空间。“docker run -rm”可以在容器停止后自动清理容器状态数据和镜像层。

20160720094456

重生?
Is there life after death?

研究发现动物的基因并不会在其身体死亡立即消失,而是会存活长达四天,甚至有一些基因会在死后变得更加活跃。对于容器的“基因”当然可以保持更长的时间,而且还可以随时“复活”。

20160720094506

昨日重现
Containers entirelife flashes in front of your eyes the second before they die

“docker event”不但让我们看到Docker Engine内部的工作,还能帮我们自动应对一些容器的事件。比如,类似“Registrator”的工具会在服务上线和下线时,使用这种机制向Consul自动发送注册和注销请求,可以告诉负载均衡器有新的实例进来,或有实例不能提供服务了。

20160720094333

生命本该如此
This is your life

来自GliderLabs的Matt Good曾用一张图描述过Docker容器整个生命周期的事件,和引发这些事件的命令。

20160720095233

熟悉Linux signal的人都会发现这很像Linux中进程的生命周期,因为容器本质上就是Linux进程。只是Docker Engine利用了Linux内核的特性,将容器隔离起来,但容器内的进程可以和文件系统或网络交互,就好像自己是系统内唯一的进程。Signal提供了一种处理异步事件的方式,可以用在Docker容器的进程上。

不能承受的容器之轻
The unbearable lightness of being a container

就像Linux一样,可以用kill命令停止一个不正常或空闲的容器,并且无需注销或重启底层的服务器。Kill命令会向容器中的主进程发送一个SIGKILL的Linux signal,同样stop命令会发送SIGSTOP的Linux Signal。这些在Brian DeHamer的文章中有详细的描述,本文将重点分析上图中其它的命令和事件:pause,OOM和destroy。

暂停
Making a pause

为什么要pause一个容器?好吧,你可能需要暂停一个拖慢进度的容器,或者想对这个容器做一下备份。

20160720095253

20160720095301

有人预测“docker pause”未来可以用在容器的热迁移上。理论上,容器的热迁移是没有意义的,因为它们是无状态的,一次性的,还可以随时重启,但是……

避免“拥堵”
Defending yourself from being choked

默认情况下,所有的容器都是平等的:它们享有同样的CPU周期和IO,还能自由地使用内存。但某些情况下,我们需要用一些限制参数打破这种平等的待遇。比如,为了防止某些容器一直占用内存,造成OOM(Out Of Memory)事件,“堵塞”服务器。但是,需要先去设置内存的限制。

首先我们来模拟下OOM的情景,来看下如果设置了内存限制,Docker Engine是如何发挥作用的:

20160720095309

这个例子基于《Docker in Practice》一书中301的一个实例,在第一种情况下,被杀死的是进程(非主进程),而第二种情况下才是容器,为什么?

健康检查
Health checks that could saveyour container

要注意Linux内核在异常情况(比如资源不足)下只会kill一个进程,可能这样做已经太迟了。为什么不在应用出现问题前,提前检查呢?

Docker 1.12中,不但可以在运行时进行限制,还能在启动容器时添加用户自定义的健康检查探针。比如,我们可以周期性地验证一个web服务是否在正常工作,而不仅仅是避免紊乱情况或内存溢出。

可以将健康检查作为“docker run”的选项,或写在Dockerfile中,“docker ps”命令除了容器的常规状态,也会显示其“健康状况”。如下所示,第一次“docker ps”执行时,监控探针还是“starting”的状态,当第一个探针通过后变成healthy的状态。接下来我们用“docker inspect”查看容器的健康状况。我们通过删除一个配置文件制造不健康的假象,在一系列的探针失败后,最终容器被加上了“unhealthy”的标志。

20160720095332

在当前的Docker Engine版本(1.12r3)中,容器不会在“unhealthy”的状态下重启,所以要检测容器的状态,手动地重启容器。

重启
Reboot your life or your container

在程序员的段子中,“你重启下试试”是很经典的一个,虽然重启不能解决所有的问题,但还是能解决大部分问题的,因为重启能清除应用的当前状态,包括任何不在正常状态的代码。

对于Docker,我们可以用restart作为一种自我修复的机制(就像《明日边缘》中汤姆克鲁斯饰演的男主角)。但是,需要注意的是,默认情况下容器不会在主进程存在的情况下重新启动。我们可以强制Docker Engine任何情况下都重新发布这个容器,或者只在主进程错误退出时重启这个容器:

20160720095349

20160720095407

重置
We should just reset

小故障可能会引起整个应用的崩溃,on-failure重启的政策可以帮助我们在容器返回非0退出时就重新发布。

20160720095417

20160720095425

注意上面Docker Engine是如何提高再启动延时的,直到其达到最大的on-failure重启数。

不被打扰的容器
Untroubled containers

在Docker 1.12中,又可以运行“daemonless”的容器啦!这意味着你可以在不影响或重启容器的情况下,停止、升级、重启Docker Engine,期间服务不会受到任何影响。这个特性之前被取消过,因为会给Docker初学者造成困惑。

为了能使用这个功能,要在启动Docker Engine时添加“live-restore”的flag,保证Docker在关闭和重启的过程中,不会kill运行中的容器。如下,使用docker-machine传递live-restore的flag:

20160720095436

20160720095452

20160720095502

颂词
A eulogy

容器确实随时都面临着“死亡”,但是有了重启,健康检查探针和热修复机制,没有必要担心。套用马克·吐温的一句话:“对死亡的恐惧源于对生活的恐惧。完全活过的容器,随时都准备着死。”

201607200943331

K8S中文社区微信公众号
分享到:更多 ()

评论 抢沙发

评论前必须登录!