Migrer un serveur EmuLinker Kaillera vers Docker Compose avec network_mode host

Published by David on






EmuLinker en Docker — migration serveur Kaillera vers Docker Compose

EmuLinker en Docker — migration serveur Kaillera vers Docker Compose

Neuvième étape : migrer EmuLinker (serveur Kaillera pour le jeu rétro en ligne)
depuis une installation Java standalone vers Docker Compose, avec conservation de la
configuration et des sessions.

Paramètres à personnaliser

Remplacez chaque {{VARIABLE}} par vos propres valeurs avant d’utiliser ce guide.

Variable Description Exemple
{{IP_ANCIEN}} IP du serveur source 203.0.113.10
{{IP_NOUVEAU}} IP du serveur cible 198.51.100.20
{{HOSTNAME_ANCIEN}} Hostname de l’ancien serveur ns1234567
{{HOSTNAME_NOUVEAU}} Hostname du nouveau serveur ns7654321
{{PORT_SSH_ANCIEN}} Port SSH de l’ancien serveur 22222
{{PORT_SSH_NOUVEAU}} Port SSH du nouveau serveur 22223
{{DOMAINE_GAMING}} Domaine gaming exemple-gaming.fr
{{NOM_SERVEUR_KAILLERA}} Nom affiché dans la master list Mon Serveur Kaillera - Europe
{{TAILLE_DONNEES_PRECEDENT}} Taille données article précédent 50 Go

Contexte

EmuLinker est un serveur Kaillera pour le jeu rétro en ligne. Le protocole Kaillera
permet aux joueurs de jouer à des jeux multijoueur locaux via Internet en utilisant
des émulateurs comme MAME, Project64, ePSXe, ZSNES, Snes9k, et bien d’autres.
Contrairement à d’autres protocoles de jeu en ligne, Kaillera synchronise les entrées
des joueurs en temps réel pour simuler une session de jeu local.

Actuellement, EmuLinker (version originale, classe org.emulinker.kaillera.pico.PicoStarter)
tourne comme un processus Java 11 (OpenJDK 11.0.30) sur l’ancien serveur
dédié ({{IP_ANCIEN}}, {{HOSTNAME_ANCIEN}}) dans /home/emulinker/,
lancé via systemd (emulinker.service).
On migre vers Docker Compose sur le nouveau serveur ({{IP_NOUVEAU}}, {{HOSTNAME_NOUVEAU}},
SSH port {{PORT_SSH_NOUVEAU}}), où tournent déjà Odoo 17, Odoo 8, Akeneo PIM, ownCloud et TeamSpeak 3.

Architecture cible : 1 conteneur Docker (OpenJDK + jar EmuLinker) en mode
network_mode: host pour optimiser la latence UDP.
Les serveurs de jeux Kaillera utilisent plus de 70 ports UDP dynamiques (27886-27944 + 54715-54730).
Le NAT Docker ajoute de la latence sur chaque paquet UDP, ce qui dégrade l’expérience de jeu.
En mode host, le conteneur partage directement le réseau de l’hôte, éliminant cette surcharge.
Domaine : {{DOMAINE_GAMING}}.

1) Identifier l’installation existante

Première étape : se connecter à l’ancien serveur et identifier la version d’EmuLinker,
la version Java, la structure des fichiers, et le mode de lancement.

ssh -p {{PORT_SSH_ANCIEN}} root@{{IP_ANCIEN}}

# Lister le contenu du répertoire EmuLinker
ls -la /home/emulinker/

# Identifier la version Java utilisée
java -version

# Trouver le jar EmuLinker
find /home/emulinker/ -name "*.jar" -type f

# Vérifier le service systemd (si existant)
systemctl status emulinker 2>/dev/null || systemctl list-units | grep -i emu

# Vérifier si lancé via screen
screen -ls

# Vérifier les ports écoutés
ss -tulnp | grep -E '27888|27886'

# Lire la configuration
cat /home/emulinker/conf/emulinker.cfg 2>/dev/null || \
  find /home/emulinker/ -name "*.cfg" -o -name "*.conf" -o -name "*.properties" | head -20

Notre installation : c’est l’EmuLinker original (pas un fork K, SF ou X).
Le serveur est lancé avec un classpath complexe incluant 13 jars de dépendances
dans lib/, et la configuration est dans emulinker.cfg à la racine
(pas dans conf/). Le répertoire conf/ est inclus dans le classpath
pour access.cfg et la configuration log4j.

