Axios in a Node.js container on Kubernetes is returning "ECONNREFUSED 127.0.0.1:30561"?

1/20/2020

Full error message: connect ECONNREFUSED 127.0.0.1:30561 at TCPConnectWrap.afterConnect

The axios request is running in a Node.js environment (Next.js), which is where the error occurs, strangely the axios request works perfectly fine when it is being run in the browser.

My component (running in Node.js) that calls axios:

import axios from 'axios'
import Router from 'next/router'
import React, { Component } from 'react'
import { initializeStore } from '~/reducers'
import { authenticate } from '~/actions/auth'
import { getCookieByName } from '~/helpers/cookie'

const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'

function getOrCreateStore(initialState) {
    // Always make a new store if server, otherwise state is shared between requests
    if (isServer) {
        return initializeStore(initialState)
    }
    // Create store if unavailable on the client and set it on the window object
    if (!window[__NEXT_REDUX_STORE__]) {
        window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
    }
    return window[__NEXT_REDUX_STORE__]
}

export default App => {
    return class AppWithRedux extends Component {
        static async getInitialProps(appContext) {

            const reduxStore = getOrCreateStore()
            
            appContext.ctx.reduxStore = reduxStore

            let appProps = {}

            if (typeof App.getInitialProps === 'function') {
                appProps = await App.getInitialProps(appContext)
            }

            const JWT = (isServer ? getCookieByName('JWT', appContext.ctx.req.headers.cookie) : getCookieByName('JWT', document.cookie))

            const pathname = appContext.ctx.pathname

            //set axios baseURL
            axios.defaults.baseURL = (isServer ? `${appContext.ctx.req.headers['x-forwarded-proto']}://${appContext.ctx.req.headers.host}` : window.location.origin)

            //if user has a JWT
            if(JWT){
                axios.defaults.headers.common['Authorization'] = `Bearer ${JWT}`
                //get user from API layer
                reduxStore.dispatch(authenticate())
            } 

            
            return {
                ...appProps,
                initialReduxState: reduxStore.getState()
            }
        }

        constructor(props) {
            super(props)
            this.reduxStore = getOrCreateStore(props.initialReduxState)
        }

        render() {
            return <App {...this.props} reduxStore={this.reduxStore} />
        }
    }
}

Specifically reduxStore.dispatch(authenticate())

And my actual axios call (using redux thunk), looking at the authenticate method:

import axios from 'axios'
import { setCookieByName } from '~/helpers/cookie'

const BASE_URL = '/api/auth'
export const TYPE_REGISTER = 'TYPE_REGISTER'
export const TYPE_AUTHENTICATE = 'TYPE_AUTHENTICATE'

export const register = values => (dispatch) => {
    return axios.post(`${BASE_URL}/register`, values)
        .then(function({data: {token, user}}){
            setCookieByName('JWT', token, 365)
            dispatch({
                type: TYPE_REGISTER,
                payload: user
            })
        })
}

export const authenticate = () => (dispatch) => {
    return axios.post(`${BASE_URL}/me`)
        .then(function({data: {user}}){
            dispatch({
                type: TYPE_AUTHENTICATE,
                payload: user
            })
        })
        .catch(function(err){
            console.log(err)
            dispatch({
                type: TYPE_AUTHENTICATE,
                payload: {}
            })
        })
}

I'm running my local Kubernetes cluster using Docker for Mac, and my Ingress controller is being accessed on http://kludge.info:30561. My domain is mapped from 127.0.0.1 kludge.info locally to allow the Ingress controller to hit the container. My theory is that when I send a request to http://kludge.info:30561/api/auth/me for example, the docker container running the Node.js app thinks it is a request to localhost (inside the container), and results in a connection error. Please note that the Node.js app inside the container is running on http://localhost:8080. Basically I'm running localhost on my machine, and localhost inside the Node instance. How could I send a request outside to http://kludge.info:30561/ where the Ingress controller is running.

I've also configured the baseURLin axios, but it does not solve the problem. My ingress controller has a path /api that will point to a PHP instance, so I need my Node.js axios call to hit that inside it's container. Any help would be much appreciated.

When I ran my K8 cluster on Minikube I did not have this problem, however minikube does provide you with the IP of the VM, while Docker for Desktop uses localhost directly on your machine.

-- thatguyjono
axios
docker
kubernetes
next.js
node.js

1 Answer

1/21/2020

If I understand you correctly I see that you open a socket on lcoalhost(127.0.0.1) so it is only accessible locally. If you want it to be accessible from outside you need to bind it to 0.0.0.0 meaning "all interfaces". Listening on 0.0.0.0 means listening from anywhere with network access to this computer. For example, from this very computer, from local network or from the Internet. And listening on 127.0.0.1 means from this computer only.

Please let me know if that helped. Or if I have misunderstood you.

-- OhHiMark
Source: StackOverflow