Kubernetes Basics
Note Repository
References
GitHub - fhsinchy/kubernetes-handbook-projects
Useful Code Snippets
# Create 'pod/<pod_name>'
# Run <image_name> inside pod
kubectl run <pod_name> \
--image=<image_name> \
--port=80
# 'ls -la' for k8s
kubectl get pod/service
# Create 'service/<pod_name>'
# Run load balancer as a service
kubectl expose pod <pod_name> \
--type=LoadBalancer \
--port=80
# Delete resource based on type and name
kubectl delete <type> <name>
Basics
🚢 Container orchestration is the process of automating the deployment
, management
, scaling
, and networking
tasks of containers.
Architecture
-
kubectl
: K8s CLI tool to communicate with your cluster via API -
node
: the machine, virtual or physical, that hosts the pods. Usually refers to worker nodes that run the containerized workloads, but could also refer to control plane nodes (centralized API, cluster management).-
kubelet
: primary agent in a node, does things such as start pods & containers or register the node with the apiserver. Receives instructions from the control plane, keeps etcd updated -
kube-proxy
: runs on each node to maintain network rules. Any network request that reaches a service inside the cluster passes through the kube-proxy service
-
-
control plane
: cluster management-
etcd
: data store that maintains a record of the state of the cluster in a distributed key-value store -
kube-scheduler
: queries state of the cluster (at etcd) to schedule/assign workloads to worker nodes, making sure no server is overloaded -
kube-api-server
: gateway to query and interact with cluster. Validates and processes requests delivered using client libraries like kubectl -
cloud-controller-manager
: interacts with cloud provider’s (GKE, EKS) API. Keeps local and cloud interactions isolated -
kube-controller-manager
: responsible for controlling the state of the cluster. Implements and tracks the lifecycle of the controllers deployed to the cluster. Main workloads managed by kube-controller-manager:-
Deployment
: declarative and good for managing stateless apps (pods are interchangeable and replaceable), it matches the current state of your cluster to the desired state mentioned in the Deployment manifest (YAML). e.g. If you create a deployment with 1 replica, it will check that the desired state of ReplicaSet is 1 and current state is 0, so it will create a ReplicaSet, which will further create the pod. -
StatefulSet
: lets you run 1+ pods that track state in some manner. Helps match pods with persistent volumes. -
DaemonSet
: ensures that the pod specified via YAML runs on all the nodes of the cluster. If a node is added/removed from a cluster, DaemonSet automatically adds/deletes the pod. e.g. Installing a monitoring agent on each node.
-
-
Objects
-
pods
: self-contained, easily replicable objects. Can host a container or a group of (very closely related — more than back-end app + db) containers with shared network IP address and filesystem volumes, meant to run a single instance of an application. -
services
: since pods are ephemeral (destroyed and replaced, not recycled — new IP every time), a service is an abstraction that groups a logical set of pods and presents them as a single entity, e.g. LoadBalancer, ClusterIP, NodePort, ExternalName-
LoadBalancer
: exposes a group of pods as one entity to the outside (of the cluster). The pod(s) become(s) exposed on the port specified. Can be accessed viaminikube service <service_name>
, which accesses the cluster’s IP + the ephemeral exposed port, which is mapped to the port exposed by the load balancer. -
ClusterIP
: same as LoadBalancer but only within the cluster
-
-
Controllers
: non-terminating control loops that watch the state of the cluster and make/request changes accordingly. There are controllers to manage each type of workload supported by Kubernetes.
Declarative App Example
Directory Tree
- src /
- (app code)
- k8s /
-
example-pod.yaml
apiVersion: v1 # k8s API version kind: Pod # object type metadata: # useful for future referencing name: example-pod labels: # services select objects via labels component: web spec: # specify state wanted for object containers: - name: example image: example/latest ports: - containerPort: 80
-
example-lb-service.yaml
apiVersion: v1 kind: Service metadata: name: example-lb-service spec: type: LoadBalancer # other options: ClusterIP, NodePort, ExternalName ports: - port: 80 # port for others to access LB pod targetPort: 80 # containers' port (must match) selector: # ID objects connected to the service component: web
-
-
Dockerfile
# copies the whole directory into node image directory # installs package.json with npm, then runs build # copies compiled app into nginx image with port 80 exposed # after that, image can run as container with port 80 publicly accessible
- index.html
- package.json
Execution
# Both 'apply' commands can be consolidated with 'kubectl apply -f <directory>'
kubectl apply -f example-pod.yaml
# pod/example-pod created
kubectl get pod
# NAME READY STATUS RESTARTS AGE
# example-pod 1/1 Running 0 3m3s
kubectl apply -f example-lb-service.yaml
# service/example-lb-service created
kubectl get service
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# example-lb-service LoadBalancer 10.107.231.120 <pending> 80:30848/TCP 7s
# Example if running locally with minikube
minikube service example-lb-service
# |-----------|--------------------|-------------|-----------------------------|
# | NAMESPACE | NAME | TARGET PORT | URL |
# |-----------|--------------------|-------------|-----------------------------|
# | default | example-lb-service | 80 | http://192.168.99.101:30848 |
# |-----------|--------------------|-------------|-----------------------------|
# ? Opening service default/example-lb-service in default browser...
Multi-Container App Example
Architecture
- Note that the ClusterIP works like a LoadBalancer and can point to a single pod or a set of pods, but it can only serve requests from within the cluster
Directory Tree
- app /
- (app source code, including Dockerfile)
- postgres /
- (db Dockerfiles and init file)
- k8s /
-
app-deployment.yaml
apiVersion: apps/v1 # based on docs kind: Deployment metadata: name: app-deployment spec: # controller specs replicas: 3 selector: matchLabels: component: app # all pods with this label will be controlled template: # container specs and metadata metadata: labels: component: app # all pods created will have this label spec: containers: - name: app image: app/latest ports: - containerPort: 3000 env: - name: DB_CONNECTION value: pg - name: DB_HOST # use service name to reach db host value: postgres-cluster-ip-service - name: DB_PORT value: '5432' - name: DB_USER value: postgres - name: DB_DATABASE # db to be used inside host value: notesdb - name: DB_PASSWORD value: 63eaQB9wtLqmNBpg
-
postgres-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: postgres-deployment spec: replicas: 1 selector: matchLabels: component: postgres template: metadata: labels: component: postgres spec: volumes: # pods' storage assignments and claims - name: postgres-storage persistentVolumeClaim: claimName: database-persistent-volume-claim containers: - name: postgres image: postgres/latest ports: - containerPort: 5432 volumeMounts: # connect pod volumes to container - name: postgres-storage mountPath: /var/lib/postgresql/data # container dir subPath: postgres # dir created inside vol for container env: - name: POSTGRES_PASSWORD # used to connect to db via API value: 63eaQB9wtLqmNBpg - name: POSTGRES_DB value: appdb
-
database-persistent-volume.yaml
# Not actually used, just an example apiVersion: v1 kind: PersistentVolume metadata: name: database-persistent-volume spec: storageClassName: manual capacity: storage: 5Gi accessModes: - ReadWriteOnce # mounted as r/w by one single node # - ReadOnlyMany # many nodes can mount it as read only hostPath: # development-specific, look into carefully on creation path: "/mnt/data"
-
database-persistent-volume-claim.yaml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: database-persistent-volume-claim spec: #storageClassName: manual # removing this dynamically creates volume accessModes: - ReadWriteOnce resources: requests: storage: 2Gi
-
postgres-cluster-ip-service.yaml
apiVersion: v1 kind: Service metadata: name: postgres-cluster-ip-service spec: type: ClusterIP selector: component: postgres ports: - port: 5432 targetPort: 5432
-
app-lb-service.yaml
apiVersion: v1 kind: Service metadata: name: app-lb-service spec: type: LoadBalancer ports: - port: 3000 targetPort: 3000 selector: component: app
-
-
docker-compose.yaml
version: "3.8" services: db: build: # custom image w/ app table pre-created context: ./postgres dockerfile: Dockerfile.dev volumes: - db-data:/var/lib/postgresql/data environment: POSTGRES_PASSWORD: 63eaQB9wtLqmNBpg POSTGRES_DB: notesdb app: build: context: ./app dockerfile: Dockerfile.dev ports: - 3000:3000 volumes: - /home/node/app/node_modules - ./app:/home/node/app environment: DB_CONNECTION: pg DB_HOST: db DB_PORT: 5432 DB_USER: postgres DB_DATABASE: notesdb DB_PASSWORD: 63eaQB9wtLqmNBpg volumes: db-data: name: app-db-dev-data
Execution
-
Debug session example:
kubectl apply -f app-deployment.yaml # deployment.apps/app-deployment created kubectl get deployment # NAME READY UP-TO-DATE AVAILABLE AGE # app-deployment 0/3 3 0 18m2s kubectl get -f app-deployment.yaml # NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR # app-deployment 0/3 3 0 19m app app/latest component=app kubectl get pod # NAME READY STATUS RESTARTS AGE # app-deployment-d59f9c884-88j45 0/1 CrashLoopBackOff 10 30m # app-deployment-d59f9c884-96hfr 0/1 CrashLoopBackOff 10 30m # app-deployment-d59f9c884-pzdxg 0/1 CrashLoopBackOff 10 30m kubectl describe pod api-deployment-d59f9c884-88j45 # (Extensive debug info) kubectl logs api-deployment-d59f9c884-88j45 # (Logs for container -- error with image)
-
After fixing image:
kubectl apply -f api-deployment.yaml # deployment.apps/api-deployment configured kubectl get deployment # NAME READY UP-TO-DATE AVAILABLE AGE # app-deployment 3/3 3 3 6m kubectl get pod # NAME READY STATUS RESTARTS AGE # app-deployment-66cdd98546-l9x8q 1/1 Running 0 7m26s # app-deployment-66cdd98546-mbfw9 1/1 Running 0 7m31s # app-deployment-66cdd98546-pntxv 1/1 Running 0 7m21s kubectl apply -f postgres-deployment.yaml # deployment.apps/postgres-deployment created kubectl get deployment # NAME READY UP-TO-DATE AVAILABLE AGE # postgres-deployment 1/1 1 1 13m kubectl get pod # NAME READY STATUS RESTARTS AGE # postgres-deployment-76fcc75998-mwnb7 1/1 Running 0 13m kubectl apply -f database-persistent-volume-claim.yaml # persistentvolumeclaim/database-persistent-volume-claim created kubectl get persistentvolumeclaim # NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE # database-persistent-volume-claim Bound pvc-525ae8af-00d3-4cc7-ae47-866aa13dffd5 2Gi RWO standard 2s kubectl apply -f postgres-cluster-ip-service.yaml kubectl apply -f app-lb-service.yaml
-
Short version:
kubectl apply -f k8s # deployment.apps/app-deployment created # service/app-load-balancer-service created # persistentvolumeclaim/database-persistent-volume-claim created # service/postgres-cluster-ip-service created # deployment.apps/postgres-deployment created kubectl get deployment # NAME READY UP-TO-DATE AVAILABLE AGE # app-deployment 3/3 3 3 106s # postgres-deployment 1/1 1 1 106s
Further Education
- Kubernetes Documentation: go through “Getting Started”, “Tutorials”, “Concepts”.
- Task Catalog: How to do common Kubernetes tasks.
- Reference page - all things API, setup (kubeadm) and component (kubelet, kube-proxy, TLS bootstrapping, etc) tools, kubectl, etc.