Oct 12, 2022

15 Kubernetes Best Practices Every Developer Should Know

 In this post, I will run through some best practices to use when using Kubernetes (K8s).

As the most popular container orchestration system, K8s is the de-facto standard for the modern cloud engineer to get to grips with. K8s is a notoriously complex system to use and maintain, so getting a good grasp of what you should and should not be doing, and knowing what is possible will get your deployment off to a solid start.

These recommendations cover common issues within 3 broad categories, application development, governance, and cluster configuration.

Kubernetes Best Practices:

Use Namespaces

Namespaces in K8s are important to utilize in order to organize your objects, create logical partitions within your cluster, and for security purposes. By default, there are 3 namespaces in a K8s cluster, default, kube-public and kube-system.

RBAC can be used to control access to particular namespaces in order to limit the access of a group to control the blast-radius of any mistakes that might occur, for example, a group of developers may only have access to a namespace called dev , and have no access to the production namespace. The ability to limit different teams to different namespaces can be valuable to avoid duplicated work or resource conflict.

LimitRange objects can also be configured against namespaces to define the standard size for a container deployed in the namespace. ResourceQuotas can also be used to limit the total resource consumption of all containers inside a Namespace. Network policies can be used against namespaces to limit traffic between pods.

Use Readiness and Liveness Probes

Readiness and Liveness probes are essentially types of health checks. These are another very important concept to utilize in K8s.

Readiness probes ensure that requests to a pod are only directed to it when the pod is ready to serve requests. If it is not ready, then requests are directed elsewhere. It is important to define the readiness probe for each container, as there are no default values set for these in K8s. For example, if a pod takes 20 seconds to start and the readiness probe was missing, then any traffic directed to that pod during the startup time would cause a failure. Readiness probes should be independent and not take into account any dependencies on other services, such as a backend database or caching service.

Liveness probes test if the application is running in order to mark it as healthy. For example, a particular path of a web app could be tested to ensure it is responding. If not, the pod will not be marked as healthy and the probe failure will cause the kubeletto launch a new pod, which will then be tested again. This type of probe is used as a recovery mechanism in case the process becomes unresponsive.

Use Autoscaling

Where it is appropriate, autoscaling can be employed to dynamically adjust the number of pods (horizontal pod autoscaler), the amount of resources consumed by the pods (vertical autoscaler), or the number of nodes in the cluster (cluster autoscaler), depending on the demand for the resources.

The horizontal pod autoscaler can also scale a replication controller, replica set, or stateful set based on CPU demand.

Using scaling also brings some challenges, such as not storing persistent data in the container’s local filesystem, as this would prevent horizontal autoscaling. Instead, a PersistentVolume could be used.

The cluster autoscaler is useful when highly variable workloads exist on the cluster that may require different amounts of resources at different times based on demand. Removing unused nodes automatically is also a great way to save money!

Use Resource Requests and Limits

Resource requests and limits (minimum and maximum amount of resources that can be used in a container) should be set to avoid a container starting without the required resources assigned, or the cluster running out of available resources.

Without limits, pods can utilize more resources than required, causing the total available resources to be reduced which may cause a problem with other applications on the cluster. Nodes may crash, and new pods may not be able to be placed corrected by the scheduler.

Without requests, if the application cannot be assigned enough resources, it may fail when attempting to start or perform erratically.

Resource requests and limits define the amount of CPU and Memory available in millicores and mebibytes. Note that if your process goes over the memory limit, the process is terminated, so it may not always be appropriate to set this in all cases. If your container goes over the CPU limit, the process is throttled.

Deploy Your Pods as Part of a Deployment, DaemonSet, ReplicaSet, or StatefulSet Across Nodes.

A single pod should never be run individually. To improve fault tolerance, instead, they should always be part of a Deployment, DaemonSet, ReplicaSet or StatefulSet. The pods can then be deployed across nodes using anti-affinity rules in your deployments to avoid all pods being run on a single node, which may cause downtime if it was to go down.

Use Multiple Nodes

Running K8s on a single node is not a good idea if you want to build in fault tolerance. Multiple nodes should be employed in your cluster so workloads can be spread between them.

Use Role-based Access Control (RBAC)

