CKAD Exam Preparation 4/4 - Deployments, Services and Networking
▶️ Introduction
This part outlines the key deployment and networking aspects of the CKAD Exam Curriculum. To learn more about the CKAD Exam please read this overview.
About this Series
During this blog series I summarize the main “study hooks” in order to be successful with your exam, as I was. The series is composed by the following articles:
- Part 1. Cross-Cutting Aspects
- Part 2. Pods and Jobs.
- Part 3. Configuration and Volumes.
- Part 4. Deployments, Services and Networking.
All the examples have been developed using minikube on macOS Catalina with VirtualBox.
🖥️ Deployments
📘 https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
Create a new Deployment boilerplate via dry-run
:
kubectl create deployment ex1 --image=nginx --dry-run=client -o=yaml > dep1.yaml
Then such deployment can be tuned, for instance setting the number of desired replicas:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: ex1
name: ex1
namespace: jmcf
spec:
replicas: 3
selector:
matchLabels:
app: ex1
strategy: {}
template:
metadata:
labels:
app: ex1
spec:
containers:
- image: nginx
name: nginx
Check Deployment status:
kubectl get deployments/ex1 -n jmcf -o=wide -w
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
ex1 3/3 3 3 2m18s nginx nginx app=ex1
kubectl get rs -n jmcf -o=wide --selector='app=ex1'
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
ex1-678d4cb9c5 3 3 3 5m23s nginx nginx app=ex1,
📌 A Replica Set is created as a subsidiary of a Deployment.
List the Pods associated to a Deployment:
kubectl get pods -n jmcf --selector='app=ex1' -o=wide
NAME READY STATUS RESTARTS AGE IP
ex1-678d4cb9c5-lx7sx 1/1 Running 0 7m30s 172.17.0.32
ex1-678d4cb9c5-lzlkk 1/1 Running 0 7m30s 172.17.0.47
ex1-678d4cb9c5-zmknk 1/1 Running 0 7m30s 172.17.0.48
📌 A Deployment is based on a templated Pod. There should be as many Pods as desired replicas.
📌 A delete of a Deployment is on cascade, i.e. all subsidiary Pods will be deleted.
A Deployment can be updated just through kubectl edit
:
kubectl edit -n jmcf -f dep1.yaml
📌 When you edit a Deployment a new revision might be created, allowing you to rollback later to a previous configuration.
Update Deployment image:
kubectl set image deployment/ex1 nginx=nginx:1.9.1 -n jmcf
kubectl get rs -n jmcf -o=wide --selector='app=ex1'
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
ex1-678d4cb9c5 0 0 0 3h30m nginx nginx app=ex1
ex1-79c777cf98 3 3 3 171m nginx nginx:1.9.1 app=ex1
📌 Changing the image of a Deployment implies the creation of a new Replica Set.
Rollout
To check the rollout status of a Deployment:
kubectl rollout status deploy ex1 -n jmcf -w
📌 maxSurge
and maxUnavailable
(spec.strategy
) are two important parameters that govern how the rollout process is conducted.
Display rollout history of a Deployment:
kubectl rollout history deployments/ex1 -n jmcf
Rolling back to a previous revision:
kubectl rollout undo deployments/ex1 --to-revision=1 -n jmcf
📌 A rollback generates a new revision.
Scaling out a Deployment:
kubectl scale deployments/ex1 --replicas=5 -n jmcf
📌 Scaling out a Deployment does not generate a new revision.
Auto scale an existing Deployment:
kubectl autoscale deployments/ex1 --min=5 --max=10 --cpu-percent=80 -n jmcf
📌 Requesting autoscaling implies the creation of an Horizontal Pod Autoscaler.
📌 An Horizontal Pod Autoscaler takes precedence over a Replica Set.
List the existing Horizontal Pod Autoscalers:
kubectl get hpa -n jmcf
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
ex1 Deployment/ex1 <unknown>/80% 5 10 5 2m57s
Remove Horizontal Autoscaling:
kubectl delete hpa/ex1 -n jmcf
🧱 Services
📘 https://kubernetes.io/docs/concepts/services-networking/service
Headless Service
Create a headless (without Cluster IP) Service:
kubectl create service clusterip my-service --tcp=80:80 --clusterip="None" --dry-run=client -o=yaml > service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: ex1
name: my-service
namespace: jmcf
spec:
clusterIP: None
ports:
- name: 80-80
port: 80
protocol: TCP
targetPort: 80
selector:
app: ex1
type: ClusterIP
📌 The name of a Service object must be a valid DNS label name.
📌 The ports exposed under a headless Service are just Pod’s container(s) ports
📌 A Service is assigned a DNS name, usually in the form <service-name>.<namespace>.svc.cluster.local
📌 A Service groups Pods based on matching labels.
kubectl describe service my-service -n jmcf
Name: my-service
Namespace: jmcf
Labels: app=ex1
Annotations: Selector: app=ex1
Type: ClusterIP
IP: None
Port: 80-80 80/TCP
TargetPort: 80/TCP
Endpoints: 172.17.0.47:80,172.17.0.48:80,172.17.0.49:80
Session Affinity: None
Events: <none>
Test that my-service
has been configured properly:
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service.jmcf
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service.jmcf.svc.cluster.local
Cluster IP Service
Create a Cluster IP Service
kubectl create service clusterip my-service --tcp=8080:80 --dry-run=client -o=yaml > service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: ex1
name: my-service-cip
namespace: jmcf
spec:
ports:
- name: 8080-80
port: 8080
protocol: TCP
targetPort: 80
selector:
app: ex1
type: ClusterIP
📌 targetPort
(the second element in the --tcp
parameter) is the Pod’s container port.
kubectl describe service my-service-cip -n jmcf
Name: my-service-cip
Namespace: jmcf
Labels: app=ex1
Annotations: Selector: app=ex1
Type: ClusterIP
IP: 10.96.72.170
Port: 8080-80 8080/TCP
TargetPort: 80/TCP
Endpoints: 172.17.0.47:80,172.17.0.48:80,172.17.0.49:80
Session Affinity: None
Events: <none>
Test that the Cluster IP has been assigned properly:
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://10.96.72.170:8080
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service-cip:8080
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service-cip.jmcf:8080
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service-cip.jmcf.svc.cluster.local:8080
Node Port Service
Create Node Port Service
kubectl create service nodeport my-service-np --tcp=8080:80 --dry-run=client -o=yaml > service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
labels:
app: ex1
name: my-service-np
namespace: jmcf
spec:
ports:
- name: 8080-80
port: 8080
protocol: TCP
targetPort: 80
selector:
app: ex1
type: NodePort
kubectl describe service my-service-np -n jmcf
Name: my-service-np
Namespace: jmcf
Labels: app=ex1
Annotations: Selector: app=ex1
Type: NodePort
IP: 10.106.215.220
Port: 8080-80 8080/TCP
TargetPort: 80/TCP
NodePort: 8080-80 31460/TCP
Endpoints: 172.17.0.47:80,172.17.0.48:80,172.17.0.49:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
Test that the Node Port has been assigned properly:
wget -O - http://192.168.99.101:31460
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://10.106.215.220:8080
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service-np:8080
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service-np.jmcf:8080
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://my-service-np.jmcf.svc.cluster.local:8080
kubectl run test1 -it --rm=true --image=busybox --restart=Never -n jmcf -- wget -O - http://172.17.0.47
🌐 Networking
Ingress
📘 https://kubernetes.io/docs/concepts/services-networking/ingress/
📌 Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster.
📌 You must have an ingress controller to satisfy an Ingress. Only creating an Ingress
resource has no effect.
An Ingress which enables reverse proxying to your Service from a canonical address:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
namespace: jmcf
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: ckad.example.org
http:
paths:
- path: /ex1
backend:
serviceName: my-service-cip
servicePort: 8080
Then you can get access to your Service through (provided external DNS entry or etc/hosts
has been set up):
curl http://ckad.example.org/ex1
📌 The annotation nginx.ingress.kubernetes.io/rewrite-target: /
enables reverse proxying.
Network Policies
📘 https://kubernetes.io/docs/concepts/services-networking/network-policies/
📌 To use Network Policies, you must be using a networking solution (such as Calico, Cilium or Flannel) which supports NetworkPolicy
. Creating a NetworkPolicy
resource without a controller that implements it will have no effect.
Define a Network Policy that allows to talk to, our previously defined, ex1
Pods only from containers belonging to the jmcf
Namespace which are labeled as role=test
.
kubectl label namespace jmcf 'project=ckad'
📌 Namespace selector needs matching labels.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: jmcf
spec:
podSelector:
matchLabels:
app: ex1
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
project: ckad
- podSelector:
matchLabels:
role: test
ports:
- protocol: TCP
port: 80
kubectl describe NetworkPolicy test-network-policy -n jmcf
Name: test-network-policy
Namespace: jmcf
Created on: 2020-06-25 14:03:55 +0200 CEST
Labels: <none>
Annotations: Spec:
PodSelector: app=ex1
Allowing ingress traffic:
To Port: 80/TCP
From:
NamespaceSelector: project=ckad
From:
PodSelector: role=test
Not affecting egress traffic
Policy Types: Ingress
📌 A Network Policy defines white lists for ingress traffic, egress traffic or both.
📌 A Network Policy applies to certain Pods (those matching labels) in a Namespace.
📌 A Network Policy ingress or egress rules determines from or to which Pods and/or Namespaces traffic is allowed.
📌 If you declare Ingress
or Egress
policy types (under policyTypes
), and no rule is provided under that category, then no traffic of such category will be allowed.