k8s - a primer
Kubernetes#
Much like the film Primer , kubernetes (a ten letter word abbreviated to k8s for reasons) is a complicated tangle of cleverness that can be quite exasperating, but also very fun.
There is already swathes of content surrounding how kubernetes came about and how it is useful. I will not add to that mountain. Instead this will be a high level overview of the main objects I have encountered and how they interact with each other.
Some additional arguments have been added to the original configs to show case some kubernetes features.
Pod#
This is the lowest, smallest, but most important object. It wraps a container that houses your code. In most instances it is a single Docker image.
Although these can be defined separately they are usually defined inside a
Deployment. The following Pod should be considered a template of a
Deployment.
# useful for selectors as well as manual kubectl commands
# breaking down your Objects in to multiple labels allows
# more granular control
metadata:
labels:
app: postgres
tier: backend
spec:
restartPolicy: Always
# rules that dictate node requirements, i.e. this Pod
# can only appear on a node with a particular OS, or
# hostname
nodeSelector:
# ...
# rules that dictate the pod must avoid matching pods,
# nodes, zones
podAntiAffinity:
# ...
# containers that are run to completion before containers,
# useful hook that can provide a way to guarantee resource
# dependencies
initContainers:
# ...
containers:
- name: postgres
# docker image to run
image: postgres
ports:
- containerPort: 5432
name: postgres
volumeMounts:
- name: postgres-data
mountPath: /var/lib/pgsql/data
# there are other methods of including env variables,
# but this will map all of our key/values from the
# specified ConfigMap
envFrom:
- configMapRef:
name: postgres-env
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: postgres-data-claim
Deployment#
A description of how many Pods of a type are desired and how they are
configured (think resource allotments, failure handling, update handling, etc).
It should also be noted than when declaring a Deployment, its definition
includes the Pod declaration.
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres-deploy
spec:
# define how many pods we want running
replicas: 1
# what do we consider to be a replica
selector:
matchLabels:
app: postgres
tier: backend
# amount of time newly created pod should be ready without
# any containers crashing before its considered available
minReadySeconds: 10
# amount of time before considered failed
progressDeadlineSeconds: 600
# update strategy and additional configuration
strategy:
type: RollingUpdate
# define the actual pod
template:
# ... see above
Service#
Defines an interface to a Deployment, i.e. if we had an API Deployment
that desired 3 replicas/instances, a Service would allow other Deployments
in the Cluster to communicate with the API without having to worry about
IP addresses or DNS.
apiVersion: v1
kind: Service
metadata:
name: postgres
labels:
app: postgres
tier: backend
spec:
# there are different service types with different options
# - ClusterIP: is the default and exposes the service to
# all pods in the cluster
# - NodePort: builds on ClusterIP and allocates a port on
# every node which routes to the clusterIP
# - LoadBalancer: builds on NodePort and creates an external
# load-balancer which routes to the clusterIP
type: ClusterIP
# select pods that are considered part of this service
selector:
app: postgres
tier: backend
# what container ports will be exposed and where
ports:
- port: 5432
targetPort: 5432
name: postgres
ConfigMap#
Provides a way of creating variables/configuration that is needed by different
objects. One use case would be turning them into environment variables used by
Deployments.
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-env
data:
POSTGRES_USER: whois.bi
POSTGRES_PASSWORD: ????
POSTGRES_DB: whois.bi
POSTGRES_URI: postgres://whois.bi:????@postgres:5432/whois.bi
PersistentStorageClaim#
Some Deployments will require persistent storage (i.e. a database), as
kubernetes provides reliability by allowing Pods to be created and ran on
different Nodes there needs to be a mechanism of providing access to this
data. There are plenty of underlying storage classes to choose from, but all
operate through claims (cloud specific offerings, NFS, glusterFS, etc).
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-data-claim
spec:
accessModes:
# in this instance, the claim is exclusive to One owner
- ReadWriteOnce
# what storage class we are making the claim against
storageClassName: postgres-data
# describe the minimum resources claimed
resources:
requests:
storage: 1Gi
Ingress#
We need a way in which we can provide a load balanced route in to the various
running services. This is available using the Ingress Object which is
essentially an nginx instance running.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress
labels:
tier: frontend
spec:
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 80
- path: /adminer
pathType: Prefix
backend:
service:
name: adminer
port:
number: 8080
- path: /rabbit
pathType: Prefix
backend:
service:
name: rabbitmq-manager
port:
number: 15672
Node#
A machine (physical, or virtual) that is part of the Cluster that runs Pods.
Cluster#
A collection of Nodes and other Kubernetes services that allow us to run all
these tasty objects.