cc by-sa flurdy

Kubernetes 101

Launch your first application with Kubernetes

Started: March 2019. Last updated: 15th Mar 2019.

Aim

A how to guide to show you how to launch your first application in a Kubernetes cluster.

It will show examples how to create a cluster, create application, services and load balancers so that your application is available on the internet.

And along the way teach some Kubernetes basics (101).

What is Kubernetes?

Kubernetes is both an orchestration and hosting tool for scalable containerised applications.

Kubernetes (often abbreviated k8s) was created by current and former Google staff to copy some of the features of Google's internal tools, notably Borg and others. It was announced and open sourced by Google in 2014.

Since that announcements Kubernetes has been adopted everywhere, and most cloud providers now offer a Kubernetes service, as well as a huge amount of 3rd party tools and services. It has become the orchestration tool for most for many reasons but important one is that it is open source, and not proprietary so companies and projects avoids being locked in to one cloud provider (ish). You can also run k8s on your local machine, and host it yourself on your own metal and colocation servers if you prefer.

Kubernetes basics

Kubernetes has become popular as it provides a lot of automated solution to scaling, self healing, optimisation, rollouts, etc. It has declarative configuration so allows source controlled configuration and easy backup, replication and extension.

K8s features is vast, and below I have summarised a few key components. Over time as you can learn more but this is a starting point.

If you want to learn Kubernetes properly I suggest following Kelsey Hightower's Kubernetes The Hard Way. But as it says on the tin it is quite comprehensive.

Cluster

A Kubernetes cluster is your entire Kubernetes system. Everything is within it.

Node

Nodes are the server instances that run inside your hosting provider to run your Kubernetes cluster on.

Nodes are essentially what you pay for if you use a cloud provider's Kubernetes offering. There are two different nodes, "worker nodes" and "management nodes". Some of the cloud providers provide the master nodes for free.

Deployment

Basically your application. A Docker containerised application that can be scaled.

There are many other types of "deployment": e.g. cron jobs, etc, but for the purpose of this guide, and most common use, think of it as your actual application.

Pod

A pod is what you actually horizontally scale across nodes. It wraps your deployment container as a unit.

It may include other deployments that you want associated closely with each other. And it can include sidecar containers as well. Most of the time though a container contains just one deployment.

A pod is often what you interact with when you are interacting and debugging something running on your Kubernetes cluster.

Service

The component that exposes your Deployment application. Mostly just internally inside your cluster, but can also be exposed externally. You can expose it externally directly via a NodePort service type in a crude but often useful manner. Or via Load Balancer (see below), or via an Ingress which is the most extensible option.

Load Balancer

A load balancer is a type of service that distributes traffic to your services from the internet. It will be associated with an external IP address. If you run Kubernetes on your own hardware it will deploy as a specific service. But if you use a cloud provider it might utilise that providers custom load balancer. Think ELB if on AWS for example, or Digital Ocean's cloud controller. Kubernetes will provision and configure the cloud provided load balancer automatically and invisibly for you.

Ingress

Ingresses lets you define which service gets traffic redirected to them. Usually with a load balancer in front to expose it. A typical configuration is routing via request host or path.

E.g. If you host several apps or websites in the same cluster, you might configure the Ingress so that requests to www.example.com goes to one service and www.dummy.org goes to another.

Or request to paths starting with /shoppingcart goes to one and /admin goes to another.

Config map

Config maps are the run time configuration and environment variables you define to configure your applications.

Secrets

Secrets are a special type of encrypted config maps. Suitable for database connections, API tokens, encryption keys and more.

And more

And there is so much more to Kubernetes. Namespaces, cluster issuers, cronjobs, storage options, DNS, liveness probes, memory limits, autoscaling, etc, etc.

Create cluster

Your first decision is where to create your Kubernetes cluster. On your laptop/desktop, your own servers or a cloud provider.

Local cluster

You can deploy a cluster locally. With amongst others:

Having a local cluster is handy for development but not essential. I suggest delaying setting up a local one until you have a real cluster running elsewhere.

Your own servers

You can follow Kubernetes The Hard Way to create a cluster on your own servers from scratch.

Or follow some ready made guides for server instances from a cloud provider:

Cloud provider k8s service

The quickest and easiest way to create a Kubernetes cluster is to use a cloud provider's Kubernetes service.

