当前位置: 首页 > news >正文

运维开发.Kubernetes探针与应用

运维系列
Kubernetes探针与应用

- 文章信息 - Author: 李俊才 (jcLee95)
Visit me at CSDN: https://jclee95.blog.csdn.net
My WebSitehttp://thispage.tech/
Email: 291148484@163.com.
Shenzhen China
Address of this article:https://blog.csdn.net/qq_28550263/article/details/139361931
HuaWei:https://bbs.huaweicloud.com/blogs/428407

【介绍】:探针让Kubernetes具备了更好的自我修复和自我调节能力,是提高应用整体可靠性不可或缺的利器。本文将详细介绍Kubernetes中探针的原理和用法。

在这里插入图片描述


1. 概述

Kubernetes中,探针(Probe)是一种监控和诊断容器健康状态的机制。它通过定期执行某种操作来判断容器是否正常运行,从而实现自动化的问题检测和恢复。

探针的引入大大提高了Kubernetes集群管理的智能化水平。通过配置合适的探测方式和参数,我们可以让Kubernetes自动处理一些常见的问题,比如:

  • 如果一个容器死锁、崩溃或者没有响应,探针可以感知到问题并自动重启容器,保证服务的持续可用;

  • 对于启动时间较长的应用,探针可以避免在应用完全启动之前将流量导入,防止将请求发送到尚未准备好的容器;

  • 探针可以判断应用是否已经准备好对外提供服务,帮助实现滚动更新的平滑切换。

Kubernetes中主要提供了三种类型的探针:

  1. 存活探针(LivenessProbe):用于判断容器是否存活,如果探测失败,Kubernetes会自动重启容器
  2. 就绪探针(ReadinessProbe):用于判断容器是否已经准备好接收请求,如果探测失败,Kubernetes会将容器从Service的后端Endpoint列表中移除,暂停向其发送请求
  3. 启动探针(StartupProbe):用于判断容器内的应用是否已经启动完成。启动探针通常用于启动时间较长的应用,可以避免存活探针过早地认为应用无响应

这三种探针可以单独使用,也可以结合使用,以实现对容器状态的全方位监控。我们将在后面的章节中详细介绍每一种探针的原理和使用方法。

2. 探针类型

2.1 存活探针(LivenessProbe)

LivenessProbe用于判断容器是否存活,是Kubernetes中最常用的一种探针。它的基本原理是定期执行一个探测操作,如果探测成功,则认为容器是健康存活的;如果探测失败,则认为容器已经死亡,Kubernetes会根据重启策略自动重启容器。

存活探针的主要作用是及时发现容器的异常状态,防止应用程序进入不可用的状态。例如,如果一个Java应用因为内存泄漏导致无法响应请求,存活探针可以探测到这种情况,然后重启容器,恢复服务。

存活探针支持三种探测方式:

  1. Exec探针:在容器内执行一个命令,如果命令的退出状态码为0,则认为探测成功
  2. TCP探针:尝试与容器的指定端口建立TCP连接,如果连接成功,则认为探测成功
  3. HTTP探针:向容器的指定端口和路径发送一个HTTP GET请求,如果响应的状态码在200到400之间,则认为探测成功

当存活探针连续多次探测失败时,Kubernetes就会认为容器已经死亡,并根据Pod的重启策略进行处理:

  • Always(默认):容器会被立即重启,并且将来会一直重启;

  • OnFailure:容器会被立即重启,但是在5分钟内最多重启10次;

  • Never:容器不会被自动重启,Pod将一直处于Failed状态;

需要注意的是,存活探针只能发现容器是否存活,但不能判断容器是否已经准备好处理请求。如果一个应用需要较长的启动时间,存活探针可能会过早地认为应用已经死亡并触发重启,导致应用反复重启却无法正常提供服务。这种情况下,我们可以使用启动探针(StartupProbe)来延迟存活探针的检查,直到应用完全启动。

此外,如果容器的主进程(PID 1)是一个脚本或者其他非常驻进程,存活探针可能会无法正确判断容器的状态。因为当主进程退出时,容器就会被认为已经死亡,即使其他进程还在运行。对于这种情况,我们可以考虑使用进程管理工具如supervisord来管理容器内的多个进程。

存活探针是实现自愈性(self-healing)应用的关键手段之一。通过恰当地配置探测方式和参数,我们可以让Kubernetes自动处理许多常见的故障,大大减轻了运维的负担。但是探针的配置也需要根据实际情况进行调整,不能完全依赖默认值,否则可能会适得其反。

2.2 就绪探针(ReadinessProbe)

ReadinessProbe用于判断容器是否已经准备好接受请求,是实现平滑滚动更新的关键。与LivenessProbe不同,ReadinessProbe并不会触发容器的重启,而是控制容器是否可以被访问。

当一个Pod中的所有容器都通过了就绪探针的检查,这个Pod就被认为是可用的,Kubernetes会将其加入到对应ServiceEndpoint列表中,开始向其发送请求。反之,如果有任何一个容器没有通过就绪探针的检查,这个Pod就会被从ServiceEndpoint列表中移除,暂停接收请求。

就绪探针的主要作用是防止将请求发送到还没有准备好的容器,特别是在滚动更新的场景下。例如,当我们使用Deployment更新一个应用的版本时,新版本的Pod在启动初期可能还无法立即处理请求,如果直接将流量切换到新Pod,可能会导致请求失败。这时,就绪探针可以检查新Pod是否已经完全启动并准备好接收请求,只有通过检查的Pod才会被加入到ServiceEndpoint中,从而实现平滑的流量切换。

就绪探针支持与存活探针相同的三种探测方式:ExecTCPHTTP GET。但是,就绪探针的判断标准通常与存活探针不同。存活探针关注的是容器的运行状态,而就绪探针关注的是应用的业务可用性。例如,一个Web服务器的存活探针可能只检查服务进程是否存在,但是就绪探针可能要检查服务是否已经加载完配置、连接上数据库、可以正常响应请求等。

下面我们通过对比,进一步理解就绪探针与存活探针的区别:

LivenessProbeReadinessProbe
作用判断容器是否存活判断容器是否就绪
探测失败后果重启容器将容器从ServiceEndpoint中移除
典型用途发现容器运行时故障,自动恢复可用性控制哪些Pod可以被访问,实现平滑上线
探测标准容器是否在运行应用是否可以正常处理请求

虽然存活探针和就绪探针的作用不同,但它们可以结合使用,以实现更全面的健康检查。例如,在滚动更新时,我们可以先用就绪探针控制流量切换,等新版本的Pod完全就绪后,再用存活探针监控其运行状态,确保整个更新过程的平稳进行。

此外,并非所有的容器都需要配置就绪探针。对于一些无状态的服务,或者启动时间非常短的应用,存活探针可能就已经足够。而对于有状态服务、启动时间较长的应用,以及需要平滑上线的场景,就绪探针就显得非常必要了。

就绪探针为Kubernetes提供了一种更细粒度的流量控制机制,是实现滚动更新、蓝绿部署等高级发布策略的基础。合理利用就绪探针,可以大大提高应用上线的平稳性和整个系统的稳定性。

2.3 启动探针(StartupProbe)

