Kubernetes Management Techniques


YAML

Imperative vs Declarative

There’s a lot of styles for how you can work in a Kubernetes cluster, you can choose to use just one approach or mix both of them to best suit your needs. Each style has its pros and cons; let’s break that down.

Imperative - Focus on how a program operates.

  • Using imperative approach you need to tell exactly how you need that resource to be created, including all the details.
  • Uses mostly kubectl run, kubectl create, kubectl update, and kubectl replace commands.

Pros

  • Easier when you know the state of your resource or object.
  • Easier to get started.

Cons

  • Not easy to automate.
  • Needs to use different commands to change resources or objects.
  • Mostly used for small environments or local development.

Declarative - Focus on what a program should accomplish.

  • On declarative approach, you just need to tell Kubernetes what you need and it will be provisioned for you.
  • Uses mostly kubectl apply -f <config_file> command.

Pros

  • Uses the same command each time (tiny exceptions for delete).
  • Resources can be inside one file, or many files because you can apply a whole directory (kubectl apply -f myyaml/) if needed.
  • Easy to automate your orchestration.
  • Used in almost all production environments.

Cons

  • Requires understanding of YAML keys and values.
  • Takes more work to start running applications.

YAML Syntax

YAML file is a simplified version of JSON that is made of maps of key-values, in which a map is just an object, containing values and keys.

A key can contain a map of subkeys and values:

keyA:
  subkeyA: subvalueA
  subkeyB: subvalueB
  subkeyC: subvalueC

The equivalent JSON will be:

{
    "keyA": {
        "subkeyA": "subvalueA",
        "subkeyB": "subvalueB",
        "subkeyC": "subvalueC"
    } 
}

A map of 3 values should look like this:

keyA: valueA
keyB: valueB
keyC: valueC

In JSON syntax should be like:

{
    "keyA": "valueA",
    "keyB": "valueB",
    "keyC": "valueC",
}

YAML list items are represented with a “-“ in front of it:

listKey:
  - listItemA
  - listItemB
  - listItemC

JSON equivalent will be:

{
    "listKey": [
        "listItemA",
        "listItemB",
        "listItemC"
    ]
}

In YAML you can have maps inside list items:

listKey:
  - listItemA
    mapItemA: mapValueA
    mapItemB: mapValueB
  - listItemB
  - listItemC

In JSON, a list item containing a map it’s just an object in an array:

{
    "listKey": [
        "listItemA",
        {
            "mapItemA": "mapValueA",
            "mapItemB": "mapValueB"
        },
        "listItemB",
        "listItemC"
    ]
}

YAML Spec

The spec format is different for each Kubernetes object. The spec is where you will be describing your object in more detail. You can have more details looking at Kubernetes API Reference.

Examples of different specs:

Specifying CPU Requests and Limits

apiVersion: v1
kind: Pod
metadata:
  name: range-pod
  namespace: cpu-limited # Namespace is used to create virtual clusters when you have multiple teams or you need to segregate resources.
spec:
  containers:
  - name: range-container
    image: nginx
    resources:
      limits: # Limits the usage of CPU to 1
        cpu: 1
      requests: # Limits the CPU requests to 0.5
        cpu: 0.5
    args: # When pod starts will be allowed to use 2 CPUs 
     - cpus
        2

Configuring a Volume

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-storage
      mountPath: /data/redis # Volume will be mounted at this path
  volumes:
  - name: redis-storage
    emptyDir: {} # Creates a initially empty volume, but if a pod is removed from the node the data is lost.

Creating a Load Balancer Service

apiVersion: v1
kind: Service
metadata:
  name: range-service
spec:
  type: LoadBalancer # Type of service
  selector:
    app: rangeforce
  ports:
    - port: 8700 # Port exposed inside the cluster
      targetPort: 8080 # Port your application is listening on

Creating a Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx # Label is used to identify an object
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx # Identify what pods the deployment will apply to.
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.19.0 
        ports:
        - containerPort: 80 # Specify the port to access on the containers belonging to pods targeted by your service.

Apply command

Apply creates and updates resources through files in a cluster, by running kubectl apply. This is the recommended way of managing Kubernetes applications on production.

Kubernetes manifests can be defined in YAML or JSON. The file extensions accepted are .yaml, .yml, and .json.

kubectl apply usage:

kubectl apply (-f FILENAME | -k DIRECTORY)

Create resource(s)

kubectl apply -f ./my-manifest.yaml

Create resource(s) from multiple files

kubectl apply -f ./my1.yaml -f ./my2.yaml

Create resource(s) in all manifest files in /root/home

kubectl apply -f /root/home

Create resource(s) from url

kubectl apply -f <url>

Conclusion

A good understanding of YAML syntax, keys, and values is capital when you are using a declarative approach. This can help you create more consistent and assertive manifests for your applications.

Further Reading

Kaleby Cadorin