Update Docker configuration and Nginx setup

This commit is contained in:
Janis
2023-12-10 22:01:37 +01:00
parent 92054a114d
commit 2908389772
8 changed files with 178 additions and 79 deletions

View File

@@ -1,7 +1,7 @@
# Use the official Nginx base image # Use the official Nginx base image
FROM nginx:1.25.3 FROM nginx:1.25.3
ENV PORT_IP_MAP "" ENV PORT_MAP ""
ENV PYTHONUNBUFFERED=1 ENV PYTHONUNBUFFERED=1
ENV PLACEHOLDER_SERVER_SLEEPING_IP "" ENV PLACEHOLDER_SERVER_SLEEPING_IP ""
ENV PLACEHOLDER_SERVER_STARTING_IP "" ENV PLACEHOLDER_SERVER_STARTING_IP ""

View File

@@ -1,4 +1,5 @@
import logging import logging
from requestHandler import RequestHandler from requestHandler import RequestHandler
from utils import docker_container_mapping from utils import docker_container_mapping
from dockerHandler import DockerHandler from dockerHandler import DockerHandler
@@ -9,24 +10,45 @@ logging.basicConfig(level=logging.INFO)
def main(): def main():
try: try:
port_ip_map = docker_container_mapping() port_map = docker_container_mapping()
# Create a DockerHandler instance # Create a DockerHandler instance
docker_handler = DockerHandler( docker_handler = DockerHandler(
'unix://var/run/docker.sock', port_ip_map) 'unix://var/run/docker.sock', port_map)
# Create an NginxHandler instance # Create an NginxHandler instance
nginx_handler = NginxHandler('/etc/nginx/nginx.conf') nginx_handler = NginxHandler('/etc/nginx/nginx.conf')
print(1)
docker_handler.get_ip_by_dns_name(
docker_handler.get_current_container_name())
print(2)
nginx_handler.setup_config_file( nginx_handler.setup_config_file(
docker_container_mapping(), docker_handler.get_current_container_ip()) port_map, docker_handler.get_ip_by_dns_name(docker_handler.get_current_container_name()), docker_handler)
print(3)
nginx_handler.print_config() nginx_handler.print_config()
# Create a RequestHandler instance for each port # Create a RequestHandler instance for each port
for port in port_ip_map.keys(): for port in port_map.keys():
logging.info(f'Creating request handler for port {port}') logging.info(f'Creating request handler for port {port}')
request_handler = RequestHandler(int(port), docker_handler) request_handler = RequestHandler(int(port), docker_handler)
request_handler.start() request_handler.start()
# DEBUG
logging.info(
'-----------------------------DEBUG--------------------------------')
logging.info(
f'Current container: {docker_handler.get_current_container_name()}')
logging.info(
f'Current container ip: {docker_handler.get_ip_by_dns_name(docker_handler.get_current_container_name())}')
logging.info(
f'Current network: {docker_handler.get_current_network()}')
docker_handler.print_all_container_names()
logging.info(
'-----------------------------DEBUG END--------------------------------')
except Exception as e: except Exception as e:
logging.error(f'An error occurred: {e}') logging.error(f'An error occurred: {e}')

View File