Using RBAC in your K8s cluster is essential to properly secure your system. Users, Groups, and Service accounts can be assigned permissions to perform permitted actions on a particular namespace (a Role), or to the entire cluster (ClusterRole). Each role can have multiple permissions. To tie the defined roles to the users, groups, or service accounts, RoleBinding or ClusterRoleBinding objects are used.

RBAC roles should be set up to grant using the principle of least privilege, i.e. only permissions that are required are granted. For example, the admins group may have access to all resources, and your operators group may be able to deploy, but not be able to read secrets.

Host Your Kubernetes Cluster Externally (Use a Cloud Service)

Hosting a K8s cluster on your own hardware can be a complex undertaking. Cloud services offer K8s clusters as platform as a service (PaaS), such as AKS (Azure Kubernetes Service) on Azure, or EKS (Amazon Elastic Kubernetes Service) on Amazon Web Services. Taking advantage of this means the underlying infrastructure will be managed by your cloud provider, and tasks around scaling your cluster, such as adding and removing nodes can be much more easily achieved, leaving your engineers to the management of what is running on the K8s cluster itself.

Upgrade Your Kubernetes Version

As well as introducing new features, new K8s versions also include vulnerability and security fixes, which make it important to run an up-to-date version of K8s on your cluster. Support for older versions will likely not be as good as newer ones.

Migrating to a new version should be treated with caution however as certain features can be depreciated, as well as new ones added. Also, the apps running on your cluster should be checked that they are compatible with the newer targeted version before upgrading.

Monitor Your Cluster Resources and Audit Policy Logs

Monitoring the components in the K8s control plane is important to keep resource consumption under control. The control plane is the core of K8s, these components keep the system running and so are vital to correct K8s operations. Kubernetes API, kubelet, etcd, controller-manager, kube-proxy and kube-dns make up the control plane.

Control plane components can output metrics in a format that can be used by Prometheus, the most common K8s monitoring tool.

Automated monitoring tools should be used rather than manually managing alerts.

Audit logging in K8s can be turned on whilst starting the kube-apiserver to enable deeper investigation using the tools of your choice. The audit.log will detail all requests made to the K8s API and should be inspected regularly for any issues that might be a problem on the cluster. The Kubernetes cluster default policies are defined in the audit-policy.yaml file and can be amended as required.

A log aggregation tool such as Azure Monitor can be used to send logs to a log analytics workspace from AKS for future interrogation using Kusto queries. On AWS Cloudwatch can be used. Third-party tools also provide deeper monitoring functionality such as Dynatrace and Datadog.

Finally, a defined retention period should be in place for the logs, around 30–45 days is common.

Use a Version Control System

K8s configuration files should be controlled in a version control system (VCS). This enables a raft of benefits, including increased security, enabling an audit trail of changes, and will increase the stability of the cluster. Approval gates should be put in place for any changes made so the team can peer-review the changes before they are committed to the main branch.

Use a Git-based Workflow (GitOps)

Successful deployments of K8s require thought on the workflow processes used by your team. Using a git-based workflow enables automation through the use of CI/CD (Continuous Integration / Continuous Delivery) pipelines, which will increase application deployment efficiency and speed. CI/CD will also provide an audit trail of deployments. Git should be the single source of truth for all automation and will enable unified management of the K8s cluster. You can also consider using a dedicated infrastructure delivery platform such as Spacelift, which recently introduced Kubernetes support.

Reduce the Size of Your Containers

Smaller image sizes will help speed up your builds and deployments and reduce the amount of resources the containers consumed on your K8s cluster. Uneccesery packages should be removed where possible, and small OS distribution images such as Alpine should be favored. Smaller images can be pulled faster than larger images, and consume less storage space.

Following this approach will also provide security benefits as there will be fewer potential vectors of attack for malicious actors.

Organize Your Objects with Labels

K8s labels are key-value pairs that are attached to objects in order to organize your cluster resources. Labels should be meaningful metadata that provide a mechanism to track how different components in the K8s system interact.

Recommended labels for pods in the official K8s documentation include name, instance, version, component, part-of, and managed-by.

Labels can also be used in a similar way to using tags in a cloud environment on resources in order to track things related to the business, such as object ownership, and the environmentan object should belong to.

Also recommended is to use labels to detail security requirements, including confidentiality and compliance.

Use Network Policies

