|
| 1 | +--- |
| 2 | +title: Set up remote deployment |
| 3 | +personas: [platform-owner] |
| 4 | +--- |
| 5 | + |
| 6 | +# Set up remote deployment |
| 7 | + |
| 8 | +Use this how-to to deploy Platform Mesh across multiple clusters, where the operator runs on one cluster but manages resources on separate runtime and infra clusters. |
| 9 | + |
| 10 | +## When to use this |
| 11 | + |
| 12 | +Remote deployment fits scenarios where Platform Mesh's control-plane workloads cannot live on the same cluster as the operator. Common cases: |
| 13 | + |
| 14 | +- **Separation of concerns** — keep kcp, OCM, and the `PlatformMesh` resource on a dedicated runtime cluster, while the operator and GitOps tooling run elsewhere. |
| 15 | +- **Shared GitOps cluster** — a central cluster runs FluxCD or ArgoCD and rolls out releases to one or more runtime clusters. |
| 16 | +- **Compliance or isolation** — runtime workloads must run in a network or tenant boundary that the operator cluster cannot host. |
| 17 | + |
| 18 | +If everything runs on the same cluster, you do not need this guide. |
| 19 | + |
| 20 | +::: warning Alpha feature |
| 21 | +Remote deployment is functional but limited to a single remote deployment per operator instance. APIs and Helm values may change. |
| 22 | +::: |
| 23 | + |
| 24 | +## Prerequisites |
| 25 | + |
| 26 | +- A running Kubernetes cluster for the operator (the **operator** cluster) |
| 27 | +- A separate Kubernetes cluster for the runtime workloads (the **runtime** cluster) — this is where kcp, OCM, and the PlatformMesh resource live |
| 28 | +- Optionally, a third cluster for FluxCD or ArgoCD (the **infra** cluster) — if not provided, the operator cluster serves this role |
| 29 | +- `kubectl` configured to access all clusters |
| 30 | +- Helm 3.x installed |
| 31 | + |
| 32 | +## Architecture overview |
| 33 | + |
| 34 | +```mermaid |
| 35 | +graph LR |
| 36 | + subgraph Operator["Operator cluster"] |
| 37 | + OP["platform-mesh-operator<br/>(leader election)"] |
| 38 | + end |
| 39 | +
|
| 40 | + subgraph Runtime["Runtime cluster"] |
| 41 | + PM["PlatformMesh CR"] |
| 42 | + PROF["Profile ConfigMap"] |
| 43 | + KCP["kcp (RootShard, FrontProxy)"] |
| 44 | + OCM["OCM Resources"] |
| 45 | + end |
| 46 | +
|
| 47 | + subgraph Infra["Infra cluster (optional)"] |
| 48 | + FLUX["FluxCD / ArgoCD"] |
| 49 | + HR["HelmReleases"] |
| 50 | + OCI["OCIRepositories"] |
| 51 | + APPS["HelmRepositories"] |
| 52 | + end |
| 53 | +
|
| 54 | + OP -->|"reconciles"| PM |
| 55 | + OP -->|"reads"| PROF |
| 56 | + OP -->|"creates"| HR |
| 57 | + FLUX -->|"deploys via kubeConfig secret"| Runtime |
| 58 | +``` |
| 59 | + |
| 60 | +Remote deployment is considered when the **Runtime** and **Infra** clusters are different. FluxCD HelmReleases receive a `kubeConfig.secretRef` that tells FluxCD to deploy workloads to the runtime cluster. ArgoCD Applications receive a `destination.server` pointing to the runtime cluster API. |
| 61 | + |
| 62 | +## Step 1: Create a kubeconfig secret for the runtime cluster |
| 63 | + |
| 64 | +Generate a kubeconfig that gives the operator access to the runtime cluster. Create a Secret on the **operator** cluster: |
| 65 | + |
| 66 | +```bash |
| 67 | +kubectl create secret generic platform-mesh-kubeconfig \ |
| 68 | + --namespace platform-mesh-system \ |
| 69 | + --from-file=kubeconfig=<path-to-runtime-kubeconfig> |
| 70 | +``` |
| 71 | + |
| 72 | +## Step 2: Create a kubeconfig secret for FluxCD to reach the runtime cluster |
| 73 | + |
| 74 | +FluxCD (running on the infra cluster) needs credentials to deploy workloads to the runtime cluster. Create a Secret on the **infra** cluster: |
| 75 | + |
| 76 | +```bash |
| 77 | +kubectl create secret generic platform-mesh-runtime-secret \ |
| 78 | + --namespace platform-mesh-system \ |
| 79 | + --from-file=kubeconfig=<path-to-runtime-kubeconfig-for-fluxcd> |
| 80 | +``` |
| 81 | + |
| 82 | +This secret is referenced by every HelmRelease via `spec.kubeConfig.secretRef`. |
| 83 | + |
| 84 | +## Step 3: (Optional) Create a kubeconfig secret for the infra cluster |
| 85 | + |
| 86 | +If the operator does not run on the infra cluster (that is, **Operator** != **Infra**), create a Secret on the operator cluster with credentials to reach the infra cluster: |
| 87 | + |
| 88 | +```bash |
| 89 | +kubectl create secret generic platform-mesh-infra-kubeconfig \ |
| 90 | + --namespace platform-mesh-system \ |
| 91 | + --from-file=kubeconfig=<path-to-infra-kubeconfig> |
| 92 | +``` |
| 93 | + |
| 94 | +Skip this step if the operator runs on the same cluster as FluxCD or ArgoCD. |
| 95 | + |
| 96 | +## Step 4: Install the operator with remote deployment enabled |
| 97 | + |
| 98 | +Install the platform-mesh-operator Helm chart with remote deployment values: |
| 99 | + |
| 100 | +```bash |
| 101 | +helm install platform-mesh-operator \ |
| 102 | + oci://ghcr.io/platform-mesh/helm-charts/platform-mesh-operator \ |
| 103 | + --namespace platform-mesh-system --create-namespace \ |
| 104 | + --set remoteRuntime.enabled=true \ |
| 105 | + --set remoteRuntime.secretName=platform-mesh-kubeconfig \ |
| 106 | + --set remoteRuntime.secretKey=kubeconfig \ |
| 107 | + --set remoteRuntime.infra.secretName=platform-mesh-runtime-secret \ |
| 108 | + --set remoteRuntime.infra.secretKey=kubeconfig |
| 109 | +``` |
| 110 | + |
| 111 | +If **Operator** != **Infra**, also add: |
| 112 | + |
| 113 | +```bash |
| 114 | + --set remoteInfra.enabled=true \ |
| 115 | + --set remoteInfra.secretName=platform-mesh-infra-kubeconfig \ |
| 116 | + --set remoteInfra.secretKey=kubeconfig |
| 117 | +``` |
| 118 | + |
| 119 | +### Helm values reference |
| 120 | + |
| 121 | +| Value | Default | Description | |
| 122 | +|-------|---------|-------------| |
| 123 | +| `remoteRuntime.enabled` | `false` | Enable remote runtime cluster reconciliation | |
| 124 | +| `remoteRuntime.secretName` | `platform-mesh-secret` | Secret with kubeconfig to reach the runtime cluster | |
| 125 | +| `remoteRuntime.secretKey` | `kubeconfig` | Key within the secret | |
| 126 | +| `remoteRuntime.infra.secretName` | `platform-mesh-secret` | Secret for FluxCD to deploy to the runtime cluster | |
| 127 | +| `remoteRuntime.infra.secretKey` | `kubeconfig` | Key within the infra secret | |
| 128 | +| `remoteInfra.enabled` | `false` | Enable remote infra cluster (only if Operator != Infra) | |
| 129 | +| `remoteInfra.secretName` | `platform-mesh-kubeconfig` | Secret with kubeconfig to reach the infra cluster | |
| 130 | +| `remoteInfra.secretKey` | `kubeconfig` | Key within the secret | |
| 131 | + |
| 132 | +## Step 5: Create the PlatformMesh resource on the runtime cluster |
| 133 | + |
| 134 | +The PlatformMesh CR and its profile ConfigMap must live on the **runtime** cluster. Switch your kubectl context to the runtime cluster and apply them: |
| 135 | + |
| 136 | +```bash |
| 137 | +kubectl config use-context <runtime-cluster-context> |
| 138 | +``` |
| 139 | + |
| 140 | +Create the profile ConfigMap: |
| 141 | + |
| 142 | +```yaml |
| 143 | +apiVersion: v1 |
| 144 | +kind: ConfigMap |
| 145 | +metadata: |
| 146 | + name: platform-mesh-sample-profile |
| 147 | + namespace: platform-mesh-system |
| 148 | +data: |
| 149 | + profile.yaml: | |
| 150 | + infra: |
| 151 | + deploymentTechnology: fluxcd |
| 152 | + certManager: |
| 153 | + enabled: true |
| 154 | + name: cert-manager |
| 155 | + interval: 5m |
| 156 | + targetNamespace: cert-manager |
| 157 | + ocmResourceName: charts |
| 158 | + values: |
| 159 | + crds: |
| 160 | + enabled: true |
| 161 | + components: |
| 162 | + deploymentTechnology: fluxcd |
| 163 | + ocm: |
| 164 | + repo: |
| 165 | + name: platform-mesh |
| 166 | + component: |
| 167 | + name: platform-mesh |
| 168 | + referencePath: |
| 169 | + - name: core |
| 170 | + services: |
| 171 | + account-operator: |
| 172 | + enabled: true |
| 173 | + values: |
| 174 | + ingress: |
| 175 | + host: "account-operator.{{ .baseDomain }}" |
| 176 | +``` |
| 177 | +
|
| 178 | +Create the PlatformMesh resource: |
| 179 | +
|
| 180 | +```yaml |
| 181 | +apiVersion: core.platform-mesh.io/v1alpha1 |
| 182 | +kind: PlatformMesh |
| 183 | +metadata: |
| 184 | + name: platform-mesh-sample |
| 185 | + namespace: platform-mesh-system |
| 186 | +spec: |
| 187 | + exposure: |
| 188 | + baseDomain: example.com |
| 189 | + port: 443 |
| 190 | + protocol: https |
| 191 | + ocm: |
| 192 | + repo: |
| 193 | + name: platform-mesh |
| 194 | + component: |
| 195 | + name: platform-mesh |
| 196 | + referencePath: |
| 197 | + - name: core |
| 198 | + kcp: |
| 199 | + providerConnections: |
| 200 | + - endpointSliceName: core.platform-mesh.io |
| 201 | + path: root:platform-mesh-system |
| 202 | + secret: platform-mesh-operator-kubeconfig |
| 203 | + adminAuth: true |
| 204 | +``` |
| 205 | +
|
| 206 | +The operator links the two resources by naming convention: a PlatformMesh instance named `platform-mesh-sample` expects a ConfigMap named `platform-mesh-sample-profile` in the same namespace. Override this with `spec.profileConfigMap` if needed. |
| 207 | + |
| 208 | +## Step 6: Verify the deployment |
| 209 | + |
| 210 | +Check the operator logs on the operator cluster: |
| 211 | + |
| 212 | +```bash |
| 213 | +kubectl logs -n platform-mesh-system -l app=platform-mesh-operator --tail=50 |
| 214 | +``` |
| 215 | + |
| 216 | +Verify the PlatformMesh resource status on the runtime cluster: |
| 217 | + |
| 218 | +```bash |
| 219 | +kubectl get platformmesh -n platform-mesh-system -o yaml |
| 220 | +``` |
| 221 | + |
| 222 | +Look for status conditions showing all subroutines succeeded: |
| 223 | + |
| 224 | +```yaml |
| 225 | +status: |
| 226 | + conditions: |
| 227 | + - type: DeploymentSubroutine |
| 228 | + status: "True" |
| 229 | + - type: Ready |
| 230 | + status: "True" |
| 231 | +``` |
| 232 | + |
| 233 | +Check that HelmReleases on the infra cluster include the kubeConfig reference: |
| 234 | + |
| 235 | +```bash |
| 236 | +kubectl get helmreleases -n platform-mesh-system -o yaml | grep -A3 kubeConfig |
| 237 | +``` |
| 238 | + |
| 239 | +## Troubleshooting |
| 240 | + |
| 241 | +| Symptom | Likely cause | |
| 242 | +|---------|-------------| |
| 243 | +| Operator fails to start | The kubeconfig secret does not exist or has the wrong key | |
| 244 | +| PlatformMesh not reconciled | The CR was created on the wrong cluster — it must be on the runtime cluster | |
| 245 | +| HelmRelease stuck | The FluxCD `kubeConfig.secretRef` secret is missing on the infra cluster | |
| 246 | +| cert-manager not ready | OCIRepository not created yet — check ResourceSubroutine logs | |
| 247 | + |
| 248 | +## Related |
| 249 | + |
| 250 | +- [Platform Mesh operator reference](/reference/components/platform-mesh-operator.md) |
| 251 | +- [Set up Platform Mesh locally](./set-up-platform-mesh-locally.md) |
| 252 | +- [Architecture](/concepts/architecture.md) |
0 commit comments