本文目录一览:
- 1、springboot2.X使用k8s的configmap
- 2、Jenkins-配置K8S负载
- 3、Kubernetes 的REST API指的是什么?
- 4、给 运行在 k8s 里的 springboot 指定 jvm 参数
- 5、开发和运维对K8S中的应用都做了什么?
springboot2.X使用k8s的configmap
处于项目需要使用kubernetes 的configmap作为配置中心。
这里的 app-config 是configmap 的名称可根据不同项目不同环境来自定义如: test-app-main-config 或者 test-redis-config 等等。
创建完成后 使用如下命令查看k8s中的configmap 我这里查看的默认 namespace 下的cm 。
使用下列命令查看 configmap中的 内容
会把指定文件夹下的 文件都存入configmap中,这里不展开赘述
这里的 --from-literal 可以指定多个 key-value 键值对
创建的配置到此创建完毕。接下来说重点
配置完成后 reimport 一下
在springboot项目 -- resources 文件下创建 bootstrap.yml 文件 如果有其他配置文件建议都移动到 自己新建一个文件下面
文件内容如下
这里的 @ConfigurationProperties(prefix = "common.redis") 需要注意 common.redis 这个东西是配置文件中读取的前缀。需要更具自己的项目所用的配置来定义
如propertes 文件
创建RedisConfigProperties.java
自行写个controller 来测试,当然我这边也提供一个 方便大家复制粘贴
至此springboot 中获取 configmap主要配置结束。本地如果有k8s环境则可以直接启动访 上面controller的连接来验证是否加载了配置。
注意 如果使用 @Vaule 注解来获取配置 时不会在configmap有变化的时候 获取到变化的值
这个配置到算简单。重点是在你的springboot项目部署到 pod 中时 会提示无法访问 configmap 。是因为在k8s集群环境中 创建的 pod 默认使用的是 RBAC 模式授权 ,使用的是default的权限,然而 default 权限无法在pod内访问到 APIservice 开放的 restful 接口。
思路:创建 RBAC 模式的 角色 和权限
创建 config-reader-role.yml 文件 。
文件内容如下:
创建文件完成后直接执行如下命令:
创建完成后需要在我们创建部署的 deployment.yml 文件中指定 使用 config-reader 的权限 。在下面节点
spec-- template-- spec 添加 serviceAccountName: config-reader
下面贴出 deployment.yml 文件内容:
如此依赖 启动的 pod 内 springboot项目就能获取到configmap 的配置文件了。
Jenkins-配置K8S负载
随着Jenkins被大量使用,单台打包机完全无法应对打包慢,线程不够等一系列问题
这时候可以添加固定Node的方式去解决问题,但是慢慢你会发现固定Node也无法解决问题,如果并不是超级多的打包任务,或者根本没得使用K8S的这个条件请移步 Jenkins配置从节点 来解决单台打包机性能不够的问题
其中最主要的矛盾如下
1.不同的打包环境指定不同的打包机
2.集中在一个时间段打包,特别是在发版本的前夕,所有项目组都在打包,显得特别无力
3.大部分时间闲置的打包负载显得有点浪费
这时候就急需一个可以动态缩放的Node来解决这个问题。
将K8S作为Jenkins负载就是为了解决动态缩放,不同环境需要指定不同打包机的问题(Windows和Mac还是需要单独处理)。
官网文档 在 Kubernetes 上扩展 Jenkins
首先在Jenkins中搜索 Kubernetes 这个插件,安装上
配置K8S荷载
其中这个Kubernetes地址就是K8S-Api-Server的地址,可以在kube.config中找到
然后点击连接测试,出现如下就对了,但是讲道理会出现一个
找不到 /var/lib/jenkins/.kube/config.json 的错误,出现这个错误就要将kubeConfig放到这个位置
(Tips:如果你是Rancher搭建的K8S集群就在这个位置找API-Server的地址和kubeConfig)
这里是比较重要的,Pod模板是为了方便打包的, jnlp 这个镜像是必须要的,没有这个镜像就无法连接上Jenkins,这个镜像是可以拓展的。
不建议再这个镜像中安装Docker,安装Dotnet,安装Java,安装NodeJs,因为这些都可以用多容器和Jenkins插件来解决
可以在这个镜像中安装例如解压缩这样的小工具。
重写的配置如下
这种K8S中的Node有一个很大的缺陷就是缓存的问题,如Nuget缓存,这时候就需要挂载一个盘去进行缓存了,有条件的建议挂载一个PVC,但是像我这样没条件的HostPath也香。
PS:这里用Dotnet打包为例子
Dotnet打包需要用到的镜像是dotnetsdk,所以需要在jnlp的基础上再加上一个其他镜像,使用的时候就
利用 container 这个指令去指定容器。后面的名称就是容器模板中定义的名称,需要唯一指定。
Dotnet打包要想快就要将Nuget的包全部缓存下来,所以需要将~/.nuget 这个文件夹里面的东西用PV缓存起来(鄙人没这个条件,用的HostPath)
使用 node 这个命令去指定节点
然后再NodeManager中查看就会看到出现了一个新的节点了,这个节点就是K8S中调度的,在打包完成后就会回收掉这个Pod。
到这里为Jenkins配置K8S的负载就全部完毕了。
Kubernetes 的REST API指的是什么?
REST API是Kubernetes系统的重要部分,组件之间的所有操作和通信均由API Server处理的REST API调用,大多数情况下,API定义和实现都符合标准的HTTP REST格式,可以通过 kubectl命令管理工具或其他命令行工具来执行。
API 版本
为了在兼容旧版本的同时不断升级新的API,Kubernetes支持多种API版本,每种API版本都有不同的API路径,例如/api/v1或 /apis/extensions/v1beta1。
API版本规则是通过基于API level选择版本,而不是基于资源和域级别选择,是为了确保API能够描述一个清晰的连续的系统资源和行为的视图,能够控制访问的整个过程和控制实验性API的访问。
JSON和Protobuf序列化模式遵循相同的模式变化原则,以下所有描述都涵盖了这两种模式。
需要注意,API版本和软件的版本没有直接关系,不同API版本有不同程度稳定性,API文档中详细描述了每个级别的标准。
Alpha级别:
包含alpha名称的版本(例如v1alpha1)。
该软件可能包含错误。启用一个功能可能会导致bug。默认情况下,功能可能会被禁用。
随时可能会丢弃对该功能的支持,恕不另行通知。
API可能在以后的软件版本中以不兼容的方式更改,恕不另行通知。
该软件建议仅在短期测试集群中使用,因为错误的风险增加和缺乏长期支持。
Beta级别:
包含beta名称的版本(例如v2beta3)。
该软件经过很好的测试。启用功能被认为是安全的。默认情况下功能是开启的。
细节可能会改变,但功能在后续版本不会被删除
对象的模式或语义在随后的beta版本或Stable版本中可能以不兼容的方式发生变化。如果这种情况发生时,官方会提供迁移操作指南。这可能需要删除、编辑和重新创建API对象。
该版本在后续可能会更改一些不兼容地方,所以建议用于非关键业务,如果你有多个可以独立升级的集群,你也可以放宽此限制。
大家使用过的Beta版本后,可以多给社区反馈,如果此版本在后续更新后将不会有太大变化。
Stable级别:
该版本名称命名方式:vX这里X是一个整数。
Stable版本的功能特性,将出现在后续发布的软件版本中。
Kubernetes API 概述
给 运行在 k8s 里的 springboot 指定 jvm 参数
我们知道,对于 tomcat 来说,设置 JAVA_OPTS 就可以给 jvm 设置一些参数, 比如 -Xms -Xmx 之类的堆大小参数
但是 对于 Spring boot 来说,因为是直接运行 java -jar 的,除非你修改 dockerfile , 不然直接设置 JAVA_OPTS 是没有效果的
最近在网上找了一些资料,得到了答案,分享到这里
如果容器是直接运行 tomcat 的, 那么 入口其实是指定运行 catalina.sh
JAVA_OPTS 是 catalina.sh 使用到的一个环境变量,在运行 org.apache.catalina.startup.Bootstrap 前, 会把 JAVA_OPTS 参数拼到前面
所以这是我们直接在 k8s yaml 里设置变量 JAVA_OPTS 可以生效的原因
大概看下 catalina.sh
Spring boot 项目打出来的一般是 jar , 我们的 dockerfile 入口一般也是 java -jar xxx.jar
修改 dockerfile, 变成 java $JAVA_OPTS -jar xxx.jar 思路肯定是可行的,但是里面有些坑,
此处不详细描述,可以见 stackoverflow
在不修改 dockerfile 的情况下有一种很简单的方法,可以达到传递 jvm 参数的效果
就是使用 JAVA_TOOL_OPTIONS
我们以 初始堆大小 参数为例,来看一下
我们什么环境变量都不加
可以看到默认 大小是 24MB
命令解释一下:
执行三个命令
jps 看下运行的java进程pid 是啥
jinfo -flag InitialHeapSize {pid} 看下 初始堆大小参数是多少, 去掉 -flag InitialHeapSize 看所有的参数(具体看jvm厂商有没有实现这个功能,据我所知,oracle 的 openjdk 实现了,IcedTea的OpenJdk没有实现)
echo $(( {size} 10 10)) 位运算,jinfo 输出的是 byte, 除以1024 是 KB, 再除以1024 是 MB, 1024=2^10,所以除以1024等于位运算右移10位,计算更快
JAVA_OPTS -XX:InitialHeapSize=66m
可以看到 还是 24MB
细心的应该注意到了 exec 的 pod name 变了,因为我修改了环境变量,需要重启,重启之后pod name 自然就变了
可以看到 变成 66MB 了,符合我们的设置
另外,执行 jps 的时候,就输出了 Picked up JAVA_TOOL_OPTIONS: -XX:InitialHeapSize=66m
前面两种场景是没有输出的哦
开发和运维对K8S中的应用都做了什么?
在应用的整个生命周期里,开发和运维都和它密不可分。一个塑造它,一个保养它。
如果应用需要部署到K8S中,开发和运维在其中都做了什么呢?
从开发侧来说,我们的应用应该具备以下能力:
健康 检测接口用于检测应用的 健康 状态,在K8S中,使用Readiness和Liveness分别来探测应用是否就绪和是否存活,如果未就绪或者未存活,K8S会采取相应的措施来确保应用可用。
如果我们应用未定义好相应的 健康 检测接口,K8S就无法判断应用是否正常可用,整个应用对我们来说就是黑匣子,也就谈不上应用稳定性了。
定义一个简单的 健康 检测接口如下:
如上我们定义了 health 接口,当应用启动后,只需要探测这个接口,如果返回OK,表示应用是正常的。
当然,上面的接口是非常简单的,在实际情况下,应用本身也许还依赖起来应用,比如redis,mysql,mq等,如果它们异常,应用是不是异常的呢?那我们的应用 健康 检测需不需要检测其他应用的 健康 状态呢?
既然我们定义好了 健康 检测接口,那我们的YAML模板就可以增加 健康 检测功能,如下:
应用发版是常规不能再常规的操作,通常情况下都是滚动更新的方式上线,也就是先起一个新应用,再删一个老应用。
如果这时候老应用有部分的流量,突然把老应用的进程杀了,这部分流量就无法得到正确的处理,部分用户也会因此受到影响。
怎么才会不受影响呢?
假如我们在停止应用之前先告诉网关或者注册中心,等对方把我们应用摘除后再下线,这样就不会有任何流量受到影响了。
在K8S中,当我们要删除Pod的时候,Pod会变成Terminating状态,kubelet看到Pod的状态如果为Terminating,就会开始执行关闭Pod的流程,给Pod发SIGTERM信号,如果达到宽限期Pod还未结束就给Pod发SIGKILL信号,从Endpoints中摘除Pod等。
从上面可知,Pod在停止之前会收到SIG信号,如果应用本身没有处理这些信号的能力,那应用如果知道什么时候该结束呢?
下面简单定义一个处理SIG信号的功能。
当接收到SIG信号的时候,就会调用 Shutdown 方法做应用退出处理。
除此,还要结合K8S的 PreStop Hook 来定义结束前的钩子,如下:
如果使用注册中心,比如nacos,我们可以在 PreStop Hook 中先告诉nacos要下线,如下:
Metrics主要用来暴露应用指标,可以根据实际情况自定义指标,以便于监控工具Prometheus进行数据收集展示。
有些语言有现成的exporter,比如java的jmx_exporter,没有的就需要自己在应用中集成。
比如:
这种会暴露默认的Http指标,可以通过 curl 127.0.0.1:9527/metrics 获取指标。
如果需要自定义指标的话,只需按规则定义即可,如下:
这样就定义了 httpserver_request_total 和 httpserver_request_duration_seconds 指标,引用过后就能在 /metrics 中看到对应的数据。
定义好了指标,下面就是收集了。既可以通过自定义收集规则收集,也可以通过自动发现的方式收集,为了方便,主要采用自动发现的方式。
我们只需要在deployment的templates中定义好annotation,prometheeus就会自动添加采集目标,如下:
Trace用于跟踪,每个请求都会生成一个 TraceID ,这个ID会伴随请求的整个生命周期,我们也可以根据这个ID查询请求的整个链路情况。
链路追踪,目前市面上有很多开源系统,比如Skywalking,Jeager,Zipkin等,它们各有各的特点,如下。
我比较推荐使用Jaeger,它是CNCF的毕业项目,成长空间和云原生的系统架构兼容性比较好。
不过,我这里采用的Skywalking。
Skywalking有许多现成的客户端,比如Java、Python等,可以直接使用,它们都会自动埋点,但是对于Go来说就只有自己手动埋点了,需要我们自己去写代码。
比如:
定义reporter用于上报数据给Skywalking,这就是一个简单的集成Trace的例子。
应用的可观测性主要来源日志、监控、链路追踪,标准的日志有利于日志收集以及排查问题。
原则上,不论是什么类型的日志输出,什么格式的日志内容,都能收集。但是为了方便友好,建议把日志输出到标准输出,这样收集更方便。
我个人理解,在K8s中,完全没必要把日志输出到文件,浪费不说,没多大意义,因为所有的日志我们都会收集到日志系统,而输出到文件的日志也会随着应用发版而丢失,所以输出到文件的意义是什么呢?
开发把系统开发完,就会交付给运维部署。为了保障应用的稳定性,运维在部署应用的时候应该考虑以下几点。
K8S中可以部署有状态应用,也可以部署无状态应用。对于有状态应用,我其实很少部署到K8S中,大部分还是部署的无状态应用,至于为什么,用多了就晓得了。
对于业务应用,强烈建议使其保持无状态,就算有需要持久化的东西,要么保存到数据库,要么保存到对象存储或者其他单独的文件系统中,不要挂载到应用Pod上。
这样的好处是,应用和数据是分开的,应用可以随意启停、扩展、迁移等。
保持高可用应该是每个运维人员的使命。
在K8S中,我们应该怎么配置呢?(1)应用Pod应该是多副本
(2)应用Pod之间做反亲和性,避免同一应用调度到同一台主机,如下。
(3) 为了避免应用因为节点维护等原因驱逐Pod,导致全部Pod被驱逐,特别配置了PodDisruptionBudget,保障应用至少有一个可用,如下。
(4)如果某个节点因为一些原因需要驱逐一些Pod,为了避免重要应用被驱逐,应该给应用配置较高的QoS,如下:
所谓优雅上线能力,就是要确保应用能够提供服务了,再接入外界流量,不能在还没完全启动的情况下就提供服务。
在K8S中,应用在启动后会加入endpoints中,然后通过service接入流量,那在什么情况下才算启动成功呢?主要是通过K8S的 ReadinessProbe 来进行检测。这时候开发的 健康 检测接口就派上用场了,如下:
所以我们K8S的YAML文件应该加上如上的配置。
所谓异常自愈,就是应用本身在出现Crash,或者应用Pod所在节点出现异常的情况,应用能够自动重启或者迁移。这时候就需要通过K8S的 LivenessProbe 来进行检测了,如下。
当K8S的YAML清单加上如上配置过后,就会定时去探测应用是否正常,如果异常,就会触发重启的动作。如果是节点异常,K8S会对Pod进行重新调度。
应用通过HTTPS访问是比较常见的,企业级应用建议自己购买相应的SSL证书,然后进行配置即可。
比如。
上面介绍了开发和运维对于应用上线应该做的工作, 不全但够用 。
在不同的企业都有不同的尿性,但是作为运维,我们都要牢牢记住 稳定 永远是第一尿性。通过上面的梳理,我们的应用模板就整理如下:
如果我的文章对你有所帮助,还请帮忙一下,你的支持会激励我输出更高质量的文章,非常感谢!
你还可以把我的公众号设为「 星标 」,这样当公众号文章更新时,你会在第一时间收到推送消息,避免错过我的文章更新。