k8s环境下spring cloud优雅停机

目的

 Spring cloud 微服务、k8s容器化部署的架构下,单个服务升级过程中,不停止对外服务,降低对用户的影响。从而实现优雅停机。

实现

负载均衡器

  1. spring cloud ribbon(k8s同namespace):k8s同一命名空间之间的服务调用,采用ribbon、eureka做服务注册和负载均衡。

     问题:

        a、ribbon和eureka(CP)注重服务的可用性,所以存在服务实例的缓存,本地缓存:(1)ribbon本地缓存 (2)eureka client本地缓存 。远程缓存:(1)eureka server 三级缓存

        b、service 提供者停止,pod 向容器发送SIGTERM信号,service 提供者会向eureka server下线服务,这个时候service消费者会存在原有service的实例信息

  2. k8s  service (k8s不同namespce):  k8s不同命名空间之间的服务调用,采用k8s service、kube-proxy做服务注册和负载均衡。

     问题:k8s service 支持通过服务的负载均衡,通过livenessProbe、只有容器探针检测成功后,才会停止旧的容器Service A。两种探针实现RollingUpdate。前提要存在多个实例

基于spring cloud 负载均衡的优雅停机

  针对spring cloud负载均衡存在的问题,要做到优雅停机,需要进行如下配置:

 a、缩短缓存时间

   1. 修改Client配置(ribbon和eureka client)

 1 ribbon:
 2   # ribbon刷新eureka的时间5s, 默认30s
 3   ServerListRefreshInterval: 5000
 4 #注册中心
 5 eureka:
 6   client:
 7     serviceUrl:
 8       defaultZone: http://eureka-server:8761/eureka
 9     # eureka客户端需要多长时间发定时刷新本地缓存时间,默认30s
10     registry-fetch-interval-seconds: 5
11   instance:
12     prefer-ip-address: true
13     instance-id: ${spring.cloud.client.ipAddress}:${server.port}
14     # eureka客户端需要多长时间发送心跳给eureka服务器,表明他仍然活着,默认30秒
15     lease-renewal-interval-in-seconds: 5
16     # eureka服务器在接受到实力的最后一次发出的心跳后,需要等待多久才可以将此实例删除 默认90秒
17     lease-expiration-duration-in-seconds: 15

    缩短ribbon本地缓存时间,eureka client拉取注册表、心跳间隔时间 

   2. 修Server配置(eureka server)

 1 eureka:
 2   server:
 3     # 禁用readOnlyCacheMap
 4     useReadOnlyResponseCache: false
 5     # 主动失效检测间隔, 默认60s
 6     evictionIntervalTimerInMs: 5000
 7   instance:
 8     # eureka服务器的标识,如果是集群就可以写成 eurekaSer1,eurekaSer2,eurekaSer3..
 9     hostname: eureka-server
10     health-check-url-path: /actuator/health
11   client:
12     # 开启客户端存活状态监测
13     healthcheck:
14       enabled: true
15     registerWithEureka: false
16     fetchRegistry: false

  缩短失效检测的实现,同时禁用readOnlye缓存。 eureka server 存在三级缓存 ,均是纯内存操作,详细见下图

(1)registry(ConcurrentMap):注册表
(2)readWriteCacheMap(GuavaCache):读写缓存
(3)readOnlyCacheMap(ConcurrentMap):只读缓存

 
b、Service先下线,再停机
1、增加Service 下线端点,阻塞到缓存更新完成,可参考spring actuator实现
 1 @ConfigurationProperties(prefix = "endpoints.offline")
 2 public class OfflineEndpoint extends AbstractEndpoint<String> {
 3 
 4     public OfflineEndpoint() {
 5         super("offline", false);
 6     }
 7 
 8 
 9     @Override
10     public String invoke() {
11         String remoteIp = IPUtils.getIp();
12         String localIp = IPUtils.getLocalIp();
13         log.info("remoteIp:{} trigger service offline", remoteIp);
14         if(localIp.substring(0, localIp.lastIndexOf(".")).equals(remoteIp.substring(0, remoteIp.lastIndexOf(".")))) {
15             log.info("service start execute offline");
16             DiscoveryManager.getInstance().shutdownComponent();
17             try {
18                 Thread.sleep(10000);
19             } catch (InterruptedException e) {
20                 log.error("sleep exception",e);
21                 throw new RuntimeException(e);
22             }
23             log.info("service end execute offline");
24             return "SUCCESS";
25         } else {
26             log.warn("remoteIp:{} localIp:{} not trigger service ll_offline", remoteIp, localIp);
27             return "NOT_ALLOW";
28         }
29 
30     }
31 }
View Code

    2、利用pod preStop hook,触发服务停止前,主动下线

1  lifecycle:
2           preStop:
3             httpGet:
4               port: 9494 
5               scheme: HTTP
6               path: offline

 c、流程总结

   针对容器化RollingUpdate,Service 旧版本A,新版本A1发布流程如下:

   1、新版本容器Service A1,而在Service A1启动后就会注册到Eureka Server,

   2、Service A1,只有容器探针检测成功后,才会停止旧的容器Service A。所以此时 Service A1 和 Service A都注册在 Eureka Server上,接受流量

   3、Service A1,容器探测成功,下线Service A

   4、Service A 触发 preStop的offline操作,将Service A 从Eureka Server中下线,直至缓存更新完成

   5、Service A1 完成本次滚动更新

基于k8s service 负载均衡的优雅停机

   k8s Deployment本身就支持Rolling update, 但容器启动成功并不会注册到service,只有需要容器readinessPro成功后,才会注册到Service。

   所以这里优雅停机,需要先扩容到2个实例,然后再缩容到1个实例,实现单服务的滚动更新

     



         

猜你喜欢

转载自www.cnblogs.com/mxmbk/p/12705014.html
今日推荐