Kubernetes makes it really easy to work with HTTP/S websites as containers. Most ingress controllers work out of the box with them. But, I needed a way to use it with a different TCP service, such as SSH on TCP port 22. I am running this on Rancher 2.5 with Kubernetes 1.19. I don’t know if how it sets up the networking in Kubernetes is any different than any other setup.

Create the files

First, we need to create a deployment.yaml file to include the port we want to use on the container.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: debian
  namespace: default
  labels:
    app-name: debian
    version: buster
spec:
  selector:
    matchLabels:
      app: debian
  replicas: 1
  template:
    metadata:
      labels:
        app: debian
    spec:
      containers:
      - name: debian
        image: debian
        livenessProbe:
          tcpSocket:
            port: 22
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          tcpSocket:
            port: 22
          initialDelaySeconds: 5
          periodSeconds: 10
          failureThreshold: 20
        ports:
        - containerPort: 22
          protocol: TCP

The “containerPort” line in the Ports section is where we specify the port we want to connect to for this container.

Next we create a service.yaml spec file using the same port number in it.

apiVersion: v1
kind: Service
metadata:
  name: debian
  namespace: default
spec:
  selector:
    app: debian
  ports:
    - protocol: TCP
      port: 22
      targetPort: 22
      name: ssh
  type: LoadBalancer

The above will take an incoming port 22 and forward it to our container. We also set the type to “LoadBalancer”.

Now we create a configmap.yaml file for the NGINX ingress controller. If you are going to use a UDP port for a different service, you can use “udp-services” on the fourth line below.

apiVersion: v1
kind: ConfigMap
metadata:
  name: tcp-services
  namespace: ingress-nginx
data:
  22: "default/debian:22"

The last line is formatted as <incoming port>: “<namespace>/<container name>:<container port>.

Finally, we need to make a patch.yaml file for patching the ingress controller daemonset. This will open the port on the node’s IP address. The below is what is required to minimally add a new port. All of it was copied from my existing ingress controller config except for the containerPort section for the port we are adding. You can get your config by running this command.

kubectl --cluster=prod-wlan --user=prod-wlan --namespace=ingress-nginx get daemonsets.apps nginx-ingress-controller -o yaml
apiVersion: apps/v1
kind: DaemonSet
spec:
  template:
    spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --default-backend-service=$(POD_NAMESPACE)/default-http-backend
        - --configmap=$(POD_NAMESPACE)/nginx-configuration
        - --election-id=ingress-controller-leader
        - --ingress-class=nginx
        - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
        - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
        - --annotations-prefix=nginx.ingress.kubernetes.io
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
        image: rancher/nginx-ingress-controller:nginx-0.35.0-rancher1
        imagePullPolicy: IfNotPresent
        name: nginx-ingress-controller
        ports:
        - containerPort: 80
          hostPort: 80
          name: http
          protocol: TCP
        - containerPort: 443
          hostPort: 443
          name: https
          protocol: TCP
        - containerPort: 22
          hostPort: 22
          name: ssh
          protocol: TCP

Remember to make sure all of the rest of the file looks the same as your existing config. We are only adding the last containerPort section.

Applying the files

All of the above files can be applied with these commands

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f configmap.yaml
kubectl patch daemonsets.apps nginx-ingress-controller --patch "$(patch.yaml)" --type=merge

References

  1. https://kubernetes.github.io/ingress-nginx/user-guide/exposing-tcp-udp-services/
  2. https://minikube.sigs.k8s.io/docs/tutorials/nginx_tcp_udp_ingress/
  3. https://kubernetes.io/docs/concepts/services-networking/ingress/