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).
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 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.
A Kubernetes cluster is your entire Kubernetes system. Everything is within it.
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.
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.
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.
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.
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.
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 maps are the run time configuration and environment variables you define to configure your applications.
Secrets are a special type of encrypted config maps. Suitable for database connections, API tokens, encryption keys and more.
And there is so much more to Kubernetes. Namespaces, cluster issuers, cronjobs, storage options, DNS, liveness probes, memory limits, autoscaling, etc, etc.
Your first decision is where to create your Kubernetes cluster. On your laptop/desktop, your own servers or a cloud provider.
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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
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).
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).
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.
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.
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.