EKS by AWS is probably the most used, primarly because so many already use AWS. However it is not cheap.

GKE was launched a long time before the others, and is comprehensive. Google's offering seems to be the one where Kubernetes is the priority of their entire cloud offering. For most people I would suggest taking a look at GKE first.

I have heard good things about AKS, as I have with a lot of the new Microsoft. But I have not tried it yet.

Digital Ocean's Kubernetes is still in Βeta and is the newest. It is however my prefered Kubernetes provider as it is quite polished and uncomplicated.

3rd party providers and tools

Many tools exists to abstract the creation and management of clusters in different ways. Some offer their own hosting, some allows abstract multi cloud clusters, some a fancy UI, etc. For example:

Some of these are really good. But I would suggest using vanilla Kubernetes initially to understand it properly. You might find you don't need any abstractions on top of it.

Create a Digital Ocean cluster

You can create a cluster wherever you prefer, but for the purpose of this howto I would assume it was with Digital Ocean. As Kubernetes is fairly agnostic the rest of the howto is pretty much the same wherever and with whomever your cluster was created.

Login or create an account with Digital Ocean. You can use my referral link.

You may need to activate their Kubernetes product if it is still in Βeta. Create a new cluster in the admin tool.

Cluster Nodes size

During cluster creation you will probably be asked to specify nodes required. Digital Ocean defaults to 3 nodes of the 2gb memory instance. I suggest changing that to 1 node and perhaps 4gb.

You can change and scale this differently later when you know your requirements better. And then you might need more redundancy and scaling requirements.

Make sure you download the config that you will use in the next section to set up kubectl.

Note, Digital Ocean's Kubernetes config certificates are rotated weekly. So you will have to update your ~/.kube/config every time. A bit annoying, an security feature that I hope they create a more smoother work around soon.

Local Kubernetes tools

Install kubectl

To connect to and control remote Kubernetes clusters you need the kubectl command line tool.

It is available for most operating systems and environments. It is available as .deb & .rpm for Linux OSes.

As an Ubuntu Snap package:

sudo snap install kubectl --classic

For MacOs you can install it via Homebrew:

brew install kubernetes-cli

And various options if on Windows.

Config context

When you created your cluster your provider hopefully supplied a context config for you. Open ~/.kube/config and insert these in to it.

It should contain a cluster section, a user section, and a context section that ties these together. Over time this file will contain config for other staging and production clusters, so make sure no YAML typos are made.

Connect

Check if the context is correct with this command that connects your cluster:

kubectl cluster-info

Any "can not log in" errors usually means there is a typo in your YAML config.

Deploy a dummy application

We will use a real application later. But first we will use a simple dummy one to test the tracer bullet stack all the way.

A simple Nginx webserver image will suffice, and Hashicorp also provides a special echo image that is very suitable for this.

Echo deployment

Open a deployment.yml file:

vi echo1-deployment.yml apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo1
spec:
  selector:
    matchLabels:
      app: echo1
  replicas: 2
  template:
    metadata:
      labels:
        app: echo1
  spec:
    containers:
    - name: echo1
      image: hashicorp/http-echo
      args:
      - "-text=echoNumberOne"
      ports:
      - containerPort: 5678

Note, I strongly suggest your version control all your Kubernetes configuration. Either as part of a project repository in e.g. a root kube folder. Or as separate config repositories.

Lets apply this to your Kubernetes cluster:

kubectl apply -f echo1-deployment.yml

You can use the apply or create keyword on initial creation. But only apply to update an existing one from then on.

You can see if it deployed without problems with:

kubectl get deployments

The output should be something like:

NAME    DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
echo1   2        2        2           0          2s
Echo service

We will put a simple service on top that routes port 80 to the replicated containers port 5678.

vi echo1-service.yml apiVersion: v1
kind: Service
metadata:
  name: echo1
spec:
  ports:
  - port: 80
  targetPort: 5678
  selector:
    app: echo1

Note, you can combine the deployment and service into one YAML file, seperated by a line containing ---. Some people prefer that. I don't.

Before we apply this Kubernetes lets check if anything is running. There should be a kubernetes service on port 443 and nothing else:

kubectl get services NAME        TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE
kubernetes  ClusterIP  10.24.0.1      <none>       443/TCP  2d

