Exécuter des serveurs de jeu dédiés dans GKE

L'empaquetage d'applications serveur sous forme d'images de conteneur gagne en popularité sur l'ensemble du paysage technologique. Nombre d'acteurs du jeu vidéo sont également intéressés par le recours aux conteneurs pour améliorer l'utilisation des machines virtuelles, ainsi que pour exploiter le paradigme d'exécution isolé propre à cette technologie. Mais en dépit de ce haut niveau d'intérêt, de nombreux éditeurs de jeux vidéo ne savent pas par où commencer. Nous vous recommandons d'utiliser le framework d'orchestration de conteneurs Kubernetes pour déployer des parcs de serveurs de jeux dédiés à l'échelle d'un environnement de production.

Ce tutoriel décrit une architecture extensible permettant d'exécuter sur Kubernetes Engine (GKE) des serveurs de jeu dédiés multijoueurs, fonctionnant par sessions et en temps réel. Un processus de gestionnaire de scaling démarre et arrête automatiquement les instances de machine virtuelle en fonction des besoins. La configuration des machines en tant que nœuds Kubernetes est gérée automatiquement par des groupes d'instances gérés.

La structure de jeu en ligne présentée dans ce tutoriel a été volontairement simplifiée de façon à en faciliter la compréhension et la mise en œuvre. Le cas échéant, les domaines dans lesquels des éléments de complexité supplémentaires pourraient être utiles sont signalés.

Objectifs

  • Créer une image de conteneur d'OpenArena, un serveur de jeu dédié Open Source sous Linux utilisant Docker. Cette image de conteneur n'ajoute que les fichiers binaires et les bibliothèques nécessaires à une image Linux de base.
  • Stocker les éléments sur un volume de disque persistant distinct en lecture seule, et les installer dans le conteneur au moment de l'exécution.
  • Installez et configurez les processus de base du programmeur à l'aide des API Kubernetes et Google Cloud pour créer ou arrêter des nœuds en fonction de la demande.

Coûts

Ce tutoriel utilise les composants facturables Google Cloud suivants :

Vous pouvez utiliser le simulateur de coût pour générer une estimation des coûts en fonction de votre utilisation prévue.

Avant de commencer

Ce tutoriel est destiné à être exécuté à partir d'un environnement Linux ou macOS.

  1. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder à la page de sélection du projet

  2. Assurez-vous que la facturation est activée pour votre projet Cloud. Découvrez comment vérifier que la facturation est activée pour votre projet.

  3. Activez l'API Compute Engine.

    Activer l'API

  4. Installez et initialisez le SDK Cloud.

    Remarque : Ce tutoriel ne permet pas l'utilisation de Cloud Shell. Vous devez installer le SDK Cloud.

  5. Installez kubectl, l'interface de ligne de commande de Kubernetes :
    gcloud components install kubectl
  6. Clonez le dépôt du tutoriel à partir de GitHub :
    git clone https://github.com/GoogleCloudPlatform/gke-dedicated-game-server.git
    
  7. Installez Docker

    Ce tutoriel ne vous fait pas exécuter les commandes Docker en tant qu'utilisateur racine. Veillez par conséquent à suivre aussi les instructions de post-installation pour gérer Docker en tant qu'utilisateur racine.
  8. (Facultatif) Si vous souhaitez tester une connexion au serveur de jeu à la fin du tutoriel, installez le client de jeu OpenArena. L'exécution de ce client de jeu nécessite un environnement de bureau. Ce tutoriel comprend les instructions requises pour effectuer les tests sous Linux et sur macOS.

Architecture

présentation de la solution de jeu

La page Présentation de l'infrastructure de jeu dans le cloud décrit les composants de haut niveau communs à de nombreuses architectures de jeu en ligne. Dans ce tutoriel, vous implémentez un service d'interface de cluster de serveurs de jeu dédiés Kubernetes ainsi qu'un service de backend de gestionnaire de scaling. Une infrastructure de jeu complète en phase de production inclurait également de nombreux autres services d'interface et de backend, qui sortent toutefois du cadre du présent tutoriel.

Contraintes de conception relatives à ce tutoriel