1.1 Résumé de l’installation

Résumé de notre installation :

  • Version : EmuLinker original (org.emulinker.kaillera.pico.PicoStarter)
  • Runtime : OpenJDK 11.0.30 (image Docker : eclipse-temurin:11-jre)
  • JVM : -Xms64m -Xmx128m
  • Configuration principale : emulinker.cfg (à la racine, pas dans conf/)
  • Configuration accès : conf/access.cfg
  • Lancement : classpath complexe avec 13 jars dans lib/
  • Service : emulinker.service (systemd, enabled)
  • Port principal : 27888 (Kaillera standard)
  • Ports jeux : 27886-27944 (UDP dynamique)
  • Ports P2P : 54715-54730 (UDP peer-to-peer)
  • Domaine : {{DOMAINE_GAMING}}
  • Nom serveur : {{NOM_SERVEUR_KAILLERA}}
  • Utilisateur système : emulinker
  • Répertoire : /home/emulinker/
  • Max joueurs : 100

1.2 Fichiers critiques à préserver

  • lib/emulinker.jar + 13 jars de dépendances dans lib/
    (commons-collections, commons-configuration, commons-lang, commons-logging,
    dom4j, log4j, picocontainer, etc.)
  • emulinker.cfg — configuration principale du serveur (nom, ports,
    master list, limites de connexion, messages)
  • conf/access.cfg — règles d’accès et permissions
  • conf/log4j.xml — configuration des logs (si présent dans le classpath)
  • wordlist.txt, questions.txt — listes pour les mini-jeux intégrés
  • scores.txt, scores_scrambler.txt — scores des joueurs
  • launch.sh — script de lancement (pour référence, remplacé par Docker)

2) Créer la structure des répertoires Docker

Sur le nouveau serveur :

mkdir -p /home/docker/emulinker/server
mkdir -p /home/docker/emulinker/backup
  • server/ — contiendra tous les fichiers EmuLinker (jar, configuration, logs)
  • backup/ — pour stocker les sauvegardes avant migration

3) Transférer les fichiers depuis l’ancien serveur

Depuis le nouveau serveur :

# Copier tout le répertoire EmuLinker
rsync -avz --progress -e "ssh -p {{PORT_SSH_ANCIEN}}" \
  root@{{IP_ANCIEN}}:/home/emulinker/ \
  /home/docker/emulinker/server/

EmuLinker est extrêmement léger comparé à TeamSpeak (quelques Mo contre {{TAILLE_DONNEES_PRECEDENT}}).
Le transfert devrait prendre quelques secondes seulement.

4) Créer le docker-compose.yml

nano /home/docker/emulinker/docker-compose.yml

Contenu :

services:
  emulinker:
    image: eclipse-temurin:11-jre
    container_name: emulinker-server
    restart: always
    network_mode: host
    working_dir: /opt/emulinker
    command:
      - java
      - -Xms64m
      - -Xmx128m
      - -cp
      - "./conf:./lib/*"
      - org.emulinker.kaillera.pico.PicoStarter
    volumes:
      - ./server:/opt/emulinker

