|
| 1 | +# Manage apps on remote Kubernetes clusters with ArgoCD |
| 2 | + |
| 3 | +This tutorial shows how to use [ArgoCD](https://argo-cd.readthedocs.io/en/stable/) with [Inlets Uplink](/uplink/) to manage applications across multiple remote Kubernetes clusters from a centralized GitOps control plane. |
| 4 | + |
| 5 | +With Inlets Uplink, each remote cluster connects back to the control plane over a secure, TLS-encrypted tunnel. This allows Argo CD to manage remote clusters without exposing their APIs to the public internet. |
| 6 | + |
| 7 | + |
| 8 | + |
| 9 | +In this tutorial, we will: |
| 10 | + |
| 11 | +- Create Uplink tunnels for one or more remote Kubernetes clusters. |
| 12 | +- Register those clusters with Argo CD. |
| 13 | +- Show how an applicatioin can be deployed to all registerd cluster. |
| 14 | + |
| 15 | +Why use this approach? |
| 16 | + |
| 17 | +- Security - Cluster APIs stay private, with no public exposure. Inlets Uplink creates a secure, encrypted tunnel for Argo CD to connect. |
| 18 | +- Centralized management - Operate and deploy to all clusters from a single Argo CD control plane. |
| 19 | +- Scalability - Add new clusters easily by creating a tunnel and registering it in Argo CD. |
| 20 | + |
| 21 | +This tutorial has been tested with K3s clusters but works with any Kubernetes distribution. |
| 22 | + |
| 23 | +## Prerequisites |
| 24 | + |
| 25 | +- A central cluster with [Inlets Uplink installed](/uplink/installation/) |
| 26 | +- One or more remote K3s clusters to manage |
| 27 | +- [arkade](https://arkade.dev) installed |
| 28 | +- Basic knowledge of Kubernetes and GitOps |
| 29 | + |
| 30 | +## Step 1: Create Uplink tunnels for remote clusters |
| 31 | + |
| 32 | +For each remote cluster, create a dedicated tunnel. These could be created in separate namespaces for better isolation when e.g. adding clusters for multiple different tenants. |
| 33 | + |
| 34 | +Create a namespace for the first remote cluster: |
| 35 | + |
| 36 | +```bash |
| 37 | +export CLUSTER_NAME="production-east" |
| 38 | +export NS="tenant1" |
| 39 | + |
| 40 | +kubectl create namespace $NS |
| 41 | +kubectl label namespace $NS "inlets.dev/uplink"=1 |
| 42 | +``` |
| 43 | + |
| 44 | +Copy your Uplink license to the new namespace: |
| 45 | + |
| 46 | +```bash |
| 47 | +export LICENSE=$(kubectl get secret -n inlets inlets-uplink-license -o jsonpath='{.data.license}' | base64 -d) |
| 48 | + |
| 49 | +kubectl create secret generic \ |
| 50 | + -n $NS \ |
| 51 | + inlets-uplink-license \ |
| 52 | + --from-literal license=$LICENSE |
| 53 | +``` |
| 54 | + |
| 55 | +Create the tunnel resource for the Kubernetes API: |
| 56 | + |
| 57 | +```bash |
| 58 | +kubectl apply -f - <<EOF |
| 59 | +apiVersion: uplink.inlets.dev/v1alpha1 |
| 60 | +kind: Tunnel |
| 61 | +metadata: |
| 62 | + name: kubernetes-$CLUSTER_NAME |
| 63 | + namespace: $NS |
| 64 | +spec: |
| 65 | + licenseRef: |
| 66 | + name: inlets-uplink-license |
| 67 | + namespace: $NS |
| 68 | + tcpPorts: |
| 69 | + - 6443 |
| 70 | +EOF |
| 71 | +``` |
| 72 | + |
| 73 | +> See [create tunnel](/uplink/create-tunnels/) for more details on managing tunnels. |
| 74 | +
|
| 75 | +Get the inlets uplink management CLI, and use it to generate a Kubernetes Deployment definition for the tunnel client. |
| 76 | + |
| 77 | +```bash |
| 78 | +inlets-pro plugin get tunnel |
| 79 | + |
| 80 | +inlets-pro tunnel connect kubernetes-$CLUSTER_NAME \ |
| 81 | + --namespace $NS \ |
| 82 | + --domain uplink.example.com \ |
| 83 | + --upstream 6443=kubernetes.default.svc:443 \ |
| 84 | + --format k8s_yaml > $CLUSTER_NAME-tunnel-client.yaml |
| 85 | +``` |
| 86 | + |
| 87 | +Replace `uplink.example.com` with your actual Uplink domain. |
| 88 | + |
| 89 | +The client Deployment YAML will have to be applied to the remote cluster. |
| 90 | + |
| 91 | +Switch over to the customer’s Kubernetes cluster and apply the YAML for the tunnel client: |
| 92 | + |
| 93 | +```bash |
| 94 | +# Switch to remote cluster context |
| 95 | +kubectl config use-context $CLUSTER_NAME |
| 96 | + |
| 97 | +# Deploy the tunnel client |
| 98 | +kubectl apply -f $CLUSTER_NAME-tunnel-client.yaml |
| 99 | + |
| 100 | +# Verify connection |
| 101 | +kubectl logs deploy/kubernetes-$CLUSTER_NAME-inlets-client |
| 102 | +``` |
| 103 | + |
| 104 | +> See [connect the tunnel client](/uplink/connect-tunnel-client/) for more details on connecting the client. |
| 105 | +
|
| 106 | +Repeat these steps for each remote cluster you want to manage. |
| 107 | + |
| 108 | +## Step 2: Install ArgoCD on the central cluster |
| 109 | + |
| 110 | +Switch back to your central cluster and install ArgoCD: |
| 111 | + |
| 112 | +```bash |
| 113 | +arkade install argocd |
| 114 | +``` |
| 115 | + |
| 116 | +Get the ArgoCD CLI: |
| 117 | + |
| 118 | +```bash |
| 119 | +arkade get argocd |
| 120 | +``` |
| 121 | + |
| 122 | +Port-forward the ArgoCD server: |
| 123 | + |
| 124 | +```bash |
| 125 | +kubectl port-forward svc/argocd-server -n argocd 8080:443 & |
| 126 | +``` |
| 127 | + |
| 128 | +Login to ArgoCD: |
| 129 | + |
| 130 | +```bash |
| 131 | +PASS=$(kubectl get secret argocd-initial-admin-secret \ |
| 132 | + -n argocd \ |
| 133 | + -o jsonpath="{.data.password}" | base64 -d) |
| 134 | + |
| 135 | +argocd login 127.0.0.1:8080 --insecure \ |
| 136 | + --username admin \ |
| 137 | + --password $PASS |
| 138 | +``` |
| 139 | + |
| 140 | +## Step 3: Register remote clusters with ArgoCD |
| 141 | + |
| 142 | +Clusters can be registered declaratively with Argo CD by adding a sectet containing the cluster credentials. Each secret must have label `argocd.argoproj.io/secret-type: cluster`. |
| 143 | +Checkout the [Argo CD documentation](https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#clusters) for more info. |
| 144 | + |
| 145 | +We provide a small utility to generate these secrets from a kubeconfig file. |
| 146 | + |
| 147 | +Generate and apply the cluster secret for a remote cluster: |
| 148 | + |
| 149 | +```bash |
| 150 | +# Ensure the tunnel plugin is installed |
| 151 | +inlets-pro plugin get tunnel |
| 152 | + |
| 153 | +inlets-rpo tunnel argo generate $CLUSTER_NAME \ |
| 154 | + --kubeconfig ~/.kube/$CLUSTER_NAME.yaml \ |
| 155 | + --upstream https://kubernetes-$CLUSTER_NAME.$NS:6443 | \ |
| 156 | + kubectl apply -f - |
| 157 | +``` |
| 158 | + |
| 159 | +Repeat this for each remoter cluster you want to add. |
| 160 | + |
| 161 | +Verify the cluster is registered: |
| 162 | + |
| 163 | +```bash |
| 164 | +argocd cluster list |
| 165 | +``` |
| 166 | + |
| 167 | +You should see your remote cluster listed with the tunneled URL. |
| 168 | + |
| 169 | +## Deploy applications to all clusters |
| 170 | + |
| 171 | +Now that the cluster are reachable and registered with ArgoCD we can start deploying applications to them. From here on we are just going to apply standard ArgoCD practices and patterns. |
| 172 | + |
| 173 | +> Checkout the Argo CD documentation for more info on a [declarative application setup](https://www.openfaas.com/blog/argocd-image-updater-for-functions/) |
| 174 | +
|
| 175 | +As an example we are going to install the [OpenFaaS ArgoCD App](https://github.com/welteki/openfaas-argocd-example). This is a sample application that uses the [app of apps pattern](https://argo-cd.readthedocs.io/en/stable/operator-manual/cluster-bootstrapping/) to deploy two applications. The first application is named `openfaas-core` and deploys [OpenFaaS](https://www.openfaas.com/). The second application is named `openfaas-functions` and deploys a set of OpenFaaS functions. |
| 176 | + |
| 177 | +For a detailed description of the application you can read our blog post: [How to update your OpenFaaS functions automatically with the Argo CD Image Updater](https://www.openfaas.com/blog/argocd-image-updater-for-functions/) |
| 178 | + |
| 179 | +We can deploy the application to a single cluster as described in the original article but ideally we want to deploy it to multiple remote clusters. To achieve this we can use an [ApplicationSet](https://argo-cd.readthedocs.io/en/stable/operator-manual/applicationset/) to automatically generate multiple applications. |
| 180 | + |
| 181 | +An ApplicationSet uses generators to create Applications dynamically. The cluster generator discovers all registered clusters and creates an Application for each one. This eliminates the need to manually create separate Application manifests for every cluster. |
| 182 | + |
| 183 | +Example ApplicationSet for deploying to multiple clusters: |
| 184 | + |
| 185 | +```yaml |
| 186 | +apiVersion: argoproj.io/v1alpha1 |
| 187 | +kind: ApplicationSet |
| 188 | +metadata: |
| 189 | + name: openfaas-multi-cluster |
| 190 | + namespace: argocd |
| 191 | +spec: |
| 192 | + generators: |
| 193 | + # Cluster Generator: Creates parameters for every registered cluster |
| 194 | + - clusters: |
| 195 | + # Use a label selector to filter for cluster Secrets. |
| 196 | + # The 'cluster' label is automatically added to Secrets |
| 197 | + # created for *remote* clusters, but not for the local 'in-cluster' setup. |
| 198 | + selector: |
| 199 | + matchLabels: |
| 200 | + argocd.argoproj.io/secret-type: cluster |
| 201 | + template: |
| 202 | + metadata: |
| 203 | + # Template creates unique app names using cluster name: openfaas-production-east |
| 204 | + name: openfaas-{{name}} |
| 205 | + namespace: argocd |
| 206 | + spec: |
| 207 | + project: default |
| 208 | + source: |
| 209 | + repoURL: https://github.com/welteki/openfaas-argocd-example.git |
| 210 | + path: chart/openfaas |
| 211 | + helm: |
| 212 | + parameters: |
| 213 | + # Override the destination server URL and appNameSuffix with the values |
| 214 | + # provided by the generator |
| 215 | + - name: spec.destination.server |
| 216 | + value: "{{server}}" |
| 217 | + - name: appNameSuffix |
| 218 | + value: "{{name}}" |
| 219 | + destination: |
| 220 | + # Deploy to ArgoCD namespace on central cluster (manages remote deployments) |
| 221 | + server: "https://kubernetes.default.svc" |
| 222 | + namespace: argocd |
| 223 | + syncPolicy: |
| 224 | + automated: |
| 225 | + selfHeal: true |
| 226 | +``` |
| 227 | +
|
| 228 | +Apply the ApplicationSet: |
| 229 | +
|
| 230 | +```bash |
| 231 | +kubectl apply -f openfaas-applicationset.yaml |
| 232 | +``` |
| 233 | + |
| 234 | +The ApplicationSet will automatically create individual Applications for each registered cluster. You can monitor the deployment progress in the ArgoCD UI or via CLI: |
| 235 | + |
| 236 | +```bash |
| 237 | +argocd app list |
| 238 | +``` |
| 239 | + |
| 240 | + |
| 241 | +> Argo CD UI showing the openfaas application deployed to 3 different remore clusters. |
| 242 | +
|
| 243 | +## Conclusion |
| 244 | + |
| 245 | +You now have a reference architecture for centralized GitOps management of multiple remote Kubernetes clusters using ArgoCD and Inlets Uplink. This approach provides: |
| 246 | + |
| 247 | +- **Security**: No public exposure of cluster APIs |
| 248 | +- **Scalability**: Easy addition of new clusters |
| 249 | +- **Centralization**: Single point of control for all deployments |
| 250 | +- **Reliability**: Secure, encrypted tunnel connections |
| 251 | + |
| 252 | +The combination of Inlets Uplink tunnels and ArgoCD provides a robust foundation for multi-cluster GitOps at scale while maintaining strong security boundaries. |
| 253 | + |
| 254 | +If you run into network reliability issues consider running the Uplink tunnels in [demux mode](/uplink/troubleshooting/#demux-mode) to improve reliability. |
0 commit comments