Then lets apply the echo service to Kubernetes:

kubectl apply -f echo1-service.yml

And the status of the service(s):

kubectl get services NAME        TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE
echo1       ClusterIP  10.24.40.234   <none>       80/TCP   5m
kubernetes  ClusterIP  10.24.0.1      <none>       443/TCP  2d

Note, no external port.

You can expose this service directly to the internet by changing the service to a NodePort type. Or you can add an external IP by changing the service to a LoadBalancer type.

Echo load balancer

Below we will modify the echo1 service to be a load balancer.

vi echo1-service-loadbalancer.yml apiVersion: v1
kind: Service
metadata:
  name: echo1
spec:
  type: LoadBalancer
  ports:
  - port: 80
  targetPort: 5678
  selector:
    app: echo1

Note there was only one line added from the original service, the type set to LoadBalancer in the spec.

kubectl apply -f echo1-service-loadbalancer.yml kubectl get services NAME        TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE
echo1       ClusterIP  10.24.40.234   <pending>    80/TCP   5m
kubernetes  ClusterIP  10.24.0.1      <none>       443/TCP  2d

Note the pending in external IP column. If you keep polling with that command eventualy this changes to:

NAME        TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE
echo1       ClusterIP  10.24.40.234   1.2.3.4      80/TCP   5m
kubernetes  ClusterIP  10.24.0.1      <none>       443/TCP  2d

You can now curl or use a browser to reach that external IP. Or add it your /etc/hosts file or a DNS.

curl 1.2.3.4 echoNumberOne

Your application

You now have an application hosted with Kubernetes exposed to the internet. If you want to now is the time to change the echo deployment to your own applications' Docker container image(s).

Docker build

To go into detail on how to build a Docker image is out of scope of this howto. However for a simple example please refer to my Migrate blog to Jekyll howto which describes how to use CircleCI to build a docker image, and how to upload it to a 3rd party Docker registry (Quay.io).

Docker registry

To use any publicly available images from Docker hub just refer to the image name and tag in your deployment's spec/containers/image section.

...
spec:
  containers:
  - name: anotherapp
    image: somecompany/someimage:sometag
...

However most of the time you will use images you have created, that are private and not publicly available. They may even be stored in a 3rd party Docker Registry, for example:

For that you need to configure the authentication that Docker will use to download the image. This is done via Kubernetes Secrets.

In this example I have upload an image to Quay.io. Quay recommends using Robot accounts, so I have set one up with my private repository with read access. Using its username and token I create a docker-registry Secret:

kubectl create secret docker-registry MYAPP-registry --docker-server=quay.io \
  --docker-username:MYROBOTUSER --docker-password=MYROBOTPASSWORD

Then modify the deployment file to add a imagePullSecrets to the spec/template/spec section. For example if we just reused the echo1 configuration:

vi echo1-deployment.yml apiVersion: apps/v1
kind: Deployment
metadata:
  name: echo1
spec:
  selector:
    matchLabels:
      app: echo1
  replicas: 2
  template:
    metadata:
      labels:
        app: echo1
  spec:
    containers:
    - name: echo1
      image: MYORG/MYAPP:MYTAG
      imagePullSecrets:
        - name: MYAPP-registry
      ports:
      - containerPort: 5678
kubectl apply -f echo1-deployment.yml

This should download the container image from the private repository. As this often goes wrong due to typos or permissions, it is worth checking the status of your pods for a little while.

kubectl get pods --selector app=echo1

If you see ImagePullBackOff as status then you have some checking to do. Otherwise if it says Running then it worked.

Multiple applications

Using Kubernetes for a single application is significant overkill.

In part two of this Kubernetes introduction we will add an Ingress controller so you can route traffic to multiple applications in the same cluster.

We will add a Cluster Issuer so that we can use Let's Encrypt to add a TLS certificate to your site(s). And that howto will at the same time introduce Helm so that installing complicated applications in Kubernetes becomes very simple.

Please continue to Kubernetes Ingress & ssl with helm for a more extendable and manageable Kubernetes cluster.

Feedback

Please fork and send a pull request for to correct any typos, or useful additions.

Buy a t-shirt if you found this guide useful. Hire me for short term advice or long term consultancy.

Otherwise contact me. Especially for things factually incorrect. Apologies for procrastinated replies.