CKA Self-Study Mod 3
In this module of the RX-M online CKA prep course, we will be covering the services and networking topics identified by the CNCF CKA Exam Curriculum. If you are not already familiar with the curriculum, take a moment to familiarize yourself, as you will be required to know each of the topics in order to pass the test.
Services and Other Network Primitives
A Service is an abstraction of a logical set of pods and a policy that defines inbound and network access. A service uses a selector to target pods by the pods’ label. A service exposes a logical set of pods as a network service providing a single IP address, DNS name, or load balancing to access the pods.
The Service type is defined in the manifest. The following are available Service types:
- ClusterIP - exposes the Service on an internal IP in the Kubernetes cluster (default)
- NodePort - exposes the Service on the same port of each node in the Kubernetes cluster
- LoadBalancer - creates an external load balancer with a cloud provider (e.g. GCE ForwardingRules, AWS Elastic Load Balancer, Azure Load Balancer) and assigns a public IP to the Service
- ExternalName - exposes the Service using an arbitrary name
Services can be created imperatively for a running resource. At minimum the resource type, resource name, and the service’s exposed proxy port are required e.g.
kubectl expose <resource> <resource_name> --port=<port number>.
$ kubectl create deploy webserver --image nginx deployment.apps/webserver created $ kubectl expose deploy webserver --port 80 service/webserver exposed $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 443/TCP 33d webserver ClusterIP 10.103.175.171 80/TCP 4s $
Services select pods using labels, and for each pod creates an endpoint resource. The endpoint resource describes all active network targets (pods) that the service routes traffic to. Each endpoint object in a cluster places an additional iptables rule with a target pod’s IP. An alternative to endpoints are EndpointSlices. EndpointSlices are conceptually and functionally similar to endpoints, but are restricted to up to 100 endpoints to improve management at scale.
$ kubectl get endpoints webserver NAME ENDPOINTS AGE webserver 10.32.0.8:80 43s $ kubectl get pods -o wide -l app=webserver NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES webserver-d698d7bd6-ktxvn 1/1 Running 0 83s 10.32.0.8 ubuntu $
Ingresses are another resource that interact with Services. Ingresses bind Services to external endpoints that an Ingress controller on the cluster then exposes to the outside world. Ingresses reference Services directly in their manifests, as shown here:
apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: webserver-ingress annotations: spec: rules: - http: paths: - path: /testpath backend: serviceName: webserver servicePort: 80
Learn more about:
Ingress Controllers and Ingress Resources
The Ingress resource manages external access to Kubernetes services via HTTP and HTTPS routes. An Ingress controller is required to satisfy an Ingress. The Ingress controller reads and implements the rules of the Ingress resource.
Use the following command to set up an Ingress Controller in your Kubernetes cluster:
$ kubectl apply -f https://rx-m-k8s.s3-us-west-2.amazonaws.com/ingress-drill-setup.yaml namespace/nginx-ingress created serviceaccount/nginx-ingress created clusterrole.rbac.authorization.k8s.io/nginx-ingress created service/nginx-ingress created clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress created secret/default-server-secret created deployment.apps/nginx-ingress created $
Roles and ClusterRoles are assigned to users and processes using RoleBindings and ClusterRoleBindings. RoleBindings associate a user, like a Service Account, with a Role. Any permissions granted by a role are passed to the user through the RoleBinding.
Create the following Deployment of Apache webserver that exposes the container port 80:
$ kubectl create deploy apache-webserver --image=httpd --port=80 deployment.apps/apache-webserver created $
Create a NodePort Service to expose the
apache-webserver Deployment on the node port 30111 and maps port 80 on the ClusterIP to port 80 on the container:
$ kubectl create service nodeport apache-webserver --tcp=80:80 --node-port=30111 service/apache-webserver created $
Create the following Ingress resource for the
apache-webserver Service that controls traffic to the host domain www.example.com, exposes an http prefix path to /, routes all traffic sent to www.example.com:30111/ to the
apache-webserver Service on port 80:
$ nano apache-webserver-ingress.yaml && apache-webserver-ingress.yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: apache-weberver-ingress spec: rules: - host: www.example.com http: paths: - pathType: Prefix path: / backend: service: name: apache-webserver port: number: 80 $ kubectl apply -f apache-webserver-ingress.yaml ingress.networking.k8s.io/apached-webserver-ingress created $
Test the Ingress rules with
curl --resolve www.example.com:30111: http://www.example.com:30111/:
$ curl --resolve www.example.com:30111: http://www.example.com:30111 <h1>It works!</h1> $
Learn more about:
Service Discovery with CoreDNS
Kubernetes uses CoreDNS for DNS-based service discovery. CoreDNS is flexible and changes can be made in the ConfigMap for CoreDNS.
Every Service is assigned with a DNS name in the syntax:
Pods are assigned a DNS A record in the syntax of:
Let’s confirm the DNS entry of a service with a name server lookup with
nslookup from within a pod.
Create a ClusterIP Service to test its DNS entry and retrieve it’s ClusterIP:
$ kubectl create service clusterip my-service --tcp=8080:8080 service/my-service created $ kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 443/TCP 10d my-service ClusterIP 10.109.165.220 8080/TCP 5m31s $
Run a pod with the
busybox image and run a nslookup on the Service’s IP:
$ kubectl run busybox --image=busybox -it -- /bin/sh If you don’t see a command prompt, try pressing enter. / # nslookup 10.109.165.220 Server: 10.96.0.10 Address: 10.96.0.10:53 18.104.22.168.in-addr.arpa name = my-service.default.svc.cluster.local / # exit $
Learn more about:
Manage TLS Certificates for Cluster Components
All cluster components that need to communicate with the API server must authenticate using a certificate signed by the cluster CA certificate. Each CA certificate must contain the user as a subject name and a group as an organization. The CA certificate that signs all of a kubeadm cluster’s component certificates are found under
Kubeadm clusters automatically distribute cluster CA signed certificates to all control plane components at bootstrap. The cluster certificates are temporarily stored in the cluster as secrets for up to 2 hours after bootstrap. To reupload the cluster certificates to create a new master node, kubeadm can rerun the upload-certs phase:
$ sudo kubeadm init phase upload-certs --upload-certs [upload-certs] Storing the certificates in Secret "kubeadm-certs" in the "kube-system" Namespace [upload-certs] Using certificate key: 7d42b0fbecf1f12597591513e6b1e1009fd46bd617f33679c050abe30310b006 $
Then, create a
join command on the master node using the certificate key to generate a join command for additional control plane nodes:
$ sudo kubeadm token create --print-join-command --certificate-key 7d42b0fbecf1f12597591513e6b1e1009fd46bd617f33679c050abe30310b006 kubeadm join 192.168.229.134:6443 \ --token yrl04z.14yaclt7m8hljjpw \ --discovery-token-ca-cert-hash sha256:50fecf38c50b760131e7ff3ae6c80d89aa01243e9c6c1d634077eedeb4940929 \ --control-plane \ --certificate-key 7d42b0fbecf1f12597591513e6b1e1009fd46bd617f33679c050abe30310b006 $
This join command instructs kubeadm to have the new worker nodes download the certificates.
For worker nodes, the process is the same; using kubeadm join instructs the target node’s kubelet to perform a TLS bootstrap to automatically request a new certificate to the cluster.
Learn more about:
Work with Images Securely
Image security is handled in different ways. One way is to control access to private repositories using
imagePullSecret, which contains the necessary credentials to access a repository. An image pull secret is based on Docker’s config.json, which is created after using
docker login. You can create an
imagePullSecret imperatively by supplying your credentials to:
$ kubectl create secret docker-registry myregistry \ --docker-server=https://my.image.registry \ --docker-username=my-user --docker-password=my-pw \ --firstname.lastname@example.org $
Container images that need to be pulled from the my.image.registry private registry retrieve those credentials using the
imagePullSecret key in their spec:
apiVersion: v1 kind: Pod metadata: creationTimestamp: null labels: run: fluentbitcommandpod name: fluentbitcommandpod spec: containers: - command: - /fluent-bit/bin/fluent-bit - -i - mem - -o - stdout image: myregistry/my-fluent-bit name: fluentbitcommandpod imagePullSecrets: - name: myregistry
Container images can be referred to using the sha256 hash of the image. This tells the container runtime to use an exact version of the image at all times. Here is an example of updating a Kubernetes deployment using a specific image SHA:
kubectl set image deploy nginx-prod nginx=myregistry/nginx@sha256:2397b05f8a7df1cf48d51314a5e2c249f1e83a4211cd78ceb58e8372bf087f07 --record=true
Security contexts define privilege and access control settings for a Pod or Container. A
securityContext array in a container spec under a pod enables granular control over the user or group a container runs with, the permissions granted to those users, and other options like filesystem access or the ability to run as root.
To specify a securityContext, include the
securityContext key inside a pod or container manifest:
apiVersion: v1 kind: Pod metadata: name: cka-security-context spec: containers: - name: sec-ctx-demo image: busybox command: [ "sh", "-c", "sleep 1h" ] securityContext: allowPrivilegeEscalation: false securityContext: runAsUser: 1000
Security contexts allow adjustment of the pod and container security posture and capability. For example, the pod in the spec above runs as a non-root user, and the container is not allowed to use privilege escalation (mechanisms like sudo).
Learn more about configuring pod and container security contexts
Secure Persistent Key Value Store
The persistent key-value store in Kubernetes is etcd. Only the API server has access to the etcd instance running in a cluster. Access to etcd is restricted to principals bearing a certificate signed by the etcd CA. In kubeadm clusters, the etcd certificates are found under
A client must provide the CA certificate and a client key and certificate to contact the etcd instance from outside the Kubernetes cluster. One surefire way do this is by imitating the API server’s access:
$ ps -ef | grep "kube-apiserver" root 3288 3219 1 Feb25 ? 00:23:11 kube-apiserver … --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key --etcd-servers=https://127.0.0.1:2379 ... $
Etcd uses its own CA certificate; any clients that need to connect to etcd must have a certificate signed by this CA to communicate with etcd. By providing those certificates, you can use an external client like etcdctl to interact with the etcd cluster:
$ sudo etcdctl member list \ --endpoints 127.0.0.1:2379 \ --cacert /etc/kubernetes/pki/etcd/ca.crt \ --cert /etc/kubernetes/pki/apiserver-etcd-client.crt \ --key /etc/kubernetes/pki/apiserver-etcd-client.key f093f1e641b93448, started, ubuntu, https://192.168.229.134:2380, https://192.168.229.134:2379, false $
Run the following command:
kubectl run --generator run-pod/v1 --image nginx nginx-drill
Create a NodePort Service that allows you to send a curl request to the nginx-drill pod at port 80 through your machine’s IP address.
As another exercise, create a ClusterIP Service called
kubectl create and use a label selector to associate it with the nginx-drill Deployment created above.