Skip to content

[Feature] Support ordered CustomComponentsPattern startup and intra-role component discovery #367

Description

@Dayuxiaoshui

Checklist

Motivation

Checklist:

  • This is a feature request, not a question.
  • This issue is written in English.

Motivation:

CustomComponentsPattern is the most flexible pattern for heterogeneous,multi-pod-per-replica workloads. It is the right abstraction for topologies such as Router -> Leader -> Worker or Leader/Worker -> Router, where each component has a different startup dependency and needs to discover peer components in the same RoleInstance.

Today users can describe the components, but production inference topologies also need two controller-managed behaviors:

  1. Component lifecycle ordering inside one CustomComponentsPattern role.
    A component should be created only after its dependencies are ready.For example, a router can wait for leader and worker components before it starts, or a worker can wait for a leader. Scale-down should respect the reverse order or an explicit deleteAfter order.

  2. Intra-role service discovery for sibling components.
    A component should be able to receive peer component pod FQDNs and allocated port values as environment variables, without hard-coding generated pod names or duplicating controller naming logic in user manifests.

Proposed API:

Use component-level annotations on CustomComponentsPattern components:

  • rolebasedgroup.workloads.x-k8s.io/component-depends-on

    • JSON value with optional startAfter and deleteAfter arrays.
    • Example: {"startAfter": ["leader", "worker"]}.
  • rolebasedgroup.workloads.x-k8s.io/component-discovery

    • JSON value with optional addressRefs and portRefs.
    • addressRefs inject peer pod FQDNs.
    • portRefs inject values allocated by rolebasedgroup.workloads.x-k8s.io/port-allocator.

Example:

customComponentsPattern:
  components:
    - name: leader
      size: 1
      annotations:
        rolebasedgroup.workloads.x-k8s.io/port-allocator: |
          {"allocations":[{"name":"leader-grpc","env":"LEADER_GRPC_PORT","scope":"RoleScoped"}]}
      template:
        spec:
          containers:
            - name: leader
              image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6
    - name: worker
      size: 2
      annotations:
        rolebasedgroup.workloads.x-k8s.io/component-depends-on: |
          {"startAfter":["leader"]}
        rolebasedgroup.workloads.x-k8s.io/component-discovery: |
          {
            "addressRefs":[{"env":"LEADER_ADDR","component":"leader","index":0}],
            "portRefs":[{"env":"LEADER_GRPC_PORT","component":"leader","portName":"leader-grpc"}]
          }
      template:
        spec:
          containers:
            - name: worker
              image: anolis-registry.cn-zhangjiakou.cr.aliyuncs.com/openanolis/nginx:1.14.1-8.6

Expected behavior:

  • If no component-depends-on annotation is present, CustomComponentsPattern keeps the existing parallel create/delete behavior.
  • On scale-out, a component with startAfter is created only when every referenced component has ReadyReplicas >= Size.
  • On scale-in, delete gates are derived from the reverse of startAfter and merged with explicit deleteAfter.
  • Cycles in startup or deletion dependency graphs are detected and handled safely.
  • component-discovery injects address and port environment variables into containers and init containers before pod creation.
  • Controller-directive annotations should be accepted from component-level annotations, not only from template.metadata.annotations.

Acceptance criteria:

  • Unit tests cover dependency parsing, cycle detection, create gating, delete gating, and malformed annotations.
  • Unit tests cover address injection, role-scoped port injection, pod-scoped port injection, unknown components, and missing port values.
  • Reconciler tests cover the end-to-end RBG -> RoleInstanceSet -> RoleInstance component annotation flow.
  • A single-node example demonstrates a three-component topology with ordered startup and intra-role discovery.
  • Documentation clarifies that the feature applies to CustomComponentsPattern only.

Local validation:

The following tests were run successfully in a container without a Kubernetes cluster, using Go 1.24.1:

/usr/local/go/bin/go test ./pkg/component-discovery
/usr/local/go/bin/go test ./pkg/reconciler/roleinstance/sync
/usr/local/go/bin/go test ./pkg/reconciler
/usr/local/go/bin/go test ./api/workloads/v1alpha2
/usr/local/go/bin/go test ./pkg/reconciler/roleinstance/sync -run TestAllNamedComponentsReady -v
/usr/local/go/bin/go test ./pkg/component-discovery -run 'Test(ParseAllComponentDependencies|BuildDeletionGates|InjectComponentDiscovery)' -v

One notable follow-up found during local review:

The RBG construction path merges component-level annotations into the PodTemplate before creating RoleInstance templates, but the RoleInstance scale path checks some controller directives from component.Template.Annotations. If direct RoleInstance usage is intended to be supported, the scale path should also read component.Annotations consistently, or the API documentation should state that RoleInstance is controller-internal.

Related resources:

  • keps/173-enhanced-custom-components-pattern/README.md
  • doc/features/patterns.md
  • examples/basic/rbg/patterns/custom-components-ordered-discovery.yaml
  • pkg/component-discovery/component_lifecycle.go
  • pkg/component-discovery/component_discovery.go
  • pkg/reconciler/roleinstance/sync/instance_scale.go

Related resources

No response
#315

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