StartupProbeKubernetes 1.16版本引入的一种新的探针类型,它的主要作用是判断容器内的应用是否已经启动完成。与存活探针和就绪探针不同,启动探针专门用于处理启动时间较长的应用场景。

在某些情况下,应用程序可能需要较长的时间来初始化和启动,例如加载大量的配置文件、预热缓存、建立数据库连接等。如果我们直接使用存活探针来检查这类应用,就可能会遇到一个问题:

存活探针可能会在应用完全启动之前多次检查失败,从而触发容器的重启。这样不仅会影响应用的可用性,还可能加重系统的负载。

启动探针就是为了解决这个问题而设计的。它的基本原理是:

在容器启动之后的一段时间内,只执行启动探针,而不执行存活探针和就绪探针。只有当启动探针检查成功后,才会开始执行存活探针和就绪探针。

这样,我们就可以给应用一个宽限期,让它有足够的时间完成启动,而不会被过早地判定为失败。

启动探针支持与存活探针和就绪探针相同的三种探测方式:ExecTCPHTTP GET。但是,启动探针有一些特殊的配置参数:

  • failureThreshold:启动探针的失败阈值,默认为3。即启动探针最多可以连续失败3次,超过这个次数,容器就会被重启。

  • periodSeconds:启动探针的执行周期,默认为10秒。即每隔10秒执行一次启动探针。

  • timeoutSeconds:启动探针的超时时间,默认为1秒。即启动探针的执行时间不能超过1秒,否则会被视为失败。

下面我们通过一个例子来说明启动探针与其他两种探针的关系:

假设我们有一个应用需要2分钟的时间来完成启动。如果我们只配置了存活探针,并将其初始延迟(initialDelaySeconds)设置为30秒,那么在应用启动的前2分钟内,存活探针会连续多次检查失败,导致容器被反复重启。

现在,我们可以添加一个启动探针,并将其失败阈值设置为12。这意味着启动探针最多可以连续失败12次,即允许应用最多花费2分钟(12 *10秒)来完成启动。在这2分钟内,只会执行启动探针,而不会执行存活探针。只有当启动探针检查成功后,才会开始执行存活探针,此时应用已经完全启动,可以正常处理请求了。

可见,启动探针提供了一种延迟存活探针的机制,让应用有充足的时间来完成启动,避免了过早的重启。同时,启动探针本身也会检查应用的启动状态,如果应用确实无法在指定时间内启动,启动探针最终还是会触发容器的重启,以恢复应用的可用性。

启动探针是对存活探针的一种补充,适用于启动时间较长、启动过程复杂的应用场景。它与就绪探针的关系相对独立,主要影响的是存活探针的行为。在实际使用时,我们可以根据应用的特点,灵活地组合使用这三种探针,以实现更精细的健康检查和状态管理。

3. 探测方式

3.1 ExecAction

ExecActionKubernetes探针的一种探测方式,它通过在容器内执行一个命令来判断容器的健康状态。ExecAction的原理是:

  1. Kubelet根据探针的配置,定期在容器内执行一个命令。这个命令通常是一个用于健康检查的脚本或者工具,例如针对Web服务器的curl命令,或者针对数据库的连接测试命令等;
  2. 命令在容器内运行,并返回一个退出状态码。如果退出状态码为0,则表示命令执行成功,Kubelet认为容器是健康的;如果退出状态码非0,则表示命令执行失败,Kubelet认为容器是不健康的;
  3. Kubelet根据探测结果决定后续的操作:
    • 对于LivenessProbe,如果探测失败,Kubelet会根据重启策略重启容器;
    • 对于ReadinessProbe,如果探测失败,Kubelet会将容器从Service的Endpoint列表中移除;
    • 对于StartupProbe,如果探测失败,Kubelet会根据失败阈值决定是否重启容器。

可以看到,ExecAction通过在容器内运行命令,将容器的健康状态转化为命令的退出状态码,从而实现了对容器的健康检查。这种方式具有以下优点:

  • 灵活性高:可以根据应用的特点,自定义健康检查的逻辑和标准。例如,可以检查进程是否存在、端口是否可用、日志是否包含错误关键字等;

  • 适用性广:可以适用于各种类型的应用,不限于Web服务。只要能找到一种方式将应用的健康状态转化为命令的退出码,就可以使用ExecAction进行检查;

  • 与应用解耦:ExecAction只需要关注命令的退出码,而不需要了解应用的内部实现细节。这样可以将健康检查的逻辑与应用代码分离,提高可维护性。

当然,ExecAction也有一些局限性:

  • 性能开销:每次探测都需要在容器内启动一个新的进程来执行命令,这会消耗一定的CPU和内存资源。如果探测频率较高,可能会对应用性能产生影响;

  • 安全风险:由于需要在容器内执行命令,因此必须谨慎地控制命令的权限和范围。如果使用了不安全的命令或者参数,可能会被攻击者利用,从而威胁到整个系统的安全;

  • 故障诊断:当ExecAction探测失败时,我们只能知道命令的退出码,但无法知道具体的失败原因。这会给问题诊断和修复带来一定的难度。

3.2 TCPSocketAction

TCPSocketActionKubernetes探针的一种探测方式,它通过尝试与容器的指定端口建立TCP连接来判断容器是否健康。如果能够成功建立连接,就认为容器是正常的;如果无法建立连接,就认为容器是异常的。

TCPSocketAction的工作原理是:

  1. Kubelet根据探针的配置,周期性地尝试与容器的指定端口建立TCP连接;
  2. 如果连接成功建立,Kubelet就认为本次探测成功,并记录探测结果;
  3. 如果连接建立失败(比如端口未监听、拒绝连接、超时等),Kubelet就认为本次探测失败,并记录探测结果;
  4. Kubelet会根据连续成功或失败的次数,来决定将容器判定为正常还是异常。

相比于ExecActionTCPSocketAction的优点是:

  • 更轻量级:不需要在容器内执行命令,对容器的影响更小;

  • 更通用:不依赖于容器内的任何程序,适用于所有基于TCP的应用;

  • 更安全:不需要在容器内执行任意命令,降低了安全风险。

但是,TCPSocketAction也有一些局限性:

  • 只能检测端口的可达性,无法判断应用的实际状态;

  • 对于HTTP等基于TCP的应用层协议,无法检测具体的HTTP状态码、响应内容等;

  • 如果容器内的进程绑定了本地回环地址(127.0.0.1),则TCPSocketAction可能会误判;

TCPSocketAction仍然是一种非常实用的探测方式。特别是对于一些无状态的服务(如RedisMemcached等),我们通常只需要确保服务端口可达,而不需要关心更多的细节,这时使用TCPSocketAction就非常方便。

在实际使用中,我们需要根据应用的特点,合理地配置TCPSocketAction的参数,主要包括:

参数名称描述默认值
port要探测的容器端口号。HTTPGetAction会向该端口发送HTTP GET请求。-
host要连接的主机名或IP地址。默认为PodIP,也可以指定为一个域名。PodIP
scheme连接使用的协议,必须是HTTPHTTPSHTTP
path访问的HTTP服务器的路径。/
httpHeaders请求中自定义的HTTP头。这可以用于传递一些特殊的参数,如身份验证信息等。-
initialDelaySeconds容器启动后到开始执行探测的延迟时间,单位为秒。这个参数可以用于给应用程序一个初始化的时间,避免过早地开始探测。0
periodSeconds执行探测的频率,单位为秒。10
timeoutSeconds探测的超时时间,单位为秒。如果在指定时间内没有收到响应,则认为探测失败。1
successThreshold探测失败后认为成功的最小连续成功次数。1
failureThreshold探测成功后认为失败的最小连续失败次数。如果连续失败达到指定次数,就认为容器已经不健康,需要重启或移除。3

