Warm tip: This article is reproduced from serverfault.com, please click

Docker: Multiple Client/Server Containers without Hardcoding IP Addresses

发布于 2021-02-24 17:48:09

Good afternoon. I am brand new to Docker and I'm struggling to come up with a scalable solution for generating network traffic. I want to have many client and server containers talking with each other across a docker bridge network. Right now I have one client and one server container, and they talk to each other but it is incredibly slow. Additionally, I had to hard-code their IP addresses for them to be able to find each other. There has to be a better way to scale this up, but I'm struggling because my client.py code needs the server's IP address and port number to find the server.

Client.py

from pyModbusTCP.client import ModbusClient
import time    

c = ModbusClient()
c.host("172.20.0.2")
c.port(502)
    
while True:
    if not c.is_open():
        if not c.open():
            print("Unable to connect to Server at 172.20.0.2:502")
        
    if c.is_open():
        regs = c.read_holding_registers(0, 4)
        if regs:
            print("Register #0: " + str(regs[0]))
            print("Register #1: " + str(regs[1]))
            print("Register #2: " + str(regs[2]))
            print("Register #3: " + str(regs[3]))
    time.sleep(5)

Client DockerFile

FROM python:3
    
WORKDIR ~/client-server-docker-test/client
    
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
    
COPY . .
    
CMD ["python", "./client.py"]

Server.py

from pyModbusTCP.server import ModbusServer, DataBank
from time import sleep
from random import uniform
    
# set up server
server = ModbusServer('localhost',502, no_block=True)
# initialize register 0 with value of 80
DataBank.set_words(0, [80])
    
try:
    print("Start server...")
        server.start()
        print("Server is online...")
        # change register value every 5 seconds. 
        while True:
            # Set Register @ Address 0 to random int. value
            DataBank.set_words(0, [int(uniform(0,100))])
            # Tank 2
            DataBank.set_words(1, [int(uniform(0,100))])
            # Tank 3
            DataBank.set_words(2, [int(uniform(0,100))])
            # Tank 4
            DataBank.set_words(3, [int(uniform(0,100))])
            sleep(5)
# when hit ctrl+C in CMD line, shut down server
except:
    print("Shutdown server....")
    server.stop()
    print("Server is offline...")
    sleep(0.5)

Server DockerFile

FROM python:3
    
WORKDIR ~/client-server-docker-test/server
    
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
    
COPY . .
    
CMD ["python", "./server.py"]

Docker-Compose File

version: '2.1'
services:
  server:
    image: server1
    container_name: server1_con
    ports:
      - 502:502
    networks:
      test_net:
        ipv4_address: 172.20.0.2
    
  client:
    image: client1
    container_name: client1_con
    depends_on:
      - server
    networks:
      test_net:
        ipv4_address: 172.20.0.3
    
networks:
  test_net:
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 172.20.0.0/16

This runs, but it takes forever for my client to print out register values to the command line. Besides being slow, my current method isn't scalable. I have to use the MODBUS TCP protocol for communications, which is why the python code uses pyModbusTCP. But there has to be a way to have 10 client and server containers talking to each other without having to specify an IP address for each one?

I've looked into docker swarm, but I couldn't see how it would help my problem about assigning static IP addresses for each container. I've also tried to add an environmental variable where docker gets the IP address of the server and passes it into the client.py code, but I couldn't get it to work correctly. I'm very stuck, and any help would be greatly appreciated. Thank you so much for your time.

Edit: Its incredibly slow means when I run "docker-compose up", I get a notification "Attaching to server1_con, client1_con" but then it takes around 10 minutes for my command line to start showing print outs.

Questioner
Vic
Viewed
0
Brits 2021-02-25 04:57:31

There are a number of issues here; firstly with regards to "around 10 minutes for my command line to start showing print outs"; run python in unbuffered mode e.g.:

CMD ["python","-u", "client.py"]

The second issue is with the way you are starting the server ModbusServer('localhost',502, no_block=True) - this will bind to localhost and not be accessible outside of the host (or container in this case). Use ModbusServer('0.0.0.0',502, no_block=True) to bind to all addresses on the host.

Third is removing the need to hardcode IP addresses; update your docker-compose.yml to (note: I have added build paths):

version: '2.1'
services:
  server:
    build: ./server
    networks:
      - test_net

  client:
    build: ./client
    depends_on:
      - server
    networks:
      - test_net

networks:
  test_net:
    driver: bridge
    ipam:
      driver: default

and change client.py to use the service name c.host("server"). With those changes your application does what I believe you expect when I run it on my machine.