Afin de proposer un exemple à la fois instructif et relativement facile à étendre, ce tutoriel pose un ensemble de contraintes en ce qui concerne le jeu :

  • Il s'agit d'un jeu en temps réel sous forme de parties, avec un serveur de jeu dédié de référence simulant l'état du jeu.
  • Le serveur de jeu dédié communique avec le client via UDP.
  • Chaque processus de serveur de jeu dédié exécute une partie et une seule.
  • Tous les processus de serveur de jeu dédié génèrent approximativement la même charge.
  • La durée maximale des parties est limitée.
  • Le temps de démarrage d'un serveur de jeu dédié est négligeable, et il n'est pas nécessaire de préchauffer le processus de serveur de jeu dédié.
  • Durant la phase de réduction de la capacité consécutive à un pic d'activité, aucune partie n'est arrêtée de manière prématurée dans le but de réduire les coûts. La priorité est d'éviter tout impact sur l'expérience du joueur.
  • Si un processus de serveur de jeu dédié rencontre une erreur et ne peut pas continuer, l'état de la partie est perdu, et le joueur doit faire appel au client de jeu pour rejoindre une autre partie.
  • Le processus de serveur de jeu dédié charge des éléments statiques à partir d'un disque, mais ne nécessite pas d'accès en écriture pour ces éléments.

Ces contraintes ont toutes des précédents dans l'industrie du jeu vidéo et correspondent à un cas d'utilisation concret.

Préparer l'environnement de travail GCP

Afin de faciliter l'exécution des commandes gcloud, vous pouvez définir les propriétés ci-dessous pour éviter d'avoir à spécifier les options correspondantes dans chaque commande.

  1. Définissez votre projet par défaut en remplaçant [PROJECT_ID] par l'ID de votre projet dans la commande suivante :

    gcloud config set project [PROJECT_ID]
  2. Définissez votre zone Compute Engine par défaut en remplaçant [ZONE] par votre zone préférée :

    gcloud config set compute/zone [ZONE]

Conteneuriser le serveur de jeu dédié

Dans ce tutoriel, vous utiliserez OpenArena, décrit comme "un jeu de tir à la première personne (FPS, First Person Shooter) et en mode Combat à mort, produit par la communauté et basé sur la technologie GPL idTech3". Bien que sa technologie date de plus de 15 ans, ce jeu reste un bon exemple de modèle de serveur de jeu dédié largement utilisé :

  • Un binaire serveur est compilé à partir de la même base de code que le client de jeu.
  • Les seuls éléments de données inclus dans ce binaire serveur sont ceux dont le serveur a besoin pour exécuter la simulation.
  • Par rapport à l'image de base du conteneur de système d'exploitation, l'image du conteneur de serveur de jeu n'ajoute que les binaires et les bibliothèques nécessaires à l'exécution du processus du serveur.
  • Les éléments sont installés à partir d'un volume distinct.

Cette architecture présente de nombreux avantages : elle accélère la distribution des images, elle réduit la charge de mise à jour dans la mesure où seuls les binaires sont remplacés et elle utilise moins d'espace disque.

Créer l'image du conteneur

L'image à construire est décrite dans un fichier Dockerfile. Le fichier Dockerfile de ce tutoriel se trouve dans le dépôt à l'emplacement suivant openarena/Dockerfile. À partir du répertoire openarena/, exécutez la commande Docker build pour générer l'image de conteneur, puis attribuez-lui un tag l'identifiant comme la version 0.8.8 du serveur OpenArena :

docker build -t openarena:0.8.8 .

Générer un disque d'éléments

