centos8使用docker_SpringBoot 项目在容器中使用优雅关闭

最近在研究微服务,研发有需求,就是在Pod消亡前,nacos是能实现服务优雅下线。找到这篇文章,分享下:

说明

  • 环境

工具版本

SpringBoot 2.3.3

Docker 19.03.12

Kubernetes 1.14

  • 背景

  服务端要支持 N 多个 Tcp Client 连接,所以做了负载,Tcp Client 会根据负载策略连接到不同的后端 Pod 上,这样就需要维护一个路由表:内部 ip <<==>> Tcp Client 的映射关系。所以在项目关闭的时候要有个关闭前处理(把当前 Pod 路由从路由表中去掉)的过程,也就是优雅关闭①。

SpringBoot 优雅关闭

  • 配置
server:  # 开启优雅关闭,默认:IMMEDIATE,立即关闭  shutdown: gracefulspring:  lifecycle:    # 配置优雅关闭宽限时间    timeout-per-shutdown-phase: 30s
  1. **server.shutdown:**配置是否开启优雅关闭。GRACEFUL:支持优雅关闭。IMMEDIATE:不支持优雅关闭,直接关闭。
  2. **spring.lifecycle.timeout-per-shutdown-phase:**优雅关闭最大容忍时间。比如这里设置 30s,那么就是在发起关闭请求②到真正关闭③项目之间,允许项目做未做完、关闭之前要做的事的最长时间为 30s,如果 30s 都没处理完,那么久强制关闭掉。

支持优雅关闭的服务器

Web Server开始优雅关闭之后新的接口访问行为说明tomcat 9.0.33+停止接收请求,客户端新请求等待超时。Reactor Netty停止接收请求,客户端新请求等待超时。Undertow停止接收请求,客户端新请求直接返回 503。

SpringBoot 结合 Docker 的优雅关闭

以下操作需本地安装好 Docker 并配置好 Idea。

  • SpringBoot 中引入以下插件
com.spotify    dockerfile-maven-plugin    1.4.10${docker.image.prefix}/${project.artifactId}${project.version}${project.build.finalName}.jartruebuild-imagepackagebuildpush-imageinstallpush
  • 项目根目录增加 Dockerfile 文件
# 这里可以使用你自己的镜像FROM ******/centos8:jdk14-baseARG JAR_FILECOPY target/$JAR_FILE app.jarRUN echo 'export LANG=zh_CN.utf8 && exec java $JAVA_OPTS -jar /app.jar' > docker-entrypoint.sh     && chmod +x /docker-entrypoint.shENTRYPOINT ["sh", "-c", "/docker-entrypoint.sh"]
  • 之后就可以 mvn clean package 即可

docker stop + -t 参数可以指定优雅关闭容忍时间。

问题

  这里有个问题要说一下,SpringBoot 项目开启了优雅关闭,这里 Docker 容器使用了 CentOS8,也就是说想要 SpringBoot 项目想要让自己的优雅关闭生效,其进程就需要接收到系统发送的 SIGINT(kill -2)/SIGTERM(kill -15) 信号,然后执行优雅关闭流程。

  一般我们配置 ENTRYPOINT、CMD 时会有这样几种配置方式。

ENTRYPOINT ["java", "-jar", "/app.jar"]1
ENTRYPOINT ["sh", "-c", "/docker-entrypoint.sh"]1

  前者不能加动态参数,后者倒是可以,但是如果直接在脚本中这样调用:java -jar $OPTION /app.jar 是可以加动态参数,但是无法达到优雅关闭的目的。总结来说:前者可以不能加动态参数但是可以优雅关闭,后者可以加动态参数但是不能优雅关闭

  究其原因是在使用 docker stop container_id 命令时,Docker 会把 SIGTERM 信号传递给容器内 pid 为 1 的进程。前者启动方式,java 进程的 pid 是为 1 的,后者启动 sh 的进程是为 1 的。也在网上找了不少资料。看了如下这篇:

  https://my.oschina.net/u/2552286/blog/3039592

  结果并不好,没有效果,也可能没配置到位。后又在官方找到了这种解决方法,完美解决:

  https://spring.io/guides/topicals/spring-boot-docker

20290e43bf93b6c4cad2fad3fa22df02.png

SpringBoot 结合 Kubernetes 的优雅关闭

