Part 1

Introduction to Networking

Now back to development! Restarting and following logs has been a treat. Next, we'll open an endpoint to the application and access it via HTTP.

Simple networking application

Let's develop our application so that it has an HTTP server responding with two hashes: a hash that is stored until the process is exited and a hash that is request specific. The response body can be something like "Application abc123. Request 94k9m2". Choose any port to listen to.

I've prepared one here. By default, it will listen on port 3000.

$ kubectl apply -f
  deployment.apps/hashresponse-dep created

Connecting from outside of the cluster

We can confirm that the hashresponse-dep is working with the port-forward command. Let's see the name of the pod first and then port forward there:

$ kubectl get po
  NAME                                READY   STATUS    RESTARTS   AGE
  hashgenerator-dep-5cbbf97d5-z2ct9   1/1     Running   0          20h
  hashresponse-dep-57bcc888d7-dj5vk   1/1     Running   0          19h

$ kubectl port-forward hashresponse-dep-57bcc888d7-dj5vk 3003:3000
  Forwarding from -> 3000
  Forwarding from [::1]:3003 -> 3000

Now we can view the response from http://localhost:3003 and confirm that it is working as expected.

External connections with docker used the flag -p -p 3003:3000 or in docker-compose ports declaration. Unfortunately, Kubernetes isn't as simple. We're going to use either a Service resource or an Ingress resource.

Before anything else

Because we are running our cluster inside docker with k3d we will have to do a few preparations. Opening a route from outside of the cluster to the pod will not be enough if we have no means of accessing the cluster inside the containers!

$ docker ps
  CONTAINER ID        IMAGE                      COMMAND                  CREATED             STATUS              PORTS                             NAMES
  b60f6c246ebb        rancher/k3d-proxy:v3.0.0   "/bin/sh -c nginx-pr…"   2 hours ago         Up 2 hours          80/tcp,>6443/tcp   k3d-k3s-default-serverlb
  553041f96fc6        rancher/k3s:latest         "/bin/k3s agent"         2 hours ago         Up 2 hours                                            k3d-k3s-default-agent-1
  aebd23c2ef99        rancher/k3s:latest         "/bin/k3s agent"         2 hours ago         Up 2 hours                                            k3d-k3s-default-agent-0
  a34e49184d37        rancher/k3s:latest         "/bin/k3s server --t…"   2 hours ago         Up 2 hours                                            k3d-k3s-default-server-0

K3d has helpfully prepared us a port to access the API in 6443 and, in addition, has opened a port to 80. All requests to the load balancer here will be proxied to the same ports of all server nodes of the cluster. However, for testing purposes, we'll want an individual port open for a single node. Let's delete our old cluster and create a new one with port some ports open.

K3d documentation tells us how the ports are opened, we'll open local 8081 to 80 in k3d-k3s-default-serverlb and local 8082 to 30080 in k3d-k3s-default-agent-0. The 30080 is chosen almost completely randomly, but needs to be a value between 30000-32767 for the next step:

$ k3d cluster delete
  INFO[0000] Deleting cluster 'k3s-default'
  INFO[0002] Successfully deleted cluster k3s-default!

$ k3d cluster create --port '8082:30080@agent[0]' -p 8081:80@loadbalancer --agents 2
  INFO[0000] Created network 'k3d-k3s-default'
  INFO[0021] Cluster 'k3s-default' created successfully!
  INFO[0021] You can now use it like this:
  kubectl cluster-info

$ kubectl apply -f
  deployment.apps/hashresponse-dep created

Above the "agent[0]" and "loadbalancer" are based on k3d documentation and reading code from here and here

Now we have access through port 8081 to our server node (actually all nodes) and 8082 to one of our agent nodes port 30080. They will be used to showcase different methods of communicating with the servers.

What is a Service?

As Deployment resources took care of deployments for us. Service resource will take care of serving the application to connections from outside of the cluster.

Create a file service.yaml into the manifests folder and we need the service to do the following things:

  1. Declare that we want a Service
  2. Declare which port to listen to
  3. Declare the application where the request should be directed to
  4. Declare the port where the request should be directed to

This translates into a yaml file with contents


apiVersion: v1
kind: Service
  name: hashresponse-svc
  type: NodePort
    app: hashresponse # This is the app as declared in the deployment.
    - name: http
      nodePort: 30080 # This is the port that is available outside. Value for nodePort can be between 30000-32767
      protocol: TCP
      port: 1234 # This is a port that is available to the cluster, in this case it can be ~ anything
      targetPort: 3000 # This is the target port
$ kubectl apply -f manifests/service.yaml
  service/hashresponse-svc created

As we've published 8082 as 30080 we can access it now via http://localhost:8082.

We've now defined a nodeport with type: NodePort. NodePorts simply ports that are opened by Kubernetes to all of the nodes and the service will handle requests in that port. NodePorts are not flexible and require you to assign a different port for every application. As such NodePorts are not used in production but are helpful to know about.

What we'd want to use instead of NodePort would be a LoadBalancer type service but this "only" works with cloud providers as it configures a, possibly costly, load balancer for it. We'll get to know them in part 3.

There's one additional resource that will help us with serving the application, Ingress.

What is an Ingress?

Incoming Network Access resource Ingress is a completely different type of resource from Services. If you've got your OSI model memorized, it works in layer 7 while services work on layer 4. You could see these used together: first the aforementioned LoadBalancer and then Ingress to handle routing. In our case, as we don't have a load balancer available we can use the Ingress as the first stop. If you're familiar with reverse proxies like Nginx, Ingress should seem familiar.

Ingresses are implemented by various different "controllers". This means that ingresses do not automatically work in a cluster, but gives you the freedom of choosing which ingress controller works for you the best. K3s has Traefik installed already. Other options include Istio and Nginx Ingress Controller, more here.

Switching to Ingress will require us to create an Ingress resource. Ingress will route incoming traffic forward to a Services, but the old NodePort Service won't do.

$ kubectl delete -f manifests/service.yaml
  service "hashresponse-svc" deleted

A ClusterIP type Service resource gives the Service an internal IP that'll be accessible in the cluster.

The following will let TCP traffic from port 2345 to port 3000.


apiVersion: v1
kind: Service
  name: hashresponse-svc
  type: ClusterIP
    app: hashresponse
    - port: 2345
      protocol: TCP
      targetPort: 3000

For resource 2 the new Ingress.

  1. Declare that it should be an Ingress
  2. And route all traffic to our service


apiVersion: extensions/v1beta1
kind: Ingress
  name: dwk-material-ingress
  - http:
      - path: /
          serviceName: hashresponse-svc
          servicePort: 2345

Then we can apply everything and view the result

$ kubectl apply -f manifests/service.yaml
  service/hashresponse-svc created
$ kubectl apply -f manifests/ingress.yaml
  ingress.extensions/dwk-material-ingress created

$ kubectl get svc
  NAME               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
  kubernetes         ClusterIP      <none>        443/TCP    23h
  hashresponse-svc   ClusterIP   <none>        2345/TCP   4m23s

$ kubectl get ing
  NAME                    HOSTS   ADDRESS      PORTS   AGE
  dwk-material-ingress    *   80      77s

We can see that the ingress is listening on port 80. As we already opened port there we can access the application on http://localhost:8081.


Login to view the exercise

You have reached the end of this section! Continue to the next section: