# Adding Traefik to Kubernetes
As you configure, build, and scale your applications and services in any form of distributed system, you need to ensure that you balance demand on those services across resources. With systems that aren't using Kubernetes, this is typically a [load balancer](https://en.wikipedia.org/wiki/Load_balancing_(computing)) that acts as an entry point for each public service, and distributes requests to resources that can handle them.
Instead, Kubernetes uses "[Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/)" to achieve the same task. But instead of creating a load balancer for every public service, Ingress is one entry point to all the public services in a Kubernetes cluster.
To use an Ingress, you need an Ingress controller, and this is where Traefik comes in.
In this blog post, I create a simple application that consists of two services:
- [The Containous whoami Docker image](https://github.com/containous/whoami) which is a Go webserver that prints OS information and HTTP requests.
- A blog for the whoami service built on [the Ghost blogging platform](https://hub.docker.com/_/ghost).
I use [minikube](https://minikube.sigs.k8s.io/docs/) for my cluster, so it's easier to follow along and recreate steps.
## Create the Services
The Services for this post are simple ones to focus on the Traefik implementation.
First, create a folder to hold the Kubernetes objects called _k8s_.
### whoami Service
Create a new file inside the _k8s_ folder called _whoami.yml_, and add the following `Deployment` and `Service` declarations:
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: whoami-deployment
spec:
replicas: 2
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- name: whoami-container
image: containous/whoami
imagePullPolicy: Always
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- name: http
targetPort: 80
port: 80
selector:
app: whoami
```
### Blog Service
For the Ghost blog, create another file called _ghost.yml_ and add the following `Deployment`, `Service`, and `PersistentVolumeClaim`.
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: blog
labels:
app: blog
spec:
replicas: 1
selector:
matchLabels:
app: blog
template:
metadata:
labels:
app: blog
spec:
containers:
- name: blog
image: ghost:3.13.1-alpine
imagePullPolicy: Always
ports:
- containerPort: 2368
env:
- name: url
value: http://blog.minikube
volumeMounts:
- mountPath: /var/lib/ghost/content
name: content
volumes:
- name: content
persistentVolumeClaim:
claimName: blog-content
---
apiVersion: v1
kind: Service
metadata:
name: blog
spec:
selector:
app: blog
ports:
- name: http
port: 80
targetPort: 2368
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: blog-content
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
```
There's nothing that different in this `Service` from the whoami `Service`, except adding a `PersistentVolumeClaim` that Ghost needs to store content data.
## Add Traefik to the Cluster
### Giving Traefik Permission
Traefik uses specific Kubernetes API paths to manage and access the resources it needs. These resources include Services, Endpoints, and Ingresses, and you need to configure [Role Based Access Control (RBAC)](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) to allow access to them.
Create a new YAML file called _traefik.yml_ and add the following `ClusterRole` object:
```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
```
For added security, add a `ClusterRoleBinding` object that restricts the above permissions to only the namespaces (a virtual cluster running on the same physical cluster) that Traefik watches.
Add the YAML below to the same _traefik.yml_ file below the current contents:
```yaml
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: traefik-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: kube-system
```
### Deploy Traefik to Cluster
You can deploy Traefik to a cluster with a [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or a [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/), depending on your requirements for controlling scaling. I use a Deployment, and you can [find out how to use a DaemonSet in the Traefik documentation](https://docs.traefik.io/v1.7/user-guide/kubernetes/#deploy-traefik-using-a-deployment-or-daemonset).
To create the Deployment, you need three different Kubernetes objects, a `ServiceAccount` that defines an account for a process, a `Deployment`, and a `Service`.
Add the following to _traefik.yml_ beneath the current contents:
```yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: traefik-ingress-controller
namespace: kube-system
```
The important key in this configuration is defining the `namespace` the account has access to.
Below, add the following:
```yaml
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: traefik-ingress-controller
namespace: kube-system
labels:
k8s-app: traefik-ingress-lb
spec:
replicas: 1
selector:
matchLabels:
k8s-app: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 60
containers:
- image: traefik:v1.7
name: traefik-ingress-lb
ports:
- name: http
containerPort: 80
- name: admin
containerPort: 8080
args:
- --api
- --kubernetes
- --logLevel=INFO
```
Crucially here, set the `serviceAccountName` to match the `ServiceAccount` defined above, and define the ports that Traefik uses for its Web UI and to access services exposed via Ingress.
Next, add the `Service` for the Ingress itself. It defines and routes both ports:
```yaml
---
kind: Service
apiVersion: v1
metadata:
name: traefik-ingress-service
namespace: kube-system
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- protocol: TCP
port: 80
name: web
- protocol: TCP
port: 8080
name: admin
type: NodePort
```
Add the Traefik Web UI as a separate `Service`, which routes the port it uses back to the Ingress `Service`, as the Ingress handles all incoming requests:
```yaml
---
apiVersion: v1
kind: Service
metadata:
name: traefik-web-ui
namespace: kube-system
spec:
selector:
k8s-app: traefik-ingress-lb
ports:
- name: web
port: 80
targetPort: 8080
```
### Add Traefik as Ingress
With Traefik defined, next, add it to the cluster as an Ingress.
Create a new YAML file called _ingress.yml_ and add the following two `Ingress` definitions:
```yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: traefik-web-ui
namespace: kube-system
spec:
rules:
- host: traefik-ui.minikube
http:
paths:
- path: /
backend:
serviceName: traefik-web-ui
servicePort: web
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: blog.minikube
http:
paths:
- path: /
backend:
serviceName: blog
servicePort: http
- host: whoami.minikube
http:
paths:
- path: /
backend:
serviceName: whoami
servicePort: http
```
The `traefik-web-ui` Ingress routes requests to one sub-domain to the Web UI service using the `serviceName` for that `Service` and the matching `servicePort`. As this Ingress routes traffic to the `kube-system` Namespace, you could use further security measures to ensure that only specific traffic sources can access it.
The `app-ingress` Ingress routes all other requests, matching a sub-domain to the appropriate service and port. This is an example of [name-based routing](https://docs.traefik.io/v1.7/user-guide/kubernetes/#name-based-routing), you can also configure Traefik to match paths of the same domain, [read the documentation for more details](https://docs.traefik.io/v1.7/user-guide/kubernetes/#path-based-routing).
## Deploying the Cluster
With all the configuration files in place, start Minikube, and apply the directory of files with [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/):
```shell
kubectl apply -f k8s
```
As the Ingress uses three subdomains for routing, you need to add these to your _/etc/hosts_ file (Linux and macOS) or _c:\windows\system32\drivers\etc\hosts_ file (Windows), matching the domains to the IP address of your Minikube instance. You can find the IP address by running `minikube ip`. For example:
```text
192.168.64.39 traefik-ui.minikube
192.168.64.39 blog.minikube
192.168.64.39 whoami.minikube
```
As we used a Deployment to start Traefik, you need to know the NodePort for the `traefik-ingress-service`. You can find this by running `minikube service list` and noting the port value after the URL in the `URL` column of the Service.
For example with a port of `30428` use _http://traefik-ui.minikube:30428_ to access the Web UI, _http://blog.minikube:30428_ to access the blog and _http://whoami.minikube:30428_ to access the whoami Service.
{IMGS}
## Next Steps
In this article, I covered adding Traefik as an Ingress to a Kubernetes cluster, and some of the configuration options available to suit your needs.
For more details on Kubernetes configuration, [read this guide in our documentation](https://docs.traefik.io/v1.7/user-guide/kubernetes) and [the configuration section](https://docs.traefik.io/v1.7/configuration/commons/) for more general options.