通过调整这些参数,我们可以控制探测的时机、频率和灵敏度,以适应不同应用的需求。同时,我们还要注意设置合理的初始延迟和超时时间,以避免因为应用启动慢或者网络延迟而导致探测失败。

3.3 HTTPGetAction

HTTPGetActionKubernetes探针的一种探测方式,它通过向容器发送HTTP GET请求来判断容器的健康状态。与ExecActionTCPSocketAction不同,HTTPGetAction在应用层面检查容器的状态,因此可以执行更加精细和定制化的健康检查逻辑。

HTTPGetAction的工作原理是:

  1. Kubelet根据探针的配置,定期向容器的指定端口发送一个HTTP GET请求。请求的路径、端口、HTTP头等参数都可以在探针中自定义;
  2. 容器收到请求后,由应用程序根据自身的业务逻辑进行处理,并返回一个HTTP响应;
  3. Kubelet接收到HTTP响应后,根据响应的状态码来判断探测是否成功。如果状态码在200到400之间(不包括400),则认为探测成功,否则认为探测失败;
  4. 如果探测成功,Kubelet会认为容器是健康的,继续执行下一次探测。如果探测失败,Kubelet会根据探针的类型(存活、就绪、启动)和重试次数等参数,决定是否重启容器或者将容器从Service的后端中移除。

可以看到,HTTPGetAction将健康检查的主动权交给了应用程序自身。这意味着我们可以在应用程序中实现任意的健康检查逻辑,例如:

  • 检查应用程序的关键组件(如数据库连接、缓存服务等)是否正常工作

  • 检查应用程序的内部状态和性能指标是否正常

  • 根据业务规则动态调整应用程序的健康状态

  • 提供一个专门用于健康检查的API接口,与应用程序的业务接口分离

ExecActionTCPSocketAction相比,HTTPGetAction的优点在于:

  1. 更加灵活和可定制。我们可以在应用程序中自由地实现健康检查逻辑,而不仅仅局限于进程状态和端口连通性;
  2. 与应用程序的业务逻辑更加贴近。通过在应用层面进行健康检查,我们可以更全面地评估应用程序的真实状态,而不是仅仅依赖底层的运行状态;
  3. 可以与应用程序的其他管理功能相结合,例如监控、告警、自愈等,构建一个完整的应用程序管理体系。

当然,HTTPGetAction也有一些需要注意的地方:

  1. 应用程序需要提供一个专门用于健康检查的API接口,这会增加开发和维护的工作量;
  2. 健康检查接口的实现需要谨慎,不能影响应用程序的正常服务,也不能占用过多的系统资源;
  3. 健康检查接口本身的可用性和正确性也需要保证。如果接口本身出现问题,会影响到整个健康检查机制的可靠性。

4. 探针通用参数

Kubernetes中,不同类型的探针都支持一些通用的配置参数,通过这些参数我们可以精细地控制探针的行为。下面我们重点介绍几个关键的参数:

  • initialDelaySeconds: 容器启动后到开始执行探测的延迟时间,单位为秒。默认为0,即容器启动后立即开始探测。但在实际场景中,许多应用在刚启动时还无法正常提供服务,如果立即开始探测可能会产生大量的失败记录,甚至导致容器被误杀。通过设置一个合理的initialDelaySeconds,我们可以给应用一个启动的缓冲期,避免过早探测引起的问题。

  • periodSeconds: 执行探测的频率,单位为秒。默认为10秒,即每10秒执行一次探测。选择探测频率需要权衡及时发现故障和减少探测开销两个因素:

    • 频率太低可能会延迟故障的发现,影响应用的可用性;

    • 频率太高又会增加探测对系统资源的消耗,尤其是使用ExecHTTP探测方式时。

通常建议根据应用的重要程度和状态变化频率来设置,核心应用可以设置得更频繁一些。

  • timeoutSeconds: 探测的超时时间,单位为秒。默认为1秒,即在1秒内如果没有得到探测结果,就认为本次探测超时失败。超时时间的设置需要考虑到容器内进程的响应时间、网络延迟等因素。

    • 如果超时时间太短,正常的探测可能会因为偶尔的延迟而失败;

    • 如果超时时间太长,故障的发现时间就会被延后。

通常建议根据应用的实际响应时间来设置,留出一定的冗余度。

  • successThreshold: 探测失败后认为成功的最小连续成功次数。默认为1,即探测一次成功就认为容器恢复正常。

    在实际使用中,有时为了防止探测结果的抖动,我们会通过successThreshold要求连续多次探测成功才认为恢复正常。例如设置为3,就是要求连续3次探测都成功,才最终判定为成功。

    这个值不宜设置得过大,否则会延长故障状态到恢复状态的判定时间。

  • failureThreshold: 探测成功后认为失败的最小连续失败次数。默认为3,即连续3次探测失败才最终判定为失败。

    failureThreshold的作用是防止偶发的探测失败导致容器被频繁重启。

    只有连续多次失败,才认为容器的确出现了故障,需要重启或移除流量。

    successThreshold类似,这个值也不宜设置得过大,以免延长故障的发现时间。

5. 探针的应用

5.1 在Deployment中使用存活探针和就绪探针

首先,我们创建一个名为liveness-readiness-probe.yaml的文件,内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:name: my-app
spec:replicas: 3selector:matchLabels:app: my-apptemplate:metadata:labels:app: my-appspec:containers:- name: my-appimage: my-app:v1ports:- containerPort: 8080livenessProbe:httpGet:path: /healthzport: 8080initialDelaySeconds: 30 # 容器启动后30秒开始执行存活探针periodSeconds: 10       # 每10秒执行一次存活探针timeoutSeconds: 5       # 每次探针超时时间为5秒failureThreshold: 3     # 连续3次探测失败则重启容器readinessProbe:httpGet:path: /readyport: 8080initialDelaySeconds: 10 # 容器启动后10秒开始执行就绪探针periodSeconds: 5        # 每5秒执行一次就绪探针timeoutSeconds: 2       # 每次探针超时时间为2秒successThreshold: 3     # 连续3次探测成功则将Pod标记为就绪

在这个例子中,我们定义了一个Deployment,它管理着3个副本的my-app应用。在template部分,我们定义了容器的规格,包括存活探针(livenessProbe)和就绪探针(readinessProbe)。

对于存活探针,我们使用了httpGet方式,它会向容器的8080端口发送一个GET请求,请求路径为/healthz。我们设置initialDelaySeconds30秒,给应用一个启动的缓冲期。之后每隔10秒(periodSeconds)执行一次探测,如果连续3次(failureThreshold)探测失败,每次探测超过5秒(timeoutSeconds)未响应,就会重启容器。

