How to connect socket.io inside docker-compose between containers - docker

I have one container that is serving http on port 4000.
it has socket server attached
docker-compose:
dashboard-server:
image: enginetonic:compose1.2
container_name: dashboard-server
command: node src/service/endpoint/dashboard/dashboard-server/dashboard-server.js
restart: on-failure
ports:
- 4000:4000
integration-test:
image: enginetonic:compose1.2
container_name: integration-test
testRegex "(/integration/.*|(\\.|/)(integration))\\.jsx?$$"
tty: true
server:
const http = require('http').createServer(handler)
const io = Io(http)
io.on('connection', socket => {
logger.debug('socket connected')
})
io.use((socket, next) => {
logger.debug('socket connection established.')
})
http.listen(4000, '127.0.0.1', () => {
console.log(
`Server running at http://127.0.0.1:4000/`
)
output in docker:
Server running at http://127.0.0.1:4000/
https is listening: true
Now, I am trying to connect to this server from another container like this:
file:
const url = `ws://dashboard-server:4000`
const ioc = IoC.connect(url)
ioc.on('error', error => {
console.log(error.message)
})
ioc.on('connect', res => {
console.log('connect')
})
ioc.on('connect_error', (error) => {
console.log(error.message)
})
output:
xhr poll error
When I run both locally in terminal, I get correct response
{"message":"socket connection established","level":"debug"}
Why isnt socket making connection inside container, but locally it is?
What am I doing wrong?
edit: only part of files are displayed for readability. socket connects normaly on local machine with with spawning both files in separate terminals

You need to link the docker containers and refer to them by name, not 127.0.0.1. https://docs.docker.com/compose/networking provides more doc. You'll also need to listen to '0.0.0.0' so that you accept connections across the docker network.
I only see one container in your compose file. If you're trying to connect to the docker containers from outside docker, you'll have to expose a port. The same reference shows you how.
http.listen(4000, '127.0.0.1', () => {
should become
http.listen(4000, '0.0.0.0', () => {
so that the server is listening on all addresses, including the address that docker is automatically allocating on a docker network.
Then the client has to refer to the server by the name given in docker compose, so
const url = `ws://127.0.0.1:4000`
becomes
const url = `ws://dashboard-server:4000`

Related

How to establish zmq connections between docker containers using docker compose option network_mode: host?

I want to establish a connection using the draft server/client pattern of ZeroMQ across different docker containers. For that, I have a zmq.server that is running in docker_container_server. In addition, I have a zmq.client which runs in docker_container_client. Both containers are based on different docker images. Despite using the network_mode: host option with docker compose, this does not work. If I do the same within one container, it just works fine.
The docker-compose files are the following.
docker-compose-client.yaml:
services:
client:
privileged: true
image: client-image
container_name: client
command: tail -F anything
network_mode: host
environment:
- "ROS_MASTER_URI=http://127.0.0.1:11311"
- "ROS_HOSTNAME=127.0.0.1"
build:
context: ./client
network: host
dockerfile: Dockerfile
volumes:
- /var/run/docker.sock:/var/run/docker.sock
docker-compose-server.yaml:
services:
server:
image: server-image
privileged: true
container_name: server
network_mode: host
command: tail -F anything
volumes:
- /var/run/docker.sock:/var/run/docker.sock
build:
context: .
network: host
dockerfile: Dockerfile.server
The server application uses pyzmq as follows:
import zmq
class ServerExample():
def __init__(self):
self.stop = False
self.ctx = zmq.Context.instance()
self.url = 'tcp://127.0.0.1:5555'
self.server = self.ctx.socket(zmq.SERVER)
self.server.bind(self.url)
try:
while not self.stop:
msg = self.server.recv(copy=False) # stucks in this line
print(msg.routing_id)
if __name__ == '__main__':
server = ServerExample()
The client uses cppzmq as follows: (I also tried a pyzmq-client. This also works within a container but not across different containers.)
#include <zmq.hpp>
class ClientExample
{
private:
zmq::socket_t socket;
zmq::context_t ctx;
public:
ClientExample()
{
socket = zmq::socket_t(ctx, zmq::socket_type::client);
socket_.connect("tcp://127.0.0.1:5555");
char[] some_data;
socket_.send(zmq::buffer(some_data)); // not received by server
}
};
The containers are started by docker compose up server and docker compose up client.
When I do everything in one container and have a look on netstat -t | grep 5555, I get the same result in the container and on the host: (In this case, there are two clients and one server)
tcp 0 2856 localhost:56674 localhost:5555 ESTABLISHED
tcp 0 2856 localhost:5555 localhost:47666 ESTABLISHED
tcp 0 0 localhost:5555 localhost:56674 ESTABLISHED
That is why I think that the docker compose option network_mode: host is properly working in my case.
When I start the docker_container_server and docker_container_client, the execution stucks in the line where the server should receive a message. netstat -t | grep 5555 ouputs both on the host and in the containers just nothing.
The versions I use:
Ubuntu 20.04
Docker Compose version v2.12.2
Docker Engine Version 20.10.21
libzmq v4.3.4
cppzmq v4.9.0
pyzmq v25.0.0b1
I searched a lot through the internet, read the documentations of docker, docker compose, zmq etc. I came to the conclusion, that it should work with the network_mode: host option and I think I confirmed that the network_mode: host option works as it should. The code itself should not contain any mistakes, as it works fine within one container. But I didn't figure out, why the communication doesn't work across containers.

Request between Docker containers failing dial tcp 172.18.0.6:3050: connect: connection refused

I am struggling with Go requests between containers.
The issue that I have that the rest of my containers can send request to the node Container that give response, but when I send request from my GoLang application to node I get that refuse error "dial tcp 172.18.0.6:3050: connect: connection refused".
So my whole docker set up is:
version: "3.3"
services:
##########################
### SETUP SERVER CONTAINER
##########################
node:
# Tell docker what file to build the server from
image: myUserName/mernjs:node-dev
build:
context: ./nodeMyApp
dockerfile: Dockerfile.dev
# The ports to expose
expose:
- 3050
# Port mapping
ports:
- 3050:3050
# Volumes to mount
volumes:
- ./nodeMyApp/src:/app/server/src
# Run command
# Nodemon for hot reloading (-L flag required for polling in Docker)
command: nodemon -L src/app.js
# Connect to other containers
links:
- mongo
# Restart action
restart: always
react:
ports:
- 8000:8000
build:
context: ../reactMyApp
dockerfile: Dockerfile.dev
volumes:
- ../reactMyApp:/usr/src/app
- /usr/src/app/node_modules
- /usr/src/app/.next
restart: always
environment:
- NODE_ENV=development
golang:
build:
context: ../goMyApp
environment:
- MONGO_URI=mongodb://mongo:27017
# Volumes to mount
volumes:
- ../goMyApp:/app/server
links:
- mongo
- node
restart: always
So my React app can send the request to "http://node:3050/api/greeting/name" and it get the response even that react app is not linked to the node app but when Golang app sends request to node docker container it gets connection refuse message GetJson err: Get "http://node:3050/api/greeting/name": dial tcp 172.18.0.6:3050: connect: connection refused
func GetJson(url string, target interface{}) error {
r, err := myClient.Get(url)
if err != nil {
fmt.Println("GetJson err: ", err)
return err
}
defer r.Body.Close()
return json.NewDecoder(r.Body).Decode(target)
}
type ResultsDetails struct {
Greeting string `bson:"greatingMessage" json:"greatingMessage"`
Message string `bson:"message" json:"message"`
}
func GetGreetingDetails(name string) ResultsDetails {
var resp ResultsDetails
GetJson("http://node:3050/api/greeting/"+name, &resp)
return resp
}
So how do I solve the Golang request to another Docker Node Container when docker doesnt see the host as the name of my container 'node'?
Update:
By accident i put Golang port, which it doenst run on any port since it is application that checks on database records. So it hasnt got any api, therefore it is not running on any port.
Is that could be the problem why my golang application cannot communication to other containers?
Since i have also another golang application which is api application and it is running on 5000 port and it is well communicating to my node application?
Network info:
After checking the network if node and golang share the same network and the answer is yes. All containers share the same network
(Unrelated to my issue) To anyone who has "dial tcp connection refused" issue I suggest to go though that guide https://maximorlov.com/4-reasons-why-your-docker-containers-cant-talk-to-each-other/. Really helpful. To those who this guide wont help prob read bellow this, maybe you trying to request the container api after just containers were built :D
For those who was interested what was wrong:
Technically reason why I was getting this error is because of the request that I was trying to run, was just when all containers were built.
I believe there is some delay to the network after containers are built. Thats why there host was throwing "dial tcp 172.18.0.6:3050: connect: connection refused" I've run that test on other containers that could possibly send request to that node container and they were all failing after the build time. But when re-requesting after few seconds all worked out.
Sorry to bother you guys. I really spent 3 days into this issue. And I was looking into completely wrong direction. Never thought that the issue is that silly :D
Thanks for you time.
I've met the same error in my harbor registry service.
After I docker exec -it into the container, and check if the service is available, and finally I found that http_proxy has been set.
Remove the http_proxy settings for docker service, then it works like a charm.
Failed on load rest config err:Get "http://core:8080/api/internal/configurations": dial tcp 172.22.0.8:8080: connect: connection refused
$docker exec -it harbor-jobservice /bin/bash
$echo $http_proxy $https_proxy

spark app socket communication between container on docker spark cluster

So I have a Spark cluster running in Docker using Docker Compose. I'm using docker-spark images.
Then i add 2 more containers, 1 is behave as server (plain python) and 1 as client (spark streaming app). They both run on the same network.
For server (plain python) i have something like
import socket
s.bind(('', 9009))
s.listen(1)
print("Waiting for TCP connection...")
while True:
# Do and send stuff
And for my client (spark app) i have something like
conf = SparkConf()
conf.setAppName("MyApp")
sc = SparkContext(conf=conf)
sc.setLogLevel("ERROR")
ssc = StreamingContext(sc, 2)
ssc.checkpoint("my_checkpoint")
# read data from port 9009
dataStream = ssc.socketTextStream(PORT, 9009)
# What's PORT's value?
So what is PORT's value? is it the IP Adress value from docker inspect of the container?
Okay so i found that i can use the IP of the container, as long as all my containers are on the same network.
So i check the IP by running
docker inspect <container_id>
and check the IP, and use that as host for my socket
Edit:
I know it's kinda late, but i just found out that i can actually use the container's name as long as they're in the same network
More edit:
i made changes in docker-compose like:
container-1:
image: image-1
container_name: container-1
networks:
- network-1
container-2:
image: image-2
container_name: container-2
ports:
- "8000:8000"
networks:
- network-1
and then in my script (container 2):
conf = SparkConf()
conf.setAppName("MyApp")
sc = SparkContext(conf=conf)
sc.setLogLevel("ERROR")
ssc = StreamingContext(sc, 2)
ssc.checkpoint("my_checkpoint")
# read data from port 9009
dataStream = ssc.socketTextStream("container-1", 9009) #Put container's name here
I also expose the socket port in Dockerfile, I don't know if that have effect or not

Cannot connect Asp.net Core WebApi in Docker with EFCore Mysql provider to non Docker Mysql on host server

I have an Asp.Net Core 3.1.3 WebApi with EfCore 3.1.3 and Pomelo.EntityFrameworkCore.MySql as provider
This WebApi is built and deployed in Docker.
The host server is Debian 10, it is configured with UFW as firewall.
I have a Mysql Server installed on the host server.
I set the bind address to 0.0.0.0 for Mysql
/etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
log-error = /var/log/mysql/error.log
bind-address = 0.0.0.0
I created a rule in UFW to allow only localhost and docker0 bridge ip address
In my docker-compose config, I added the ip address of my host server, but the connection to MySql fails
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
Entity Framework Core 3.1.3 initialized 'ApplicationContext' using provider 'Pomelo.EntityFrameworkCore.MySql' with options: using lazy-loading proxies ServerVersion 8.0.19 MySql
fail: Microsoft.EntityFrameworkCore.Database.Connection[20004]
An error occurred using the connection to database '<db_name>' on server '<host_ip_address>'.
info: Microsoft.EntityFrameworkCore.Infrastructure[10404]
A transient exception has been encountered during execution and the operation will be retried after 0ms.
MySql.Data.MySqlClient.MySqlException (0x80004005): Connect Timeout expired.
---> System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.Sockets.Socket'.
In Startup.cs, here is the config for EfCore:
services.AddDbContext<ApplicationContext>(opts =>
{
opts
.UseLazyLoadingProxies()
.UseMySql(connectionString, mySqlOptions =>
mySqlOptions
.ServerVersion(new Version(dbOptions.Version), ServerType.MySql)
.CharSet(CharSet.Utf8Mb4)
.EnableRetryOnFailure(3))
.EnableDetailedErrors();
});
To build the connection string, I use MySql.Data.MySqlClient.MySqlConnectionStringBuilder:
var builder = new MySql.Data.MySqlClient.MySqlConnectionStringBuilder
{
Server = dbOptions.Server,
Database = dbOptions.Name,
UserID = dbOptions.User,
Password = dbOptions.Password,
PersistSecurityInfo = true
};
return builder.ToString();
I generate the migration script and deployed it on the server, since Pomelo sets Database Name to Empty String when calling dbContext.Database.Migrate();
In the dockerfile, I expose ports 80 and 443.
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
...
I sent Database params as Environment variables in the docker-compose file.
I did some data seeding to add users and roles from Identity.
var roleManager = provider.GetRequiredService<RoleManager<RoleModel>>();
roleManager.CreateAsync(new RoleModel(roleName)).GetAwaiter().GetResult();
...
What did I miss?
I did not find any other solution than to deploy my docker container using host network mode in my docker-compose.
docker-compose.yml
services:
<service_name>:
image: <image_name>
network_mode: "host" #important here
ports:
- "5000:5000"
- "5001:5001"
environment:
ASPNETCORE_Kestrel__Certificates__Default__Password: "${ASPNETCORE_KESTREL_CERTIFICATE_PASSWORD}"
ASPNETCORE_Kestrel__Certificates__Default__Path: "${ASPNETCORE_KESTREL_CERTIFICATE_PATH}"
ASPNETCORE_ENVIRONMENT: Production
"Database:Server": "${Database_Server}"
"Database:Version": "${Database_Version}"
"Database:Name": "${Database_Name}"
"Database:User": "${Database_User}"
"Database:Password": "${Database_Password}"
build:
context: ./Src
dockerfile: Dockerfile-build
volumes:
- ${CERTIFICATE_PATH}:/https
- ${LOGS_PATH}:/app/logs
In that case, I just need to use the ip address of the host server to connect to MySql (with all Ufw, MySql configuration which come with that).

Socket connections between docker containers fails

I have multiple containers being deployed through a docker-compose file seen below
version: '3'
services:
module2:
restart: always
build:
dockerfile: Dockerfile
context: ./Module-2
ports:
- '16667:16667'
module3:
build:
dockerfile: Dockerfile
context: ./Module-3
ports:
- '16669:16669'
Module 2 takes a socket request from an outside source and works as intended. The trouble begins when module 2 tries to connect with module 3
Module 2 code (JAVA)
private int socket_port = 16669;
private String server = "127.0.0.1";
public TextOutputSocket() {
}
public TextOutputSocket(String host, int socket_port) {
this.server = host;
this.socket_port = socket_port;
}
public void sendText(String textToSend) {
OutputStream os = null;
Socket sock = null;
try {
System.out.println("Connecting to " + server + ":" + socket_port);
sock = new Socket(server, socket_port);
os = sock.getOutputStream();
module 3 code (GO)
ln, err := net.Listen("tcp", ":16669")
if err != nil {
fmt.Println(err)
// handle error
}
Module 2 recieves a connection refused error when ever i try to send the request.
I feel I don't have the best understanding of docker networks and i assume this is where the problem lies.
Thank you for the help in advance
In your case, when you spin up docker-compose, module2 and module3 2 containers will be in the same docker network and they can connect to each other using their DNS names i.e. module2 and module3 respectively.
As a result, you should update your module2 code to be like this
private int socket_port = 16669;
private String server = "module3";
public TextOutputSocket() {
}
...
Note that you will not need to do a port mapping like - '16667:16667' or - '16669:16669' in order for these 2 modules to talk to each other.
First you need to understand how docker containers work. Each of you applications are deployed in two seperate containers. So when trying to connect to a different container you need to give the ip or the hostname of that specific container.
Here you have tried to connect to 1669 of localhost, instead what you should be doing is try to connect to the other container. This can be done by setting the container name of the module3 container and docker dns will resolve the ip address for you.
Simple replace 127.0.0.1 with module3

Resources