docs snapshot for crossplane version `master`

This commit is contained in:
Crossplane 2019-10-28 17:11:34 +00:00
parent 399a9314e4
commit 34f3be89db
3 changed files with 571 additions and 1359 deletions

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,8 @@ toc: true
weight: 440
indent: true
---
# Deploying Wordpress in Azure
# Azure Services Guide
This user guide will walk you through Wordpress application deployment using
Crossplane managed resources and the official Wordpress Docker image.
@ -12,12 +13,18 @@ Crossplane managed resources and the official Wordpress Docker image.
## Table of Contents
1. [Pre-requisites](#pre-requisites)
2. [Preparation](#preparation)
3. [Set Up AKS Cluster](#set-up-aks-cluster)
4. [Set Up Crossplane](#set-up-crossplane)
5. [Install Wordpress](#install-wordpress)
6. [Uninstall](#uninstall)
7. [Conclusion and Next Steps](#conclusion-and-next-steps)
1. [Preparation](#preparation)
1. [Set Up Crossplane](#set-up-crossplane)
1. [Install in Target Cluster](#install-in-target-cluster)
1. [Cloud Provider](#cloud-provider)
1. [Resource Classes](#resource-classes)
1. [Configure Managed Service Access](#configure-managed-service-access)
1. [Provision MySQL](#provision-mysql)
1. [Resource Claim](#resource-claim)
1. [Virtual Network Rule](#virtual-network-rule)
1. [Install Wordpress](#install-wordpress)
1. [Clean Up](#clean-up)
1. [Conclusion and Next Steps](#conclusion-and-next-steps)
## Pre-requisites
@ -35,65 +42,24 @@ local machine.
## Preparation
This guide assumes that you have setup the Azure CLI and are logged in to your
desired account.
desired account. It also assumes that you have an existing AKS cluster in a
Virtual Network. Make sure to populate the environment variables below with the
relevant values for your AKS cluster.
*Note: environment variables are used throughout this guide. You may use the
values below or create your own.*
*Note: environment variables are used throughout this guide.*
```bash
export RESOURCE_GROUP_NAME=myResourceGroup
export RESOURCE_GROUP_LOCATION=eastus
export AKS_NAME=myAKSCluster
export AKS_NODE_COUNT=1
export AKS_RESOURCE_GROUP=MC_${RESOURCE_GROUP_NAME}_${AKS_NAME}_${RESOURCE_GROUP_LOCATION}
export AKS_RESOURCE_GROUP=myAKSResourceGroup
export AKS_VNET=myAKSVnet
export AKS_NAME=myAKSName
export SUBSCRIPTION_ID=$(az account list | jq -j '.[0].id')
```
### Set Up AKS Cluster
## Set Up Crossplane
Azure maintains a succinct [walkthrough][aks-walkthrough] for setting up an AKS
cluster using the Azure CLI. The basic steps are as follows:
### Install in Target Cluster
1. Create a Resource Group
```bash
az group create --name $RESOURCE_GROUP_NAME --location $RESOURCE_GROUP_LOCATION
```
2. Create AKS Cluster (this may take a few minutes)
```bash
az aks create \
--resource-group $RESOURCE_GROUP_NAME \
--name $AKS_NAME \
--node-count $AKS_NODE_COUNT \
--enable-addons monitoring \
--generate-ssh-keys
```
3. Enable SQL Service Endpoint
Get name of AKS node Virtual Network:
```bash
export AKS_VNET=$(az network vnet list -g $AKS_RESOURCE_GROUP | jq -j '.[0].name')
```
Add Service Endpoint to AKS subnet:
```bash
az network vnet subnet update -g $AKS_RESOURCE_GROUP --vnet-name $AKS_VNET -n aks-subnet --service-endpoints Microsoft.Sql
```
4. Connect to AKS Cluster
```bash
az aks get-credentials --resource-group $RESOURCE_GROUP_NAME --name $AKS_NAME
```
5. Make sure `kubectl` is able to communicate with AKS Cluster
```bash
kubectl cluster-info
```
### Set Up Crossplane
Using the newly provisioned cluster:
Assuming you are [connected][aks-kubectl] to your AKS cluster via `kubectl`:
1. Install Crossplane from alpha channel. (See the [Crossplane Installation
Guide][crossplane-install] for more information.)
@ -108,13 +74,8 @@ helm install --name crossplane --namespace crossplane-system crossplane-alpha/cr
```yaml
cat > stack-azure.yaml <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: azure
---
apiVersion: stacks.crossplane.io/v1alpha1
kind: StackRequest
kind: ClusterStackInstall
metadata:
name: stack-azure
namespace: crossplane-system
@ -128,88 +89,24 @@ kubectl apply -f stack-azure.yaml
3. Obtain Azure credentials. (See the [Cloud Provider Credentials][cloud-creds]
docs for more information.)
#### Infrastructure Namespaces
### Cloud Provider
Kubernetes namespaces allow for separation of environments within your cluster.
You may choose to use namespaces to group resources by team, application, or any
other logical distinction. For this guide, we will create a namespace called
`app-project1-dev`, which we will use to group our Azure infrastructure
components.
It is essential to make sure that the GCP user credentials are configured in
Crossplane as a provider. Please follow the steps [provider
guide][azure-provider-guide] for more information.
* Define a `Namespace` in `azure-infra-dev-namespace.yaml` and create it:
### Resource Classes
```yaml
cat > azure-infra-dev.yaml <<EOF
---
apiVersion: v1
kind: Namespace
metadata:
name: azure-infra-dev
EOF
kubectl apply -f azure-infra-dev-namespace.yam
```
* You should see the following output:
> namespace/azure-infra-dev.yaml created
#### Azure Provider
It is essential to make sure that the Azure Service Principal is configured with
all permissions outlined in the [provider guide][azure-provider-guide].
Using Azure Service Principal `crossplane-azure-provider-key.json`:
* Generate BASE64ENCODED_AZURE_PROVIDER_CREDS encoded value:
To keep your resource configuration organized, start by creating a new
directory:
```bash
export BASE64ENCODED_AZURE_PROVIDER_CREDS=$(base64 crossplane-azure-provider-key.json | tr -d "\n")
mkdir wordpress && cd $_
```
* Define an Azure `Provider` and `Secret` in `azure-provider.yaml` and create
them:
```yaml
cat > azure-provider.yaml <<EOF
---
# Azure Admin service account secret - used by Azure Provider
apiVersion: v1
kind: Secret
metadata:
name: demo-provider-azure-dev
namespace: azure-infra-dev
type: Opaque
data:
credentials: $BASE64ENCODED_AZURE_PROVIDER_CREDS
---
# Azure Provider with service account secret reference - used to provision resources
apiVersion: azure.crossplane.io/v1alpha2
kind: Provider
metadata:
name: demo-azure
namespace: azure-infra-dev
spec:
credentialsSecretRef:
name: demo-provider-azure-dev
key: credentials
EOF
kubectl apply -f azure-provider.yaml
```
* Verify Azure provider was successfully registered by the crossplane
```bash
kubectl get providers.azure.crossplane.io -n azure-infra-dev
kubectl get secrets -n azure-infra-dev
```
#### Cloud-Specific Resource Classes
Cloud-specific resource classes are used to define a reusable configuration for
a specific managed service. Wordpress requires a MySQL database, which can be
satisfied by an [Azure Database for MySQL][azure-mysql] instance.
Resource classes are used to define a reusable configuration for a specific
managed service. Wordpress requires a MySQL database, which can be satisfied by
an [Azure Database for MySQL][azure-mysql] instance.
* Define an Azure MySQL `SQLServerClass` in `azure-mysql-standard.yaml` and
create it:
@ -217,14 +114,16 @@ satisfied by an [Azure Database for MySQL][azure-mysql] instance.
```yaml
cat > azure-mysql-standard.yaml <<EOF
---
apiVersion: database.azure.crossplane.io/v1alpha2
apiVersion: database.azure.crossplane.io/v1alpha3
kind: SQLServerClass
metadata:
name: azure-mysql-standard
namespace: azure-infra-dev
labels:
size: standard
demo: true
specTemplate:
adminLoginName: myadmin
resourceGroupName: $RESOURCE_GROUP_NAME
resourceGroupName: $AKS_RESOURCE_GROUP
location: EAST US
sslEnforced: false
version: "5.6"
@ -253,7 +152,7 @@ kubectl apply -f azure-mysql-standard.yaml
* You can verify creation with the following command and output:
```bash
$ kubectl get sqlserverclasses -n azure-infra-dev
$ kubectl get sqlserverclasses
NAME PROVIDER-REF RECLAIM-POLICY AGE
azure-mysql-standard demo-azure Delete 11s
```
@ -262,124 +161,48 @@ You are free to create more Azure `SQLServerClass` instances to define more
potential configurations. For instance, you may create `large-azure-mysql` with
field `storageGB: 100`.
#### Application Namespaces
### Configure Managed Service Access
Earlier, we created a namespace to group our Azure infrastructure resources.
Because our application resources may be satisfied by services from any cloud
provider, we want to separate them into their own namespace. For this demo, we
will create a namespace called `app-project1-dev`, which we will use to group
our Wordpress resources.
In order for the AKS cluster to talk to the MySQL Database, you must condigure a
`Microsoft.Sql` service endpoint on the AKS Virtual Network. If you do not
already have this configured, Azure has a [guide][service endpoint] on how to
set it up.
* Define a `Namespace` in `app-project1-dev-namespace.yaml` and create it:
## Provision MySQL
```yaml
cat > app-project1-dev-namespace.yaml <<EOF
---
apiVersion: v1
kind: Namespace
metadata:
name: app-project1-dev
EOF
### Resource Claims
kubectl apply -f app-project1-dev-namespace.yaml
```
Resource claims are used for dynamic provisioning of a managed resource (like a
MySQL instance) by matching the claim to a resource class. This can be done in
several ways: (a) rely on the default class marked
`resourceclass.crossplane.io/is-default-class: "true"`, (b) use a
`claim.spec.classRef` to a specific class, or (c) match on class labels using a
`claim.spec.classSelector`.
* You should see the following output:
*Note: claims may also be used in [static provisioning] with a reference to an
existing managed resource.*
> namespace/app-project1-dev created
#### Portable Resource Classes
Portable resource classes are used to define a class of service in a single
namespace for an abstract service type. We want to define our Azure
`SQLServerClass` as the standard MySQL class of service in the namespace that
our Wordpress resources will live in.
* Define a `MySQLInstanceClass` in `mysql-standard.yaml` for namespace
`app-project1-dev` and create it:
```yaml
cat > mysql-standard.yaml <<EOF
---
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstanceClass
metadata:
name: mysql-standard
namespace: app-project1-dev
classRef:
kind: SQLServerClass
apiVersion: database.azure.crossplane.io/v1alpha2
name: azure-mysql-standard
namespace: azure-infra-dev
EOF
kubectl apply -f mysql-standard.yaml
```
* You should see the following output:
> mysqlinstanceclass.database.crossplane.io/mysql-standard created
* You can verify creation with the following command and output:
```bash
$ kubectl get mysqlinstanceclasses -n app-project1-dev
NAME AGE
mysql-standard 27s
```
Once again, you are free to create more `MySQLInstanceClass` instances in this
namespace to define more classes of service. For instance, if you created
`mysql-azure-large` above, you may want to create a `MySQLInstanceClass` named
`mysql-large` that references it. You may also choose to create MySQL resource
classes for other non-Azure providers, and reference them for a class of service
in the `app-project1-dev` namespace.
You may specify *one* instance of a portable class kind as *default* in each
namespace. This means that the portable resource class instance will be applied
to claims that do not directly reference a portable class. If we wanted to make
our `mysql-standard` instance the default `MySQLInstanceClass` for namespace
`app-project1-dev`, we could do so by adding a label:
```yaml
---
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstanceClass
metadata:
name: mysql-standard
namespace: app-project1-dev
labels:
default: "true"
classRef:
kind: SQLServerClass
apiVersion: database.azure.crossplane.io/v1alpha2
name: azure-mysql-standard
namespace: azure-infra-dev
```
#### Resource Claims
Resource claims are used to create external resources by referencing a class of
service in the claim's namespace. When a claim is created, Crossplane uses the
referenced portable class to find a cloud-specific resource class to use as the
configuration for the external resource. We need a to create a claim to
provision the MySQL database we will use with Azure.
In the `SQLServerClass` above, we added the labels `size: standard` and `demo:
true`, so our claim will be scheduled to that class using the labels are
specified in the `claim.spec.classSelector`. If there are multiple classes which
match the specified label(s) one will be chosen at random.
* Define a `MySQLInstance` claim in `mysql-claim.yaml` and create it:
```yaml
cat > mysql-claim.yaml <<EOF
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstance
metadata:
name: mysql-claim
namespace: app-project1-dev
spec:
classRef:
name: mysql-standard
writeConnectionSecretToRef:
name: wordpressmysql
engineVersion: "5.6"
kind: MySQLInstance
metadata:
name: mysql-claim
spec:
classSelector:
matchLabels:
size: standard
demo: true
engineVersion: "5.6"
writeConnectionSecretToRef:
name: wordpressmysql
EOF
kubectl apply -f mysql-claim.yaml
@ -390,7 +213,7 @@ indicates the managed resource was successfully provisioned and is ready for
consumption. You can see when claim is bound using the following:
```bash
$ kubectl get mysqlinstances -n app-project1-dev
$ kubectl get mysqlinstances
NAME STATUS CLASS VERSION AGE
mysql-claim Bound mysql-standard 5.6 11m
```
@ -399,9 +222,9 @@ If the `STATUS` is blank, we are still waiting for the claim to become bound.
You can observe resource creation progression using the following:
```bash
$ kubectl describe mysqlinstance mysql-claim -n app-project1-dev
$ kubectl describe mysqlinstance mysql-claim
Name: mysql-claim
Namespace: app-project1-dev
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"database.crossplane.io/v1alpha1","kind":"MySQLInstance","metadata":{"annotations":{},"name":"mysql-claim","namespace":"team..."}}
@ -420,7 +243,7 @@ Spec:
Name: mysql-standard
Engine Version: 5.6
Resource Ref:
API Version: database.azure.crossplane.io/v1alpha2
API Version: database.azure.crossplane.io/v1alpha3
Kind: MySQLServer
Name: mysqlinstance-6a7fe064-d888-11e9-ab90-42b6bb22213a
Namespace: azure-infra-dev
@ -442,24 +265,7 @@ Events: <none>
*Note: You must wait until the claim becomes bound before continuing with this
guide. It could take a few minutes for Azure to complete MySQL creation.*
We referenced our portable `MySQLInstanceClass` directly in the claim above, but
if you specified that `mysql-standard` was the default `MySQLInstanceClass` for
namespace `app-project1-dev`, we could have omitted the claim's `classRef` and
it would automatically be assigned:
```yaml
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstance
metadata:
name: mysql-claim
namespace: app-project1-dev
spec:
writeConnectionSecretToRef:
name: wordpressmysql
engineVersion: "5.6"
```
#### Virtual Network Rule
### Virtual Network Rule
Before we install Wordpress, we need establish connectivity between our MySQL
database and our AKS cluster. We can do this by creating a [Virtual Network
@ -468,7 +274,7 @@ Rule][azure-vnet-rule].
* Set `MYSQL_NAME` environment variable:
```bash
export MYSQL_NAME=$(kubectl get -o json mysqlinstance mysql-claim -n app-project1-dev | jq -j '.spec.resourceRef.name')
export MYSQL_NAME=$(kubectl get -o json mysqlinstance mysql-claim | jq -j '.spec.resourceRef.name')
```
* Define a `MySQLServerVirtualNetworkRule` in `wordpress-vnet-rule.yaml` and
@ -477,14 +283,13 @@ export MYSQL_NAME=$(kubectl get -o json mysqlinstance mysql-claim -n app-project
```yaml
cat > wordpress-vnet-rule.yaml <<EOF
---
apiVersion: database.azure.crossplane.io/v1alpha2
apiVersion: database.azure.crossplane.io/v1alpha3
kind: MySQLServerVirtualNetworkRule
metadata:
name: wordpress-vnet-rule
namespace: app-project1-dev
spec:
name: wordpress-vnet-rule
serverName: ${MYSQL_NAME
serverName: ${MYSQL_NAME}
resourceGroupName: ${AKS_RESOURCE_GROUP}
properties:
virtualNetworkSubnetId: /subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${AKS_RESOURCE_GROUP}/providers/Microsoft.Network/virtualNetworks/${AKS_VNET}/subnets/aks-subnet
@ -500,7 +305,7 @@ kubectl apply -f wordpress-vnet-rule.yaml
* You can verify creation with the following command and output:
```bash
kubectl get mysqlservervirtualnetworkrules -n app-project1-dev
kubectl get mysqlservervirtualnetworkrules
NAME AGE
wordpress-vnet-rule 27s
```
@ -516,11 +321,13 @@ the claim became `Bound`.
* Check to make sure `wordpressmysql` exists and is populated:
```bash
$ kubectl describe secret wordpressmysql -n app-project1-dev
$ kubectl describe secret wordpressmysql
Name: wordpressmysql
Namespace: app-project1-dev
Namespace: default
Labels: <none>
Annotations: <none>
Annotations: crossplane.io/propagate-from-name: 6a7fe064-d888-11e9-ab90-42b6bb22213a
crossplane.io/propagate-from-namespace: crossplane-system
crossplane.io/propagate-from-uid: c539fcef-f698-11e9-a957-12a4af141bea
Type: Opaque
@ -538,7 +345,6 @@ cat > wordpress-app.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: app-project1-dev
name: wordpress
labels:
app: wordpress
@ -577,7 +383,6 @@ spec:
apiVersion: v1
kind: Service
metadata:
namespace: app-project1-dev
name: wordpress
labels:
app: wordpress
@ -608,55 +413,42 @@ becomes available, then navigate to the address. You should see the following:
![alt wordpress](wordpress-start.png)
## Uninstall
### Wordpress
## Clean Up
All Wordpress components that we installed can be deleted with one command:
Because we put all of our configuration in a single directory, we can delete it
all with this command:
```bash
kubectl delete -f wordpress-app.yaml
kubectl delete -f wordpress/
```
### Crossplane Configuration
To delete all created resources, but leave Crossplane and the Azure stack
running, execute the following commands:
If you would like to also uninstall Crossplane and the AWS stack, run the
following command:
```bash
kubectl delete -f wordpress-vnet-rule.yaml
kubectl delete -f mysql-claim.yaml
kubectl delete -f mysql-standard.yaml
kubectl delete -f azure-mysql-standard.yaml
kubectl delete -f app-project1-dev-namespace.yaml
kubectl delete -f azure-provider.yaml
kubectl delete -f azure-infra-dev-namespace.yaml
kubectl delete namespace crossplane-system
```
## Conclusion and Next Steps
In this guide we:
* Setup an AKS Cluster using the Azure CLI
* Installed Crossplane from alpha channel
* Installed the Azure stack
* Created an infrastructure (`azure-infra-dev`) and application
(`app-project1-dev`) namespace
* Setup an Azure `Provider` with our account
* Created a `SQLServerClass` in the ` with configuration for a MySQL database on
Azure
* Created a `MySQLInstanceClass` that specified the `SQLServerClass` as
`mysql-standard` in the `app-project1-dev` namespace
* Created a `MySQLInstance` claim in the `app-project1-dev1` namespace that
referenced `mysql-standard`
* Created a `MySQLInstance` claim in the that was scheduled to the
`mysql-standard` resource class
* Created a `MySQLServerVirtualNetworkRule` to establish secure connectivity
between our AKS Cluster and MySQL database
* Created a `Deployment` and `Service` to run Wordpress on our AKS Cluster and
assign an external IP address to it
If you would like to try out a similar workflow using a different cloud
provider, take a look at the other [services guides][services]. If you would like
to learn more about stacks, checkout the [stacks guide][stacks]
provider, take a look at the other [services guides][services]. If you would
like to learn more about stacks, checkout the [stacks guide][stacks].
<!-- Named links -->
[azure-cli]: https://docs.microsoft.com/en-us/cli/azure/?view=azure-cli-latest
@ -664,8 +456,8 @@ to learn more about stacks, checkout the [stacks guide][stacks]
[install-kubectl]: https://kubernetes.io/docs/tasks/tools/install-kubectl/
[using-helm]: https://docs.helm.sh/using_helm/
[jq-docs]: https://stedolan.github.io/jq/
[aks-walkthrough]: https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough
[service endpoint]: https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-service-endpoint-policies-overview
[aks-kubectl]: https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough#connect-to-the-cluster
[crossplane-install]: ../install-crossplane.md#alpha
[azure-stack-install]: ../install-crossplane.md#azure-stack
@ -675,6 +467,7 @@ to learn more about stacks, checkout the [stacks guide][stacks]
[azure-mysql]: https://azure.microsoft.com/en-us/services/mysql/
[azure-vnet-rule]: https://docs.microsoft.com/en-us/azure/mysql/concepts-data-access-and-security-vnet
[static provisioning]: ../concepts.md#dynamic-and-static-provisioning
[services]: ../services-guide.md
[stacks]: ../stacks-guide.md

View File

@ -7,26 +7,6 @@ indent: true
# GCP Services Guide
## Table of Contents
1. [Introduction](#introduction)
2. [Pre-requisites](#pre-requisites)
3. [Preparation](#preparation)
1. [GKE Cluster Steps](#gke-cluster-steps)
2. [Crossplane Steps](#crossplane-steps)
1. [Installation](#installation)
2. [GCP Provider](#gcp-provider)
3. [Cloud Specific Resource Classes](#cloud-specific-resource-classes)
4. [Namespaces](#namespaces)
5. [Portable Resource Classes](#portable-resource-classes)
6. [Connecting MySQL Instance and GKE Cluster](#connecting-mysql-instance-and-gke-cluster)
4. [Install Wordpress](#install-wordpress)
5. [Clean Up](#clean-up)
1. [Wordpress](#wordpress)
2. [Crossplane](#crossplane)
## Introduction
This user guide will walk you through Wordpress application deployment using
your existing Kubernetes cluster and Crossplane managed resources. We will:
* Install Crossplane to your cluster.
@ -34,10 +14,23 @@ your existing Kubernetes cluster and Crossplane managed resources. We will:
* Create network resources to get GKE cluster to connect to MySQL instance.
* Deploy Wordpress.
## Table of Contents
1. [Pre-requisites](#pre-requisites)
1. [Preparation](#preparation)
1. [Set Up Crossplane](#set-up-crossplane)
1. [Install in Target Cluster](#install-in-target-cluster)
1. [Cloud Provider](#cloud-provider)
1. [Resource Classes](#resource-classes)
1. [Configure Managed Service Access](#configure-managed-service-access)
1. [Provision MySQL](#provision-mysql)
1. [Resource Claim](#resource-claim)
1. [Install Wordpress](#install-wordpress)
1. [Clean Up](#clean-up)
1. [Conclusion and Next Steps](#conclusion-and-next-steps)
## Pre-requisites
* [gcloud CLI](https://cloud.google.com/sdk/docs/quickstarts)
* Make sure to configure the authentication after installing gcloud cli.
* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
* A GKE cluster.
@ -46,8 +39,8 @@ your existing Kubernetes cluster and Crossplane managed resources. We will:
This guide assumes that you have setup the gcloud CLI and are logged in to your
desired account.
*Note: environment variables are used throughout this guide. You should use
your own values.*
*Note: environment variables are used throughout this guide. You should use your
own values.*
Run the following:
```bash
@ -56,123 +49,37 @@ export NETWORK_NAME=default # the network that your GKE cluster lives in.
export SUBNETWORK_NAME=default # the subnetwork that your GKE cluster lives in.
```
### GKE Cluster Steps
## Set Up Crossplane
We are assuming you've got a GKE cluster up and running created either via
GCP Console or gcloud CLI. If you don't, the basic command to create one is
the following:
### Installation
1. Create a GKE cluster:
```bash
gcloud beta container --project "${PROJECT_ID}" clusters create "my-existing-cluster" \
--zone "us-central1-a" \
--cluster-version "1.13.7-gke.8" \
--machine-type "n1-standard-1" \
--image-type "COS" \
--disk-type "pd-standard" \
--disk-size "100" \
--metadata disable-legacy-endpoints=true \
--scopes "https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/trace.append" \
--num-nodes "1" \
--enable-cloud-logging --enable-cloud-monitoring --enable-ip-alias --enable-autoupgrade --enable-autorepair --no-enable-basic-auth \
--network "projects/${PROJECT_ID}/global/networks/${NETWORK_NAME}" \
--subnetwork "projects/${PROJECT_ID}/regions/us-central1/subnetworks/${SUBNETWORK_NAME}" \
--default-max-pods-per-node "110" \
--addons HorizontalPodAutoscaling,HttpLoadBalancing
```
This may take a while to finish.
2. Connect to your GKE cluster
```bash
gcloud container clusters get-credentials "my-existing-cluster" --zone us-central1-a --project "${PROJECT_ID}"
```
3. Make sure `kubectl` is able to communicate with your GKE cluster
```bash
kubectl cluster-info
```
4. Make sure Helm is installed with permissions to work on `crossplane-system`
namespace. The easiest way to achieve that is to run the following command
that makes Helm's server side component `tiller` cluster admin:
```bash
kubectl -n kube-system create serviceaccount tiller
kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller
helm init --service-account=tiller
```
### Crossplane Steps
#### Installation
Assuming you are
[connected](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl)
to your GKE cluster via `kubectl`:
* Install Crossplane from alpha channel using the [Crossplane Installation
Guide](../install-crossplane.md#alpha)
* Install the GCP stack into Crossplane using the [GCP stack
section](../install-crossplane.md#gcp-stack) of the install guide.
We will be using `gcp-infra-dev` namespace to store our cloud-specific resource
classes, cloud resources and providers. But you can use any namespace name for
that matter.
To keep your resource configuration organized, start by creating a new
directory:
```bash
kubectl create namespace gcp-infra-dev
export INFRA_NAMESPACE=gcp-infra-dev
mkdir wordpress && cd $_
```
#### GCP Provider
### Cloud Provider
It is essential to make sure that a GCP service account to be used by
Crossplane is configured with all the necessary permissions outlined in the
[provider guide](../cloud-providers/gcp/gcp-provider.md).
It is essential to make sure that the GCP user credentials are configured in
Crossplane as a provider. Please follow the steps [provider
guide](../cloud-providers/gcp/gcp-provider.md) for more information.
Using the service account json `crossplane-gcp-provider-key.json` that you
acquired from GCP:
### Resource Classes
* Generate Base64 encoded value to store in a `Secret`:
```bash
export BASE64ENCODED_GCP_PROVIDER_CREDS=$(base64 crossplane-gcp-provider-key.json | tr -d "\n")
```
* Define a GCP `Provider` and `Secret`:
```bash
cat > gcp-provider.yaml <<EOF
---
apiVersion: v1
data:
credentials.json: $BASE64ENCODED_GCP_PROVIDER_CREDS
kind: Secret
metadata:
name: gcp-provider-creds
type: Opaque
---
apiVersion: gcp.crossplane.io/v1alpha2
kind: Provider
metadata:
name: gcp-provider
spec:
credentialsSecretRef:
name: gcp-provider-creds
key: credentials.json
projectID: $PROJECT_ID
EOF
kubectl -n $INFRA_NAMESPACE apply -f gcp-provider.yaml
unset BASE64ENCODED_GCP_PROVIDER_CREDS # we don't need this anymore.
```
* Verify GCP provider was successfully registered by the crossplane
```bash
kubectl -n $INFRA_NAMESPACE get providers.gcp.crossplane.io
kubectl -n $INFRA_NAMESPACE get secrets
```
#### Cloud-Specific Resource Classes
Cloud-specific resource classes are used to define a reusable configuration for
a specific managed service. Wordpress requires a MySQL database, which can be
satisfied by a [Google Cloud SQL Instance](https://cloud.google.com/sql/docs/mysql/).
Resource classes are used to define a reusable configuration for a specific
managed service. Wordpress requires a MySQL database, which can be satisfied by
a [Google Cloud SQL Instance](https://cloud.google.com/sql/docs/mysql/).
* Define a GCP CloudSQL class `CloudSQLInstanceClass`:
@ -183,7 +90,10 @@ apiVersion: database.gcp.crossplane.io/v1beta1
kind: CloudSQLInstanceClass
metadata:
name: standard-cloudsql
labels:
size: standard
specTemplate:
writeConnectionSecretsToNamespace: crossplane-system
forProvider:
databaseVersion: MYSQL_5_7
region: us-central1
@ -197,18 +107,17 @@ specTemplate:
privateNetwork: projects/$PROJECT_ID/global/networks/$NETWORK_NAME
providerRef:
name: gcp-provider
namespace: $INFRA_NAMESPACE
reclaimPolicy: Delete
EOF
kubectl -n $INFRA_NAMESPACE apply -f gcp-mysql-standard.yaml
kubectl apply -f gcp-mysql-standard.yaml
```
* You can verify creation with the following command and output:
*Command*
```bash
kubectl get cloudsqlinstanceclass -n ${INFRA_NAMESPACE}
kubectl get cloudsqlinstanceclasses
```
*Output*
```bash
@ -220,185 +129,11 @@ You are free to create more GCP `CloudSQLInstanceClass` instances to define more
potential configurations. For instance, you may create `large-gcp-mysql` with
field `storageGB: 100`.
#### Namespaces
### Configure Managed Service Access
Kubernetes namespaces allow for separation of environments within your cluster.
You may choose to use namespaces to group resources by team, application, or any
other logical distinction. For this demo, we will create a namespace called
`app-project1-dev`, which we will use to group our Wordpress resources.
```bash
kubectl create namespace app-project1-dev
```
#### Portable Resource Classes
Portable resource classes are used to define a class of service in a single
namespace for an abstract service type. We want to define our GCP
`CloudSQLInstanceClass` as the standard MySQL class of service in the namespace
that our Wordpress resources will live in.
* Define a `MySQLInstanceClass` in `mysql-standard.yaml` for namespace `app-project1-dev`:
```bash
cat > mysql-standard.yaml <<EOF
---
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstanceClass
metadata:
name: mysql-standard
classRef:
kind: CloudSQLInstanceClass
apiVersion: database.gcp.crossplane.io/v1beta1
name: standard-cloudsql
namespace: $INFRA_NAMESPACE
EOF
kubectl -n app-project1-dev apply -f mysql-standard.yaml
```
* You can verify creation with the following command and output:
*Command*
```bash
kubectl -n app-project1-dev get mysqlinstanceclasses
```
*Output*
```bash
NAME AGE
mysql-standard 27s
```
Once again, you are free to create more `MySQLInstanceClass` instances in this
namespace to define more classes of service. For instance, if you created
`mysql-gcp-large` above, you may want to create a `MySQLInstanceClass` named
`mysql-large` that references it. You may also choose to create MySQL resource
classes for other non-GCP providers, and reference them for a class of service
in the `app-project1-dev` namespace.
> Portable classes are the resource classes that are not cloud specific and
their purpose is to refer to cloud specific resource classes that will be
used for provisioning. For example, MySQLInstanceClass is a portable class
that can refer to CloudSQLInstanceClass (GCP) or RDSInstanceClass (AWS).
You may specify *one* instance of a portable class kind as *default* in each
namespace. This means that the portable resource class instance will be applied
to claims that do not directly reference a portable class. If we wanted to make
our `mysql-standard` instance the default `MySQLInstanceClass` for namespace
`app-project1-dev`, we could do so by adding a label:
```bash
kubectl -n app-project1-dev label mysqlinstanceclass mysql-standard default="true"
```
For more details about resource classes in Crossplane, see
[Default Resource Classes One-Pager](https://github.com/crossplaneio/crossplane/blob/master/design/one-pager-default-resource-class.md).
#### Resource Claims
Resource claims are used to create external resources by referencing a class of
service in the claim's namespace. When a claim is created, Crossplane uses the
referenced portable class to find a cloud-specific resource class to use as the
configuration for the external resource. We need to create a claim to
provision the MySQL database we will use in GCP.
* Define a `MySQLInstance` claim in `mysql-claim.yaml`.
You can omit `spec.classRef` if you want to use the default MySQLInstanceClass:
```bash
cat > mysql-claim.yaml <<EOF
---
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstance
metadata:
name: mysql-claim
spec:
classRef:
name: mysql-standard
engineVersion: "5.7"
# A secret is exported by providing the secret name
# to export it under. This is the name of the secret
# in the crossplane cluster, and it's scoped to this claim's namespace.
writeConnectionSecretToRef:
name: wordpressmysql
EOF
kubectl -n app-project1-dev apply -f mysql-claim.yaml
```
What we are looking for is for `STATUS` value to become `Bound` which indicates
the managed resource was successfully provisioned and is ready for consumption.
You can see when claim is bound using the following:
*Command*
```bash
kubectl -n app-project1-dev get mysqlinstances
```
*Output*
```bash
NAME STATUS CLASS VERSION AGE
mysql-claim Bound mysql-standard 5.6 11m
```
If the `STATUS` is blank, we are still waiting for the claim to become bound.
You can observe resource creation progression using the following:
*Command*
```bash
kubectl -n app-project1-dev describe mysqlinstance mysql-claim --watch
```
*Output*
```
Name: mysql-claim
Namespace: app-project1-dev
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"database.crossplane.io/v1alpha1","kind":"MySQLInstance","metadata":{"annotations":{},"name":"mysql-claim","namespace":"team..."}}
API Version: database.crossplane.io/v1alpha1
Kind: MySQLInstance
Metadata:
Creation Timestamp: 2019-09-16T13:46:42Z
Finalizers:
finalizer.resourceclaim.crossplane.io
Generation: 2
Resource Version: 4256
Self Link: /apis/database.crossplane.io/v1alpha1/namespaces/app-project1-dev/mysqlinstances/mysql-claim
UID: 6a7fe064-d888-11e9-ab90-42b6bb22213a
Spec:
Class Ref:
Name: mysql-standard
Engine Version: 5.6
Resource Ref:
API Version: database.gcp.crossplane.io/v1beta1
Kind: CloudSQLInstance
Name: mysqlinstance-6a7fe064-d888-11e9-ab90-42b6bb22213a
Namespace: gcp-infra-dev
Write Connection Secret To Ref:
Name: wordpressmysql
Status:
Conditions:
Last Transition Time: 2019-09-16T13:46:42Z
Reason: Managed claim is waiting for managed resource to become bindable
Status: False
Type: Ready
Last Transition Time: 2019-09-16T13:46:42Z
Reason: Successfully reconciled managed resource
Status: True
Type: Synced
Events: <none>
```
*Note: You must wait until the claim becomes bound before continuing with this
guide. It could take a few minutes for GCP to complete CloudSQL creation.*
#### Connecting MySQL Instance and GKE Cluster
Before we install Wordpress, we need to establish connectivity between our MySQL
database and our GKE cluster. We can do this by creating a [Private Service
Before we install Wordpress, we need to establish connectivity between the the
MySQL database and the GKE cluster. We can do this by creating a [Private
Service
Connection](https://cloud.google.com/vpc/docs/configure-private-services-access).
You can create it by following the instructions at the link above, or you could
@ -411,14 +146,13 @@ use Crossplane to do it:
---
# 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
apiVersion: compute.gcp.crossplane.io/v1alpha3
kind: GlobalAddress
metadata:
name: example-globaladdress
spec:
providerRef:
name: gcp-provider
namespace: $INFRA_NAMESPACE
reclaimPolicy: Delete
name: example-globaladdress
purpose: VPC_PEERING
@ -428,22 +162,21 @@ use Crossplane to do it:
---
# 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
apiVersion: servicenetworking.gcp.crossplane.io/v1alpha3
kind: Connection
metadata:
name: example-connection
spec:
providerRef:
name: gcp-provider
namespace: $INFRA_NAMESPACE
reclaimPolicy: Delete
parent: services/servicenetworking.googleapis.com
network: projects/$PROJECT_ID/global/networks/$NETWORK_NAME
reservedPeeringRanges:
- example-globaladdress
reservedPeeringRangeRefs:
- name: example-globaladdress
EOF
kubectl -n $INFRA_NAMESPACE apply -f network.yaml
kubectl apply -f network.yaml
```
* You can verify creation with the following command and output:
@ -451,17 +184,172 @@ use Crossplane to do it:
*Command*
```bash
kubectl -n $INFRA_NAMESPACE get connection example-connection -o custom-columns='NAME:.metadata.name,FIRST_CONDITION:.status.conditions[0].status,SECOND_CONDITION:.status.conditions[1].status'
kubectl describe connection.servicenetworking.gcp.crossplane.io example-connection
```
*Output*
```bash
NAME FIRST_CONDITION SECOND_CONDITION
example-connection True True
```yaml
Name: example-connection
Namespace:
Labels: <none>
Annotations: crossplane.io/external-name: example-connection
kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"servicenetworking.gcp.crossplane.io/v1alpha3","kind":"Connection","metadata":{"annotations":{},"name":"example-connection"}...
API Version: servicenetworking.gcp.crossplane.io/v1alpha3
Kind: Connection
Metadata:
Creation Timestamp: 2019-10-28T14:10:23Z
Finalizers:
finalizer.managedresource.crossplane.io
Generation: 1
Resource Version: 7245
Self Link: /apis/servicenetworking.gcp.crossplane.io/v1alpha3/connections/example-connection
UID: aeae7e4d-f98c-11e9-8275-42010a800122
Spec:
Network: projects/crossplane-playground/global/networks/default
Parent: services/servicenetworking.googleapis.com
Provider Ref:
Name: gcp-provider
Reclaim Policy: Delete
Reserved Peering Ranges:
example-globaladdress
Status:
Conditions:
Last Transition Time: 2019-10-28T14:10:23Z
Reason: Successfully resolved managed resource references to other resources
Status: True
Type: ReferencesResolved
Last Transition Time: 2019-10-28T14:10:23Z
Reason: Managed resource is being created
Status: False
Type: Ready
Last Transition Time: 2019-10-28T14:10:23Z
Reason: Successfully reconciled managed resource
Status: True
Type: Synced
Events: <none>
```
Wait for both conditions to be true to continue.
We are looking for the `Connection` resource to report `Type: Ready` `Status:
True` in its `status.conditions`.
## Provision
### Resource Claim
Resource claims are used for dynamic provisioning of a managed resource (like a
MySQL instance) by matching the claim to a resource class. This can be done in
several ways: (a) rely on the default class marked
`resourceclass.crossplane.io/is-default-class: "true"`, (b) use a
`claim.spec.classRef` to a specific class, or (c) match on class labels using a
`claim.spec.classSelector`.
*Note: claims may also be used in [static
provisioning](../concepts.md#dynamic-and-static-provisioning) with a reference
to an existing managed resource.*
In the `CloudSQLInstanceClass` above, we added the label `size: standard`, so
our claim will be scheduled to that class using the label is specified in the
`claim.spec.classSelector`. If there are multiple classes which match the
specified label(s) one will be chosen at random.
* Define a `MySQLInstance` claim in `mysql-claim.yaml`:
```bash
cat > mysql-claim.yaml <<EOF
---
apiVersion: database.crossplane.io/v1alpha1
kind: MySQLInstance
metadata:
name: mysql-claim
spec:
classSelector:
matchLabels:
size: standard
engineVersion: "5.7"
# A secret is exported by providing the secret name
# to export it under. This is the name of the secret
# in the crossplane cluster, and it's scoped to this claim's namespace.
writeConnectionSecretToRef:
name: wordpressmysql
EOF
kubectl apply -f mysql-claim.yaml
```
What we are looking for is for the claim's `STATUS` value to become `Bound`
which indicates the managed resource was successfully provisioned and is ready
for consumption. You can see when claim is bound using the following:
*Command*
```bash
kubectl get mysqlinstances
```
*Output*
```bash
NAME STATUS CLASS-KIND CLASS-NAME RESOURCE-KIND RESOURCE-NAME AGE
mysql-claim Bound CloudSQLInstanceClass standard-cloudsql CloudSQLInstance default-mysql-claim-vtnf7 3m
```
If the `STATUS` is blank, we are still waiting for the claim to become bound.
You can observe resource creation progression using the following:
*Command*
```bash
kubectl describe mysqlinstance mysql-claim
```
*Output*
```
Name: mysql-claim
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration:
{"apiVersion":"database.crossplane.io/v1alpha1","kind":"MySQLInstance","metadata":{"annotations":{},"name":"mysql-claim","namespace":"defa...
API Version: database.crossplane.io/v1alpha1
Kind: MySQLInstance
Metadata:
Creation Timestamp: 2019-10-28T14:18:55Z
Finalizers:
finalizer.resourceclaim.crossplane.io
Generation: 3
Resource Version: 9011
Self Link: /apis/database.crossplane.io/v1alpha1/namespaces/default/mysqlinstances/mysql-claim
UID: e0329d69-f98d-11e9-8275-42010a800122
Spec:
Class Ref:
API Version: database.gcp.crossplane.io/v1beta1
Kind: CloudSQLInstanceClass
Name: standard-cloudsql
UID: 431580bd-f989-11e9-8275-42010a800122
Class Selector:
Match Labels:
Size: standard
Engine Version: 5.7
Resource Ref:
API Version: database.gcp.crossplane.io/v1beta1
Kind: CloudSQLInstance
Name: default-mysql-claim-vtnf7
UID: e07c42c5-f98d-11e9-8275-42010a800122
Write Connection Secret To Ref:
Name: wordpressmysql
Status:
Conditions:
Last Transition Time: 2019-10-28T14:18:56Z
Reason: Managed claim is waiting for managed resource to become bindable
Status: False
Type: Ready
Last Transition Time: 2019-10-28T14:18:56Z
Reason: Successfully reconciled managed resource
Status: True
Type: Synced
Events: <none>
```
*Note: You must wait until the claim becomes bound before continuing with this
guide. It could take a few minutes for GCP to complete CloudSQL creation.*
## Install Wordpress
@ -472,31 +360,42 @@ variables. It should have been populated with our MySQL connection details after
the claim became `Bound`.
> Binding status tells you whether your resource has been provisioned and ready
to use. Crossplane binds the actual resource to the claim via changing the
readiness condition to `Bound`. This happens only when the resource is ready
to be consumed.
to use. Crossplane binds the actual resource to the claim via changing the
readiness condition to `Bound`. This happens only when the resource is ready to
be consumed.
* Check to make sure `wordpressmysql` exists and is populated:
*Command*
```bash
kubectl -n app-project1-dev describe secret wordpressmysql
kubectl describe secret wordpressmysql
```
*Output*
```bash
Name: wordpressmysql
Namespace: app-project1-dev
Namespace: default
Labels: <none>
Annotations: <none>
Annotations: crossplane.io/propagate-from-name: 330cccf5-f991-11e9-8275-42010a800122
crossplane.io/propagate-from-namespace: crossplane-system
crossplane.io/propagate-from-uid: 33581ec7-f991-11e9-8275-42010a800122
Type: Opaque
Data
====
endpoint: 75 bytes
password: 27 bytes
username: 58 bytes
endpoint: 10 bytes
password: 27 bytes
publicIP: 13 bytes
serverCACertificateCert: 1272 bytes
serverCACertificateCommonName: 98 bytes
serverCACertificateCreateTime: 24 bytes
serverCACertificateExpirationTime: 24 bytes
privateIP: 10 bytes
serverCACertificateCertSerialNumber: 1 bytes
serverCACertificateInstance: 25 bytes
serverCACertificateSha1Fingerprint: 40 bytes
username: 4 bytes
```
* Define the `Deployment` and `Service` in `wordpress.yaml`:
@ -555,7 +454,7 @@ username: 58 bytes
type: LoadBalancer
EOF
kubectl -n app-project1-dev apply -f wordpress.yaml
kubectl apply -f wordpress.yaml
```
* You can verify creation with the following command and output:
@ -563,17 +462,17 @@ username: 58 bytes
*Command*
```bash
kubectl -n app-project1-dev get -f wordpress.yaml
kubectl get -f wordpress.yaml
```
*Output*
```bash
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/wordpress 1/1 1 1 11m
deployment.apps/wordpress 1/1 1 1 77s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/wordpress LoadBalancer 10.0.128.30 52.168.69.6 80:32587/TCP 11m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/wordpress LoadBalancer 10.12.3.121 35.223.147.148 80:30287/TCP 77s
```
If the `EXTERNAL-IP` field of the `LoadBalancer` is `<pending>`, wait until it
@ -583,31 +482,21 @@ becomes available, then navigate to the address. You should see the following:
## Clean Up
### Wordpress
The `Service` and `Deployment` of Wordpress we installed can be removed with the following command:
Because we put all of our configuration in a single directory, we can delete it
all with this command:
```bash
kubectl -n app-project1-dev delete -f wordpress.yaml
kubectl delete -f wordpress/
```
### Crossplane
To delete all created resources, but leave Crossplane and the GCP stack
running, execute the following commands:
If you would like to also uninstall Crossplane and the AWS stack, run the
following command:
```bash
kubectl -n app-project1-dev delete -f mysql-claim.yaml
kubectl -n app-project1-dev delete -f mysql-standard.yaml
kubectl -n $INFRA_NAMESPACE delete -f network.yaml
kubectl -n $INFRA_NAMESPACE delete -f gcp-mysql-standard.yaml
kubectl -n $INFRA_NAMESPACE delete -f gcp-provider.yaml
kubectl delete namespace app-project1-dev
kubectl delete namespace $INFRA_NAMESPACE
kubectl delete namespace crossplane-system
```
## Conclusion
## Conclusion and Next Steps
We're done!
@ -620,21 +509,17 @@ In this guide, we:
* Connected our GKE cluster to our MySQL database.
* Installed Wordpress to our GKE cluster.
## Next Steps
In this guide, we used an existing GKE cluster but actually Crossplane can
provision a Kubernetes cluster from GCP just like it provisions a MySQL
database.
We deployed Wordpress using bare `Deployment` and `Service` resources
but there is actually a Wordpress App stack that creates these resources
for us!
We deployed Wordpress using bare `Deployment` and `Service` resources but there
is actually a Wordpress App stack that creates these resources for us!
Check out the [stack guides](link to stack guides page)!
Check out the [stacks guides](../stacks-guide.md)!
## References
* [gcloud CLI](https://cloud.google.com/sdk/docs/quickstarts)
* [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/)
* [Crossplane Installation Guide](../install-crossplane.md#alpha)
* [GCP Stack Installation](../install-crossplane.md#gcp-stack)