Documentation

PLEASE NOTE: This document applies to v0.7 version and not to the latest release v0.8

Documentation for other releases can be found by using the version selector in the top right of any doc page.

Azure Services Guide

This user guide will walk you through Wordpress application deployment using Crossplane managed resources and the official Wordpress Docker image.

Table of Contents

  1. Pre-requisites
  2. Preparation
  3. Set Up Crossplane
    1. Install in Target Cluster
    2. Cloud Provider
    3. Resource Classes
    4. Configure Managed Service Access
  4. Provision MySQL
    1. Resource Claim
    2. Virtual Network Rule
  5. Install Wordpress
  6. Clean Up
  7. Conclusion and Next Steps

Pre-requisites

These tools are required to complete this guide. They must be installed on your local machine.

Preparation

This guide assumes that you have setup the Azure CLI and are logged in to your 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.

export AKS_RESOURCE_GROUP=myAKSResourceGroup
export AKS_VNET=myAKSVnet
export AKS_REGION=myRegion
export SUBSCRIPTION_ID=$(az account list | jq -j '.[0].id')

Set Up Crossplane

Install in Target Cluster

Assuming you are connected to your AKS cluster via kubectl:

  1. Install Crossplane from alpha channel. (See the Crossplane Installation Guide for more information.)
helm repo add crossplane-alpha https://charts.crossplane.io/alpha
helm install --name crossplane --namespace crossplane-system crossplane-alpha/crossplane
  1. Install the Azure stack into Crossplane. (See the Azure stack section of the install guide for more information.)
cat > stack-azure.yaml <<EOF
apiVersion: stacks.crossplane.io/v1alpha1
kind: ClusterStackInstall
metadata:
  name: stack-azure
  namespace: crossplane-system
spec:
  package: "crossplane/stack-azure:v0.5.0"
EOF

kubectl apply -f stack-azure.yaml
  1. Obtain Azure credentials. (See the Cloud Provider Credentials docs for more information.)

Cloud Provider

It is essential to make sure that the Azure user credentials are configured in Crossplane as a provider. Please follow the steps in the Azure provider guide for more information.

Resource Classes

To keep your resource configuration organized, start by creating a new directory:

mkdir wordpress && cd $_

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 instance.

cat > azure-mysql-standard.yaml <<EOF
---
apiVersion: database.azure.crossplane.io/v1beta1
kind: SQLServerClass
metadata:
  name: azure-mysql-standard
  labels:
    size: standard
    demo: "true"
specTemplate:
  forProvider:
    administratorLogin: myadmin
    resourceGroupName: $AKS_RESOURCE_GROUP
    location: $AKS_REGION
    sslEnforcement: Disabled
    version: "5.6"
    sku:
      tier: GeneralPurpose
      capacity: 2
      family: Gen5
    storageProfile:
      storageMB: 25600
  writeConnectionSecretsToNamespace: crossplane-system
  providerRef:
    name: azure-provider
  reclaimPolicy: Delete
EOF

kubectl apply -f azure-mysql-standard.yaml

sqlserverclass.database.azure.crossplane.io/azure-mysql-standard created

$ kubectl get sqlserverclasses
NAME                   PROVIDER-REF     RECLAIM-POLICY   AGE
azure-mysql-standard   azure-provider   Delete           17s

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.

Configure Managed Service Access

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 for all subnets. If you do not already have this configured, Azure has a guide on how to set it up.

Provision MySQL

Resource Claims

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 with a reference to an existing managed resource.

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.

cat > mysql-claim.yaml <<EOF
apiVersion: database.crossplane.io/v1alpha1
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

What we are looking for is for the 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:

$ kubectl get mysqlinstances
NAME          STATUS   CLASS-KIND       CLASS-NAME             RESOURCE-KIND   RESOURCE-NAME               AGE
mysql-claim   Bound    SQLServerClass   azure-mysql-standard   MySQLServer     default-mysql-claim-bm4ft   9s

If the STATUS is blank, we are still waiting for the claim to become bound. You can observe resource creation progression using the following:

$ kubectl describe mysqlinstance mysql-claim
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-28T15:43:28Z
  Finalizers:
    finalizer.resourceclaim.crossplane.io
  Generation:        3
  Resource Version:  11072
  Self Link:         /apis/database.crossplane.io/v1alpha1/namespaces/default/mysqlinstances/mysql-claim
  UID:               afff42b3-f999-11e9-a2d5-c64d758a651f
Spec:
  Class Ref:
    API Version:  database.azure.crossplane.io/v1beta1
    Kind:         SQLServerClass
    Name:         azure-mysql-standard
    UID:          5710f3db-f999-11e9-a2d5-c64d758a651f
  Class Selector:
    Match Labels:
      Demo:        true
      Size:        standard
  Engine Version:  5.6
  Resource Ref:
    API Version:  database.azure.crossplane.io/v1beta1
    Kind:         MySQLServer
    Name:         default-mysql-claim-bm4ft
    UID:          b02c1389-f999-11e9-a2d5-c64d758a651f
  Write Connection Secret To Ref:
    Name:  wordpressmysql
Status:
  Conditions:
    Last Transition Time:  2019-10-28T15:43:29Z
    Reason:                Managed claim is waiting for managed resource to become bindable
    Status:                False
    Type:                  Ready
    Last Transition Time:  2019-10-28T15:43:29Z
    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 Azure to complete MySQL creation.

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 Rule.

export MYSQL_NAME=$(kubectl get -o json mysqlinstance mysql-claim | jq -j '.spec.resourceRef.name')
cat > wordpress-vnet-rule.yaml <<EOF
---
apiVersion: database.azure.crossplane.io/v1alpha3
kind: MySQLServerVirtualNetworkRule
metadata:
  name: wordpress-vnet-rule
spec:
  name: wordpress-vnet-rule
  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
  providerRef:
    name: azure-provider
  reclaimPolicy: Delete
EOF

kubectl apply -f wordpress-vnet-rule.yaml
$ kubectl get mysqlservervirtualnetworkrules
NAME                  STATE   AGE
wordpress-vnet-rule   Ready   17s

Install Wordpress

Installing Wordpress requires creating a Kubernetes Deployment and load balancer Service. We will point the deployment to the wordpressmysql secret that we specified in our claim above for the Wordpress container environment variables. It should have been populated with our MySQL connection details after the claim became Bound.

$ kubectl describe secret wordpressmysql
Name:         wordpressmysql
Namespace:    default
Labels:       <none>
Annotations:  crossplane.io/propagate-from-name: 084b9476-f99e-11e9-a2d5-c64d758a651f
              crossplane.io/propagate-from-namespace: crossplane-system
              crossplane.io/propagate-from-uid: 2e71f6f9-f99e-11e9-a2d5-c64d758a651f

Type:  Opaque

Data
====
endpoint:  50 bytes
password:  27 bytes
username:  33 bytes
cat > wordpress-app.yaml <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - name: wordpress
          image: wordpress:4.6.1-apache
          env:
            - name: WORDPRESS_DB_HOST
              valueFrom:
                secretKeyRef:
                  name: wordpressmysql
                  key: endpoint
            - name: WORDPRESS_DB_USER
              valueFrom:
                secretKeyRef:
                  name: wordpressmysql
                  key: username
            - name: WORDPRESS_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: wordpressmysql
                  key: password
          ports:
            - containerPort: 80
              name: wordpress
---
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
    - port: 80
  selector:
    app: wordpress
  type: LoadBalancer
EOF

kubectl apply -f wordpress-app.yaml
$ kubectl get -f wordpress-app.yaml
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/wordpress   1/1     1            1           11m

NAME                TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
service/wordpress   LoadBalancer   10.0.128.30   52.168.69.6   80:32587/TCP   11m

If the EXTERNAL-IP field of the LoadBalancer is <pending>, wait until it becomes available, then navigate to the address. You should see the following:

alt wordpress

Clean Up

Because we put all of our configuration in a single directory, we can delete it all with this command:

kubectl delete -f wordpress/

If you would like to also uninstall Crossplane and the AWS stack, run the following command:

kubectl delete namespace crossplane-system

Conclusion and Next Steps

In this guide we:

If you would like to try out a similar workflow using a different cloud provider, take a look at the other services guides. If you would like to learn more about stacks, checkout the stacks guide.