Update Dockerfile and utils.py

This commit is contained in:
dertyp7
2023-12-27 16:21:45 +01:00
parent e0bfc6a0ef
commit 0eb86721ff
10 changed files with 556 additions and 563 deletions

View File

@@ -3,11 +3,9 @@ FROM nginx:1.25.3
ENV PORT_MAP "" ENV PORT_MAP ""
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV PLACEHOLDER_SERVER_SLEEPING_IP ""
ENV PLACEHOLDER_SERVER_STARTING_IP ""
# Install Python and pip # Install Python, pip and git
RUN apt-get update && apt-get install -y python3 python3-pip python3-venv RUN apt-get update && apt-get install -y python3 python3-pip python3-venv
# Create a virtual environment and activate it # Create a virtual environment and activate it
RUN python3 -m venv /app/venv RUN python3 -m venv /app/venv

1
app/FakeMCServer Submodule

Submodule app/FakeMCServer added at d7d7bedb2b

View File

@@ -1,56 +1,76 @@
from math import log import os
import time import time
from dockerHandler import DockerHandler from dockerHandler import DockerHandler
from nginxHandler import NginxHandler from nginxHandler import NginxHandler
from minecraftServerHandler import MinecraftServerHandler from minecraftServerHandler import MinecraftServerHandler
from requestHandler import RequestHandler from requestHandler import RequestHandler
import logging import logging
from FakeMCServer.fake_mc_server import FakeMCServer
import threading
logging.basicConfig(level=logging.INFO)
logging.basicConfig(level=logging.INFO)
def main() -> None:
try: def init_placeholder_servers():
logging.info('[INIT] initializing auto starter...') sleeping = FakeMCServer(port=20000, motd={
logging.info('[INIT] initializing docker handler...') "1": "sleeping!", "2": "§aCheck example.com for more information!"})
docker_handler: DockerHandler = DockerHandler( starting = FakeMCServer(port=20001, motd={
'unix://var/run/docker.sock') "1": "starting!", "2": "§aCheck example.com for more information!"})
logging.info('[INIT] docker handler initialized')
# Create threads for each server initialization
logging.info('[INIT] initializing nginx handler...') sleeping_thread = threading.Thread(target=sleeping.start_server)
nginx_handler: NginxHandler = NginxHandler('/etc/nginx/nginx.conf') starting_thread = threading.Thread(target=starting.start_server)
nginx_handler.update_config_file( # Start the threads
docker_handler) sleeping_thread.start()
starting_thread.start()
logging.info('[INIT] nginx handler initialized')
logging.info('[INIT] initializing minecraft server handler...') def main() -> None:
minecraft_server_handler: MinecraftServerHandler = MinecraftServerHandler( try:
docker_handler, nginx_handler) logging.info('[INIT] initializing placeholder servers...')
init_placeholder_servers()
# Find all Minecraft servers and add them to the MinecraftServerHandler instance logging.info('[INIT] placeholder servers initialized')
for service_name in docker_handler.get_port_map().values():
minecraft_server_handler.add_server(service_name) logging.info('[INIT] initializing auto starter...')
logging.info('[INIT] initializing docker handler...')
logging.info('[INIT] wait 20 seconds before stopping all servers...') docker_handler: DockerHandler = DockerHandler(
time.sleep(20) 'unix://var/run/docker.sock')
minecraft_server_handler.stop_all_servers() logging.info('[INIT] docker handler initialized')
logging.info('[INIT] minecraft server handler initialized')
logging.info('[INIT] initializing nginx handler...')
logging.info('[INIT] initializing request handlers...') nginx_handler: NginxHandler = NginxHandler('/etc/nginx/nginx.conf')
# Create a RequestHandler instance for each port
for port in docker_handler.get_port_map().keys(): nginx_handler.update_config_file(
logging.info(f'[INIT] creating request handler for port {port}') docker_handler)
request_handler: RequestHandler = RequestHandler(
int(port), docker_handler, minecraft_server_handler) logging.info('[INIT] nginx handler initialized')
request_handler.start()
logging.info('[INIT] request handlers initialized') logging.info('[INIT] initializing minecraft server handler...')
minecraft_server_handler: MinecraftServerHandler = MinecraftServerHandler(
except Exception as e: docker_handler, nginx_handler)
logging.error(f'An error occurred: {e}')
# Find all Minecraft servers and add them to the MinecraftServerHandler instance
for service_name in docker_handler.get_port_map().values():
if __name__ == "__main__": minecraft_server_handler.add_server(service_name)
main()
logging.info('[INIT] wait 20 seconds before stopping all servers...')
time.sleep(20)
minecraft_server_handler.stop_all_servers()
logging.info('[INIT] minecraft server handler initialized')
logging.info('[INIT] initializing request handlers...')
# Create a RequestHandler instance for each port
for port in docker_handler.get_port_map().keys():
logging.info(f'[INIT] creating request handler for port {port}')
request_handler: RequestHandler = RequestHandler(
int(port), docker_handler, minecraft_server_handler)
request_handler.start()
logging.info('[INIT] request handlers initialized')
except Exception as e:
logging.error(f'An error occurred: {e}')
if __name__ == "__main__":
main()