terminationGracePeriodSeconds

  一旦 Kubernetes 决定终止您的 Pod,就会发生一系列事件。让我们看看 Kubernetes 终止生命周期的每一步。

  1. Pod 设置为 Terminating 状态,并从所有服务的 Endpoints 列表中删除。此时,Pod 停止获得新的流量。但在 Pod中 运行的容器不会受到影响。
  2. preStop Hook 被执行(如果设置了)。preStop Hook 是一个发送到 Pod 中的容器特殊命令或 Http 请求。如果您的应用程序在接收 SIGTERM 时没有正常关闭,您可以使用 preStop Hook 来触发正常关闭。接收 SIGTERM 时大多数程序都会正常关闭,但如果您使用的是第三方代码或管理的系统无法控制,则 preStop Hook 是在不修改应用程序的情况下触发正常关闭的好方法。
  3. SIGTERM 信号被发送到 Pod,此时,Kubernetes 将向 pod 中的容器发送 SIGTERM 信号。这个信号让容器知道它们很快就会关闭。您的代码应该监听此事件并在此时开始干净利落关闭。这可能包括停止任何长期连接(如数据库连接或 WebSocket 流),保存当前状态或其它类似的事情。即使您使用 preStop Hook,如果您发送 SIGTERM 信号,测试应用程序会发生什么情况也很重要,以确保您对生产环境并不感到惊讶!
  4. Kubernetes 等待优雅的终止,Kubernetes 等待指定的时间称为优雅终止宽限期。默认情况下,这是 30 秒。值得注意的是,这与 preStop Hook 和 SIGTERM 信号并行发生。Kubernetes 不会等待 preStop Hook 完成。如果你的应用程序完成关闭并在 terminationGracePeriod 完成之前退出,Kubernetes 会立即进入下一步。如果您的 Pod 通常需要超过 30 秒才能关闭,请确保增加优雅终止宽限期。您可以通过在 Pod YAML 中设置 terminationGracePeriodSeconds ④选项来实现。
  5. SIGKILL 信号被发送到 Pod,并删除 Pod 如果容器在优雅终止宽限期后仍在运行,则会发送 SIGKILL 信号并强制删除。与此同时,所有的 Kubernetes 对象也会被清除。
  • 结论:Kubernetes 可以出于各种原因终止 Pod,并确保您的应用程序优雅地处理这些终止,这是创建稳定系统和提供出色用户体验的核心。
  • 注意 1:Kubernetes 文档指出,有些步骤是同时执行的(步骤 1,2,3)。因此有可能会导致该 Pod 仍然列在服务的 Endpoints 中并仍然接收流量,而它已经收到 SIGTERM 并且已经停止,因此负载均衡器上可能会有一些 Http 504。目前解决这个问题可以使用 preStop Hook 在容器收到 SIGTERM 时 sleep 一段时间,以确终止期间的流量可以正确处理。设置方式:
apiVersion: v1kind: Podmetadata:  name: my-podspec:  containers:  - name: my-container    image: busybox    lifecycle:      preStop:        exec:          command:          - sleep          - 30  terminationGracePeriodSeconds: 60
  • 注意 2:preStop Hook 并不会影响 SIGTERM 的处理,因此有可能 preStopHook 还没有执行完就收到 SIGKILL 导致容器强制退出。因此如果 preStop Hook 设置了 n 秒,需要设置 terminationGracePeriodSeconds 为 terminationGracePeriodSeconds + n 秒。

注解

①:**优雅关闭:**就是在真正关闭项目之前,把未做完、关闭之前要做的事执行完毕。
②:**发起关闭请求:**Idea 中点击停止项目请求、Docker 中使用 docker stop container_id、Linux 中使用 kill -2 pid 或者 kill -15 pid、Kubernetes 中删除旧 Pod(滚动更新、删除 Pod等)。
③:**真正关闭:**项目进程已经被被关闭掉了,没了。比如:② 中的执行结果、kill -9 pid。
④:**在 Pod YAML 中设置 terminationGracePeriodSeconds 选项:**比如你可以设置 60 秒。

apiVersion: v1kind: Podmetadata:  name: my-podspec:  containers:  - name: my-container    image: busybox  terminationGracePeriodSeconds: 60
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用 Jenkins 和 Docker 进行 CentOS 部署 Spring Boot 项目的详细步骤和命令: 1. 在 CentOS 安装 DockerDocker Compose ```bash # 安装 Docker sudo yum update sudo yum install docker # 安装 Docker Compose sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose ``` 2. 在 CentOS 拉取 Docker 镜像 ```bash # 从 Docker 仓库拉取镜像 sudo docker pull your-docker-registry/image-name:tag ``` 3. 在 CentOS 运行 Spring Boot 镜像 ```bash # 运行 Spring Boot 镜像 sudo docker run -d -p 8080:8080 your-docker-registry/image-name:tag # 如果你的 Spring Boot 应用需要使用数据库,你需要为其配置一个数据库服务。你可以使用 Docker Compose 来管理容器编排。 # 在服务器上创建一个文件夹来存储 Docker Compose 配置文件 mkdir ~/docker-compose # 在该文件夹创建一个 docker-compose.yml 文件 nano ~/docker-compose/docker-compose.yml # 编辑该文件并添加以下内容: version: "3" services: app: image: your-docker-registry/image-name:tag ports: - "8080:8080" db: image: postgres environment: POSTGRES_USER: your-db-user POSTGRES_PASSWORD: your-db-password POSTGRES_DB: your-db-name # 启动 Docker Compose cd ~/docker-compose sudo docker-compose up -d # 如果你需要停止 Docker Compose sudo docker-compose down ``` 4. 在 Jenkins 配置 Maven 插件和 Docker 插件 - 在 Jenkins 安装 Maven 插件和 Docker 插件。 - 在 Jenkins 配置 Docker 插件,确保 Jenkins 可以与 Docker 交互。 5. 在 Jenkins 创建一个新的构建项目 - 在 Jenkins 创建一个新的 Maven 项目,并配置 Git 代码库以获取源代码。 - 在构建项目配置 Maven 构建步骤,构建出可执行的 Spring Boot jar 包。 - 在构建项目添加一个后置操作步骤,使用 Docker 插件进行 Docker 镜像的构建。你需要为 Docker 镜像指定一个名称和标签,并指定构建上下文。 - 在构建项目添加另一个后置操作步骤,使用 Docker 插件将构建好的镜像推送到 Docker 仓库。 6. 在目标服务器上使用 Docker 运行 Spring Boot 镜像 - 在目标服务器上拉取构建好的 Spring Boot 镜像。 - 在目标服务器上使用 Docker 运行 Spring Boot 镜像,你可以使用 Docker Compose 或 Docker Stack 进行容器编排和管理。 以上是使用 Jenkins 和 Docker 进行 CentOS 部署 Spring Boot 项目的详细步骤和命令,希望对你有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值