Spring Boot + K8S 中的滚动发布、优雅停机、弹性伸缩、应用监控、配置分离
前言
K8s + SpringBoot实现零宕机发布:健康检查+滚动更新+优雅停机+弹性伸缩+Prometheus监控+配置分离(镜像复用)
配置
健康检查
健康检查类型:就绪探针(readiness)+ 存活探针(liveness)
探针类型:exec(进入容器执行脚本)、tcpSocket(探测端口)、httpGet(调用接口)
业务层面
项目依赖 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
定义访问端口、路径及权限 application.yaml
management:
server:
port: 50000 # 启用独立运维端口
endpoint: # 开启health端点
health:
probes:
enabled: true
endpoints:
web:
exposure:
base-path: /actuator # 指定上下文路径,启用相应端点
include: health
将暴露/actuator/health/readiness和/actuator/health/liveness两个接口,访问方式如下:
http://127.0.0.1:50000/actuator/health/readiness
http://127.0.0.1:50000/actuator/health/liveness
运维层面
k8s部署模版deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: {APP_NAME}
image: {IMAGE_URL}
imagePullPolicy: Always
ports:
- containerPort: {APP_PORT}
- name: management-port
containerPort: 50000 # 应用管理端口
readinessProbe: # 就绪探针
httpGet:
path: /actuator/health/readiness
port: management-port
initialDelaySeconds: 30 # 延迟加载时间
periodSeconds: 10 # 重试时间间隔
timeoutSeconds: 1 # 超时时间设置
successThreshold: 1 # 健康阈值
failureThreshold: 6 # 不健康阈值
livenessProbe: # 存活探针
httpGet:
path: /actuator/health/liveness
port: management-port
initialDelaySeconds: 30 # 延迟加载时间
periodSeconds: 10 # 重试时间间隔
timeoutSeconds: 1 # 超时时间设置
successThreshold: 1 # 健康阈值
failureThreshold: 6 # 不健康阈值
滚动更新
k8s资源调度之滚动更新策略,若要实现零宕机发布,需支持健康检查
apiVersion: apps/v1
kind: Deployment
metadata:
name: {APP_NAME}
labels:
app: {APP_NAME}
spec:
selector:
matchLabels:
app: {APP_NAME}
replicas: {REPLICAS} # Pod副本数
strategy:
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 1 # 升级过程中最多可以比原先设置的副本数多出的数量
maxUnavailable: 1 # 升级过程中最多有多少个POD处于无法提供服务的状态
优雅停机
在K8s中,当我们实现滚动升级之前,务必要实现应用级别的优雅停机。否则滚动升级时,还是会影响到业务。使应用关闭线程、释放连接资源后再停止服务
业务层面
项目依赖 pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
定义访问端口、路径及权限 application.yaml
spring:
application:
name: <xxx>
profiles:
active: @profileActive@
lifecycle:
timeout-per-shutdown-phase: 30s # 停机过程超时时长设置30s,超过30s,直接停机
server:
port: 8080
shutdown: graceful # 默认为IMMEDIATE,表示立即关机;GRACEFUL表示优雅关机
management:
server:
port: 50000 # 启用独立运维端口
endpoint: # 开启shutdown和health端点
shutdown:
enabled: true
health:
probes:
enabled: true
endpoints:
web:
exposure:
base-path: /actuator # 指定上下文路径,启用相应端点
include: health,shutdown
将暴露/actuator/shutdown接口,调用方式如下:
curl -X POST 127.0.0.1:50000/actuator/shutdown
运维层面
确保dockerfile模版集成curl工具,否则无法使用curl命令
FROM openjdk:8-jdk-alpine
#构建参数
ARG JAR_FILE
ARG WORK_PATH="/app"
ARG EXPOSE_PORT=8080
#环境变量
ENV JAVA_OPTS=""\
JAR_FILE=${JAR_FILE}
#设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \
&& apk add --no-cache curl
#将maven目录的jar包拷贝到docker中,并命名为for_docker.jar
COPY target/$JAR_FILE $WORK_PATH/
#设置工作目录
WORKDIR $WORK_PATH
# 指定于外界交互的端口
EXPOSE $EXPOSE_PORT
# 配置容器,使其可执行化
ENTRYPOINT exec java $JAVA_OPTS -jar $JAR_FILE
k8s部署模版deployment.yaml
注:经验证,java项目可省略结束回调钩子的配置
此外,若需使用回调钩子,需保证镜像中包含curl工具,且需注意应用管理端口(50000)不能暴露到公网
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
- name: {APP_NAME}
image: {IMAGE_URL}
imagePullPolicy: Always
ports:
- containerPort: {APP_PORT}
- containerPort: 50000
lifecycle:
preStop: # 结束回调钩子
exec:
command: ["curl", "-XPOST", "127.0.0.1:50000/actuator/shutdown"]
弹性伸缩
为pod设置资源限制后,创建HPA
apiVersion: apps/v1
kind: Deployment
metadata:
name: {APP_NAME}
labels:
app: {APP_NAME}
spec:
template:
spec:
containers:
- name: {APP_NAME}
image: {IMAGE_URL}
imagePullPolicy: Always
resources: # 容器资源管理
limits: # 资源限制(监控使用情况)
cpu: 0.5
memory: 1Gi
requests: # 最小可用资源(灵活调度)
cpu: 0.15
memory: 300Mi
---
kind: HorizontalPodAutoscaler # 弹性伸缩控制器
apiVersion: autoscaling/v2beta2
metadata:
name: {APP_NAME}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {APP_NAME}
minReplicas: {REPLICAS} # 缩放范围
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu # 指定资源指标
target:
type: Utilization
averageUtilization: 50
Prometheus集成
业务层面
项目依赖 pom.xml
<!-- 引入Spring boot的监控机制-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
定义访问端口、路径及权限 application.yaml
management:
server:
port: 50000 # 启用独立运维端口
metrics:
tags:
application: ${spring.application.name}
endpoints:
web:
exposure:
base-path: /actuator # 指定上下文路径,启用相应端点
include: metrics,prometheus
将暴露/actuator/metric和/actuator/prometheus接口,访问方式如下:
http://127.0.0.1:50000/actuator/metric
http://127.0.0.1:50000/actuator/prometheus
运维层面
deployment.yaml
apiVersion: apps/v1
kind: Deployment
spec:
template:
metadata:
annotations:
prometheus:io/port: "50000"
prometheus.io/path: /actuator/prometheus # 在流水线中赋值
prometheus.io/scrape: "true" # 基于pod的服务发现
配置分离
方案:通过configmap挂载外部配置文件,并指定激活环境运行
作用:配置分离,避免敏感信息泄露;镜像复用,提高交付效率
通过文件生成configmap
# 通过dry-run的方式生成yaml文件
kubectl create cm -n <namespace> <APP_NAME> --from-file=application-test.yaml --dry-run=1 -oyaml > configmap.yaml
# 更新
kubectl apply -f configmap.yaml
挂载configmap并指定激活环境
apiVersion: apps/v1
kind: Deployment
metadata:
name: {APP_NAME}
labels:
app: {APP_NAME}
spec:
template:
spec:
containers:
- name: {APP_NAME}
image: {IMAGE_URL}
imagePullPolicy: Always
env:
- name: SPRING_PROFILES_ACTIVE # 指定激活环境
value: test
volumeMounts: # 挂载configmap
- name: conf
mountPath: "/app/config" # 与Dockerfile中工作目录一致
readOnly: true
volumes:
- name: conf
configMap:
name: {APP_NAME}
汇总配置
业务层面
项目依赖 pom.xml
<!-- 引入Spring boot的监控机制-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
定义访问端口、路径及权限 application.yaml
spring:
application:
name: project-sample
profiles:
active: @profileActive@
lifecycle:
timeout-per-shutdown-phase: 30s # 停机过程超时时长设置30s,超过30s,直接停机
server:
port: 8080
shutdown: graceful # 默认为IMMEDIATE,表示立即关机;GRACEFUL表示优雅关机
management:
server:
port: 50000 # 启用独立运维端口
metrics:
tags:
application: ${spring.application.name}
endpoint: # 开启shutdown和health端点
shutdown:
enabled: true
health:
probes:
enabled: true
endpoints:
web:
exposure:
base-path: /actuator # 指定上下文路径,启用相应端点
include: health,shutdown,metrics,prometheus
运维层面
确保dockerfile模版集成curl工具,否则无法使用curl命令
FROM openjdk:8-jdk-alpine
#构建参数
ARG JAR_FILE
ARG WORK_PATH="/app"
ARG EXPOSE_PORT=8080
#环境变量
ENV JAVA_OPTS=""\
JAR_FILE=${JAR_FILE}
#设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezone
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories \
&& apk add --no-cache curl
#将maven目录的jar包拷贝到docker中,并命名为for_docker.jar
COPY target/$JAR_FILE $WORK_PATH/
#设置工作目录
WORKDIR $WORK_PATH
# 指定于外界交互的端口
EXPOSE $EXPOSE_PORT
# 配置容器,使其可执行化
ENTRYPOINT exec java $JAVA_OPTS -jar $JAR_FILE
k8s部署模版deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {APP_NAME}
labels:
app: {APP_NAME}
spec:
selector:
matchLabels:
app: {APP_NAME}
replicas: {REPLICAS} # Pod副本数
strategy:
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
name: {APP_NAME}
labels:
app: {APP_NAME}
annotations:
timestamp: {TIMESTAMP}
prometheus.io/port: "50000" # 不能动态赋值
prometheus.io/path: /actuator/prometheus
prometheus.io/scrape: "true" # 基于pod的服务发现
spec:
affinity: # 设置调度策略,采取多主机/多可用区部署
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- {APP_NAME}
topologyKey: "kubernetes.io/hostname" # 多可用区为"topology.kubernetes.io/zone"
terminationGracePeriodSeconds: 30 # 优雅终止宽限期
containers:
- name: {APP_NAME}
image: {IMAGE_URL}
imagePullPolicy: Always
ports:
- containerPort: {APP_PORT}
- name: management-port
containerPort: 50000 # 应用管理端口
readinessProbe: # 就绪探针
httpGet:
path: /actuator/health/readiness
port: management-port
initialDelaySeconds: 30 # 延迟加载时间
periodSeconds: 10 # 重试时间间隔
timeoutSeconds: 1 # 超时时间设置
successThreshold: 1 # 健康阈值
failureThreshold: 9 # 不健康阈值
livenessProbe: # 存活探针
httpGet:
path: /actuator/health/liveness
port: management-port
initialDelaySeconds: 30 # 延迟加载时间
periodSeconds: 10 # 重试时间间隔
timeoutSeconds: 1 # 超时时间设置
successThreshold: 1 # 健康阈值
failureThreshold: 6 # 不健康阈值
resources: # 容器资源管理
limits: # 资源限制(监控使用情况)
cpu: 0.5
memory: 1Gi
requests: # 最小可用资源(灵活调度)
cpu: 0.1
memory: 200Mi
env:
- name: TZ
value: Asia/Shanghai
---
kind: HorizontalPodAutoscaler # 弹性伸缩控制器
apiVersion: autoscaling/v2beta2
metadata:
name: {APP_NAME}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {APP_NAME}
minReplicas: {REPLICAS} # 缩放范围
maxReplicas: 6
metrics:
- type: Resource
resource:
name: cpu # 指定资源指标
target:
type: Utilization
averageUtilization: 50
Spring Boot + K8S 中的滚动发布、优雅停机、弹性伸缩、应用监控、配置分离的更多相关文章
- Spring Boot 2.3.0正式发布:优雅停机、配置文件位置通配符新特性一览
当大潮退去,才知道谁在裸泳..关注公众号[BAT的乌托邦]开启专栏式学习,拒绝浅尝辄止.本文 https://www.yourbatman.cn 已收录,里面一并有Spring技术栈.MyBatis. ...
- (转)Spring Boot 2 (九):【重磅】Spring Boot 2.1.0 权威发布
http://www.ityouknow.com/springboot/2018/11/03/spring-boot-2.1.html 如果这两天登录 https://start.spring.io/ ...
- 【重磅】Spring Boot 2.1.0 权威发布
如果这两天登录 https://start.spring.io/ 就会发现,Spring Boot 默认版本已经升到了 2.1.0.这是因为 Spring Boot 刚刚发布了 2.1.0 版本,我们 ...
- 在Spring Boot项目中使用Spock框架
转载:https://www.jianshu.com/p/f1e354d382cd Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring B ...
- 在Spring Boot项目中使用Spock测试框架
本文首发于个人网站:在Spring Boot项目中使用Spock测试框架 Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring Boot项目 ...
- Spring Boot 2 中的默认日志管理与 Logback 配置详解
Spring Boot在所有内部日志中使用Commons Logging,但是对底层日志的实现是开放的.在Spring Boot生态中,为Java Util Logging .Log4J2 和Logb ...
- 在spring boot环境中使用fastjson + redis的高速缓存技术
因为项目需求,需要在spring boot环境中使用redis作数据缓存.之前的解决方案是参考的http://wiselyman.iteye.com/blog/2184884,具体使用的是Jackso ...
- 你真的理解 Spring Boot 项目中的 parent 吗?
前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...
- Spring Boot项目中使用Swagger2
Swagger2是一款restful接口文档在线生成和在线接口调试工具,Swagger2在Swagger1.x版本的基础上做了些改进,下面是在一个Spring Boot项目中引入Swagger2的简要 ...
- spring boot JPA中实体类常用注解
spring boot jpa中的注解很多,参数也比较多.没必要全部记住,但是经常查看官方文档也比较麻烦,记录一下一些常用的注解.通过一些具体的例子来帮助记忆. @Entity @Table(name ...
随机推荐
- OpenGL和OpenCL区别
1.OpenGL用于图形渲染程序:OpenCL用于复杂的计算.他们都是由Khronos管理并使用C语言编译. 2.OpneGL使编程能够进行图形操作:OpenCL使编程能够在多个处理器中进行计算. 3 ...
- 墨天轮PostgreSQL精品学习资源合集(含基础手册、实操技巧&案例、书籍推荐)
近日,PostgreSQL 15 的第一个 beta 版本发布,这一最新版本在开发者体验.性能表现等方面都有提升.从最新的DB-Engines排名可以发现,PostgreSQL近十年来得分一路高涨,目 ...
- 59 张高清大图,带你实战入门 KubeSphere DevOps
作者:运维有术星主 KubeSphere 基于 Jenkins 的 DevOps 系统是专为 Kubernetes 中的 CI/CD 工作流设计的,它提供了一站式的解决方案,帮助开发和运维团队用非常简 ...
- es之增删改查
查询 index: GET task_results/_search/ 普通查询: {"query":{"bool":{"must":[{& ...
- go: 在proto中使用oneof类型
在proto中,可以使用OneOf类型,使用一个字段存储不同类型的数据.类似go中的interface. 假设有proto如下,Val是一个OneOf数据类型,它可以为double/int/str.. ...
- 分享一个大模型在请求api接口上的巧用
前言 自从Chatgpt横空出世以来,各种智能工具层出不穷,聊天.绘画.视频等各种工具帮助很多人高效的工作.作为一个开发者,目前常用应用包括代码自动填充,聊天助手等. 这些是工具层面的使用,有没有将大 ...
- 快速上手web前端开发(超详细教程)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 第一步 下安装vscode 第二步 vscode汉化 第三步 安装前端所需插件 1.Live Server 2.Jav ...
- Mysql导出文本文件
使用mysqldump命令导出文本文件 mysqldump -u root -pPassword -T 目标目录 dbname [tables] [option]; 其中: Password 参数表示 ...
- 01_GoLand debug时出现Connected并且程序卡住的问题
01_GoLand debug时出现Connected并且程序卡住的问题 环境:win10.go version go1.19.4 windows/amd64.GoLand 2020.3.5 x64 ...
- jquery datatable 全选,反选 参考文档
1 版本号 DataTables 1.10.12 2 API文档地址 http://datatables.club/reference/option/ 官方option http://datatabl ...