对于就绪探针,我们也使用了httpGet方式,但是请求路径为/ready。就绪探针的initialDelaySeconds设置为10秒,比存活探针更短,因为通常应用可以更快地准备好接受请求。之后每隔5秒执行一次探测,如果连续3次(successThreshold)探测成功,每次探测超过2秒未响应,就会将Pod标记为就绪,加入到Service的后端。

现在,我们应用这个Deployment

kubectl apply -f liveness-readiness-probe.yaml

然后我们可以观察Pod的状态变化:

kubectl get pods -w

输出结果类似如下:

NAME                      READY   STATUS    RESTARTS   AGE
my-app-6c5f79d9f8-7bglx   0/1     Running   0          5s
my-app-6c5f79d9f8-7bglx   0/1     Running   1          36s
my-app-6c5f79d9f8-7bglx   1/1     Running   1          46s
my-app-6c5f79d9f8-kdg9p   0/1     Running   0          5s
my-app-6c5f79d9f8-kdg9p   0/1     Running   1          36s
my-app-6c5f79d9f8-kd

我们可以看到,Pod刚创建时,READY列为0/1,表示还没有通过就绪探针。30秒后,存活探针开始执行,如果/healthz接口没有正确响应,Pod会被重启,RESTARTS列会增加。当/ready接口正确响应后,Pod最终变为就绪状态,READY列变为1/1

如果我们删除存活探针和就绪探针的定义,再次应用这个Deployment,会发现Pod的行为有所不同:

  • 没有了存活探针,即使应用程序无响应,Pod也不会被自动重启,可能会长时间处于不可用状态;

  • 没有了就绪探针Pod会立即被标记为就绪,加入到Service的后端,但此时应用程序可能还没准备好处理请求,导致请求失败。

通过这个例子,我们可以直观地感受到存活探针和就绪探针在保障应用可用性方面的重要作用。合理地使用探针,配置适当的参数,可以让Kubernetes更智能地管理应用程序,提高整个系统的稳定性和恢复能力。

当然,探针的定义还需要根据实际的应用特点来调整。我们需要提供相应的/healthz/ready接口,合理设置初始延迟和超时时间等参数。

5.2 为有启动时间的应用配置启动探针

在实际生产环境中,我们经常会遇到一些启动时间较长的应用,比如需要加载大量配置、初始化缓存、建立外部连接等。这些应用在刚启动时,虽然进程已经运行,但是还无法正常提供服务。如果我们直接使用存活探针来检测这类应用,就很可能会出现存活探针在应用完全启动之前,连续多次探测失败,导致容器被反复重启,影响服务的可用性。

这时,启动探针就可以帮助我们解决这个问题。我们可以在应用的容器规范中同时定义启动探针和存活探针:

  • 启动探针负责检查应用是否已经启动完成,如果检查失败,会继续等待下一次检查,直到达到最大失败次数(failureThreshold)才会重启容器。

  • 存活探针负责检查应用是否健康运行,如果检查失败,会立即重启容器。

关键的是,在启动探针检查成功之前,存活探针是不会执行的。这就给了应用一个"启动保护期",避免了存活探针过早介入而引发的问题。

下面是一个具体的例子,假设我们有一个Java应用需要60秒的时间来完成初始化,我们可以为它配置如下的启动探针和存活探针:

apiVersion: v1
kind: Pod
metadata:name: my-app
spec:containers:- name: my-appimage: my-app:v1.0startupProbe:httpGet:path: /healthzport: 8080failureThreshold: 6  # 最多允许失败6次,即60秒的启动时间periodSeconds: 10    # 每10秒执行一次livenessProbe:httpGet:path: /healthzport: 8080initialDelaySeconds: 60  # 启动60秒后开始执行存活探针periodSeconds: 5         # 每5秒执行一次

在这个例子中:

  • startupProbe使用HTTP GET请求访问容器的/healthz接口,如果返回2xx3xx的状态码则认为成功。startupProbe每10秒执行一次(periodSeconds: 10),最多允许失败6次(failureThreshold: 6),即容器有60秒的时间来完成启动。

  • livenessProbe同样使用HTTP GET请求访问/healthz接口,不同的是它要求容器启动60秒后才开始第一次检查(initialDelaySeconds: 60),然后每5秒执行一次(periodSeconds: 5)。

这样,在容器启动的前60秒内,只有startupProbe在执行,而livenessProbe不会干扰应用的启动。一旦startupProbe检查通过,说明应用已经完全启动,这时livenessProbe才开始工作,监控应用的运行健康状态。

如果应用在启动过程中出现了问题,无法在60秒内完成启动,那么startupProbe会在重试6次后重启容器,而不是让应用一直处于中间状态。

可见,启动探针提供了一种"延时"机制,推迟了存活探针的介入时间,避免了存活探针因为应用启动慢而过早判定失败的问题。同时,启动探针本身也会检查应用的启动状态,确保应用可以在有限的时间内完成启动,如果启动失败,则会通过重启容器来恢复。

5.3 使用HTTP探针实现业务层面的健康检查

在前面的章节中,我们介绍了存活探针和就绪探针主要关注容器的运行状态,比如进程是否存在、端口是否可达等。但是有时候,我们还需要在更高的层次上对应用的健康状态进行检查,比如:

  • 应用的关键依赖(如数据库、缓存等)是否正常可用;

  • 应用的关键流程(如登录、下单等)是否能够正常执行;

  • 应用的性能指标(如响应时间、错误率等)是否在正常范围内。

这就需要我们在应用程序内部实现一个专门用于健康检查的接口,通过这个接口暴露应用的内部状态,然后在Kubernetes中用HTTP探针来访问该接口,以判断应用的实际健康情况。

假设我们有一个Django应用,它有两个关键依赖:一个MySQL数据库和一个Redis缓存。

我们希望在健康检查中判断这两个依赖是否可用,同时还要检查Django应用自身是否能够正常处理请求。

首先,我们在Django应用中实现一个/health接口:

urlpatterns = [...path('health/', views.health),...
]
from django.http import HttpResponse
from django.db import connections
from django.db.utils import OperationalError
from redis import Redis, ConnectionErrordef health(request):try:# 检查MySQL连接cursor = connections['default'].cursor()cursor.execute("SELECT 1")cursor.fetchone()# 检查Redis连接redis = Redis(host='redis', port=6379)redis.ping()# 检查Django应用return HttpResponse("OK", status=200)except OperationalError:return HttpResponse("MySQL is not available", status=500)except ConnectionError:return HttpResponse("Redis is not available", status=500)except Exception as e:return HttpResponse(str(e), status=500)

通过这个/health接口,我们可以检查Django应用的关键依赖,如数据库连接、缓存连接等是否正常。这比仅仅检查进程是否存在、端口是否可达更进一步,能够反映应用的真实健康状态。

这个/health接口中,我们分别检查了MySQL连接、Redis连接和Django应用本身的可用性:

  • 对于MySQL,我们通过Djangoconnections对象获取到默认的数据库连接,然后执行一个简单的SELECT 1查询。如果查询成功,说明MySQL连接是正常的;

  • 对于Redis,我们直接创建了一个Redis客户端,然后调用ping()方法。如果连接正常,该方法会返回True,否则会抛出ConnectionError异常;

  • 对于Django应用,我们直接返回了一个200状态码的HttpResponse,表示应用是健康的。如果在处理请求的过程中发生了任何异常,Django会自动返回500错误;

  • 如果所有检查都通过,/health接口会返回字符串’**OK’**和状态码200,表示应用是健康的;

  • 如果MySQL连接失败,就会返回**‘MySQL is not available’**和状态码500;

  • 如果Redis连接失败,就会返回**‘Redis is not available’**和状态码500;

  • 如果发生了其他异常,就会返回错误信息和状态码500

