Skip to content

Commit

Permalink
Expand volume section
Browse files Browse the repository at this point in the history
  • Loading branch information
jpetazzo committed Nov 11, 2019
1 parent c15aa70 commit ed27ad1
Show file tree
Hide file tree
Showing 5 changed files with 298 additions and 21 deletions.
8 changes: 8 additions & 0 deletions k8s/nginx-1-without-volume.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-without-volume
spec:
containers:
- name: nginx
image: nginx
13 changes: 13 additions & 0 deletions k8s/nginx-2-with-volume.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-with-volume
spec:
volumes:
- name: www
containers:
- name: nginx
image: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html/
2 changes: 1 addition & 1 deletion k8s/nginx-with-volume.yaml → k8s/nginx-3-with-git.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-with-volume
name: nginx-with-git
spec:
volumes:
- name: www
Expand Down
20 changes: 20 additions & 0 deletions k8s/nginx-4-with-init.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Pod
metadata:
name: nginx-with-init
spec:
volumes:
- name: www
containers:
- name: nginx
image: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html/
initContainers:
- name: git
image: alpine
command: [ "sh", "-c", "apk add --no-cache git && git clone https://github.com/octocat/Spoon-Knife /www" ]
volumeMounts:
- name: www
mountPath: /www/
276 changes: 256 additions & 20 deletions slides/k8s/volumes.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,87 @@ class: extra-details

---

## A simple volume example
## Adding a volume to a Pod

- We will start with the simplest Pod manifest we can find

- We will add a volume to that Pod manifest

- We will mount that volume in a container in the Pod

- By default, this volume will be an `emptyDir`

(an empty directory)

- It will "shadow" the directory where it's mounted

---

## Our basic Pod

```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-without-volume
spec:
containers:
- name: nginx
image: nginx
```
This is a MVP! (Minimum Viable Pod😉)
It runs a single NGINX container.
---
## Trying the basic pod
.exercise[
- Create the Pod:
```bash
kubectl create -f ~/container.training/k8s/nginx-1-without-volume.yaml
```

- Get its IP address:
```bash
IPADDR=$(kubectl get pod nginx-without-volume -o jsonpath={.status.podIP})
```

- Send a request with curl:
```bash
curl $IPADDR
```

]

(We should see the "Welcome to NGINX" page.)

---

## Adding a volume

- We need to add the volume in two places:

- at the Pod level (to declare the volume)

- at the container level (to mount the volume)

- We will declare a volume named `www`

- No type is specified, so it will default to `emptyDir`

(as the name implies, it will be initialized as an empty directory at pod creation)

- In that pod, there is also a container named `nginx`

- That container mounts the volume `www` to path `/usr/share/nginx/html/`

---

## The Pod with a volume

```yaml
apiVersion: v1
Expand All @@ -86,30 +166,57 @@ spec:
---
## A simple volume example, explained
## Trying the Pod with a volume
- We define a standalone `Pod` named `nginx-with-volume`
.exercise[
- In that pod, there is a volume named `www`
- Create the Pod:
```bash
kubectl create -f ~/container.training/k8s/nginx-2-with-volume.yaml
```

- No type is specified, so it will default to `emptyDir`
- Get its IP address:
```bash
IPADDR=$(kubectl get pod nginx-with-volume -o jsonpath={.status.podIP})
```

(as the name implies, it will be initialized as an empty directory at pod creation)
- Send a request with curl:
```bash
curl $IPADDR
```

- In that pod, there is also a container named `nginx`
]

- That container mounts the volume `www` to path `/usr/share/nginx/html/`
(We should now see a "403 Forbidden" error page.)

---

## Populating the volume with another container

- Let's add another container to the Pod

- Let's mount the volume in *both* containers

- That container will populate the volume with static files

- NGINX will then serve these static files

- To populate the volume, we will clone the Spoon-Knife repository

- this repository is https://github.com/octocat/Spoon-Knife

- it's very popular (more than 100K stars!)

---

## A volume shared between two containers
## Sharing a volume between two containers

.small[
```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-with-volume
name: nginx-with-git
spec:
volumes:
- name: www
Expand Down Expand Up @@ -147,30 +254,72 @@ spec:

---

## Sharing a volume, in action
## Trying the shared volume

- This one will be time-sensitive!

- We need to catch the Pod IP address *as soon as it's created*

- Let's try it!
- Then send a request to it *as fast as possible*

.exercise[

- Create the pod by applying the YAML file:
- Watch the pods (so that we can catch the Pod IP address)
```bash
kubectl apply -f ~/container.training/k8s/nginx-with-volume.yaml
kubectl get pods -o wide --watch
```

- Check the IP address that was allocated to our pod:
]

---

## Shared volume in action

.exercise[

- Create the pod:
```bash
kubectl get pod nginx-with-volume -o wide
IP=$(kubectl get pod nginx-with-volume -o json | jq -r .status.podIP)
kubectl create -f ~/container.training/k8s/nginx-3-with-git.yaml
```

- Access the web server:
- As soon as we see its IP address, access it:
```bash
curl $IP
```

- A few seconds later, the state of the pod will change; access it again:
```bash
curl $IP
```

]

The first time, we should see "403 Forbidden".

The second time, we should see the HTML file from the Spoon-Knife repository.

---

## Explanations

- Both containers are started at the same time

- NGINX starts very quickly

(it can serve requests immediately)

- But at this point, the volume is empty

(NGINX serves "403 Forbidden")

- The other containers installs git and clones the repository

(this takes a bit longer)

- When the other container is done, the volume holds the repository

(NGINX serves the HTML file)

---

## The devil is in the details
Expand All @@ -183,13 +332,100 @@ spec:

- That's why we specified `restartPolicy: OnFailure`

---

## Inconsistencies

- There is a short period of time during which the website is not available

(because the `git` container hasn't done its job yet)

- This could be avoided by using [Init Containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/)
- With a bigger website, we could get inconsistent results

(where only a part of the content is ready)

- In real applications, this could cause incorrect results

- How can we avoid that?

---

## Init Containers

- We can define containers that should execute *before* the main ones

- They will be executed in order

(instead of in parallel)

- They must all succeed before the main containers are started

- This is *exactly* what we need here!

- Let's see one in action

.footnote[See [Init Containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) documentation for all the details.]

---

## Defining Init Containers

.small[
```yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-with-init
spec:
volumes:
- name: www
containers:
- name: nginx
image: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html/
initContainers:
- name: git
image: alpine
command: [ "sh", "-c", "apk add --no-cache git && git clone https://github.com/octocat/Spoon-Knife /www" ]
volumeMounts:
- name: www
mountPath: /www/
```
]

---

## Trying the init container

- Repeat the same operation as earlier

(try to send HTTP requests as soon as the pod comes up)

- This time, instead of "403 Forbidden" we get a "connection refused"

- NGINX doesn't start until the git container has done its job

- We never get inconsistent results

(a "half-ready" container)

---

## Other uses of init containers

- Load content

- Generate configuration (or certificates)

- Database migrations

- Waiting for other services to be up

(to avoid flurry of connection errors in main container)

(we will see a live example in a few sections)
- etc.

---

Expand Down

0 comments on commit ed27ad1

Please sign in to comment.