This repository contains the hands-on Gitops tutorial given at Kubecon 2018 in Seattle. You can find the slides by clicking the image below. Follow the tutorial by reading through this README.
If you see errors, mistakes or typos in this tutorial, please feel free to submit a pull request.
In order to go through this tutorial, you'll need two things.
In order to complete this tutorial, you will need a test kubernetes cluster. You have several options to quickly get a development cluster to test out Gitops.
You can use Katacoda's Kubernetes playground. This will allow you to access a small Kubernetes cluster on demand. The kubectl
command line is set up by default and should be used from the master node.
There are also plenty of other options, such as using minikube, Using Google Kubernetes Engine or EKS.
But you may have another way of getting a kubernetes cluster up and running. If that's the case, great! This tutorial should work with any valid kubernetes installation.
Before you begin, you should make sure that the kubectl
command works and you can query basic information from kubernetes. for example, run
➤ kubectl get pods --all-namespaces
To get a listing of all the running pod on your cluster, or
➤ kubectl cluster-info
To get a quick status check on your cluster. If these commands complete succesfully, you're ready to go onto the next step.
You'll want to create a new repository for this tutorial and make it available from your cluster through a git URL. If you're using Bitbucket, make sure you have SSH keys enabled as we'll need these to configure the deploy operator access.
Of course, you can also configure the Flux operator to use a private git host, but configuring this is outside the scope of this introductory tutorial.
In order to control the operation of your cluster using gitops, you'll need to have a control repository in which the state of your cluster can be defined. github.com/bricef/gitops-tutorial
is already set up with all the files you'll need to follow along. Fork it to your Bitbucket or github account. When you have your own remote repository, clone it to your local workspace:
➤ git clone <url of your forked repository>
Now that you have a repository set up, it's time to install the deployment operators on your cluster. For this we'll use Weavework's Flux.
The needed deployment manifests are kept in this repository's flux
directory.
If you have kubectl
configured correctly, you'll be able to install the Flux operator by applying the manifests to your cluster:
➤ cd gitops-tutorial
➤ kubectl apply -f ./flux/
You should see that this will create several objects in your cluster
serviceaccount/flux created
clusterrole.rbac.authorization.k8s.io/flux created
clusterrolebinding.rbac.authorization.k8s.io/flux created
deployment.apps/flux created
secret/flux-git-deploy created
deployment.apps/memcached created
service/memcached created
Alongside flux, we have also installed a memcache service for flux to use, and appropriate permissions for the flux operator to act on the cluster itself through a role and rolebinding.
You should now be able to see the flux pod running in the cluster:
➤ kubectl get pods
NAME READY STATUS RESTARTS AGE
flux-9d69f6fc4-5t5w6 1/1 Running 0 18m
memcached-dbb59cb58-phk9s 1/1 Running 0 18m
Now that the operator is running in your cluster, we'll need to configure it to have access to your repository, so that it can read changes you make, as well as create commits when a service is automatically deployed.
To do this, we'll need to do two things. Firstly, we'll need to allow the flux operator to act on the repository; secondly, we'll need the flux operator to be configured to look for cluster configuration in a particular repository.
To allow flux to operate on the repository, we'll need to copy its public SSH key to Github or Bitbucket.
We can get the operator's public key using the fluxctl tool.
First, we'll need to install the fluxctl
tool. There are different ways of installing it depending on your platoform. For this tutorial, we can grab the binary release directly.
➤ wget https://github.com/weaveworks/flux/releases/download/1.8.1/fluxctl_linux_amd64
➤ chmod +x fluxctl_linux_amd64
Once installed and executable, we can use the fluxctl command line tool to get the Flux operator's public key:
➤ ./fluxctl_linux_amd64 identity
The command should provide you with a public key:
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCl05 [...]
You now need to copy this key and add it to github or bitbucket as a R/W key for your remote repository.
See the documentation on how to do this for Github and Bitbucket. Bitbucket will require the keys to be added to your profile instead of your repository.
Now that your repository will accept read and write requests from the flux operator, we should configure Flux to use your remote repository. To do this, we'll reconfigure the Flux deployment.
Edit the flux/flux-deployment.yaml
file to reconfigure the Flux deployment with the url of your repository by changing the arguments. Make sure to add the --git-path=deploy/kubernetes
argument as well to point the Flux operator to the right folder.
diff --git a/flux/flux-deployment.yaml b/flux/flux-deployment.yaml
index 15d0f31..c57d3b0 100644
--- a/flux/flux-deployment.yaml
+++ b/flux/flux-deployment.yaml
@@ -100,8 +100,9 @@ spec:
- --ssh-keygen-dir=/var/fluxd/keygen
# replace or remove the following URL
- - --git-url=git@github.com:weaveworks/flux-get-started
+ - --git-url=<GIT URL OF YOUR REPOSITORY>
+ - --git-path=deploy/kubernetes
- --git-branch=master
# include these next two to connect to an "upstream" service
# (e.g., Weave Cloud). The token is particular to the service
Once you've edited and saved the file, reapply the configuration to your cluster.
➤ kubectl apply -f flux/flux-deployment.yaml
Now that the operator is set up to react to changes in your repository's deploy/kubernetes
directory, we need to provide it with some configuration for it to synchronise.
We have a template deployment in the examples
directory. Copy this example to the deploy/kubernetes
directory that Flux is watching, then commit and push your changes to your remote repository.
➤ cp examples/podinfo-dep.yaml deploy/kubernetes
➤ git add deploy
➤ git commit -m "Add new deployment to cluster"
➤ git push origin master
If everything has been configured correctly so far, you should be able to watch for changes in your cluster and see the podinfo
deployment running after a little while.
➤ watch kubectl get pods
NAME READY STATUS RESTARTS AGE
flux-6f9798f7f-6hcc9 1/1 Running 0 12m
memcached-dbb59cb58-gn8pf 1/1 Running 0 16m
Then after a few minutes
NAME READY STATUS RESTARTS AGE
flux-6f9798f7f-6hcc9 1/1 Running 0 12m
memcached-dbb59cb58-gn8pf 1/1 Running 0 16m
podinfo-6466ffddb-l9nq2 1/1 Running 0 3m
Now that we know that the gitops operator is running in your cluster, we should expect it to return the system to a know good state if we make an operator error. Let's try this by manually deleting the podinfo
deployment.
➤ kubectl delete deploy/podinfo
This will delete the deployment from the cluster. We can see this by looking at the deployments, where the podinfo
deployment should be missing.
➤ kubectl get deployments
We haven't changed the state of the repository, however, so the Flux operator still expects the deployment to be running. Since it's missing from our cluster, Flux will attempt to recreate it. We can see this if we monitor the deployments:
➤ watch kubectl get deployments
After a few minutes (be patient, it will get there), a new deployment will be created to replace the missing podinfo
one.
This is an example of how the Flux operator is acting as a control loop for your cluster, always bringing it back to the desired state if possible.
Now we know that the flux operator is working, let's see it act on changes to our repository.
We'll scale up our podinfo deployment to multiple replicas in our configuration repository, and we will see how Flux automatically applies this new configuration.
First, let's take a look at the podinfo deployment
➤ kubectl get deploy/podinfo
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
podinfo 1 1 1 1 11m
As we can see, the deployment currently only has one pod. Let's change this in the deployment yaml (deploy/kubernetes/podinfo-dep.yaml
).
diff --git a/deploy/kubernetes/podinfo-dep.yaml b/deploy/kubernetes/podinfo-dep.yaml
index e79ce86..8cd6c13 100644
--- a/deploy/kubernetes/podinfo-dep.yaml
+++ b/deploy/kubernetes/podinfo-dep.yaml
@@ -4,7 +4,7 @@ kind: Deployment
metadata:
name: podinfo
spec:
- replicas: 1
+ replicas: 4
template:
metadata:
labels:
Now save and commit the changes.
We can watch Flux automatically update the deployment. Be patient as this can take a few minutes.
➤ watch kubectl get deploy/podinfo
We've now seen Flux react to changes in the repository. Any new kubernetes configuration yaml placed in the deploy/kubernetes
folder and any future commited changes will now be automatically be applied to our cluster. Goodbye kubectl apply
!
Making manual changes to the deployment yaml is tedious, especially if we always want to deploy the latest version of our application, ie, do continuous deployment.
Instead, Flux can monitor the image repository and whenever a new image matches a specified filter, deploy it to our cluster automatically.
Let's take a look at the current running version of podinfo in our cluster:
➤ kubectl get deploy/podinfo -o jsonpath='{..spec.template.spec.containers[0].image}{ "\n"}'
Now let's tell Flux to automatically update to the latest version using an annotation in the deployment:
diff --git a/deploy/kubernetes/podinfo-dep.yaml b/deploy/kubernetes/podinfo-dep.yaml
index 8cd6c13..334dac9 100644
--- a/deploy/kubernetes/podinfo-dep.yaml
+++ b/deploy/kubernetes/podinfo-dep.yaml
@@ -3,6 +3,9 @@ apiVersion: apps/v1beta1 # for versions before 1.6.0 use extensions/v1beta1
kind: Deployment
metadata:
name: podinfo
+ annotations:
+ flux.weave.works/tag.podinfo: glob:*
+ flux.weave.works/automated: 'true'
spec:
replicas: 4
template:
These will tell flux to automatically deploy the latest available image. Once we have saved, committed and pushed these changes to our remote repository, flux will deploy the latest version automatically.
We can watch this as usual:
➤ watch kubectl get deploy/podinfo -o jsonpath='{..spec.template.spec.containers[0].image}{ "\n"}'
Flux will actually create a new commit in your control repository, with the new version of the podinfo. Once we have the new version deployed we can look at the effect on the repository by pulling the changes Flux made:
➤ git pull
➤ git log -p
Which will show the commit made by Flux to deploy the latest version.
diff --git a/deploy/kubernetes/podinfo-dep.yaml b/deploy/kubernetes/podinfo-dep.yaml
index 334dac9..b5b4e6d 100644
--- a/deploy/kubernetes/podinfo-dep.yaml
+++ b/deploy/kubernetes/podinfo-dep.yaml
@@ -15,6 +15,6 @@ spec:
spec:
containers:
- name: podinfo
- image: quay.io/stefanprodan/podinfo:1.0.1
+ image: quay.io/stefanprodan/podinfo:1.4.1
ports:
- containerPort: 3000
Now that our deployment is automated, every new version of podinfo will be commited to our control repository, and then automatically deployed to our cluster.
We can specify the filter used by Flux to choose images to deploy automatically by changing the flux.weave.works/tag.dotnet-load-tester
annotation value. For example, glob:master-*
will only deploy images tagged with the prefix master-
.
Flux is also capable of filtering on semantic versions. for exmaple, setting flux.weave.works/tag.dotnet-load-tester
to semver:>0.1.1
will only deploy tagged versions with semantic version greater than 0.1.1.
You can also use regular expressions to filter out the images for even more power. Use the format regexp:...
with a custom regexp instead of ...
.
Polling delays
You might find that the delays between changes to the repository and the changes to the cluster are too long. By default, Flux is configured to poll the Git repository every 5 minutes, so this is quite slow.
It's probably not a good idea to poll more often, as it's surprisingly easy to go over cloud provider API limits. However, we can speed up the process another way.
Weave Cloud comes with the option of adding webhooks to notify flux of changes in the git repository or the image registry, so that as soon as changes are made, the Flux daemon will update the cluster.
Helm
We didn't include a Helm demo as that's a little more involved. However, you can use the same approac with Helm, but you'll have to install a custom operator on the cluster that's able to communicate between Flux an Helm's Tiller. You can find out more in the gitops-helm project.
Gitops Flux
Noticeably, we don't manage Flux using Flux itself. This could easily get us in a bind when updating and we'd loose the ability to control our cluster. We'll always need a service to stand outside of the Gitops to provide the ability to manage the cluster, and it won't be able to manage itself safely. That's ok.
The commercial Weave Cloud agents will automatically update, by using a separate update path managed through a deployed service.
Automatically deleting services
Flux won't automatially delete services that are running on the cluster but don't exist in the control repository. While that might seem like a good idea at first, this would not allow Flux to be deployed to to existing clusters, as it would immediately delete all running deployments as soon as installed! Instead, you'll have to manually delete resources after removing them from the control repository.
Custom Resource Definitions
Flux will play well with CRDs. If you have a CRD definition or a custom resource yaml in the control repository, then they will be applied to the cluster like any standard resources.
You can find much more about Gitops online.