Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HMR binding does not work in kubernetes #13101

Open
jplimack opened this issue Dec 3, 2024 · 1 comment
Open

HMR binding does not work in kubernetes #13101

jplimack opened this issue Dec 3, 2024 · 1 comment

Comments

@jplimack
Copy link

jplimack commented Dec 3, 2024

Describe the bug

I'm running a local kind cluster on my macbook and trying to deploy my Sveltekit app. Its working great outside of k8s, but I need to setup a bunch of ancillary things and its time to upgrade the stack.

In order to fully test my stack, I require TLS to work and use mkcert to generate local certificates, and also have cert-manager configured to provide self-signed certificates.

I cannot figure out the magical incantation to get both node+HMR to bind to IPv4 AND also provide a client configuration pointing at the dns-name, (not 0.0.0.0).

So if in my vite config I set things to bind to 0.0.0.0, I get IPv4, but then my client sees

"hmr" was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint This request has been blocked; this endpoint must be available over WSS.

or

       GET https://0.0.0.0/hmr/ net::ERR_CERT_AUTHORITY_INVALID

if i set vite to use my dns name, it will bind to IPv6, and then nothing works. Wish there was a way to provide both a bind/listen parameter as well as the hostname to provide to the client so that it would understand that my Ingress controller is terminating TLS. (if I enable passthru TLS, then its L4 and can't do the ws connection upgrade which is L7).

Reproduction

ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: frontend
  namespace: myapp
  annotations:
    cert-manager.io/cluster-issuer: local-issuer-dev
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # Backend uses HTTPS
    nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
    nginx.ingress.kubernetes.io/proxy-set-headers: "Upgrade $http_upgrade"
    nginx.ingress.kubernetes.io/proxy-set-connection: "Connection upgrade"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/websocket-services: "frontend-ws"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - myapp.com
      secretName: myapp-tls
  rules:
    - host: myapp.com
      http:
        paths:
          - path: /hmr/
            pathType: Prefix
            backend:
              service:
                name: frontend-ws
                port:
                  number: 24678
          - path: /
            pathType: Prefix
            backend:
              service:
                name: frontend-service
                port:
                  number: 3030

service

# Service for the WebSocket
apiVersion: v1
kind: Service
metadata:
  name: frontend-ws
  namespace: {{ .Values.global.namespace }}
  labels:
    app: frontend
    type: websocket
spec:
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 24678 # WebSocket service port
      targetPort: 24678 # WebSocket container port
  type: ClusterIP

---
# Service for the main frontend
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
  namespace: {{ .Values.global.namespace }}
  labels:
    app: frontend
    type: main
spec:
  selector:
    app: frontend
  ports:
    - protocol: TCP
      port: 3030 # Main frontend service port
      targetPort: 3030 # Main frontend container port
  type: ClusterIP

deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend
  namespace: {{ .Values.global.namespace }}
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      securityContext:
        sysctls:
          - name: net.ipv6.conf.all.disable_ipv6
            value: "1"
          - name: net.ipv6.conf.default.disable_ipv6
            value: "1"
      initContainers:
        - name: disable-ipv6
          image: docker.io/busybox
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c"]
          args: 
            - sysctl -w net.ipv6.conf.all.disable_ipv6=1; # these seem to have zero effect
              sysctl -w net.ipv6.conf.default.disable_ipv6=1 # these seem to have zero effect
          securityContext:
            privileged: true
      containers:
        - name: frontend
          image: {{ .Values.image.repository }}:{{ .Values.image.tag }}
          ports:
            - containerPort: 3030
            - containerPort: 24678 # Expose the WebSocket port
          volumeMounts:
            - name: tls-volume
              mountPath: "/app/certs"
              readOnly: true
      volumes:
        - name: tls-volume
          secret:
            secretName: myapp-tls

Logs

No response

System Info

Kubernetes (kind)  v1.27.3

# laptop

  System:
    OS: macOS 14.1
    CPU: (12) arm64 Apple M3 Pro
    Memory: 96.73 MB / 36.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 23.3.0 - /opt/homebrew/bin/node
    Yarn: 1.22.22 - /opt/homebrew/bin/yarn
    npm: 10.9.1 - /opt/homebrew/bin/npm
  Browsers:
    Brave Browser: 131.1.73.91
    Chrome: 131.0.6778.86
    Safari: 17.1
  npmPackages:
    @sveltejs/adapter-auto: ^3.2.5 => 3.3.1
    @sveltejs/adapter-static: ^3.0.5 => 3.0.6
    @sveltejs/kit: ^2.0.0 => 2.8.2
    @sveltejs/vite-plugin-svelte: ^3.0.0 => 3.1.2
    svelte: ^4.2.7 => 4.2.19
    vite: ^5.0.3 => 5.4.11

inside container

 Binaries:
    Node: 23.3.0 - /usr/local/bin/node
    Yarn: 1.22.22 - /usr/local/bin/yarn
    npm: 10.9.0 - /usr/local/bin/npm
  npmPackages:
    @sveltejs/adapter-auto: ^3.2.5 => 3.3.1
    @sveltejs/adapter-static: ^3.0.5 => 3.0.6
    @sveltejs/kit: ^2.0.0 => 2.8.2
    @sveltejs/vite-plugin-svelte: ^3.0.0 => 3.1.2
    svelte: ^4.2.7 => 4.2.19
    vite: ^5.0.3 => 5.4.11

Severity

blocking all usage of SvelteKit

Additional Information

vite.config.ts

import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';

// import fs from 'fs';
// import path from 'path';
// let key = fs.readFileSync(path.resolve(__dirname, 'certs/tls.key'))
// let cert = fs.readFileSync(path.resolve(__dirname, 'certs/tls.crt'))

export default defineConfig({
	plugins: [sveltekit(),


	],
	server: {
		host: '0.0.0.0',
		port: 3030,
		strictPort: true,
		hmr: {
			protocol: 'wss', // Use WebSocket protocol / use wss for tls, but not behind an ingress.
			// host: 'myapp.com', // This will mess up the binding, as it will bind to IPv6 addresses
			host: '0.0.0.0', // this will mess up the client as the client wants a DNS name
			port: 24678, // Match the expected HMR port
			clientPort: 443, // Adjust as needed
			overlay: true,    // Show HMR errors on the browser overlay
			path: "/hmr/",
		},
		// https: {
		// 	key: key,
		// 	cert: cert,
		// },
	},
	test: {
		include: ['src/**/*.{test,spec}.{js,ts}']
	}
});

GET https://0.0.0.0/hmr/ net::ERR_CERT_AUTHORITY_INVALID

If i flip to use the dns name, it binds wrong

/app # lsof -Pni | grep LISTEN
node     21 root 21u  IPv4 2802096      0t0  TCP 127.0.0.1:24678 (LISTEN)
node     21 root 34u  IPv4 2802097      0t0  TCP *:3030 (LISTEN)

/app # grep -A2 hmr vite.config.ts
		hmr: {
			protocol: 'wss', // Use WebSocket protocol / use wss for tls, but not behind an ingress.
			host: 'myapp.com', // This will fuck up the binding, as it will bind to IPv6 addresses
@jplimack
Copy link
Author

jplimack commented Dec 3, 2024

seems like maybe theres a way to pass a localAddress to bind to, and this would need to be done through the plumbing of Vite? https://github.com/nodejs/node/blob/main/doc/api/http.md

I'm not a JS expert by any means, any and all help/pointers/tips are appreciated. im also having a hard time believing I'm the first to pioneer this path, so I'm assuming that I'm doing many things wrong.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant