Websockets not working on Kubernetes + Docker release stable-5142-3

Docker version: stable-5142-3

It took me a few days to wrap my head around what I was seeing after upgrading to stable-5142-3 and whether I was misconfiguring stuff. I still can’t manage to use websockets in docker for either prosody and JVB.

The error I am seeing initially is that /xmpp-websocket URL is not accessible through wss:// protocol. When I use the docker variable ENABLE_XMPP_WEBSOCKET=1 in both web and prosody docker container environments. This is the error I get at the Chrome console

After some digging around I was able to change the base url as you see in the picture, by setting the PUBLIC_URL environment variable. But it turns out that my prosody container doesn’t seem to be listening on this public domain (this could be an issue with my architecture that I need your help understanding)

Questions (@saghul @Shotster @damencho @bbaldino )

Q1) Do I need to configure a proxy rule to point port 443 to prosody? I thought this was already pointing to the web. How does the system know that /xmpp-websocket is supposed to be redirected to the prosody docker container? (I am using kubernetes, but it’s similar to the docker compose environments that the reference https://github.com/jitsi/docker-jitsi-meet project uses. The difference is my exposed web container is listening on port 80 and there’s a kubernetes haproxy ingress that handles terminating SSL traffic and passing it down to the web container)

Q2) When a connection is made to wss://BASE_DOMAIN/xmpp-websocket which container is supposed to receive the landing connection? Is that supposed to be the web container or the prosody container? Reason why I ask it because prior to having websockets (in previous versions of the docker stable releases) I used to have port 5280 exposed directly to the prosody container. What’s the proper setup now? wss://base_domain/xmpp-websocket needs to go to prosody container directly? If so which port of the prosody container should receive this traffic (443)?

Q3) My prosody container does not seem to have SSL set up. Do I need to install an SSL certificate to enable wss:// on it? Meaning, should I activate service ‘https’ at prosody level for wss:// to work with this setup? I’m currently seeing “Activated service ‘https’ on no ports” The only ports my prosody container seems to be listening on are 5280 and 5222, is that correct?

  • If I disable the environment variable ENABLE_XMPP_WEBSOCKET=0, then I can go a bit further. I imagine that the system now falls back to BOSH /http-bind? (Not sure) But still can’t perform a successful conference. The error now becomes with the colibri-ws endpoint (I assume this is between client and JVB, it looks like it’s trying websockets for that as well)

Sorry, I know this post is long but which container is supposed to receive this request? Web, Prosody or JVB? But by the shape of this URL it looks like WEB container should receive this request and then internally use the PATH param to route this call to the proper JVB container, is this correct understanding? I’m inferring that from the following config piece $1 in live 41 of meet.conf in web rootfs:

How can I adapt this routing architecture to be able to get websockets traffic between client and JVB using docker and kubernetes?

I’m solving similar problem here, so let me reply with what i know.

At first, there will be two websocket connections in full websocket environment:

  • one to prosody - on your edge reverse proxy for your domain (server name as your dns) you should catch /xmpp-websocket endpoint and send it to prosody
  • second one to jvb - this is for video quality control between client and jvb, you have to have every jvb uniquely discoverable through dns, jvb will then announce their public fqdn to client and client will directly connect to jvb via websockets. this one have to be configured for /colibri-ws/… endpoint on the jvb server name

with that in mind
Q1) yes, your reverse proxy should listen on 443, catch /xmpp-websocket and send it to prosody pod, but send other traffic to web pod, that is how it is designed, you can terminate ssl here, just let prosody know that it should consider bosh secure
Q2+Q3) it should land in prosody, 5280 is for non-SSL, 5281 for SSL. For ssl to work in prosody i had to comment out this https_ports = { } in prosody config, which essentially sets https ports to empty list. SSL certs are generated on prosody startup.

That line 41 relates to websocket connection to jvb, it’s not connection to prosody. We are also using kubernetes, our setup is, that we have VM where jvb image is run in NodePort mode, on that same VM we have also reverse proxy that that catches on port 443 requests to colibri and send them to 127.0.0.1 (NodePort, right), something like