Dans la plupart des jeux, la taille des fichiers binaires est plusieurs fois inférieure à celle des éléments. Il est donc pertinent de créer une image de conteneur ne contenant que les binaires. Les éléments peuvent être placés sur un disque persistant et associés à plusieurs instances de machine virtuelle qui exécutent le conteneur du serveur de jeu dédié. Cette architecture permet de réaliser des économies et élimine le besoin de distribuer les éléments à toutes les instances de machine virtuelle.

  1. Créez une petite instance de machine virtuelle Compute Engine à l'aide de gcloud :

    gcloud compute instances create openarena-asset-builder \
        --machine-type f1-micro --image-family debian-9 \
        --image-project debian-cloud
  2. Créez un disque persistant :

    gcloud compute disks create openarena-assets --size=50GB \
        --type=pd-ssd --description="OpenArena data disk. \
        Mount read-only at /usr/share/games/openarena/baseoa/"

    Ce disque persistant doit être distinct du disque de démarrage et configuré de façon à ne pas être supprimé lors de la suppression de la machine virtuelle. La fonctionnalité persistentVolume de Kubernetes fonctionne mieux lorsqu'elle est utilisée dans GKE avec des disques persistants. Selon la documentation de Compute Engine, ces disques persistants sont constitués d'un seul système de fichiers ext4 sans table de partition.

  3. Associez le disque persistant openarena-assets à l'instance de VM openarena-asset-builder :

    gcloud compute instances attach-disk openarena-asset-builder \
        --disk openarena-assets
  4. Formatez le nouveau disque.

    1. Connectez-vous à l'instance de VM openarena-asset-builder et formatez le disque.

      gcloud compute ssh openarena-asset-builder
    2. Comme la commande mkfs.ext4 que vous allez exécuter à l'étape suivante est une commande destructive, veillez à bien vérifier l'ID d'appareil du disque openarena-assets. Si vous suivez ce tutoriel depuis le début et que vous utilisez un nouveau projet, l'ID est /dev/sdb. Pour le vérifier, examinez les disques joints et leurs partitions à l'aide de la commande lsblk :

      sudo lsblk

      Le résultat devrait indiquer le disque de système d'exploitation de 10 Go sda et son unique partition sda1, ainsi que le disque openarena-assets de 50 Go sans partition, correspondant à l'identifiant d'appareil sdb :

      NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
      sda 8:0 0 10G 0 disk
      └─sda1 8:1 0 10G 0 part /
      sdb 8:16 0 50G 0 disk
    3. Formatez le disque openarena-assets :

      sudo mkfs.ext4 -m 0 -F -E \
          lazy_itable_init=0,lazy_journal_init=0,discard /dev/[DEVICE_ID]
  5. Installez OpenArena sur l'instance de VM openarena-asset-builder et copiez les archives des éléments compressés sur le disque persistant openarena-assets.

    Les éléments de ce jeu sont des fichiers .pk3 situés dans le répertoire /usr/share/games/openarena/baseoa/. Afin de vous faciliter la tâche, la séquence de commandes suivante installe le disque d'éléments dans ce répertoire avant l'installation, de sorte que tous les fichiers .pk3 soient placés sur le disque par le processus d'installation. Veillez à utiliser l'ID d'appareil que vous venez de vérifier.

    sudo mkdir -p /usr/share/games/openarena/baseoa/
    
    sudo mount -o discard,defaults /dev/[DEVICE_ID] \
        /usr/share/games/openarena/baseoa/
    
    sudo apt-get update && sudo apt-get -y install openarena-server
  6. Quittez l'instance, puis supprimez-la :

    exit
    gcloud compute instances delete openarena-asset-builder

    Le disque est prêt à être utilisé comme volume persistant dans Kubernetes.

Lorsque vous implémentez des disques persistants dans le cadre de votre pipeline de développement de jeux, configurez votre système de développement de sorte qu'il crée le disque persistant avec tous les fichiers d'éléments dans une structure de répertoires appropriée. Cela peut prendre la forme d'un simple script exécutant des commandes gcloud ou d'un plug-in GCP pour le système de compilation de votre choix. Il est également recommandé de créer plusieurs copies du disque persistant, et de connecter les instances de machine virtuelle à ces copies de manière équilibrée, aussi bien pour augmenter le débit que pour gérer les risques de défaillance.

Configurer un cluster Kubernetes

En tant que projet développé par la communauté Open Source, Kubernetes peut être configuré pour fonctionner dans la plupart des environnements, y compris sur site.

Créer un cluster Kubernetes sur Kubernetes Engine

Ce tutoriel utilise un cluster Kubernetes Engine standard équipé du type de machine n1-highcpu, qui correspond au profil d'utilisation d'OpenArena.

  1. Créez un réseau VPC destiné à votre jeu, nommé game :

    gcloud compute networks create game
  2. Créez une règle de pare-feu pour OpenArena :

    gcloud compute firewall-rules create openarena-dgs --network game \
        --allow udp:27961-28061
  3. À l'aide de la commande gcloud, créez un cluster comportant trois nœuds chacun dotés de quatre cœurs de processeur virtuel et utilisant votre réseau game :

    gcloud container clusters create openarena-cluster \
        --network game --num-nodes 3 --machine-type n1-highcpu-4 \
        --addons KubernetesDashboard
  4. Une fois le nouveau cluster démarré, configurez votre interface système locale avec les identifiants d'authentification Kubernetes appropriés pour contrôler ce cluster :

    gcloud container clusters get-credentials openarena-cluster