Network policies should be employed to restrict traffic between objects in the K8s cluster. By default, all containers can talk to each other in the network, something that presents a security risk if malicious actors gain access to a container, allowing them to traverse objects in the cluster. Network policies can control traffic at the IP and port level, similar to the concept of security groups in cloud platforms to restrict access to resources. Typically, all traffic should be denied by default, then allow rules should be put in place to allow required traffic.

Use a Firewall

As well as using network policies to restrict internal traffic on your K8s cluster, you should also put a firewall in front of your K8s cluster in order to restrict requests to the API server from the outside world. IP addresses should be whitelisted and open ports restricted.

Key Points

Following the best practices listed in this article when designing, running, and maintaining your Kubernetes cluster will put you on the path to success on your modern application journey!

Feb 15, 2022

[DevOps] Create your first CI/CD pipeline!!

 

[DevOps] Create your first CI/CD pipeline!!

What is CI/CD pipeline?

A CI/CD pipeline is a series of steps that must be performed in order to deliver a new version of software.A CI/CD pipeline introduces monitoring and  to improve the process of application development, particularly at the integration and testing phases, as well as during delivery and deployment. Although it is possible to manually execute each of the steps of a CI/CD pipeline, the true value of CI/CD pipelines is realized through automation.

Elements of a CI/CD pipeline

  • Build — The stage where the application is compiled.
  • Test — The stage where code is tested. Automation here can save both time and effort.
  • Release — The stage where the application is delivered to the repository.
  • Deploy — In this stage code is deployed to production.
  • Validation and compliance — The steps to validate a build are determined by the needs of your organization. Image security scanning tools, like , can ensure the quality of images by comparing them to known .

Tools

Overview

This is what we will create in this article.

What we use?

  • Ec2 Ubuntu
  • Java, Jenkins, Maven
  • Tomcat
  • Docker, Jira
  • DefectDojo

Setup

1: prepare 2 Ubuntu server . Name one for “Jenkins”. one for “Tomcat” Recommend more than t2.small instance type.

2: make sure you can ssh into the both servers.

Setting up Jenkins server

1: update

sudo apt-get update -y

2: Install Java (Java Runtime Environment)

sudo apt search openjdk

3: Install the JDK

sudo apt-get install default-jdk -y

4: Check your version

ubuntu@jenkins:~$ javac -version
javac 11.0.13
ubuntu@jenkins:~$ java -version
openjdk version "11.0.13" 2021-10-19
OpenJDK Runtime Environment (build 11.0.13+8-Ubuntu-0ubuntu1.20.04)
OpenJDK 64-Bit Server VM (build 11.0.13+8-Ubuntu-0ubuntu1.20.04, mixed mode, sharing)

5: Add the Jenkins Repository

curl -fsSL  | sudo tee \
/usr/share/keyrings/jenkins-keyring.asc > /dev/null
echo deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
https://pkg.jenkins.io/debian binary/ | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/null

6: install Jenkins

sudo apt updatesudo apt-get install jenkins

7: confirm it is running.

ubuntu@jenkins:~$ sudo systemctl status jenkins
● jenkins.service - LSB: Start Jenkins at boot time
Loaded: loaded (/etc/init.d/jenkins; generated)
Active: active (exited) since Fri 2021-12-31 23:28:46 UTC; 57s ago
Docs: man:systemd-sysv-generator(8)
Tasks: 0 (limit: 2355)
Memory: 0B
CGroup: /system.slice/jenkins.service
Dec 31 23:28:45 jenkins systemd[1]: Starting LSB: Start Jenkins at boot time...
Dec 31 23:28:45 jenkins jenkins[4565]: Correct java version found
Dec 31 23:28:45 jenkins jenkins[4565]: * Starting Jenkins Automation Server jenkins
Dec 31 23:28:45 jenkins su[4599]: (to jenkins) root on none
Dec 31 23:28:45 jenkins su[4599]: pam_unix(su-l:session): session opened for user jenkins by (uid=0)
Dec 31 23:28:45 jenkins su[4599]: pam_unix(su-l:session): session closed for user jenkins
Dec 31 23:28:46 jenkins jenkins[4565]: ...done.
Dec 31 23:28:46 jenkins systemd[1]: Started LSB: Start Jenkins at boot time.

Also check the IP + port 8080 to see the console.

Please enter this command to check the password.

