Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
243 changes: 114 additions & 129 deletions federated-learning-controller/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,35 @@ This Kubernetes controller automates the deployment and management of federated

![Controller Architecture](./assets/images/architecture.png)

---
---

## Architecture

### Flower 1.26.x (SuperLink/SuperNode)

The Flower path leverages the [Flower Addon](../flower-addon/) to pre-deploy the SuperLink/SuperNode infrastructure. The controller deploys only the application layer on top:

```
Hub Cluster:
├── SuperLink (pre-deployed by Flower Addon)
├── SuperExec-ServerApp Deployment (deployed by FederatedLearning Controller)
│ └── connects to SuperLink exec API (port 9091)
└── FederatedLearning Controller

Managed Clusters:
├── SuperNode (pre-deployed by Flower Addon)
└── SuperExec-ClientApp Deployment (deployed via ManifestWorkReplicaSet)
└── connects to SuperNode ClientAppIO API (port 9094)
```

- **ServerApp** is deployed as a Deployment on the hub cluster, connecting to the SuperLink exec API.
- **ClientApp** is deployed to managed clusters via a single ManifestWorkReplicaSet, which automatically handles cluster adds/removes through OCM Placement.

### OpenFL

The OpenFL path manages the full server/client lifecycle using Jobs, Services, and per-cluster ManifestWorks.

---

## Getting Started

Expand All @@ -24,9 +52,9 @@ Ensure the following tools are installed:
Optional (for container image building):

- Podman or Docker
- Go (version 1.19 or later)
- Go (version 1.23 or later)

---
---

### Set Up the Environment

Expand All @@ -51,11 +79,34 @@ cluster1 true https://cluster1-control-plane:6443 True True
cluster2 true https://cluster2-control-plane:6443 True True 3m
```

#### 4. Deploy the Flower Addon (required for Flower framework)

The Flower addon pre-deploys SuperLink on the hub and SuperNode on managed clusters:

```bash
cd ../flower-addon
make deploy
make enable-addon CLUSTER=cluster1
make enable-addon CLUSTER=cluster2
```

Verify the addon is running:

```bash
$ kubectl get pods -n flower-system
NAME READY STATUS RESTARTS AGE
superlink-c8d95648d-6vdv8 1/1 Running 0 1m

$ kubectl get managedclusteraddons -A | grep flower
cluster1 flower-addon True
cluster2 flower-addon True
```

#### Optional: Configure Environment for Observability

Please refer to the [Observability Setup](docs/configure-environment-observability.md) documentation for more details.

---
---

### Deploy Federated Learning Controller

Expand Down Expand Up @@ -124,15 +175,13 @@ federated-learning-controller-d7df846c9-nb4wc 1/1 Running 0 3
```
</details>

---

### Deploy the Federated Learning Instance
---

#### 1. Deploy a Federated Learning Instance
### Deploy a Federated Learning Instance (Flower)

In this example, both the server and clients use the pre-built image `quay.io/open-cluster-management/federated-learning-application:flower-mnist-latest`. Once the resource is created, the server is deployed to the hub cluster, and the clients are prepared for deployment to the managed clusters.
#### 1. Create a FederatedLearning Resource

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#### 1. Create a FederatedLearning Resource
#### 1. Create a Federated Learning Resource

Should there be a space between FederatedLearning?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the resource name is FederatedLearning


Create a `FederatedLearning` resource in the controller namespace on the hub cluster:
The Flower path requires the Flower addon to be installed. The controller deploys a SuperExec-ServerApp on the hub and SuperExec-ClientApp Deployments on managed clusters via ManifestWorkReplicaSet.

