From fef3fa31fbdc1d07f62baa018ceba777841ac40f Mon Sep 17 00:00:00 2001 From: Jerome Petazzoni Date: Sat, 28 Mar 2020 13:53:45 -0500 Subject: [PATCH] Update for Kubernetes 1.18 `kubectl run` In Kubernetes 1.18, `kubectl run` no longer creates a Deployment, and cannot create Jobs or CronJobs anymore. It only creates Pods. Since we were using `kubectl run` to create our first Deployment, I've changed the materials to explain that change, and explain how the behavior differs between 1.17- and 1.18+, since I expect that people will deal with a mix of both scenarios for a while (at least a year). --- slides/k8s/kubectlrun.md | 672 ++++++++++++++++++++++++++++----------- 1 file changed, 486 insertions(+), 186 deletions(-) diff --git a/slides/k8s/kubectlrun.md b/slides/k8s/kubectlrun.md index 0cb5bf33..f2877ee9 100644 --- a/slides/k8s/kubectlrun.md +++ b/slides/k8s/kubectlrun.md @@ -10,14 +10,47 @@ - In that container in the pod, we are going to run a simple `ping` command -- Then we are going to start additional copies of the pod +-- + +- Sounds simple enough, right? + +-- + +- Except ... that the `kubectl run` command changed in Kubernetes 1.18! + +- We'll explain what has changed, and why + +--- + +## Choose your own adventure + +- First, let's check which version of Kubernetes we're running + +.exercise[ + +- Check our API server version: + ```bash + kubectl version + ``` + +- Look at the **Server Version** in the second part of the output + +] + +- In the following slides, we will talk about 1.17- or 1.18+ + + (to indicate "up to Kubernetes 1.17" and "from Kubernetes 1.18") --- ## Starting a simple pod with `kubectl run` +- `kubectl run` is convenient to start a single pod + - We need to specify at least a *name* and the image we want to use +- Optionally, we can specify the command to run in the pod + .exercise[ - Let's ping the address of `localhost`, the loopback interface: @@ -25,90 +58,289 @@ kubectl run pingpong --image alpine ping 127.0.0.1 ``` - + ] --- +--- -(Starting with Kubernetes 1.12, we get a message telling us that -`kubectl run` is deprecated. Let's ignore it for now.) +## What do we see? + +- In Kubernetes 1.18+, the output tells us that a Pod is created: + ``` + pod/pingpong created + ``` + +- In Kubernetes 1.17-, the output is much more verbose: + ``` + kubectl run --generator=deployment/apps.v1 is DEPRECATED + and will be removed in a future version. Use kubectl run + --generator=run-pod/v1 or kubectl create instead. + deployment.apps/pingpong created + ``` + +- There is a deprecation warning ... + +- ... And a Deployment was created instead of a Pod + +🤔 What does that mean? --- -## Behind the scenes of `kubectl run` +## Show me all you got! -- Let's look at the resources that were created by `kubectl run` +- What resources were created by `kubectl run`? .exercise[ -- List most resource types: +- Let's ask Kubernetes to show us *all* the resources: ```bash kubectl get all ``` ] +Note: `kubectl get all` is a lie. It doesn't show everything. + +(But it shows a lot of "usual suspects", i.e. commonly used resources.) + +--- + +## The situation with Kubernetes 1.18+ + +``` +NAME READY STATUS RESTARTS AGE +pod/pingpong 1/1 Running 0 9s + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/kubernetes ClusterIP 10.96.0.1 443/TCP 3h30m +``` + +We wanted a pod, we got a pod, named `pingpong`. Great! + +(We can ignore `service/kubernetes`, it was already there before.) + +--- + +## The situation with Kubernetes 1.17- + +``` +NAME READY STATUS RESTARTS AGE +pod/pingpong-6ccbc77f68-kmgfn 1/1 Running 0 11s + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/kubernetes ClusterIP 10.96.0.1 443/TCP 3h45 + +NAME READY UP-TO-DATE AVAILABLE AGE +deployment.apps/pingpong 1/1 1 1 11s + +NAME DESIRED CURRENT READY AGE +replicaset.apps/pingpong-6ccbc77f68 1 1 1 11s +``` + +Our pod is not named `pingpong`, but `pingpong-xxxxxxxxxxx-yyyyy`. + +We have a Deployment named `pingpong`, and an extra Replica Set, too. What's going on? + +--- + +## From Deployment to Pod + +We have the following resources: + +- `deployment.apps/pingpong` + + This is the Deployment that we just created. + +- `replicaset.apps/pingpong-xxxxxxxxxx` + + This is a Replica Set created by this Deployment. + +- `pod/pingpong-xxxxxxxxxx-yyyyy` + + This is a *pod* created by the Replica Set. + +Let's explain what these things are. + +--- + +## Pod + +- Can have one or multiple containers + +- Runs on a single node + + (Pod cannot "straddle" multiple nodes) + +- Pods cannot be moved + + (e.g. in case of node outage) + +- Pods cannot be scaled + + (except by manually creating more Pods) + +--- + +class: extra-details + +## Pod details + +- A Pod is not a process; it's an environment for containers + + - it cannot be "restarted" + + - it cannot "crash" + +- The containers in a Pod can crash + +- They may or may not get restarted + + (depending on Pod's restart policy) + +- If all containers exit successfully, the Pod ends in "Succeeded" phase + +- If some containers fail and don't get restarted, the Pod ends in "Failed" phase + +--- + +## Replica Set + +- Set of identical (replicated) Pods + +- Defined by a pod template + number of desired replicas + +- If there are not enough Pods, the Replica Set creates more + + (e.g. in case of node outage; or simply when scaling up) + +- If there are too many Pods, the Replica Set deletes some + + (e.g. if a node was disconnected and comes back; or when scaling down) + +- We can scale up/down a Replica Set + + - we update the manifest of the Replica Set + + - as a consequence, the Replica Set controller creates/deletes Pods + +--- + +## Deployment + +- Replica Sets control *identical* Pods + +- Deployments are used to roll out different Pods + + (different image, command, environment variables, ...) + +- When we update a Deployment with a new Pod definition: + + - a new Replica Set is created with the new Pod definition + + - that new Replica Set is progressively scaled up + + - meanwhile, the old Replica Set(s) is(are) scaled down + +- This is a *rolling update*, minimizing application downtime + +- When we scale up/down a Deployment, it scales up/down its Replica Set + +--- + +## `kubectl run` through the ages + +- When we want to run an app on Kubernetes, we *generally* want a Deployment + +- Up to Kubernetes 1.17, `kubectl run` created a Deployment + + - it could also create other things, by using special flags + + - this was powerful, but potentially confusing + + - creating a single Pod was done with `kubectl run --restart=Never` + + - other resources could also be created with `kubectl create ...` + +- From Kubernetes 1.18, `kubectl run` creates a Pod + + - other kinds of resources can still be created with `kubectl create` + +--- + +## Creating a Deployment the proper way + +- Let's destroy that `pingpong` app that we created + +- Then we will use `kubectl create deployment` to re-create it + +.exercise[ + +- On Kubernetes 1.18+, delete the Pod named `pingpong`: + ```bash + kubectl delete pod pingpong + ``` + +- On Kubernetes 1.17-, delete the Deployment named `pingpong`: + ```bash + kubectl delete deployment pingpong + ``` + +] + +--- + +## Running `ping` in a Deployment + + + +- When using `kubectl create deployment`, we cannot indicate the command to execute + + (at least, not in Kubernetes 1.18) + +- We can: + + - write a custom YAML manifest for our Deployment + -- -We should see the following things: -- `deployment.apps/pingpong` (the *deployment* that we just created) -- `replicaset.apps/pingpong-xxxxxxxxxx` (a *replica set* created by the deployment) -- `pod/pingpong-xxxxxxxxxx-yyyyy` (a *pod* created by the replica set) + - (yeah right ... too soon!) -Note: as of 1.10.1, resource types are displayed in more detail. +-- + + - use an image that has the command to execute baked in + + - (much easier!) + +-- + +- We will use the image `jpetazzo/ping` + + (it has a default command of `ping 127.0.0.1`) --- -## What are these different things? +## Creating a Deployment running `ping` -- A *deployment* is a high-level construct +- Let's create a Deployment named `pingpong` - - allows scaling, rolling updates, rollbacks +- It will use the image `jpetazzo/ping` - - multiple deployments can be used together to implement a - [canary deployment](https://kubernetes.io/docs/concepts/cluster-administration/manage-deployment/#canary-deployments) +.exercise[ - - delegates pods management to *replica sets* +- Create the Deployment: + ```bash + kubectl create deployment pingpong --image=jpetazzo/ping + ``` -- A *replica set* is a low-level construct +- Check the resources that were created: + ```bash + kubectl get all + ``` - - makes sure that a given number of identical pods are running + - - allows scaling - - - rarely used directly - -- A *replication controller* is the (deprecated) predecessor of a replica set - ---- - -## Our `pingpong` deployment - -- `kubectl run` created a *deployment*, `deployment.apps/pingpong` - -``` -NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE -deployment.apps/pingpong 1 1 1 1 10m -``` - -- That deployment created a *replica set*, `replicaset.apps/pingpong-xxxxxxxxxx` - -``` -NAME DESIRED CURRENT READY AGE -replicaset.apps/pingpong-7c8bbcd9bc 1 1 1 10m -``` - -- That replica set created a *pod*, `pod/pingpong-xxxxxxxxxx-yyyyy` - -``` -NAME READY STATUS RESTARTS AGE -pod/pingpong-7c8bbcd9bc-6c9qz 1/1 Running 0 10m -``` - -- We'll see later how these folks play together for: - - - scaling, high availability, rolling updates +] --- @@ -294,132 +526,6 @@ Let's leave `kubectl logs` running while we keep exploring. --- - -## What if we wanted something different? - -- What if we wanted to start a "one-shot" container that *doesn't* get restarted? - -- We could use `kubectl run --restart=OnFailure` or `kubectl run --restart=Never` - -- These commands would create *jobs* or *pods* instead of *deployments* - -- Under the hood, `kubectl run` invokes "generators" to create resource descriptions - -- We could also write these resource descriptions ourselves (typically in YAML), -
and create them on the cluster with `kubectl apply -f` (discussed later) - -- With `kubectl run --schedule=...`, we can also create *cronjobs* - ---- - -## Scheduling periodic background work - -- A Cron Job is a job that will be executed at specific intervals - - (the name comes from the traditional cronjobs executed by the UNIX crond) - -- It requires a *schedule*, represented as five space-separated fields: - - - minute [0,59] - - hour [0,23] - - day of the month [1,31] - - month of the year [1,12] - - day of the week ([0,6] with 0=Sunday) - -- `*` means "all valid values"; `/N` means "every N" - -- Example: `*/3 * * * *` means "every three minutes" - ---- - -## Creating a Cron Job - -- Let's create a simple job to be executed every three minutes - -- Cron Jobs need to terminate, otherwise they'd run forever - -.exercise[ - -- Create the Cron Job: - ```bash - kubectl run every3mins --schedule="*/3 * * * *" --restart=OnFailure \ - --image=alpine sleep 10 - ``` - -- Check the resource that was created: - ```bash - kubectl get cronjobs - ``` - -] - ---- - -## Cron Jobs in action - -- At the specified schedule, the Cron Job will create a Job - -- The Job will create a Pod - -- The Job will make sure that the Pod completes - - (re-creating another one if it fails, for instance if its node fails) - -.exercise[ - -- Check the Jobs that are created: - ```bash - kubectl get jobs - ``` - -] - -(It will take a few minutes before the first job is scheduled.) - ---- - - -## What about that deprecation warning? - -- As we can see from the previous slide, `kubectl run` can do many things - -- The exact type of resource created is not obvious - -- To make things more explicit, it is better to use `kubectl create`: - - - `kubectl create deployment` to create a deployment - - - `kubectl create job` to create a job - - - `kubectl create cronjob` to run a job periodically -
(since Kubernetes 1.14) - -- Eventually, `kubectl run` will be used only to start one-shot pods - - (see https://github.com/kubernetes/kubernetes/pull/68132) - ---- - -## Various ways of creating resources - -- `kubectl run` - - - easy way to get started - - versatile - -- `kubectl create ` - - - explicit, but lacks some features - - can't create a CronJob before Kubernetes 1.14 - - can't pass command-line arguments to deployments - -- `kubectl create -f foo.yaml` or `kubectl apply -f foo.yaml` - - - all features are available - - requires writing YAML - ---- - ## Viewing logs of multiple pods - When we specify a deployment name, only one single pod's logs are shown @@ -428,13 +534,15 @@ Let's leave `kubectl logs` running while we keep exploring. - A selector is a logic expression using *labels* -- Conveniently, when you `kubectl run somename`, the associated objects have a `run=somename` label +- If we check the pods created by the deployment, they all have the label `app=pingpong` + + (this is just a default label that gets added when using `kubectl create deployment`) .exercise[ -- View the last line of log from all pods with the `run=pingpong` label: +- View the last line of log from all pods with the `app=pingpong` label: ```bash - kubectl logs -l run=pingpong --tail 1 + kubectl logs -l app=pingpong --tail 1 ``` ] @@ -449,7 +557,7 @@ Let's leave `kubectl logs` running while we keep exploring. - Combine `-l` and `-f` flags: ```bash - kubectl logs -l run=pingpong --tail 1 -f + kubectl logs -l app=pingpong --tail 1 -f ``` @@ -586,3 +694,195 @@ class: extra-details - This is a quick way to check connectivity (if we can reach 1.1, we probably have internet access) + +--- + +## Creating other kinds of resources + +- Deployments are great for stateless web apps + + (as well as workers that keep running forever) + +- Jobs are great for "long" background work + + ("long" being at least minutes our hours) + +- CronJobs are great to schedule Jobs at regular intervals + + (just like the classic UNIX `cron` daemon with its `crontab` files) + +- Pods are great for one-off execution that we don't care about + + (because they don't get automatically restarted if something goes wrong) + +--- + +## Creating a Job + +- A Job will create a Pod + +- If the Pod fails, the Job will create another one + +- The Job will keep trying until: + + - either a Pod succeeds, + + - or we hit the *backoff limit* of the Job (default=6) + +.exercise[ + +- Create a Job that has a 50% chance of success: + ```bash + kubectl create job flipcoin --image=alpine -- sh -c 'exit $(($RANDOM%2))' + ``` + +] + +--- + +## Our Job in action + +- Our Job will create a Pod named `flipcoin-xxxxx` + +- If the Pod succeeds, the Job stops + +- If the Pod fails, the Job creates another Pod + +.exercise[ + +- Check the status of the Pod(s) created by the Job: + ```bash + kubectl get pods --selector=job-name=flipcoin + ``` + +] + +--- + +class: extra-details + +## More advanced jobs + +- We can specify a number of "completions" (default=1) + +- This indicates how many times the Job must be executed + +- We can specify the "parallelism" (default=1) + +- This indicates how many Pods should be running in parallel + +- These options cannot be specified with `kubectl create job` + + (we have to write our own YAML manifest to use them) + +--- + +## Scheduling periodic background work + +- A Cron Job is a Job that will be executed at specific intervals + + (the name comes from the traditional cronjobs executed by the UNIX crond) + +- It requires a *schedule*, represented as five space-separated fields: + + - minute [0,59] + - hour [0,23] + - day of the month [1,31] + - month of the year [1,12] + - day of the week ([0,6] with 0=Sunday) + +- `*` means "all valid values"; `/N` means "every N" + +- Example: `*/3 * * * *` means "every three minutes" + +--- + +## Creating a Cron Job + +- Let's create a simple job to be executed every three minutes + +- Careful: make sure that the job terminates! + + (The Cron Job will not hold if a previous job is still running) + +.exercise[ + +- Create the Cron Job: + ```bash + kubectl create cronjob every3mins --schedule="*/3 * * * *" \ + --image=alpine -- sleep 10 + ``` + +- Check the resource that was created: + ```bash + kubectl get cronjobs + ``` + +] + +--- + +## Cron Jobs in action + +- At the specified schedule, the Cron Job will create a Job + +- The Job will create a Pod + +- The Job will make sure that the Pod completes + + (re-creating another one if it fails, for instance if its node fails) + +.exercise[ + +- Check the Jobs that are created: + ```bash + kubectl get jobs + ``` + +] + +(It will take a few minutes before the first job is scheduled.) + +--- + +class: extra-details + +## What about `kubectl run` before v1.18? + +- Creating a Deployment: + + `kubectl run` + +- Creating a Pod: + + `kubectl run --restart=Never` + +- Creating a Job: + + `kubectl run --restart=OnFailure` + +- Creating a Cron Job: + + `kubectl run --restart=OnFailure --schedule=...` + +*Avoid using these forms, as they are deprecated since Kubernetes 1.18!* + +--- + +## Beyond `kubectl create` + +- As hinted earlier, `kubectl create` doesn't always expose all options + + - can't express parallelism or completions of Jobs + + - can't express Pods with multiple containers + + - can't express healthchecks, resource limits + + - etc. + +- `kubectl create` and `kubectl run` are *helpers* that generate YAML manifests + +- If we write these manifests ourselves, we can use all features and options + +- We'll see later how to do that!