zoukankan      html  css  js  c++  java
  • spring cloud之Eureka不能注销docker部署的实例

    1 起因

    事件的起因是这样的,我们在微服务改造的过程中,选择将服务注册到eureka中,开发的时候还好,使用场景是这样的:

    • 在idea中启动服务,成功注册到eureka,关闭服务,eureka成功注销该服务实例
    • java -jar方式启动服务,成功注册到eureka中,ctrl-c停止服务,eureka成功注销该服务实例

    有一天,在服务器上部署服务的时候,我们选择了docker启所有的服务,预料之外的事情发生了:

    docker run 服务成功注册到eureka,docker stop之后,eureka却没有将该服务实例注销, 那就docker rm 将容器删除,eureka中这个服务实例还是存在,这样每docker run一下,eureka中就会多注册一个实例,就多了好多僵尸实例。

    eureka有问题?还是docker启服务有问题?

    我docker容器都干掉了,eureka中还存在这有点儿说不过去吧

    2 手动下线

    eureka不能自动给下线我就手动delete一下吧

    DELETE http://{ip}:{port}/eureka/apps/{appName}/{inatanceId}

    3 改实例ID

    但是这样就是你每重启一下就得删一下,很麻烦。发现容器启的实例id都有个特点,containerId:服务名称:服务端口号,物理机上启的主机ip:服务名称:服务端口号

    那就eureka.instance.instance-id=${spring.application.name}:${server.port}定制一下实例id,这样docker run的服务端口相同的话,实例id就会相同,比如都是rms:8080,这样docker run多少次也不会有那么多僵尸实例了,后起的会把前边儿启的同id实例挤掉。

    但是这也并没有从实质上解决自动下线问题,到底是为什么呢?

    4 优雅的停止容器

    直到看到一个知识点儿你真的优雅的停止容器了吗?不就是docker stop吗,其实:

    当你发出Docker stop命令时,Docker会很好地要求进程停止,如果进程在10秒内没有关闭,它将强制终止进程。

    docker stop命令首先尝试通过向容器中的根进程(pid 1)发送sigterm信号来停止正在运行的容器。如果进程在超时期间没有退出,则发送SIGKID信号。

    虽然进程可以选择忽略sigterm,但sigkill直接进入内核,内核将终止进程。这个过程根本看不到信号。

    使用Docker Stop时,唯一可以控制的是Docker守护进程在发送sigkill之前等待的秒数:docker stop --time=30 foo

    这才慌然大悟,启容器,进入容器,杀进程:

    这里如果kill -9 同样不能正常下线,原因看图,-9 也是发送的SIGKILL

    忽然想到java -jar 方式起的服务可以正常注册到eureka,ctrl-c可以正常停止进程使eureka成功注销该实例,如果我nohup java -jar启的服务,就不能ctrl-c了

    只能kill掉进程了,贯用的kill -9 pid,发现这样杀死的服务实例,确实eureka中也没有下线。这样就与docker stop服务实例不能正常注销的原因一致了。

    看来不能高兴的太早,发现docker stop指定时间不好使:

    docker stop --time=80 rms-consul
    

    等呀等,真的等了80s强制退出,eureka并没有注销该实例,感觉这种方式不太好把握进程停止真正需要的时间,也可能这种方式就是无效。

    到底应该如何优雅的停止容器?https://www.ctl.io/developers/blog/post/gracefully-stopping-docker-containers/

    试了一圈还是这好使:

    5 docker 不能正常停止服务是因为没有正确的开始

    说好的优雅停止容器,为啥还是不能优雅的解决问题呢?到目前为止其实就是正常的kill解决了问题,继续找原因。找到dockerfile

    ENTRYPOINT [ "sh", "-c", "/start.sh"]
    $ cat start.sh  
    java -jar /apps/rms.jar
    

    这个问题是shell script接收SIGTERM信号,而没有发送给通过shell脚本生成的java进程,因此spring boot无法正常退出。

    解决办法:

    通过运行exec命令,它将代替shell进程把SIGTERM传播到spring boot。

    ENTRYPOINT [ "sh", "-c", "exec java -jar /apps/rms.jar"]
    

    再次

    docker build...
    docker run...
    docker stop rms
    

    居然成功了,eureka也能成功注销该容器实例了

    再试一下上边儿不生效的优雅停止容器方式也成功了,如:

    docker kill -s SIGTERM rms
    

    还是蛮优雅的嘛。

    其实说来说去,就是只要能让spring boot优雅退出,eureka就能让该实例优雅注销。

    另外spring cloud consul也是如此。

    The miracle is this--the more we share, the more we have
  • 相关阅读:
    Python3中zipfile模块文件名乱码问题
    python zipfile 文件压缩和文件
    django+celery+rabitmq
    解决 Jumpserver coco 使用登录用户(ldap)进行SSH连接目标主机,忽略系统用户
    日志实时查看、轮询、统计、监控工具 Loggrove
    python django 多级业务树形结构规划及页面渲染
    nginx+uwsgi 部署 django
    python tornado websocket 实时日志展示
    python tornado websocket 多聊天室(返回消息给部分连接者)
    python 实时遍历日志文件
  • 原文地址:https://www.cnblogs.com/dreamfly2016/p/11698759.html
Copyright © 2011-2022 走看看