TLS Web App
In this tutorial you will deploy a TLS-secured nginx application on a local Kubernetes cluster. You will push a container image to the Local Registry, expose it through Envoy Gateway with HTTPS termination, use cert-manager to issue a self-signed TLS certificate, and rely on MetalLB to assign the gateway an external IP — all without any manual addon installation.
Prerequisites
Section titled “Prerequisites”- kinder installed — see Installation
- Docker (or Podman) installed and running
kubectlinstalled and on PATHcurlinstalled (HTTPS verification uses the-kflag to accept the self-signed certificate)
Step 1: Create the cluster
Section titled “Step 1: Create the cluster”kinder create clusterExpected output:
Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.32.0) 🖼 ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 ✓ Installing addons metallb 1.8s local-registry 0.3s envoy-gateway 4.2s cert-manager 38.1sSet kubectl context to "kind-kind"Step 2: Verify addons are ready
Section titled “Step 2: Verify addons are ready”Check that all cert-manager components are running:
kubectl get pods -n cert-managerExpected output:
NAME READY STATUS RESTARTS AGEcert-manager-... 1/1 Running 0 60scert-manager-cainjector-... 1/1 Running 0 60scert-manager-webhook-... 1/1 Running 0 60sCheck that the Envoy Gateway controller is running:
kubectl get pods -n envoy-gateway-systemExpected output:
NAME READY STATUS RESTARTS AGEenvoy-gateway-... 1/1 Running 0 60sCheck that MetalLB is running:
kubectl get pods -n metallb-systemExpected output:
NAME READY STATUS RESTARTS AGEcontroller-... 1/1 Running 0 60sspeaker-... 1/1 Running 0 60sVerify the local registry container is running:
docker ps --filter name=kind-registryExpected output:
CONTAINER ID IMAGE ... PORTS NAMESabc123 registry:2 ... 0.0.0.0:5001->5000/tcp kind-registryStep 3: Push an image to the local registry
Section titled “Step 3: Push an image to the local registry”Pull the nginx Alpine image, tag it for the local registry, and push it:
docker pull nginx:alpinedocker tag nginx:alpine localhost:5001/myapp:v1docker push localhost:5001/myapp:v1Expected output:
v1: digest: sha256:a1b2c3d4e5f6... size: 8192Step 4: Deploy the application
Section titled “Step 4: Deploy the application”Apply the Deployment and Service in one step:
kubectl apply -f - <<EOFapiVersion: apps/v1kind: Deploymentmetadata: name: myappspec: replicas: 1 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp image: localhost:5001/myapp:v1 ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: myappspec: selector: app: myapp ports: - port: 80 targetPort: 80EOFVerify the pod is running:
kubectl get podsExpected output:
NAME READY STATUS RESTARTS AGEmyapp-... 1/1 Running 0 15sStep 5: Create a TLS certificate
Section titled “Step 5: Create a TLS certificate”Apply a Certificate resource that cert-manager will fulfill using the pre-installed selfsigned-issuer:
kubectl apply -f - <<EOFapiVersion: cert-manager.io/v1kind: Certificatemetadata: name: myapp-tls namespace: defaultspec: secretName: myapp-tls issuerRef: name: selfsigned-issuer kind: ClusterIssuer dnsNames: - myapp.localEOFWait for the certificate to become ready:
kubectl get certificate myapp-tlsExpected output:
NAME READY SECRET AGEmyapp-tls True myapp-tls 15sStep 6: Create the TLS gateway
Section titled “Step 6: Create the TLS gateway”Apply a Gateway that terminates TLS using the certificate secret:
kubectl apply -f - <<EOFapiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: myapp-gatewayspec: gatewayClassName: eg listeners: - name: https protocol: HTTPS port: 443 tls: mode: Terminate certificateRefs: - name: myapp-tlsEOFWait for MetalLB to assign an external IP and for the gateway to become programmed:
kubectl get gateway myapp-gatewayExpected output:
NAME CLASS ADDRESS PROGRAMMED AGEmyapp-gateway eg 172.20.255.200 True 20sStep 7: Create an HTTPRoute
Section titled “Step 7: Create an HTTPRoute”Route incoming HTTPS traffic to the myapp service:
kubectl apply -f - <<EOFapiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: myapp-routespec: parentRefs: - name: myapp-gateway rules: - backendRefs: - name: myapp port: 80EOFStep 8: Verify HTTPS access
Section titled “Step 8: Verify HTTPS access”Get the gateway’s external IP and send an HTTPS request:
GATEWAY_IP=$(kubectl get gateway myapp-gateway -o jsonpath='{.status.addresses[0].value}')curl -k https://$GATEWAY_IPExpected output:
<!DOCTYPE html><html><head><title>Welcome to nginx!</title>...</html>The -k flag tells curl to accept the self-signed certificate.
Clean up
Section titled “Clean up”Delete the cluster when you are done:
kinder delete cluster