You probably already heard of Kubernetes, a powerful orchestrator that will ease deployment and automatically manage your applications on a set of machines, called a Cluster.
Most of the resources will be contained inside a namespace, thus unaware of resources from other namespaces. Only a few kinds of resources are completely agnostic of namespaces, and they define computational power or storage sources (i.e. Nodes and PersistentVolumes). However, access to those can be limited by namespace using Quotas.
Further reading:
Official Kubernetes Glossary
Official Concepts Documentation
Watch out: some resources such as StorageClass do no use a single entry point as described above
# <metadata> narrows down selection and identify the resource
metadata:
# The <name> entry is required and used to identify the resource
name: my-resource
namespace: my-namespace-or-default
# <labels> is optional but often needed for resource selection
labels:
app: application-name
category: back
# <annotations> is optional and not needed for the configuration of Kubernetes
annotations:
version: 4.2
Further reading:
Naming and Identification
Labels and Selectors
Annotations
Infrastructure centric configuration files
Resources definition
In this section, we will take a closer look at the configuration of the most used resources on a Kubernetes application. This is also the occasion to showcase the interactions between resources.
ConfigMap
ConfigMap is used to hold properties that can be used later in your resources.
apiVersion: v1
kind: ConfigMap
metadata:
name: simple-web-config
namespace: default
data:
configuration_key: "Configuration value"
The configuration defined above can then be selected from another resource definition with the following snippet:
valueFrom:
configMapKeyRef:
name: simple-web-config
key: configuration_key
Note: ConfigMaps are only available in the namespace in which they are defined.
The secret defined above can then be selected from another resource definition with the following snippet:
valueFrom:
secretKeyRef:
name: simple-web-secrets
key: secret_configuration_key
Note: Secrets are only available in the namespace in which they are defined.
Pod
-
ports
to define the ports to open on both the container and pod.
-
env
to define the environment variables to load on the container.
-
args
and
entrypoint
to customize the container startup sequence.
Pods are usually not created as standalone resources on Kubernetes,
as the best practice indicates to use pod as part of a higher-level definition
(e.g. Deployment). In those cases, the Pod file’s content will simply be embedded in the other resource’s file.
apiVersion: v1
kind: Pod
metadata:
name: my-web-server
spec:
# <containers> is a list of container definition to embed in the pod
containers:
- name: web
image: nginx
ports:
- name: web
containerPort: 80
protocol: TCP
env:
- name: SOME_CONFIG
# Create a line "value: <config_entry>" from the ConfigMap data
valueFrom:
configMapKeyRef:
name: simple-web-config
key: configuration_key
- name: SOME_SECRET
# Create a line "value: <config_entry>" from the Secret data
valueFrom:
secretKeyRef:
name: simple-web-secrets
key: secret_configuration_key
Note: Pods are only available in the namespace in which they are defined.
The Deployment is generally used as the atomic working unit since it will automatically:
Note: Deployments are only available in the namespace in which they are defined.
Further reading:
Deployment Documentation
Service
A pod might be deleted and recreated at any time. When it occurs the pod’s IP address will change, which could result in a loss of connection if you are directly contacting it. To solve this issue, a Service provides a stable contact point to a set of Pods, while remaining agnostic of their state and configuration.
Usually, Pods are chosen to be part of a Service through a
selector
entry, thus based on its
labels
. A Pod is selected if and only if all the labels in the selector are worn by the pod.
There are three types of services that are acting quite differently, among which you can select using the type entry.
The ClusterIP service is bound to an internal IP from the cluster, hence only internally reachable. This is the type of service created by default and is suitable for binding different applications inside the same cluster.
A NodePort service will bind a port (by default in range 30000 to 32767) on the nodes hosting the selected pods. This enables you to contact the service directly through the node IP. That also means that your service will be as accessible as the virtual or physical machines hosting those pods.
Note: Using NodePort can pose security risks, as it enables a direct connection from outside the cluster.
A LoadBalancer service will automatically create a load balancer instance from the cloud service provider on which the cluster is running. This load balancer is created outside the cluster but will automatically be bound to the nodes hosting the selected pods.
This is an easy way to expose your service but can end up being costly as each service will be managed by a single load balancer.
If you are setting up your own Ingress as we will do here, you may want to use a
ClusterIp
service, as other services are made for specific use cases.
apiVersion: v1
kind: Service
metadata:
name: simple-web-service-clusterip
spec:
# ClusterIP is the default service <type>
type: ClusterIP
# Select all pods declaring a <label> entry "app: webserver"
selector:
app: webserver
ports:
- name: http
protocol: TCP
# <port> is the port to bind on the service side
port: 80
# <targetPort> is the port to bind on the Pod side
targetPort: 80
Note: Services are defined in a namespace but can be contacted from other namespaces.
Further reading:
Service Documentation
In Depth Service Comparison
Create an External Load Balancer
Ingress
Ingress enables you to publish internal services without necessarily using a load balancer from cloud service providers. You usually need only one ingress per namespace, where you can bind as many routing
rules
and
backends
as you want. A backend will typically be an internally routed ClusterIP service.
Please note that Kubernetes does not handle ingress resources by itself and relies on third-party implementations. As a result, you will have to choose and install an Ingress Controller before using any ingress resource. On the other hand, it makes the ingress resource customizable depending on the needs of your cluster.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-web-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
# Using <host> redirects all request matching the given DNS name to this rule
- host: "*.minikube.internal"
http:
paths:
- path: /welcome
pathType: Prefix
backend:
service:
name: simple-web-service-clusterip
port:
number: 80
# All other requests will be redirected through this rule
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: simple-web-service-clusterip
port:
number: 80
Note: Ingresses are defined in the namespace but may contact services from other namespaces and are publicly accessible outside the cluster.
Further reading:
Ingress Documentation
Available Ingress Controllers
Enable Ingress on Minikube
Nginx Ingress Annotations
CLI Usage
Create and manage resources
This section showcases the basic CLI commands to manipulate resources. As said before, while it is possible to manually manage resources, a better practice is to use files.
# <kind> is the type of resource to create (e.g. deployment, secret, namespace, quota, ...)
$ kubectl create <kind> <name>
$ kubectl edit <kind> <name>
$ kubectl delete <kind> <name>
# All those commands can be used through a description file.
$ kubectl create -f <resource>.yaml
$ kubectl edit -f <resource>.yaml
$ kubectl delete -f <resource>.yaml
To ease resources manipulations through files, you can reduce the interactions to the CLI to the two following commands:
# Create and update any resource
$ kubectl apply -f <resource>.yaml
# Delete any resource
$ kubectl delete -f <resource>.yaml
Further reading:
Managing Resources
Monitor and Debug
Fetch resources
You can see all resources running through the CLI using
kubectl get <kind>
. This command is pretty powerful and lets you filter the kind of resources to display or select the resources you want to see
Note: if not specified, Kubernetes will work on the
default
namespace. You can specify
-n <namespace>
to work on a specific namespace or
-A
to show every namespace.
# Fetch everything
$ kubectl get all
NAME READY STATUS RESTARTS AGE
pod/my-web-server-deployment-58c4fd887f-5vm2b 1/1 Running 0 128m
pod/my-web-server-deployment-58c4fd887f-gq6lr 1/1 Running 0 128m
pod/my-web-server-deployment-58c4fd887f-gs6qb 1/1 Running 0 128m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/simple-web-service-clusterip ClusterIP 10.96.96.241 <none> 80/TCP,443/TCP 60m
service/simple-web-service-lb LoadBalancer 10.108.182.232 <pending> 80:31095/TCP,443:31940/TCP 60m
service/simple-web-service-np NodePort 10.101.77.203 <none> 80:31899/TCP,443:31522/TCP 60m
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/my-web-server-deployment 3/3 3 3 136m
NAME DESIRED CURRENT READY AGE
replicaset.apps/my-web-server-deployment-58c4fd887f 3 3 3 128m
# We can ask for more details
$ kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
my-web-server-deployment 3/3 3 3 121m web nginx app=webserver
# Some resources are not visible using "all" but available
$ kubectl get configmap
NAME DATA AGE
kube-root-ca.crt 1 38d
simple-web-config 3 3h17m
Dig into a particular resource
This section will show you how to dig into resources. Most of the required day-to-day operations are doable through the three following commands.
The first command will give you the resource’s complete configuration, using
kubectl describe <kind>/<name>
# Let's describe the ingress for the sake of example
$ kubectl describe ingress/simple-web-ingress
Name: simple-web-ingress
Namespace: default
Address: 192.168.64.2
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
Rules:
Host Path Backends
---- ---- --------
*.minikube.internal
/welcome simple-web-service-clusterip:80 (172.17.0.4:80,172.17.0.5:80,172.17.0.6:80 + 1 more...)
*
/ simple-web-service-clusterip:80 (172.17.0.4:80,172.17.0.5:80,172.17.0.6:80 + 1 more...)
Annotations: nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal UPDATE 7m6s (x6 over 23h) nginx-ingress-controller Ingress default/simple-web-ingress
Another important command is
kubectl logs <kind>/<name>
, as you might expect it shows you the resources’ logs if applicable. As the logs are produced by Pods, running such a command on a resource above a Pod will dig through Kubernetes to display the logs of a randomly chosen Pod underneath it.
$ kubectl logs deployments/my-web-server-deployment
Found 3 pods, using pod/my-web-server-deployment-755b499f77-4n5vn
# [logs]
Finally, it is sometimes useful to connect on a pod, you can do so with the command
kubectl exec -it <pod_name> -— /bin/bash
. This will open an interactive shell on the pod, enabling you to interact with its content.
# As for logs, when called on any resource enclosing Pods,
# Kubernetes will randomly chose one to execute the action
$ kubectl exec -it deployment/my-web-server-deployment -- /bin/bash
root@my-web-server-deployment-56c4554cf9-qwtm6:/# ls
# [...]
Conclusion
During this article, we saw the fundamentals behind deploying and publishing stateless services using Kubernetes. But you can do a lot more complex things with Kubernetes. If you want to learn more about it, I can recommend you to look at these resources:
- Read the Kubernetes reference documentation.
- Install a sandbox locally with Minikube, and play with it.
- Watch the video Kubernetes Tutorial for Beginners — TechWorld with Nana.
- Manually bootstrap a Kubernetes cluster: Kubernetes The Hard Way.
Incidentally, there are multiple subjects I could not deeply talk about in this article and that may be of interest.
On the developer side:
On the cluster administrator side: