문제 배경
휴대폰이 경고 문자 메시지를 받았고 온라인 환경 인터페이스가 비정상입니다! ! ! 알람 내용은 특정 외부 서비스 API 상태 코드가 비정상이며 상태 코드가 500이라는 것입니다. 좋은 사람은 첫 번째 반응에서 확인하기 위해 PaaS 플랫폼(KuberSphere)에 갔고 서비스의 포드가 다시 시작되고 다시 시작 후에도 계속 다시 시작되는 것을 발견했습니다. 이때 또 다른 알람 복구 메시지가 수신되었고(알람 메시지 후 약 1분) 상태 코드는 200이었습니다. . . 이 문서에서는 이 문제에 대해 설명합니다.
k8s 상태 확인
k8s 프로브
k8s 프로브는 컨테이너에서 kubelet이 수행하는 주기적인 진단 입니다 . 진단을 수행하기 위해 kubelet은 컨테이너에 의해 구현된 핸들러를 호출합니다 . 세 가지 유형의 핸들러가 있습니다.
- ExecAction : 컨테이너 내에서 지정된 명령을 실행합니다. 명령이 반환 코드 0과 함께 종료되면 진단이 성공한 것으로 간주됩니다.
- CPSocketAction : 지정된 포트에서 컨테이너의 IP 주소에 대해 TCP 검사를 수행합니다. 포트가 열려 있으면 진단이 성공한 것으로 간주됩니다.
- HTTPGetAction : 지정된 포트 및 경로에 있는 컨테이너의 IP 주소에 HTTP Get 요청을 수행합니다. 응답의 상태 코드가 200 이상 400 미만이면 진단이 성공한 것으로 간주됩니다.
k8s 상태 확인 프로브
- livenessProbe (survival probe) : 컨테이너가 살아 있는지(실행 상태) 확인하는 데 사용되며, livenessProbe 프로브가 컨테이너가 비정상임을 감지하면 kubelet은 컨테이너를 "중단"하고 컨테이너의 재시작 전략에 따라 해당 처리를 수행합니다. . 컨테이너에 livenessProbe 프로브가 포함되어 있지 않으면 kubelet은 컨테이너의 livenessProbe 프로브가 반환하는 값이 항상 성공이라고 생각합니다.
- readinessProbe(readiness probe) : 컨테이너 서비스가 가능한지(Ready 상태), Ready 상태에 도달한 파드가 요청을 받을 수 있는지 판단하기 위해 사용된다. 서비스에서 관리하는 포드의 경우 포드가 준비되었는지 여부에 따라 서비스와 PodEndpoint 간의 관계도 설정됩니다. 실행 중인 프로세스 중에 준비 상태가 False가 되면 시스템은 서비스의 백엔드 엔드포인트 목록에서 자동으로 격리한 다음 준비 상태로 복원된 포드를 다시 백엔드 엔드포인트 목록에 추가합니다.
- startupProbe (시작 프로브) : startupProbe가 설정되어 있으면 성공할 때까지 다른 프로브가 금지되며, 성공 후에는 더 이상 프로브를 수행하지 않습니다. 컨테이너 시작 시간이 긴 시나리오에 더 적합합니다. kubernetes 버전 v1.18 이상이 필요합니다.
문제를 식별
구성하는 상태 확인은 다음과 같습니다.
livenessProbe:
httpGet:
path: /actuator/health
port: 8080
scheme: HTTP
initialDelaySeconds: 90
timeoutSeconds: 3
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health
port: 8080
scheme: HTTP
initialDelaySeconds: 90
timeoutSeconds: 3
periodSeconds: 10
successThreshold: 1
failureThreshold: 3
复制代码
다음은 몇 가지 구성 의미입니다.
구성 | 의미 |
---|---|
httpGet.경로 | 요청 경로 가져오기 |
httpGet.port | 요청 포트 가져오기 |
httpGet.체계 | 요청 프로토콜 받기 |
initialDelaySeconds | 컨테이너가 시작 후 상태를 확인하기 전에 대기해야 하는 초기 지연 시간(초)입니다. |
timeoutSeconds | 제한 시간(초), 프로브가 완료될 때까지 대기하는 시간입니다. 시간이 초과되면 프로브가 실패한 것으로 간주됩니다. 기본값은 1초입니다. 최소값은 1입니다. |
기간초 | 프로브 실행 빈도(초), 프로브를 수행하는 빈도(초)입니다. 기본값은 10초입니다. 최소값은 1입니다. |
성공 임계값 | 상태 임계값, 감지 실패 후 최소 연속 성공 감지가 성공합니다. 기본값은 1입니다. 최소값은 1입니다. 활동성 프로브 및 시작 프로브에서 1이어야 합니다. |
실패 임계값 | 비정상 임계값, 프로브가 실패 상태로 전환하는 데 필요한 최소 연속 프로브 실패 수입니다. |
회사에서 제공하는 환경설정 문서에는 스타트업 프로브의 환경설정이 포함되어 있지 않은데, 배포된 k8s 버전이 스타트업 프로브를 지원하지 않는 것으로 추측된다.
문제의 배경에서 몇 가지 핵심 .
- 팟(Pod)이 다시 시작 중입니다.
- 포드 다시 시작이 완료되면 계속해서 다시 시작됩니다.
- 告警短信大概一分钟后告警恢复。
到这里可以联想到,存活探针发送get请求获取到的响应的状态码不在 200 和 400之间或者直接超时,所以容器重启直接影响服务,告警通知;但是配置的初始延迟为90秒太短导致一直重启;就在这时就绪探针判断 Ready 状态变为False,则系统自动将其从 Service 的后端 Endpoint 列表中隔离出去,故障 pod 排除掉之后,告警恢复。
是什么原因导致正在运行的容器,PaaS平台是有事件日志的,当时忘记截图了(盘的时候查不到了)... 记得当时有http超时事件,也有状态码为503的事件。超时可能是网络波动或者大概率是初始延迟设置过短。那么这个503状态码到底是为什么呢?
Actuator
Actuator是Springboot的一个模块,模块提供了Spring Boot的所有生产就绪功能。
Endpoints
Actuator 端点允许您监视应用程序并与之交互。 Spring Boot 包括许多内置端点,并允许您添加自己的端点。 例如,提供基本的应用程序运行状况信息的 health 端点。
Actuator的health端点
我们配置健康检查用的接口就是Actuator提供的 health 端点接口。像我们引入DB依赖,Nacos依赖啥的,这些依赖实现了Actuator的health策略接口HealthIndicator
,请求health端点的时候就会调用策略实现类检查健康状况。
health端点的返回会返回一个status,可以通过配置management.endpoint.health.show-details=always
设置返回详细信息。
下边是一个详细信息的返回值。
{
"components":{
"db":{
"components":{
"dataSource":{
"details":{
"database":"MySQL",
"result":1,
"validationQuery":"/* ping */ SELECT 1"
},
"status":{
"code":"UP",
"description":""
}
},
"dataSource2":{
"details":{
"database":"MySQL",
"result":1,
"validationQuery":"/* ping */ SELECT 1"
},
"status":{
"code":"UP",
"description":""
}
}
},
"status":{
"code":"UP",
"description":""
}
},
"discoveryComposite":{
"components":{
"discoveryClient":{
"details":{
"services":[
"***",
"***",
"***-gateway"
]
},
"status":{
"code":"UP",
"description":""
}
}
},
"status":{
"code":"UP",
"description":""
}
},
"diskSpace":{
"details":{
"total":"528309530624",
"free":"463192977408",
"threshold":10485760
},
"status":{
"code":"UP",
"description":""
}
},
"mail":{
"details":{
"location":"10.************:25"
},
"status":{
"code":"UP",
"description":""
}
},
"ping":{
"details":{
},
"status":{
"code":"UP",
"description":""
}
},
"refreshScope":{
"details":{
},
"status":{
"code":"UP",
"description":""
}
}
},
"groups":[
],
"status":{
"code":"UP",
"description":""
}
}
复制代码
返回的Status的code编码有四种。
/**
* 指示组件或子系统处于未知状态。
*/
public static final Status UNKNOWN = new Status("UNKNOWN");
/**
* 指示组件或子系统按预期运行。
*/
public static final Status UP = new Status("UP");
/**
* 指示组件或子系统发生了意外故障。
*/
public static final Status DOWN = new Status("DOWN");
/**
* 指示组件或子系统已从服务中取出,不应再使用。
*/
public static final Status OUT_OF_SERVICE = new Status("OUT_OF_SERVICE");
复制代码
翻看源码看到了这四个编码与http状态码的关系,即 DOWN 和 OUT_OF_SERVICE 会返回http状态码 503,其他返回200状态码。
health端点如果异常,即可以通过详细信息定位到异常的组件!