Migrating my old server to Kubernetes has been on my mind for a while. The last couple of months were spent on setting up the general structure of my cluster. I got a bunch of neat tools set up on the cluster, like: ArgoCD, Cilium, External DNS, NGINX Ingress Controller and a ton more. I also figured out how I could do some GitOps, with an App of Apps and an ApplicationSet for some neat Kustomize stuff to boot. That meant that it was time to get stuff hosted on there, except for one issue: I have just the one IP available, and I have a server and a cluster serving websites.

At first, I checked out CloudFlare’s cloudflared, which is nice, but it does have several limitations. It doesn’t allow WebSocket POSTs, and in my opinion it’s not great for non-HTTP(S)-related tasks (e.g., SSH). So I checked what I could do with the cluster I had available. At this point I’m not yet fully familiar with all that’s available in this ecosystem, so I figured that I could do something with an NGINX reverse proxy.

Considering I already had NGINX Ingress Controller set up, I figured it would be wise to see if I could solve this with an Ingress. I was only used to configuring Ingresses to use Services that use labels to send traffic to Deployments/Statefulsets/etc. A quick search yielded some neat options, including a helpful blog post by Kristina Devochko that had exactly what I needed. I could link a Service to an Endpoints resource or EndpointSlice, or I could change the Service’s type to ExternalName.

I created a proxy service that routes traffic to an external service (legacy-server.example.com) using the ExternalName service type:

apiVersion: v1
kind: Service
metadata:
  name: legacy-server
spec:
  type: ExternalName
  externalName: legacy-server.example.com

Note that the externalName points to a DNS name, not an IP address.

The Ingress handles the traffic and sends it to the service. Many hosts can be added, even wildcards (which might be a security issue by exposing too many services):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: HTTPS
  name: legacy-server
  namespace: proxy-legacy-server
spec:
  rules:
  - host: example.com
    http:
      paths:
      - backend:
          service:
            name: legacy-server
            port:
              number: 443
        path: /
        pathType: Prefix
  - host: registry.example.com
    http:
      paths:
      - backend:
          service:
            name: legacy-server
            port:
              number: 443
        path: /
        pathType: Prefix
  tls:
  - hosts:
    - example.com
    - registry.example.com
    secretName: example-com-tls

Note that this example assumes the legacy-server endpoint expects traffic on port 443 using the HTTPS protocol. If HTTPS isn’t needed, remove the annotation and update the port to match the service’s expected port.

Now that this part is sorted, I can gradually migrate services to the cluster without impacting DNS or requiring third-party services, and I can easily revert to the legacy setup if things go awry.

Links