@@ -1,16 +1,20 @@
from math import log
import socket
import docker import docker
import os import os
import logging import logging
class DockerHandler: class DockerHandler:
def __init__(self, base_url, port_ip_map): def __init__(self, base_url, port_map):
logging.info( logging.info(
f'Initializing docker handler with base url {base_url} and port ip map: {port_ip_map}') f'Initializing docker handler with base url {base_url} and port ip map: {port_map}')
self.base_url = base_url self.base_url = base_url
self.client = docker.DockerClient(base_url=base_url) self.client = docker.DockerClient(base_url=base_url)
self.port_ip_map = port_ip_map self.port_map = port_map
self.current_network = self.get_current_network() self.current_network = self.get_current_network()
logging.info('Docker handler initialized')
logging.info(f'Current network: {self.current_network}')
def get_current_container(self): def get_current_container(self):
current_container_name = os.environ.get('HOSTNAME') current_container_name = os.environ.get('HOSTNAME')
@@ -20,13 +24,15 @@ class DockerHandler:
logging.error(f'Container {current_container_name} not found') logging.error(f'Container {current_container_name} not found')
return None return None
def get_current_container_ip(self): def get_current_container_name(self):
# Get IP of current container # Get DNS name of current container
current_container = self.get_current_container() current_container = self.get_current_container()
if current_container: if current_container:
networks = current_container.attrs['NetworkSettings']['Networks'] networks = current_container.attrs['NetworkSettings']['Networks']
current_network = list(networks.keys())[0] current_network = list(networks.keys())[0]
return networks[current_network]['IPAddress'] if 'Aliases' in networks[current_network]:
dns_name = networks[current_network]['Aliases'][0]
return dns_name
return None return None
def get_current_network(self): def get_current_network(self):
@@ -54,7 +60,67 @@ class DockerHandler:
f'No docker container found with ip {ip} in network {self.current_network}') f'No docker container found with ip {ip} in network {self.current_network}')
return None return None
def get_container_by_name(self, name):
try:
return self.client.containers.get(name)
except docker.errors.NotFound:
logging.error(f'Container {name} not found')
return None
def is_container_starting(self, container): def is_container_starting(self, container):
if container: if container:
return container.attrs['State']['Health']['Status'] == 'starting' return container.attrs['State']['Health']['Status'] == 'starting'
return False return False
def print_all_container_names(self):
try:
containers = self.client.containers.list(
all=True, filters={"network": self.current_network})
if containers is None:
logging.info('No containers found')
return None
for container in containers:
logging.info(f'Container name: {container.name}')
# get docker compose dns name
networks = container.attrs['NetworkSettings']['Networks']
if self.current_network in networks:
logging.info(
f'Container ip: {networks[self.current_network]["IPAMConfig"]["IPv4Address"]}')
else:
logging.info(f'Container ip: None')
except docker.errors.APIError as e:
logging.error(f'Error getting container list: {e}')
return None
def get_ip_by_dns_name(self, dns_name):
# dEBUG Print all containers with their network ip and dns name
for container in self.client.containers.list(all=True):
networks = container.attrs['NetworkSettings']['Networks']
if self.current_network in networks:
logging.info(
f'Container {container.name} ip: {networks[self.current_network]["IPAMConfig"]["IPv4Address"]}, dns name: {networks[self.current_network]["Aliases"]}')
try:
containers = self.client.containers.list(
all=True, filters={"network": self.current_network})
if containers is None:
logging.info('No containers found')
return None
for container in containers:
networks = container.attrs['NetworkSettings']['Networks']
if self.current_network in networks and dns_name in networks[self.current_network]['Aliases']:
logging.info(
f'Found container {container.name} with dns name {dns_name} in network {self.current_network}')
return networks[self.current_network]['IPAMConfig']['IPv4Address']
logging.info(
f'No docker container found with dns name {dns_name} in network {self.current_network}')
return None
except docker.errors.APIError as e:
logging.error(f'Error getting container list: {e}')
return None

View File

