开启mutual TLS如何进行健康检查

在未开启双向tls认证时,kubelet发出健康检查请求,虽然经过了envoy,但是能够正常的转发到我们的应用服务。

但是在启用双向TLS时,对liveness-http服务的运行状况检查请求是由Kubelet发送的,而Kubelet没有Istio颁发的证书。因此,启用双向TLS后,运行状况检查请求将失败。

为了能够正常的响应健康检查,Istio通过重写应用程序PodSpec 就绪/活跃性探针的方式解决了此问题,从而将探针请求发送到Sidecar代理。然后,sidecar代理将请求重定向到应用程序,剥离响应主体,仅返回响应代码。

默认情况下,所有内置Istio配置文件中都启用了此功能

使用HTTP请求方法的活跃性和就绪性探针

默认情况下,Istio使用探针重写来实现HTTP探针。您可以为特定的pod或全局禁用此功能。

禁用Pod的HTTP探针重写

您可以使用注释注释pod,sidecar.istio.io/rewriteAppHTTPProbers: "false" 以禁用探针重写选项。确保将注释添加到pod资源,因为注释 将在其他任何地方(例如,在封闭的部署资源上)被忽略。

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: liveness-http
spec:
  selector:
    matchLabels:
      app: liveness-http
      version: v1
  template:
    metadata:
      labels:
        app: liveness-http
        version: v1
      annotations:
        sidecar.istio.io/rewriteAppHTTPProbers: "false"
    spec:
      containers:
      - name: liveness-http
        image: docker.io/istio/health:example
        ports:
        - containerPort: 8001
        livenessProbe:
          httpGet:
            path: /foo
            port: 8001
          initialDelaySeconds: 5
          periodSeconds: 5
EOF

这种方法在单个deployment上禁用运行状况检查探针重写,而无需重新安装Istio。

istio重写健康检查原理

当开启rewriteAppHTTPProbers时将在注入时对容器进行改写

添加路径到istio-proxy的环境变量

if rewrite && sidecar != nil {
        if prober := DumpAppProbers(&pod.Spec); prober != "" {
            sidecar.Env = append(sidecar.Env, corev1.EnvVar{Name: status.KubeAppProberEnvName, Value: prober})
        }
    }

对于上面的示例服务我们可以看到添加了以下环境变量

    - name: ISTIO_KUBE_APP_PROBERS
      value: '{"/app-health/liveness-http/livez":{"httpGet":{"path":"/foo","port":8001,"scheme":"HTTP"},"timeoutSeconds":1}}'

生成path,用于修改pod的配置

patch = append(patch, createProbeRewritePatch(pod.Annotations, &pod.Spec, sic, mesh.GetDefaultConfig().GetStatusPort())...)

分别对readyness,liveness,startup 三种probe进行重写,根据容器名称生成健康检查路径

func FormatProberURL(container string) (string, string, string) {
    return fmt.Sprintf("/app-health/%v/readyz", container),
        fmt.Sprintf("/app-health/%v/livez", container),
        fmt.Sprintf("/app-health/%v/startupz", container)
}

将原有的probe转换为新的probe配置

func convertAppProber(probe *corev1.Probe, newURL string, statusPort int) *corev1.Probe {
    if probe == nil || probe.HTTPGet == nil {
        return nil
    }
    p := probe.DeepCopy()
    // 修改容器的probe配置
    p.HTTPGet.Port = intstr.FromInt(statusPort)
    p.HTTPGet.Path = newURL
    // Kubelet -> HTTP -> Pilot Agent -> HTTPS -> Application
    if p.HTTPGet.Scheme == corev1.URISchemeHTTPS {
        p.HTTPGet.Scheme = corev1.URISchemeHTTP
    }
    return p
}

pilot-agent 响应健康请求

通过handleAppProbe对健康检查进行响应

mux.HandleFunc("/app-health/", s.handleAppProbe)

handleAppProbe根据对应的请求路径构建httpclient,请求本地的应用服务,因为在iptables的劫持策略中同用户的lo网卡的流量直接转发不经过envoy,从而达成请求真实应用服务的目的

总结

istio 通过mutatingwebhook 对原有pod的配置进行修改,生成由pilot-agent响应健康检查的配置,优雅的兼容在开启双向 tls情况下,kubelet无法请求成功的问题,同时由pilot-agent发出的请求直达应用服务,也避免了envoy产生大量健康检查日志的问题。

Last updated