```yaml
apiVersion: federation-ai.open-cluster-management.io/v1alpha1
Expand All @@ -142,156 +191,92 @@ metadata:
spec:
framework: flower
server:
image: quay.io/open-cluster-management/federated-learning-application:flower-mnist-latest
rounds: 3
image: quay.io/open-cluster-management/flower-app:cifar10-v1.0.0
minAvailableClients: 2
listeners:
- name: server-listener
port: 8080
type: NodePort
storage:
type: PersistentVolumeClaim # switch to S3Bucket for S3-backed static volumes
name: model-pvc
path: /data/models
size: 2Gi
# s3:
# bucketName: <your-bucket-name>
# region: us-east-1
# prefix: optional/prefix/
# superlink: superlink.flower-system:9091 # default, can be omitted
client:
image: quay.io/open-cluster-management/federated-learning-application:flower-mnist-latest
image: quay.io/open-cluster-management/flower-app:cifar10-v1.0.0
# supernode: flower-supernode.flower-addon:9094 # default, can be omitted
placement:
clusterSets:
- global
predicates:
- requiredClusterSelector:
claimSelector:
matchExpressions:
- key: federated-learning-sample.client-data
operator: Exists
labelSelector:
matchLabels:
feature.open-cluster-management.io/addon-flower-addon: available
```

> **Note**: Only `NodePort` is supported in KinD clusters.
Key fields:

<details>
| Field | Description | Default |
|-------|-------------|---------|
| `server.superlink` | SuperLink exec API endpoint | `superlink.flower-system:9091` |
| `client.supernode` | SuperNode ClientAppIO API endpoint | `flower-supernode.flower-addon:9094` |
| `server.minAvailableClients` | Minimum clusters required before training starts | - |
| `client.placement` | OCM Placement spec for cluster selection | - |

<summary><strong>Alternatively: Build and Use Your Own Application Image</strong></summary>
#### 2. Check the Federated Learning Instance Status

### Containerized Federated Learning Application
After creating the resource, the controller transitions through phases:

The controller manages the lifecycle of federated learning across multiple clusters by creating server and client jobs from your containerized app.
- **Waiting**: Placement is created, waiting for enough clusters to be selected.
- **Running**: ServerApp and ClientApp are deployed. The CR stays in `Running` until deleted.

- **Server Job Example**:
```bash
$ kubectl get fl
NAME PHASE AGE
federated-learning-sample Running 30s
```

```bash
server --num-rounds <number>
```
Verify the resources:

```bash
# ServerApp Deployment on hub
$ kubectl get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
federated-learning-sample-serverapp 1/1 1 1 30s

# ManifestWorkReplicaSet for client distribution
$ kubectl get manifestworkreplicasets
NAME PLACEMENT FOUND MANIFESTWORKS APPLIED
federated-learning-sample-clientapp federated-learning-sample True AsExpected True

# ClientApp Deployments on managed clusters
$ kubectl get deployments --context kind-cluster1 -n flower-addon
NAME READY UP-TO-DATE AVAILABLE AGE
federated-learning-sample-clientapp 1/1 1 1 30s
flower-supernode 1/1 1 1 10m
```

- **Client Job Example**:
#### 3. Trigger Training

```bash
client --data-config <data-path> --server-address <address> ...
```
With SuperExec-ServerApp and SuperExec-ClientApp running, use `flwr run` against the SuperLink to trigger a training round:

You can use the [Flower PyTorch App](./examples/flower/) as a reference template. Customize the model, adjust hyperparameters, add different datasets, etc. Ensure your built image can be launched as server and client using the command patterns above.
```bash
flwr run --insecure --run-config 'num-server-rounds=3' \
<your-flower-app> \
<superlink-connection>
```

**Navigate to the flower example directory:**
<details>

```bash
cd examples/flower
```
<summary><strong>Build Your Own Flower App Image</strong></summary>

**Build and push the application image:**
You can use the [Flower PyTorch App](./examples/flower/) as a reference. The app image must contain a Flower `ServerApp` and `ClientApp` that are compatible with the SuperExec architecture.

```bash
cd examples/flower
export IMAGE_REGISTRY=<your-registry>
export IMAGE_TAG=<your-tag>
export APP_NAME=flower-mnist
export APP_NAME=cifar10
make build-app-image
make push-app-image
```

This will create an image with the format: `<IMAGE_REGISTRY>/federated-learning-application:<APP_NAME>-<IMAGE_TAG>`

**Update the YAML with your custom image:**

Replace the `image` fields in both `server` and `client` sections with your custom image reference.

</details>

#### 2. Schedule the Federated Learning Clients into Managed Clusters

The above configuration schedules only clusters with a `ClusterClaim` having the key `federated-learning-sample.client-data`. You can combine this with other scheduling policies (refer to the Placement API for details).

Add the `ClusterClaim` to these clusters own the data for the client:

