Kubernetes Best Practices

Although Kubernetes extends a future-proof container solution to improve productivity, use cases also indicate that relying solely on out-of-the-box Kubernetes services to containerize application builds may not always be the best approach. To get the most out of K8s, implement best practices and follow a custom-configured model to ensure the optimal platform your application build requires.

In this post, we will explore the best practices of Kubernetes and what it should include, as well as with detailed examples.

Contents

Building Small Containers

Most developers make the mistake of using the base image out-of-the-box, which may have up to 80% of packages and libraries they don’t need.

Always use smaller container images as it helps you to create faster builds. As a best practice, you should:

  • Go for Alpine Images, as they are 10x smaller than the base images
  • Add necessary libraries and packages as required for your application. Smaller images are also less susceptible to attack vectors due to a reduced attack surface.

Organizing Kubernetes with Namespaces

apiVersion: v1
kind: Pod
metadata:
name: development
namespace: development
labels:
image: development01
spec:
containers:
- name: development01
image: nginx

Health Checks with Readiness and Liveness Probes

How Kubernetes Probes Work

In Kubernetes, probes are managed by the kubelet. The kubelet performs periodic diagnostics on containers running on the node. In order to support these diagnostics, a container must implement one of the following handlers:

  • ExecAction handler-runs a command inside the container, and the diagnostic succeeds if the command completes with status code 0.
  • TCPSocketAction handler-attempts a TCP connection to the IP address of the pod on a specific port. The diagnostic succeeds if the port is found to be open.
  • HTTPGetAction handler-performs an HTTP GET request, using the IP address of the pod, a specific port, and a specified path. The diagnostic succeeds if the response code returned is between 200–399.

What are the Three Types of Kubernetes Probes?

  • Readiness probe A readiness probe indicates whether the application running on the container is ready to accept requests from clients:
  • If it succeeds, services matching the pod continue sending traffic to the pod
  • If it fails, the endpoints controller removes the pod from all Kubernetes Services matching the pod
  • Liveness probe A liveness probe indicates if the container is operating:
  • If it succeeds, no action is taken and no events are logged
  • If it fails, the kubelet kills the container, and it is restarted in line with the pod restartPolicy
  • Startup probe A startup probe indicates whether the application running in the container has fully started:
  • If it succeeds, other probes start their diagnostics. When a startup probe is defined, other probes do not operate until it succeeds
  • If it fails, the kubelet kills the container, and is restarted in line with the pod restartPolicy

In this example, the probe pings an application to check if it is still running. If it gets the HTTP response, it then marks the pod as healthy.

apiVersion: apps/v1
kind: Deployment
metadata:
name: readiness-example
spec:
...
spec:
containers:
- name: readiness-example
image: dbdock/readiness-example:1.0.0
...
startupProbe:
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 1
successThreshold: 1
failureThreshold: 5
tcpSocket:
host:
port: 8080
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 25
timeoutSeconds: 1
periodSeconds: 2
successThreshold: 3
failureThreshold: 3
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 20
timeoutSeconds: 5
periodSeconds: 5
successThreshold: 1
failureThreshold: 1

initialDelaySeconds : Number of seconds after the container has started before liveness or readiness probes are initiated. Defaults to 0 seconds. Minimum value is 0.

timeoutSeconds : Number of seconds after which the probe times out. Defaults to 1 second. Minimum value is 1.

periodSeconds : How often (in seconds) to perform the probe. Default to 10 seconds. Minimum value is 1.

successThreshold : Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. Must be 1 for liveness and startup Probes. Minimum value is 1.

failureThreshold : When a probe fails, Kubernetes will try failureThreshold times before giving up. Giving up in case of liveness probe means restarting the container. In case of readiness probe the Pod will be marked Unready. Defaults to 3. Minimum value is 1.

Setting Resource Requests and Limits

  • Occasionally deploying an application to a production cluster can fail due limited resources available on that cluster. This is a common challenge when working with a Kubernetes cluster and it’s caused when resource requests and limits are not set. Without resource requests and limits, pods in a cluster can start utilizing more resources than required. If the pod starts consuming more CPU or memory on the node, then the scheduler may not be able to place new pods, and even the node itself may crash.
  • Resource requests specify the minimum amount of resources a container can use
  • Resource limits specify the maximum amount of resources a container can use.

In this example, we have set the limit of CPU to 500 millicores and memory to 256 mebibytes. The maximum request which the container can make at a time is 200 millicores of CPU and 128 mebibyte of memory.

containers:
- name: container1
image: dfx/readiness-example:1.0.0
resources:
requests:
memory: “128Mi”
cpu: “200m”
limits:
memory: “256Mi”
cpu: “500m”
  • The CPU is known as a compressible resource, so when your application withstands these CPU limits, k8s will start throttling the application’s CPU usage, so that only the performance of the application will decrease. The application will not be terminated.
  • However, since Memory is not a compressible resource, the application will be terminated when it reaches or exceeds the given limit.

Use Labels

For example, let’s say you are running two instances of one type of application. Both are similarly named, but each application is used by different teams (e.g., development and testing). You can help your teams differentiate between the similar applications by defining a label which uses their team’s name to demonstrate ownership.

kind: Pod
metadata:
name: ops-pod
labels:
environment: operations
team: ops01
spec:
containers:
- name: ops01
image: "Ubuntu"
resources:
limits:
cpu: 1

Enable RBAC

Role-based access control (RBAC) is an approach used to restrict access and admittance to users and applications on the system or network. An RBAC Role or ClusterRole contains rules that represent a set of permissions. Permissions are purely additive (there are no “deny” rules).

A Role always sets permissions within a particular namespace; when you create a Role, you have to specify the namespace it belongs in.

ClusterRole, by contrast, is a non-namespaced resource. The resources have different names (Role and ClusterRole) because a Kubernetes object always has to be either namespaced or not namespaced; it can’t be both.

if you want to define a role within a namespace, use a Role; if you want to define a role cluster-wide, use a ClusterRole.

Role example

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]

ClusterRole example

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" omitted since ClusterRoles are not namespaced
name: secret-reader
rules:
- apiGroups: [""]
#
# at the HTTP level, the name of the resource for accessing Secret
# objects is "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]

Autoscale

It is strongly recommended that you leverage benefits from Kubernetes’ autoscaling mechanisms to automatically scale cluster services with a surge in resource consumption.

With Horizontal Pod Autoscaler and Cluster Autoscaler, node and pod volumes get adjusted dynamically in real-time, thereby maintaining load to the optimum level and avoiding capacity downtimes.

--

--

--

Senior Software Developer at DefineX

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Deploy Spring Boot application into the AWS infrastructure

Python versus Linux: First

Jenkins — The Heart of DevOps Automation

Journey With Django Signals

QRM Solution Migration to AWS

Build Your Python Image With Dockerfile

Intro to Django

Spring Boot Threads Wihtout In Heap Queue

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Onur Cil

Onur Cil

Senior Software Developer at DefineX

More from Medium

Critical factors for DevOps to consider when moving into Microservices

Deploying Apps on Kubernetes: Understanding Helm

5 DevOps Tools to Level Up Your Kubernetes

How to write a YAML file for Kubernetes? | ARMO