11 KiB
| title | toc | weight | indent |
|---|---|---|---|
| Scheduling Workloads | true | 5 | true |
Scheduling Workloads to Remote Clusters
In the previous two examples, we provisioned infrastructure that is consumed by some form of application. However, many providers expose services that you run your application on. The most obvious example of this type of service would be a managed Kubernetes service, such as GKE, EKS, or AKS. Crossplane not only provisions and manages these types of infrastructure, but also allows you to schedule workloads to them.
In the case of a Kubernetes cluster, Crossplane lets you schedule to remote Kubernetes clusters from a single control cluster. The remote cluster may have been, but does not have to been provisioned by Crossplane. Importantly, each remote cluster maintains its own control plane. Crossplane is only responsible for sending configuration data to the remote cluster.
Control Cluster: the Kubernetes cluster where Crossplane is installed. It may also be used to run workloads besides Crossplane controllers, but it is not required to do so.
Remote Cluster: a Kubernetes cluster that Crossplane has access to and may schedule workloads to. A remote cluster may have been created from the Crossplane control cluster using a provider's managed Kubernetes service, or it may be an existing cluster whose connection information was imported into the control cluster.
Provisioning a GKE Cluster and Scheduling a Workload to it
By this point, you are familiar with both dynamic and static provisioning. In
this example, we will dynamically provision a GKECluster, but will focus on
what happens after it is ready and bound.
Create a file named gke-cluster-class.yaml with the following content:
apiVersion: compute.gcp.crossplane.io/v1alpha3
kind: GKEClusterClass
metadata:
name: gkecluster-standard
labels:
guide: quickstart
specTemplate:
writeConnectionSecretsToNamespace: crossplane-system
machineType: n1-standard-1
numNodes: 1
zone: us-central1-b
providerRef:
name: gcp-provider
reclaimPolicy: Delete
Create the GKEClusterClass resource in your cluster:
kubectl apply -f gke-cluster-class.yaml
Now create a file named k8s-cluster.yaml with the following content:
apiVersion: compute.crossplane.io/v1alpha1
kind: KubernetesCluster
metadata:
name: k8scluster
namespace: cp-quickstart
labels:
cluster: hello-world
spec:
classSelector:
matchLabels:
example: "true"
writeConnectionSecretToRef:
name: k8scluster
Then create the KubernetesCluster claim in your cluster:
kubectl apply -f k8s-cluster.yaml
As before, a GKECluster managed resource should be created and its connection
information will be propagated to the cp-quickstart namespace when it is ready
and bound:
kubectl get -f k8scluster.yaml
NAME STATUS CLASS-KIND CLASS-NAME RESOURCE-KIND RESOURCE-NAME AGE
k8scluster GKEClusterClass gkecluster-standard GKECluster cp-quickstart-k8scluster-88426 36s
As you may have guessed, the connection information for a KubernetesCluster
claim contains kubeconfig information. Once the KubernetesCluster claim is
bound, you can view the contents of the Secret in the cp-quickstart
namespace:
kubectl -n cp-quickstart describe secret k8scluster
The KubernetesCluster claim is also unique from other claim types in that when
it becomes bound, Crossplane automatically creates a KubernetesTarget that
references the connection secret in the same namespace. You can see the
KubernetesTarget that Crossplane created for this KubernetesCluster claim:
kubectl -n cp-quickstart get kubernetestargets
Note: a
KubernetesTargetthat is automatically created by Crossplane for a boundKubernetesClusterclaim will have the same labels as theKubernetesClusterclaim.
To schedule workloads to remote clusters, Crossplane requires those resource to
be wrapped in a KubernetesApplication.
Kubernetes Application: a custom resource that bundles other resources that are intended to be run on a remote Kubernetes cluster. Creating a
KubernetesApplicationwill cause Crossplane to find a suitableKubernetesTargetand attempt to create the bundled resources on the referencedKubernetesClusterusing its connectionSecret.
We can start by bundling a simple hello world app with a Namespace,
Deployment, and Service for scheduling to our GKE cluster.
Create a file named helloworld.yaml with the following content:
apiVersion: workload.crossplane.io/v1alpha1
kind: KubernetesApplication
metadata:
name: helloworld
namespace: cp-quickstart
labels:
app: helloworld
spec:
resourceSelector:
matchLabels:
app: helloworld
targetSelector:
matchLabels:
cluster: hello-world
resourceTemplates:
- metadata:
name: helloworld-namespace
labels:
app: helloworld
spec:
template:
apiVersion: v1
kind: Namespace
metadata:
name: helloworld
labels:
app: helloworld
- metadata:
name: helloworld-deployment
labels:
app: helloworld
spec:
template:
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld-deployment
namespace: helloworld
spec:
selector:
matchLabels:
app: helloworld
replicas: 1
template:
metadata:
labels:
app: helloworld
spec:
containers:
- name: hello-world
image: gcr.io/google-samples/node-hello:1.0
ports:
- containerPort: 8080
protocol: TCP
- metadata:
name: helloworld-service
labels:
app: helloworld
spec:
template:
kind: Service
metadata:
name: helloworld-service
namespace: helloworld
spec:
selector:
app: helloworld
ports:
- port: 80
targetPort: 8080
type: LoadBalancer
Create the KubernetesApplication:
kubectl apply -f helloworld.yaml
Crossplane will immediately attempt to find a compatible KubernetesTarget with
matching labels to the stanza we included on our KubernetesApplication:
targetSelector:
matchLabels:
cluster: hello-world
Because we only have one KubernetesTarget with these labels in the
cp-quickstart namespace, the KubernetesApplication will be scheduled to the
GKE cluster we created earlier. You can view the progress of creating the
resources on the remote cluster by looking at the KubernetesApplication and
the resulting KubernetesApplicationResources:
kubectl -n cp-quickstart get kubernetesapplications
NAME CLUSTER STATUS DESIRED SUBMITTED
helloworld 92184b85-4db3-48d2-99a2-36b3cf81226e Scheduled 3
kubectl -n cp-quickstart get kubernetesapplicationresources
NAME TEMPLATE-KIND TEMPLATE-NAME CLUSTER STATUS
helloworld-deployment Deployment helloworld-deployment c1c435a3-8673-46d5-95bb-55cc5040a6fd Submitted
helloworld-namespace Namespace helloworld c1c435a3-8673-46d5-95bb-55cc5040a6fd Submitted
helloworld-service Service helloworld-service c1c435a3-8673-46d5-95bb-55cc5040a6fd Submitted
Note: each in-line template in a
KubernetesApplicationresults in the creation of a correspondingKubernetesApplicationResource. Crossplane keeps the resources on the remote cluster in sync with theirKubernetesApplicationResource, and keeps each respectiveKubernetesApplicationResourcein sync with its template on theKubernetesApplication.
When all three resources have been provisioned, the KubernetesApplication will
show a 3 in the SUBMITTED column. If you inspect the
KubernetesApplication, you should see the IP address of the LoadBalancer
Service in the remote cluster. If you navigate your browser to that address,
you should be greeted by the hello world application.
kubectl -n cp-quickstart describe kubernetesapplicationresource helloworld-service
Name: helloworld-service
Namespace: cp-quickstart
Labels: app=helloworld
Annotations: <none>
API Version: workload.crossplane.io/v1alpha1
Kind: KubernetesApplicationResource
Metadata:
Creation Timestamp: 2020-03-23T22:29:16Z
Finalizers:
finalizer.kubernetesapplicationresource.workload.crossplane.io
Generation: 2
Owner References:
API Version: workload.crossplane.io/v1alpha1
Block Owner Deletion: true
Controller: true
Kind: KubernetesApplication
Name: helloworld
UID: 1f1808ad-2b82-47df-8e8f-40255511c20a
Resource Version: 31969
Self Link: /apis/workload.crossplane.io/v1alpha1/namespaces/cp-quickstart/kubernetesapplicationresources/helloworld-service
UID: 508c07d5-e3c8-4df2-9927-c320448db437
Spec:
Target Ref:
Name: c1c435a3-8673-46d5-95bb-55cc5040a6fd
Template:
Kind: Service
Metadata:
Name: helloworld-service
Namespace: helloworld
Spec:
Ports:
Port: 80
Target Port: 8080
Selector:
App: helloworld
Type: LoadBalancer
Status:
Conditioned Status:
Conditions:
Last Transition Time: 2020-03-23T22:29:54Z
Reason: Successfully reconciled resource
Status: True
Type: Synced
Remote:
Load Balancer:
Ingress:
Ip: 34.67.121.186 # the application is running at this IP address
State: Submitted
Events: <none>
Note: Creating a cluster and scheduling resources to it is a nice workflow, but it is likely that you may want to schedule resources to an existing cluster or one that is not a managed service that Crossplane supports. This is made possible by storing the base64 encoded
kubeconfigdata in aSecret, then manually creating aKubernetesTargetto point at it. This is an advanced workflow, and additional information can be found in the guide on manually adding clusters.
Clean Up
If you would like to clean up the resources created in this section, run the following commands:
kubectl delete -f helloworld.yaml
kubectl delete -f k8s-cluster.yaml