From the course: Automating Kubernetes with GitOps

Running applications the declarative way - Kubernetes Tutorial

From the course: Automating Kubernetes with GitOps

Running applications the declarative way

(no audio) - Alright, now that we know about these different resources, let's talk about running applications the declarative way in Kubernetes. In order to manage applications in a GitOps environment, there's a few requirements. First is that application update should happen through the same code as application creation. And the code must be idempotent. Idempotency of the code, that means that if you run it against Kubernetes, it doesn't matter if the application already exists or not. If it doesn't exist, it should create it. If it does exist, it should update it if that is required. To meet these requirements in a Kubernetes environment, changes should be applied the declarative way. That means that in GitOps-based Kubernetes, kubectl apply is the only thing that we should see. Because kubectl apply is doing exactly what we need, it's applying idempotent code. You may have heard of kubectl create. Kubectl create isn't updating your application. If the source code has changed, kubectl create is going to give you an error message if you run it and the application already exists. So this is maybe the most important basic thing to know about Kubernetes in a GitOps environment. There is only kubectl apply. Now an easy way to run an application in Kubernetes normally would be kubectl create deploy myapp --image=myimage --replicas=3. That would create an application with the name myapp that is using the myimage image. But what is wrong with this? Well it doesn't work in a GitOps environment because it uses kubectl create. In GitOps, the application is defined in the YAML manifest file, and Kubernetes should use an operator to pick up, and apply changes in the YAML file automatically. That means that you need to create a YAML file, and how do we do that? Well, you might have a good time with VI, and try to do it manually, but it's much smarter to generate the YAML manifest file. Use it by adding --dry-run=client-o yaml, and redirect the output to the YAML file that you want to create. And next, you push the YAML file to the Git repository, and have the GitOps operator use kubectl apply -f myapp.yaml to apply the code to the cluster. That's how you should be working. Now a little bit more about kubectl apply and kubectl create. So after defining YAML files, kubectl create -f could be used to create the application. But the disadvantage is that this command only works if the application doesn't yet exist. That's why kubectl apply -f myapp.yaml is what you should do in a GitOps environment. So what exactly is it doing? Well, if the application doesn't yet exist, it will be updated. If the application already exists, modifications will be applied. And to preview modifications that will be applied, use kubectl diff -f myapp.yaml. Kubectl diff is also a pretty neat command. It will show you the current code as compared to other code you're running, and it'll show you the differences. Each time the application is updated, kubectl apply stores a configuration in the last applied configuration annotation. And this annotation is used by kubectl diff so that it can easily detect any change in the manifest file. Managing applications the declarative way is the foundation for GitOps in a Kubernetes environment. And apart from that, application updates can be applied dynamically in some cases. The HorizontalPodAutoscaler is one solution to apply updates dynamically. So when resource usage requires it, it automatically adjusts the number of application instances. But you should know that HorizontalPodAutoscaler is taking over your work in a GitOps environment. What is going to happen if your manifest files in GitOps define that you should have two replicas and suddenly the HorizontalPodAutoscaler has scaled up to six replicas. What is going to happen the next time that your Kubernetes GitOps operator is checking the desired state in your YAML file against the current state in Kubernetes? It might just turn down the number of replicas so be careful if you're using dynamic solutions like the HorizontalPodAutoscaler. Before continuing, let me show you how to run these applications in the declarative way. All right, let me use kubectl create deploy myserver - -image=nginx. For your information, this is not declarative at all. But this is not declarative because I want to show you what's happening if you run it again. It's complaining already exists. So I'm using kubectl delete deploy myserver to delete it. And before continuing, (keyboard typing) let me also enable kubectl completion. So how do we want to run these applications? Well, kubectl create --image=nginx, same command, but followed by --dry-run=client and -o yaml, and writing that to myserver.yaml. And then I can use kubectl apply - f on myserver.yaml. Now when I use kubectl get deploy myserver - o yaml, we can see the yaml code that is behind it. And the part that matters is the annotation that has been added. Look, this is what we are looking at. This is what kubectl apply is doing. Kubectl apply is saving in JSON format, the last applied configuration. And based on this last applied configuration, the YAML file knows what has last been applied to Kubernetes, and that makes it easy to work with these differences. So as a result, you can run kubectl apply again, and that is telling us that this time it's configured. So kubectl apply is applying the updates, and that's what makes it so important. I have another demo for you. So this is why (indistinct) is so incredibly important. I just want to run another application to have a look at the different components in a slightly different way. (keyboard typing) So let me use kubectl create deploy webserver --images=nginx - -replicas=3 with the --dry-run=client and -o yaml option. And I'm writing that to webserver.yaml. So here is webserver.yaml. This is just a straightforward YAML code. And we can easily apply that using kubectl apply -f web server.yaml. So kubectl get all is showing me this new webserver.yaml and some old stuff that has been running (indistinct). As you can see, the awx environment is still running, we should probably delete it because it's just consuming resources. But I'll do that later. So... If I would use kubectl describe on the pod webserver, there we can see all the different properties of this pod. Now I'm going to edit the webserver.yaml. And in this edit, I am going to add metadata.annotation. So we don't have any annotations. Well, let's add an annotation. (keyboard typing) And the annotation that I want is environment: qa. So I hope you see what I'm doing here. I'm adding an annotation and it is annotation I'm referring to the GitOps environment. So if you want it to be in the qa environment, just put an annotation in it. And later we can do something with it. Now, before applying it, let's do a kubectl diff - f webserver.yaml. And what do we see? Well, we see in the difficult to read style that some changes have occurred. So we have environment: qa, and we have a generation in here. We have the generation is the second version, and we have the last applied configuration. That's a configuration that's being checked. As you can see, the output of the command is really a little bit hard to read but that's why diff probably stands for difficult, or would it be differences anyway. Anyways, kubectl diff allows you to see on beforehand what is going to happen. And this is the mechanism behind what is going to be happening while you use kubectl apply. It's supplying all of these differences. And that is how you use Kubernetes in the declarative way.

Contents