Kubernetes, often abbreviated as K8s, is an open-source platform designed to automate the deployment, scaling, and operation of container applications across clusters of hosts. As the most popular container orchestration tool in the world, Kubernetes simplifies the management of complex containerized applications.
To truly harness the power of Kubernetes, it’s essential to understand its fundamental objects: Pods, Services, Deployments, Ingress, ConfigMaps, and Secrets. These objects form the backbone of Kubernetes operations, enabling developers to build, scale, and manage applications efficiently.
Understanding Pods
At the heart of Kubernetes is the concept of a Pod. A Pod is the smallest and simplest Kubernetes object, representing a single instance of a running process in a cluster. Pods encapsulate one or more containers (such as Docker containers), along with shared storage/network resources and a specification for how to run the containers. The relationship between containers in a pod is almost similar to containers in a VM.
Key Features of Pods:
- Single Unit of Deployment: While you can deploy individual containers, Kubernetes wraps them into Pods, making Pods the atomic unit of deployment. This approach allows Kubernetes to handle more complex applications that might require multiple containers working together.
- Shared Namespace: Containers within a Pod share the same network namespace, including IP addresses and ports. They can communicate with each other using localhost and share storage volumes.
- Lifecycle Management: Kubernetes manages the lifecycle of Pods, ensuring that the desired state (as specified by the developer) is maintained. If a Pod fails or is deleted, Kubernetes will recreate it to meet the desired state.
This is an example of Pods YAML definitions:
apiVersion: v1 kind: Pod metadata: name: nginx-pod labels: app: nginx-app spec: containers: - name: nginx-container image: nginx:latest ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" - name: sidecar-container image: busybox command: ['sh', '-c', 'echo Hello Kubernetes! > /usr/share/nginx/html/index.html && sleep 3600'] resources: requests: memory: "64Mi" cpu: "125m" limits: memory: "128Mi" cpu: "250m"
This is the example of Pods definition that contains 2 containers inside: nginx-container
& sidecar-container
.
Although it is not mandatory to define resources
, it is recommended to define it so the scheduler.
Understanding Services
Why are Services needed? Because each time a Pod is created or destroyed, it receives a new IP address. This abstraction decouples clients from Pods, enabling Kubernetes to load balance across Pods and improve resilience.
There are 4 Types of Services:
- ClusterIP: Exposes the Service on a cluster-internal IP. This type is the default and is used for internal communication within the cluster. This type of service cannot be accessed from outside of the cluster.
- NodePort: Exposes the Service on each Node’s IP at a static port. This allows external access to the Service, though it’s less flexible than other types. The allowable port range for NodePort configuration is 30000 to 32767.
- LoadBalancer: Exposes the service externally by using a cloud provider’s load balancer. This type is commonly used in cloud environments to manage incoming traffic and can only be activated in a cloud environment such as AWS, Azure, GCP, etc.
- ExternalName: Maps a Service to a DNS name, allowing for external services to be accessed through Kubernetes.
This is the example of Services YAML definition:
apiVersion: v1 kind: Service metadata: name: nginx-service spec: type: NodePort selector: app: nginx-app ports: - protocol: TCP port: 80 targetPort: 80 nodePort: 30080
Notice the highlighted text. It indicates that we are assigning this service to Pods with the app: nginx-app
label. And yes, this means we are assigning this service to the Pod we showed you earlier.
Understanding Deployments
Deployments are a higher-level Kubernetes object that provides declarative updates to applications. They manage the creation and scaling of Pods, ensuring that a specified number of Pods are running and up-to-date.
Key Features of Deployments:
- Rolling Updates: Deployments support rolling updates to gradually replace instances of the application with new versions without downtime.
- Rollback: If something goes wrong during an update, Deployments can roll back to a previous state, ensuring stability.
- Scaling: Easily scale the number of replicas (Pods) up or down to handle varying loads.
This is the example of the Deployment YAML definition:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx-app spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 selector: matchLabels: app: nginx-app template: metadata: name: nginx-pod labels: app: nginx-app spec: containers: - name: nginx-container image: nginx:latest ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" - name: sidecar-container image: busybox command: ['sh', '-c', 'echo Hello Kubernetes! > /usr/share/nginx/html/index.html && sleep 3600'] resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m"
If you notice, the highlighted part is the same as the metadata
and spec
from the Pods we showed you earlier. I want to show you that when we create a deployment, it automatically creates a Pod based on the spec.template
defined on the yaml.
When running in production, it is recommended to create a Deployment instead of a Pod. A Deployment allows you to manage the number of Pods you want to create using the spec.replicas
and control the rollout strategy via spec.strategy
. The rollout strategy ensures that you can update your application without causing downtime.
Understanding Ingress
Ingress is an object that manages external access to services within a Kubernetes cluster, typically handling HTTP/HTTPS traffic. It provides features such as load balancing, SSL termination, and name-based virtual hosting.
While it is somewhat similar to NodePort
services, Ingress is more powerful. You might wonder why not just use a service. Here are the key benefits of Ingress that might answer this question:
- Centralized Management: Ingress simplifies the management of multiple services by providing a single entry point.
- Advanced Routing: Supports complex routing rules, such as path-based and host-based routing.
- SSL Termination: Handles SSL/TLS termination, offloading this task from individual services.
By using Ingress, we add an extra layer of reverse proxy in our application. However, if you really need any of the three features mentioned above, then you definitely need Ingress because a service alone does not support these features.
This is the example of Ingress YAML definition:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: test.facsiaginsa.com http: paths: - path: / pathType: Prefix backend: service: name: nginx-service port: number: 80
Notice that the service we used, is the service we introduced to you earlier. The name and port number should match the service configuration. Note that the highlighted port above matches the spec.selector.ports[].port
on the service yaml configuration, not the spec.selector.ports[].targetPort
, not the spec.selector.ports[].nodePort
.
Understanding ConfigMaps
ConfigMaps allow you to decouple configuration artifacts from image content, making applications more portable. ConfigMaps store configuration data in key-value pairs and can be injected into Pods as environment variables, command-line arguments, or configuration files.
Use Cases for ConfigMaps:
- Store environment-specific configuration values.
- Manage application settings that can change between deployments.
- Separate configuration data from the application’s container image.
This is the example of Configmap YAML definition:
apiVersion: v1 kind: ConfigMap metadata: name: app-configmap data: HOST: "0.0.0.0" PORT: "3000" BUCKET: "images"
This is the example of a Deployment YAML definition that uses the Configmap as an environment:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx-app spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 selector: matchLabels: app: nginx-app template: metadata: name: nginx-pod labels: app: nginx-app spec: containers: - name: nginx-container image: nginx:latest ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" envFrom: - configMapRef: name: app-configmap
Notice the highlighted text should match the name of your configmap.
Understanding Secrets
Secrets are similar to ConfigMaps but are specifically designed to store sensitive information, such as passwords, tokens, and keys. Secrets are encoded in base64 and can be mounted into Pods as files or exposed as environment variables.
Key Features of Secrets:
- Security: Secrets are intended to be more secure than plain ConfigMaps. They should be handled carefully and have restricted access.
- Decoupling: Like ConfigMaps, Secrets decouples sensitive data from the application code, enhancing security and flexibility.
This is some example of the Secret YAML definition:
apiVersion: v1 kind: Secret metadata: name: app-secret type: Opaque data: key1: dmFsdWUx # base64 encoded value of 'value1' key2: dmFsdWUy # base64 encoded value of 'value2' --- apiVersion: v1 kind: Secret metadata: name: tls-secret type: kubernetes.io/tls data: tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FUR... tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVkt... --- apiVersion: v1 kind: Secret metadata: name: docker-app-secret type: kubernetes.io/dockerconfigjson data: .dockerconfigjson: eyJhdXRocyI6IHsiaHR0cHM6Ly9pbmRleC5kb2NrZXIuaW8vd...
These 3 are the most used secret type for you as a developer. To learn how to generate this 3 secret, you can see my tutorial: How to Create Kubernetes Secrets Imperatively: A Developer’s Guide
The Opaque
and kubernetes.io/dockerconfigjson
are usually used in the Deployment yaml definition. Opaque
type secret is usually used for environment variables that are sensitive like Database Password, API Token, JWT Secret, etc, while kubernetes.io/dockerconfigjson
is used if you need to log in when you pull your images from a private registry.
Let’s see how we can implement Deployment that using a secret:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx-app spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 selector: matchLabels: app: nginx-app template: metadata: name: nginx-pod labels: app: nginx-app spec: containers: - name: nginx-container image: facsiaginsa.com/images/nginx:latest ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "250m" limits: memory: "128Mi" cpu: "500m" envFrom: - configMapRef: name: app-configmap - secretRef: name: app-secret imagePullSecrets: - name: docker-app-secret
kubernetes.io/tls
type secret is usually used in Ingress yaml definition. It contains the tls certificate & key in a base64 format. Let’s see how we can implement Ingress that using a secret:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx tls: - hosts: - test.facsiaginsa.com secretName: tls-secret rules: - host: test.facsiaginsa.com http: paths: - path: / pathType: Prefix backend: service: name: nginx-service port: number: 80
Conclusion
Understanding Kubernetes objects such as Pods, Services, Deployments, Ingress, ConfigMaps, and Secrets, is fundamental to effectively utilizing Kubernetes. These objects provide the building blocks for managing containerized applications, offering powerful abstractions that simplify deployment, scaling, and management.
By mastering these concepts, developers and operations teams can harness Kubernetes to build resilient, scalable, and manageable applications, ensuring that they can meet the demands of modern software development.
I hope this comprehensive overview of Kubernetes fundamentals helps you on your journey to mastering container orchestration. Happy coding!