Dans un cluster de production, le nombre de processeurs virtuels que vous exécutez sur chaque machine est largement influencé par deux facteurs :

  • Le nombre maximal de pods de serveur de jeu dédié simultanés que vous prévoyez d'exécuter. Le nombre de nœuds autorisés par pool dans un cluster Kubernetes est limité (bien que le projet Kubernetes prévoie d'augmenter cette limite dans les prochaines versions). Par exemple, si vous exécutez un serveur de jeu dédié par processeur virtuel, un cluster de 1 000 nœuds de machines n1-highcpu-2 n'offrira une capacité suffisante que pour 2 000 pods de serveur de jeu dédié. En revanche, un cluster de 1 000 nœuds de machines n1-highcpu-32 offre une capacité maximale de 32 000 pods.

  • La précision de vos instances de machine virtuelle. Le moyen le plus simple d'ajouter ou de supprimer des ressources dans le cluster consiste à ajouter ou retrancher un nombre entier d'instances de machine virtuelle du type choisi lors de la création du cluster. Évitez par conséquent de choisir une machine dotée de 32 processeurs virtuels si vous souhaitez pouvoir ajouter ou supprimer de la capacité par incréments inférieurs à 32 processeurs virtuels.

La fonctionnalité de groupes d'instances gérées que GKE utilise par défaut inclut des fonctionnalités d'autoscaling des instances de VM et d'équilibrage de charge HTTP. Toutefois, la commande que vous avez utilisée pour créer le cluster Kubernetes a désactivé ces fonctionnalités via l'option --disable-addons HttpLoadBalancing,HorizontalPodAutoscaling.

L'équilibrage de charge HTTP n'est pas nécessaire dans la mesure où le serveur de jeu dédié communique avec les clients à l'aide du protocole UDP, et non via le protocole TCP. L'autoscaler ne peut actuellement faire évoluer le groupe d'instances qu'en fonction de l'utilisation du processeur, qui n'est pas nécessairement un indicateur fiable de la charge des serveurs de jeu dédiés. De nombreux serveurs de jeu dédiés sont en effet conçus pour consommer des cycles d'inactivité afin d'optimiser la simulation du jeu.

C'est la raison pour laquelle de nombreux développeurs de jeux implémentent un processus de gestionnaire de scaling personnalisé compatible avec le serveur de jeu dédié pour répondre aux exigences spécifiques de ce type de charge de travail. Le groupe d'instances géré garde néanmoins un rôle important car son modèle d'image GKE par défaut contient tous les logiciels Kubernetes nécessaires, et il enregistre automatiquement le nœud auprès du plan de contrôle au démarrage.

Importer l'image de conteneur dans Google Cloud

Google propose un stockage d'images Docker privé dans Container Registry (gcr.io).

  1. Sélectionnez la région gcr.io la plus proche de votre cluster GKE (par exemple, us pour les États-Unis, eu pour l'Europe, asia pour l'Asie, comme indiqué dans la documentation), et stockez cet identifiant de région avec l'ID de votre projet dans une variable d'environnement :

    export GCR_REGION=[GCR_REGION] PROJECT_ID=[PROJECT_ID]
  2. Ajoutez un tag indiquant le nom du registre gcr.io à votre image de conteneur :

    docker tag openarena:0.8.8 \
        ${GCR_REGION}.gcr.io/${PROJECT_ID}/openarena:0.8.8
  3. Configurez l'authentification Docker pour Container Registry :

    gcloud auth configure-docker
  4. Importez l'image du conteneur dans le dépôt d'images :

    docker push ${GCR_REGION}.gcr.io/${PROJECT_ID}/openarena:0.8.8

Une fois le transfert terminé, l'image du conteneur est disponible pour s'exécuter dans votre cluster GKE. Prenez note du tag de votre image de conteneur finale, car vous l'intégrerez ultérieurement au fichier de spécification du pod.

Configurer le disque d'éléments dans Kubernetes

Le serveur de jeu dédié n'a généralement pas besoin d'accéder en écriture aux éléments de jeu. Vous pouvez donc faire en sorte que tous les pods de serveur de jeu dédié installent le même disque persistant contenant des éléments en lecture seule. Pour ce faire, utilisez les ressources persistentVolume et persistentVolumeClaim de Kubernetes.

  1. Appliquez asset-volume.yaml, qui contient la définition d'une ressource persistentVolume Kubernetes liée au disque d'éléments que vous avez créé précédemment :

    kubectl apply -f openarena/k8s/asset-volume.yaml
  2. Appliquez asset-volumeclaim.yaml. Il contient la définition d'une ressource Kubernetes persistentVolumeClaim, qui permet aux pods d'installer le disque d'éléments :

    kubectl apply -f openarena/k8s/asset-volumeclaim.yaml

    Vérifiez que le volume est à l'état lié (Bound) en exécutant la commande suivante :

    kubectl get persistentVolume

    Résultat attendu :

    NAME           CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS    CLAIM                      STORAGECLASS   REASON    AGE
    asset.volume   50Gi       ROX           Retain          Bound     default/asset.disk.claim   assets                   3m

    Vérifiez de même que la revendication est liée :

    kubectl get persistentVolumeClaim

    Résultat attendu :

    NAME               STATUS    VOLUME         CAPACITY   ACCESSMODES   STORAGECLASS   AGE
    asset.disk.claim   Bound     asset.volume   50Gi       ROX           assets         25s