有了这个/health接口,我们就可以在Kubernetes中使用HTTP探针来访问它,以实现业务层面的健康检查。下面是一个Deployment的示例配置:

apiVersion: apps/v1
kind: Deployment
metadata:name: my-app
spec:replicas: 3selector:matchLabels:app: my-apptemplate:metadata:labels:app: my-appspec:containers:- name: my-appimage: my-app:v1ports:- containerPort: 8000livenessProbe:httpGet:path: /healthport: 8000initialDelaySeconds: 30  # 容器启动30秒后开始存活性探测periodSeconds: 10        # 每10秒执行一次存活性探测readinessProbe:httpGet:path: /healthport: 8000initialDelaySeconds: 10  # 容器启动10秒后开始就绪性探测periodSeconds: 5         # 每5秒执行一次就绪性探测

在这个Deployment中,我们为容器定义了存活探针(livenessProbe)和就绪探针(readinessProbe),它们都使用HTTP GET方法访问容器的/health接口,以判断应用的健康状态。

  • 对于存活探针,我们设置了initialDelaySeconds: 30,即容器启动30秒后才开始探测,以留出时间让应用完成初始化。之后每隔10秒(periodSeconds: 10)执行一次探测。

  • 对于就绪探针,我们设置了initialDelaySeconds: 10,即容器启动10秒后就开始探测,以尽快将健康的Pod加入到Service中。之后每隔5秒(periodSeconds: 5)执行一次探测。

通过这样的配置,Kubernetes就可以根据/health接口返回的状态来判断应用的真实健康状况:

  • 如果/health返回200,说明MySQL、Redis、Django应用都是正常的,Kubernetes会认为容器是健康的,不会重启容器,并且会将其加入到Service的后端。
  • 如果/health返回500,说明某个依赖出现了问题,Kubernetes会认为容器是不健康的,会根据存活探针的重启策略重启容器,并根据就绪探针的结果将其从Service的后端移除,避免将流量导入异常的Pod。

这样,我们就通过一个简单的/health接口和HTTP探针,实现了对应用内部状态的全面检查。这种方式相比于仅检查进程和端口,能够更加准确地反映应用的实际健康状况,帮助我们及时发现和解决问题。

当然,/health接口的实现需要根据应用的实际架构和依赖来设计,上面的例子只是一个简单的演示。在实际项目中,我们可能需要检查更多的依赖和服务,判断更复杂的业务逻辑,甚至根据不同的严重程度返回不同的状态码。但是无论如何,遵循"在应用层面暴露健康状态,在Kubernetes层面通过HTTP探针来检查"的基本思路,我们就可以灵活地实现自己的健康检查策略,让Kubernetes更好地管理我们的应用。

除了在Deployment中单独使用,HTTP探针还可以与其他Kubernetes特性结合,发挥更大的作用。比如在滚动更新时,我们可以通过就绪探针来控制新旧版本的切换时机,确保应用在整个更新过程中保持可用。这部分内容我们将在后面的"5.4 利用探针实现零停机滚动更新"一节中详细介绍。

5.4 利用探针实现零停机滚动更新

Kubernetes中,我们通常使用Deployment来管理应用的多个副本,并通过更新Deployment的方式来实现应用的滚动更新。滚动更新的基本过程是:

  1. 创建一个新版本的Pod;
  2. 等待新Pod就绪;
  3. 将流量切换到新Pod;
  4. 删除旧版本的Pod。

在这个过程中,如果新Pod在就绪之前就开始接收流量,就可能会导致请求失败,影响服务的可用性。这时,就绪探针就可以发挥作用了。

我们可以在Deployment中为新版本的Pod模板定义一个就绪探针,例如:

apiVersion: apps/v1
kind: Deployment
metadata:name: my-app
spec:replicas: 3selector:matchLabels:app: my-apptemplate:metadata:labels:app: my-appspec:containers:- name: my-appimage: my-app:v2readinessProbe:httpGet:path: /healthzport: 8080initialDelaySeconds: 10  # 容器启动10秒后开始就绪探测periodSeconds: 5         # 每5秒执行一次就绪探测strategy:type: RollingUpdaterollingUpdate:maxUnavailable: 1  # 滚动更新时最多有1个Pod不可用maxSurge: 1        # 滚动更新时最多可以超出期望副本数1个Pod

在这个例子中,我们为新版本的Pod模板定义了一个基于HTTP GET的就绪探针:

  • 探针会向Pod的8080端口发送一个GET请求,请求路径为/healthz

  • 探针会在容器启动10秒后开始执行(initialDelaySeconds),然后每5秒执行一次(periodSeconds);

  • 如果探针请求的HTTP状态码在200到400之间,则认为Pod已经就绪,可以接收流量;否则,认为Pod尚未就绪,不会将其加入到Service的Endpoint中;

我们还通过.spec.strategy字段设置了滚动更新的策略:

  • maxUnavailable: 1表示在滚动更新过程中,最多可以有1个Pod处于不可用状态;

  • maxSurge: 1表示在滚动更新过程中,最多可以超出期望副本数1个Pod

这样可以更精细地控制滚动更新的过程,避免一次性创建或删除过多的Pod,从而影响服务的可用性。

现在,当我们更新这个Deployment时(比如修改Pod模板中的镜像版本),Kubernetes就会执行滚动更新:

  1. Kubernetes会先创建一个新版本的Pod
  2. Pod启动后,就绪探针会开始探测其是否已经就绪;
  3. 只有当新Pod通过就绪探针检查后,Kubernetes才会将其加入到ServiceEndpoint中,开始向其发送流量;
  4. 与此同时,Kubernetes会逐渐减少旧版本Pod的数量,直到所有旧Pod都被替换为止。

在整个滚动更新过程中,就绪探针起到了这些作用:

  • 它确保只有就绪的新Pod才会被加入到Service中,接收流量。这避免了将请求发送到尚未准备好的Pod,保证了服务的连续可用;

  • 它与Kubernetes的滚动更新策略配合,实现了平滑的版本切换。在任何时刻,总有一定数量的Pod(新版本+旧版本)可以处理请求,从而实现了零停机;

假设我们有一个简单的Web应用,提供一个/接口返回一个问候语,一个/healthz接口用于健康检查:

const express = require('express');
const app = express();// 应用版本
const version = process.env.APP_VERSION || 'v1';// 问候接口
app.get('/', (req, res) => {res.send(`Hello, world! This is ${version}.`);
});// 健康检查接口
app.get('/healthz', (req, res) => {// 检查应用的关键依赖,比如数据库连接const dbConnected = checkDatabaseConnection();if (dbConnected) {res.sendStatus(200);} else {res.sendStatus(500);}
});// 启动服务器
const port = process.env.PORT || 8080;
app.listen(port, () => {console.log(`Server is running at http://localhost:${port}`);
});// 检查数据库连接的函数(这里只是一个示例,实际情况根据应用的架构而定)
function checkDatabaseConnection() {// ...return true;  // 假设数据库连接正常
}

