容器:向死而生的一生

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

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

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

容器:向死而生的一生

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

容器:向死而生的一生

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

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

容器:向死而生的一生

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

容器:向死而生的一生

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

  • 首先,由于在本地未能找到镜像,Docker client 通过 Remote API 从 Docker Hub pull 下镜像;

  • 接下来,创建容器,添加 stdout/stderr 到终端;

  • 然后,新容器被加入到默认的 bridge 网络,被 engine 启动;

  • 最终,容器完成自己的使命后,就会被删除。在此之前,Docker engine 会将其移出默认的 bridge 网络中。

容器已死,容器万岁!The container is dead, long live the container!

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

容器:向死而生的一生

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

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

容器:向死而生的一生

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

容器:向死而生的一生

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

容器:向死而生的一生

重生?Is there life after death?

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

容器:向死而生的一生 

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

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

容器:向死而生的一生

生命本该如此This is your life

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

容器:向死而生的一生

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

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

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

暂停Making a pause

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

容器:向死而生的一生

容器:向死而生的一生

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

避免“拥堵”Defending yourself from being choked

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

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

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

健康检查Health checks that could save your container

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

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

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

容器:向死而生的一生

容器:向死而生的一生

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

重启Reboot your life or your container

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

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

容器:向死而生的一生

容器:向死而生的一生

重置We should just reset

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

容器:向死而生的一生

容器:向死而生的一生

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

不被打扰的容器Untroubled containers

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

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

容器:向死而生的一生

容器:向死而生的一生

容器:向死而生的一生

颂词A eulogy

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

容器:向死而生的一生

容器:向死而生的一生

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/41845.html

(0)
上一篇 2021年8月5日
下一篇 2021年8月5日

相关推荐

发表回复

登录后才能评论