Wordpress on Minikube

In this tutorial you’ll see how Sugarkube can be used to create a local Minikube cluster running Wordpress, install fixture data into its database and finally tear it down. We assume you’ve already installed Sugarkube.

We have a docker container with most of the dependencies you need for this tutorial except Minikube (because it doesn’t seem possible to run Minikube in a docker container). If you don’t want to install stuff on your local machine, just run our image with docker run -it sugarkube/tutorial:0.10.0 /bin/bash (but note you won’t actually be able to start a minikube cluster).


TLDR

If you’re in a hurry and don’t want to work through a long tutorial, just run the following commands. If you’ve got time or want to learn more about Sugarkube, skip this whole TLDR section.

git clone https://github.com/sugarkube/sample-project.git
cd sample-project
git checkout tutorial-0.10.0
sugarkube ws create stacks/web.yaml local-web workspaces/local-web/
sugarkube kapps install stacks/web.yaml local-web workspaces/local-web/ --run-actions --one-shot

If the last command tells you you’re missing dependencies, install them and rerun the command. Sugarkube should create a cluster. Note: you can’t create a minikube cluster if you’re using our docker image.

When you’re done exploring it, delete the minikube cluster with minikube delete as usual (but know that Sugarkube also has commands for tearing down clusters). Then move on to the Wordpress on EKS tutorial.


Grab the sample project

The first thing to do is to clone our sample project. Just run:

git clone https://github.com/sugarkube/sample-project.git
cd sample-project
git checkout tutorial-0.10.0

The sample project contains configs for different clusters, defining things like what size instances to use for various cluster types, the names of applications to install at which version, and values for variables. The configs allow different clusters to be defined, so for example there are clusters defined for a web stack containing a few Wordpress sites, and one for an ops stack containing Jenkins and monitoring applications.

From looking in the stacks/web.yaml file we can see that the local-web stack uses Minikube:

# stacks/web.yaml
local-web:        # a local stack for web development
  <<: *defaults
  cluster: standard
  provider: local
  provisioner: minikube
  profile: minikube

OK that’s a good place to start. We’ll spin up this local-web stack.

Sugarkube has a simple way of parameterising applications to be installed, and calls these bundles “kapps”. Kapps are simply git directories containing a file called sugarkube.yaml which contains various settings. Kapps are not stored directly in a Sugarkube project so we’ll need to download them next. We do this by creating a workspace. If you wanted to do any dev work on kapps, this is where you’d do it. They’re also used to install/delete kapps, so you’ll always need to create a workspace. Run this command:

sugarkube ws create stacks/web.yaml local-web workspaces/local-web/

You should see something like this:

Create a workspace

Now if you explore the contents of workspaces/local-web you should see a couple of directories, and within those other directories. The names of the top-level directories correspond to the IDs of the manifests defined for the local-web stack. See – there are 2 manifests, one called bootstrap and one called wordpress. Have a look in the manifest files (in ../manifests/web/) and you’ll see which kapps are defined for each manifest. We just created a workspace by downloading kapps from our kapps repo at various tags. The IDs of the kapps are used to name the directories they’re checked out into (e.g. workspaces/local-web/wordpress/site1, etc.).

Dependencies

It’d be pretty annoying if your machine didn’t have all the software necessary to launch a cluster. Conveniently Sugarkube has a command that validates you have everything you need. You don’t want to kick it off and go for a coffee only to find it bombed out part way through because you were missing a binary on your machine. So let’s see what we need to create a local-web cluster. Run this:

sugarkube kapps validate stacks/web.yaml local-web workspaces/local-web/ -v

You should see something like this:

Validate kapps

The -v flag makes Sugarkube print verbose output. You don’t have to supply it, but it can be useful during development.

All green means we’re all good. If you’re missing any dependencies you’ll need to install them. Please do that before continuing…

If you’re using the docker image, you’ll need to create a fake minikube executable to make validation pass. You can do this with: touch /usr/local/bin/minikube && chmod +x /usr/local/bin/minikube

Mac dependencies

If you’re on a Mac with homebrew installed, just run these commands:

brew cask install minikube
brew install kubernetes-cli
brew install kubernetes-helm
brew install terraform
brew install graphviz

Linux dependencies

If you’re on Linux install minikube, Terraform and graphviz and run this:

sudo snap install kubectl --classic
sudo snap install helm --classic

The dependency graph

Before we just blindly run some stuff that I’ve tested and that should work, let’s try to understand exactly what it’s about to do. When you ran the kapps validate command above, Sugarkube printed out a text representation of its dependency graph (a dependency graph is also called a Directed Acyclic Graph, or DAG for short):

Created the following DAG. Only nodes marked with a * will be processed:
    prelaunch:terraform-bucket (conditions failed) - depends on: <nothing>
    prelaunch:public-hosted-zone (conditions failed) - depends on: prelaunch:terraform-bucket
  * prelaunch:no-op - depends on: prelaunch:public-hosted-zone
  * bootstrap:tiller - depends on: prelaunch:no-op
  * bootstrap:cert-manager - depends on: bootstrap:tiller
  * bootstrap:nginx1 - depends on: bootstrap:cert-manager
    wordpress:site2 (conditions failed) - depends on: bootstrap:nginx1
  * wordpress:site1 - depends on: bootstrap:nginx1

So this cluster we’re going to create will set up a Wordpress site. For routing it’ll use Nginx ingress instead of Minikube’s default ingress controller, so it more closely replicates what we’ll deploy in a cloud cluster. From the graph we can see the stack actually defines 2 Wordpress sites. But notice how wordpress:site2 says conditions failed and doesn’t have a * next to it? That means Sugarkube won’t install it. Let’s find out why that is.

Have a look in stacks/web.yaml at the config for the local-web stack. Here it is in full:

local-web:        # a local stack for web development
  <<: *defaults
  cluster: standard
  provider: local
  provisioner: minikube
  profile: minikube
  defaults:
    vars:
      helm_timeout: 1200      # 20 minutes. Wordpress and MariaDB are beasts.
  manifests:        
    - uri: ../manifests/web/prelaunch.yaml
    - uri: ../manifests/web/bootstrap.yaml
    - uri: ../manifests/web/wordpress-sites.yaml
      id: wordpress       # explicitly set the manifest ID
      versions:
        site1/wordpress: master      # for this stack override the branch
      overrides:
        site2:
          conditions:
          - false             # explicitly disable this site 

The last line sets a conditions block to false for the site2 kapp in the wordpress manifest. If you comment that out and run kapps validate again, you should see that wordpress:site2 now has a * next to it indicating it’ll be installed. Presumably that site will be installed into clusters for other stacks. You might as well keep it false just to speed up creating the cluster.

Something similar is happening to disable prelaunch:terraform-bucket and prelaunch:public-hosted-zone. But this time they’re controlled by conditions blocks in the manifest file. Open manifests/web/prelaunch.yaml and you’ll see the conditions are the result of evaluating expressions:

  - id: terraform-bucket
    conditions:
      - "{{ eq .stack.provider \"aws\" }}"      # only run when using AWS
    ...

You can have a look at a kapp’s variables by running the kapps vars command, e.g.:

sugarkube kapps vars stacks/web.yaml local-web workspaces/local-web/ -i prelaunch:terraform-bucket

This prints out two groups of information – all the variables available to the kapp, and the result of rendering the kapp’s final merged config as a golang template. If you have a look through the latter you’ll find that the conditions block has a single false list entry because the local-web stack’s provider is not aws. We’ll dig more into variables in a later tutorial.

Notice above how we can override the values of variables in a stack file. In this case it’s useful because when running locally we need to increase the Helm install timeout, but it’s not necessary when targetting cloud clusters. Also, for more information on conditions see kapp settings.

Better visualisations

