# Deploying with YAML - So far, we created resources with the following commands: - `kubectl run` - `kubectl create deployment` - `kubectl expose` - We can also create resources directly with YAML manifests --- ## Why use YAML? (1/3) - Some resources cannot be created easily with `kubectl` (e.g. DaemonSets, StatefulSets, webhook configurations...) - Some features and fields aren't directly available (e.g. resource limits, healthchecks, volumes...) --- ## Why use YAML? (2/3) - Create a complicated resource with a single, simple command: `kubectl create -f stuff.yaml` - Create *multiple* resources with a single, simple command: `kubectl create -f more-stuff.yaml` or `kubectl create -f directory-with-yaml/` - Create resources from a remote manifest: `kubectl create -f https://.../.../stuff.yaml` - Create and update resources: `kubectl apply -f stuff.yaml` --- ## Why use YAML? (3/3) - YAML lets us work *declaratively* - Describe what we want to deploy/run on Kubernetes ("desired state") - Use tools like `kubectl`, Helm, kapp, Flux, ArgoCD... to make it happen ("reconcile" actual state with desired state) - Very similar to e.g. Terraform --- class: extra-details ## Overrides and `kubectl set` Just so you know... - `kubectl create deployment ... --overrides '{...}'` *specify a patch that will be applied on top of the YAML generated by `kubectl`* - `kubectl set ...` *lets us change e.g. images, service accounts, resources, and much more* --- ## Various ways to write YAML - From examples in the docs, tutorials, blog posts, LLMs... (easiest option when getting started) - Dump an existing resource with `kubectl get -o yaml ...` (includes many extra fields; it is recommended to clean up the result) - Ask `kubectl` to generate the YAML (with `kubectl --dry-run=client -o yaml create/run ...`) - Completely from scratch with our favorite editor (black belt level😅) --- ## Writing a Pod manifest - Let's use `kubectl --dry-run=client -o yaml` .lab[ - Generate the Pod manifest: ```bash kubectl run --dry-run=client -o yaml purple --image=jpetazzo/color ``` - Save it to a file: ```bash kubectl run --dry-run=client -o yaml purple --image=jpetazzo/color \ > pod-purple.yaml ``` ] --- ## Running the Pod - Let's create the Pod with the manifest we just generated .lab[ - Create all the resources (at this point, just our Pod) described in the manifest: ```bash kubectl create -f pod-purple.yaml ``` - Confirm that the Pod is running ```bash kubectl get pods ``` ] --- class: extra-details ## Comparing with direct `kubectl run` - The Pod should be identical to one created directly with `kubectl run` .lab[ - Create a Pod directly with `kubectl run`: ```bash kubectl run yellow --image=jpetazzo/color ``` - Compare both Pod manifests and status: ```bash kubectl get pod purple -o yaml kubectl get pod yellow -o yaml ``` ] --- ## Generating a Deployment manifest - After a Pod, let's create a Deployment! .lab[ - Generate the YAML for a Deployment: ```bash kubectl create deployment purple --image=jpetazzo/color -o yaml --dry-run=client ``` - Save it to a file: ```bash kubectl create deployment purple --image=jpetazzo/color -o yaml --dry-run=client \ > deployment-purple.yaml ``` - And create the Deployment: ```bash kubectl create -f deployment-purple.yaml ``` ] --- ## Updating our Deployment - What if we want to scale that Deployment? - Option 1: `kubectl scale` - Option 2: update the YAML manifest - Let's go with option 2! .lab[ - Edit the YAML manifest: ```bash vim deployment-purple.yaml ``` - Find the line with `replicas: 1` and update the number of replicas ] --- ## Applying our changes - Problem: `kubectl create` won't update ("overwrite") resources .lab[ - Try it out: ```bash kubectl create -f deployment-purple.yaml # This gives an error ("AlreadyExists") ``` ] - So, what can we do? --- ## Updating resources - Option 1: delete the Deployment and re-create it (effective, but causes downtime!) - Option 2: `kubectl scale` or `kubectl edit` the Deployment (effective, but that's cheating - we want to use YAML!) - Option 3: `kubectl apply` --- ## `kubectl apply` vs `create` - `kubectl create -f whatever.yaml` - creates resources if they don't exist - if resources already exist, don't alter them
(and display error message) - `kubectl apply -f whatever.yaml` - creates resources if they don't exist - if resources already exist, update them
(to match the definition provided by the YAML file) - stores the manifest as an *annotation* in the resource --- ## Trying `kubectl apply` .lab[ - First, delete the Deployment: ```bash kubectl delete deployment purple ``` - Re-create it using `kubectl apply`: ```bash kubectl apply -f deployment-purple.yaml ``` - Edit the YAML manifest, change the number of replicas again: ```bash vim deployment-purple.yaml ``` - Apply the new manifest: ```bash kubectl apply -f deployment-purple.yaml ``` ] --- ## `create` → `apply` - What are the differences between `kubectl create -f` an `kubectl apply -f`? - `kubectl apply` adds an annotation
(`kubectl.kubernetes.io/last-applied-configuration`) - `kubectl apply` makes an extra `GET` request
(to get the existing object, or at least check if there is one) - Otherwise, the end result is the same! - It's almost always better to use `kubectl apply` (except when we don't want the extra annotation, e.g. for huge objects like some CRDs) - From now on, we'll almost always use `kubectl apply -f` instead of `kubectl create -f` --- ## Adding a Service - Let's generate the YAML for a Service exposing our Deployment .lab[ - Run `kubectl expose`, once again with `-o yaml --dry-run=client`: ```bash kubectl expose deployment purple --port 80 -o yaml --dry-run=client ``` - Save it to a file: ```bash kubectl expose deployment purple --port 80 -o yaml --dry-run=client \ > service-purple.yaml ``` ] - Note: if the Deployment doesn't exist, `kubectl expose` won't work! --- ## What if the Deployment doesn't exist? - We can also use `kubectl create service` - The syntax is slightly different (`--port` becomes `--tcp` for some reason) .lab[ - Generate the YAML with `kubectl create service`: ```bash kubectl create service clusterip purple --tcp 80 -o yaml --dry-run=client ``` ] --- ## Combining manifests - We can put multiple resources in a single YAML file - We need to separate them with the standard YAML document separator (i.e. `---` standing by itself on a single line) .lab[ - Generate a combined YAML file: ```bash for YAMLFILE in deployment-purple.yaml service-purple.yaml; do echo --- cat $YAMLFILE done > app-purple.yaml ``` ] --- class: extra-details ## Resource ordering - *In general,* the order of the resources doesn't matter: - in many cases, resources don't reference each other explicitly
(e.g. a Service can exist even if the corresponding Deployment doesn't) - in some cases, there might be a transient error, but Kubernetes will retry
(and eventually succeed) - One exception: Namespaces should be created *before* resources in them! --- ## Using `-f` with other commands - We can also use `kubectl delete -f`, `kubectl label -f`, and more! .lab[ - Apply the resulting YAML file: ```bash kubectl apply -f app-purple.yaml ``` - Add a label to both the Deployment and the Service: ```bash kubectl label -f app-purple.yaml release=production ``` - Delete them: ```bash kubectl delete -f app-purple.yaml ``` ] --- class: extra-details ## Pruning¹ resources - We can also tell `kubectl` to remove old resources - This is done with `kubectl apply -f ... --prune` - It will remove resources that don't exist in the YAML file(s) - But only if they were created with `kubectl apply` in the first place (technically, if they have an annotation `kubectl.kubernetes.io/last-applied-configuration`) .footnote[¹If English is not your first language: *to prune* means to remove dead or overgrown branches in a tree, to help it to grow.] --- ## Advantage of YAML - Using YAML (instead of `kubectl create `) allows to be *declarative* - The YAML describes the desired state of our cluster and applications - YAML can be stored, versioned, archived (e.g. in git repositories) - To change resources, change the YAML files (instead of using `kubectl edit`/`scale`/`label`/etc.) - Changes can be reviewed before being applied (with code reviews, pull requests ...) - Our version control system now has a full history of what we deploy --- ## GitOps - This workflow is sometimes called "GitOps" - There are tools to facilitate it, e.g. Flux, ArgoCD... - Compares to "Infrastructure-as-Code", but for app deployments --- class: extra-details ## Actually GitOps? There is some debate around the "true" definition of GitOps: *My applications are defined with manifests, templates, configurations... that are stored in source repositories with version control, and I only make changes to my applications by changing these files, like I would change source code.* vs *Same, but it's only "GitOps" if the deployment of the manifests is full automated (as opposed to manually running commands like `kubectl apply` or more complex scripts or tools).* Your instructor may or may not have an opinion on the matter! 😁 --- ## YAML in practice - Get started with `kubectl create deployment` and `kubectl expose` (until you have something that works) - Then, run these commands again, but with `-o yaml --dry-run=client` (to generate and save YAML manifests) - Try to apply these manifests in a clean environment (e.g. a new Namespace) - Check that everything works; tweak and iterate if needed - Commit the YAML to a repo 💯🏆️ --- ## "Day 2" YAML - Don't hesitate to remove unused fields (e.g. `creationTimestamp: null`, most `{}` values...) - Check your YAML with: [kube-score](https://github.com/zegl/kube-score) (installable with krew) [kube-linter](https://github.com/stackrox/kube-linter) - Check live resources with tools like [popeye](https://popeyecli.io/) - Remember that like all linters, they need to be configured for your needs! --- class: extra-details ## Specifying the namespace - When creating resources from YAML manifests, the namespace is optional - If we specify a namespace: - resources are created in the specified namespace - this is typical for things deployed only once per cluster - example: system components, cluster add-ons ... - If we don't specify a namespace: - resources are created in the current namespace - this is typical for things that may be deployed multiple times - example: applications (production, staging, feature branches ...) ??? :EN:- Deploying with YAML manifests :FR:- Déployer avec des *manifests* YAML :EN:- Techniques to write YAML manifests :FR:- Comment écrire des *manifests* YAML