🖥😩

The Hardest Problem In Computer Science is Opening a Port

All I wanted was to open port 1935 so that I could run my MovieNight instance in a cluster. But I couldn’t find anywhere describing all the steps to actually open a port on Kubernetes. But #ShePersisted or whatever.

NB: a non-http port (like 1935 for RTMP, or 21 for FTP, or) can only be made accessible to one (1) service in your entire cluster. So choose wisely. Yes, this is batshit.

1. Configuration the Application

If you’re deploying your application with a Helm chart, make sure that its service type is NodePort, and specify each port you want accessible in its service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: {{ include "movienight.fullname" . }}
  labels:
    {{- include "movienight.labels" . | nindent 4 }}
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: http
      protocol: TCP
      name: http
    - port: 1935
      targetPort: rtmp
      protocol: TCP
      name: rtmp
  selector:
    {{- include "movienight.selectorLabels" . | nindent 4 }}

List the same additional ports in your application’s deployment.yaml (here 8089 is what we are exposing as port 80, since that’s what MovieNight binds to by default):

containers:
  - name: {{ .Chart.Name }}
    securityContext:
      {{- toYaml .Values.securityContext | nindent 12 }}
    image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
    imagePullPolicy: {{ .Values.image.pullPolicy }}
    ports:
      - name: http
        containerPort: 8089
        protocol: TCP
      - name: rtmp
        containerPort: 1935
        protocol: TCP

2. Configure the Ingress Controller

Find your ingress controller service and deployments in the cluster:

$ kubectl get all

NAME                                                    READY   STATUS        RESTARTS   AGE
pod/ingress-ingress-nginx-controller-7555b9d446-r5l46   1/1     Running       0          89s

NAME                                                 TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)                                     AGE
service/ingress-ingress-nginx-controller             LoadBalancer   10.128.94.66     [not telling]   80:31896/TCP,443:31855/TCP,1935:32249/TCP   36h
service/ingress-ingress-nginx-controller-admission   ClusterIP      10.128.105.144   <none>          443/TCP                                     36h
service/kubernetes                                   ClusterIP      10.128.0.1       <none>          443/TCP                                     37h

NAME                                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-ingress-nginx-controller   1/1     1            1           36h

NAME                                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-ingress-nginx-controller-7555b9d446   1         1         1       89s

To open port 1935 like you see above, first edit the deployment definition:

kubectl edit deployment.apps/ingress-ingress-nginx-controller

And add two things:

  1. in spec.containers.ports, add the port you want open (I want 1935, which is RTMP):

    ports:
    - containerPort: 80
      name: http
      protocol: TCP
    - containerPort: 443
      name: https
      protocol: TCP
    - containerPort: 1935
      name: rtmp
      protocol: TCP
    - containerPort: 8443
      name: webhook
      protocol: TCP
    
  2. in spec.containers.args, add a line for tcp-services-configmap:

    spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --publish-service=default/ingress-ingress-nginx-controller
        - --election-id=ingress-controller-leader
        - --ingress-class=nginx
        - --configmap=default/ingress-ingress-nginx-controller
        - --tcp-services-configmap=default/tcp-services
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/cert
        - --validating-webhook-key=/usr/local/certificates/key
    

default/tcp-services refers to the namespace and name of the ConfigMap you’re about to create:

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: tcp-services
  namespace: default
data:
  1935: "movienight/trashcloud-movienight:1935"

Here movienight/trashcloud-movienight refers to the namespace and service name of your application.

Install your ConfigMap:

kubectl apply -f tcp-services.yaml

Then, edit the service definition of the ingress controller:

kubectl edit service/ingress-ingress-nginx-controller

In spec.ports, add an entry for the new port, and assign it an unused NodePort between 30000 and 32767:

spec:
  clusterIP: 10.128.94.66
  externalTrafficPolicy: Cluster
  ports:
  - name: http
    nodePort: 31896
    port: 80
    protocol: TCP
    targetPort: http
  - name: https
    nodePort: 31855
    port: 443
    protocol: TCP
    targetPort: https
  - name: rtmp
    nodePort: 32249
    port: 1935
    protocol: TCP
    targetPort: 1935

And that should do it! I’m very sleep deprived on account of this nonsense so if I’ve forgotten a step let me know.