Kube Demo

Table of Contents

Home Development

Prerequisites

You will need to have Kubernetes installed. The easiest way to do with is with minikube. In order to have minikube installed, you'll need to have docker installed. You can technically run minikube in a virtual machine but docker is easiest.

minikube creates a docker container in which we can run… containers.

container.jpeg

Overview

Most things in Kubernetes are Kubernetes Objects. Objects are the basic Kubernetes building blocks and they DE-scribe the way you expect your cluster to be. Most deployable objects are configured through spec properties as you'll see shortly.

In this demo, I'm going to spin up a few different types of basic Objects. The main and most efficient way of interacting with interacting with Objects is using the kubectl cli which comes bundled with minikube. The main things we're going to go over and the main things to be done with Objects are:

  • Creating Objects (kubectl create <KUBERNETES_OBJECT>)
  • Deleting Objects (kubectl delete <KUBERNETES_OBJECT>)
  • Applying files (kubectl apply -f <FILE>.yaml) -
    • .json files can also be applied. The syntax almost identical to .yaml files, only a bit more verbose.
    • files do not have to be local. You can use http to access files on the internet. kubernetes.io has the following example:
      • kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml

Applying a file will either create the object if it doesn't exist or edit the existing object. There are a few ways to view the Objects that have been created. Namely the commands:

  • kubectl get <OBJECT_TYPE>
  • kubectl describe <OBJECT_TYPE> <SPECIFIC_OBJECT>
    • kubectl describe pod <POD_NAME> for example.

Throughout, I will be using evaluating code blocks on the fly which is essentially equivalent to kubectl apply -f and showing the Kubernetes Objects I've created with a wrapper around kubectl which essentially functions like a kubectl get <OBJECT_NAME>. I will demonstrate the first few by applying them through the command line and running the describe and get commands, but for the most part I will use my wrapper around kubectl.

A list of all kubectl commands can be found here.

The goal of this demonstration will be to walk you through a basic web app with a persistent backend all set up using Kubernetes Objects.

Namespaces

Namespaces are ways of grouping Kubernetes resources. The best way of using namespaces is to think of them as groups of related Objects. A namespace is itself an Object and therefore can be created with the standard kubectl commands or with a file.

# example-namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: my-new-namespace
  labels:
    name: my-new-namespace

We will proceed with the default namespace and the kubernetes documentation recommends only using the default namespace unless you're in a large team with many objects that need different resource allocation.

Pods

Kubernetes Pods consist of containers and resources. Typically, there is one pod per container, but it is possible to use many containers in a single pod. Think of them as wrappers around docker containers (though you don't have to use docker as your containers service specifically) which allows you to interface with them using the kubectl api. Pods are the smallest deployable unit in Kubernetes. A simple example of a pod from the Kubernetes documentation is as follows:

# example-nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

Pods have their own IP addresses and can be accessed through standard protocols (TCP, UDP, SCTP). But pods can be spun up and spun down giving them new IP addresses each time. To avoid this, Kubernetes has a concept known as Services.

Services

A Service is a creating a logical set of pods and ways to communicate between them. Given that Pods are spun up and spun down in order to meet the desired state of the application, we need a way of tracking them that doesn't rely on knowledge of the specific IP addresses.

Services need a few properties to function correctly. Firstly, they need a selector. This will tell the service which deployments it's associated with. Second, you will need to specify the port the service is open on and the targetPort the deployment is running the pods on. This is so that the service correctly forwards the request it receives to the right pods.

Additionally, Services act as load balancers and by default direct traffic to the least busy pod of all the replicas.

ConfigMaps

ConfigMaps are key/value stores typically used for configuration data. They're referenced in other Objects by name via configMapKeyRef. As many values as one wnats can be created. We will show a demo of a ConfigMap later.

Secrets

Like ConfigMaps, Secrets are key value stores, but their purpose is so we're not accessing things like passwords and tokens directly inside pods as environmental variables. They're managed by Kubernetes and the values are required to be base64 encoded.

Deployments

Deployments are an abstraction over pods. In practice, what will be used most is deployments. It's unlikely that you would spin up a pod on it's own because Kubernetes manages pods through deployments. In order to know what services the deployment needs to get traffic from, we have to add a matchLabels property to the spec. Deployments also tell us how many replicas of a given pod we need.

MongoDB Setup

ConfigMap

Here, instead of hard-coding an ip address for the pod our deployment will create, we're going to reference the subsequent service that gets created. We will need this in our node application in order to access mongo.

# mongo-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: mongo-config
data:
  mongo-url: mongo-service # to be created
configmap/mongo-config created

Service

This service tells us what port we want to access the service through as well as which port to target and specifies a selector we will use to match in our deployment.

# mongo-config.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: mongo-service
spec:
  selector:
    app: mongo
  ports:
    - protocol: TCP
      port: 27017
      targetPort: 27017 # the container port we are targeting when our service gets a request
service/mongo-service created

Secret

First, let's encode the user:

echo -n mongouser | base64
bW9uZ291c2Vy

Then we'll encode the password:

echo -n mongopassword | base64
bW9uZ29wYXNzd29yZA==

After, we can use those values to create our Mongo secret.

# mongo-secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: mongo-secret
type: Opaque
data:
  mongo-user: mongou
  mongo-password: mongop

Create Secret Via Bash

echo -n secret | base64

Use Secret In kubectl

apiVersion: v1
kind: Secret
metadata:
  name: super-secret
type: Opaque
data:
  mongo-user: ${{ secret }}

Deployment

In this deployment, we make sure to match up with the mongo service through the matchLabels selector and we configure the environmental variables of our mongo container. This information used therein is referenced from the official mongodb documentation on dockerhub.

# mongo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongo-deployment
  labels:
    app: mongo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mongo # matched from our selector in our service
  template:
    metadata:
      labels:
        app: mongo
    spec:
      containers:
      - name: mongodb
        image: mongo:5.0
        ports:
        - containerPort: 27017 # we forward traffic here from our service
        env:
        - name: MONGO_INITDB_ROOT_USERNAME
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: mongo-user
        - name: MONGO_INITDB_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: mongo-password
deployment.apps/mongo-deployment created

Next we'll move onto the Setting up the node app.

Node Application Setup

Service

This service will give us access to our web application through minikube via the minikube ip. We define the nodePort as accessible to the outside. Note that Services of type NodePort have to fall within a very specific range (30000-32767).

# webapp.yaml
---
apiVersion: v1
kind: Service
metadata:
  name: webapp-service
spec:
  type: NodePort
  selector:
    app: webapp
  ports:
    - protocol: TCP
      port: 3000
      targetPort: 3000
      nodePort: 30100
service/webapp-service created

Deployment

In this deployment, we make sure to match up with the webapp service through the matchLabels selector and we configure the environmental variables and read the values from our config map and secret.

# webapp.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
  labels:
    app: webapp
spec:
  replicas: 2
  selector:
    matchLabels:
      app: webapp # matched from our selector in our service
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: webapp
        image: nanajanashia/k8s-demo-app:v1.0
        ports:
        - containerPort: 3000
        env:
        - name: USER_NAME
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: mongo-user
        - name: USER_PWD
          valueFrom:
            secretKeyRef:
              name: mongo-secret
              key: mongo-password
        - name: DB_URL
          valueFrom:
            configMapKeyRef:
              name: mongo-config
              key: mongo-url
deployment.apps/webapp-deployment created

Author: joshua

Created: 2024-07-22 Mon 16:57

Validate