我们将这个应用打包成两个版本的Docker镜像:myapp:v1myapp:v2,它们的唯一区别是环境变量APP_VERSION的值不同。

然后,我们创建一个Deployment来运行这个应用,并设置就绪探针:

apiVersion: apps/v1
kind: Deployment
metadata:name: myapp
spec:replicas: 3selector:matchLabels:app: myapptemplate:metadata:labels:app: myappspec:containers:- name: myappimage: myapp:v1readinessProbe:httpGet:path: /healthzport: 8080initialDelaySeconds: 5periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:name: myapp
spec:selector:app: myappports:- port: 80targetPort: 8080type: LoadBalancer  # 使用LoadBalancer类型,便于从集群外部访问

我们同时创建了一个Service,将流量导向带有app: myapp标签的Pod。当我们应用这些资源时,Kubernetes会创建3个myapp:v1Pod

一旦这些Pod通过就绪探针检查,它们就会被添加到myapp服务的Endpoint中,开始接收流量。

现在,我们可以通过服务的外部IP来访问应用:

kubectl get svc myapp
NAME    TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)        AGE
myapp   LoadBalancer   10.98.219.86   123.45.67.890   80:31000/TCP   5m
curl http://123.45.67.890
Hello, world! This is v1.

接下来,我们将DeploymentYAML文件中的镜像更新为myapp:v2,然后用kubectl apply来应用这个更改,模拟一次应用升级:

kubectl apply -f myapp-deployment.yaml
deployment.apps/myapp configured

这时,Kubernetes会开始执行滚动更新:

  1. 创建一个新的myapp:v2Pod
  2. 等待新Pod通过就绪探针检查;
  3. 将新Pod添加到服务的Endpoint中,开始向其发送流量;
  4. 逐渐减少myapp:v1Pod数量,直到全部替换为myapp:v2

在这个过程中,我们可以不断地访问服务,观察其返回结果的变化:

while true; do curl http://123.45.67.890; sleep 1; done
Hello, world! This is v1.
Hello, world! This is v1.
Hello, world! This is v2.
Hello, world! This is v1.
Hello, world! This is v2.
Hello, world! This is v2.
...

可以看到,在滚动更新的过程中,服务会逐渐将流量从v1切换到v2,但在任何时刻,服务都是可用的,请求不会失败。这就实现了零停机的滚动更新。

如果我们在更新过程中描述Deployment的状态,就可以清楚地看到整个过程:

kubectl describe deploy myapp
...
Conditions:Type           Status  Reason----           ------  ------Available      True    MinimumReplicasAvailableProgressing    True    ReplicaSetUpdated
OldReplicaSets:  myapp-5f88c99fb8 (1/1 replicas created)
NewReplicaSet:   myapp-658d4f5d67 (2/2 replicas created)
...

相关文章:

运维开发.Kubernetes探针与应用

运维系列 Kubernetes探针与应用 - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite:http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this article:https://blog.csdn.net/qq_28550263…...

Spring 框架:Java 企业级开发的基石

文章目录 序言Spring 框架的核心概念Spring 框架的主要模块Spring Boot:简化 Spring 开发Spring Cloud:构建微服务架构实际案例分析结论 序言 Spring 框架自 2002 年发布以来,已经成为 Java 企业级开发的标准之一。它通过提供全面的基础设施…...

在Docker中使用GPU

一、安装nvidia-container-toolkit 总之一句话:nvidia-docker和nvidia-docker2,nvidia-container-runtime 已经被英伟达迭代了,可以认为nvidia-container-toolkit是nvidia-docker和nvidia-docker2, nvidia-container-runtime 的替…...

vue3 前端实现导出下载pdf文件

这样的数据实现导出 yourArrayBufferOrByteArray 就是后端返回数据 // 创建Blob对象const blob new Blob([new Uint8Array(res)], { type: application/pdf })// 创建一个表示该Blob的URLconst url URL.createObjectURL(blob);// 创建一个a标签用于下载const a document.cr…...

AI智能体研发之路-模型篇(五):pytorch vs tensorflow框架DNN网络结构源码级对比

博客导读: 《AI—工程篇》 AI智能体研发之路-工程篇(一):Docker助力AI智能体开发提效 AI智能体研发之路-工程篇(二):Dify智能体开发平台一键部署 AI智能体研发之路-工程篇(三&am…...

电商物流查询解决方案助力提升消费者体验

截至2023年12月,中国网络购物用户规模达9.15亿人,占网民整体的83.8%。这一庞大的数字不仅展现了电子商务的蓬勃发展,也标志着数字零售企业营销战略的转变——从以产品和流量为核心,到用户为王的新阶段。因此,提升消费者…...

【深度密码】神经网络算法在机器学习中的前沿探索

目录 🚝前言 🚍什么是机器学习 1. 基本概念 2. 类型 3. 关键算法 4. 应用领域 5. 工作流程 🚋什么是神经网络 基本结构 🚂神经网络的工作原理 前向传播(Forward Propagation): 损失函…...

搭载算能 BM1684 芯片,面向AI推理计算加速卡

搭载算能 BM1684 芯片,是面向AI推理的算力卡。可集成于服务器、工控机中,高效适配市场上所有AI算法,实现视频结构化、人脸识别、行为分析、状态监测等应用,为智慧城市、智慧交通、智慧能源、智慧金融、智慧电信、智慧工业等领域进…...

Python开发 我的世界 Painting-the-World: Minecraft 像素图片生成器

简介 Painting-the-World 是一款创新的工具,专为《我的世界》(Minecraft) 玩家及创作者设计,旨在将数字图片转变为游戏内的像素艺术。通过利用 RCON (Remote Console) 协议,本项目可以直接与《我的世界》服务器对话,根据输入的图…...

【经验分享】盘点“食用“的写文素材

一、构建框架 简介 1. 身份 擅长领域 2. 博客内容 3. 目前示例: 阿里云专家博主,华为云-云享专家,专注前、后端开发 博客内容:前后端实战教学、源码剖析、常见面试知识解析、算法题解与心得、日常考研总结等 目前正在备战考研&…...

实习碰到的问题w1

1.vueelementUI在输入框中按回车键会刷新页面 当一个 form 元素中只有一个输入框时&#xff0c;在该输入框中按下回车应提交该表单。如果希望阻止这一默认 行为&#xff0c;可以在 <el-form> 标签上添加 submit.native.prevent 。 参考&#xff1a;element-ui 表单 form …...

c#实现BPM系统网络传输接口,http协议,post

BPM通过http协议实现网络传输&#xff0c;语言使用.net(c#)&#xff0c;在这里只提供一个接口&#xff0c;具体代码如下,请参照&#xff1a; public string MakeRequest(string parameters) { ServicePointManager.ServerCertificateValidationCallback new Syst…...

如何修改开源项目中发现的bug?

如何修改开源项目中发现的bug&#xff1f; 目录 如何修改开源项目中发现的bug&#xff1f;第一步&#xff1a;找到开源项目并建立分支第二步&#xff1a;克隆分支到本地仓库第三步&#xff1a;在本地对项目进行修改第四步&#xff1a;依次使用命令行进行操作注意&#xff1a;Gi…...

结构设计模式 - 代理设计模式 - JAVA

代理设计模式 一. 介绍二. 代码示例2.1 定义 CommandExecutor 类2.2 定义 CommandExecutorProxy代理类2.3 模拟客户端2.4 测试结果 三. 结论 前言 这是我在这个网站整理的笔记,有错误的地方请指出&#xff0c;关注我&#xff0c;接下来还会持续更新。 作者&#xff1a;神的孩子…...

企业了解这些cad图纸加密方法,再也不怕图纸被盗了!

在竞争激烈的商业环境中&#xff0c;企业的核心技术、设计图纸和创意是维持其市场地位和竞争优势的关键。CAD图纸作为产品设计的重要载体&#xff0c;其安全性自然成为企业关注的焦点。为了确保CAD图纸不被非法获取或盗用&#xff0c;企业需要采取一系列有效的加密方法。本文将…...

# 详解 JS 中的事件循环、宏/微任务、Primise对象、定时器函数,以及其在工作中的应用和注意事项

为什么会突然想到写这么一个大杂烩的博文呢&#xff0c;必须要从笔者几年前的一次面试说起 当时的我年轻气盛&#xff0c;在简历上放了自己的博客地址&#xff0c;而面试官应该是翻了我的博客&#xff0c;好几道面试题都是围绕着我的博文来提问 其中一个问题&#xff0c;直接…...

神经网络与深度学习——第14章 深度强化学习

本文讨论的内容参考自《神经网络与深度学习》https://nndl.github.io/ 第14章 深度强化学习 深度强化学习 强化学习&#xff08;Reinforcement Learning&#xff0c;RL&#xff09;&#xff0c;也叫增强学习&#xff0c;是指一类从与环境交互中不断学习的问题以及解决这类问题…...

centOS 编译C/C++

安装C和C编译器 yum -y install gcc*查看CenterOS系统信息 cat /etc/system-releaseCentOS Linux release 8.2.2004 (Core)查看gcc版本 gcc --versiongcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-4) Copyright (C) 2018 Free Software Foundation, Inc. This is free software…...

java——网络原理初识

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 目录 1.网络通信概念初识1.1 IP地址1.2端口号1.3协议1.3.1协议分层协议分层带来的好处主要有两个方面 1.3.2 TCP/IP五层 (或四层模型)1.3.3 协议的层和层之间是怎么配合工作的 1.网络通信概念初识…...

js怎么判断是否为手机号?js格式校验方法

数据格式正确与否是表单填写不可避免的一个流程&#xff0c;现整理一些较为常用的信息格式校验方法。 判断是否为手机号码 // 判断是否为手机号码 function isPhoneNumber(phone) {return /^[1]\d{10}$/.test(phone) }判断是否为移动手机号 function isChinaMobilePhone(phon…...

深入理解Java中的方法重载:让代码更灵活的秘籍

关注微信公众号 “程序员小胖” 每日技术干货&#xff0c;第一时间送达&#xff01; 引言 在Java编程的世界里&#xff0c;重载(Overloading)是一项基础而强大的特性&#xff0c;它让我们的代码更加灵活、可读性强。对于追求高效、优雅编码的开发者而言&#xff0c;掌握方法重…...

鸿蒙ArkTS声明式开发:跨平台支持列表【显隐控制】 通用属性

显隐控制 控制组件是否可见。 说明&#xff1a; 开发前请熟悉鸿蒙开发指导文档&#xff1a; gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本…...

每日一题——Java编程练习题

题目&#xff1a; 键盘录入两个数字number1和number2表示一个范围&#xff0c;求这个范围之内的数字和。 我写的代码&#xff1a; public class Test {public static void main(String[] args) {Scanner sc new Scanner(System.in);System.out.print("输入第一个数:&q…...

java编辑器中如何调试程序?

目录 如何调试java程序? 待续、更新中 如何调试java程序? 1 看错误信息 2 相应位置输入输出信息: System.out.println("测试信息1 "); 以此查看哪条语句未进行输入 待续、更新中 1 顿号、: 先使用ctrl. &#xff0c;再使用一遍切回 2 下标: 21 2~1~ 3 上标: 2…...

第四范式Q1业务进展:驰而不息 用科技锻造不朽价值

5月28日&#xff0c;第四范式发布今年前三个月的核心业务进展&#xff0c;公司坚持科技创新&#xff0c;业务稳步拓展&#xff0c;用人工智能为千行万业贡献价值。 今年前三个月&#xff0c;公司总收入人民币8.3亿元&#xff0c;同比增长28.5%&#xff0c;毛利润人民币3.4亿元&…...

SpringBoot整合Kafka的快速使用教程

目录 一、引入Kafka的依赖 二、配置Kafka 三、创建主题 1、自动创建(不推荐) 2、手动动创建 四、生产者代码 五、消费者代码 六、常用的KafKa的命令 Kafka是一个高性能、分布式的消息发布-订阅系统&#xff0c;被广泛应用于大数据处理、实时日志分析等场景。Spring B…...

低边驱动与高边驱动

一.高边驱动和低边驱动 低边驱动(LSD): 在电路的接地端加了一个可控开关&#xff0c;低边驱动就是通过闭合地线来控制这个开关的开关。容易实现&#xff08;电路也比较简单&#xff0c;一般由MOS管加几个电阻、电容&#xff09;、适用电路简化和成本控制的情况。 高边驱动&am…...

【C++】入门(二):引用、内联、auto

书接上回&#xff1a;【C】入门&#xff08;一&#xff09;&#xff1a;命名空间、缺省参数、函数重载 文章目录 六、引用引用的概念引用的使用场景1. 引用做参数作用1&#xff1a;输出型参数作用2&#xff1a;对象比较大&#xff0c;减少拷贝&#xff0c;提高效率 2. 引用作为…...

编程学习 (C规划) 6 {24_4_18} 七 ( 简单扫雷游戏)

首先我们要清楚扫雷大概是如何实现的&#xff1a; 1.布置雷 2.扫雷&#xff08;排查雷&#xff09; &#xff08;1&#xff09;如果这个位置是雷就炸了&#xff0c;游戏结束 &#xff08;2&#xff09;如果不是雷&#xff0c;就告诉周围有几个雷 3.把所有不是雷的位置都找…...

【AI】llama-fs的 安装与运行

pip install -r .\requirements.txt Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved.Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows(venv) PS D:\XTRANS\pythonProject>...

Android NDK系列(五)内存监控

在日常的开发中&#xff0c;内存泄漏是一种比较比较棘手的问题&#xff0c;这是由于其具有隐蔽性&#xff0c;即使发生了泄漏&#xff0c;很难检测到并且不好定位到哪里导致的泄漏。如果程序在运行的过程中不断出现内存泄漏&#xff0c;那么越来越多的内存得不到释放&#xff0…...

软件设计师,下午题 ——试题六

模型图 简单工厂模式 工厂方法模式抽象工厂模式生成器模式原型模式适配器模式桥接模式组合模式装饰&#xff08;器&#xff09;模式亨元模式命令模式观察者模式状态模式策略模式访问者模式中介者模式 简单工厂模式 工厂方法模式 抽象工厂模式 生成器模式 原型模式 适配器模式 桥…...