View File

@@ -1,98 +1,98 @@
from typing import Dict from typing import Dict
import docker import docker
from docker import DockerClient from docker import DockerClient
from docker.models.networks import Network from docker.models.networks import Network
import os import os
import logging import logging
from utils import docker_container_mapping from utils import docker_container_mapping
class DockerHandler: class DockerHandler:
def __init__(self, base_url: str): def __init__(self, base_url: str):
logging.info( logging.info(
f'[DockerHandler] initializing docker handler with base url {base_url} and port ip map: {self.get_port_map()}...') f'[DockerHandler] initializing docker handler with base url {base_url} and port ip map: {self.get_port_map()}...')
self.base_url: str = base_url self.base_url: str = base_url
self.client: DockerClient = DockerClient(base_url=base_url) self.client: DockerClient = DockerClient(base_url=base_url)
self.current_network: Network = self.get_current_network() self.current_network: Network = self.get_current_network()
logging.info('[DockerHandler] docker handler initialized') logging.info('[DockerHandler] docker handler initialized')
logging.info( logging.info(
f'[DockerHandler] current container name: {self.get_auto_starter_container_name()}') f'[DockerHandler] current container name: {self.get_auto_starter_container_name()}')
logging.info( logging.info(
f'[DockerHandler] current network: {str(self.current_network)}') f'[DockerHandler] current network: {str(self.current_network)}')
def get_port_map(self) -> Dict[str, str]: def get_port_map(self) -> Dict[str, str]:
return docker_container_mapping() return docker_container_mapping()
def stop_container(self, container) -> None: def stop_container(self, container) -> None:
if container: if container:
logging.info( logging.info(
f'[DockerHandler] stopping container {str(container.name)}') f'[DockerHandler] stopping container {str(container.name)}')
container.stop() container.stop()
logging.info(f'[DockerHandler] container {container.name} stopped') logging.info(f'[DockerHandler] container {container.name} stopped')
else: else:
logging.info('[DockerHandler] no container to stop') logging.info('[DockerHandler] no container to stop')
def start_container(self, container) -> None: def start_container(self, container) -> None:
if container: if container:
logging.info( logging.info(
f'[DockerHandler] starting container {container.name}') f'[DockerHandler] starting container {container.name}')
container.start() container.start()
logging.info(f'[DockerHandler] container {container.name} started') logging.info(f'[DockerHandler] container {container.name} started')
else: else:
logging.info('[DockerHandler] no container to start') logging.info('[DockerHandler] no container to start')
def get_container_by_service_name(self, service_name): def get_container_by_service_name(self, service_name):
logging.info( logging.info(
f'[DockerHandler] getting container by service name {service_name}...') f'[DockerHandler] getting container by service name {service_name}...')
try: try:
containers = self.client.containers.list( containers = self.client.containers.list(
all=True, filters={"network": self.current_network}) all=True, filters={"network": self.current_network})
if containers is None: if containers is None:
logging.info('[DockerHandler] no containers found in network') logging.info('[DockerHandler] no containers found in network')
return None return None
for container in containers: for container in containers:
networks = container.attrs['NetworkSettings']['Networks'] networks = container.attrs['NetworkSettings']['Networks']
if self.current_network in networks and service_name in networks[self.current_network]['Aliases']: if self.current_network in networks and service_name in networks[self.current_network]['Aliases']:
logging.info( logging.info(
f'[DockerHandler] found container {container.name} with service name {service_name} in network {self.current_network}') f'[DockerHandler] found container {container.name} with service name {service_name} in network {self.current_network}')
return container return container
logging.info( logging.info(
f'[DockerHandler] no docker container found with service name {service_name} in network {self.current_network}') f'[DockerHandler] no docker container found with service name {service_name} in network {self.current_network}')
return None return None
except docker.errors.APIError as e: except docker.errors.APIError as e:
logging.error(f'Error getting container list: {e}') logging.error(f'Error getting container list: {e}')
return None return None
def get_auto_starter_container_name(self) -> str | None: def get_auto_starter_container_name(self) -> str | None:
return os.environ.get('HOSTNAME') return os.environ.get('HOSTNAME')
def get_auto_starter_container(self): def get_auto_starter_container(self):
hostname = os.environ.get('HOSTNAME') hostname = os.environ.get('HOSTNAME')
if hostname: if hostname:
return self.client.containers.get(hostname) return self.client.containers.get(hostname)
return None return None
def get_current_network(self) -> Network: def get_current_network(self) -> Network:
current_container = self.get_auto_starter_container() current_container = self.get_auto_starter_container()
if current_container: if current_container:
networks = current_container.attrs['NetworkSettings']['Networks'] networks = current_container.attrs['NetworkSettings']['Networks']
return list(networks.keys())[0] return list(networks.keys())[0]
return None return None
def get_ip_by_service_name(self, service_name: str) -> str: def get_ip_by_service_name(self, service_name: str) -> str:
container = self.get_container_by_service_name(service_name) container = self.get_container_by_service_name(service_name)
if container: if container:
networks = container.attrs['NetworkSettings']['Networks'] networks = container.attrs['NetworkSettings']['Networks']
return networks[self.current_network]['IPAddress'] return networks[self.current_network]['IPAddress']
return "" return ""
def get_auto_starter_container_ip(self) -> str: def get_auto_starter_container_ip(self) -> str:
container = self.get_auto_starter_container() container = self.get_auto_starter_container()
if container: if container:
networks = container.attrs['NetworkSettings']['Networks'] networks = container.attrs['NetworkSettings']['Networks']
return networks[self.current_network]['IPAddress'] return networks[self.current_network]['IPAddress']
return "" return ""

