Kubernetes
What is Kubernetes?
According to kubernetes.com, Kubernetes is…
an open-source system for automating deployment, scaling, and management of containerized applications.
I would define it as: Kubernetes is a program for creating and managing a computer cluster.
A cluster is a distributed system in which multiple computers work together to behave somewhat like one, bigger computer. There is a long history of cluster computing (see: beowulf clusters). While a distributed system is fundamentally different from a single computer, cluster management software (e.g. Kubernetes) helps to bridge that gap.
These days, practically all applications are distributed systems. Dealing with the inherent problems of a distributed system is often left to the application layer. Kubernetes is designed to directly address some (but not all) of the fundamental problems of working in a distributed system, like: how do you tell when a node is dead? And what should be done about that when it happens?
Kubernetes provides heavy abstractions for dealing with clusters. Some of the problems that need to be addressed are:
- Networking
- What does it mean for a machine to be "in" the cluster vs "out" of the cluster?
- How should inter-process communication work when processes might be on different machines?
- What happens when a machine is no longer responsive?
- Process Scheduling
- When you run a process on the cluster, which specific machine should the process actually run on?
- What if certain processes can only run on certain machines?
- Data
- How can processes share data (on disk) when they might running on different machines?
- Should every process have access to the whole host's filesystem? (Probably not.) How can we make process-local disk space?
- Security
- Should the cluster have some kind of RBAC (authn/authz) separate from that of each individual machine?
At first, I thought Kubernetes was mostly about process scheduling, but that is actually a very small part of Kubernetes. The most crucial part of Kubernetes is probably the networking constructs it provides: how an internal network is set up and how communication can happen between processes, regardless of the nodes on which they run.
Operators
An operator is just a controller that monitors a CRD instead of a resource defined by Kubernetes.
A controller monitors a set of resources (resources are just entries in the etcd database, representing some kind of "desired state"), and reconciles the database contents with the "actual state" of the system. Whenever the controller sees differences between the desired and actual states, it takes steps to reduce that difference.
Describing a Cluster
Version
kubectl version --short
Master node address
Display address of master node
kubectl cluster-info
View all available resources
kubectl api-resources
Completely Delete a Cluster
kubectl delete --all all kubectl delete secret --all kubectl delete serviceaccount --all kubectl delete pvc --all kubectl delete ingress --all kubectl delete crds --all
Kubeconfig, Context and Namespace
Set namespace for the current context
kubectl config set-context \ $(kubectl config current-context) \ --namespace default
Cluster ID
Clusters don't really have an identifier. Best idea I have seen is to use the kube-system namespace UID. For a discussion, see: Cluster ID API.
kubectl get ns kube-system \ -o jsonpath='{.metadata.uid}'
Affinity, Taints, and Tolerations
Kubernetes supports a way to indicate that specific nodes have a certain quality. Pods must opt-in to using those nodes.
That is, some nodes can say "hey, I'm a little bit different", and then some pods can say "I tolerate that difference".
An example is Azure's serverless pods. You can have physical nodes and virtual (i.e. serverless) nodes. By default, your pods will get scheduled to your physical nodes, but you could mark some pods as "tolerating" the Azure serverless nodes.
Affinity
A Pod can specify an affinity for certain nodes. Can either be a preference or a requirement.
Taints
A Node can have a taint that will not allow Pods to be scheduled to it unless the Pod can tolerate the taint.
Tolerations
A Pod can specify a toleration which allows it (but does not require it) to be scheduled to nodes with a matching taint.
ConfigMaps
kubectl get cm aws-auth \ -n kube-system \ --output=json \ | jq -r '.data'
Nodes
Master Node IP
Note you can also get the master node ip with kubectl cluster-info
.
kubectl get node -lnode-role.kubernetes.io/master \ -o jsonpath='{ $.items[*].status.addresses[?(@.type=="ExternalIP")].address }'
Worker Nodes
kubectl get nodes \ -l "kubernetes.io/role = node"
Worker Node IPs
Private IP
Without jq:
kubectl get nodes -l "kubernetes.io/role = node" \ -o jsonpath='{range .items[*]}{.metadata.annotations.flannel\.alpha\.coreos\.com/public-ip} {end}'
With jq:
kubectl get nodes \ -l "kubernetes.io/role = node" \ -o json \ | jq -r '.items[] .metadata .annotations["flannel.alpha.coreos.com/public-ip"]'
Public IPs
kubectl get nodes -o json \ -l "kubernetes.io/role = node" \ | jq -r '.items[].status.addresses[] | select(.type == "ExternalIP") | .address'
All Pods on a Node
kubectl describe node $NodeName
kubectl get pods --all-namespaces \ --field-selector spec.nodeName=$NodeName
SSH to a Pod's Node
node=$(kubectl get pod \ -o=custom-columns=NODE:.spec.nodeName $applianceConfigPod \ | sed -n 2p) ip=$(kubectl get node $node \ -o jsonpath='{ $.status.addresses[?(@.type=="ExternalIP")].address }')
Now, ssh user@nodeIp
Pods
List Containers in a Pod
kubectl get pod $pod -o json | jq -r '.spec.containers[].name'
What Node is this Pod on?
kubectl get pod \ -o custom-columns=NODE:.spec.nodeName \ $pod \ | sed -n 2p
Secrets
Create Docker Registry Secret
kubectl create secret docker-registry $SecretName \ --docker-server=$Server \ --docker-username=$User \ --docker-password=$Password
Decode Docker Registry Secret
TODO: also decode =.auths["registry.ironnet.cloud"].auth
kubectl get secret $SecretName -o json \ | jq -r '.data[".dockerconfigjson"]' \ | base64 --decode | jq
Update a Secret
kubectl create secret docker-registry $SecretName \ --docker-server=$Server \ --docker-username=$User \ --docker-password=$Password \ --dry-run=client -o yaml | kubectl apply -f -
Services
Get Services of Type
Get all services of type NodePort/LoadBalancer/ClusterIP:
kubectl get services --all-namespaces -o json \ | jq '.items[] | select(.spec.type == "LoadBalancer") | .metadata.name'
Service Accounts
Get the default service account:
kubectl get secret -o json \ | jq '.items[] | select(.metadata.annotations["kubernetes.io/service-account.name"] == "default")'
StorageClass
Get the default StorageClass:
kubectl get sc \ -o json \ | jq '.items[].metadata | select(.annotations."storageclass.kubernetes.io/is-default-class" == "true") | .name'
kubectl get sc \ -o json \ | jq -c '.items[].metadata | { "name": .name, "default?": .annotations."storageclass.kubernetes.io/is-default-class" }'
Running Commands in Cluster
Create a temporary pod
Start a shell in a temporary pod in the namespace you want to test:
kubectl run -it --rm --restart=Never alpine --image=alpine sh apk add curl
Exec in an existing pod
If the pod has multiple containers, use -c $containerName
as well.
kubectl exec $podName -- <COMMAND>
Port forwarding with kubectl
kubectl port-forward $podName 8000:8000 -n $namespace
Using the API directily
https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/#without-kubectl-proxy
APISERVER=$(kubectl config view \ --minify \ -o jsonpath='{.clusters[0].cluster.server}') SA=$(kubectl get serviceaccount default -o jsonpath='{.secrets[0].name}') TOKEN=$(kubectl get secret $SA -o jsonpath='{.data.token}' | base64 --decode ) curl $APISERVER/api --header "Authorization: Bearer $TOKEN" --insecure
Debugging
Startig a busybox pod
kubectl run -i -t busybox --image=busybox --restart=Never
Get a shell on that pod
kubectl exec -it <name-of-pod> -n my-ns sh
etcd
Pod to help with connecting
curl -LO git.io/etcdclient.yaml
Which gets: https://gist.githubusercontent.com/mauilion/2bab4b00eb7a0ab4fca7023ae251e8ee/raw/etcdclient.yaml
Or, just ssh to the master, and download etcdctl on the master node.
Try looking at a secret at /registry/secrets/default/mysecret
Autoscaling
There are two kinds of autoscaling in Kubernetes:
- Scaling the number of Pods in Deployments/StatefulSets
- Scaling the number of Nodes in the Cluster
Scaling Pods
Horizontal Pod Autoscaler
Kubernetes has a built-in autoscaling mechanism called the Horizontal Pod Autoscaler. An HPA can be created and configured using standard Kubernetes objects:
kubectl get hpa
Also, HPAs can be created with the special kubectl autoscale
command:
kubectl autoscale rs foo --min=2 --max=5 --cpu-percent=80
The HPA supports scaling based on metrics exposed through the Kubernetes
Metrics API (i.e. what kubectl top
uses). I think this means you need a
metrics-server pod running in your cluster.