**Cluster1:**

```yaml
apiVersion: cluster.open-cluster-management.io/v1alpha1
kind: ClusterClaim
metadata:
name: federated-learning-sample.client-data
spec:
value: /data/private/cluster1
```

**Cluster2:**

```yaml
apiVersion: cluster.open-cluster-management.io/v1alpha1
kind: ClusterClaim
metadata:
name: federated-learning-sample.client-data
spec:
value: /data/private/cluster2
```

#### 3. Check the Federated Learning Instance Status

- After creating the instance, the server initially shows a status of `Waiting`

Example - server in hub cluster:

```bash
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
federated-learning-sample-server-7jnfs 0/1 Completed 0 10m
```

- Once the required clients are ready, status changes to `Running`

Example - client in managed cluster

```bash
$ kubectl get pods -n open-cluster-management
NAME READY STATUS RESTARTS AGE
federated-learning-sample-client-75sc8 0/1 Completed 0 10m
```

- After the training and aggregation rounds complete, the status becomes `Completed`

Example - Federated Learning instance:

```yaml
status:
listeners:
- address: 172.18.0.2:31166
name: listener(service):federated-learning-sample-server
port: 31166
type: NodePort
message: Model training successful. Check storage for details
phase: Completed
```

#### 4. Download and Verify the Trained Model

The trained model is saved in the `model-pvc` volume.

- [Deploy a Jupyter notebook server](./examples/notebooks/deploy)
- [Validate the model](./examples/notebooks/1.hub-evaluation.ipynb)

---
---

### To Uninstall

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ type FederatedLearningSpec struct {
type ClientSpec struct {
Image string `json:"image,omitempty"`
Placement clustersv1beta1.PlacementSpec `json:"placement,omitempty"`

// SuperNode is the endpoint of the pre-deployed SuperNode ClientAppIO API.
// Format: <service>.<namespace>:<port>
// +kubebuilder:default="flower-supernode.flower-addon:9094"
// +optional
SuperNode string `json:"supernode,omitempty"`
}

// ServerSpec defines the specification for the server in federated learning.
Expand All @@ -82,6 +88,12 @@ type ServerSpec struct {
MinAvailableClients int `json:"minAvailableClients,omitempty"`
Listeners []ListenerSpec `json:"listeners,omitempty"`
Storage ModelStorageSpec `json:"storage,omitempty"`

// SuperLink is the endpoint of the pre-deployed SuperLink exec API.
// Format: <service>.<namespace>:<port>
// +kubebuilder:default="superlink.flower-system:9091"
// +optional
SuperLink string `json:"superlink,omitempty"`
}

// ModelStorageSpec defines the storage specification for the model.
Expand Down
2 changes: 2 additions & 0 deletions federated-learning-controller/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
clustersv1beta1 "open-cluster-management.io/api/cluster/v1beta1"
clusterv1beta2 "open-cluster-management.io/api/cluster/v1beta2"
workv1 "open-cluster-management.io/api/work/v1"
workv1alpha1 "open-cluster-management.io/api/work/v1alpha1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
Expand All @@ -62,6 +63,7 @@ func init() {
utilruntime.Must(clusterv1beta2.AddToScheme(scheme)) // clustersetbinding
utilruntime.Must(clusterv1.AddToScheme(scheme)) // managedcluster
utilruntime.Must(workv1.AddToScheme(scheme)) // work
utilruntime.Must(workv1alpha1.AddToScheme(scheme)) // manifestworkreplicaset
utilruntime.Must(routev1.AddToScheme(scheme))
// +kubebuilder:scaffold:scheme
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,12 @@ spec:
type: object
type: array
type: object
supernode:
default: flower-supernode.flower-addon:9094
description: |-
SuperNode is the endpoint of the pre-deployed SuperNode ClientAppIO API.
Format: <service>.<namespace>:<port>
type: string
type: object
framework:
default: other
Expand Down Expand Up @@ -596,6 +602,12 @@ spec:
description: StorageType represents the type of storage.
type: string
type: object
superlink:
default: superlink.flower-system:9091
description: |-
SuperLink is the endpoint of the pre-deployed SuperLink exec API.
Format: <service>.<namespace>:<port>
type: string
type: object
type: object
status:
Expand Down
Loading
Loading