docs snapshot for crossplane version `master`

This commit is contained in:
Crossplane 2019-09-17 20:41:31 +00:00
parent 3b1cd60a86
commit 321d52bd01
3 changed files with 789 additions and 0 deletions

View File

@ -62,6 +62,9 @@ gcloud --project $EXAMPLE_PROJECT_ID services enable sqladmin.googleapis.com
# enable Redis API
gcloud --project $EXAMPLE_PROJECT_ID services enable redis.googleapis.com
# enable Compute API
gcloud --project $EXAMPLE_PROJECT_ID services enable compute.googleapis.com
# enable Additional APIs needed for the example or project
# See `gcloud services list` for a complete list
@ -79,6 +82,7 @@ gcloud projects add-iam-policy-binding $EXAMPLE_PROJECT_ID --member "serviceAcco
gcloud projects add-iam-policy-binding $EXAMPLE_PROJECT_ID --member "serviceAccount:$EXAMPLE_SA" --role="roles/cloudsql.admin"
gcloud projects add-iam-policy-binding $EXAMPLE_PROJECT_ID --member "serviceAccount:$EXAMPLE_SA" --role="roles/container.admin"
gcloud projects add-iam-policy-binding $EXAMPLE_PROJECT_ID --member "serviceAccount:$EXAMPLE_SA" --role="roles/redis.admin"
gcloud projects add-iam-policy-binding $EXAMPLE_PROJECT_ID --member "serviceAccount:$EXAMPLE_SA" --role="roles/compute.networkAdmin"
```
## Option 2: GCP Console in a Web Browser
@ -100,6 +104,7 @@ Create a GCP example project which we will use to host our example GKE cluster,
- `Service Account User`
- `Cloud SQL Admin`
- `Kubernetes Engine Admin`
- `Compute Network Admin`
- Click `Create` button
- This should advance to the next section `3 Grant users access to this service account (optional)`
- We don't need to assign any user or admin roles to this account for the example purposes, so you can leave following two fields blank:
@ -122,6 +127,9 @@ Create a GCP example project which we will use to host our example GKE cluster,
- Enable `Cloud Memorystore for Redis`
- Navigate to [Cloud Memorystore for Redis](https://console.developers.google.com/apis/api/redis.googleapis.com/overview)
- Click `Enable`
- Enable `Compute Engine API`
- Navigate to [Compute Engine API](https://console.developers.google.com/apis/api/compute.googleapis.com/overview)
- Click `Enable`
### Enable Billing

View File

@ -0,0 +1,450 @@
---
title: "Crossplane Stacks Guide: GCP Setup"
toc: false
weight: 421
indent: true
---
# Crossplane Stacks Guide: GCP Setup
## Table of Contents
1. [Introduction](#introduction)
2. [Install the GCP Stack](#install-the-gcp-stack)
3. [Configure GCP Account](#configure-gcp-account)
4. [Configure Crossplane GCP Provider](#configure-crossplane-gcp-provider)
5. [Set Up Network Resources](#set-up-network-resources)
6. [Configure Provider Resources](#configure-provider-resources)
7. [Recap](#recap)
8. [Next Steps](#next-steps)
## Introduction
In this guide, we will set up a GCP provider in Crossplane so that we
can install and use the [WordPress sample
stack][sample-wordpress-stack], which depends on MySQL and Kubernetes!
Before we begin, you will need:
* Everything from the [Crossplane Stacks Guide][stacks-guide] before the
cloud provider setup
- A `kubectl` pointing to a Crossplane control cluster
- The [Crossplane CLI][crossplane-cli] installed
* An account on [Google Cloud Platform][gcp]
At the end, we will have:
* A Crossplane control cluster configured to use GCP
* The boilerplate of a GCP-based project spun up
* Support in the control cluster for managing MySQL and Kubernetes
cluster dependencies
* A slightly better understanding of:
- The way cloud providers are configured in Crossplane
- The way dependencies for cloud-portable workloads are configured in
Crossplane
We will **not** be teaching first principles in depth. Check out the
[Crossplane concepts document][crossplane-concepts] for that.
## Install the GCP Stack
After Crossplane has been installed, it can be extended with more
functionality by installing a [Crossplane Stack][stack-docs]! Let's
install the [stack for Google Cloud Platform][stack-gcp] (GCP) to add
support for that cloud provider. We can use the [Crossplane
CLI][crossplane-cli] for this operation. Since this is an infrastructure
stack, we need to specify that it's cluster-scoped by passing the
`--cluster` flag.
To install to a specific namespace, we can use the `generate-install`
command and pipe it to `kubectl apply` instead, which gives us more
control over how the stack's installation is handled. Everything is
a Kubernetes object!
```
kubectl create namespace gcp
kubectl crossplane stack generate-install --cluster 'crossplane/stack-gcp:master' stack-gcp | kubectl apply --namespace gcp -f -
```
If we wanted to use whatever the current namespace is, we could have
used `kubectl crossplane stack install` instead of using
`generate-install`.
The namespace that we install the stack to is where the stack will
create the resources it manages. When a developer requests a resource by
creating a [resource claim][resource-claims-docs] in a namespace `mynamespace`, the
managed cloud provider resource and any secrets will be created in the
stack's namespace. Secrets will be copied over to `mynamespace`, and the
claim will be bound to the original resource claim. For more details
about resource claims and how they work, see the [documentation on
resource claims][resource-claims-docs].
For convenience, the next steps assume that you installed GCP stack into
the `gcp` namespace.
## Configure GCP Account
We will make use of the following services on GCP:
* GKE
* CloudSQL Instance
* Network
* Subnetwork
* GlobalAddress
* Private Service Connection
For all these to work, you need to [enable the following
APIs][gcp-enable-apis] in your GCP project:
* Compute Engine API
* Service Networking API
* Kubernetes Engine API
We will also need to tell Crossplane how to use the credentials for the
GCP account. For this exercise, the GCP account that we will tell
Crossplane about should have the following [roles
assigned][gcp-assign-roles]:
* Cloud SQL Admin
* Compute Network Admin
* Kubernetes Engine Admin
* Service Account User
You need to get JSON file of the service account youll use. In the next
sections, this file will be referred to as
`crossplane-gcp-provider-key.json`
You can create the JSON file by using the [gcloud
command][gcp-create-keys] and specifying a file name of
`crossplane-gcp-provider-key.json`. If you use Crossplane's [GCP
credentials script][gcp-credentials], this is taken care of for you.
## Configure Crossplane GCP Provider
Before creating any resources, we need to create and configure a cloud
provider in Crossplane. This helps Crossplane know how to connect to the cloud
provider. All the requests from Crossplane to GCP will use the
credentials attached to the provider object. The following command
assumes that you have a `crossplane-gcp-provider-key.json` file that
belongs to the account youd like Crossplane to use. Run the command
after changing `[your-demo-project-id]` to your actual GCP project id.
You should be able to get the project id from the JSON credentials file
or from the GCP Console.
```
export PROJECT_ID=[your-demo-project-id]
export BASE64ENCODED_GCP_PROVIDER_CREDS=$(base64 crossplane-gcp-provider-key.json | tr -d "\n")
```
The environment variable `PROJECT_ID` is going to be used in multiple
YAML files in the next steps, while `BASE64ENCODED_GCP_PROVIDER_CREDS`
is only needed for this step.
Now well create our `Secret` that contains the credential and
`Provider` resource that refers to that secret:
```
cat > provider.yaml <<EOF
---
apiVersion: v1
data:
credentials.json: $BASE64ENCODED_GCP_PROVIDER_CREDS
kind: Secret
metadata:
namespace: gcp
name: gcp-provider-creds
type: Opaque
---
apiVersion: gcp.crossplane.io/v1alpha2
kind: Provider
metadata:
namespace: gcp
name: gcp-provider
spec:
credentialsSecretRef:
name: gcp-provider-creds
key: credentials.json
projectID: $PROJECT_ID
EOF
kubectl apply -f provider.yaml
```
The example YAML also exists in [the Crossplane repository][crossplane-sample-gcp-provider].
The name of the `Provider` resource in the file above is `gcp-provider`;
we'll use the name `gcp-provider` to refer to this provider when we
configure and set up other Crossplane resources.
## Set Up Network Resources
Wordpress needs a SQL database and a Kubernetes cluster. But **those**
two resources need a private network to communicate securely. So, we
need to set up the network before we set up the database and the
Kubernetes cluster. Here's an example of how to set up a network:
```
cat > network.yaml <<EOF
---
# example-network will be the VPC that all cloud instances we'll create will use.
apiVersion: compute.gcp.crossplane.io/v1alpha2
kind: Network
metadata:
name: example-network
namespace: gcp
spec:
name: example-network
autoCreateSubnetworks: false
providerRef:
name: gcp-provider
namespace: gcp
reclaimPolicy: Delete
routingConfig:
routingMode: REGIONAL
---
# example-subnetwork defines IP ranges to be used by GKE cluster.
apiVersion: compute.gcp.crossplane.io/v1alpha2
kind: Subnetwork
metadata:
name: example-subnetwork
namespace: gcp
spec:
providerRef:
name: gcp-provider
namespace: gcp
reclaimPolicy: Delete
name: example-subnetwork
region: us-central1
ipCidrRange: "192.168.0.0/24"
privateIpGoogleAccess: true
secondaryIpRanges:
- rangeName: pods
ipCidrRange: 10.0.0.0/8
- rangeName: services
ipCidrRange: 172.16.0.0/16
network: projects/$PROJECT_ID/global/networks/example-network
---
# example-globaladdress defines the IP range that will be allocated for cloud services connecting
# to the instances in the given Network.
apiVersion: compute.gcp.crossplane.io/v1alpha2
kind: GlobalAddress
metadata:
name: example-globaladdress
namespace: gcp
spec:
providerRef:
name: gcp-provider
namespace: gcp
reclaimPolicy: Delete
name: example-globaladdress
purpose: VPC_PEERING
addressType: INTERNAL
prefixLength: 16
network: projects/$PROJECT_ID/global/networks/example-network
---
# example-connection is what allows cloud services to use the allocated GlobalAddress for communication. Behind
# the scenes, it creates a VPC peering to the network that those service instances actually live.
apiVersion: servicenetworking.gcp.crossplane.io/v1alpha2
kind: Connection
metadata:
name: example-connection
namespace: gcp
spec:
providerRef:
name: gcp-provider
namespace: gcp
reclaimPolicy: Delete
parent: services/servicenetworking.googleapis.com
network: projects/$PROJECT_ID/global/networks/example-network
reservedPeeringRanges:
- example-globaladdress
EOF
kubectl apply -f network.yaml
```
The example YAML also exists in [the Crossplane repository][crossplane-sample-gcp-network].
For more details about networking and what happens when you run this
command, see [this document with more details][crossplane-gcp-networking-docs].
It takes a while to create these resources in GCP. The top-level object
is the `Connection` object; when the `Connection` is ready, everything
else is too. We can watch it by running the following command, which
assumes the GCP stack is installed in `gcp` namespace:
```
kubectl -n gcp get connection.servicenetworking.gcp.crossplane.io/example-connection -o custom-columns='NAME:.metadata.name,FIRST_CONDITION:.status.conditions[0].status,SECOND_CONDITION:.status.conditions[1].status'
```
## Configure Provider Resources
Once we have the network set up, we also need to tell Crossplane how to
satisfy WordPress's claims for a database and a Kubernetes cluster.
[Resource classes][resource-classes-docs] serve as templates for the new
claims we make. The following resource classes allow the claims for the
database and Kubernetes cluster to be satisfied with the network
configuration we just set up:
```
cat > environment.yaml <<EOF
---
apiVersion: database.gcp.crossplane.io/v1alpha2
kind: CloudsqlInstanceClass
metadata:
name: standard-cloudsql
namespace: gcp
specTemplate:
databaseVersion: MYSQL_5_7
tier: db-n1-standard-1
region: us-central1
storageType: PD_SSD
storageGB: 10
# Note from GCP Docs: Your Cloud SQL instances are not created in your VPC network.
# They are created in the service producer network (a VPC network internal to Google) that is then connected (peered) to your VPC network.
privateNetwork: projects/$PROJECT_ID/global/networks/example-network
providerRef:
name: gcp-provider
namespace: gcp
reclaimPolicy: Delete
---
apiVersion: compute.gcp.crossplane.io/v1alpha2
kind: GKEClusterClass
metadata:
name: standard-gke
namespace: gcp
specTemplate:
machineType: n1-standard-1
numNodes: 1
zone: us-central1-b
network: projects/$PROJECT_ID/global/networks/example-network
subnetwork: projects/$PROJECT_ID/regions/us-central1/subnetworks/example-subnetwork
enableIPAlias: true
clusterSecondaryRangeName: pods
servicesSecondaryRangeName: services
providerRef:
name: gcp-provider
namespace: gcp
reclaimPolicy: Delete
EOF
kubectl apply -f environment.yaml
```
The example YAML also exists in [the Crossplane
repository][crossplane-sample-gcp-environment].
The steps that we have taken so far have been related to things that can
be shared by all resources in all namespaces of the Crossplane control
cluster. Now, we will use a namespace specific to our application, and
we'll populate it with resources that will help Crossplane know what
configuration to use to satisfy our application's resource claims.
If you have been following along with the rest of the stacks guide, the
namespace should already be created. But in case it isn't, this is what
you would run to create it:
```
kubectl create namespace app-project1-dev
```
Now that we have a namespace, we need to tell Crossplane which resource
classes should be used to satisfy our claims in that namespace. We will
create [portable classes][portable-classes-docs] that have have
references to the cloud-specific classes that we created earlier.
For example, `MySQLInstanceClass` is a portable class. It may refer to
GCP's `CloudSQLInstanceClass`, which is a non-portable class.
To read more about portable classes, how they work, and how to use them
in different ways, including by specifying default classes when no
reference is provided, see the [portable classes and claims
documentation][portable-classes-docs].
```
cat > namespace.yaml <<EOF
---
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstanceClass
metadata:
name: standard-mysql
namespace: app-project1-dev
labels:
default: "true"
classRef:
kind: CloudsqlInstanceClass
apiVersion: database.gcp.crossplane.io/v1alpha2
name: standard-cloudsql
namespace: gcp
---
apiVersion: compute.crossplane.io/v1alpha1
kind: KubernetesClusterClass
metadata:
name: standard-cluster
namespace: app-project1-dev
labels:
default: "true"
classRef:
kind: GKEClusterClass
apiVersion: compute.gcp.crossplane.io/v1alpha2
name: standard-gke
namespace: gcp
---
EOF
kubectl apply -f namespace.yaml
```
The example YAML also exists in [the Crossplane
repository][crossplane-sample-gcp-namespace].
## Recap
To recap what we've set up now in our environment:
* Our provider account, both on the provider side and on the Crossplane
side.
* A Network for all instances to share.
* A Subnetwork for the GKE cluster to use in the network.
* A GlobalAddress resource for Googles service connection.
* A Connection resource that connects Googles service network to ours
in order to connect CloudSQL instance in Googles network with GKE
cluster in our network.
* A GKEClusterClass and a CloudSQLInstanceClass with the right
configuration to use the mentioned networking setup.
* A namespace for our app resources to reside with default MySQLInstanceClass
and KubernetesClusterClass that refer to our GKEClusterClass and CloudSQLInstanceClass.
## Next Steps
Next we'll set up a Crossplane App Stack and use it! Head [back over to
the Stacks Guide document][stacks-guide-continue] so we can pick up
where we left off.
<!-- Links -->
[crossplane-cli]: https://github.com/crossplaneio/crossplane-cli
[crossplane-gcp-networking-docs]: https://github.com/crossplaneio/crossplane/blob/master/design/one-pager-resource-connectivity-mvp.md#google-cloud-platform
[stacks-guide]: stacks-guide.md
[crossplane-concepts]: concepts.md
[gcp-credentials]: https://github.com/crossplaneio/crossplane/blob/master/cluster/examples/gcp-credentials.sh
[gcp-enable-apis]: https://cloud.google.com/endpoints/docs/openapi/enable-api
[gcp-assign-roles]: https://cloud.google.com/iam/docs/granting-roles-to-service-accounts
[gcp-create-keys]: https://cloud.google.com/sdk/gcloud/reference/iam/service-accounts/keys/create
[gcp]: https://cloud.google.com/
[stacks-guide-continue]: stacks-guide.html#install-support-for-our-application-into-crossplane
[sample-wordpress-stack]: https://github.com/crossplaneio/sample-stack-wordpress
[stack-docs]: https://github.com/crossplaneio/crossplane/blob/master/design/design-doc-stacks.md#crossplane-stacks
[stack-gcp]: https://github.com/crossplaneio/stack-gcp
[resource-claims-docs]: concepts.md#resource-claims-and-resource-classes
[resource-classes-docs]: concepts.md#resource-claims-and-resource-classes
[portable-classes-docs]: https://github.com/crossplaneio/crossplane/blob/master/design/one-pager-default-resource-class.md
[crossplane-sample-gcp-provider]: https://github.com/crossplaneio/crossplane/blob/master/cluster/examples/workloads/kubernetes/wordpress/gcp/provider.yaml
[crossplane-sample-gcp-network]: https://github.com/crossplaneio/crossplane/blob/master/cluster/examples/workloads/kubernetes/wordpress/gcp/network.yaml
[crossplane-sample-gcp-environment]: https://github.com/crossplaneio/crossplane/blob/master/cluster/examples/workloads/kubernetes/wordpress/gcp/environment.yaml
[crossplane-sample-gcp-namespace]: https://github.com/crossplaneio/crossplane/blob/master/cluster/examples/workloads/kubernetes/wordpress/gcp/namespace.yaml

331
docs/master/stacks-guide.md Normal file
View File

@ -0,0 +1,331 @@
---
title: "Crossplane Stacks Guide"
toc: true
weight: 401
indent: false
---
# Crossplane Stacks Guide
## Table of Contents
1. [Introduction](#introduction)
2. [Concepts](#concepts)
3. [Before you get started](#before-you-get-started)
4. [Install the Crossplane CLI](#install-the-crossplane-cli)
5. [Install and configure Crossplane](#install-and-configure-crossplane)
6. [Install support for our application into
Crossplane](#install-support-for-our-application-into-crossplane)
7. [Create a Wordpress](#create-a-wordpress)
8. [Clean up](#clean-up)
9. [Conclusion](#conclusion)
10. [Next steps](#next-steps)
11. [References](#references)
## Introduction
Welcome to the Crossplane Stack guide! In this document, we will:
* Learn how to install an existing stack
* Interact with a stack to see how to use it
* Glimpse what is possible with a stack
* Touch a little bit on how stacks work
We will **not**:
* Learn first principles (see the [concepts
document][crossplane-concepts] for that level of detail)
* Develop our own stack from scratch (go to [this development
guide][stack-developer-guide] to learn how to do that)
Let's go!
## Concepts
There are a bunch of things you might want to know to fully understand what's happening in this document. This guide won't cover them, but there are other ones that do. Here are some links!
* [Crossplane concepts][crossplane-concepts]
* [Kubernetes concepts][kubernetes-concepts]
## Before you get started
This guide assumes you are using a *nix-like environment. It also assumes you have a basic working familiarity with the following:
* The terminal environment
* Setting up cloud provider accounts for the cloud provider you want to
use
* [Kubernetes][kubernetes-docs] and [kubectl][kubectl-docs]
You will need:
* A *nix-like environment
* A cloud provider account, for the cloud provider of your choice (out
of the supported providers)
* A locally-configured kubectl which points to a configured Kubernetes
cluster. We will put Crossplane in this cluster, and we'll refer to it
as the control cluster.
## Install the Crossplane CLI
To interact with stacks, we're going to use the [Crossplane
CLI][crossplane-cli], because it's more convenient. To install it, we
can use the one-line curl bash:
```
RELEASE=0.0.1
curl -sL https://raw.githubusercontent.com/crossplaneio/crossplane-cli/"${RELEASE}"/bootstrap.sh | bash
```
To use the latest release, you can use `master` as the `RELEASE` instead
of using a specific version.
## Install and configure Crossplane
To use Crossplane, we'll need to install and configure it. In this case,
we want to use Crossplane with a cloud provider, so we'll need to
configure the provider.
### Install Crossplane
The recommended way of installing Crossplane is by using
[helm][helm-install]. We can grab the most stable version currently
available by using:
```
helm repo add crossplane-alpha https://charts.crossplane.io/alpha
helm install --name crossplane --namespace crossplane-system crossplane-alpha/crossplane
```
For more options for installing, including how to install a more
bleeding-edge version, or how to uninstall, see the [full install
documentation][crossplane-install-docs].
### Create the application namespace
[Kubernetes namespaces][kubernetes-namespace-docs] are used to isolate
resources in the same cluster, and we'll use them in our Crossplane
control cluster too. Let's create a namespace for our application's
resources. We'll call it `app-project1-dev` for the purposes of this
guide, but any name can be used.
```
kubectl create namespace app-project1-dev
```
The reason we need to create the namespace before we configure the cloud
provider is because we will be setting up some cloud provider
configuration in that namespace. The configuration will help our
application not care about which specific provider it uses. For more
details on how this works, see the Crossplane documentation on [portable
classes][portable-classes-docs].
### Configure support for your cloud provider
Next we'll set up support for our cloud provider of choice! See the
provider-specific guides:
* [AWS][aws-setup]
* [GCP][gcp-setup]
* [Azure][azure-setup]
Then come back here! Don't worry; we'll still be here when you're ready.
Don't see your favorite cloud provider? [Help us add
support][provider-stack-developer-guide] for it!
## Install support for our application into Crossplane
Now that we've got Crossplane set up and configured to use a cloud
provider, we're ready to add support for creating WordPresses! We'll do
this using a Crossplane Stack. For more information about stacks, see
the [full Stack documentation][stack-docs].
We can use the [Crossplane CLI][crossplane-cli] to install our stack which adds support for
Wordpress. Let's install it into a namespace for our project, which
we'll call `app-project1-dev` for the purposes of this guide. To install
to the current namespace, `install` can be used, but since we want to
install to a specific namespace, we will use `generate-install`:
```
kubectl crossplane stack generate-install 'crossplane/sample-stack-wordpress:latest' 'sample-stack-wordpress' | kubectl apply --namespace app-project1-dev -f -
```
Using the `generate-install` command and piping the output to `kubectl
apply` instead of using the `install` command gives us more control over
how the stack's installation is handled. Everything is a Kubernetes
object!
This pulls the stack package from a registry to install it into
Crossplane. For more details about how to use the CLI, see the
[documentation for the CLI][crossplane-cli-docs]. For more details about how stacks work behind
the scenes, see the documentation about the [stack
manager][stack-manager-docs] and the [stack
format][stack-format-docs].
## Create a Wordpress
Now that Crossplane supports Wordpress creation, we can ask Crossplane
to spin up a Wordpress for us. We can do this by creating a Kubernetes
resource that our Wordpress stack will recognize:
```
cat > my-wordpress.yaml <<EOF
apiVersion: wordpress.samples.stacks.crossplane.io/v1alpha1
kind: WordpressInstance
metadata:
name: my-wordpressinstance
EOF
kubectl apply --namespace app-project1-dev -f my-wordpress.yaml
```
If the control cluster doesn't recognize the Wordpress instance type, it
could be because the stack is still being installed. Wait a few seconds,
and try creating the Wordpress instance again.
### Wait
The Wordpress can take a while to spin up, because behind the scenes
Crossplane is creating all of its dependendencies, which is a database
and Kubernetes cluster. To check the status, we can look at the
resources that Crossplane is creating for us:
```
# The claim for the database
kubectl get -n app-project1-dev mysqlinstance
# The claim for the Kubernetes cluster
kubectl get -n app-project1-dev kubernetescluster
# The workload definition
kubectl get -n app-project1-dev kubernetesapplication
# The things created on the Kubernetes cluster as part of the workload
kubectl get -n app-project1-dev kubernetesapplicationresource
```
For more information about how Crossplane manages databases and
Kubernetes clusters for us, see the more complete documentation about
[claims][claims-docs], [resource classes][resource-classes-docs], and
[workloads][workloads-docs].
### Use
Once everything has been created, the ip address for the Wordpress
instance will show up in the [Crossplane
KubernetesApplicationResource][kubernetesapplicationresource-docs]
which represents the workload's service. Here's a way to watch for the
ip:
```
kubectl get kubernetesapplicationresource -n app-project1-dev -o custom-columns='NAME:.metadata.name,NAMESPACE:.spec.template.metadata.namespace,KIND:.spec.template.kind,SERVICE-EXTERNAL-IP:.status.remote.loadBalancer.ingress[0].ip' --watch
```
The ip will show up on the one which has a `Service` kind.
If you navigate to the ip, you should see the Wordpress first-time
start-up screen in your browser.
If you see it, things are working!
## Clean up
When we want to get rid of everything, we can delete the Wordpress
instance and let Crossplane and Kubernetes clean up the rest. To read
more about how cleanup works, see the documentation on reclaim policies
in Crossplane and garbage collection in Kubernetes.
To delete the Wordpress instance:
```
kubectl delete -n app-project1-dev wordpressinstance my-wordpressinstance
```
We can also remove the stack, using the Crossplane CLI:
```
kubectl crossplane stack uninstall sample-stack-wordpress -n
app-project1-dev
```
Removing the stack removes any Wordpress instances that were created.
The cloud provider stack can also be removed using the `kubectl
crossplane stack uninstall` command. Use `kubectl crossplane stack list`
to see what's installed.
## Conclusion
We're done!
In this guide, we:
* Set up Crossplane on a control cluster
* Installed functionality for a cloud provider
* Extended Crossplane to manage Wordpress workloads for us
* Created a Wordpress workload
* Got some initial exposure to some of the tools and concepts of
Crossplane, Crossplane Stacks, and the Crossplane CLI
## Next steps
Crossplane can do a lot.
Now that we've gone through how to use a Crossplane Stack, you may want
to learn more about which stacks are available, or about how to write
your own stack.
To learn more about which stacks are available, check out the [stack registry][stack-registry].
To learn more about how to write your own stack, see the [stack developer
guide][stack-developer-guide].
## References
* [The Crossplane Concepts guide][crossplane-concepts]
* [The Stacks Concepts guide][stack-concepts]
* [Crossplane Install Guide][crossplane-install-docs]
* [The Crossplane CLI][crossplane-cli]
* [Stacks Quick Start][stack-quick-start]
* [Stack Registry][stack-registry]
* [Stacks Developer Guide][stack-developer-guide]
* [Provider Stack Developer Guide][provider-stack-developer-guide]
* [AWS documentation][aws-docs]
* [GCP documentation][gcp-docs]
* [Azure documentation][azure-docs]
* [Kubernetes documentation][kubernetes-docs]
<!-- Named links -->
[crossplane-cli]: https://github.com/crossplaneio/crossplane-cli
[crossplane-cli-docs]: https://github.com/crossplaneio/crossplane-cli/blob/master/README.md
[crossplane-concepts]: concepts.md
[crossplane-install-docs]: install-crossplane.md
[kubernetesapplicationresource-docs]: TODO
[claims-docs]: concepts.md#resource-claims-and-resource-classes
[resource-classes-docs]: concepts.md#resource-claims-and-resource-classes
[portable-classes-docs]: https://github.com/crossplaneio/crossplane/blob/master/design/one-pager-default-resource-class.md
[workloads-docs]: concepts.md#resources-and-workloads
[kubernetes-concepts]: https://kubernetes.io/docs/concepts/
[kubernetes-docs]: https://kubernetes.io/docs/home/
[kubernetes-namespaces-docs]: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/
[kubectl-docs]: https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands
[helm-install]: https://github.com/helm/helm#install
[aws-docs]: https://docs.aws.amazon.com/
[gcp-docs]: https://cloud.google.com/docs/
[azure-docs]: https://docs.microsoft.com/azure/
[aws-setup]: TODO
[gcp-setup]: stacks-guide-gcp.md
[azure-setup]: TODO
[stack-docs]: https://github.com/crossplaneio/crossplane/blob/master/design/design-doc-stacks.md#crossplane-stacks
[stack-quick-start]: https://github.com/crossplaneio/crossplane-cli#quick-start-stacks
[stack-concepts]: https://github.com/crossplaneio/crossplane/blob/master/design/design-doc-stacks.md#crossplane-stacks
[stack-manager-docs]: https://github.com/crossplaneio/crossplane/blob/master/design/design-doc-stacks.md#installation-flow
[stack-format-docs]: https://github.com/crossplaneio/crossplane/blob/master/design/design-doc-stacks.md#stack-package-format
[stack-registry]: TODO
[stack-developer-guide]: TODO
[provider-stack-developer-guide]: TODO