I would like to get mapped port from inside node.js application.
ex.
docker-compose
:
my-app:
build:
context: ./my-app
So the first thing is that currently, docker doesn't give the metadata information inside the container in any direct manner. There are workarounds to the same though
Now passing a docker socket inside your container is something that you can live with when it concerns a development environment but not prod environment
So what you want to do is build your own metadata service and then let it give the information back to your container without exposing the docker socket itself.
Now to get this metadata you can use two approaches
In this case, you create a simple endpoint like /metadata/container_id
and then return the data you want.
This service can run on the main host itself or on the container which has the docker socket mapped.
Now in your NodeJS service, you will get the current container ID and then use the same to call this service. This can be done in different ways as listed in below thread
Docker, how to get container information from within the container?
The data returned you can restrict, in case I only want ports to be exposed, I will only return the detail of the ports only.
The problem with this approach is that you are still letting the service figure out its own container id and also at the same time trusting that it is sending the correct container id
To get metadata information without passing information, we need to identify which pad made a call to the metadata server
This can be identified by using the source IP. Then we need to identify which container belongs to this IP. Once we have the container ID, we can return the metadata.
Below is a simple bash script that does the same
#!/bin/bash
CONTAINER_IP=$SOCAT_PEERADDR
CONTAINER_ID=$(docker ps -q | xargs docker inspect -f '{{.Id}}|{{range .NetworkSettings.Networks}}{{.IPAddress}}|{{end}}' | grep "|$CONTAINER_IP|" | cut -d '|' -f 1)
echo -e "HTTP/1.1 200 OK\r\nContent-Type: application/json;\r\nServer: $SOCAT_SOCKADDR:$SOCAT_SOCKPORT\r\nClient: $SOCAT_PEERADDR:$SOCAT_PEERPORT\r\nConnection: close\r\n";
METADATA=$(docker inspect -f '{{ json .NetworkSettings.Ports }}' $CONTAINER_ID)
echo $METADATA
Now to convert this to a web server we can use socat
.
sudo socat -T 1 TCP-L:10081,pktinfo,reuseaddr,fork EXEC:./return_metadata.sh
Now inside the container when we call this metadata server
root@a9cf6dabdfb4:/# curl 192.168.33.100:10081
{"80/tcp":[{"HostIp":"0.0.0.0","HostPort":"8080"}]}
and the docker ps for the same
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a9cf6dabdfb4 nginx "nginx -g 'daemon of…" 3 hours ago Up 3 hours 0.0.0.0:8080->80/tcp nginx
Now how you create such a metadata server is upto you. You can use any approach
You can also add the IP of the service using extra_hosts
in your docker-compose.yml
and make the call like curl metaserver:10081
This should be a decently secure approach, without compromising on what you need