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

11 KiB
Raw Blame History

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:

    kubectl run --dry-run=client -o yaml purple --image=jpetazzo/color
    
  • Save it to a file:

    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:

    kubectl create -f pod-purple.yaml
    
  • Confirm that the Pod is running

    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:

    kubectl run yellow --image=jpetazzo/color
    
  • Compare both Pod manifests and status:

    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:

    kubectl create deployment purple --image=jpetazzo/color -o yaml --dry-run=client
    
  • Save it to a file:

    kubectl create deployment purple --image=jpetazzo/color -o yaml --dry-run=client \
    > deployment-purple.yaml
    
  • And create the Deployment:

    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:

    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:
    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:

    kubectl delete deployment purple
    
  • Re-create it using kubectl apply:

    kubectl apply -f deployment-purple.yaml
    
  • Edit the YAML manifest, change the number of replicas again:

    vim deployment-purple.yaml
    
  • Apply the new manifest:

    kubectl apply -f deployment-purple.yaml
    

]


createapply

  • 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:

    kubectl expose deployment purple --port 80 -o yaml --dry-run=client
    
  • Save it to a file:

    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:
    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:
      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:

    kubectl apply -f app-purple.yaml
    
  • Add a label to both the Deployment and the Service:

    kubectl label -f app-purple.yaml release=production
    
  • Delete them:

    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 (installable with krew)

    kube-linter

  • Check live resources with tools like popeye

  • 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