Learn Kubernetes part 2 – NGINX Ingress Controller

Large organizations need to control the incoming traffic to the Kubernetes cluster. The most secure way is to use an ingress controller and create an ingress to channel all incoming traffic to the cluster.

In Learn Kubernetes part 1 – Web application in a Kubernetes cluster we have created a simple web application pod and exposed it to the outside world with a service using a load balancer. We will use the files we have created in that exercise with one change. The deployment is the same:

app1-frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1-frontend-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: app1-frontend-template-label
  template:
    metadata:
      labels:
        app: app1-frontend-template-label
    spec:
      containers:
        - name: app1-frontend-container-label
          image: nginx:1.7.9
          ports:
          - containerPort: 80

In this exercise we will expose the service via an NGINX ingress controller. Delete type: LoadBalancer in the app1-frontend-service.yaml file, so Kubernetes will use type: ClusterIP, the default value.

app1-frontend-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: app1-frontend-service
spec:
  selector:
    app: app1-frontend-template-label
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 80

Create an ingress controller

In this example we will use the kubernetes/ingress-nginx ingress controller maintained by the Kubernetes community. See kubernetes/ingress-nginx NGINX Ingress Controller Installation Guide to configure the NGINX Ingress Controller in your environment.

To start the kubernetes/ingress-nginx ingress controller in any operating system, execute this command to create the ‘nginx-ingress-controller’ deployment with containers

  • k8s_nginx-ingress-controller_nginx-ingress-controller
  • k8s_POD_nginx-ingress-controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

Based on the operating system, also execute this to create the ‘ingress-nginx’ service

On Macintosh

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml

Verify the ingress controller installation to make sure it has successfully started

kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch
NAMESPACE       NAME                                        READY   STATUS    RESTARTS   AGE
ingress-nginx   nginx-ingress-controller-86449c74bb-rlx6h   1/1     Running   0          2d4h

Script the ingress

Connect the ingress to the service

Set the name of the service in the spec: … backend: serviceName:

ingress_nginx.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: app1-frontend-service
          servicePort: 80

Create the resources

To launch the application and configure the resources to expose it outside of the Kubernetes cluster, open a terminal in the directory where you saved the files and execute

 kubectl apply -f .

Verify the ingress

List the ingresses

kubectl get ingress
NAME      HOSTS   ADDRESS     PORTS   AGE
ingress   *       localhost   80      59s

To verify the ingress execute

kubectl describe ingress MY_INGRESS_NAME

Troubleshooting Kubernetes Ingress-Nginx

See Troubleshooting Kubernetes Ingress-Nginx

See Kubernetes Ingress Controllers for more info.

Accessing the application

To access the application through the ingress, open a web browser and access the application via the ADDRESS and PORTS values: http://localhost:80

The browser will display a warning, click the Advanced button

Click the Proceed to localhost (unsafe) link

You should see the NGINX default page

Delete the resources

If you want to delete these resources from the Kubernetes cluster, execute

kubectl delete -f .

Delete the ingress controller service

kubectl delete service ingress-nginx -n ingress-nginx

Delete the deployment

kubectl delete deployment nginx-ingress-controller -n ingress-nginx

Delete the ingress-nginx service

kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml

Delete the ‘nginx-ingress-controller’ deployment

kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

Check if all ingress pods are deleted

kubectl get pods --all-namespaces -l app.kubernetes.io/name=ingress-nginx --watch

Next: Learn Kubernetes part 3 – Traefik Ingress Controller

Kubernetes Dashboard

This is the official Kubernetes dashboard, and for security reasons currently it is not recommended to use, but we can still use it in our local cluster to inspect the resources.

The latest installation information is at https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/

Launch the Kubernetes Dashboard

To launch the Kubernetes Dashboard in your cluster execute the command

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta1/aio/deploy/recommended.yaml

Create a proxy to access the cluster. The proxy will run in the background, to stop it when not needed anymore press CTRL-C.

kubectl proxy

To open the Kubernetes Dashboard web UI in a web browser navigate to http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

Start the Kubernetes Dashboard

If you get the error message: Internal error (500): Not enough data to create auth info structure.

  • Execute the commands to add the token to the ~/.kube/config file
TOKEN=$(kubectl -n kube-system describe secret default| awk '$1=="token:"{print $2}')
kubectl config set-credentials kubernetes-admin --token="${TOKEN}"
  • Copy the token from the ~/.kube/config file to the login screen

Troubleshooting Kubernetes Ingress-Nginx

Use the ingress-nginx Kubernetes plugin to troubleshoot the Kubernetes Ingress-Nginx.

Verify the cluster you are connected to

kubectl config current-context

To switch to another cluster

kubectl config use-context docker-for-desktop

Get the address of the cluster

kubectl cluster-info

Get the list of pods

kubectl get pods --all-namespaces

Forward a port to the pod you want to test. The port forwarder will run in the background, press CTRL-C to stop it.

kubectl port-forward MY_POD_NAME MY_HOST_PORT:MY_POD_PORT