Trying to understand textual representations of a dependency graph isn’t too easy. Because of that, Sugarkube has a very helpful command: kapps graph. If you have graphviz installed, run this command:

sugarkube kapps graph stacks/web.yaml local-web

You won’t see an SVG if you’re using our docker image. To avoid seeing an error you should also pass --no-open.

Sugarkube should open your SVG application to show you this:

local-web dependencies

Wow, that’s handy! We can clearly see the dependency graph now, and it’s obvious what will be installed and what won’t be. This graph is pretty simple, but let’s just peek at the dependencies of the cloud-backed version of the web stack:

sugarkube kapps graph stacks/web.yaml dev-web

dev-web dependencies

That’s much more complicated. It can be really useful to visualise the dependency graph like this.

Sugarkube’s DAG traversal algorithm is simple: A kapp will only be installed if it has no parents or all it’s parents have been installed, and it’ll only be deleted if it has no children or all its children have been deleted.

Creating the local-web cluster

OK now we’ve made sure we’ve got all the required software installed and know roughly what Sugarkube will do, let’s kick it off.

There are actually 2 ways to create a cluster with Sugarkube depending on how a stack is configured. There’s a sugarkube cluster create command that will only create a cluster and won’t install any kapps. Sometimes it’s useful to just spin up a vanilla cluster, but not too often. Ideally manifests should contain actions that call back to Sugarkube telling it when to create a cluster. This lets them prepare a cloud account first, e.g. to create hosted zones or S3 buckets for Terraform, etc. But since this cluster is local let’s see whether it includes any actions. Let’s use sugarkube kapps install and see whether it works:

sugarkube kapps install stacks/web.yaml local-web workspaces/local-web/

You should see this: kapps install

Sugarkube bombs out. That’s because actions can be dangerous. If you have an existing cluster and install some kapps that define the cluster_update action it may reconfigure the cluster if someone’s also changed the cluster config. That might not be what you want, especially if you’re installing a kapp into a prod cluster. So for that reason Sugarkube shows a warning to make you explicitly choose whether to run or skip actions.

Let’s tell it we want to run actions, because we do:

sugarkube kapps install stacks/web.yaml local-web workspaces/local-web/ --run-actions

You should see this: kapps install

Sugarkube’s installation model

Sugarkube installs kapps in a two-pass model. First it plans the installation during which time no destructive changes are made. To actually make it install kapps – and in this case create a cluster – we need to pass the --yes flag.

Planning or applying are two of Sugarkube’s run phases. And in fact because Sugarkube just executes whatever run steps have been defined for a kapp, it’s the responsibility of a kapp’s author to make sure it doesn’t perform any destructive changes while planning. Common activities that kapps perform during the plan run phase are to invoke terraform plan if they use Terraform and/or helm lint if they use Helm.

Anyway, we could just pass the --yes flag and it’d work in this case. But as we’ll see in a later tutorial, things get a bit more complicated when deploying to the cloud. That’s because some kapps will create cloud infrastructure required by other kapps – for example a bucket to store Terraform state files in. Without that infrastructure existing there’s just no way later kapps can plan their changes at all. The two-pass model doesn’t work in this case.

The solution is to use Sugarkube’s --one-shot flag. Ordinarily Sugarkube would plan all kapps, then apply all kapps once the --yes flag is given. The --one-shot flag tightens that process, so Sugarkube will plan and then immediately apply each kapp before moving on to the next. That way a kapp that e.g. creates a Terraform state bucket will actually create the bucket before Sugarkube moves on to plan and apply subsequent kapps that use it.

So, let’s finally create the local-web cluster and install all our kapps with this single command:

sugarkube kapps install stacks/web.yaml local-web workspaces/local-web/ --run-actions --one-shot

Here’s a truncated recording of bringing up the cluster with the above command. one-shot install

Minikube can take a while to come online and so can Wordpress. If you haven’t got the patience you can use a selector to exclude Wordpress which should be enough to give you the gist:

sugarkube kapps install stacks/web.yaml local-web workspaces/local-web/ --run-actions --one-shot -x 'wordpress:*'

Accessing services

So, after all that let’s test it out. Running kubectl -n wordpress-site1 get ingress tells us there’s a host called site1.localhost. If you open it in a browser you probably won’t see much. You’ll need to add Minikube’s IP to your /etc/hosts file:

  1. Get the IP and nodeport that nginx-ingress is running on with e.g. minikube service -n nginx1 nginx1-nginx-ingress-controller --url
  2. The output of the above command should be something like http://192.168.99.117:32133.
  3. Edit /etc/hosts and add an entry like 192.168.99.117 site1.localhost
  4. Now use the port number from above to access the site, e.g.: https://site.localhost:32133. You’ll have to accept the self-signed cert, but now you’ll be accessing Wordpress through nginx, just as you would when hosted in the Cloud.

Annoyingly you’ll have to do something similar each time you create a Minikube cluster because of an open issue with Minikube.

Anyway, when Wordpress loads you should notice that it says Site 1 - Created with Sugarkube at the top. The Wordpress kapp has even installed fixture data when running against a local Minikube cluster!

Tear it down

Launching an ephemeral cluster is only half the story. We also need to be able to tear them down. In this case because no cloud resources were created we could just nuke the Minikube cluster with minikube delete. But let’s take this opportunity to show off a few extra commands.

First, let’s just delete the Wordpress kapp but leave everything else intact. Since it’s a deletion let’s run a dry-run first so we get some idea of what Sugarkube would do:

sugarkube kapps delete stacks/web.yaml local-web workspaces/local-web/ -i 'wordpress:*' -n

Now let’s plan the deletion for real:

sugarkube kapps delete stacks/web.yaml local-web workspaces/local-web/ -i 'wordpress:*' 

Before actually applying it:

sugarkube kapps delete stacks/web.yaml local-web workspaces/local-web/ -i 'wordpress:*' --yes 

Here’s a recording of what all that looks like: Delete wordpress

Notice how the wordpress-site1 namespace is deleted as well. Kapps should remove all traces of themselves to be really useful.

OK, now let’s tear down the cluster with Sugarkube. This time let’s plan the deletion in verbose mode:

sugarkube kapps delete stacks/web.yaml local-web workspaces/local-web/ --run-actions -v

Which gives: Cluster delete plan

It shows you the paths of templates it’s rendering and will also show commands being executed. Let’s apply the deletion. You can use -y instead of typing --yes since you may be getting a bit tired.

sugarkube kapps delete stacks/web.yaml local-web workspaces/local-web/ --run-actions -vy

We’ll see this: Cluster delete error

Whoa. What’s that? Why’s it failing? Oh yeah, we already deleted Wordpress so of course we can’t run helm delete on it. This situation may crop up for several reasons. Perhaps we only installed a subset of the cluster using selectors. Or perhaps we’re developing some kapps and they’re failing. Maybe a developer uninstalled some stuff and went home and we just want a script to nuke the cluster as best it can. We could fiddle around excluding individual kapps by passing -x "wordpress:*" to the above command. But a simpler solution is just to tell Sugarkube to --ignore-errors:

sugarkube kapps delete stacks/web.yaml local-web workspaces/local-web/ --run-actions -vy --ignore-errors

Running this will tear the cluster down and it was a cloud cluster that’d include deleting all cloud infrastructure created by kapps too: Cluster delete success

Notice how it deletes kapps in the reverse order to how it installed them? It traverses the DAG in reverse. This is how Sugarkube is able to clean up after itself and is what truly opens the door to ephermal clusters.

Summary

This has been an in-depth introduction to Sugarkube. But if you look at what we’ve done, we could have launched a Minikube cluster running Wordpress with fixture data with only 3 commands (cloning the project repo, creating a workspace and installing kapps), and torn it down with one! Not bad at all.

From here check out our other tutorials on creating cloud-based clusters or learn more about Sugarkube’s concepts.