View File

@@ -1,92 +1,92 @@
import os import os
import logging import logging
from typing import TextIO, Dict from typing import TextIO, Dict
from dockerHandler import DockerHandler from dockerHandler import DockerHandler
class NginxHandler: class NginxHandler:
def __init__(self, config_path: str): def __init__(self, config_path: str):
logging.info('[NginxHandler] initializing nginx handler...') logging.info('[NginxHandler] initializing nginx handler...')
self.config_path: str = config_path self.config_path: str = config_path
def start(self) -> None: def start(self) -> None:
logging.info('[NginxHandler] starting nginx...') logging.info('[NginxHandler] starting nginx...')
os.system('nginx > /dev/null 2>&1 &') os.system('nginx > /dev/null 2>&1 &')
logging.info('[NginxHandler] nginx started') logging.info('[NginxHandler] nginx started')
def stop(self) -> None: def stop(self) -> None:
logging.info('[NginxHandler] stopping nginx...') logging.info('[NginxHandler] stopping nginx...')
os.system('nginx -s stop') os.system('nginx -s stop')
logging.info('[NginxHandler] nginx stopped') logging.info('[NginxHandler] nginx stopped')
def restart(self) -> None: def restart(self) -> None:
self.stop() self.stop()
self.start() self.start()
def print_config(self) -> None: def print_config(self) -> None:
logging.info('[NginxHandler] printing nginx config file...') logging.info('[NginxHandler] printing nginx config file...')
logging.info('========================================') logging.info('========================================')
with open(self.config_path, 'r') as f: with open(self.config_path, 'r') as f:
logging.info(f.read()) logging.info(f.read())
logging.info('========================================') logging.info('========================================')
logging.info('[NginxHandler] nginx config file printed') logging.info('[NginxHandler] nginx config file printed')
def update_config_file(self, docker_handler: DockerHandler) -> None: def update_config_file(self, docker_handler: DockerHandler) -> None:
logging.info('[NginxHandler] updating nginx config file...') logging.info('[NginxHandler] updating nginx config file...')
self.stop() self.stop()
port_map: Dict[str, str] = docker_handler.get_port_map() port_map: Dict[str, str] = docker_handler.get_port_map()
if port_map is None: if port_map is None:
logging.error('[NginxHandler] port_map is None') logging.error('[NginxHandler] port_map is None')
return return
proxy_timeout: str = "5s" proxy_timeout: str = "5s"
logging.info('[NginxHandler] setting up NGINX config file...') logging.info('[NginxHandler] setting up NGINX config file...')
logging.info('[NginxHandler] port_map: {}'.format(port_map)) logging.info('[NginxHandler] port_map: {}'.format(port_map))
nginx_conf: TextIO = open(self.config_path, 'w+') nginx_conf: TextIO = open(self.config_path, 'w+')
nginx_conf.truncate() nginx_conf.truncate()
nginx_conf.write('worker_processes 5;\n') nginx_conf.write('worker_processes 5;\n')
nginx_conf.write('events { \n') nginx_conf.write('events { \n')
nginx_conf.write(' worker_connections 1024;\n') nginx_conf.write(' worker_connections 1024;\n')
nginx_conf.write(' multi_accept on;\n') nginx_conf.write(' multi_accept on;\n')
nginx_conf.write('}\n') nginx_conf.write('}\n')
nginx_conf.write('stream {\n') nginx_conf.write('stream {\n')
# This looks confusing, but the nginx.conf looks good when it's done # This looks confusing, but the nginx.conf looks good when it's done
# Example for the nginx-example.conf file is in the repo root directory # Example for the nginx-example.conf file is in the repo root directory
if isinstance(port_map, dict): if isinstance(port_map, dict):
for port in port_map: for port in port_map:
ip = docker_handler.get_ip_by_service_name(port_map[port]) ip = docker_handler.get_ip_by_service_name(port_map[port])
nginx_conf.write( nginx_conf.write(
f' # docker service {port_map[port]} on port {port}\n') f' # docker service {port_map[port]} on port {port}\n')
nginx_conf.write(f' upstream upstream_{port} {{\n') nginx_conf.write(f' upstream upstream_{port} {{\n')
if ip == "": if ip == "":
nginx_conf.write(f' server 127.0.0.1:{port};\n') nginx_conf.write(f' server 127.0.0.1:{port};\n')
else: else:
nginx_conf.write(f' server {ip}:25565;\n') nginx_conf.write(f' server {ip}:25565;\n')
nginx_conf.write( nginx_conf.write(
f' server 127.0.0.1:{port} backup;\n') f' server 127.0.0.1:{port} backup;\n')
nginx_conf.write(' }\n') nginx_conf.write(' }\n')
nginx_conf.write(' server {\n') nginx_conf.write(' server {\n')
nginx_conf.write( nginx_conf.write(
f' listen {docker_handler.get_auto_starter_container_ip()}:{port};\n') f' listen {docker_handler.get_auto_starter_container_ip()}:{port};\n')
nginx_conf.write( nginx_conf.write(
f' proxy_connect_timeout {proxy_timeout};\n') f' proxy_connect_timeout {proxy_timeout};\n')
nginx_conf.write(f' proxy_timeout {proxy_timeout};\n') nginx_conf.write(f' proxy_timeout {proxy_timeout};\n')
nginx_conf.write(f' proxy_pass upstream_{port};\n') nginx_conf.write(f' proxy_pass upstream_{port};\n')
nginx_conf.write(' }\n') nginx_conf.write(' }\n')
else: else:
logging.error('port_map is not a dictionary') logging.error('port_map is not a dictionary')
nginx_conf.write('}\n') nginx_conf.write('}\n')
nginx_conf.close() nginx_conf.close()
logging.info('[NginxHandler] nginx config file setup complete') logging.info('[NginxHandler] nginx config file setup complete')
self.start() self.start()
# Restart for good measure. Add inconsistency issues with nginx # Restart for good measure. Add inconsistency issues with nginx
self.restart() self.restart()