To send a request to the pod, keep the port-forwarding running in the terminal, open a browser and navigate to

http://127.0.0.1:MY_HOST_PORT

Verify if the ingress controller pod is running

kubectl get pods --all-namespaces | grep ingress

Check the cluster ingresses in the default namespace

kubectl ingress-nginx ingresses -n default

Get the Nginx ingress configuration

kubectl ingress-nginx conf -n ingress-nginx

Check if the service is accessible from the ingress pod

kubectl ingress-nginx exec -n ingress-nginx -- curl http://127.0.0.1

Learn Kubernetes part 1 – Web application in a Kubernetes cluster

This is a tutorial to script a simple web application deployment in an enterprise grade Kubernetes cluster that you can follow on your Macintosh. You only need to install Docker and enable Kubernetes.

The frontend of the web application is represented by an NGINX container that listens on port 80 and returns the NGINX default page. The application is exposed outside of the cluster via a kubernetes/ingress-nginx NGINX Ingress Controller, at the address http://localhost

Save all files in the same directory. During the development process open a terminal in the directory of the files, and periodically test the configuration with kubectl apply -f . to check the code (don’t forget the period at the end of the command). This way Kubernetes will build the system step-by-step giving you continuous feedback.

I have used unique label values to demonstrate which labels make the connection between the resources using the label and selector values. Most of the time the application name is used as label for easy maintenance, but as you learn Kubernetes, it is important to understand the relationships between the resources..

Script the deployment

The deployment configures the containers running in the pods and contains the label that has to match the selector of the service.

Connect the deployment to the pods

The label in spec: selector: matchLabels: connects the deployment to the pods specified in the deployment template via the same deployment’s spec: template: metadata: labels:

app1-frontend-deployment.yam
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1-frontend-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: app1-frontend-template-label
  template:
    metadata:
      labels:
        app: app1-frontend-template-label
    spec:
      containers:
        - name: app1-frontend-container-label
          image: nginx:1.7.9
          ports:
          - containerPort: 80

Create the pod

Launch the pod from the terminal. Don’t forget the period at the end of the line.

 kubectl apply -f .

Test the pod

To make sure the container in the pod is running, we can test the pod. Get the list of pods

kubectl get pods
NAME          READY   STATUS    RESTARTS   AGE
MY_POD_NAME   1/1     Running   0          10m

Temporarily set up port forwarding to access the pod from outside of the cluster. We only use this to test the pod.

kubectl port-forward MY_POD_NAME 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

Connect to the pod with a web browser. Navigate to http://127.0.0.1:8080/

You should see the NGINX default page.

To stop the port forwarding, press CTRL-C in the terminal.

See Kubernetes Deployments for more info

Script the service

The service specifies the environment variables of the pods backing the service and exposes the pods to the rest of the Kubernetes cluster or to the outside world.

Connect the service to the pods

The label in the service’s spec: selector: has to match the label in spec: template: metadata: labels: of the deployment.

We will expose the service outside of the cluster with type: LoadBalancer

app1-frontend-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: app1-frontend-service
spec:
  selector:
    app: app1-frontend-template-label
  type: LoadBalancer
  ports:
    - protocol: TCP
      port: 8080
      targetPort: 80

See Kubernetes Services for more info.

Create the resources

To launch the application and configure the resources to expose it outside of the Kubernetes cluster, open a terminal in the directory where you saved the files and execute

 kubectl apply -f .

Accessing the application

To access the application get the address of the service

kubectl get service
NAME                    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
app1-frontend-service   LoadBalancer   10.99.210.235   localhost     8080:32569/TCP   9s

Open a web browser and navigate to the address indicated by the EXTERNAL-IP and PORT: http://localhost:8080

You should see the NGINX default page

Delete the resources

If you want to delete these resources from the Kubernetes cluster, execute

kubectl delete -f .

Next: Learn Kubernetes part 2 – NGINX Ingress Controller

Kubernetes Ingress Controllers

For security reasons it is not a good practice to create individual load balancers for each service.

The safer way is to create one application load balancer outside of the cluster and launch ingress controller NGINX containers to proxy the traffic to the individual services.

Ingress

“Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster.”

To get the list of ingresses

kubectl get ingress

To show the details of an ingress

kubectl describe ingress

Creating an ingress

  • specify the URL of the application in spec: rules: – host:
    • if no host set, this rule handles the request to any URL
  • specify the path this rule applies to at spec: rules: – host: http: paths: – backend: path:
  • set the service name at spec: rules: … backend: serviceName:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: app1-frontend-service
          servicePort: 80

Kubernetes Pods

A pod can contain one or multiple containers, usually one.

Kubernetes only recommends to launch multiple containers in a pod, when those containers need to share a volume. For example a syslog-ng container saves log files in a volume, a Splunk Heavy Forwarder container monitors them and sends the log entries to the Splunk Indexer.

Create a deployment to specify the image that will run in the pod as a container.To list the pods

kubectl get pods

