diff --git a/Dockerfile b/Dockerfile index 03d325d..d435410 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ FROM nginx:1.25.3 ENV PORT_IP_MAP "" ENV PYTHONUNBUFFERED=1 +ENV PLACEHOLDER_SERVER_SLEEPING_IP "" # Install Python and pip RUN apt-get update && apt-get install -y python3 python3-pip python3-venv diff --git a/app/app.py b/app/app.py index d309ea2..6451b96 100644 --- a/app/app.py +++ b/app/app.py @@ -1,68 +1,20 @@ -import socket -import threading -import os +from requestHandler import RequestHandler +from utils import docker_container_mapping from dockerHandler import DockerHandler from nginxHandler import NginxHandler -def docker_container_mapping(): - port_ip_map_str = os.environ.get('PORT_IP_MAP') - # Convert the environment variable to a Python dictionary - port_ip_map = {} - for line in port_ip_map_str.split('\n'): - if line: # ignore empty lines - port, ip = line.split(':') - port_ip_map[port.strip()] = ip.strip() - - return port_ip_map - - -class RequestHandler(threading.Thread): - def __init__(self, port, docker_handler): - super().__init__() - self.port = port - self.docker_handler = docker_handler - # Create a TCP/IP socket - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # Bind the socket to the port - server_address = ('localhost', self.port) - print('starting up on {} port {}'.format(*server_address)) - self.sock.bind(server_address) - # Listen for incoming connections - self.sock.listen(1) - - def run(self): - while True: - print('waiting for a connection on port {}'.format(self.port)) - self.connection, self.client_address = self.sock.accept() - try: - print('connection from', self.client_address) - self.handle_request() - finally: - self.connection.close() - - def handle_request(self): - print('handling request on port {}'.format(self.port)) - # Get the Docker container name for this port - container_ip = docker_container_mapping().get(str(self.port)) - if container_ip: - # Start the Docker container - print('starting docker container {}'.format(container_ip)) - self.docker_handler.get_container_by_ip(container_ip).start() - # Send a response - self.connection.sendall("Server starting...".encode('utf-8')) - else: - print('no docker container mapped to this port') - - def main(): + # Create a DockerHandler instance docker_handler = DockerHandler( 'unix://var/run/docker.sock', docker_container_mapping()) + # Create a RequestHandler instance for each port for port in range(25560, 25571): request_handler = RequestHandler(port, docker_handler) request_handler.start() + # Create an NginxHandler instance nginx_handler = NginxHandler('/etc/nginx/nginx.conf') nginx_handler.setup_config_file( docker_container_mapping(), docker_handler.get_current_container_ip()) diff --git a/app/dockerHandler.py b/app/dockerHandler.py index c62329a..dc6b8cd 100644 --- a/app/dockerHandler.py +++ b/app/dockerHandler.py @@ -32,13 +32,6 @@ class DockerHandler: containers = self.client.containers.list(all=True) for container in containers: - print('current network: {}'.format(self.current_network)) - print('checking container {}'.format(container.name)) - print('container networks: {}'.format( - container.attrs['NetworkSettings']['Networks'])) - print('container ip: {}'.format( - container.attrs['NetworkSettings']['Networks'][self.current_network]['IPAMConfig']['IPv4Address'])) - networks = container.attrs['NetworkSettings']['Networks'] if self.current_network in networks and networks[self.current_network]['IPAMConfig']['IPv4Address'] == ip: print('found docker container {} with ip {} in network {}'.format( diff --git a/app/nginxHandler.py b/app/nginxHandler.py index 103b91d..2b05d6e 100644 --- a/app/nginxHandler.py +++ b/app/nginxHandler.py @@ -1,4 +1,5 @@ import os +import time class NginxHandler: @@ -24,7 +25,8 @@ class NginxHandler: print(f.read()) def setup_config_file(self, port_ip_map, current_container_ip): - proxy_timeout = "300ms" + placeholder_ip = os.environ.get('PLACEHOLDER_SERVER_SLEEPING_IP') + proxy_timeout = "5s" self.stop() print('Setting up NGINX config file...') print('port_ip_map: {}'.format(port_ip_map)) @@ -39,6 +41,10 @@ class NginxHandler: port_ip_map[port])) nginx_conf.write( ' server 127.0.0.1:{} backup;\n'.format(port)) + + nginx_conf.write( + ' server {}:25565 backup;\n'.format(placeholder_ip)) + nginx_conf.write(' }\n') nginx_conf.write(' server {\n') diff --git a/app/requestHandler.py b/app/requestHandler.py new file mode 100644 index 0000000..d587aff --- /dev/null +++ b/app/requestHandler.py @@ -0,0 +1,58 @@ +import json +import socket +import threading +from utils import docker_container_mapping + + +class RequestHandler(threading.Thread): + def __init__(self, port, docker_handler): + super().__init__() + self.port = port + self.docker_handler = docker_handler + # Create a TCP/IP socket + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # Bind the socket to the port + server_address = ('localhost', self.port) + print('starting up on {} port {}'.format(*server_address)) + self.sock.bind(server_address) + # Listen for incoming connections + self.sock.listen(1) + + def run(self): + while True: + print('waiting for a connection on port {}'.format(self.port)) + self.connection, self.client_address = self.sock.accept() + try: + print('connection from', self.client_address) + self.handle_request() + finally: + self.connection.close() + + def handle_request(self): + print('handling request on port {}'.format(self.port)) + container_ip = docker_container_mapping().get(str(self.port)) + if container_ip: + request = self.connection.recv(1024) + print('---------------------------') + print(request) + print('---------------------------') + + if request[0] == 0x10: + if b'\x01' in request: + print('ping') + self.redirect_to_placeholder() + elif b'\x02' in request: + print('join') + print('starting docker container {}'.format(container_ip)) + self.docker_handler.get_container_by_ip( + container_ip).start() + + elif request[0] == 0xFE: + print('legacy server list ping') + self.redirect_to_placeholder() + + else: + print('no docker container mapped to this port') + + def redirect_to_placeholder(self): + print('redirecting to placeholder') diff --git a/app/utils.py b/app/utils.py new file mode 100644 index 0000000..05cc7b0 --- /dev/null +++ b/app/utils.py @@ -0,0 +1,13 @@ +import os + + +def docker_container_mapping(): + port_ip_map_str = os.environ.get('PORT_IP_MAP') + # Convert the environment variable to a Python dictionary + port_ip_map = {} + for line in port_ip_map_str.split('\n'): + if line: # ignore empty lines + port, ip = line.split(':') + port_ip_map[port.strip()] = ip.strip() + + return port_ip_map diff --git a/docker-compose.yml b/docker-compose.yml index d56122b..c8d6909 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,32 +10,51 @@ networks: services: auto_starter: + container_name: mc_auto_starter build: . ports: - 25565:25565 - 25566:25566 environment: + PLACEHOLDER_SERVER_SLEEPING_IP: "172.20.0.3" PORT_IP_MAP: | - 25565: 172.20.0.3 - 25566: 172.20.0.4 + 25565: 172.20.0.5 + 25566: 172.20.0.6 networks: mc_network: ipv4_address: 172.20.0.2 volumes: - /var/run/docker.sock:/var/run/docker.sock + mc_placeholder_server: + container_name: mc_placeholder_server + image: itzg/minecraft-server + environment: + type: "PAPER" + EULA: "TRUE" + MOTD: "Sleeping | Join & Wait to wake up" + MAX_PLAYERS: "0" + MAX_MEMORY: "500M" + INIT_MEMORY: "100M" + networks: + mc_network: + ipv4_address: 172.20.0.3 + mc: + container_name: example_mc_server_1 image: itzg/minecraft-server environment: type: "PAPER" EULA: "TRUE" MOTD: "TEST1" + MAX_PLAYERS: "0" #! Dont change SERVER_PORT. Use PORT_IP_MAP in auto_starter instead. #! SERVER_PORT default is "25565" networks: mc_network: - ipv4_address: 172.20.0.3 + ipv4_address: 172.20.0.5 mc2: + container_name: example_mc_server_2 image: itzg/minecraft-server environment: type: "PAPER" @@ -45,4 +64,4 @@ services: #! SERVER_PORT default is "25565" networks: mc_network: - ipv4_address: 172.20.0.4 + ipv4_address: 172.20.0.6 diff --git a/nginx.conf b/nginx.conf index f9708af..1a36c73 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,19 +1,25 @@ events { } - stream { - upstream upstream_25565 { - server 172.20.0.3:25565; - server 127.0.0.1:25565 backup; - } - server { - listen 172.20.0.2:25565; - proxy_pass upstream_25565; - } - upstream upstream_25566 { - server 172.20.0.4:25565; - server 127.0.0.1:25566 backup; - } - server { - listen 172.20.0.2:25566; - proxy_pass upstream_25566; - } - } \ No newline at end of file +stream { + upstream upstream_25565 { + server 172.20.0.5:25565; + server 127.0.0.1:25565 backup; + server 172.20.0.3:25565 backup; + } + server { + listen 172.20.0.2:25565; + proxy_connect_timeout 5s; + proxy_timeout 5s; + proxy_pass upstream_25565; + } + upstream upstream_25566 { + server 172.20.0.6:25565; + server 127.0.0.1:25566 backup; + server 172.20.0.3:25565 backup; + } + server { + listen 172.20.0.2:25566; + proxy_connect_timeout 5s; + proxy_timeout 5s; + proxy_pass upstream_25566; + } +} \ No newline at end of file