View File

@@ -1,150 +1,147 @@
import os import os
import socket import socket
import logging import logging
import threading import threading
from typing import Literal from typing import Literal
from dockerHandler import DockerHandler from dockerHandler import DockerHandler
from minecraftServerHandler import MinecraftServerHandler from minecraftServerHandler import MinecraftServerHandler
from objects.minecraftServer import MinecraftServer from objects.minecraftServer import MinecraftServer
class RequestHandler(threading.Thread): class RequestHandler(threading.Thread):
def __init__(self, port: str, docker_handler: DockerHandler, minecraft_server_handler: MinecraftServerHandler): def __init__(self, port: str, docker_handler: DockerHandler, minecraft_server_handler: MinecraftServerHandler):
logging.info( logging.info(
f'[RequestHandler:{port}] initializing request handler...') f'[RequestHandler:{port}] initializing request handler...')
super().__init__() super().__init__()
self.port: str = port self.port: str = port
if not self.port: if not self.port:
logging.info( logging.info(
f'[RequestHandler:{self.port}] no port specified') f'[RequestHandler:{self.port}] no port specified')
return return
self.docker_handler: DockerHandler = docker_handler self.docker_handler: DockerHandler = docker_handler
self.minecraft_server_handler: MinecraftServerHandler = minecraft_server_handler self.minecraft_server_handler: MinecraftServerHandler = minecraft_server_handler
self.sock: socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock: socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_address: tuple[Literal['localhost'], str] = ( server_address: tuple[Literal['localhost'], str] = (
'localhost', self.port) 'localhost', self.port)
logging.info( logging.info(
f'[RequestHandler:{self.port}] starting up on {server_address[0]} port {server_address[1]}') f'[RequestHandler:{self.port}] starting up on {server_address[0]} port {server_address[1]}')
self.sock.bind(server_address) self.sock.bind(server_address)
self.sock.listen(1) self.sock.settimeout(5)
logging.info( self.sock.listen(30)
f'[RequestHandler:{self.port}] request handler initialized') logging.info(
f'[RequestHandler:{self.port}] request handler initialized')
def restart(self):
logging.info( def restart(self):
f'[RequestHandler:{self.port}] restarting request handler for port {self.port}') logging.info(
self.sock.close() f'[RequestHandler:{self.port}] restarting request handler for port {self.port}')
self.__init__(self.port, self.docker_handler, self.sock.close()
self.minecraft_server_handler) self.__init__(self.port, self.docker_handler,
self.minecraft_server_handler)
def run(self) -> None:
while True: def run(self) -> None:
try: while True:
logging.info( try:
f'[RequestHandler:{self.port}] waiting for a connection on port {self.port}') logging.info(
self.connection, self.client_address = self.sock.accept() f'[RequestHandler:{self.port}] waiting for a connection on port {self.port}')
try: self.connection, self.client_address = self.sock.accept()
logging.info( try:
f'[RequestHandler:{self.port}] connection from {self.client_address}') logging.info(
self.handle_request() f'[RequestHandler:{self.port}] connection from {self.client_address}')
except Exception as e: self.handle_request()
logging.info( except Exception as e:
f'[RequestHandler:{self.port}] error in request handler for port {self.port}: {e}') logging.info(
logging.info( f'[RequestHandler:{self.port}] error in request handler for port {self.port}: {e}')
'[RequestHandler:{self.port}] restarting request handler...') logging.info(
self.restart() '[RequestHandler:{self.port}] restarting request handler...')
finally: self.restart()
self.connection.close() finally:
self.restart() self.connection.close()
except Exception as e: self.restart()
logging.info( except Exception as e:
f'[RequestHandler:{self.port}] error in request handler for port {self.port}: {e}') logging.info(
logging.info( f'[RequestHandler:{self.port}] error in request handler for port {self.port}: {e}')
'[RequestHandler:{self.port}] restarting request handler...') logging.info(
self.restart() '[RequestHandler:{self.port}] restarting request handler...')
self.restart()
def handle_request(self) -> None:
logging.info( def handle_request(self) -> None:
f'[RequestHandler:{self.port}] handling request on port {self.port}') logging.info(
f'[RequestHandler:{self.port}] handling request on port {self.port}')
service_name = self.docker_handler.get_port_map().get(str(self.port))
logging.info( service_name = self.docker_handler.get_port_map().get(str(self.port))
f'[RequestHandler:{self.port}] service name: {service_name}') logging.info(
f'[RequestHandler:{self.port}] service name: {service_name}')
if service_name:
minecraft_server: MinecraftServer = self.minecraft_server_handler.get_server( if service_name:
service_name) minecraft_server: MinecraftServer = self.minecraft_server_handler.get_server(
service_name)
if not minecraft_server:
logging.info( if not minecraft_server:
f'[RequestHandler:{self.port}] no minecraft server found for service name {service_name}') logging.info(
return f'[RequestHandler:{self.port}] no minecraft server found for service name {service_name}')
request = self.connection.recv(1024) return
logging.info( request = self.connection.recv(1024)
f'[RequestHandler:{self.port}] received request: {request}') logging.info(
# b'\x1b\x00\xfb\x05\x14mc.tealfire.de\x00FML3\x00c\xa0\x02\x1a\x00\x07DerTyp7\x01\xf2]\x9a\x18*\xeaJ\xed\xbe0g\x9c\x8aT\xa9t' f'[RequestHandler:{self.port}] received request: {request}')
if request[0] == 0x10 or request[0] == 0x15 or request[0] == 0x1b: # b'\x1b\x00\xfb\x05\x14mc.tealfire.de\x00FML3\x00c\xa0\x02\x1a\x00\x07DerTyp7\x01\xf2]\x9a\x18*\xeaJ\xed\xbe0g\x9c\x8aT\xa9t'
if b'\x02' in request: if request[0] == 0x10 or request[0] == 0x15 or request[0] == 0x1b:
logging.info( if b'\x02' in request:
f'[RequestHandler:{self.port}] detected join/login request for {service_name}') logging.info(
if minecraft_server.is_starting() == True: f'[RequestHandler:{self.port}] detected join/login request for {service_name}')
logging.info( if minecraft_server.is_starting() == True:
f'[RequestHandler:{self.port}] container {service_name} is already starting...') logging.info(
self.forward_request_to_placeholder( f'[RequestHandler:{self.port}] container {service_name} is already starting...')
request, minecraft_server) self.forward_request_to_placeholder(
else: request, minecraft_server)
logging.info( else:
f'[RequestHandler:{self.port}] starting container {service_name}') logging.info(
self.minecraft_server_handler.start_server( f'[RequestHandler:{self.port}] starting container {service_name}')
service_name) self.minecraft_server_handler.start_server(
elif b'\x01' in request: service_name)
logging.info( elif b'\x01' in request:
f'[RequestHandler:{self.port}] detected ping request for {service_name}') logging.info(
self.forward_request_to_placeholder( f'[RequestHandler:{self.port}] detected ping request for {service_name}')
request, minecraft_server) self.forward_request_to_placeholder(
request, minecraft_server)
elif request[0] == 0xFE:
logging.info( elif request[0] == 0xFE:
f'[RequestHandler:{self.port}] detected legacy ping request for {service_name}') logging.info(
self.forward_request_to_placeholder(request, minecraft_server) f'[RequestHandler:{self.port}] detected legacy ping request for {service_name}')
else: self.forward_request_to_placeholder(request, minecraft_server)
logging.info( else:
f'[RequestHandler:{self.port}] detected unknown request for {service_name}') logging.info(
self.forward_request_to_placeholder(request, minecraft_server) f'[RequestHandler:{self.port}] detected unknown request for {service_name}')
self.forward_request_to_placeholder(request, minecraft_server)
else:
logging.info( else:
f'[RequestHandler:{self.port}] no container mapped to port {self.port}') logging.info(
f'[RequestHandler:{self.port}] no container mapped to port {self.port}')
def forward_request_to_placeholder(self, request, minecraft_server: MinecraftServer) -> None:
logging.info( def forward_request_to_placeholder(self, request, minecraft_server: MinecraftServer) -> None:
'[RequestHandler:{self.port}] forwarding request to placeholder server') logging.info(
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: '[RequestHandler:{self.port}] forwarding request to placeholder server')
ip = self.docker_handler.get_ip_by_service_name( with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
os.environ.get('PLACEHOLDER_SERVER_SLEEPING_SERVICE')) ip = "127.0.0.1"
if minecraft_server.is_starting() == True: logging.info(
logging.info( f'[RequestHandler:{self.port}] placeholder server ip: {ip}')
'[RequestHandler:{self.port}] container is starting. Using starting placeholder ip') try:
ip = self.docker_handler.get_ip_by_service_name( if minecraft_server.is_starting() == True:
os.environ.get('PLACEHOLDER_SERVER_STARTING_SERVICE')) logging.info(
'[RequestHandler:{self.port}] container is starting. Using placeholder port 20001')
if not ip: server_socket.connect((ip, 20001))
logging.info( else:
'[RequestHandler:{self.port}] no placeholder server ip found') logging.info(
return '[RequestHandler:{self.port}] container is not starting. Using placeholder port 20000')
server_socket.connect((ip, 20000))
logging.info(
f'[RequestHandler:{self.port}] placeholder server ip: {ip}') server_socket.sendall(request)
try: response = server_socket.recv(1024)
server_socket.connect((ip, 25565)) self.connection.sendall(response)
server_socket.sendall(request) except Exception as e:
response = server_socket.recv(1024) logging.info(
self.connection.sendall(response) f'[RequestHandler:{self.port}] error while handling request on port {self.port}: {e}')
except Exception as e: self.restart()
logging.info(
f'[RequestHandler:{self.port}] error while handling request on port {self.port}: {e}')
self.restart()

View File

@@ -1,20 +1,50 @@
import logging import logging
import os import os
import socket import json
import json
from typing import List, Dict
def docker_container_mapping():
port_map_str = os.environ.get('PORT_MAP')
def docker_container_mapping() -> Dict[str, str]:
port_map = {} port_map_str = os.environ.get('PORT_MAP')
for line in port_map_str.split('\n'):
if line: port_map = {}
port, name = line.split(':') for line in port_map_str.split('\n'):
port_map[port.strip()] = name.strip().replace( if line:
"'", "").replace('"', "").strip() port, name = line.split(':')
port_map[port.strip()] = name.strip().replace(
# print port map for debugging "'", "").replace('"', "").strip()
logging.info('PORT_MAP:')
for port in port_map: # print port map for debugging
logging.info(f'{port} -> {port_map[port]}') logging.info('PORT_MAP:')
return port_map for port in port_map:
logging.info(f'{port} -> {port_map[port]}')
return port_map
# motd = {
# "1": "§4Maintenance!",
# "2": "§aCheck example.com for more information!"
# }
# version_text = "§4Maintenance"
# samples = ["§bexample.com", "", "§4Maintenance"]
# kick_message = ["§bSorry", "", "§aThis server is offline!"]
def generate_placeholder_server_config_file(path: str, ip: str, port: int, motd: Dict[str, str], version_text: str, samples: List[str], kick_message: List[str]) -> None:
config = {
"ip": ip,
"kick_message": kick_message,
"motd": motd,
"player_max": 0,
"player_online": 0,
"port": port,
"protocol": 2,
"samples": samples,
"server_icon": "server_icon.png",
"show_hostname_if_available": True,
"show_ip_if_hostname_available": True,
"version_text": version_text
}
with open(path, 'w') as f:
json.dump(config, f, indent=4)

View File

@@ -1,99 +1,46 @@
version: "3.9" version: "3.9"
services: services:
auto_starter: auto_starter:
container_name: mc_auto_starter container_name: mc_auto_starter
restart: no restart: no
image: dertyp7/minecraft_server_auto_starter:latest image: dertyp7/minecraft_server_auto_starter:latest
ports: ports:
- 25565:25565 - 25565:25565
- 25566:25566 - 25566:25566
environment: environment:
# The ip of the placeholder servers below # Port mapping for the servers
PLACEHOLDER_SERVER_SLEEPING_SERVICE: "mc_placeholder_server_sleeping" # The key is the external port of the placeholder server
PLACEHOLDER_SERVER_STARTING_SERVICE: "mc_placeholder_server_starting" # The value is the internal ip of the actual server
# Don't change the server port in the actual server. Use this instead
# Port mapping for the servers PORT_MAP: |
# The key is the external port of the placeholder server 25565: "mc"
# The value is the internal ip of the actual server 25566: "mc2"
# Don't change the server port in the actual server. Use this instead volumes:
PORT_MAP: | - /var/run/docker.sock:/var/run/docker.sock
25565: "mc"
25566: "mc2" # These are the actual servers
volumes: # For itzg/minecraft-server you can find the documentation here: https://docker-minecraft-server.readthedocs.io/en/latest/variables/
- /var/run/docker.sock:/var/run/docker.sock mc:
container_name: example_mc_server_1
# These are the placeholder servers. They are used to show the player a message image: itzg/minecraft-server
# They are not needed, but they are nice to have restart: unless-stopped #! This is important. If you restart the server always automatically, the auto_starter will not work
# Keep in mind these servers are consuming some resources environment:
mc_placeholder_server_sleeping: type: "PAPER"
container_name: mc_placeholder_server_sleeping EULA: "TRUE"
restart: always MOTD: "Example Server 1"
image: itzg/minecraft-server:java8 MAX_PLAYERS: "1"
environment: #! Dont change SERVER_PORT. Use PORT_IP_MAP in auto_starter instead.
VERSION: "1.12.2" # SERVER_PORT default is "25565"
EULA: "TRUE"
MOTD: "Sleeping | Join to wake up" mc2:
container_name: example_mc_server_2
# The placeholder servers should be as lightweight as possible image: itzg/minecraft-server
MAX_PLAYERS: "0" restart: unless-stopped #! This is important. If you restart the server always automatically, the auto_starter will not work
MAX_MEMORY: "512M" environment:
INIT_MEMORY: "512M" type: "PAPER"
LEVEL_TYPE: "FLAT" EULA: "TRUE"
JVM_XX_OPTS: "-XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCompressedOops" MOTD: "Example Server 2"
VIEW_DISTANCE: "1" MAX_PLAYERS: "1"
SPAWN_ANIMALS: "false" #! Dont change SERVER_PORT. Use PORT_IP_MAP in auto_starter instead.
SPAWN_MONSTERS: "false" # SERVER_PORT default is "25565"
SNOOPER_ENABLED: "false"
GENERATE_STRUCTURES: "false"
ALLOW_NETHER: "false"
ALLOW_END: "false"
mc_placeholder_server_starting:
container_name: mc_placeholder_server_starting
restart: always
image: itzg/minecraft-server:java8
environment:
VERSION: "1.12.2"
EULA: "TRUE"
MOTD: "Starting, please wait..."
# The placeholder servers should be as lightweight as possible
MAX_PLAYERS: "0"
MAX_MEMORY: "512M"
INIT_MEMORY: "512M"
LEVEL_TYPE: "FLAT"
JVM_XX_OPTS: "-XX:+UseConcMarkSweepGC -XX:+DisableExplicitGC -XX:+UseCompressedOops"
VIEW_DISTANCE: "1"
SPAWN_ANIMALS: "false"
SPAWN_MONSTERS: "false"
SNOOPER_ENABLED: "false"
GENERATE_STRUCTURES: "false"
ALLOW_NETHER: "false"
ALLOW_END: "false"
# These are the actual servers
# For itzg/minecraft-server you can find the documentation here: https://docker-minecraft-server.readthedocs.io/en/latest/variables/
mc:
container_name: example_mc_server_1
image: itzg/minecraft-server
restart: unless-stopped #! This is important. If you restart the server always automatically, the auto_starter will not work
environment:
type: "PAPER"
EULA: "TRUE"
MOTD: "Example Server 1"
MAX_PLAYERS: "1"
#! Dont change SERVER_PORT. Use PORT_IP_MAP in auto_starter instead.
# SERVER_PORT default is "25565"
mc2:
container_name: example_mc_server_2
image: itzg/minecraft-server
restart: unless-stopped #! This is important. If you restart the server always automatically, the auto_starter will not work
environment:
type: "PAPER"
EULA: "TRUE"
MOTD: "Example Server 2"
MAX_PLAYERS: "1"
#! Dont change SERVER_PORT. Use PORT_IP_MAP in auto_starter instead.
# SERVER_PORT default is "25565"

View File

@@ -1,27 +1,27 @@
worker_processes 5; worker_processes 5;
events { events {
worker_connections 1024; worker_connections 1024;
multi_accept on; multi_accept on;
} }
stream { stream {
upstream upstream_25565 { upstream upstream_25565 {
server 192.168.144.5:25565; server 192.168.144.5:25565;
server 127.0.0.1:25565 backup; server 127.0.0.1:25565 backup;
} }
server { server {
listen 192.168.144.6:25565; listen 192.168.144.6:25565;
proxy_connect_timeout 5s; proxy_connect_timeout 5s;
proxy_timeout 5s; proxy_timeout 5s;
proxy_pass upstream_25565; proxy_pass upstream_25565;
} }
upstream upstream_25566 { upstream upstream_25566 {
server 192.168.144.3:25565; server 192.168.144.3:25565;
server 127.0.0.1:25566 backup; server 127.0.0.1:25566 backup;
} }
server { server {
listen 192.168.144.6:25566; listen 192.168.144.6:25566;
proxy_connect_timeout 5s; proxy_connect_timeout 5s;
proxy_timeout 5s; proxy_timeout 5s;
proxy_pass upstream_25566; proxy_pass upstream_25566;
} }
} }

View File

@@ -1,17 +1,17 @@
# Minecraft Server Auto Starter for Docker Compose # Minecraft Server Auto Starter for Docker Compose
This container will manage the access to your Minecraft server. It will start the Minecraft server when a player tries to connect. This container will manage the access to your Minecraft server. It will start the Minecraft server when a player tries to connect.
This container is designed to work with the [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) container. This container is designed to work with the [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) container.
It uses the AutoStop feature of the [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) container to stop the Minecraft server when no player is connected. It uses the AutoStop feature of the [itzg/minecraft-server](https://hub.docker.com/r/itzg/minecraft-server) container to stop the Minecraft server when no player is connected.
## Usage ## Usage
See the [docker-compose.yml](https://github.com/DerTyp7/docker_minecraft_server_auto_starter/blob/main/docker-compose.yml) file for an example. See the [docker-compose.yml](https://github.com/DerTyp7/docker_minecraft_server_auto_starter/blob/main/docker-compose.yml) file for an example.
## Environment Variables ## Environment Variables
| Variable | Description | Default | Example | | Variable | Description | Default | Example |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------- | -------------- | | -------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------- | -------------- |
| `PLACEHOLDER_SERVER_SLEEPING_IP` | (optional) The internal docker-compose IP for the placeholder server when a server is sleeping | `""` | `"172.20.0.3"` | | `PLACEHOLDER_SERVER_SLEEPING_IP` | (optional) The internal docker-compose IP for the placeholder server when a server is sleeping | `""` | `"172.20.0.3"` |
| `PLACEHOLDER_SERVER_STARTING_IP` | (optional) The internal docker-compose IP for the placeholder server when a server is starting | `""` | `"172.20.0.4"` | | `PLACEHOLDER_SERVER_STARTING_IP` | (optional) The internal docker-compose IP for the placeholder server when a server is starting | `""` | `"172.20.0.4"` |
| `PORT_IP_MAP` | Map which matches the external Minecraft ports to the internal docker-compose IPs for the Minecraft-Servers | | ![image](https://github.com/DerTyp7/docker_minecraft_server_auto_starter/assets/76851529/4319a42c-7fc4-4be6-8e9d-710475dfde9a)| | `PORT_IP_MAP` | Map which matches the external Minecraft ports to the internal docker-compose IPs for the Minecraft-Servers | | ![image](https://github.com/DerTyp7/docker_minecraft_server_auto_starter/assets/76851529/4319a42c-7fc4-4be6-8e9d-710475dfde9a)|