sudo cat /var/lib/jenkins/secrets/initialAdminPassword

8:install the suggested plugins

After the plugins installation, go to the console and choose “manage jenkins”

we need these ones. Start installing without restart

After completing that,

9:Install Docker

Update the apt package index and install packages to allow apt to use a repository over HTTPS:

sudo apt-get updatesudo apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release

Add Docker’s official GPG key:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

Use the following command to set up the stable repository. To add the nightly or test repository, add the word nightly or test (or both) after the word stable in the commands below.

echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Install Docker Engine

Update the apt package index, and install the latest version of Docker Engine and containerd, or go to the next step to install a specific version:

sudo apt-get updatesudo apt-get install docker-ce docker-ce-cli containerd.io

List the versions available in your repo:

apt-cache madison docker-ce

Install a specific version using the version string from the second column, for example,

sudo apt-get install docker-ce=5:20.10.12~3-0~ubuntu-focal docker-ce-cli=5:20.10.12~3-0~ubuntu-focal containerd.io

Verify that Docker Engine is installed correctly by running the hello-world image.

sudo docker run hello-worldUnable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
2db29710123e: Pull complete
Digest: sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
For more examples and ideas, visit:

check the status

ubuntu@jenkins:~$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2021-12-31 23:40:27 UTC; 4min 55s ago
TriggeredBy: ● docker.socket
Docs:
Main PID: 6261 (dockerd)
Tasks: 9
Memory: 34.4M
CGroup: /system.slice/docker.service
└─6261 /usr/bin/docke

Lets add our username to the docker group

sudo usermod -aG docker jenkins

install maven

sudo apt updatesudo apt install mavenubuntu@jenkins:~$ mvn -version
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 11.0.13, vendor: Ubuntu, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "5.11.0-1022-aws", arch: "amd64", family: "unix"

Setting up Tomcat server

1: install java (please see the previous steps)

ubuntu@tomcat:~$ java -version
openjdk version "11.0.13" 2021-10-19
OpenJDK Runtime Environment (build 11.0.13+8-Ubuntu-0ubuntu1.18.04)
OpenJDK 64-Bit Server VM (build 11.0.13+8-Ubuntu-0ubuntu1.18.04, mixed mode, sharing)

2: install tomcat

mkdir /prod
cd /prod
sudo wget
sudo apt install unzipsudo unzip apache-tomcat-9.0.56.zip

3: start tomcat

cd apache-tomcat-9.0.56/binchmod +x catalina.shubuntu@tomcat:/prod/apache-tomcat-9.0.56/bin$ sudo bash startup.sh
Using CATALINA_BASE: /prod/apache-tomcat-9.0.56
Using CATALINA_HOME: /prod/apache-tomcat-9.0.56
Using CATALINA_TMPDIR: /prod/apache-tomcat-9.0.56/temp
Using JRE_HOME: /usr
Using CLASSPATH: /prod/apache-tomcat-9.0.56/bin/bootstrap.jar:/prod/apache-tomcat-9.0.56/bin/tomcat-juli.jar
Using CATALINA_OPTS:
Tomcat started.

we can see it is running.

4: config settings (For tomcat 9)

sudo vi /prod/apache-tomcat-9.0.56/webapps/manager/META-INF/context.xml

Please comment out

  • add <role rolename="manager-gui"/>
  • add <user username="tomcat" password="<yourpassword>" roles="manager-gui"/>

Also, we need to add the username

cd ../bin/ubuntu@tomcat:/prod/apache-tomcat-9.0.56/bin$ sudo bash shutdown.shubuntu@tomcat:/prod/apache-tomcat-9.0.56/bin$ sudo bash startup.sh

Creating build pipeline in Jenkins

create a new item (pipeline)

Provide a name for your new item (e.g. Pipeline webapp) and select Multibranch Pipeline

Click the Add Source button, choose the type of repository you want to use and fill in the details.

put your GitHub repo address and click validate.

It shows ok if there is no credentials and click save.

After saving that, you can see it on the console.

Go to the GitHub repos you put in the preveious steps and create new file there.

Make a file called “jenkinsfile” and the inside

pipeline {
agent { docker { image 'maven:3.8.4-openjdk-11-slim' } }
stages {
stage('build') {
steps {
sh 'mvn --version'
}
}
}
}