Self managed Kubernetes with Nginx Ingress

Posted by

In this tutorial we see how to use Nginx-Ingress on a single Kubernetes node. This tutorial is working on bare-metal and cloud instances. We will use Ubuntu as base but it should be similar on other systems.

Goals:

  • Working Nginx Ingress
  • Certificates generation with Let’s Encrypt
  • Get the real visitor’s IP

Requirements:

  • An updated Ubuntu system
  • Kubernetes up and running on the private interface
  • A private Network Interface
  • Helm3 binary

For the private Network Interface, you can just add another IP to your primary interface. This network will be used for the Load Balancer.

We will use the 172.16.1.0/24 network and reserve 172.16.1.250 for the Load Balancer.

The Reverse Proxy

First we will need to install and configure Nginx as reverse Proxy. It will listen on our Public Interface and redirect the traffic to the Load Balancer.

curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
echo "deb http://nginx.org/packages/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
sudo apt update
sudo apt install nginx

Let’s configure the Reverse Proxy, in /etc/nginx/nginx.conf

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
  worker_connections  1024;
}

http {
  default_type  application/octet-stream;

  log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

  access_log  off;

  sendfile    on;
  tcp_nopush  on;
  tcp_nodelay on;

  keepalive_timeout  65;

  gzip  on;
  gzip_min_length 10240;
  gzip_proxied expired no-cache no-store private auth;
  gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/json application/xml;
  gzip_disable msie6;

  server_tokens off;
}

stream {
  server {
    listen 80;
    proxy_protocol on;
    proxy_pass 172.16.1.250:80;
  }
  server {
    listen 443;
    proxy_protocol on;
    proxy_pass 172.16.1.250:443;
  }
}

Don’t forget to restart Nginx.

service nginx restart

Configure the Load Balancer

We will do it easily with Metal-lb. Create the metallb-config.yml file (the default pool part is not required).

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 172.16.1.251/32
    - name: nginx-ingress
      protocol: layer2
      addresses:
      - 172.16.1.250/32

Let’s configure everything

kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.9.3/manifests/namespace.yaml
kubectl apply -f https://raw.githubusercontent.com/google/metallb/v0.9.3/manifests/metallb.yaml
kubectl create secret generic -n metallb-system memberlist --from-literal=secretkey="$(openssl rand -base64 128)"
kubectl apply -f metallb-config.yml

Install the Nginx Ingress

The Nginx Ingress service will receive the connection and redirect it to the wanted service. Create the configuration file, nginx-config.yml.

apiVersion: v1
kind: Namespace
metadata:
  name: nginx-ingress
  labels:
    name: nginx-ingress
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-ingress-controller
  namespace: nginx-ingress
  labels:
    app.kubernetes.io/name: nginx-ingress
    app.kubernetes.io/part-of: nginx-ingress
data:
  proxy-protocol: "True"
  use-proxy-protocol: "True"
  real-ip-header: "proxy_protocol"

We will install the Ingress controller

kubectl apply -f nginx-config.yml
helm repo add stable https://kubernetes-charts.storage.googleapis.com
helm install nginx-ingress stable/nginx-ingress --namespace nginx-ingress --set controller.service.externalTrafficPolicy=Local --set controller.service.annotations."metallb\.universe\.tf/address-pool"=nginx-ingress

Certificate management

For this part we will use cert-manager. Create the configuration file, cert-manager.yml.

---
global:
  rbac:
    create: true
  leaderElection:
    namespace: cert-manager
installCRDs: true
ingressShim:
  defaultIssuerName: letsencrypt-prod
  defaultIssuerKind: ClusterIssuer

Create cluster-issuer.yml

apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-stage
spec:
  acme:
    # The ACME server URL
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: youremail@domain.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-stage
    # Enable the HTTP-01 challenge provider
    solvers:
    - http01:
        ingress:
          class: nginx
---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: youremail@domain.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-prod
    # Enable the HTTP-01 challenge provider
    solvers:
    - http01:
        ingress:
          class: nginx

Let’s create Everything

kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager --namespace cert-manager --values cert-manager.yml
kubectl apply -f cluster-issuer.yml

Conclusion

We now have Kubernetes with Load Balancer compatibility, Ingress compatibility and automatic certificate deployment. You can create your Ingress objects using the following configuration.

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    app.kubernetes.io/name: nginx-ingress
    app.kubernetes.io/part-of: nginx-ingress
    ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  tls:
  - hosts:
    - yourhost.com
    secretName: tls-secret
  rules:
  - host: "yourhost.com"
    http:
      paths:
      - path: /
        backend:
          serviceName: yourservice
          servicePort: 80

Leave a Reply

Your email address will not be published. Required fields are marked *