Hope you were having fun with my Kubernetes tutorial!
Today, I've got a new comment from Thorsten Hirsch, asking about TLS certificate. Well, @thorstenhirsch, I'm interested as with this thing as well :), and it was the first thing I had to check while I was learning Kubernetes. So I'm gonna share with you guys how did I solve this problem.
Requirement
We'd like to add a TLS certificate to our application, which we built here
Problem
What? problem? there is no problem! we were using certbot
thousands times before. Just install it, run a magical script to get a free Let's Encrypt, then config it with nginx.
If you are thinking like this, wait a second and think about this:
- Your application lives in read-only containers, managed by Pods. And Pods are added/removed dynamically, by your configurations. And you will need to give nginx certificates when you start it.
- You can generate certificates, then pack it within the image, and deloy it. It's a terrible solution. Because you'd need to deploy every time the certificates are gonna expired.
- You can store it in a shared volume, and config nginx to take certificates from it. It's another terrible solution. Because you'd still need to manage the certificates yourself (even when you want to run it via a cron?)
- ...
Solution
Let's try to think about another solution (Spoiler: It's awesome):
- We will keep our infrastructure as before, with minimum modifications. No changes to application, no changes to nginx, no changes to deployment.
- TLS certificate will be monitored and renewed automatically.
First of all, I'd like to introduce you a new guy - Ingress.
Basically, Ingress is like a Router, which takes the incoming traffic then passes it to the corresponding Service, with the help from Ingress Controllers.
Simple example: You have 3 services:
-
apple-service
(which runs apple.com website, selling fruits) -
pineapple-service
(another website - pineapple.com, selling phone, tablet, computer,...) -
pineapple-cloud-service
(offering some cloud services for Pineapple's clients)
What you want is:
- apple.com will be pointed to
apple-service
- pineapple.com will be pointed to
pineapple-service
- pineapple.com/cloud will be pointed to
pineapple-cloud-service
Ingress comes to help you here: Just need to create an Ingress object, with routing rules you want, then point those domains into the Ingress IP. And everything will be handled correctly!
Preparing stuffs
Before everything, we need to prepare stuffs for our Ingress configuration.
Install ingress-nginx
ingress-nginx is a Ingress Controller, which helps Ingress to route the traffic easily.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
For this Lab, we'd like to expose the Ingress with the Kube master's IP (192.168.1.33
as we configured before), so let's edit this controller a bit:
kubectl edit svc/ingress-nginx -n ingress-nginx
spec:
externalIPs:
- 192.168.1.33
Install cert-manager
cert-manager is a Kubernetes controller, which helps you to manage certificates without pain. We're gonna use it to request & renew Let's encrypt TLS certificate for our application.
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.12.0/cert-manager.yaml
Check all the pods are running correctly
kubectl get pods --namespace cert-manager
Setup Ingress & TLS
Back to our infrastructure: Before we were using a LoadBalancer as the start point, now we can just change it to stay behind in the cluster, and add an Ingress as the start point.
Before
(Users) ---> Service ---> Pods
Now
(Users) ---> Ingress ---> Service ---> Pods
We're gonna edit a bit the service file service-loadbalancer.yml
(We've created it before)
apiVersion: v1
kind: Service
metadata:
name: service-loadbalancer
spec:
selector:
name: templated-pod
type: ClusterIP
ports:
- name: http
nodePort: null
port: 80
targetPort: 80
protocol: TCP
# type: LoadBalancer
# ports:
# - port: 80
# targetPort: 80
# externalIPs:
# - 192.168.1.33
Apply it
kubectl apply -f service-loadbalancer.yml
Create a ClusterIssuer
Create new file cert_issuer.yml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-prod-site
namespace: cert-manager
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: YOUR-EMAIL@HERE.TLD
privateKeySecretRef:
name: letsencrypt-prod-site
solvers:
- http01:
ingress:
class: nginx
Apply it
kubectl apply -f cert_issuer.yml
Create an Ingress
Here comes our Rockstar tonight: ingress.yml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod-site"
spec:
tls:
- hosts:
- YOUR-DOMAIN-HERE.TLD
secretName: site-tls
rules:
- host: YOUR-DOMAIN-HERE.TLD
http:
paths:
- path: /
backend:
serviceName: service-loadbalancer
servicePort: 80
Nothing special:
- Line 12: Define your hostname which will run under this Ingress
- Line 16-22: Define a rule, to tell this Ingress: When user browses
YOUR-DOMAIN-HERE.TLD
, under path/
, you'd like to pass him to the serviceservice-loadbalancer
on port80
(defined inservice-loadbalancer.yml
)
Remember with this Lab, you must point YOUR-DOMAIN-HERE.TLD to your IP address, then forward all traffic on port 80
and 443
to the kube-master IP 192.168.1.33
Show time:
kubectl apply -f ingress.yml
*Note: requesting/renewing a new certificate from Let's encrypt could take some minutes. You can monitor here: *
kubectl describe certificate site-tls
Now you can try to browser https://YOUR-DOMAIN-HERE.TLD/ on your favorite web browser, and open a prosecco :)
I've updated the repository https://gitlab.com/martinpham/kubernetes-fun, so you can take all the files we were talking about.
Top comments (0)