@@ -24,11 +24,16 @@ class NginxHandler:
with open(self.config_path, 'r') as f: with open(self.config_path, 'r') as f:
logging.info(f.read()) logging.info(f.read())
def setup_config_file(self, port_ip_map, current_container_ip): def setup_config_file(self, port_map, current_container_ip, docker_handler):
print(f'-------------------> {port_map}')
if port_map is None:
logging.error('port_map is None')
return
proxy_timeout = "5s" proxy_timeout = "5s"
self.stop() self.stop()
logging.info('Setting up NGINX config file...') logging.info('Setting up NGINX config file...')
logging.info('port_ip_map: {}'.format(port_ip_map)) logging.info('port_map: {}'.format(port_map))
nginx_conf = open(self.config_path, 'w+') nginx_conf = 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')
@@ -40,9 +45,14 @@ class NginxHandler:
# 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
for port in port_ip_map: if isinstance(port_map, dict):
for port in port_map:
print(f'-------------------> {port_map}')
ip = docker_handler.get_ip_by_dns_name(port_map[port])
print(f'-------------------> {port_map[port]}, {ip}')
nginx_conf.write(f' upstream upstream_{port} {{\n') nginx_conf.write(f' upstream upstream_{port} {{\n')
nginx_conf.write(f' server {port_ip_map[port]}:25565;\n') nginx_conf.write(f' server {ip}:25565;\n')
nginx_conf.write(f' server 127.0.0.1:{port} backup;\n') nginx_conf.write(f' server 127.0.0.1:{port} backup;\n')
nginx_conf.write(' }\n') nginx_conf.write(' }\n')
@@ -56,6 +66,9 @@ class NginxHandler:
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:
logging.error('port_map is not a dictionary')
nginx_conf.write('}\n') nginx_conf.write('}\n')
nginx_conf.close() nginx_conf.close()
logging.info('NGINX config file setup complete') logging.info('NGINX config file setup complete')

View File

@@ -47,7 +47,8 @@ class RequestHandler(threading.Thread):
def handle_request(self): def handle_request(self):
logging.info(f'Handling request on port {self.port}') logging.info(f'Handling request on port {self.port}')
container_ip = docker_container_mapping().get(str(self.port)) container_ip = self.docker_handler.get_ip_by_dns_name(
docker_container_mapping().get(str(self.port)))
if container_ip: if container_ip:
container = self.docker_handler.get_container_by_ip( container = self.docker_handler.get_container_by_ip(
container_ip) container_ip)
@@ -85,11 +86,13 @@ class RequestHandler(threading.Thread):
def forward_request_to_placeholder(self, request, isStarting=False): def forward_request_to_placeholder(self, request, isStarting=False):
logging.info('Forwarding request to placeholder server') logging.info('Forwarding request to placeholder server')
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket:
ip = os.environ.get('PLACEHOLDER_SERVER_SLEEPING_IP') ip = self.docker_handler.get_ip_by_dns_name(
os.environ.get('PLACEHOLDER_SERVER_SLEEPING_SERVICE'))
if isStarting: if isStarting:
logging.info( logging.info(
'Container is starting. Using starting placeholder IP') 'Container is starting. Using starting placeholder IP')
ip = os.environ.get('PLACEHOLDER_SERVER_STARTING_IP') ip = self.docker_handler.get_ip_by_dns_name(
os.environ.get('PLACEHOLDER_SERVER_STARTING_SERVICE'))
if not ip: if not ip:
logging.info('No placeholder server IP found') logging.info('No placeholder server IP found')

View File

@@ -1,8 +1,11 @@
import logging
import os import os
import socket
import socket
def docker_container_mapping(): def docker_container_mapping():
port_ip_map_str = os.environ.get('PORT_IP_MAP') port_ip_map_str = os.environ.get('PORT_MAP')
# Convert the environment variable to a Python dictionary # Convert the environment variable to a Python dictionary
port_ip_map = {} port_ip_map = {}
for line in port_ip_map_str.split('\n'): for line in port_ip_map_str.split('\n'):
@@ -11,3 +14,11 @@ def docker_container_mapping():
port_ip_map[port.strip()] = ip.strip() port_ip_map[port.strip()] = ip.strip()
return port_ip_map return port_ip_map
def get_ip_by_dns_name(dns_name):
try:
return socket.gethostbyname(dns_name, resolver='127.0.0.1')
except socket.gaierror:
logging.error(f'Could not resolve dns name {dns_name}')
return None

View File