Configurer le pod de serveur de jeu dédié

Dans la mesure où les tâches de planification et de mise en réseau sont gérées par Kubernetes, et où les temps de démarrage et d'arrêt des conteneurs de serveur de jeu dédié sont négligeables, les instances de serveur de jeu dédié utilisées dans ce tutoriel sont créées à la demande.

Chaque instance de serveur de jeu dédié ne dure que le temps d'une seule partie, sachant que la durée des parties est limitée à la valeur spécifiée dans le fichier de configuration du serveur de jeu dédié OpenArena. Lorsque la partie est terminée, le conteneur se ferme. Si les joueurs veulent jouer à nouveau, ils demandent une autre partie. Cette conception simplifie un certain nombre d'aspects du cycle de vie des pods et constitue la base de la règle d'autoscaling décrite plus loin dans la section Configurer le gestionnaire de scaling.

Ce processus n'est pas parfaitement fluide avec OpenArena pour la simple raison que le tutoriel ne subdivise pas et ne modifie pas le code du client de jeu. Dans une version commercialisée du jeu, le processus de demande de nouvelle partie serait masqué à l'utilisateur par l'affichage des écrans de résultats des parties précédentes et des temps de chargement. Le code qui demande au client de se connecter à une nouvelle instance de serveur entre deux parties n'implique pas de temps de développement supplémentaire, car ce code est de toute façon indispensable pour gérer les reconnexions des clients dues à des circonstances imprévues, telles que problèmes réseau ou des serveurs qui ont planté.

Par souci de simplicité, ce tutoriel suppose que les nœuds GKE disposent de la configuration réseau par défaut, qui attribue une adresse IP publique à chaque nœud et permet les connexions client.

Gérer le processus de serveur de jeu dédié

Dans les serveurs de jeux commercialisés, toutes les fonctionnalités supplémentaires qui ne sont pas liées au serveur de jeu dédié mais concourent à son bon fonctionnement dans un conteneur doivent être intégrées directement dans les binaires du serveur de jeu dédié.

Selon les bonnes pratiques, le serveur de jeu dédié devrait éviter de communiquer directement avec le gestionnaire de scaling ou de mise en correspondance, et doit simplement exposer son état à l'API Kubernetes. Les processus externes doivent quant à eux lire l'état du serveur de jeu dédié à partir des points de terminaison Kubernetes appropriés, plutôt que d'interroger directement le serveur. Vous trouverez plus d'informations sur l'accès direct à l'API Kubernetes dans la documentation de Kubernetes.

