# 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
    <br/>(and display error message)

- `kubectl apply -f whatever.yaml`

  - creates resources if they don't exist

  - if resources already exist, update them
    <br/>(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
    <br/>
    (`kubectl.kubernetes.io/last-applied-configuration`)

  - `kubectl apply` makes an extra `GET` request
    <br/>
    (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
    <br/>
    (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
    <br/>
    (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 <kind>`) 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