@@ -1,14 +1,5 @@
version: "3.9" version: "3.9"
networks:
# You need to have a network with static ips for the servers
mc_network:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.20.0.0/16
services: services:
auto_starter: auto_starter:
container_name: mc_auto_starter container_name: mc_auto_starter
@@ -19,19 +10,16 @@ services:
- 25566:25566 - 25566:25566
environment: environment:
# The ip of the placeholder servers below # The ip of the placeholder servers below
PLACEHOLDER_SERVER_SLEEPING_IP: "172.20.0.3" PLACEHOLDER_SERVER_SLEEPING_SERVICE: "mc_placeholder_server_sleeping"
PLACEHOLDER_SERVER_STARTING_IP: "172.20.0.4" PLACEHOLDER_SERVER_STARTING_SERVICE: "mc_placeholder_server_starting"
# Port mapping for the servers # Port mapping for the servers
# The key is the external port of the placeholder server # The key is the external port of the placeholder server
# The value is the internal ip of the actual server # The value is the internal ip of the actual server
# Don't change the server port in the actual server. Use this instead # Don't change the server port in the actual server. Use this instead
PORT_IP_MAP: | PORT_MAP: |
25565: 172.20.0.5 25565: "mc"
25566: 172.20.0.6 25566: "mc2"
networks:
mc_network:
ipv4_address: 172.20.0.2
volumes: volumes:
- /var/run/docker.sock:/var/run/docker.sock - /var/run/docker.sock:/var/run/docker.sock
@@ -60,15 +48,37 @@ services:
GENERATE_STRUCTURES: "false" GENERATE_STRUCTURES: "false"
ALLOW_NETHER: "false" ALLOW_NETHER: "false"
ALLOW_END: "false" ALLOW_END: "false"
networks:
mc_network:
ipv4_address: 172.20.0.3
mc_placeholder_server_starting: mc_placeholder_server_starting:
container_name: mc_placeholder_server_starting container_name: mc_placeholder_server_starting
restart: always restart: always
image: itzg/minecraft-server:java8 image: itzg/minecraft-server:java8
environment: environment:
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: no #! This is important. If you restart the server automatically, the auto_starter will not work
environment:
type: "PAPER"
VERSION: "1.12.2" VERSION: "1.12.2"
EULA: "TRUE" EULA: "TRUE"
MOTD: "Starting, please wait..." MOTD: "Starting, please wait..."
@@ -86,31 +96,7 @@ services:
GENERATE_STRUCTURES: "false" GENERATE_STRUCTURES: "false"
ALLOW_NETHER: "false" ALLOW_NETHER: "false"
ALLOW_END: "false" ALLOW_END: "false"
networks:
mc_network:
ipv4_address: 172.20.0.4
# 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: no #! This is important. If you restart the server automatically, the auto_starter will not work
environment:
type: "PAPER"
EULA: "TRUE"
MOTD: "Example Server 1"
MAX_PLAYERS: "1"
# Enable autostop, so the auto_starter makes sense
ENABLE_AUTOSTOP: "TRUE"
AUTOSTOP_TIMEOUT_EST: "10"
AUTOSTOP_TIMEOUT_INIT: "10"
#! 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.5
mc2: mc2:
container_name: example_mc_server_2 container_name: example_mc_server_2
image: itzg/minecraft-server image: itzg/minecraft-server
@@ -122,11 +108,8 @@ services:
MAX_PLAYERS: "1" MAX_PLAYERS: "1"
# Enable autostop, so the auto_starter makes sense # Enable autostop, so the auto_starter makes sense
ENABLE_AUTOSTOP: "TRUE" ENABLE_AUTOSTOP: "FALSE"
AUTOSTOP_TIMEOUT_EST: "10" AUTOSTOP_TIMEOUT_EST: "10"
AUTOSTOP_TIMEOUT_INIT: "10" AUTOSTOP_TIMEOUT_INIT: "10"
#! Dont change SERVER_PORT. Use PORT_IP_MAP in auto_starter instead. #! Dont change SERVER_PORT. Use PORT_IP_MAP in auto_starter instead.
# SERVER_PORT default is "25565" # SERVER_PORT default is "25565"
networks:
mc_network:
ipv4_address: 172.20.0.6

View File

@@ -1 +1,2 @@
docker compose up -d docker compose down
docker compose up -d --remove-orphans