《Kubernetes部署篇:基于麒麟V10+ARM64架构部署harbor v2.4.0镜像仓库》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;企业级K8s集群运维实战 一、环境信息 K8S版本 操作系统 CPU架构 服务版本 1.26.15 Kylin Linux Advanced Server V10 ARM64 harbor v2.4.0 二、部…...

远程工作/线上兼职网站整理(数字游民友好)

文章目录 国外线上兼职网站fiverrupwork 国内线上兼职网站甜薪工场猪八戒网云队友 国外线上兼职网站 fiverr https://www.fiverr.com/start_selling?sourcetop_nav upwork https://www.upwork.com/ 国内线上兼职网站 甜薪工场 https://www.txgc.com/ 猪八戒网 云队友 …...

elasticsearch7.15实现用户输入自动补全

Elasticsearch Completion Suggester&#xff08;补全建议&#xff09; Elasticsearch7.15安装 官方文档 补全建议器提供了根据输入自动补全/搜索的功能。这是一个导航功能&#xff0c;引导用户在输入时找到相关结果&#xff0c;提高搜索精度。 理想情况下&#xff0c;自动补…...

掌握正则表达式的力量:全方位解析PCRE的基础与进阶技能

Perl 兼容正则表达式&#xff08;PCRE&#xff09;是 Perl scripting language 中所使用的正则表达式语法标准。这些正则表达式在 Linux 命令行工具&#xff08;如 grep -P&#xff09;及其他编程语言和工具中也有广泛应用。以下是一些基础和进阶特性&#xff0c;帮你掌握和使用…...

FastFM库,一款强大神奇的Python系统分析预测的工具

FastFM库概述 在机器学习领域,Factorization Machines&#xff08;FM&#xff09;是处理稀疏数据集中特征间交互的重要工具.Python的fastFM库提供了高效的实现,特别适合用于推荐系统、评分预测等任务.本文将全面介绍fastFM的安装、特性、基本和高级功能,并结合实际应用场景展示…...

R语言绘图 --- 饼状图(Biorplot 开发日志 --- 2)

「写在前面」 在科研数据分析中我们会重复地绘制一些图形&#xff0c;如果代码管理不当经常就会忘记之前绘图的代码。于是我计划开发一个 R 包&#xff08;Biorplot&#xff09;&#xff0c;用来管理自己 R 语言绘图的代码。本系列文章用于记录 Biorplot 包开发日志。 相关链接…...

用于日常任务的实用 Python 脚本

Python 是一种多功能编程语言&#xff0c;以其简单易读而闻名。它广泛应用于从 Web 开发到数据分析等各个领域。Python 脚本&#xff0c;它们可以通过自动执行常见任务来使您的生活更轻松。 用于日常任务的实用 Python 脚本 1. 使用 Pandas 进行数据分析2. 使用 BeautifulSoup …...

7-Zip是什么呢

1. 简介 7-Zip 是一个功能强大、免费开源的文件压缩和解压缩工具&#xff0c;适用于个人用户和企业用户&#xff0c;可以在多种操作系统上进行使用&#xff0c;并且支持广泛的压缩格式和高级功能。 2. 特点与优势 开源免费&#xff1a;7-Zip 是免费的开源软件&#xff0c;可…...

Satellite Stereo Pipeline学习

1.在Anaconda某个环境中安装s2p pip install s2p 2.在Ubuntu系统中安装s2p源代码 git clone https://github.com/centreborelli/s2p.git --recursive cd s2p pip install -e ".[test]" 3.在s2p中进行make all处理 中间会有很多情况&#xff0c;基本上哪个包出问题…...

linux-gpio

在Linux shell中测试GPIO通信&#xff0c;通常需要使用GPIO的设备文件&#xff0c;这些文件通常位于/sys/class/gpio目录下。要使用特定的GPIO引脚&#xff0c;比如GPIO92&#xff0c;你需要执行以下步骤&#xff1a; 导出GPIO引脚&#xff1a;首先&#xff0c;需要确保GPIO92已…...

C# 代码配置的艺术

文章目录 1、代码配置的定义及其在软件工程中的作用2、C# 代码配置的基本概念和工具3、代码配置的实践步骤4、实现代码配置使用属性&#xff08;Properties&#xff09;使用配置文件&#xff08;Config Files&#xff09;使用依赖注入&#xff08;Dependency Injection&#xf…...

268 基于matlab的模拟双滑块连杆机构运动

基于matlab的模拟双滑块连杆机构运动&#xff0c;并绘制运动动画&#xff0c;连杆轨迹可视化输出&#xff0c;并输出杆件质心轨迹、角速度、速度变化曲线。可定义杆长、滑块速度&#xff0c;滑块初始位置等参数。程序已调通&#xff0c;可直接运行。 268 双滑块连杆机构运动 连…...

进口铝合金电动隔膜泵

进口铝合金电动隔膜泵是一种高效、可靠的工业泵&#xff0c;其特点、性能与应用广泛&#xff0c;以下是对其的详细分析&#xff1a; 特点 材质与结构&#xff1a; 采用铝合金材料制造&#xff0c;具有良好的耐腐蚀性和轻量化特点。铝合金材质使得泵体结构紧凑、轻便&#xff…...

G4 - 可控手势生成 CGAN

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 代码总结与心得 代码 关于CGAN的原理上节已经讲过&#xff0c;这次主要是编写代码加载上节训练后的模型来进行指定条件的生成 图像的生成其实只需要使用…...

使用 DuckDuckGo API 实现多种搜索功能

在日常生活中&#xff0c;我经常使用搜索引擎来查找信息&#xff0c;如谷歌和百度。然而&#xff0c;当我想通过 API 来实现这一功能时&#xff0c;会发现这些搜索引擎并没有提供足够的免费 API 服务。如果有这样的免费 API, 就能定时获取“关注实体”的相关内容&#xff0c;并…...

【DrissionPage爬虫库 1】两种模式分别爬取Gitee开源项目

文章目录 DrissionPage爬虫库简介1. 浏览器操控模式&#xff08;类似于游戏中的后台模拟鼠标键盘&#xff09;2. 数据包收发模式&#xff08;类似于游戏中的协议封包&#xff09; 实战中学习需求&#xff1a;爬取Gitee开源项目的标题与描述解决方案1&#xff1a;用数据包方式获…...

leetcode 115.不同的子序列

思路&#xff1a;LCS类dp 这道题的思考思路其实就是把以两个字符串结尾作为状态方程。 dp[i][j]的意义就是在s字符串在以s[i]结尾的字符串的情况下&#xff0c;所能匹配出t字符串以t[j]结尾的字符串个数。 本质上其实是一个LCS类的状态方程&#xff0c;只不过是意义不一样了…...

二叉树的顺序实现-堆

一、什么是堆 在数据结构中&#xff0c;堆&#xff08;Heap&#xff09;是一种特殊的树形数据结构&#xff0c;用数组存储&#xff0c;通常被用来实现优先队列。 堆具有以下特点&#xff1a; 堆是一棵完全二叉树&#xff08;Complete Binary Tree&#xff09;&#xff0c;即…...