基于 Kubernetes 的应用开发注意点
Last updated
Was this helpful?
Last updated
Was this helpful?
应用开发者必须允许应用可以被相对频繁地迁移。主要从下面两个方面进行考虑。
当一个 Pod 被重新创建时,它的 IP 和 主机名会发生变化(StatefulSet 管理的 Pod 的主机名不会变化)。
解决方法:使用 FQDN 或 Service 访问 Pod,而不依赖 IP 地址。
Pod 的重新调度以及内部容器的重启都会导致写入容器可写层的数据消失。
解决方法:使用存储卷来实现跨容器持久化数据。
有时候,可能多个 Pod 之间是有严格的启动顺序的。
解决方法:可以使用 ,在 init 容器中探测依赖的其他 Pod 是否已运行,在确定已运行后,再允许常规容器启动即可。
使用 init 容器只能保证启动时的正确性,不能应对该 Pod 在运行过程中,其依赖的服务崩溃的情况。
因此最好在开发应用时,构建一个不需要它所依赖的服务都准备好后才能启动的应用。并且可以使用 间歇性的探测其所依赖的服务是否运行正常。
应用应该通过 启动关闭流程 来响应 SIGTERM 信号,并且在 关闭流程 结束后终止运行。或者可以通过 停止前钩子 来收取关闭通知。
需要给 Pod 制定充足的 终止宽限时间,保证 Pod 能够正常的结束。
在一些会有许多重要数据保存在存储卷中的情况中,可能在指定的终止宽限时间内,来不及将所有数据迁移至其他 Pod 中。
解决方法:针对这类业务场景,可以使用一个 CronJob 对象,周期性的运行数据迁移的 Pod,查看是否存在孤立的存储卷,若存在,则将其中的数据迁移至存活的 Pod 中。
首先分析一下为何需要避免这种情况,这种情况是如何产生的。
下面这张图展示了在 Pod 被删除时发生的事件:
从 A 和 B 两条时间线中可以看出,A 的操作少于 B 的操作,因此在这种情况下,若 A 执行结束,即 Pod 中的容器已经被删除,但是 iptables 还存在,即还是会将客户端的请求转发至不存在的 Pod 中,这可能会导致用户收到如“连接被拒绝”之类的错误。
针对这种问题,唯一可以做的,就是在容器进程被结束前 延长几秒钟,让 kube-proxy 更新完 iptables 后,已经存在的连接被妥善处理后再退出。
妥善关闭应用步骤包括(即延长了 A 事件的完成时间,等到 B 事件完成后再结束):
等待几秒钟,然后停止接收新的连接(等待 kube-proxy 更新完 iptables 规则,即 B 事件完成);
关闭所有没有请求过来的长连接;
等待所有的请求完成;
完全关闭应用;
使用的基础镜像能够小一点最好,可以除了包含业务代码外,再包含一些常用工具即可。
给镜像打标签时,应当合理,可以根据版本进行区分,不要全部使用 latest。
使用 ImagePullPolicy 时,可以根据需要进行指定。
给所有使用的资源都打上标签,可以有助于管理,使用多维度标签可以使得能够通过不同维度来选择他们。
标签可以包含如下的内容:
资源所属的应用(或微服务)名称
应用层级(前端、后端,等等)
运行环境(开发、测试、预发、生产,等等)
版本号
发布类型(稳定版、金丝雀、蓝绿开发中的绿色或蓝色,等等)
租户(如果你在每个租户中运行不同的 Pod 而不是使用命名空间)
分片(带分片的系统)
资源至少应该包括一个 描述资源的注解 和一个 描述资源负责人的注解。
在微服务框架中,Pod 应该包含一个注解来描述 该 Pod 依赖的其他服务名称。
为了更加容易的调查容器的终止原因,可以在 pod.spec.containers.terminationMessagePath
中指定终止消息写入文件的路径,然后在容器中将终止时的消息写入该文件中。这样,当容器被终止时,可以通过 kubectl describe
查看对应容器的 State 或 Last State 中的 Message 就可以看到终止原因。
下面展示一个案例:
通过 kubectl describe
查看该容器的终止原因:
当容器内应用将日志写到 标准输出终端 时,可以直接使用 kubectl logs
命令查看日志。
如果想查看当前容器之前的崩溃容器的日志,可以加上 --previous
参数查看日志。
如果应用比较庞大,也可以使用 EFK 栈去实现一套日志采集引擎 —— ElasticSearch、FluentD、Kibana。首先在每个宿主机中部署一个 FluentD 代理(通过 DaemonSet 作为 Pod 部署),负责从容器中采集日志,给日志打上和 Pod 相关的信息;然后将日志信息发送给 ElasticSearch,由 ES 持久化存储,并提供高级搜索功能;最后由 Kibana 提供前端可视化界面,对接 ES,为运维人员提供可视化操作。
在了解了 之后,需要在 Pod 被关闭时进行合理的收尾操作。
针对在 Pod 启动时,客户端连接断开的情况,可以为 Pod 设置 ,只有当探针确认服务准备就绪时,才将 Pod 纳入 Service 的后端,为客户端提供服务。
至少可以通过使用一个 来等待几秒钟再退出(因为当存在 停止前钩子 时,只有当钩子执行结束,无论成功失败,才会发送 SIGTERM 信号):