Merge branch 'master' into 2020-02-enix

This commit is contained in:
Jerome Petazzoni
2020-02-02 09:35:30 -06:00
17 changed files with 631 additions and 149 deletions
+15
View File
@@ -0,0 +1,15 @@
apiVersion: apiextensions.k8s.io/v1alpha1
kind: CustomResourceDefinition
metadata:
name: coffees.container.training
spec:
group: container.training
version: v1alpha1
scope: Namespaced
names:
plural: coffees
singular: coffee
kind: Coffee
shortNames:
- cof
+32
View File
@@ -0,0 +1,32 @@
apiVersion: apiextensions.k8s.io/v1alpha1
kind: CustomResourceDefinition
metadata:
name: coffees.container.training
spec:
group: container.training
version: v1alpha1
scope: Namespaced
names:
plural: coffees
singular: coffee
kind: Coffee
shortNames:
- cof
additionalPrinterColumns:
- JSONPath: .spec.taste
description: Subjective taste of that kind of coffee bean
name: Taste
type: string
- JSONPath: .metadata.creationTimestamp
name: Age
type: date
validation:
openAPIV3Schema:
properties:
spec:
required:
- taste
properties:
taste:
description: Subjective taste of that kind of coffee bean
type: string
+29
View File
@@ -0,0 +1,29 @@
---
kind: Coffee
apiVersion: container.training/v1alpha1
metadata:
name: arabica
spec:
taste: strong
---
kind: Coffee
apiVersion: container.training/v1alpha1
metadata:
name: robusta
spec:
taste: stronger
---
kind: Coffee
apiVersion: container.training/v1alpha1
metadata:
name: liberica
spec:
taste: smoky
---
kind: Coffee
apiVersion: container.training/v1alpha1
metadata:
name: excelsa
spec:
taste: fruity
+1 -1
View File
@@ -13,7 +13,7 @@ spec:
mountPath: /usr/share/nginx/html/
- name: git
image: alpine
command: [ "sh", "-c", "apk add --no-cache git && git clone https://github.com/octocat/Spoon-Knife /www" ]
command: [ "sh", "-c", "apk add git && git clone https://github.com/octocat/Spoon-Knife /www" ]
volumeMounts:
- name: www
mountPath: /www/
+1 -1
View File
@@ -61,6 +61,6 @@ TAG=$PREFIX-$SETTINGS
--count $((3*$STUDENTS))
./workshopctl deploy $TAG
./workshopctl kube $TAG 1.14.6
./workshopctl kube $TAG 1.16.6
./workshopctl cards $TAG
+166 -61
View File
@@ -81,7 +81,7 @@
## What version are we running anyway?
- When I say, "I'm running Kubernetes 1.11", is that the version of:
- When I say, "I'm running Kubernetes 1.16", is that the version of:
- kubectl
@@ -139,6 +139,73 @@
---
## Important questions
- Should we upgrade the control plane before or after the kubelets?
- Within the control plane, should we upgrade the API server first or last?
- How often should we upgrade?
- How long are versions maintained?
- All the answers are in [the documentation about version skew policy](https://kubernetes.io/docs/setup/release/version-skew-policy/)!
- Let's review the key elements together ...
---
## Kubernetes uses semantic versioning
- Kubernetes versions look like MAJOR.MINOR.PATCH; e.g. in 1.17.2:
- MAJOR = 1
- MINOR = 17
- PATCH = 2
- It's always possible to mix and match different PATCH releases
(e.g. 1.16.1 and 1.16.6 are compatible)
- It is recommended to run the latest PATCH release
(but it's mandatory only when there is a security advisory)
---
## Version skew
- API server must be more recent than its clients (kubelet and control plane)
- ... Which means it must always be upgraded first
- All components support a difference of one¹ MINOR version
- This allows live upgrades (since we can mix e.g. 1.15 and 1.16)
- It also means that going from 1.14 to 1.16 requires going through 1.15
.footnote[¹Except kubelet, which can be up to two MINOR behind API server,
and kubectl, which can be one MINOR ahead or behind API server.]
---
## Release cycle
- There is a new PATCH relese whenever necessary
(every few weeks, or "ASAP" when there is a security vulnerability)
- There is a new MINOR release every 3 months (approximately)
- At any given time, three MINOR releases are maintained
- ... Which means that MINOR releases are maintained approximately 9 months
- We should expect to upgrade at least every 3 months (on average)
---
## In practice
- We are going to update a few cluster components
@@ -151,47 +218,6 @@
---
## Updating kubelet
- These nodes have been installed using the official Kubernetes packages
- We can therefore use `apt` or `apt-get`
.exercise[
- Log into node `test3`
- View available versions for package `kubelet`:
```bash
apt show kubelet -a | grep ^Version
```
- Upgrade kubelet:
```bash
sudo apt install kubelet=1.15.3-00
```
]
---
## Checking what we've done
.exercise[
- Log into node `test1`
- Check node versions:
```bash
kubectl get nodes -o wide
```
- Create a deployment and scale it to make sure that the node still works
]
---
## Updating the API server
- This cluster has been deployed with kubeadm
@@ -228,7 +254,7 @@
sudo vim /etc/kubernetes/manifests/kube-apiserver.yaml
```
- Look for the `image:` line, and update it to e.g. `v1.15.0`
- Look for the `image:` line, and update it to e.g. `v1.17.0`
]
@@ -249,9 +275,27 @@
---
## Was that a good idea?
--
**No!**
--
- Remember the guideline we gave earlier:
*To update a component, use whatever was used to install it.*
- This control plane was deployed with kubeadm
- We should use kubeadm to upgrade it!
---
## Updating the whole control plane
- As an example, we'll use kubeadm to upgrade the entire control plane
- Let's make it right, and use kubeadm to upgrade the entire control plane
(note: this is possible only because the cluster was installed with kubeadm)
@@ -264,11 +308,11 @@
]
Note 1: kubeadm thinks that our cluster is running 1.15.0.
Note 1: kubeadm thinks that our cluster is running 1.17.0.
<br/>It is confused by our manual upgrade of the API server!
Note 2: kubeadm itself is still version 1.14.6.
<br/>It doesn't know how to upgrade do 1.15.X.
Note 2: kubeadm itself is still version 1.16.6.
<br/>It doesn't know how to upgrade do 1.17.X.
---
@@ -290,8 +334,8 @@ Note 2: kubeadm itself is still version 1.14.6.
]
Note: kubeadm still thinks that our cluster is running 1.15.0.
<br/>But at least it knows about version 1.15.X now.
Note: kubeadm still thinks that our cluster is running 1.17.0.
<br/>But at least it knows about version 1.17.X now.
---
@@ -307,28 +351,89 @@ Note: kubeadm still thinks that our cluster is running 1.15.0.
- Perform the upgrade:
```bash
sudo kubeadm upgrade apply v1.15.3
sudo kubeadm upgrade apply v1.17.2
```
]
---
## Updating kubelets
## Updating kubelet
- After updating the control plane, we need to update each kubelet
- These nodes have been installed using the official Kubernetes packages
- This requires to run a special command on each node, to download the config
- We can therefore use `apt` or `apt-get`
(this config is generated by kubeadm)
.exercise[
- Log into node `test3`
- View available versions for package `kubelet`:
```bash
apt show kubelet -a | grep ^Version
```
- Upgrade kubelet:
```bash
sudo apt install kubelet=1.17.2-00
```
]
---
## Checking what we've done
.exercise[
- Log into node `test1`
- Check node versions:
```bash
kubectl get nodes -o wide
```
- Create a deployment and scale it to make sure that the node still works
]
---
## Was that a good idea?
--
**Almost!**
--
- Yes, kubelet was installed with distribution packages
- However, kubeadm took care of configuring kubelet
(when doing `kubeadm join ...`)
- We were supposed to run a special command *before* upgrading kubelet!
- That command should be executed on each node
- It will download the kubelet configuration generated by kubeadm
---
## Upgrading kubelet the right way
- The command that we need to run was shown by kubeadm
(after upgrading the control plane)
.exercise[
- Download the configuration on each node, and upgrade kubelet:
```bash
for N in 1 2 3; do
ssh test$N sudo kubeadm upgrade node config --kubelet-version v1.15.3
ssh test$N sudo apt install kubelet=1.15.3-00
ssh test$N sudo kubeadm upgrade node config --kubelet-version v1.17.2
ssh test$N sudo apt install kubelet=1.17.2-00
done
```
]
@@ -337,7 +442,7 @@ Note: kubeadm still thinks that our cluster is running 1.15.0.
## Checking what we've done
- All our nodes should now be updated to version 1.15.3
- All our nodes should now be updated to version 1.17.2
.exercise[
@@ -354,12 +459,12 @@ class: extra-details
## Skipping versions
- This example worked because we went from 1.14 to 1.15
- This example worked because we went from 1.16 to 1.17
- If you are upgrading from e.g. 1.13, you will generally have to go through 1.14 first
- If you are upgrading from e.g. 1.14, you will have to go through 1.15 first
- This means upgrading kubeadm to 1.14.X, then using it to upgrade the cluster
- This means upgrading kubeadm to 1.15.X, then using it to upgrade the cluster
- Then upgrading kubeadm to 1.15.X, etc.
- Then upgrading kubeadm to 1.16.X, etc.
- **Make sure to read the release notes before upgrading!**
+23
View File
@@ -10,6 +10,29 @@
---
## What can we do with Kubernetes?
- Let's imagine that we have a 3-tier e-commerce app:
- web frontend
- API backend
- database (that we will keep out of Kubernetes for now)
- We have built images for our frontend and backend components
(e.g. with Dockerfiles and `docker build`)
- We are running them successfully with a local environment
(e.g. with Docker Compose)
- Let's see how we would deploy our app on Kubernetes!
---
## Basic things we can ask Kubernetes to do
--
+15 -15
View File
@@ -52,7 +52,7 @@
<!-- ##VERSION## -->
- Unfortunately, as of Kubernetes 1.15, the CLI cannot create daemon sets
- Unfortunately, as of Kubernetes 1.17, the CLI cannot create daemon sets
--
@@ -427,7 +427,7 @@ class: extra-details
- We need to change the selector of the `rng` service!
- Let's add another label to that selector (e.g. `enabled=yes`)
- Let's add another label to that selector (e.g. `active=yes`)
---
@@ -445,11 +445,11 @@ class: extra-details
## The plan
1. Add the label `enabled=yes` to all our `rng` pods
1. Add the label `active=yes` to all our `rng` pods
2. Update the selector for the `rng` service to also include `enabled=yes`
2. Update the selector for the `rng` service to also include `active=yes`
3. Toggle traffic to a pod by manually adding/removing the `enabled` label
3. Toggle traffic to a pod by manually adding/removing the `active` label
4. Profit!
@@ -464,7 +464,7 @@ be any interruption.*
## Adding labels to pods
- We want to add the label `enabled=yes` to all pods that have `app=rng`
- We want to add the label `active=yes` to all pods that have `app=rng`
- We could edit each pod one by one with `kubectl edit` ...
@@ -474,9 +474,9 @@ be any interruption.*
.exercise[
- Add `enabled=yes` to all pods that have `app=rng`:
- Add `active=yes` to all pods that have `app=rng`:
```bash
kubectl label pods -l app=rng enabled=yes
kubectl label pods -l app=rng active=yes
```
]
@@ -495,7 +495,7 @@ be any interruption.*
.exercise[
- Update the service to add `enabled: yes` to its selector:
- Update the service to add `active: yes` to its selector:
```bash
kubectl edit service rng
```
@@ -504,7 +504,7 @@ be any interruption.*
```wait Please edit the object below```
```keys /app: rng```
```key ^J```
```keys noenabled: yes```
```keys noactive: yes```
```key ^[``` ]
```keys :wq```
```key ^J```
@@ -530,7 +530,7 @@ be any interruption.*
- If we want the string `"42"` or the string `"yes"`, we have to quote them
- So we have to use `enabled: "yes"`
- So we have to use `active: "yes"`
.footnote[For a good laugh: if we had used "ja", "oui", "si" ... as the value, it would have worked!]
@@ -542,7 +542,7 @@ be any interruption.*
- Update the YAML manifest of the service
- Add `enabled: "yes"` to its selector
- Add `active: "yes"` to its selector
<!--
```wait Please edit the object below```
@@ -566,7 +566,7 @@ If we did everything correctly, the web UI shouldn't show any change.
- We want to disable the pod that was created by the deployment
- All we have to do, is remove the `enabled` label from that pod
- All we have to do, is remove the `active` label from that pod
- To identify that pod, we can use its name
@@ -600,7 +600,7 @@ If we did everything correctly, the web UI shouldn't show any change.
- In another window, remove the label from the pod:
```bash
kubectl label pod -l app=rng,pod-template-hash enabled-
kubectl label pod -l app=rng,pod-template-hash active-
```
(The stream of HTTP logs should stop immediately)
@@ -623,7 +623,7 @@ class: extra-details
- If we scale up our cluster by adding new nodes, the daemon set will create more pods
- These pods won't have the `enabled=yes` label
- These pods won't have the `active=yes` label
- If we want these pods to have that label, we need to edit the daemon set spec
+145 -19
View File
@@ -8,6 +8,8 @@ We are going to cover:
- Admission Webhooks
- The Aggregation Layer
---
## Revisiting the API server
@@ -46,6 +48,90 @@ We are going to cover:
---
## A very simple CRD
The YAML below describes a very simple CRD representing different kinds of coffee:
```yaml
apiVersion: apiextensions.k8s.io/v1alpha1
kind: CustomResourceDefinition
metadata:
name: coffees.container.training
spec:
group: container.training
version: v1alpha1
scope: Namespaced
names:
plural: coffees
singular: coffee
kind: Coffee
shortNames:
- cof
```
---
## Creating a CRD
- Let's create the Custom Resource Definition for our Coffee resource
.exercise[
- Load the CRD:
```bash
kubectl apply -f ~/container.training/k8s/coffee-1.yaml
```
- Confirm that it shows up:
```bash
kubectl get crds
```
]
---
## Creating custom resources
The YAML below defines a resource using the CRD that we just created:
```yaml
kind: Coffee
apiVersion: container.training/v1alpha1
metadata:
name: arabica
spec:
taste: strong
```
.exercise[
- Create a few types of coffee beans:
```bash
kubectl apply -f ~/container.training/k8s/coffees.yaml
```
]
---
## Viewing custom resources
- By default, `kubectl get` only shows name and age of custom resources
.exercise[
- View the coffee beans that we just created:
```bash
kubectl get coffees
```
]
- We can improve that, but it's outside the scope of this section!
---
## What can we do with CRDs?
There are many possibilities!
@@ -81,7 +167,7 @@ There are many possibilities!
- Generally, when creating a CRD, we also want to run a *controller*
(otherwise nothing will happen when we create resources of that type)
(otherwise nothing will happen when we create resources of that type)
- The controller will typically *watch* our custom resources
@@ -95,6 +181,22 @@ Examples:
---
## (Ab)using the API server
- If we need to store something "safely" (as in: in etcd), we can use CRDs
- This gives us primitives to read/write/list objects (and optionally validate them)
- The Kubernetes API server can run on its own
(without the scheduler, controller manager, and kubelets)
- By loading CRDs, we can have it manage totally different objects
(unrelated to containers, clusters, etc.)
---
## Service catalog
- *Service catalog* is another extension mechanism
@@ -109,7 +211,7 @@ Examples:
- ClusterServiceClass
- ClusterServicePlan
- ServiceInstance
- ServiceBinding
- ServiceBinding
- It uses the Open service broker API
@@ -117,17 +219,13 @@ Examples:
## Admission controllers
- When a Pod is created, it is associated with a ServiceAccount
- Admission controllers are another way to extend the Kubernetes API
(even if we did not specify one explicitly)
- Instead of creating new types, admission controllers can transform or vet API requests
- That ServiceAccount was added on the fly by an *admission controller*
- The diagram on the next slide shows the path of an API request
(specifically, a *mutating admission controller*)
- Admission controllers sit on the API request path
(see the cool diagram on next slide, courtesy of Banzai Cloud)
(courtesy of Banzai Cloud)
---
@@ -137,7 +235,7 @@ class: pic
---
## Admission controllers
## Types of admission controllers
- *Validating* admission controllers can accept/reject the API call
@@ -151,7 +249,27 @@ class: pic
(see [documentation](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#what-does-each-admission-controller-do) for a list)
- But we can also define our own!
- We can also dynamically define and register our own
---
class: extra-details
## Some built-in admission controllers
- ServiceAccount:
automatically adds a ServiceAccount to Pods that don't explicitly specify one
- LimitRanger:
applies resource constraints specified by LimitRange objects when Pods are created
- NamespaceAutoProvision:
automatically creates namespaces when an object is created in a non-existent namespace
*Note: #1 and #2 are enabled by default; #3 is not.*
---
@@ -191,19 +309,25 @@ class: pic
---
## (Ab)using the API server
## The aggregation layer
- If we need to store something "safely" (as in: in etcd), we can use CRDs
- We can delegate entire parts of the Kubernetes API to external servers
- This gives us primitives to read/write/list objects (and optionally validate them)
- This is done by creating APIService resources
- The Kubernetes API server can run on its own
(check them with `kubectl get apiservices`!)
(without the scheduler, controller manager, and kubelets)
- The APIService resource maps a type (kind) and version to an external service
- By loading CRDs, we can have it manage totally different objects
- All requests concerning that type are sent (proxied) to the external service
(unrelated to containers, clusters, etc.)
- This allows to have resources like CRDs, but that aren't stored in etcd
- Example: `metrics-server`
(storing live metrics in etcd would be extremely inefficient)
- Requires significantly more work than CRDs!
---
@@ -218,3 +342,5 @@ class: pic
- [Built-in Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/)
- [Dynamic Admission Controllers](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)
- [Aggregation Layer](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/)
+2 -2
View File
@@ -314,7 +314,7 @@ class: extra-details
- List all the resources created by this release:
```bash
kuectl get all --selector=release=java4ever
kubectl get all --selector=release=java4ever
```
]
@@ -416,4 +416,4 @@ All unspecified values will take the default values defined in the chart.
curl localhost:$PORT/sample/
```
]
]
+23 -9
View File
@@ -120,19 +120,13 @@
- We want our ingress load balancer to be available on port 80
- We could do that with a `LoadBalancer` service
- The best way to do that would be with a `LoadBalancer` service
... but it requires support from the underlying infrastructure
- We could use pods specifying `hostPort: 80`
- Instead, we are going to use the `hostNetwork` mode on the Traefik pods
... but with most CNI plugins, this [doesn't work or requires additional setup](https://github.com/kubernetes/kubernetes/issues/23920)
- We could use a `NodePort` service
... but that requires [changing the `--service-node-port-range` flag in the API server](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/)
- Last resort: the `hostNetwork` mode
- Let's see what this `hostNetwork` mode is about ...
---
@@ -170,6 +164,26 @@
---
class: extra-details
## Other techniques to expose port 80
- We could use pods specifying `hostPort: 80`
... but with most CNI plugins, this [doesn't work or requires additional setup](https://github.com/kubernetes/kubernetes/issues/23920)
- We could use a `NodePort` service
... but that requires [changing the `--service-node-port-range` flag in the API server](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/)
- We could create a service with an external IP
... this would work, but would require a few extra steps
(figuring out the IP address and adding it to the service)
---
## Running Traefik
- The [Traefik documentation](https://docs.traefik.io/user-guide/kubernetes/#deploy-trfik-using-a-deployment-or-daemonset) tells us to pick between Deployment and Daemon Set
+126 -35
View File
@@ -1,20 +1,76 @@
# Exposing containers
- `kubectl expose` creates a *service* for existing pods
- We can connect to our pods using their IP address
- A *service* is a stable address for a pod (or a bunch of pods)
- Then we need to figure out a lot of thigns:
- If we want to connect to our pod(s), we need to create a *service*
- how do we look up the IP address of the pod(s)?
- Once a service is created, CoreDNS will allow us to resolve it by name
- how do we connect from outside the cluster?
(i.e. after creating service `hello`, the name `hello` will resolve to something)
- how do we load balance traffic?
- There are different types of services, detailed on the following slides:
- what if a pod fails?
- Kubernetes has a resource type named *Service*
- Services address all these questions!
---
## Services in a nutshell
- Services give us a *stable endpoint* to connect to a pod or a group of pods
- An easy way to create a service is to use `kubectl expose`
- If we have a deployment named `my-little-deploy`, we can run:
`kubectl expose deployment my-little-deploy --port=80`
... and this will create a service with the same name (`my-little-deploy`)
- Services are automatically added to an internal DNS zone
(in the example above, our code can now connect to http://my-little-deploy/)
---
## Advantages of services
- We don't need to look up the IP address of the pod(s)
(we resolve the IP address of the service using DNS)
- There are multiple service types; some of them allow external traffic
(e.g. `LoadBalancer` and `NodePort`)
- Services provide load balancing
(for both internal and external traffic)
- Service addresses are independent from pods' addresses
(when a pod fails, the service seamlessly sends traffic to its replacement)
---
## Many kinds and flavors of service
- There are different types of services:
`ClusterIP`, `NodePort`, `LoadBalancer`, `ExternalName`
- HTTP services can also use `Ingress` resources (more on that later)
- There are also *headless services*
- Services can also have optional *external IPs*
- There is also another resource type called *Ingress*
(specifically for HTTP services)
- Wow, that's a lot! Let's start with the basics ...
---
@@ -73,24 +129,6 @@
---
class: extra-details
## `ExternalName`
- No load balancer (internal or external) is created
- Only a DNS entry gets added to the DNS managed by Kubernetes
- That DNS entry will just be a `CNAME` to a provided record
Example:
```bash
kubectl create service externalname k8s --external-name kubernetes.io
```
*Creates a CNAME `k8s` pointing to `kubernetes.io`*
---
## Running containers with open ports
- Since `ping` doesn't have anything to connect to, we'll have to run something else
@@ -175,9 +213,7 @@ kubectl create service externalname k8s --external-name kubernetes.io
- As a result: you *have to* indicate the port number for your service
- Running services with arbitrary port (or port ranges) requires hacks
(e.g. host networking mode)
(with some exceptions, like `ExternalName` or headless services, covered later)
---
@@ -218,7 +254,48 @@ Try it a few times! Our requests are load balanced across multiple pods.
class: extra-details
## If we don't need a load balancer
## `ExternalName`
- Services of type `ExternalName` are quite different
- No load balancer (internal or external) is created
- Only a DNS entry gets added to the DNS managed by Kubernetes
- That DNS entry will just be a `CNAME` to a provided record
Example:
```bash
kubectl create service externalname k8s --external-name kubernetes.io
```
*Creates a CNAME `k8s` pointing to `kubernetes.io`*
---
class: extra-details
## External IPs
- We can add an External IP to a service, e.g.:
```bash
kubectl expose deploy my-little-deploy --port=80 --external-ip=1.2.3.4
```
- `1.2.3.4` should be the address of one of our nodes
(it could also be a virtual address, service address, or VIP, shared by multiple nodes)
- Connections to `1.2.3.4:80` will be sent to our service
- External IPs will also show up on services of type `LoadBalancer`
(they will be added automatically by the process provisioning the load balancer)
---
class: extra-details
## Headless services
- Sometimes, we want to access our scaled services directly:
@@ -238,7 +315,7 @@ class: extra-details
class: extra-details
## Headless services
## Creating a headless services
- A headless service is obtained by setting the `clusterIP` field to `None`
@@ -324,18 +401,32 @@ error: the server doesn't have a resource type "endpoint"
class: extra-details
## `ExternalIP`
## The DNS zone
- When creating a servivce, we can also specify an `ExternalIP`
- In the `kube-system` namespace, there should be a service named `kube-dns`
(this is not a type, but an extra attribute to the service)
- This is the internal DNS server that can resolve service names
- It will make the service availableon this IP address
- The default domain name for the service we created is `default.svc.cluster.local`
(if the IP address belongs to a node of the cluster)
.exercise[
- Get the IP address of the internal DNS server:
```bash
IP=$(kubectl -n kube-system get svc kube-dns -o jsonpath={.spec.clusterIP})
```
- Resolve the cluster IP for the `httpenv` service:
```bash
host httpenv.default.svc.cluster.local $IP
```
]
---
class: extra-details
## `Ingress`
- Ingresses are another type (kind) of resource
-2
View File
@@ -102,8 +102,6 @@
]
- Some tools like Helm will create namespaces automatically when needed
---
## Using namespaces
+1 -1
View File
@@ -1,6 +1,6 @@
## Versions installed
- Kubernetes 1.17.1
- Kubernetes 1.17.2
- Docker Engine 19.03.5
- Docker Compose 1.24.1
+2 -2
View File
@@ -50,7 +50,7 @@ class: extra-details
- *Volumes*:
- appear in Pod specifications (see next slide)
- appear in Pod specifications (we'll see that in a few slides)
- do not exist as API resources (**cannot** do `kubectl get volumes`)
@@ -232,7 +232,7 @@ spec:
mountPath: /usr/share/nginx/html/
- name: git
image: alpine
command: [ "sh", "-c", "apk add --no-cache git && git clone https://github.com/octocat/Spoon-Knife /www" ]
command: [ "sh", "-c", "apk add git && git clone https://github.com/octocat/Spoon-Knife /www" ]
volumeMounts:
- name: www
mountPath: /www/
+49
View File
@@ -91,3 +91,52 @@
because the resources that we created lack the necessary annotation.
We can safely ignore them.)
---
## Deleting resources
- We can also use a YAML file to *delete* resources
- `kubectl delete -f ...` will delete all the resources mentioned in a YAML file
(useful to clean up everything that was created by `kubectl apply -f ...`)
- The definitions of the resources don't matter
(just their `kind`, `apiVersion`, and `name`)
---
## 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.]
---
## YAML as source of truth
- Imagine the following workflow:
- do not use `kubectl run`, `kubectl create deployment`, `kubectl expose` ...
- define everything with YAML
- `kubectl apply -f ... --prune --all` that YAML
- keep that YAML under version control
- enforce all changes to go through that YAML (e.g. with pull requests)
- Our version control system now has a full history of what we deploy
- Compares to "Infrastructure-as-Code", but for app deployments
+1 -1
View File
@@ -28,7 +28,7 @@ class Interstitials(object):
def next(self):
index = self.index % len(self.images)
index += 1
self.index += 1
return self.images[index]