Skip to content

Flaky: pipelines installation readiness wait expires when the KinD API server degrades #3563

Description

@danish9039

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

  • Reduce control-plane pressure during installation, for example by
    staggering component installation rather than applying a whole stack at
    once.
  • Narrow the readiness wait in tests/pipelines_install.sh so that it
    reports the genuinely unhealthy pod. The current
    kubectl wait --for=condition=Ready pods --all --all-namespaces blocks on
    every pod in the cluster, so one unhealthy pod fails the job while naming
    eleven healthy ones, which sends every investigation in the wrong
    direction.
  • Review the inconsistent wait budgets across the installation scripts.
    tests/dashboard_install.sh allows sixty seconds for a rollout while
    tests/pipelines_install.sh allows six hundred seconds for readiness. The
    sixty second budget is tighter than any measured healthy duration.
  • Evaluate whether the ml-pipeline-api-server startup budget of sixty
    seconds is sufficient for a cold cluster. That file is synchronized from
    kubeflow/pipelines under applications/pipeline/upstream, so it must
    not be edited directly. It requires either an overlay in this repository
    or a change accepted upstream.
  • Consider whether components should tolerate a temporarily unavailable
    control plane rather than crash-looping, both workflow-controller
    against the API server and admission against istiod.
  • Determine whether the trigger is control plane slowness or an Istio
    sidecar network path that is not ready when the application starts, since
    the fix differs.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions