生产环境经验java
一、限制了容器资源,还常常被杀死? 二、滚动更新之健康检查重要性 三、滚动更新之流量的丢失
先说一下第一个问题,限制容器资源,还常常去杀死的缘由?
就是说部署的java应用,不一会就重启了,其实重启就是在重建了,这就意味着你的pod是不健康的,而后k8s从新再帮你去拉取了,这样的话就要去找问题去排查了,说白了其实就是被杀死了,能够经过describe去查看一下事件,通常都会能看到,因为健康状态检查没经过,而后再去拉取的,由于是java应用,因为堆内存溢出,而后被kill掉,在日志最后一行会出现一个kill的字段,看一下它重启的缘由,以前我遇到的是它的堆内存没限制住,它自己有jvm,jvm主要有内存作交换数据的,它堆内存主要是性能设计的一个体现,因此他的堆内存很容易就会超出,超出以后呢,极可能会被k8s杀死掉,为何k8s会kill它,由于它超出了限制,默认容器会使用宿主机全部的资源,若是不作资源限制,会影响整个宿主机,而后整个宿主机资源不够会实现飘移,会转移到其余主机上,而后再异常,可能会起到一种雪崩的效应,因此通常咱们都是要作资源限制的,难道这个限制,限制不了java应用吗?其余它是限制不住的,虽然k8s部署应用仍是很方便的,可是部署java应用仍是不兼容的,
好比它不能识别当前java容器的限制,就是不能识别咱们指定的限制,也就是你在yaml去限制的,容器的堆内存没有限制,它就会超出这个限制,若是超出这个limits的限制,k8s就会把它杀掉,k8s自身它有这个策略,若是超出这个容量的限制,就会帮你杀掉,再帮你拉起。docker
面对java这样的堆内存稍微有一点流量突发,就可能资料利用率就上来了,因此这个幅度仍是比较大的,这样就会致使k8s杀掉,而后再拉起,这样循环,可能一天几百次的这样效果。shell
说到这个问题,怎么来解决docker的资源限制,其实这个资源限制仍是用的这个docker来作的,只不过k8s把他转换成了,只不过k8s去调用接口去作一些限制,其实就是怎么让docker去识别java堆内存的限制,来解决这个问题。
来解决这个问题有两种方案,也就是为这个java再配置堆内存的使用
配置java堆内存的使用
Java -Xmx最大的堆内存使用,一个是-Xms是初始的堆内存使用
通常都设置一个最大的堆内存使用,若是不设置超出这个设置会不断使用宿主机的一个内存,而致使物理内存不够用,而出现堆内存溢出的现象,这个很广泛,咱们就用这个java -Xmx就是当快用满时,它会有一个垃圾回收,再进行循环的使用,这样能保证这个java应用稳定的运行,全部咱们在yaml去配置资源限制确定是不够的,咱们必须为这个java去设置这个堆内存,咱们不可能手动的在Dockerfile去写这个吧,通常在dockerfile去传入这个值,在yaml文件里设置一个变量数据库
env: - name: JAVA_OPTS value: “-Xmx1g “
下面就是咱们以前配置的容器资源的一些限制这个变量就会就会传入pod里面,也就是这个构建镜像的容器里,也就是起到容器CMD 命令下去传入$JAVA_OPTS的变量,它会调用咱们系统的系统变量,这个系统变量已经赋予它值了,因此它能直接饮用这个变量了,去起到这个应用,从而设置这个堆内存大小,这个值建议要比limlts要小一点,小个10%吧,由于超过这个limits限制就会杀死,再拉取了。
通常设置好,重建一下镜像,进入容器中去查看一下进程就能够看到,其余的也是这么设置的后端
第二个问题,滚动更新之健康检查的重要性
滚动更新是k8s的默认策略,通常我们部署到k8s以后第一个会使用到的,当你配置了健康检查时,滚动更新会根据probe的状态来判断是否是继续的更新容许接入流量,也就是你当前的应用是否是提供服务,这样你滚动更新的过程当中,才会确保你是否是有可用的节点,保证一个平滑的升级,因此这是滚动更新设置的一个之初,那健康状态检查是很重要的
健康状态检查在滚动更新启动什么做用呢?api
列入一个副本,启动以后提供业务须要一分钟才能提供服务,好比java启动比较慢,若是没有健康状态检查,来确保他是否是准备好,就直接认为它准备好了,这期间启动起来,一分钟以内它是没法提供服务的,因此新来的流量确定是没法处理的,这是第一种状况,第二种状况,因为人为的配置错误,好比链接不上数据库了,或者链接不上其余地方了,或者配置文件哪里写错了,那触发一个滚动更新,那这个pod呢所有滚动更新完成了,结果都是由问题的,这样的话,新副本都把旧副本替换掉了,这样的话,生产环境中后果很严重的,不少服务都会没法提供了,因此在配置滚动更新的时候,健康状态检查必定要配上,那配上了健康状态检查以后,新的副本检查完成以后才会转发新的流量,若是它没有经过它不会所有替换的,也就是它不会继续更新了,由于它是有一个可用节点的限制,若是可用节点达不到这个数,就不会继续更新,健康状态检查有两种类型,readinessprobe是就绪的检查,这两种检查方式也有不一样的方式,好比http,探测一个url,还有tcp socket,端口的探测,还有一个执行shell命令,执行一个shell命令,判断一个返回值,因此提供者三种健康状态检查的方法,readinessprobe就绪检查也就是你的Pod检查失败,若是是http,能够经过一个页面去探测,判断这个返回状态码,若是探测这个本地的端口不通的话,它不会让它加入service后面,由于service做为你整个的统一访问入口嘛,若是它不经过的话,新的流量也不会转发给它,这就是就绪检查,若是健康状态不经过不会给你转发新的流量,另外就是initialDelaySeconds:60,就是检查60秒,由于通常java应用启动也就是一分钟左右,还有一个periodSeconds:10,也就是没经过10秒钟在作一次,而后就是livenessProbe:存活检查。jvm
也就是检查失败它会杀死容器,根据你重启策略,通常是重建,再给你新拉取一个容器,再判断有没有准备好,判断的方法也是根据端口的探测这些的,或者特能够用其余两种方法,http,exec,因此通常这两个都要配置上,就绪检查呢就是不为你分配新的流量,存活检查就是去帮你从新拉取。socket
最后一个问题滚动更新之丢失的流量
通常就是链接拒绝,响应错误,调用不到
通常滚动更新是关闭现有的pod,再起一新的pod,关闭现有的实际上是就是删除了一个pod,而后apiserver会通知给kubelet,而后kubelet会关闭这个容器,而后从service后端摘掉,就不分发新的流量了,而后移除掉,而后再告诉kube-proxy,你能够处理新的转发规则了,而后调度到节点上,其实这也是一个pod的下线周期
另外再转发的过程当中,转发新的pod时间段里,它是有间隔的,关闭pod以后会有一个等待时间,在这个时间呢,可能还会接入一些新的流量,可是它的服务已经再也不处理新的请求了,因此会致使链接拒绝了,怎么去解决这个问题呢,实际上readiness探针在整个过程当中并起到关键的做用,一旦endpoint收到pod 的删除事件后,这已经就与readiness探测结果不相关了tcp
怎么保证它优雅的去处理呢?
其实之须要在关闭这个pod 时加个休眠的时间,其实就能够解决这个问题了,在关闭和启动都是有一个钩子存在的,全部能够在关闭容器前,执行这个钩子,钩子这个定义一个shell,y也能够定义一个http请求,也就是支持者两种类型,也就是在container同级,env这里
休眠5秒也就是你关闭的容器不会立刻退出,而后休眠5秒钟,再去关闭着应用,这5秒可以足够让kube-proxy刷新这个规则,这样的话,就不会将新加入的流量转发到这个刚关闭的pod上,增长这个钩子就能暂缓你关闭pod的时间,从而让kube-proxy增长刷新规则的时间,
添加ide
lifecycle : preStop : exec : command : - sh - -c - “sleep 5”
这样,你不须要修改你应用的代码,这样的话,滚动更新就不会转发即将关闭的pod上了,因此也能解决这个相关的问题了。