Points clés :

  • eclipse-temurin:11-jre — correspond à l’OpenJDK 11 utilisé sur l’ancien serveur
  • network_mode: host — le conteneur partage directement le réseau de l’hôte,
    pas de NAT Docker. Optimal pour les serveurs de jeux avec beaucoup de ports UDP
  • command — reproduit le classpath complexe du launch.sh original :
    le répertoire conf/ + tous les jars dans lib/ via le wildcard ./lib/*
  • ./server:/opt/emulinker — tout le répertoire EmuLinker monté dans le conteneur

Classpath Java : EmuLinker ne se lance pas avec un simple java -jar.
Il nécessite un classpath qui inclut conf/ (pour access.cfg et log4j)
et les 13 jars de lib/. Le wildcard ./lib/* fonctionne avec Java 6+
et évite de lister chaque jar individuellement (comme le faisait le launch.sh original).
La classe principale est org.emulinker.kaillera.pico.PicoStarter.

Mode réseau host : network_mode: host signifie que le conteneur
partage directement le réseau de l’hôte. Pas besoin de mapper les ports individuellement
— tous les ports sur lesquels EmuLinker écoute sont directement accessibles.
C’est le choix optimal pour les serveurs de jeux avec beaucoup de ports UDP
(27886-27944 + 54715-54730 = plus de 70 ports), car le NAT Docker ajoute de la latence
sur les paquets UDP, ce qui dégrade l’expérience de jeu.

5) Adapter la configuration EmuLinker

Après le transfert, certains paramètres de configuration doivent être mis à jour
pour refléter le nouveau serveur.

# La configuration principale est à la racine (pas dans conf/)
nano /home/docker/emulinker/server/emulinker.cfg

Modification obligatoire — adresse IP du serveur :

# Remplacer l'ancienne IP par la nouvelle
# Ligne actuelle :
masterList.serverConnectAddress={{IP_ANCIEN}}

# Remplacer par :
masterList.serverConnectAddress={{IP_NOUVEAU}}

Critique : si masterList.serverConnectAddress n’est pas mis à jour,
les serveurs master list Kaillera continueront de pointer les joueurs vers l’ancienne IP.
Le serveur apparaîtra dans la liste publique mais les connexions échoueront.

Autres paramètres à vérifier (normalement OK sans modification) :

  • masterList.serverName={{NOM_SERVEUR_KAILLERA}} — nom affiché dans la liste publique
  • masterList.serverWebsite=http://{{DOMAINE_GAMING}} — site web du serveur
  • server.port=27888 — port principal Kaillera (ne pas changer)
  • server.maxUsers=100 — limite de joueurs simultanés
  • server.maxGames=50 — limite de parties simultanées

Configuration accès : le fichier conf/access.cfg contient les règles
d’accès (bans, admins, etc.). Il est transféré automatiquement avec le rsync de l’étape 3
et ne nécessite normalement aucune modification.

Serveurs master list Kaillera : les IPs 216.189.101.117 et
50.203.71.60 sont les serveurs master list Kaillera historiques
(anti3d.com et kaillera.com). Ils permettent aux joueurs de trouver votre serveur
dans la liste publique des clients Kaillera. Si votre serveur est privé
(connexion directe par IP/domaine), ces règles firewall ne sont pas strictement nécessaires.

6) Vérifier les règles firewall

Les règles firewall sont déjà configurées sur le nouveau serveur via MyVestaCP.
Vérifions qu’elles sont bien en place :

# Vérifier les règles iptables
iptables -L INPUT -n | grep -E '27888|27886|27944|54715|80'

Résultat attendu :

ACCEPT     tcp  --  216.189.101.117  0.0.0.0/0    tcp dpt:80
ACCEPT     tcp  --  50.203.71.60     0.0.0.0/0    tcp dpt:80
ACCEPT     udp  --  0.0.0.0/0        0.0.0.0/0    udp dpts:27888:27944
ACCEPT     udp  --  0.0.0.0/0        0.0.0.0/0    udp dpts:27886:27887
ACCEPT     udp  --  0.0.0.0/0        0.0.0.0/0    udp dpts:54715:54730

Explication des règles :

  • Emulinker-Master-List / Master-List-2 — TCP port 80 restreint aux IPs
    des serveurs master list Kaillera (216.189.101.117 et 50.203.71.60). Permet à ces serveurs
    de vérifier que notre serveur est en ligne et de l’afficher dans la liste publique
  • Emulinker-Classic-Kaillera — UDP 27888 (port principal) + 27889-27944
    (ports de jeu dynamiques) — ouverts à tous pour que les joueurs puissent se connecter
  • Emulinker-p2p — UDP 27886-27887 + 54715-54730 — ports pour le mode
    peer-to-peer, permettant le jeu direct entre joueurs sans transiter par le serveur

Règles déjà en place : les règles firewall sont déjà en place
sur le nouveau serveur. Si ce n’est pas le cas, les ajouter via MyVestaCP CLI :

# Ports Kaillera classiques (UDP)
/usr/local/vesta/bin/v-add-firewall-rule ACCEPT 0.0.0.0/0 27888 UDP Emulinker-Kaillera-Main
/usr/local/vesta/bin/v-add-firewall-rule ACCEPT 0.0.0.0/0 27889:27944 UDP Emulinker-Kaillera-Games
/usr/local/vesta/bin/v-add-firewall-rule ACCEPT 0.0.0.0/0 27886:27887 UDP Emulinker-P2P
/usr/local/vesta/bin/v-add-firewall-rule ACCEPT 0.0.0.0/0 54715:54730 UDP Emulinker-P2P-Extended

# Master list (TCP) - restreint aux IPs des serveurs master list
/usr/local/vesta/bin/v-add-firewall-rule ACCEPT 216.189.101.117 80 TCP Emulinker-Master-List-2
/usr/local/vesta/bin/v-add-firewall-rule ACCEPT 50.203.71.60 80 TCP Emulinker-Master-List

# Appliquer
/usr/local/vesta/bin/v-update-firewall

Format des commentaires MyVestaCP : les commentaires ne doivent pas contenir
d’espaces. Utiliser des tirets (Emulinker-Kaillera-Main) au lieu d’espaces
(Emulinker Kaillera Main), sinon l’erreur
Error: invalid comment format apparaît.

7) Arrêter l’ancien serveur

Avant de démarrer le nouveau serveur Docker, arrêter l’ancien pour éviter
les conflits de port et de master list :

# Sur l'ancien serveur
ssh -p {{PORT_SSH_ANCIEN}} root@{{IP_ANCIEN}}

# Arrêter le service systemd
systemctl stop emulinker.service

# Désactiver le démarrage automatique
systemctl disable emulinker.service

# Vérifier que le port est libéré
ss -tulnp | grep 27888

Si la dernière commande ne retourne rien, le port est bien libéré.

8) Démarrer EmuLinker en Docker

Retour sur le nouveau serveur :

cd /home/docker/emulinker/
docker compose up -d

Vérifier que le conteneur est en cours d’exécution :

docker compose ps
docker compose logs --tail=50

Vérifier que le port 27888 est bien en écoute :

ss -tulnp | grep 27888

Résultat attendu :

udp   UNCONN 0      0            0.0.0.0:27888       0.0.0.0:*

Redémarrage en boucle : si le conteneur redémarre en boucle,
vérifier les logs : docker compose logs --tail=100.
Les causes les plus fréquentes sont : mauvais nom de jar dans command,
version Java incompatible, ou fichier de configuration manquant.

9) Tester la connexion

Tester que les ports sont bien accessibles depuis l’extérieur :

# Vérifier que les ports sont ouverts depuis l'extérieur
# (depuis une autre machine)
nc -uzv {{IP_NOUVEAU}} 27888

Test avec un client Kaillera :

  1. Ouvrir un émulateur compatible Kaillera (MAME, Project64, ePSXe avec plugin Kaillera, ZSNES, Snes9k, etc.)
  2. Dans le client Kaillera, ajouter le serveur manuellement : {{IP_NOUVEAU}}:27888
  3. Ou une fois le DNS mis à jour : {{DOMAINE_GAMING}}:27888
  4. Vérifier que le serveur apparaît et que la connexion fonctionne
  5. Tenter de créer une salle de jeu et de démarrer une partie pour valider le fonctionnement complet

Master list publique : si le serveur est enregistré sur la master list Kaillera,
il devrait apparaître automatiquement dans la liste publique du client après quelques minutes
(une fois que les serveurs master list ont détecté le nouveau serveur).

10) Désactiver définitivement l’ancien serveur

Une fois la migration validée :

# Sur l'ancien serveur (si pas déjà fait)
ssh -p {{PORT_SSH_ANCIEN}} root@{{IP_ANCIEN}}
systemctl stop emulinker.service
systemctl disable emulinker.service

DNS partagé : le domaine {{DOMAINE_GAMING}} est partagé entre
TeamSpeak (port 9988) et EmuLinker (port 27888). Si l’enregistrement A a déjà été
mis à jour pour TeamSpeak (vers {{IP_NOUVEAU}}), EmuLinker en bénéficie
automatiquement. Les joueurs pourront se connecter via {{DOMAINE_GAMING}}:27888.


Dépannage

EmuLinker ne démarre pas : « ClassNotFoundException » ou « NoClassDefFoundError »

Cause : le classpath dans command ne trouve pas les classes nécessaires.

# Vérifier que les jars sont bien présents
ls /home/docker/emulinker/server/lib/*.jar

# Vérifier que le répertoire conf/ existe
ls /home/docker/emulinker/server/conf/

# Vérifier les logs Docker pour l'erreur exacte
docker compose logs --tail=50

# Redémarrer après correction
docker compose down
docker compose up -d

EmuLinker ne démarre pas : « UnsupportedClassVersionError »

Cause : la version Java dans l’image Docker ne correspond pas.
Notre installation utilise Java 11 (eclipse-temurin:11-jre).

# Vérifier que l'image est bien eclipse-temurin:11-jre dans docker-compose.yml
grep "image:" /home/docker/emulinker/docker-compose.yml

# Redémarrer après correction
docker compose down
docker compose up -d

Le serveur n’apparaît pas dans la master list

Causes possibles :

  • Les règles firewall TCP port 80 pour les IPs master list ne sont pas actives
  • La configuration EmuLinker ne contient pas les bonnes URLs de master list
  • Les serveurs master list Kaillera sont hors ligne (ce sont des infrastructures anciennes, parfois instables)
# Vérifier les règles firewall
iptables -L INPUT -n | grep -E '216.189.101.117|50.203.71.60'

# Vérifier la configuration EmuLinker (fichier à la racine, pas dans conf/)
grep -i "master" /home/docker/emulinker/server/emulinker.cfg

# Vérifier que l'IP est bien la nouvelle
grep "serverConnectAddress" /home/docker/emulinker/server/emulinker.cfg

# Tester manuellement la connexion aux master lists
curl -I http://216.189.101.117/
curl -I http://50.203.71.60/

Les joueurs ne peuvent pas se connecter

# Vérifier les règles firewall
iptables -L INPUT -n | grep 27888

# Vérifier que le port est en écoute
ss -tulnp | grep 27888

# Vérifier que Docker tourne
docker compose ps

# Vérifier les logs
docker compose logs --tail=100

Latence élevée en jeu

Causes possibles :

  • Le mode network_mode: host n’est pas activé (le NAT Docker ajoute de la latence)
  • Le serveur est surchargé (CPU/RAM)
  • Problème réseau entre les joueurs et le serveur
# Vérifier la configuration réseau dans docker-compose.yml
grep "network_mode" /home/docker/emulinker/docker-compose.yml
# Doit afficher : network_mode: host

# Vérifier l'utilisation CPU/RAM
docker stats emulinker-server

# Tester la latence depuis un client
ping {{IP_NOUVEAU}}

Sensibilité à la latence : le protocole Kaillera est très sensible
à la latence. Le mode network_mode: host minimise la surcharge réseau
en évitant le NAT Docker. Si la latence reste élevée malgré cette configuration,
le problème vient probablement de la distance géographique entre les joueurs
ou de la qualité de leur connexion Internet.


Récapitulatif de l’architecture finale

Services Docker sur {{IP_NOUVEAU}} ({{HOSTNAME_NOUVEAU}})
──────────────────────────────────────────────

Applications web (via reverse proxy MyVestaCP) :
├── Odoo 17        → 127.0.0.1:8069/8169/8269/8369
├── Odoo 8         → 127.0.0.1:8469
├── Akeneo PIM     → 127.0.0.1:8480
└── ownCloud       → 127.0.0.1:8490

Serveurs de jeux (ports exposés directement) :
├── TeamSpeak 3    → 9987/udp, 9988/udp, 30033/tcp
└── EmuLinker      → 27888/udp (+ 27886-27944, 54715-54730)

Domaine {{DOMAINE_GAMING}} :
├── TeamSpeak 3    → :9988 (serveur virtuel 2)
└── EmuLinker      → :27888 (Kaillera)

Liste complète des ports utilisés :

  • 8069/8169/8269/8369 — Odoo 17 (via reverse proxy)
  • 8469 — Odoo 8 (via reverse proxy)
  • 8480 — Akeneo PIM (via reverse proxy)
  • 8490 — ownCloud (via reverse proxy)
  • 9987/udp + 9988/udp — TeamSpeak 3 voix (direct)
  • 10011/tcp — TeamSpeak 3 ServerQuery (localhost seulement)
  • 30033/tcp — TeamSpeak 3 transfert fichiers (direct)
  • 27886-27944/udp — EmuLinker Kaillera (direct, host network)
  • 54715-54730/udp — EmuLinker P2P (direct, host network)

À suivre

EmuLinker est maintenant opérationnel en Docker avec une configuration optimisée
pour la latence minimale (mode host network).
Dans les prochains articles :

  • Counter-Strike 1.6 en Docker — migration du serveur de jeu CS 1.6
  • Sauvegardes automatisées — stratégie de backup pour tous les services Docker
  • Monitoring et alertes — surveillance centralisée des conteneurs

Article précédent : TeamSpeak 3 en Docker — migration serveur vocal avec {{TAILLE_DONNEES_PRECEDENT}} de fichiers.



Catégories : Non classé

0 commentaire

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *