Upgrade to UXP quickstart
This quickstart runs an open source Crossplane cluster on your laptop, upgrades it to Upbound Crossplane (UXP), and connects it to a Hub. It takes about 20 minutes and uses two local kind clusters that you delete when you're done.
You start with a Crossplane control plane running a real workload, so you can watch that workload come through the upgrade unchanged and then appear in the Hub Console.
To upgrade a control plane you already run, follow Upgrade Crossplane v2 to Upbound Crossplane instead.
Prerequisites
Before you begin, make sure you have:
Create a Crossplane cluster
Create a kind cluster and install open source Crossplane. You'll install a configuration on this control plane to walk through the upgrade process.
kind create cluster --name uxp-upgrade-demo
When you create the cluster, your kubecontext switches to
kind-uxp-upgrade-demo. Now, install Crossplane on your demo cluster.
helm repo update
helm install crossplane crossplane-stable/crossplane \
--namespace crossplane-system --create-namespace \
--version 2.3.4 --wait
Install a Configuration
This demo uses configuration-app which offers an App API that deploys a Ghost
blog as a Helm release.
kubectl apply -f - <<'EOF'
apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
name: configuration-app
spec:
package: xpkg.upbound.io/upbound/configuration-app:v2.1.0
EOF
Installing the Configuration also installs what it depends on, including
provider-helm. Wait for both to become healthy:
kubectl wait --for=condition=healthy configuration/configuration-app --timeout=5m
kubectl wait --for=condition=healthy provider/upbound-provider-helm --timeout=5m
Configure the Helm provider
provider-helm needs a ProviderConfig, permission to create resources, and
the database credentials the Ghost chart reads.
kubectl apply -f - <<'EOF'
apiVersion: helm.m.crossplane.io/v1beta1
kind: ProviderConfig
metadata:
name: default
namespace: default
spec:
credentials:
source: InjectedIdentity
EOF
kubectl create clusterrolebinding crossplane-provider-helm-cluster-admin \
--clusterrole=cluster-admin \
--group=system:serviceaccounts:crossplane-system
kubectl -n default create secret generic ghost-db \
--from-literal=host=mariadb.default.svc.cluster.local \
--from-literal=username=ghost \
--from-literal=password=ghost
This grants cluster-admin to Crossplane's ServiceAccounts to keep the
quickstart short. Scope the provider's permissions down in a real cluster.
Create a composite resource
Create an App. Crossplane composes it into a Helm Release that deploys
Ghost.
kubectl apply -f - <<'EOF'
apiVersion: platform.upbound.io/v1alpha1
kind: App
metadata:
name: quickstart
namespace: default
spec:
parameters:
providerConfigName: default
helm:
chart:
name: ghost
repo: "oci://registry-1.docker.io/bitnamicharts"
version: 25.0.4
wait: false
passwordSecretRef:
namespace: default
name: ghost-db
EOF
Record the state you're upgrading from
helm list -n crossplane-system
kubectl get pkg
kubectl get app,releases.helm.m.crossplane.io -n default
helm list shows chart crossplane-2.3.4. The App and its Release both
show SYNCED: True and READY: True. You compare against these same commands
after the upgrade.
Upgrade to UXP
UXP needs permission to create the Kubernetes resources its added controllers manage. Grant it before you upgrade.
kubectl apply -f - <<'EOF'
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: crossplane-clusteradmin
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: crossplane
namespace: crossplane-system
EOF
UXP versions match the Crossplane version they build on, up to an -up.N
suffix. This cluster runs Crossplane 2.3.4, so upgrade it to 2.3.4-up.2.
export UXP_VERSION=2.3.4-up.2
helm repo add upbound-stable https://charts.upbound.io/stable && helm repo update
helm upgrade --install crossplane --namespace crossplane-system \
upbound-stable/crossplane --version "${UXP_VERSION}" --wait
Confirm the workload survived
helm list -n crossplane-system
kubectl get pkg
kubectl get app,releases.helm.m.crossplane.io -n default
kubectl get composite -A
helm list now shows chart crossplane-2.3.4-up.2. Every package is still
INSTALLED: True and HEALTHY: True, and the same App and Release are
still READY: True. The upgrade replaced the control plane underneath your
resources without touching the resources themselves.
Create a Hub cluster
The Hub gives you one API and Console for every control plane you run. Run one on a second kind cluster in demo mode, which bundles the Postgres, OIDC, and Gateway that a production install expects you to bring yourself.
Demo mode publishes the Hub gateway on node port 30443, so map it to 8443
on your machine when you create the cluster.
kind create cluster --name hub-quickstart --config - <<'EOF'
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: hub-quickstart
nodes:
- role: control-plane
extraPortMappings:
- containerPort: 30443
hostPort: 8443
protocol: TCP
EOF
When your create this cluster your kubecontext switches to kind-hub-quickstart. The
remaining commands name their context explicitly.
helm upgrade --install hub oci://xpkg.upbound.io/upbound/hub \
--kube-context kind-hub-quickstart \
--namespace hub --create-namespace --version 1.2.0 \
--set global.demo.enabled=true --wait
The demo PostgreSQL writes to an emptyDir, so its pod losing its node loses
your Hub data. Demo mode is for trying the Hub out. Don't run it in production.
Create a registration token
Every control plane gets its own registration token. The connector presents that token the first time it contacts the Hub API, which tells the Hub which control plane the connector speaks for.
- Open the control planes
page and
sign in as
adminwith the passwordadmin. The demo uses a self-signed certificate, so accept your browser's warning to continue. - Register a control plane in the
defaultrealm and name itupgraded. - Copy the registration token. The Hub displays it once.
- Save it for the next step:
export REG_TOKEN=<paste-registration-token-here>
Connect the upgraded control plane
Install hub-connector on the UXP cluster. The connector reaches the Hub at
https://hub.127.0.0.1.nip.io:8443, the same address as your browser, because
the Hub's tokens name that address as their issuer. Inside the UXP cluster,
that name resolves to the Pod itself, so route it to the Hub cluster's node over
the kind Docker network.
These routing steps exist only because both clusters run on your laptop. A production install gives the Hub a hostname every cluster resolves.
Create a Service that forwards port 8443 to the Hub node's gateway port
30443:
HUB_NODE_IP=$(docker inspect hub-quickstart-control-plane \
--format '{{.NetworkSettings.Networks.kind.IPAddress}}')
kubectl --context kind-uxp-upgrade-demo create namespace hub
kubectl --context kind-uxp-upgrade-demo -n hub apply -f - <<EOF
apiVersion: v1
kind: Service
metadata:
name: hub-gateway
spec:
ports:
- name: https
port: 8443
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: hub-gateway
labels:
kubernetes.io/service-name: hub-gateway
addressType: IPv4
ports:
- name: https
port: 30443
endpoints:
- addresses:
- "$HUB_NODE_IP"
EOF
Add a CoreDNS rule that resolves the Hub hostname to that Service. This replaces
kind's default CoreDNS configuration with the same configuration plus one
rewrite line:
kubectl --context kind-uxp-upgrade-demo -n kube-system replace -f - <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
rewrite name exact hub.127.0.0.1.nip.io hub-gateway.hub.svc.cluster.local answer auto
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30 {
disable success cluster.local
disable denial cluster.local
}
loop
reload
loadbalance
}
EOF
kubectl --context kind-uxp-upgrade-demo -n kube-system rollout restart deployment coredns
kubectl --context kind-uxp-upgrade-demo -n kube-system rollout status deployment coredns
The connector only talks to the Hub over HTTPS and verifies its certificate. Copy the demo gateway's self-signed certificate into the UXP cluster, store the registration token, and write the connector values:
kubectl --context kind-hub-quickstart -n hub get secret hub-demo-gateway-tls \
-o jsonpath='{.data.tls\.crt}' | base64 --decode > hub-ca.crt
kubectl --context kind-uxp-upgrade-demo -n hub create configmap hub-ca \
--from-file=ca.crt=hub-ca.crt
kubectl --context kind-uxp-upgrade-demo -n hub \
create secret generic hub-connector-credentials \
--from-literal=registrationToken="$REG_TOKEN"
cat > hub-connector-values.yaml <<'EOF'
connector:
hub:
url: https://hub.127.0.0.1.nip.io:8443
extraVolumes:
- name: hub-ca
configMap:
name: hub-ca
extraVolumeMounts:
- name: hub-ca
mountPath: /etc/hub-ca
readOnly: true
extraEnv:
- name: SSL_CERT_FILE
value: /etc/hub-ca/ca.crt
EOF
extraVolumes and extraEnv mount the certificate and point the connector's
trust store at it. Install the connector:
helm install hub-connector oci://xpkg.upbound.io/upbound/hub-connector \
--kube-context kind-uxp-upgrade-demo \
--version 1.2.0 --namespace hub \
--values hub-connector-values.yaml \
--wait --timeout 3m
Confirm the connector is running:
kubectl --context kind-uxp-upgrade-demo -n hub get pods
See the upgraded control plane in the Hub
The control planes
page now
lists upgraded with a Ready status.
Open the resources
page and filter by that
control plane. The App you created before the upgrade, the Release it
composes, and the packages UXP now manages all appear there.
Clean up
kind delete cluster --name uxp-upgrade-demo
kind delete cluster --name hub-quickstart
Next steps
- Upgrade Crossplane v2 to Upbound Crossplane for upgrading a control plane you already run
- Insights for everything the Explore pages can do
- Builders workshop for real cloud resources
- Crossplane Web UI to browse all your managed resources
- Upbound Query API to query resource states in real time