Hands-on Exercises with Kubernetes 💻🚀¶
Kubernetes is the go-to platform for container orchestration, and mastering it is essential for modern DevOps and cloud-native development. One fundamental aspect of Kubernetes is managing resources using either imperative or declarative methods. In this hands-on blog post, we'll dive into both approaches with practical examples and real-time use cases.
Kubernetes syntax 🧩¶
Let's dive into the basic Kubernetes syntax for four key fields: apiVersion
, kind
, metadata
, and spec
:
-
apiVersion:
Purpose: The
apiVersion
field specifies the version of the Kubernetes API that the resource definition uses. It ensures compatibility between the resource and the Kubernetes cluster.Format: Typically, the
apiVersion
field consists of two parts separated by a forward slash ("/"). The first part is the API group, and the second part is the API version within that group.Examples: Common API versions include
v1
(core API for basic resources),apps/v1
(for application-related resources), andnetworking.k8s.io/v1
(for network policies).Usage: It tells the Kubernetes cluster how to interpret the resource definition. It's essential to specify the correct
apiVersion
to create resources successfully. -
kind:
Purpose: The
kind
field defines the type of Kubernetes resource you are creating or managing. It determines the behavior and attributes of the resource.Examples: Common resource kinds include
Pod
,Service
,Deployment
,ConfigMap
,Secret
,Namespace
, and more.Usage: The
kind
field ensures that Kubernetes understands the type of resource you intend to create or modify. -
metadata:
Purpose: The
metadata
field provides information about the resource, such as its name, labels, and annotations.Format: It is a YAML dictionary that includes various metadata fields like
name
,labels
,annotations
, and more.Examples: Setting
name
specifies the name of the resource, whilelabels
allow you to add key-value pairs for categorization and selection.Usage: Metadata helps in identifying, organizing, and managing resources within the Kubernetes cluster.
-
spec:
Purpose: The
spec
field describes the desired state of the resource. It specifies the configuration settings, properties, and behaviors that the resource should have.Format: It depends on the resource type and its attributes. For example, in a Pod, the
spec
field may include details about containers, volumes, and networking.Usage: The
spec
field is where you define how you want the resource to function within the Kubernetes cluster. It represents the resource's desired state.
Here's an example of how these fields come together in a Kubernetes YAML file for creating a Pod:
# Kubernetes YAML file for a basic Pod
#The 'apiVersion' field specifies the Kubernetes API version.
apiVersion: v1 # (1)!
# The 'kind' field defines the type of Kubernetes resource.
kind: Pod # (2)!
# Metadata section provides information about the resource.
metadata:
# 'name' specifies the name of the Pod.
name: my-pod
# 'labels' are key-value pairs used to organize and select resources.
# Here, we label the Pod as 'app: my-app.'
labels:
app: my-app
# The 'spec' section describes the desired state of the resource.
spec:
# 'containers' is a list of containers to run within the Pod.
containers:
- name: my-container
# 'image' specifies the Docker image to use for this container.
# In this case, we use the 'nginx:latest' image.
image: nginx:latest
# 'ports' section defines the ports to be exposed by the container.
ports:
-
# 'containerPort' specifies the port number (80) on which the container listens.
containerPort: 80
-
Common API versions include:
-
v1: Core resources like Pods, Services, ConfigMaps, and more.
-
apps/v1: Resources like Deployments, StatefulSets, and DaemonSets.
-
extensions/v1beta1: Older version for resources like Ingress.
-
networking.k8s.io/v1: Resources like NetworkPolicies.
-
-
In this case, we are creating a Pod, but other common resource types include:
-
Deployment
-
Service
-
ConfigMap
-
Secret
-
PersistentVolume
-
PersistentVolumeClaim
-
StatefulSet
-
Namespace
-
In this example, we have:
apiVersion: v1
indicating the core API version.kind: Pod
specifying that we are defining a Pod resource.metadata
providing metadata like the Pod's name and labels.spec
describing the desired state of the Pod, including container details and port configuration.
Understanding and correctly using these basic Kubernetes syntax elements is crucial for defining and managing Kubernetes resources effectively.
Imperative 🆚 Declarative Methods¶
Imperative Method:¶
Example
Imperative commands directly manipulate Kubernetes resources in real-time. They are ideal for quick, one-off tasks. Let's explore some imperative examples:
1. Create a Namespace:
kubectl create namespace my-namespace
2. Run a Pod:
kubectl run my-pod --image=nginx --port=80
3. Scale a Deployment:
kubectl scale deployment my-deployment --replicas=3 # (1)
- 🖐️ Modify the quantity of replicas here
Real-time Use Case:¶
Suppose you have a sudden spike in traffic to your website, and you need to quickly scale up your application. Imperative commands allow you to react swiftly without worrying about YAML configuration files.
Declarative Method:¶
Example
Declarative Kubernetes management relies on YAML files to define the desired state of resources. Here are some declarative examples:
1. Create a Namespace
apiVersion: v1
kind: Namespace
metadata:
name: my-namespace
2. Run a Pod
apiVersion: v1 # Kubernetes API version 🌐
kind: Pod # Type of resource: a Pod 🚀
metadata:
name: my-pod # Name of the Pod 🏷️
spec:
containers:
- name: nginx-container # Container name 🐳
image: nginx # Docker image used 📦
ports:
- containerPort: 80 # Container's port configuration 🔌
3. Scale a Deployment
apiVersion: apps/v1 # Kubernetes API version for Deployments 🌐
kind: Deployment # Type of resource: a Deployment 🚀
metadata:
name: my-deployment # Name of the Deployment 🏷️
spec:
replicas: 3 # Number of desired replicas of the app 🔄
selector:
matchLabels:
app: my-app # Label selector for matching Pods 🏷️
template:
metadata:
labels:
app: my-app # Labels for Pods created by this Deployment 🏷️
spec:
containers:
- name: my-container # Container name 🐳
image: my-image # Docker image used for the container 📦
Real-time Use Case:¶
Imagine you are setting up a Continuous Integration/Continuous Deployment (CI/CD) pipeline. Declarative manifests are the preferred choice as they clearly define the desired state of your resources, making it easier to track changes and collaborate with your team.
Converting Imperative Kubernetes Commands to Declarative YAML Files¶
kubectl create deployment my-nginx-pod --image=nginx --dry-run=client -o yaml > pod.yaml
kubectl
.
Sample Pod File - Imperative vs. Declarative:¶
Let's create a sample Pod file to run an Nginx container using both imperative and declarative methods:
**Imperative **
kubectl run my-nginx-pod --image=nginx --port=80
Declarative
apiVersion: v1 # Kubernetes API version 🌐
kind: Pod # Type of resource: a Pod 🚀
metadata:
name: my-nginx-pod # Name of the Pod 🏷️
spec:
containers:
- name: nginx-container # Container name 🐳
image: nginx # Docker image used 📦
ports:
- containerPort: 80 # Port configuration for the container 🔌
Sample Deployment and Service Files - Imperative vs. Declarative:¶
Now, let's create deployment and service files for deploying Nginx with a NodePort service using both methods:
Imperative (command-line):
kubectl create deployment nginx-deployment --image=nginx
kubectl expose deployment nginx-deployment --port=80 --type=NodePort
Declarative
apiVersion: apps/v1 # Kubernetes API version for Deployments 🌐
kind: Deployment # Type of resource: a Deployment 🚀
metadata:
name: nginx-deployment # Name of the Deployment 🏷️
spec:
replicas: 3 # Number of desired replicas of the app 🔄
selector:
matchLabels:
app: nginx # Label selector for matching Pods 🏷️
template:
metadata:
labels:
app: nginx # Labels for Pods created by this Deployment 🏷️
spec:
containers:
- name: nginx-container # Container name 🐳
image: nginx # Docker image used for the container 📦
Declarative
apiVersion: v1 # Kubernetes API version for Services 🌐
kind: Service # Type of resource: a Service 🚀
metadata:
name: nginx-service # Name of the Service 🏷️
spec:
selector:
app: nginx # Label selector for matching Pods 🏷️
ports:
- protocol: TCP # Protocol used for the port 🔌
port: 80 # Port exposed by the Service 🌐
targetPort: 80 # Target port on Pods 🎯
type: NodePort # Type of Service: NodePort for external access 🏃♂️🏢
Deploying Grafana with LoadBalancer:¶
To further demonstrate declarative YAML, let's create a deployment and service file to deploy Grafana with a NodePort service. You can customize the Grafana deployment based on your requirements.
apiVersion: apps/v1 # Kubernetes API version for Deployments 🌐
kind: Deployment # Type of resource: a Deployment 🚀
metadata:
name: grafana-deployment # Name of the Deployment 🏷️
spec:
replicas: 1 # Number of desired replicas of the app 🔄
selector:
matchLabels:
app: grafana # Label selector for matching Pods 🏷️
template:
metadata:
labels:
app: grafana # Labels for Pods created by this Deployment 🏷️
spec:
containers:
- name: grafana-container # Container name 🐳
image: grafana/grafana:latest # Docker image used for the container 📦
ports:
- containerPort: 3000 # Port configuration for the container 🔌
apiVersion: v1 # Kubernetes API version for Services 🌐
kind: Service # Type of resource: a Service 🚀
metadata:
name: grafana-service # Name of the Service 🏷️
spec:
selector:
app: grafana # Label selector for matching Pods 🏷️
ports:
- protocol: TCP # Protocol used for the port 🔌
port: 80 # Port exposed by the Service 🌐
targetPort: 3000 # Target port on Pods 🎯
type: LoadBalancer # Type of Service: LoadBalancer link for external access 🏃♂️🏢
In this example, we're deploying Grafana using declarative YAML files, allowing for a well-defined and repeatable process.
Website Deployment¶
apiVersion: apps/v1
kind: Deployment
metadata:
name: wavecafe # 🚀 Name of the Deployment
spec:
replicas: 1 # 🚶 Number of desired replicas
selector:
matchLabels:
app: cafe # 🏷️ Selector to match pods with the label "app: cafe"
template:
metadata:
labels:
app: cafe # 🏷️ Labels applied to pods created by this template
spec:
containers:
- name: my-app-container # 📦 Name of the container
image: saitejairrinki/wavecafe:v1 # 🐳 Docker image to use
ports:
- name: cafe-port # 🌐 Name of the port
containerPort: 80 # 🚪 Port that the container listens on
apiVersion: v1
kind: Service
metadata:
name: wave-cafe # ☕ Name of the Service
spec:
selector:
app: cafe # 🏷️ Select pods with the label "app: cafe"
ports:
- protocol: TCP # 🌐 Protocol for the port
port: 80 # 🚪 Port on the Service
targetPort: cafe-port # 🚀 Port on the pods to forward traffic to
type: NodePort # 🏢 Expose the Service as a NodePort