Pods, services, deployments... it all sounds the same until you've had the chance to get hands on with it. This will be a short reference to the terminology of Kubernetes resources for a new cluster operator or developer.
Pods are one or more containers (but usually just one) running together; they are the smallest executable resource in Kubernetes.
Do not use pods directly, they don't scale and cannot be relocated to a different server. Use one of the below abstractions to manage templates of pods.
apiVersion: v1
kind: Pod
metadata:
name: my-app
labels:
app.kubernetes.io/name: my-app
spec:
containers:
- name: server
image: gcr.io/my-org/my-app:v1.2.3
ports:
- name: http
containerPort: 8080
Containers in a pod share a network interface so they can communicate over localhost
.
They can also be configured to share a process namespace (.spec.shareProcessNamespace: true
) which is useful in rare situations.
Pod controllers are higher-level abstractions that manage templates of pods to simplify scaling and management of applications. Most of these controllers create a ReplicaSet as an intermediary controller between themselves and the pods they manage; we can generally ignore these.
Deployments are the simplest controller, they ensure that .spec.replicas
copies of a pod are running at all times.
Most applications are stateless and will be run as a deployment.
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/part-of: my-app
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: my-app
app.kubernetes.io/managed-by: deployment/my-app
app.kubernetes.io/part-of: my-app
template:
metadata:
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/managed-by: deployment/my-app
app.kubernetes.io/part-of: my-app
spec:
containers:
- name: server
image: gcr.io/my-org/my-app:v1.2.3
ports:
- name: http
containerPort: 8080
The above deployment will create three pods that look like the following.
apiVersion: v1
kind: Pod
metadata:
name: my-app-b94bc5c87-5jdxm
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/managed-by: deployment/my-app
app.kubernetes.io/part-of: my-app
pod-template-hash: b94bc5c87
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: my-app-b94bc5c87
uid: d5afcfed-eb3e-4071-b659-535c6e1bbe7b
spec:
containers:
- name: server
image: gcr.io/my-org/my-app:v1.2.3
ports:
- name: http
containerPort: 8080
Some interesting things to note are the hash appended to the pod's name (b94bc5c87
) which matches the pod-template-hash
label and the name
of a ReplicaSet in the pod's metadata.ownerReferences
section.
Pods with this same hash are from the same version of a deployment; other versions of a deployment (say we updated to v1.2.4 of the same container image) would have a different hash.
StatefulSets perform a similar job as deployments but also provide some stateful guarantees:
- numbered pod names that persist across versions (
my-statefulset-0
,my-statefulset-1
, ... instead of hash suffixes) - consistent storage volume mounting (same pod gets same volume mounted across versions)
- ordered rollouts (one pod at a time starting with the lowest number)
These are useful for clustered programs like databases, although I would recommend running third-party managed services in place of self-hosting them. Statefulsets are rare in normal development.
DaemonSets also manage identical copies of pods, however they are limited by design to one pod per server. These are useful to run per-server process monitors (like the datadog agent) or an ingress controller to properly balance network traffic to nodes behind a load balancer.
Jobs run pods until they complete successfully, with backoff logic a configurable number of retries. These are perfect for running pre-deploy and post-deploy tasks, like database backups and migrations.
CronJobs run pods (through jobs) automatically on an interval. These are great for running frequent tasks like batch jobs or nightly database backups (you can never have enough).
Kubernetes contains configuration primitives that may be mounted into a container (see: configmaps and secrets) to control its behavior without being built into the container's image. Both can also be injected into a container's environment.
Configmaps contain plaintext key-value pairs and files.
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
labels:
app.kubernetes.io/name: my-app-config
app.kubernetes.io/part-of: my-app
data:
some-key: some-value
a-file.json: |
{"this is a json file": "embedded within a kubernetes manifest"}
Secrets contain, as their name might suggest, sensitive values and files. These are not encrypted in any way, they are simply base64-encoded to contain binary data.
The following secret has data equivalent to the above configmap.
apiVersion: v1
kind: Secret
metadata:
name: my-app-config
labels:
app.kubernetes.io/name: my-app-config
app.kubernetes.io/part-of: my-app
data:
some-key: c29tZS12YWx1ZQ==
a-file.json: eyJ0aGlzIGlzIGEganNvbiBmaWxlIjogImVtYmVkZGVkIHdpdGhpbiBhIGt1YmVybmV0ZXMgbWFuaWZlc3QifQ==
Under Kubernetes networking each pod gets its own in-cluster IP and DNS name. Because pods are ephemeral it is beter to use the following abstractions when making network calls between each other.
Services are load balancers for pods.
They select pods by their labels; a service's .spec.selector
should match a pod's .metadata.labels
.
Be strict with your label selectors, a pod must match all of them to be included in the service's load balancing, but the service will select any and all pods that match the selector, possibly including unintentional ones.
apiVersion: v1
kind: Service
metadata:
name: my-app
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/part-of: my-app
spec:
ports:
- name: http
port: 8080
targetPort: http
selector:
app.kubernetes.io/name: my-app
app.kubernetes.io/managed-by: deployment/my-app
app.kubernetes.io/part-of: my-app
type: ClusterIP
Services create Endpoints resources automatically, which can be inspected to reveal exactly which pods a service is selecting. These are included in this list because of their usefulness when debugging, you will almost never create endpoints directly.
apiVersion: v1
kind: Endpoints
metadata:
name: my-app
labels:
app.kubernetes.io/name: my-app
app.kubernetes.io/part-of: my-app
subsets:
- addresses:
- ip: 10.0.1.222
nodeName: gke-my-cluster-my-node-a8764c1c-1drj
targetRef:
kind: Pod
name: my-app-b94bc5c87-5jdxm
namespace: default
ports:
- name: http
port: 8080
protocol: TCP
Ingress resources map external hostnames to services (which are, generally, only accessible within the cluster), allowing them to be exposed to the public internet. Ingresses are managed by an "ingress controller", the complexities of which are described in another document, but it can generally be thought of as a Kubernetes-native reverse proxy.
Ingresses route hostname and path prefix (.spec.rules[].host
and .spec.rules[].http.paths[].path
respectively) matches to selected services within the same namespace by their names and ports (.spec.rules[].http.paths[].backend.serviceName
and servicePort
respectively).
In the following example, any request to foo.bar.com/
will be routed to svc/my-app
's http
port.
The .spec.tls
stanza controls TLS termination, mapping a collection of hostnames to a secret containing the corresponding TLS certificate and private key.
The stanza can be omitted entirely if TLS termination is handled elsewhere in the network.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: foo-bar-com
labels:
app.kubernetes.io/name: foo.bar.com
app.kubernetes.io/part-of: my-app
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: my-app
servicePort: http
path: /
tls:
- hosts:
- foo.bar.com
secretName: foo-bar-com-tls
status:
loadBalancer:
ingress:
- ip: 34.0.0.0
DNS is managed separately, foo.bar.com
must still point to the exposed ingress controller.