In my last blog post I talked about setting up your own Kubernetes cluster at home. Since we don’t run our cluster on a big cloud platform, we have no access to comfortable tools like a built-in load balancer.
Time to go ashore
But, don’t despair! We can deploy our own load balancer using the Kubernetes Ingress object. It allows us to define routing rules and use a backend of our choice that uses these rules to route traffic to our cluster. And the most exiting thing: to build an Ingress controller is really simple.
But, since I don’t want to get into building your own controller yet (at least this time), we will use a
proven ecosystem. Luckily, the nginx-ingress
is maintained by the Kubernetes project.
cURL and apply!
Setting up the basic infrastructure is once again copying and pasting commands.
# For more information, see https://kubernetes.github.io/ingress-nginx/deploy/#mandatory-command
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml
Now our backend should be configured successfully. Maybe you already noticed that we did not expose anything yet, no IP to access, nothing. That is mostly because the frontend service is custom for each cluster deployment.
echo > ingress-nginx.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
externalTrafficPolicy: Cluster
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
- name: https
port: 443
protocol: TCP
targetPort: 443
selector:
app: ingress-nginx
sessionAffinity: None
type: NodePort
externalIPs:
- <your-master-ip>
EOF
kubectl apply -f ingress-nginx.yaml
Remember to replace the external IP with your own master node IP. If you have your own DNS running (or this is a public cluster), I recommend to point add a A record to it.
Exposing our blog
Let’s assume you have your blog running on Kubernetes and exposed it using a Service called blog
. To expose this application using your new nginx-ingress
,
you just need to add the routing rules to your cluster.
echo > blog-ingress.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: blog
annotations:
kubernetes.io/ingress.class: nginx
spec:
backend:
serviceName: blog
servicePort: 80
EOF
kubectl apply -f blog-ingress.yaml
In the case, you have a domain name pointing to your Ingress, you can also use host-based routing.
echo > blog-ingress.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: blog
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: <your-blog-domain>
http:
paths:
- backend:
serviceName: blog
servicePort: 80
path: /
EOF
kubectl apply -f blog-ingress.yaml
Adding certificates using Let’s Encrypt
Of couse, if you publish content on the web, you want to serve it encrypted to your users. Once upon a time, using HTTPS was hard and expensive because you had to buy a certificate from a trusted company. Luckily, again, times have changed, and there is this wonderful initiative called Let’s Encrypt and a cluster component called certmanager
.
To install certmanager
and setup our ClusterIssuers, we have to run a few commands again.
# install certmanager
kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/master/contrib/manifests/cert-manager/with-rbac.yaml
# setup ClusterIssuer for letsencrypt-staging
echo > letsencrypt-staging.yaml <<EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
server: https://acme-staging.api.letsencrypt.org/directory
email: <your-email-address>
privateKeySecretRef:
name: letsencrypt-staging
http01: {}
EOF
kubectl apply -f letsencrypt-staging.yaml
# setup ClusterIssuer for letsencrypt-prod
echo > letsencrypt-prod.yaml <<EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: <your-email-address>
privateKeySecretRef:
name: letsencrypt-prod
http01: {}
EOF
kubectl apply -f letsencrypt-prod.yaml
Remember to replace <your-email-address>
with your own email. When you want to test your deployments, you can use letsencrypt-staging
as a test issuer to check out if things work out as expected (because you do not have to deal with rate limits and such). Of couse, when you have a stable version running you should move to use letsencrypt-prod
because the staging issuer is not trusted by default.
We finally have to update our Ingress, so we can test if our auto-encryption works well!
echo > blog-ingress.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: blog
annotations:
kubernetes.io/ingress.class: nginx
certmanager.k8s.io/cluster-issuer: letsencrypt-staging
spec:
tls:
- hosts:
- <your-blog-domain>
secretName: blog-tls
rules:
- host: <your-blog-domain>
http:
paths:
- backend:
serviceName: blog
servicePort: 80
path: /
EOF
kubectl apply -f blog-ingress.yaml
Finally, our blog should be exposed using HTTPS! Remember to replace letsencrypt-staging
with letsencrypt-prod
when you are going public.