@@ -10,16 +10,14 @@ See [pods](../../docs/pods.md) for more details.
...
@@ -10,16 +10,14 @@ See [pods](../../docs/pods.md) for more details.
Trivially, a single container might be a pod. For example, you can express a simple web server as a pod:
Trivially, a single container might be a pod. For example, you can express a simple web server as a pod:
```yaml
```yaml
apiVersion:v1beta1
apiVersion:v1beta3
kind:Pod
kind:Pod
id:www
metadata:
desiredState:
name:www
manifest:
spec:
version:v1beta1
containers:
id:www
-name:nginx
containers:
image:nginx
-name:nginx
image:nginx
```
```
A pod definition is a declaration of a _desired state_. Desired state is a very important concept in the Kubernetes model. Many things present a desired state to the system, and it is Kubernetes' responsibility to make sure that the current state matches the desired state. For example, when you create a Pod, you declare that you want the containers in it to be running. If the containers happen to not be running (e.g. program failure, ...), Kubernetes will continue to (re-)create them for you in order to drive them to the desired state. This process continues until you delete the Pod.
A pod definition is a declaration of a _desired state_. Desired state is a very important concept in the Kubernetes model. Many things present a desired state to the system, and it is Kubernetes' responsibility to make sure that the current state matches the desired state. For example, when you create a Pod, you declare that you want the containers in it to be running. If the containers happen to not be running (e.g. program failure, ...), Kubernetes will continue to (re-)create them for you in order to drive them to the desired state. This process continues until you delete the Pod.
...
@@ -29,49 +27,39 @@ See the [design document](../../DESIGN.md) for more details.
...
@@ -29,49 +27,39 @@ See the [design document](../../DESIGN.md) for more details.
### Volumes
### Volumes
Now that's great for a static web server, but what about persistent storage? We know that the container file system only lives as long as the container does, so we need more persistent storage. To do this, you also declare a ```volume``` as part of your pod, and mount it into a container:
Now that's great for a static web server, but what about persistent storage? We know that the container file system only lives as long as the container does, so we need more persistent storage. To do this, you also declare a ```volume``` as part of your pod, and mount it into a container:
```yaml
```yaml
apiVersion:v1beta1
apiVersion:v1beta3
kind:Pod
kind:Pod
id:storage
metadata:
desiredState:
name:storage
manifest:
spec:
version:v1beta1
containers:
id:storage
-name:redis
containers:
image:redis
-name:redis
volumeMounts:
image:redis
# name must match the volume name below
volumeMounts:
-name:redis-persistent-storage
# name must match the volume name below
# mount path within the container
-name:redis-persistent-storage
mountPath:/data/redis
# mount path within the container
volumes:
mountPath:/data/redis
-name:redis-persistent-storage
volumes:
emptyDir:{}
-name:redis-persistent-storage
source:
emptyDir:{}
```
```
Ok, so what did we do? We added a volume to our pod:
Ok, so what did we do? We added a volume to our pod:
```
```yaml
volumes:
# ...
- name: redis-persistent-storage
volumes:
emptyDir: {}
-name:redis-persistent-storage
source:
emptyDir:{}
# ...
```
```
And we added a reference to that volume to our container:
And we added a reference to that volume to our container:
```yaml
```
# ...
volumeMounts:
volumeMounts:
# name must match the volume name below
# name must match the volume name below
- name: redis-persistent-storage
-name:redis-persistent-storage
# mount path within the container
# mount path within the container
mountPath: /data/redis
mountPath:/data/redis
# ...
```
```
In Kubernetes, ```emptyDir``` Volumes live for the lifespan of the Pod, which is longer than the lifespan of any one container, so if the container fails and is restarted, our persistent storage will live on.
In Kubernetes, ```emptyDir``` Volumes live for the lifespan of the Pod, which is longer than the lifespan of any one container, so if the container fails and is restarted, our persistent storage will live on.
...
@@ -89,32 +77,29 @@ The examples below are syntactically correct, but some of the images (e.g. kuber
...
@@ -89,32 +77,29 @@ The examples below are syntactically correct, but some of the images (e.g. kuber
However, often you want to have two different containers that work together. An example of this would be a web server, and a helper job that polls a git repository for new updates:
However, often you want to have two different containers that work together. An example of this would be a web server, and a helper job that polls a git repository for new updates:
```yaml
```yaml
apiVersion:v1beta1
apiVersion:v1beta3
kind:Pod
kind:Pod
id:www
metadata:
desiredState:
name:www
manifest:
spec:
version:v1beta1
containers:
id:www
-name:nginx
containers:
image:nginx
-name:nginx
volumeMounts:
image:nginx
-mountPath:/srv/www
volumeMounts:
name:www-data
-name:www-data
readOnly:true
mountPath:/srv/www
-name:git-monitor
readOnly:true
image:kubernetes/git-monitor
-name:git-monitor
env:
image:kubernetes/git-monitor
-name:GIT_REPO
env:
value:http://github.com/some/repo.git
-name:GIT_REPO
volumeMounts:
value:http://github.com/some/repo.git
-mountPath:/data
volumeMounts:
name:www-data
-name:www-data
volumes:
mountPath:/data
-name:www-data
volumes:
emptyDir:{}
-name:www-data
source:
emptyDir:{}
```
```
Note that we have also added a volume here. In this case, the volume is mounted into both containers. It is marked ```readOnly``` in the web server's case, since it doesn't need to write to the directory.
Note that we have also added a volume here. In this case, the volume is mounted into both containers. It is marked ```readOnly``` in the web server's case, since it doesn't need to write to the directory.
@@ -18,58 +18,57 @@ Lists all pods who name label matches 'nginx'. Labels are discussed in detail [
...
@@ -18,58 +18,57 @@ Lists all pods who name label matches 'nginx'. Labels are discussed in detail [
OK, now you have an awesome, multi-container, labelled pod and you want to use it to build an application, you might be tempted to just start building a whole bunch of individual pods, but if you do that, a whole host of operational concerns pop up. For example: how will you scale the number of pods up or down and how will you ensure that all pods are homogenous?
OK, now you have an awesome, multi-container, labelled pod and you want to use it to build an application, you might be tempted to just start building a whole bunch of individual pods, but if you do that, a whole host of operational concerns pop up. For example: how will you scale the number of pods up or down and how will you ensure that all pods are homogenous?
Replication controllers are the objects to answer these questions. A replication controller combines a template for pod creation (a "cookie-cutter" if you will) and a number of desired replicas, into a single API object. The replica controller also contains a label selector that identifies the set of objects managed by the replica controller. The replica controller constantly measures the size of this set relative to the desired size, and takes action by creating or deleting pods. The design of replica controllers is discussed in detail [elsewhere](http://docs.k8s.io/replication-controller.md).
Replication controllers are the objects to answer these questions. A replication controller combines a template for pod creation (a "cookie-cutter" if you will) and a number of desired replicas, into a single API object. The replication controller also contains a label selector that identifies the set of objects managed by the replication controller. The replication controller constantly measures the size of this set relative to the desired size, and takes action by creating or deleting pods. The design of replication controllers is discussed in detail [elsewhere](http://docs.k8s.io/replication-controller.md).
An example replica controller that instantiates two pods running nginx looks like:
An example replication controller that instantiates two pods running nginx looks like:
```yaml
```yaml
id:nginx-controller
apiVersion:v1beta3
apiVersion:v1beta1
kind:ReplicationController
kind:ReplicationController
desiredState:
metadata:
name:nginx-controller
spec:
replicas:2
replicas:2
# replicaSelector identifies the set of Pods that this
# selector identifies the set of Pods that this
# replicaController is responsible for managing
# replication controller is responsible for managing
replicaSelector:
selector:
name:nginx
name:nginx
# podTemplate defines the 'cookie cutter' used for creating
# podTemplate defines the 'cookie cutter' used for creating
# new pods when necessary
# new pods when necessary
podTemplate:
template:
desiredState:
metadata:
manifest:
labels:
version:v1beta1
# Important: these labels need to match the selector above
id:nginx
# The api server enforces this constraint.
containers:
name:nginx
-name:nginx
spec:
image:nginx
containers:
ports:
-name:nginx
-containerPort:80
image:nginx
# Important: these labels need to match the selector above
ports:
# The api server enforces this constraint.
-containerPort:80
labels:
name:nginx
```
```
### Services
### Services
Once you have a replicated set of pods, you need an abstraction that enables connectivity between the layers of your application. For example, if you have a replication controller managing your backend jobs, you don't want to have to reconfigure your front-ends whenever you re-scale your backends. Likewise, if the pods in your backends are scheduled (or rescheduled) onto different machines, you can't be required to re-configure your front-ends. In Kubernetes the Service API object achieves these goals. A Service basically combines an IP address and a label selector together to form a simple, static rallying point for connecting to a micro-service in your application.
Once you have a replicated set of pods, you need an abstraction that enables connectivity between the layers of your application. For example, if you have a replication controller managing your backend jobs, you don't want to have to reconfigure your front-ends whenever you re-scale your backends. Likewise, if the pods in your backends are scheduled (or rescheduled) onto different machines, you can't be required to re-configure your front-ends. In Kubernetes the Service API object achieves these goals. A Service basically combines an IP address and a label selector together to form a simple, static rallying point for connecting to a micro-service in your application.
For example, here is a service that balances across the pods created in the previous nginx replication controller example:
For example, here is a service that balances across the pods created in the previous nginx replication controller example:
```yaml
```yaml
apiVersion:v1beta3
kind:Service
kind:Service
apiVersion:v1beta1
metadata:
# must be a DNS compatible name
name:nginx-example
id:nginx-example
spec:
# the port that this service should serve on
ports:
port:8000
-port:8000# the port that this service should serve on
# just like the selector in the replication controller,
# the container on each pod to connect to, can be a name
# but this time it identifies the set of pods to load balance
# (e.g. 'www') or a number (e.g. 80)
# traffic to.
targetPort:80
selector:
protocol:TCP
name:nginx
# just like the selector in the replication controller,
# the container on each pod to connect to, can be a name
# but this time it identifies the set of pods to load balance
# (e.g. 'www') or a number (e.g. 80)
# traffic to.
containerPort:80
selector:
name:nginx
```
```
When created, each service is assigned a unique IP address. This address is tied to the lifespan of the Service, and will not change while the Service is alive. Pods can be configured to talk to the service, and know that communication to the service will be automatically load-balanced out to some pod that is a member of the set identified by the label selector in the Service. Services are described in detail [elsewhere](http://docs.k8s.io/services.md).
When created, each service is assigned a unique IP address. This address is tied to the lifespan of the Service, and will not change while the Service is alive. Pods can be configured to talk to the service, and know that communication to the service will be automatically load-balanced out to some pod that is a member of the set identified by the label selector in the Service. Services are described in detail [elsewhere](http://docs.k8s.io/services.md).
...
@@ -125,31 +124,27 @@ In all cases, if the Kubelet discovers a failure, the container is restarted.
...
@@ -125,31 +124,27 @@ In all cases, if the Kubelet discovers a failure, the container is restarted.
The container health checks are configured in the "LivenessProbe" section of your container config. There you can also specify an "initialDelaySeconds" that is a grace period from when the container is started to when health checks are performed, to enable your container to perform any necessary initialization.
The container health checks are configured in the "LivenessProbe" section of your container config. There you can also specify an "initialDelaySeconds" that is a grace period from when the container is started to when health checks are performed, to enable your container to perform any necessary initialization.
Here is an example config for a pod with an HTTP health check:
Here is an example config for a pod with an HTTP health check:
```yaml
```yaml
apiVersion:v1beta3
kind:Pod
kind:Pod
apiVersion:v1beta1
metadata:
desiredState:
name:pod-with-healthcheck
manifest:
spec:
version:v1beta1
containers:
id:php
-name:nginx
containers:
image:nginx
-name:nginx
# defines the health checking
image:nginx
livenessProbe:
ports:
# an http probe
-containerPort:80
httpGet:
# defines the health checking
path:/_status/healthz
livenessProbe:
port:8080
# turn on application health checking
# length of time to wait for a pod to initialize
enabled:true
# after pod startup, before applying health checking
type:http
initialDelaySeconds:30
# length of time to wait for a pod to initialize
timeoutSeconds:1
# after pod startup, before applying health checking
The first atom of Kubernetes is a _pod_. A pod is a collection of containers that are symbiotically grouped.
See [pods](../../../docs/pods.md) for more details.
### Intro
Trivially, a single container might be a pod. For example, you can express a simple web server as a pod:[pod1.yaml](pod1.yaml)
A pod definition is a declaration of a _desired state_. Desired state is a very important concept in the Kubernetes model. Many things present a desired state to the system, and it is Kubernetes' responsibility to make sure that the current state matches the desired state. For example, when you create a Pod, you declare that you want the containers in it to be running. If the containers happen to not be running (e.g. program failure, ...), Kubernetes will continue to (re-)create them for you in order to drive them to the desired state. This process continues until you delete the Pod.
See the [design document](../../../../DESIGN.md) for more details.
### Volumes
Now that's great for a static web server, but what about persistent storage? We know that the container file system only lives as long as the container does, so we need more persistent storage. To do this, you also declare a ```volume``` as part of your pod, and mount it into a container:
[pod2.yaml](pod2.yaml)
In Kubernetes, ```emptyDir``` Volumes live for the lifespan of the Pod, which is longer than the lifespan of any one container, so if the container fails and is restarted, our persistent storage will live on.
If you want to mount a directory that already exists in the file system (e.g. ```/var/logs```) you can use the ```hostDir``` directive.
See [volumes](../../../docs/volumes.md) for more details.
### Multiple Containers
_Note:
The examples below are syntactically correct, but some of the images (e.g. kubernetes/git-monitor) don't exist yet. We're working on turning these into working examples._
However, often you want to have two different containers that work together. An example of this would be a web server, and a helper job that polls a git repository for new updates:
```yaml
apiVersion:v1beta3
kind:Pod
metadata:
name:www
spec:
containers:
-name:nginx
image:nginx
volumeMounts:
-mountPath:/srv/www
name:www-data
readOnly:true
-name:git-monitor
image:kubernetes/git-monitor
env:
-name:GIT_REPO
value:http://github.com/some/repo.git
volumeMounts:
-mountPath:/data
name:www-data
volumes:
-name:www-data
emptyDir:{}
```
Note that we have also added a volume here. In this case, the volume is mounted into both containers. It is marked ```readOnly``` in the web server's case, since it doesn't need to write to the directory.
Finally, we have also introduced an environment variable to the ```git-monitor``` container, which allows us to parameterize that container with the particular git repository that we want to track.
### What's next?
Continue on to [Kubernetes 201](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/walkthrough/k8s201.md) or
for a complete application see the [guestbook example](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/guestbook/README.md)
# Kubernetes 201 - Labels, Replication Controllers, Services and Health Checking
### Overview
When we had just left off in the [previous episode](README.md) we had learned about pods, multiple containers and volumes.
We'll now cover some slightly more advanced topics in Kubernetes, related to application productionization, deployment and
scaling.
### Labels
Having already learned about Pods and how to create them, you may be struck by an urge to create many, many pods. Please do! But eventually you will need a system to organize these pods into groups. The system for achieving this in Kubernetes is Labels. Labels are key-value pairs that are attached to each API object in Kubernetes. Label selectors can be passed along with a RESTful ```list``` request to the apiserver to retrieve a list of objects which match that label selector. For example:
```sh
cluster/kubectl.sh get pods -lname=nginx
```
Lists all pods who name label matches 'nginx'. Labels are discussed in detail [elsewhere](http://docs.k8s.io/labels.md), but they are a core concept for two additional building blocks for Kubernetes, Replication Controllers and Services
### Replication Controllers
OK, now you have an awesome, multi-container, labelled pod and you want to use it to build an application, you might be tempted to just start building a whole bunch of individual pods, but if you do that, a whole host of operational concerns pop up. For example: how will you scale the number of pods up or down and how will you ensure that all pods are homogenous?
Replication controllers are the objects to answer these questions. A replication controller combines a template for pod creation (a "cookie-cutter" if you will) and a number of desired replicas, into a single API object. The replica controller also contains a label selector that identifies the set of objects managed by the replica controller. The replica controller constantly measures the size of this set relative to the desired size, and takes action by creating or deleting pods. The design of replica controllers is discussed in detail [elsewhere](http://docs.k8s.io/replication-controller.md).
An example replica controller that instantiates two pods running nginx looks like: [replication-controller](replication-controller.yaml)
### Services
Once you have a replicated set of pods, you need an abstraction that enables connectivity between the layers of your application. For example, if you have a replication controller managing your backend jobs, you don't want to have to reconfigure your front-ends whenever you re-scale your backends. Likewise, if the pods in your backends are scheduled (or rescheduled) onto different machines, you can't be required to re-configure your front-ends. In Kubernetes the Service API object achieves these goals. A Service basically combines an IP address and a label selector together to form a simple, static rallying point for connecting to a micro-service in your application.
For example, here is a service that balances across the pods created in the previous nginx replication controller example: [service](service.yaml)
When created, each service is assigned a unique IP address. This address is tied to the lifespan of the Service, and will not change while the Service is alive. Pods can be configured to talk to the service, and know that communication to the service will be automatically load-balanced out to some pod that is a member of the set identified by the label selector in the Service. Services are described in detail [elsewhere](http://docs.k8s.io/services.md).
### Health Checking
When I write code it never crashes, right? Sadly the [kubernetes issues list](https://github.com/GoogleCloudPlatform/kubernetes/issues) indicates otherwise...
Rather than trying to write bug-free code, a better approach is to use a management system to perform periodic health checking
and repair of your application. That way, a system, outside of your application itself, is responsible for monitoring the
application and taking action to fix it. It's important that the system be outside of the application, since of course, if
your application fails, and the health checking agent is part of your application, it may fail as well, and you'll never know.
In Kubernetes, the health check monitor is the Kubelet agent.
#### Low level process health-checking
The simplest form of health-checking is just process level health checking. The Kubelet constantly asks the Docker daemon
if the container process is still running, and if not, the container process is restarted. In all of the Kubernetes examples
you have run so far, this health checking was actually already enabled. It's on for every single container that runs in
Kubernetes.
#### Application health-checking
However, in many cases, this low-level health checking is insufficient. Consider for example, the following code:
```go
lockOne:=sync.Mutex{}
lockTwo:=sync.Mutex{}
gofunc(){
lockOne.Lock();
lockTwo.Lock();
...
}()
lockTwo.Lock();
lockOne.Lock();
```
This is a classic example of a problem in computer science known as "Deadlock". From Docker's perspective your application is
still operating, the process is still running, but from your application's perspective, your code is locked up, and will never respond correctly.
To address this problem, Kubernetes supports user implemented application health-checks. These checks are performed by the
Kubelet to ensure that your application is operating correctly for a definition of "correctly" that _you_ provide.
Currently, there are three types of application health checks that you can choose from:
* HTTP Health Checks - The Kubelet will call a web hook. If it returns between 200 and 399, it is considered success, failure otherwise.
* Container Exec - The Kubelet will execute a command inside your container. If it returns "ok" it will be considered a success.
* TCP Socket - The Kubelet will attempt to open a socket to your container. If it can establish a connection, the container is considered healthy, if it can't it is considered a failure.
In all cases, if the Kubelet discovers a failure, the container is restarted.
The container health checks are configured in the "LivenessProbe" section of your container config. There you can also specify an "initialDelaySeconds" that is a grace period from when the container is started to when health checks are performed, to enable your container to perform any necessary initialization.
Here is an example config for a pod with an HTTP health check: [pod-with-http-healthcheck](pod-with-http-healthcheck.yaml)
### What's next?
For a complete application see the [guestbook example](https://github.com/GoogleCloudPlatform/kubernetes/tree/master/examples/guestbook).