To display the standard out (stdout) messages of a container

kubectl logs MY_POD_NAME

Execute a command in a container of a pod. As usually there is only one container runs in the pod, we don’t have to specify the container name.

kubectl exec -it MY_POD_NAME /bin/bash

If the pod has multiple containers add the –container option

kubectl exec -it MY_POD_NAME --container MY_CONTAINER_NAME /bin/bash

Kubernetes Deployments

You only need a deployment to launch a container in Kubernetes. Deployments tell Kubernetes

  • what container to run by specifying the Docker image name and tag
    • spec: template: spec: containers: – image:
  • when to pull the image from the registry
    • spec: template: spec: containers: imagePullPolicy:
      • If the image is always rebuilt with the same version, like “latest”, set the policy to Always to disable the image caching
  • what to do when the container crashes
    • spec: template: spec: containers: restartPolicy:
      • usually set to Always
  • how many replicas to launch simultaneously
    • spec: replicas
  • how to map this deployment to actual running containers
    • The label in spec: selector: matchLabels: connects the deployment to the pod specified in the deployment template via the same deployment’s spec: template: metadata: labels:
  • the way Kubernetes should replace containers when we update the deployment
    • strategy:
      • rollingUpdate (default)
  • the namespace
    • metadata: namespace:
      • if not specified, the “Default” namespace is used
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app1-frontend-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: app1-frontend-template-label
  template:
    metadata:
      labels:
        app: app1-frontend-template-label
    spec:
      containers:
        - name: app1-frontend-container-label
          image: nginx:1.7.9
          ports:
          - containerPort: 80

To list all deployments

kubectl get deployments

To launch the container specified in the deployment file

kubectl apply -f ./MY_DEPLOYMENT_FILE.yml

Display information about the deployment

kubectl describe deployment MY_DEPLOYMENT

The deployment creates pods. A pod can contain one or multiple containers, usually one. To list the pods

kubectl get pods

Working with Kubernetes in enterprise settings

How many Kubernetes clusters do I need?

Clusters

First, we want to separate the non-production and production environments:

  • Create two Kubernetes clusters for every application or application suite. One for pre-production and one for production.

Namespaces

We also want to separate each non-production and production like environment. Kubernetes offers namespaces to create segregated areas, resources in separate namespaces cannot see each other. Create a namespace for each environment:

  • In the non-production cluster
    • Dev namespace
    • QA namespace
    • UAT namespace
  • In the production cluster
    • Demo namespace
    • Stage namespace
    • Production namespace

To list all resources in a namespace use the -n option in the commands

kubectl get all -n MY_NAMESPACE

Security

Load Balancers

Load balancers are external to the cluster and point to the nodes to expose the applications outside of the cluster.

For security reasons large organizations don’t allow the creation of multiple load balancers. During the cluster creation they temporarily lift the restriction and one ingress load balancer is created. All inbound communication to the cluster passes through that load balancer.

Container images

Do not use the :latest version, as it is hard to control the actual version of the launched image and to roll back to an earlier (unidentified) version.

Use the imagePullPolicy: Always. The Docker caching semantics makes it very efficient, as the layers are cached in the cluster to avid unnecessary downloads..

Order of resource creation

Install the CoreDNS  add-on to provide in-cluster DNS service for pods, so all pods can find all services by name within the cluster.

Create the service before the deployment it refers to, because the service passes environment variables into the deployment when the containers start.

Create the target service and deployment before the caller deployment, so the target is available when the request is generated.

Switch between Kubernetes clusters

Companies launch multiple Kubernetes clusters, and the DevOps team needs access to all of them. The kubectl command-line utility can only work with one cluster at a time. To work with multiple Kubernetes clusters you need to switch between Kubernetes configurations on your workstation.

To connect to a Kubernetes cluster, add the cluster-info to the ~/.kube/config file. If you use AWS EKS the simplest way is to use the AWS CLI to update the file.

aws eks --region MY_REGION update-kubeconfig --name MY_CLUSTER_NAME

To see the configuration values execute

kubectl config view

To test the connectivity execute

kubectl get svc

If you are not the creator of the cluster you will get the error message

error: You must be logged in to the server (Unauthorized)

To access the cluster, in the [default] profile of the ~/.aws/credentials file use the access keys of the account that created the cluster. For more information see How do I resolve an unauthorized server error when I connect to the Amazon EKS API server?

Get the list of configured Kubernetes clusters. The asterisk in the first column of the output shows the currently selected cluster.

kubectl config get-contexts

Switch to another cluster

kubectl config use-context THE_VALUE_OF_THE_CONTEXT_NAME # (the name:attribute of the context)

To remove a cluster from the kube config

Display the config

kubectl config view

Delete the user

kubectl config unset users.THE_NAME_VALUE_OF_THE_USER

Delete the cluster

kubectl config unset clusters.THE_NAME_VALUE_OF_THE_USER

Delete the context

kubectl config unset contexts.THE_NAME_VALUE_OF_THE_CONTEXT