How to expose Kubernetes services to the public using DNS Load Balancing

Kubernetes Services in CloudFleet Kubernetes Engine (CFKE) are exposed via a DNS load balancing mechanism. All services get a unique DNS name in the format:

<service_name>.<cluster_id>.cfke.cloudfleet.dev

This DNS name resolves to the external IP of the Ingress controller, which acts as a load balancer to distribute incoming traffic to the appropriate service endpoints.

Note: Cloudfleet relies on nodes having an external IP set. If an external IP is not set, for example, in certain private networking configurations or self-managed on-premises Kubernetes nodes, Cloudfleet will default to using the private IP. This ensures that your service is accessible whether from within the cluster or from outside, provided that appropriate networking rules are in place. See Exposing applications to the Internet for more information.

Expose a web server using Kubernetes

Here’s a simple example of how to expose a web server using Kubernetes:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: helloworld
    labels:
        app: helloworld
spec:
    replicas: 1
    selector:
        matchLabels:
            app: helloworld
    template:
        metadata:
            labels:
                app: helloworld
        spec:
            containers:
                - name: webserver
                  image: paulbouwer/hello-kubernetes:1.10
                  ports:
                      - containerPort: 8080
                  env:
                      - name: MESSAGE
                        value: "Hello, I'm running on Cloudfleet!"
                      - name: KUBERNETES_NAMESPACE
                        valueFrom:
                            fieldRef:
                                fieldPath: metadata.namespace
                      - name: KUBERNETES_POD_NAME
                        valueFrom:
                            fieldRef:
                                fieldPath: metadata.name
                      - name: KUBERNETES_NODE_NAME
                        valueFrom:
                            fieldRef:
                                fieldPath: spec.nodeName
                  resources:
                      limits:
                          cpu: 50m
                          memory: 100Mi
                      requests:
                          cpu: 10m
                          memory: 15Mi
---
apiVersion: v1
kind: Service
metadata:
    name: helloworld
    labels:
        app: helloworld
spec:
    type: NodePort
    selector:
        app: helloworld
    ports:
        - name: http
          port: 8080
          targetPort: 8080
          protocol: TCP
          nodePort: 30080

In this example:

  • The Nginx web server is deployed using a Deployment.
  • A Service of type NodePort exposes it externally, and will be accessible via http://helloworld.<cluster_id>.cfke.cloudfleet.dev:30080.

Serving Traffic with TLS

To ensure secure communication, CloudFleet uses TLS certificates issued by Let’s Encrypt. The certificate is for the DNS name in the form:

<service_name>.<cluster_id>.cfke.cloudfleet.dev

The TLS certificate is valid for 90 days and is automatically renewed by cert-manager, simplifying certificate management. The Ingress controller handles HTTPS termination, ensuring all traffic to your services is securely encrypted.

The certificate is stored in Kubernetes as a TLS secret named ingress in the default namespace, and it is automatically applied by the Ingress controller. Here’s an example configuration for HTTPS in the Ingress resource:

apiVersion: apps/v1
kind: Deployment
metadata:
    name: helloworld
    labels:
        app: helloworld
spec:
    replicas: 1
    selector:
        matchLabels:
            app: helloworld
    template:
        metadata:
            labels:
                app: helloworld
        spec:
            affinity:
                nodeAffinity:
                    requiredDuringSchedulingIgnoredDuringExecution:
                        nodeSelectorTerms:
                            - matchExpressions:
                                  - key: kubernetes.io/arch
                                    operator: In
                                    values:
                                        - amd64
            containers:
                - name: nginx-tls-terminator
                  image: eknert/nginx-tls-terminator:1.0.1
                  ports:
                      - containerPort: 8443
                  volumeMounts:
                      - mountPath: /etc/nginx/ssl
                        name: tls-terminator-cert
                        readOnly: true
                      - mountPath: /tmp
                        name: tmp
                  securityContext:
                      allowPrivilegeEscalation: false
                      runAsNonRoot: true
                      runAsUser: 2000
                      runAsGroup: 2000
                      readOnlyRootFilesystem: true
                      capabilities:
                          drop:
                              - all
                  resources:
                      limits:
                          cpu: 50m
                          memory: 100Mi
                      requests:
                          cpu: 10m
                          memory: 15Mi
                - name: webserver
                  image: paulbouwer/hello-kubernetes:1.10
                  ports:
                      - containerPort: 8080
                  env:
                      - name: MESSAGE
                        value: "Hello, I'm running on Cloudfleet!"
                      - name: KUBERNETES_NAMESPACE
                        valueFrom:
                            fieldRef:
                                fieldPath: metadata.namespace
                      - name: KUBERNETES_POD_NAME
                        valueFrom:
                            fieldRef:
                                fieldPath: metadata.name
                      - name: KUBERNETES_NODE_NAME
                        valueFrom:
                            fieldRef:
                                fieldPath: spec.nodeName
                  resources:
                      limits:
                          cpu: 50m
                          memory: 100Mi
                      requests:
                          cpu: 10m
                          memory: 15Mi
            volumes:
                - name: tls-terminator-cert
                  secret:
                      secretName: ingress
                - name: tmp
                  emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
    name: helloworld
    labels:
        app: helloworld
spec:
    type: NodePort
    selector:
        app: helloworld
    ports:
        - name: http
          port: 8080
          targetPort: 8080
          protocol: TCP
          nodePort: 30080
        - name: https
          port: 8443
          targetPort: 8443
          protocol: TCP
          nodePort: 30433

This example illustrates:

  • The use of a tls block to configure HTTPS for the service.
  • The use of Let’s Encrypt for certificate management.
  • The use of a custom Nginx image to terminate TLS and forward traffic to the web server.

By using this setup, you can ensure that your public services are accessible securely over HTTPS.