Un processus unique exécuté dans un conteneur et doté d'une durée de vie limitée ainsi que de critères de réussite définis semblerait a priori constituer un cas d'utilisation pour les tâches Kubernetes. Pourtant, le recours aux tâches ne se justifie pas dans la pratique. Les processus de serveur de jeu dédié n'ont pas besoin de la fonctionnalité d'exécution parallèle des travaux. Ils ne requièrent pas non plus la capacité de garantir la réussite par un redémarrage automatique (de façon générale, lorsqu'un serveur de jeu dédié basé sur une session cesse de fonctionner pour une raison quelconque, son état est perdu et les joueurs rejoignent simplement un autre serveur de jeu dédié). Compte tenu de ces éléments, la planification de pods Kubernetes individuels est préférable pour ce cas d'utilisation.

En production, les pods de serveur de jeu dédié sont démarrés directement par votre gestionnaire de mise en correspondance à l'aide de l'API Kubernetes. Pour les besoins de ce tutoriel, un fichier YAML lisible par un utilisateur et décrivant la ressource du pod de serveur de jeu dédié est inclus dans le dépôt du tutoriel à l'emplacement suivant : openarena/k8s/openarena-pod.yaml. Lorsque vous créez des configurations pour des pods de serveur de jeu dédié, vérifiez attentivement les propriétés du volume pour vous assurer que le disque d'éléments pourra être installé en lecture seule dans plusieurs pods.

Configurer le gestionnaire de scaling

Le gestionnaire de scaling est un processus qui fait évoluer le nombre de machines virtuelles utilisées comme nœuds GKE en fonction de la charge actuelle de serveur de jeu dédié. Le scaling s'effectue à l'aide d'un ensemble de scripts exécutés en permanence qui inspectent le nombre total de pods de serveur de jeu dédié exécutés et demandés, et qui redimensionnent le pool de nœuds en fonction des besoins. Les scripts sont empaquetés dans des images de conteneurs Docker avec les bibliothèques appropriées et le SDK Cloud. Vous pouvez créer et transférer des images Docker vers gcr.io en suivant la procédure décrite ci-après.

  1. Si nécessaire, stockez la valeur gcr.io GCR_REGION ainsi que votre PROJECT_ID dans des variables d'environnement destinées aux scripts de compilation et de transfert. Vous pouvez ignorer cette étape si vous avez déjà effectué cette opération lors de l'importation de l'image de conteneur.

    export REGION=[GCR_REGION] PROJECT_ID=[PROJECT_ID]
  2. Accédez au répertoire du script :

    cd scaling-manager
  3. Exécutez le script de compilation pour créer toutes les images de conteneur, puis transmettez celles-ci à gcr.io :

    ./build-and-push.sh
  4. À l'aide d'un éditeur de texte, ouvrez le fichier de déploiement Kubernetes situé à cet emplacement : scaling-manager/k8s/openarena-scaling-manager-deployment.yaml.

    Les scripts du gestionnaire de scaling sont conçus pour être exécutés dans un déploiement Kubernetes, ce qui garantit leur redémarrage en cas d'échec.

  5. Remplacez les variables d'environnement par les valeurs propres à votre déploiement, comme indiqué dans le tableau suivant :

    Variable d'environnement Valeur par défaut Notes
    REGION [GCR_REGION] Remplacement obligatoire. Spécifiez la région de votre dépôt gcr.io.
    PROJECT_ID [PROJECT_ID] Remplacement obligatoire. Spécifiez le nom de votre projet.
    GKE_BASE_INSTANCE_NAME gke-openarena-cluster-default-pool-[REPLACE_ME] Remplacement obligatoire. Cette valeur diffère pour chaque cluster GKE. Pour obtenir la valeur du champ [REPLACE_ME], exécutez la commande gcloud compute instance-groups managed list.
    GCP_ZONE [ZONE] Remplacement obligatoire. Indiquez le nom de la zone GCP que vous avez spécifié au début de ce tutoriel.
    K8S_CLUSTER openarena-cluster Spécifiez le nom du cluster Kubernetes.
  6. Revenez au répertoire parent :

    cd ..
  7. Ajoutez ce déploiement à votre cluster Kubernetes :

    kubectl apply \
        -f scaling-manager/k8s/openarena-scaling-manager-deployment.yaml

Processus de scaling des nœuds

Pour assurer le scaling des nœuds, le gestionnaire de scaling utilise l'API Kubernetes pour examiner l'utilisation actuelle des nœuds. Au besoin, le gestionnaire redimensionne le groupe d'instances géré du cluster Kubernetes Engine qui exécute les machines virtuelles sous-jacentes.

Préoccupations liées au scaling des pods de serveur de jeu dédié

Les obstacles les plus courants au scaling des pods de serveur de jeu dédié sont les suivants :

  • Il est fréquent que les métriques standards concernant l'utilisation du processeur et de la mémoire ne capturent pas suffisamment d'informations pour entraîner le scaling d'une instance de serveur de jeu.
  • Il est essentiel de conserver une mémoire tampon de nœuds disponibles et sous-utilisés, car la planification d'un conteneur de serveur de jeu dédié optimisé sur un nœud existant ne prend que quelques secondes. En revanche, l'ajout d'un nœud peut prendre plusieurs minutes, ce qui constitue une latence inacceptable pour un joueur en attente.
  • De nombreux autoscalers ne sont pas capables de gérer les arrêts de pod en douceur. Il est important de drainer les pods des nœuds en cours de suppression. La fermeture d'un nœud comportant ne serait-ce qu'une seule partie en cours est souvent inacceptable.

Les scripts fournis par ce tutoriel sont élémentaires, mais cette simplicité de conception facilite l'ajout de logique supplémentaire. Les serveurs de jeu dédiés possèdent généralement des caractéristiques de performances bien établies, qu'il vous suffit donc de convertir en métriques pour déterminer quand ajouter ou supprimer des instances de machine virtuelle. Parmi les métriques de scaling les plus courantes, il convient de citer le nombre d'instances de serveur de jeu dédié par CPU (utilisé dans ce tutoriel) ou le nombre d'emplacements disponibles pour les joueurs.

Augmenter la capacité

L'augmentation de la capacité ne nécessite aucune manipulation particulière dans le cadre de ce tutoriel. Par souci de simplicité, ce tutoriel définit les propriétés de pod limits et requests spécifiées dans le fichier YAML du pod (openarena/k8s/openarena-pod.yaml) de façon à réserver environ un processeur virtuel pour chaque serveur de jeu dédié, ce qui constitue un ratio suffisant pour OpenArena.

Comme le cluster a été créé à l'aide de la famille d'instances n1-highcpu, qui dispose d'un ratio fixe de 600 Mo de mémoire pour un processeur virtuel, la mémoire devrait être suffisante si un serveur de jeu dédié est planifié pour chaque processeur virtuel. Vous pouvez ainsi gérer le scaling en fonction du nombre de pods présents dans le cluster par rapport au nombre de processeurs présents sur l'ensemble des nœuds de ce cluster. Ce ratio détermine les ressources restantes disponibles, ce qui vous permet d'ajouter des nœuds si la valeur tombe en dessous d'un certain seuil. Dans ce tutoriel, des nœuds sont ajoutés lorsque plus de 70 % des processeurs virtuels sont actuellement alloués à des pods.

Dans un backend de jeu en ligne en phase de production, il est recommandé d'établir avec précision le profil d'utilisation du serveur de jeu dédié, en termes de processeur, de mémoire et de réseau, puis de définir les propriétés de pods limits et requests en conséquence. Pour de nombreux jeux, il est judicieux de créer plusieurs types de pods pour les scénarios de serveur de jeu dédié correspondant à différents profils d'utilisation – par exemple, types de jeu, cartes spécifiques ou nombre d'emplacements pour les joueurs. De telles considérations sortent du cadre de ce tutoriel.

Réduire la capacité

Contrairement à l'augmentation de la capacité, la réduction de la capacité est un processus en plusieurs étapes, qui justifie à lui seul l'utilisation d'un gestionnaire de scaling personnalisé et compatible avec Kubernetes pour le serveur de jeu dédié personnalisé. Dans ce tutoriel, le script scaling-manager.sh gère automatiquement les étapes suivantes :

  1. Il sélectionne un nœud approprié pour la suppression. Étant donné qu'un programmeur Kubernetes personnalisé et orienté jeu à part entière n'entre pas dans le cadre de ce tutoriel, les nœuds sont simplement sélectionnés dans l'ordre dans lequel ils ont été renvoyés par l'API.

  2. Il marque le nœud sélectionné comme indisponible dans Kubernetes. Cela empêche le démarrage de pods supplémentaires sur ce nœud.

  3. Il supprime le nœud sélectionné du groupe d'instances géré à l'aide de la commande abandon-instance. Cela empêche le groupe d'instances géré d'essayer de recréer l'instance.

Le script node-stopper.sh surveille par ailleurs l'absence de pods de serveur de jeu dédié sur les nœuds abandonnés et non planifiables. Une fois que toutes les parties exécutées sur un nœud sont terminées, et que les pods sont fermés, le script arrête l'instance de machine virtuelle.

Gérer le scaling du nombre de pods de serveur de jeu dédié

Dans les backends de jeu en phase de production, c'est généralement le gestionnaire de mise en correspondance qui détermine le moment où de nouvelles instances de serveur de jeu dédié doivent être ajoutées. Étant donné que les pods de serveur de jeu dédié de ce tutoriel sont configurés pour se fermer en fin de partie (comme indiqué plus haut dans la section concernant les contraintes de conception), aucune action explicite n'est nécessaire pour réduire leur nombre. Si le système de mise en correspondance ne reçoit pas assez de demandes de joueurs pour générer de nouvelles parties, les pods de serveur de jeu dédié se retirent peu à peu du cluster Kubernetes à mesure que les parties prennent fin.

Tester la configuration

Jusqu'à présent, vous avez créé l'image de conteneur OpenArena et vous l'avez transférée dans Container Registry, puis vous avez démarré le cluster Kubernetes de serveurs de jeu dédiés. Vous avez également généré le disque d'éléments de jeu et vous l'avez configuré pour une utilisation dans Kubernetes, puis vous avez démarré le déploiement du gestionnaire de scaling. À ce stade, il est temps de démarrer les pods de serveur de jeu dédié pour les tests.

Demander une nouvelle instance de serveur de jeu dédié

Dans un système de production type, lorsque le processus de mise en correspondance dispose des joueurs appropriés pour générer une partie, il demande directement une instance via l'API Kubernetes. Afin de tester la configuration utilisée dans ce tutoriel, vous pouvez envoyer directement une requête d'instance.

  1. Dans un éditeur de texte, ouvrez le fichier openarena/k8s/openarena-pod.yaml et recherchez la ligne spécifiant l'image de conteneur à exécuter.

  2. Modifiez cette valeur pour la faire correspondre au tag de l'image de votre conteneur openarena en exécutant la commande docker tag comme indiqué précédemment dans ce tutoriel.

  3. Exécutez la commande kubectl apply en spécifiant le fichier openarena-pod.yaml :

    kubectl apply -f openarena/k8s/openarena-pod.yaml
  4. Patientez quelques instants, puis vérifiez l'état du pod :

    kubectl get pods

    Le résultat ressemble à ceci :

    NAME             READY     STATUS    RESTARTS   AGE
    openarena.dgs    1/1       Running   0          25s

Se connecter au serveur de jeu dédié

Une fois le pod démarré, vérifiez que vous pouvez vous connecter au serveur de jeu dédié en lançant le client OpenArena.

Depuis un bureau macOS ou Linux :

export NODE_NAME=$(kubectl get pod openarena.dgs \
    -o jsonpath="{.spec.nodeName}")
export DGS_IP=$(gcloud compute instances list \
    --filter="name=( ${NODE_NAME} )" \
    --format='table[no-heading](EXTERNAL_IP)')
openarena +connect ${DGS_IP}

Tester le gestionnaire de scaling

Le gestionnaire de scaling adapte le nombre d'instances de machine virtuelle présentes dans le cluster Kubernetes en fonction du nombre de pods de serveur de jeu dédié. Par conséquent, le test du gestionnaire de scaling nécessite d'envoyer un certain nombre de demandes de pods sur une période donnée et de vérifier que le nombre de nœuds évolue en conséquence. Pour que vous puissiez ensuite constater la réduction du nombre de nœuds, il faut que les parties aient une durée limitée, spécifiée dans le fichier de configuration du serveur. Le fichier de configuration de serveur utilisé dans ce tutoriel (openarena/single-match.cfg) limite la durée des parties à cinq minutes. Il est utilisé par défaut dans les pods de serveur de jeu dédié du tutoriel. Pour effectuer le test, exécutez le script suivant, qui ajoute des pods de serveur de jeu dédié à intervalles réguliers pour une durée de cinq minutes :

./scaling-manager/tests/test-loader.sh

Vous devriez voir le nombre de nœuds augmenter et baisser en exécutant la commande kubectl get nodes à intervalles réguliers.

Nettoyer

Pour éviter que les ressources utilisées lors de ce tutoriel soient facturées sur votre compte Google Cloud, supprimez le projet contenant les ressources, ou conservez le projet et les ressources individuelles.

Supprimer le projet

Le moyen le plus simple d'empêcher la facturation est de supprimer le projet que vous avez créé pour ce tutoriel.

Pour supprimer le projet :

  1. Dans Cloud Console, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Supprimer le cluster GKE

Si vous ne souhaitez pas supprimer l'ensemble du projet, exécutez la commande suivante pour supprimer le cluster GKE :

gcloud container clusters delete openarena-cluster

Supprimer les disques persistants

Pour supprimer un disque persistant, procédez comme suit :

  1. Dans Cloud Console, accédez à la page "Disques" de Compute Engine.

    Accéder aux disques Compute Engine

  2. Sélectionnez le disque à supprimer.

  3. Cliquez sur le bouton Supprimer en haut de la page.

Étapes suivantes

Ce tutoriel esquisse une architecture épurée permettant d'exécuter des serveurs de jeu dédiés dans des conteneurs, et d'assurer l'autoscaling d'un cluster Kubernetes en fonction de la charge du jeu. Vous pouvez ajouter de nombreuses fonctionnalités, telles que la transition transparente du joueur d'une session à l'autre, en programmant une compatibilité élémentaire côté client. Pour ajouter d'autres fonctionnalités, telles que la possibilité pour les joueurs de former des groupes et celle de déplacer des groupes d'un serveur à un autre, vous pouvez créer un service de plate-forme distinct exécuté parallèlement au service de mise en correspondance. Vous aurez ainsi la possibilité de former des groupes, d'envoyer, accepter ou rejeter des invitations de groupe, et de transférer solidairement des groupes de joueurs dans des instances de serveur de jeu dédié.

Une autre fonctionnalité courante réside dans un programmeur Kubernetes personnalisé capable de choisir les nœuds pour vos pods de serveur de jeu dédié d'une manière plus intelligente et plus axée sur le jeu. Pour la plupart des jeux, il est hautement souhaitable de disposer d'un programmeur personnalisé capable de regrouper les pods, ce qui vous permet d'établir facilement l'ordre dans lequel les nœuds doivent être supprimés lors d'une réduction de la capacité consécutive à un pic d'activité.

Autres ressources de référence concernant l'exécution d'un serveur de jeu dédié sur GCP :