Problem
Workflows that call tests/pipelines_install.sh fail intermittently. The
visible symptom is the readiness wait in tests/pipelines_install.sh expiring
after six hundred seconds while reporting twelve pods as not ready:
timed out waiting for the condition on pods/ml-pipeline-6848b5d4d7-pcd2v
timed out waiting for the condition on pods/ml-pipeline-persistenceagent-646f88694b-gh5sc
timed out waiting for the condition on pods/ml-pipeline-scheduledworkflow-f4994d98-sqbcq
timed out waiting for the condition on pods/ml-pipeline-ui-6f6789c496-sn7b4
timed out waiting for the condition on pods/ml-pipeline-viewer-crd-74d9fbf5cd-xw9bj
timed out waiting for the condition on pods/ml-pipeline-visualizationserver-787f4cb9f7-jt969
timed out waiting for the condition on pods/mysql-5b6cf8556-kx2rr
timed out waiting for the condition on pods/seaweedfs-5f6996f6f-kfprv
timed out waiting for the condition on pods/workflow-controller-cd549fb55-gwt7f
timed out waiting for the condition on pods/local-path-provisioner-855c7b7774-j5b72
timed out waiting for the condition on pods/oauth2-proxy-55765f6898-95fm4
timed out waiting for the condition on pods/oauth2-proxy-55765f6898-9kwrj
##[error]Process completed with exit code 1.
That message is misleading. kubectl wait --all --all-namespaces reports every
pod it was still watching, not the pods that are actually broken. Diagnostics
collected at the moment of failure show that only one or two pods were ever
unhealthy.
The timeout is not the cause
The wait budget is six hundred seconds. A healthy run completes the same wait in
about thirty five seconds, measured from a sampled green run: the pipelines
installation step ran from 07:56:37 to 07:58:03, and sixty of those eighty six
seconds are the hardcoded sleep 60 on the line above. The margin is roughly
seventeen times. Raising the timeout would not fix this and would only make
the failure slower to surface.
Root cause
Calls into cluster critical services do not complete while roughly a dozen pods
and their Istio sidecars start at once. Two different services have been
observed failing this way, so this is not specific to one component.
The Kubernetes API server. workflow-controller cannot complete a TLS
handshake against the API server service address:
level=ERROR msg="Failed to register watch for controller config map"
error="Get \"https://10.96.0.1:443/api/v1/namespaces/kubeflow/configmaps/workflow-controller-configmap\": net/http: TLS handshake timeout"
ml-pipeline-api-server fails its startup probe for the same reason and is
terminated by the kubelet:
Warning Unhealthy Startup probe failed: HTTP probe failed with statuscode: 500
Normal Killing Container ml-pipeline-api-server failed startup probe, will be restarted
Last State: Terminated
Reason: Error
Exit Code: 137
Ready: False
Restart Count: 6
Its startup budget is failureThreshold: 12 multiplied by periodSeconds: 5,
that is sixty seconds, defined in
applications/pipeline/upstream/base/pipeline/ml-pipeline-apiserver-deployment.yaml.
Every termination restarts initialisation from the beginning, so the loop
sustains itself. All three observed occurrences reached exactly six restarts.
The Istio injection webhook. In a separate occurrence the dashboard pod was
never created at all, because admission timed out:
Error creating: Internal error occurred: failed calling webhook
"namespace.sidecar-injector.istio.io": failed to call webhook:
Post "https://istiod.istio-system.svc:443/inject?timeout=10s": context deadline exceeded
Correction to an earlier version of this issue
This issue previously stated the cause as API server degradation, citing
kube-apiserver ... Readiness probe failed: HTTP probe failed with statuscode: 500. That event appears in two of the four captured failures and is absent
from the others, so it is a symptom that sometimes accompanies the problem
rather than the cause. Whether the underlying trigger is API server slowness or
an Istio sidecar network path that is not ready when the application starts is
still undetermined.
A misleading symptom
kubectl wait --for=condition=Ready pods --all --all-namespaces reports every
pod it was still watching, not the pods that are broken. It names about twelve
pods while diagnostics show only one to four are genuinely unhealthy, which
sends investigations toward innocent components such as
local-path-provisioner and oauth2-proxy.
Evidence from four captured failures
The first two were reproduced deliberately by re-triggering the affected
workflows. The third and fourth were captured automatically on unrelated pull
requests once the diagnostics were merged, which shows this affects whatever
work is in flight.
| Run |
Pull request |
Workflow and failing step |
Signature |
| 30247776370 |
3560 |
pipeline_run_from_notebook, Install KF Pipelines |
ml-pipeline 1/2 ready, exit 137, 6 restarts |
| 30259363189 |
3560 |
pipeline_run_from_notebook, Install KF Pipelines |
same, plus workflow-controller crash loop, 6 restarts |
| 30433986686 |
3524 |
workspaces_pipeline_run_test, Install KF Pipelines |
same, plus ml-pipeline-scheduledworkflow, ml-pipeline-persistenceagent, metadata-grpc |
| 30437973166 |
3566 |
pipeline_run_from_notebook, Install Dashboard |
dashboard and profiles pods never created, istiod webhook timed out |
The ml-pipeline container reached exactly six restarts in every occurrence
where it was involved. Persistent volume claims were Bound and no node
reported a pressure condition in every run where those were captured.
What the evidence rules out
- Memory exhaustion. Available memory never fell below 10458 MiB of 15 GiB
in a sampled run, no node reported MemoryPressure, DiskPressure or
PIDPressure, and the terminated container reports Reason: Error rather
than OOMKilled.
- Processor starvation as the direct trigger. Load average at the point of
failure was 0.60 over one minute and 1.29 over fifteen minutes on four
processors.
- A storage provisioner cascade. Both persistent volume claims were
Bound
in both failures, and claims bind in about eleven seconds on a healthy run.
- An insufficient wait budget. See the measurement above.
Proposed work
Related
Problem
Workflows that call
tests/pipelines_install.shfail intermittently. Thevisible symptom is the readiness wait in
tests/pipelines_install.shexpiringafter six hundred seconds while reporting twelve pods as not ready:
That message is misleading.
kubectl wait --all --all-namespacesreports everypod it was still watching, not the pods that are actually broken. Diagnostics
collected at the moment of failure show that only one or two pods were ever
unhealthy.
The timeout is not the cause
The wait budget is six hundred seconds. A healthy run completes the same wait in
about thirty five seconds, measured from a sampled green run: the pipelines
installation step ran from 07:56:37 to 07:58:03, and sixty of those eighty six
seconds are the hardcoded
sleep 60on the line above. The margin is roughlyseventeen times. Raising the timeout would not fix this and would only make
the failure slower to surface.
Root cause
Calls into cluster critical services do not complete while roughly a dozen pods
and their Istio sidecars start at once. Two different services have been
observed failing this way, so this is not specific to one component.
The Kubernetes API server.
workflow-controllercannot complete a TLShandshake against the API server service address:
ml-pipeline-api-serverfails its startup probe for the same reason and isterminated by the kubelet:
Its startup budget is
failureThreshold: 12multiplied byperiodSeconds: 5,that is sixty seconds, defined in
applications/pipeline/upstream/base/pipeline/ml-pipeline-apiserver-deployment.yaml.Every termination restarts initialisation from the beginning, so the loop
sustains itself. All three observed occurrences reached exactly six restarts.
The Istio injection webhook. In a separate occurrence the dashboard pod was
never created at all, because admission timed out:
Correction to an earlier version of this issue
This issue previously stated the cause as API server degradation, citing
kube-apiserver ... Readiness probe failed: HTTP probe failed with statuscode: 500. That event appears in two of the four captured failures and is absentfrom the others, so it is a symptom that sometimes accompanies the problem
rather than the cause. Whether the underlying trigger is API server slowness or
an Istio sidecar network path that is not ready when the application starts is
still undetermined.
A misleading symptom
kubectl wait --for=condition=Ready pods --all --all-namespacesreports everypod it was still watching, not the pods that are broken. It names about twelve
pods while diagnostics show only one to four are genuinely unhealthy, which
sends investigations toward innocent components such as
local-path-provisionerandoauth2-proxy.Evidence from four captured failures
The first two were reproduced deliberately by re-triggering the affected
workflows. The third and fourth were captured automatically on unrelated pull
requests once the diagnostics were merged, which shows this affects whatever
work is in flight.
pipeline_run_from_notebook,Install KF Pipelinesml-pipeline1/2 ready, exit 137, 6 restartspipeline_run_from_notebook,Install KF Pipelinesworkflow-controllercrash loop, 6 restartsworkspaces_pipeline_run_test,Install KF Pipelinesml-pipeline-scheduledworkflow,ml-pipeline-persistenceagent,metadata-grpcpipeline_run_from_notebook,Install DashboardThe
ml-pipelinecontainer reached exactly six restarts in every occurrencewhere it was involved. Persistent volume claims were
Boundand no nodereported a pressure condition in every run where those were captured.
What the evidence rules out
in a sampled run, no node reported
MemoryPressure,DiskPressureorPIDPressure, and the terminated container reportsReason: Errorratherthan
OOMKilled.failure was 0.60 over one minute and 1.29 over fifteen minutes on four
processors.
Boundin both failures, and claims bind in about eleven seconds on a healthy run.
Proposed work
staggering component installation rather than applying a whole stack at
once.
tests/pipelines_install.shso that itreports the genuinely unhealthy pod. The current
kubectl wait --for=condition=Ready pods --all --all-namespacesblocks onevery pod in the cluster, so one unhealthy pod fails the job while naming
eleven healthy ones, which sends every investigation in the wrong
direction.
tests/dashboard_install.shallows sixty seconds for a rollout whiletests/pipelines_install.shallows six hundred seconds for readiness. Thesixty second budget is tighter than any measured healthy duration.
ml-pipeline-api-serverstartup budget of sixtyseconds is sufficient for a cold cluster. That file is synchronized from
kubeflow/pipelinesunderapplications/pipeline/upstream, so it mustnot be edited directly. It requires either an overlay in this repository
or a change accepted upstream.
control plane rather than crash-looping, both
workflow-controlleragainst the API server and admission against istiod.
sidecar network path that is not ready when the application starts, since
the fix differs.
Related
ci: collect cluster diagnostics for failing component tests #3560
ci: set a job level timeout on the KinD based workflows #3566