location ~ ^/colibri-ws/([a-zA-Z0-9-\.]+)/(.*) {
    proxy_pass         http://127.0.0.1:9090/colibri-ws/$1/$2$is_args$args;

and this works perfectly. I don’t know your setup, but you might give it a try.

With that said, i’m stil struggling to get websockets working on my side :confused: but at least i have traffic routed somehow correctly.

1 Like

@nosmo Thank you very much for taking time and reading this. Still couldn’t get it to work, but maybe I understood it wrong.

Our current architecture here is the following:

My question is, are you saying that to make this work I will have to route traffic going to the path: /xmpp-websocket to prosody container directly? And also the same for /colibri-ws? Let me try to update my diagram for that to make it more clear. (New components are in red) What ports to use for JVB and Prosody Websockets traffic? Also since this is wss:// do I need an SSL certificate?

/xmpp-websocket is communication between client and prosody, so it should go directly to prosody, wss scheme to 5281 but it has to be enabled, as i wrote before
/colibri-ws is communication between client and jvb, so it should go to jvb, NOT to prosody, jvb uses default 9090 i think, port is configured in jvb.conf (new way) or sip-communicator.properties (old way)

generally you don’t have to setup ssl certificates for prosody and jvb in this setup. prosody will generate it’s own ssl certificates on startup. on jvb part i don’t know, in our setup we just send it to http://jvb:9090 and it works :smiley:

1 Like

@Arthur_Morales Did you able to fix it? I’m planning to move from docker swarm(using websockets) to K8s because of Auto scaling feature. Auto scaling in docker swarm is pain. Is websockets with K8s working for you?

@metadata i can’t tell if @Arthur_Morales solved the problem, but i can confirm that we have perfectly working jitsi with websockets (for prosody and for jvb’s) deployed in kubernetes and it works like a charm…

our problem was caused somewhere completely else at infrastructure level…

1 Like

Thanks @nosmo for confirming this. If possible can you share your deployment files?

@metadata i’m sorry, i can’t share our deployment files, since we deploy through ansible and there is lot off other stuff mixed in.

NP @nosmo :slight_smile:

Hi I didnt solve the problem yet. I just can’t get websockets to run in the stable-5142-3 docker images on Kubernetes.

@nosmo since you can’t share your deployment files. Can you just help me understand how routing works for your websockets? In my diagram here the lines that I do not have set up yet are the lines in RED on the image. Is that how I should route the requests for the websockets traffic to work? Basically routes /colibri-ws get routed to Videobridge container and /xmpp-websocket route gets routed to prosody container? If that’s the correct routing what ports should I use for these websockets (443)?

@Arthur_Morales are you reading my replies? i have already answered your questions, so once more…

yes, your routes are correct, our nginx part looks like this:

  • for prosody:
    # xmpp websockets
    location = /xmpp-websocket {
            proxy_pass      https://prosody-public.jitsi.svc:5281/xmpp-websocket;
            proxy_http_version      1.1;
            proxy_set_header        Upgrade $http_upgrade;
            proxy_set_header        Connection "upgrade";

            proxy_set_header        Host conference.ourdomain.com;
            proxy_set_header        X-Forwarded-For $remote_addr;
            proxy_read_timeout      900s;

            tcp_nodelay             on;
            proxy_ssl_session_reuse on;
    }

since in your scheme you have just 5280, you will use that port. i don’t know, whether websockets over http (5280 is for http, 5281 is for https in default setup) are supported by jitsi, we are using secure (encrypted) communication over 5281.

  • and for colibri:
# colibri (JVB) websockets for jvb
location ~ ^/colibri-ws/([a-zA-Z0-9-\.]+)/(.*) {
    proxy_pass              http://127.0.0.1:9090/colibri-ws/$1/$2$is_args$args;
    proxy_http_version      1.1;
    proxy_set_header        Upgrade $http_upgrade;
    proxy_set_header        Connection "Upgrade";
    tcp_nodelay             on;

    proxy_set_header        Host            $host;
    proxy_set_header        X-Forwarded-For $remote_addr;
}

proxy_pass url here is relevant for ours instalation, your might be different !!! Port is as configured by default setting for jvb.

Maybe, if you can describe what is wrong with your setup, we could provide you with some leads of what to check or where to look, your problem might be elsewhere than you think.

Hello @Arthur_Morales Any updates on this? Is websocket working for you now?

I guess you are also trying to update this repo to use websockets?

@nosmo Do I have to make any changes to ha-proxy config as well to support websockets? jitsi-deployment/haproxy-configmap.yaml at master · hpi-schul-cloud/jitsi-deployment · GitHub. I’m using RKE on baremetal with 3 worker nodes. MetalLB as network loadbalancer, k8s-ingress-controller and haproxy for L7 loadbalancing.

@metadata I really don’t know, maybe it’s better if you describe your problem, checking some config when i do not know how the problem manifests is hard. if you want some advie, then describe your problem.

i’m not that much familiar with haproxy config, so can’t verify, if your’s correct. at least here is a snippet of ours config for l7 shard balancing:

backend jitsi-shards
        balance leastconn
        stick-table type string len 256 size 200k expire 120m peers haproxy-jitsi-peers
        stick on urlp(room) table jitsi-shards
        mode http
        timeout connect 6000
        timeout server 60000

        server prosody1:5281 check id 1 inter 6000 ssl verify none
        server prosody2:5281 check id 2 inter 6000 ssl verify none

@nosmo I’m getting below errors in console logs.

Logger.js:154
2021-02-10T07:50:49.664Z [modules/RTC/BridgeChannel.js] <WebSocket.e.onclose>:  Channel closed: 1006 
o @ lib-jitsi-meet.min.js?v=4652:10
e.onclose @ lib-jitsi-meet.min.js?v=4652:17
Logger.js:154

2021-02-10T07:50:49.973Z [modules/RTC/BridgeChannel.js] <l._send>:  Bridge Channel send: no opened channel.
o @ lib-jitsi-meet.min.js?v=4652:10
_send @ lib-jitsi-meet.min.js?v=4652:17
sendMessage @ lib-jitsi-meet.min.js?v=4652:17
sendChannelMessage @ lib-jitsi-meet.min.js?v=4652:1
re.sendEndpointMessage @ lib-jitsi-meet.min.js?v=4652:10
re.sendMessage @ lib-jitsi-meet.min.js?v=4652:10
(anonymous) @ lib-jitsi-meet.min.js?v=4652:10
sendRequest @ lib-jitsi-meet.min.js?v=4652:17
setInterval (async)
d @ lib-jitsi-meet.min.js?v=4652:17
participantJoined @ lib-jitsi-meet.min.js?v=4652:17
a.emit @ lib-jitsi-meet.min.js?v=4652:1
re.onMemberJoined @ lib-jitsi-meet.min.js?v=4652:10
a.emit @ lib-jitsi-meet.min.js?v=4652:1
onPresence @ lib-jitsi-meet.min.js?v=4652:10
onPresence @ lib-jitsi-meet.min.js?v=4652:10
run @ lib-jitsi-meet.min.js?v=4652:1
(anonymous) @ lib-jitsi-meet.min.js?v=4652:1
forEachChild @ lib-jitsi-meet.min.js?v=4652:1
_dataRecv @ lib-jitsi-meet.min.js?v=4652:1
_onMessage @ lib-jitsi-meet.min.js?v=4652:1
Logger.js:154

2021-02-10T07:50:49.973Z [JitsiConference.js] <u.sendMessage>:  Failed to send E2E ping request or response. undefined
o @ lib-jitsi-meet.min.js?v=4652:10
(anonymous) @ lib-jitsi-meet.min.js?v=4652:10
sendRequest @ lib-jitsi-meet.min.js?v=4652:17
setInterval (async)
d @ lib-jitsi-meet.min.js?v=4652:17
participantJoined @ lib-jitsi-meet.min.js?v=4652:17
a.emit @ lib-jitsi-meet.min.js?v=4652:1
re.onMemberJoined @ lib-jitsi-meet.min.js?v=4652:10
a.emit @ lib-jitsi-meet.min.js?v=4652:1
onPresence @ lib-jitsi-meet.min.js?v=4652:10
onPresence @ lib-jitsi-meet.min.js?v=4652:10
run @ lib-jitsi-meet.min.js?v=4652:1
(anonymous) @ lib-jitsi-meet.min.js?v=4652:1
forEachChild @ lib-jitsi-meet.min.js?v=4652:1
_dataRecv @ lib-jitsi-meet.min.js?v=4652:1
_onMessage @ lib-jitsi-meet.min.js?v=4652:1
Logger.js:154 

2021-02-10T07:50:49.978Z [modules/RTC/BridgeChannel.js] <l._send>:  Bridge Channel send: no opened channel.
o @ lib-jitsi-meet.min.js?v=4652:10
_send @ lib-jitsi-meet.min.js?v=4652:17
sendMessage @ lib-jitsi-meet.min.js?v=4652:17
sendChannelMessage @ lib-jitsi-meet.min.js?v=4652:1
re.sendEndpointMessage @ lib-jitsi-meet.min.js?v=4652:10
re.broadcastEndpointMessage @ lib-jitsi-meet.min.js?v=4652:10
_broadcastLocalStats @ lib-jitsi-meet.min.js?v=4652:17
_updateLocalStats @ lib-jitsi-meet.min.js?v=4652:17
a.emit @ lib-jitsi-meet.min.js?v=4652:1
m._processAndEmitReport @ lib-jitsi-meet.min.js?v=4652:10
m.processStatsReport @ lib-jitsi-meet.min.js?v=4652:10
(anonymous) @ lib-jitsi-meet.min.js?v=4652:10
Logger.js:154 

2021-02-10T07:50:52.022Z [JitsiMeetJS.js] <Object.getGlobalOnErrorHandler>:  UnhandledError: null Script: null Line: null Column: null StackTrace:  Error: Strophe: Websocket closed unexpectedly
    at Object.r.Strophe.log (lib-jitsi-meet.min.js?v=4652:17)
    at Object.error (lib-jitsi-meet.min.js?v=4652:1)
    at N.Websocket._onClose (lib-jitsi-meet.min.js?v=4652:1)
o @ lib-jitsi-meet.min.js?v=4652:10
getGlobalOnErrorHandler @ lib-jitsi-meet.min.js?v=4652:17
window.onerror @ app.bundle.min.js?v=4652:204
callErrorHandler @ lib-jitsi-meet.min.js?v=4652:1
r.Strophe.log @ lib-jitsi-meet.min.js?v=4652:17
error @ lib-jitsi-meet.min.js?v=4652:1
_onClose @ lib-jitsi-meet.min.js?v=4652:1
Logger.js:154 2021-02-10T07:50:52.023Z [modules/xmpp/strophe.util.js] <Object.r.Strophe.log>:  Strophe: Websocket closed unexpectedly
o @ lib-jitsi-meet.min.js?v=4652:10
r.Strophe.log @ lib-jitsi-meet.min.js?v=4652:17
error @ lib-jitsi-meet.min.js?v=4652:1
_onClose @ lib-jitsi-meet.min.js?v=4652:1
Logger.js:154 2021-02-10T07:51:25.317Z [JitsiMeetJS.js] <Object.getGlobalOnErrorHandler>:  UnhandledError: null Script: null Line: null Column: null StackTrace:  Error: Strophe: Websocket closed unexpectedly
    at Object.r.Strophe.log (lib-jitsi-meet.min.js?v=4652:17)
    at Object.error (lib-jitsi-meet.min.js?v=4652:1)
    at N.Websocket._onClose (lib-jitsi-meet.min.js?v=4652:1)
o @ lib-jitsi-meet.min.js?v=4652:10
getGlobalOnErrorHandler @ lib-jitsi-meet.min.js?v=4652:17
window.onerror @ app.bundle.min.js?v=4652:204
callErrorHandler @ lib-jitsi-meet.min.js?v=4652:1
r.Strophe.log @ lib-jitsi-meet.min.js?v=4652:17
error @ lib-jitsi-meet.min.js?v=4652:1
_onClose @ lib-jitsi-meet.min.js?v=4652:1
Logger.js:154 

2021-02-10T07:51:25.318Z [modules/xmpp/strophe.util.js] <Object.r.Strophe.log>:  Strophe: Websocket closed unexpectedly
o @ lib-jitsi-meet.min.js?v=4652:10
r.Strophe.log @ lib-jitsi-meet.min.js?v=4652:17
error @ lib-jitsi-meet.min.js?v=4652:1
_onClose @ lib-jitsi-meet.min.js?v=4652:1
Logger.js:154 2021-02-10T07:51:29.151Z [modules/RTC/BridgeChannel.js] <WebSocket.e.onclose>:  Channel closed: 1006 
o @ lib-jitsi-meet.min.js?v=4652:10
e.onclose @ lib-jitsi-meet.min.js?v=4652:17
Logger.js:154 2021-02-10T07:51:29.377Z [modules/RTC/BridgeChannel.js] <l._send>:  Bridge Channel send: no opened channel

meet.conf file

    {{ $ENABLE_XMPP_WEBSOCKET := .Env.ENABLE_XMPP_WEBSOCKET | default "1" | toBool }}
    {{ $ENABLE_JVB_WEBSOCKET := .Env.ENABLE_JVB_WEBSOCKET | default "1" | toBool }}
    {{ $ENABLE_SUBDOMAINS := .Env.ENABLE_SUBDOMAINS | default "true" | toBool -}}

    server_name _;

    client_max_body_size 0;

    root /usr/share/jitsi-meet;

    # ssi on with javascript for multidomain variables in config.js
    ssi on;
    ssi_types application/x-javascript application/javascript;

    index index.html index.htm;
    error_page 404 /static/404.html;

    # Security headers
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";

    location = /config.js {
        alias /config/config.js;
    }

    location = /interface_config.js {
        alias /config/interface_config.js;
    }

    location = /external_api.js {
        alias /usr/share/jitsi-meet/libs/external_api.min.js;
    }

    # ensure all static content can always be found first
    location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$
    {
        add_header 'Access-Control-Allow-Origin' '*';
        alias /usr/share/jitsi-meet/$1/$2;
    }

    {{ if $ENABLE_JVB_WEBSOCKET }}
    # colibri (JVB) websockets
    location ~ ^/colibri-ws/([a-zA-Z0-9-\.]+)/(.*) {
        proxy_pass http://$1:9090/colibri-ws/$1/$2$is_args$args;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        tcp_nodelay on;
    }
    {{ end }}

    # BOSH
    location = /http-bind {
        proxy_pass {{ .Env.XMPP_BOSH_URL_BASE }}/http-bind;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host {{ .Env.XMPP_DOMAIN }};
    }

    {{ if $ENABLE_XMPP_WEBSOCKET }}
    # xmpp websockets
    location = /xmpp-websocket {
        proxy_pass {{ .Env.XMPP_BOSH_URL_BASE }}/xmpp-websocket;
        proxy_http_version 1.1;

        proxy_set_header Connection "upgrade";
        proxy_set_header Upgrade $http_upgrade;

        proxy_set_header Host {{ .Env.XMPP_DOMAIN }};
        proxy_set_header X-Forwarded-For $remote_addr;
        tcp_nodelay on;
    }
    {{ end }}

    location ~ ^/([^/?&:'"]+)$ {
        try_files $uri @root_path;
    }

    location @root_path {
        rewrite ^/(.*)$ / break;
    }

    location ~ ^/([^/?&:'"]+)/config.js$
    {
        set $subdomain "$1.";
        set $subdir "$1/";

        alias /config/config.js;
    }

    #Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
    location ~ ^/([^/?&:'"]+)/(.*)$ {
        set $subdomain "$1.";
        set $subdir "$1/";
        rewrite ^/([^/?&:'"]+)/(.*)$ /$2;
    }

    {{ if $ENABLE_SUBDOMAINS }}
    {{ if $ENABLE_XMPP_WEBSOCKET }}
    # websockets for subdomains
    location ~ ^/([^/?&:'"]+)/xmpp-websocket {
        set $subdomain "$1.";
        set $subdir "$1/";
        set $prefix "$1";

    rewrite ^/(.*)$ /xmpp-websocket;
    }
    {{ end }}

    # BOSH for subdomains
    location ~ ^/([^/?&:'"]+)/http-bind {
        set $subdomain "$1.";
        set $subdir "$1/";
        set $prefix "$1";

        rewrite ^/(.*)$ /http-bind;
    }
    {{ end }}

    {{ if .Env.ETHERPAD_URL_BASE }}
    # Etherpad-lite
    location /etherpad/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;

        proxy_pass {{ .Env.ETHERPAD_URL_BASE }}/;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_buffering off;
        proxy_set_header Host {{ .Env.XMPP_DOMAIN }};
    }
    {{ end }}

@metadata from the logs i would guess, that there is a problem establishing ws connection to the bridge.
Are your jvb bridges available on public url, resp. what is your JVB_WS_DOMAIN and JVB_WS_SERVER_ID for each bridge?
Also, in meet.conf file i see ENABLE_JVB_WEBSOCKET, this is not present in current docker-jitsi-meet, is it your own modification, or where does it come from?

Hello @nosmo , WS_SERVER_ID is internal address i.e 10.42.6.72. Yes I’m using custom images. I have added ENABLE_JVB_WEBSOCKET for my testing to enable/disable websockets for jvb and enable_sctp.

I’m using same docker images with docker-swarm setup and it is working fine. though in case swarm I set JVB_WS_SERVER_ID=jvb1. It changes per jvb i.e based on the number of jvb’s I’m running for example jvb1, jvb2 etc.

{{ $WS_SERVER_ID := .Env.JVB_WS_SERVER_ID | default .Env.LOCAL_ADDRESS -}}
{{ $WS_DOMAIN := .Env.JVB_WS_DOMAIN | default $PUBLIC_URL_DOMAIN -}}

jvb.conf

videobridge {
    ice {
        udp {
            port = 30300
        }
        tcp {
            enabled = false
            port = <no value>

        }
    }
    apis {
        xmpp-client {
            configs {
                shard {
                    HOSTNAME = "shard-0-prosody"
                    DOMAIN = "auth.example.com"
                    USERNAME = "jvb1"
                    PASSWORD = "f8412a7d2f9821e8e9313fb6dad8143"
                    MUC_JIDS = "jvbbrewery@internal-muc.example.com"
                    MUC_NICKNAME = "shard-0-jvb-0"
                    DISABLE_CERTIFICATE_VERIFICATION = true
                }
            }
        }

        # The COLIBRI REST API
        rest {
            enabled = true
        }

        jvb-api {
            enabled = true
        }

    }
    # Configuration of the different REST APIs.
    # Note that the COLIBRI REST API is configured under videobridge.apis.rest instead.
    rest {

        debug {
        enabled = true
        }
        health {
        enabled = true
        }
        shutdown {
        # Note that the shutdown API requires the COLIBRI API to also be enabled.
        enabled = true
        }

    }
    stats {
        enabled = true
    }

    websockets {
        enabled = true
        domain = "example.com"
        tls = true
        server-id = "10.42.6.72"
    }
    http-servers {
        private {
          host = 0.0.0.0
        }
        public {
            host = 0.0.0.0
            port = 9090
        }
    }
    octo {
        enabled = true
        bind-address = "0.0.0.0"
        public-address = "203.112.X.X"   
        bind-port = "30900"
        region = "Region1"
    }
}
ice4j {
    harvest {
        mapping {
            stun {
                addresses = [ "meet-jit-si-turnrelay.jitsi.net:443" ]
            }
        }
    }
}

@metadata what is your actual value of websockets { domain: “???” } you have here example.com.
This has to be unique for each bridge and dns resolvable url by clients, so that the clients can connect to this address. Normally, WS_DOMAIN has the unique dns record for each jvb.
You can check that as a client, just open developers tools in chrome before you enter room and watch what WS network connection is made. One will be to your jitsi domain, when there are 2 participants, ws connection will be made to bridge, this will be the one to /colibri-ws.
I don’t know, whether you already did that, so if yes, then you might ignore this :wink:
Rest looks good to me in relation to websockets.

its
wss://example.com/colibri-ws/10.42.6.72/391d2cb1e3909776/d7e6dee3\?pwd\=7o8pbjfsc0sg1hifm1nf3hr0u1

example.com is for reference. else it is public_url that I’m using to access the setup.

Even In swarm, the key was to set JVB_WS_SERVER_ID=jvb1 else I was getting websocket error.

Even the connection is fine too.

 nc -vz -u  203.112.X.X 30300
Connection to 203.112.X.X 30300 port [udp/*] succeeded!
nc -vz -u  203.112.X.X 30900
Connection to 203.112.X.X 30900 port [udp/*] succeeded!

ok, i think i got baisc layout of your setup…
just to be sure…
so request for bridge wss goes to your main domain example.com, where is processed by k8s-ingress (meet.conf) which proxy pass it to jvb address based on your WS_SERVER_ID, which is IP address in your docker environment.
What does your k8s-ingress log says? There should be at least some entry about proxy upstream failed or something, which would mean, that the jvb cannot be reached. I would then check jvb logs, maybe enable jetty logging (with DEBUG log level, jetty also has configuration for websockets logging).

If there is no log entry for upstream, then the problem is in k8s-ingress itself, but since your jitsi app loaded in browser and is trying to connect to bridge, that means, that is works probably ok.