Files
container.training/slides/k8s/yamldeploy.md
2023-05-14 19:58:45 +02:00

556 lines
11 KiB
Markdown

# 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