This is the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Code Blind Documentation

Documentation and usage guides on how to develop and host dedicated game servers on top of Code Blind.

Release version: 1.38.0

These pages show you how to get up and running as quickly as possible in Code Blind.

If you are new to Code Blind, start with Overview to get familiar with Code Blind' features and services.

The Installation guide will take you through creating a Kubernetes cluster and getting Code Blind installed.

The Getting Started section will have your first GameServer up and running, and then have you spinning up Fleets after that.

1 - Overview

Code Blind is a library for hosting, running and scaling dedicated game servers on Kubernetes.

Agones, is derived from the Greek word agōn which roughly translates to “contest”, “competition at games” and “gathering” (source).

What is Code Blind?

Code Blind is an open source platform, for deploying, hosting, scaling, and orchestrating dedicated game servers for large scale multiplayer games, built on top of the industry standard, distributed system platform Kubernetes.

Code Blind replaces bespoke or proprietary cluster management and game server scaling solutions with an open source solution that can be utilized and communally developed - so that you can focus on the important aspects of building a multiplayer game, rather than developing the infrastructure to support it.

Built with both Cloud and on-premises infrastructure in mind, Code Blind can adjust its strategies as needed for Fleet management, autoscaling, and more to ensure the resources being used to host dedicated game servers are cost optimal for the environment that they are in.

Why Code Blind?

Some of Code Blind’ advantages:

  • Lower development and operational costs for hosting, scaling and orchestrating multiplayer game servers.
  • Any game server that can run on Linux can be hosted and orchestrated on Code Blind - in any language, or set of dependencies.
  • Run Code Blind anywhere Kubernetes can run - in the cloud, on premise, on your local machine or anywhere else you need it.
  • Game services and your game servers can be on the same foundational platform - simplifying your tooling and your operations knowledge.
  • By extending Kubernetes, Code Blind allows you to take advantage of the thousands of developers that have worked on the features of Kubernetes, and the ecosystem of tools that surround it.
  • Code Blind is free, open source and developed entirely in the public. Help shape its future by getting involved with the community.

Major Features

Code Blind incorporates these abilities:

  • Code Blind extends Kubernetes, such that it gets native abilities to create, run, manage and scale dedicated game server processes within Kubernetes clusters using standard Kubernetes tooling and APIs.
  • Run and update Fleets of Game Servers, without worrying about having Game Servers shutdown that have active players on them.
  • Deploy game servers inside a Docker container, with any combination of dependencies or binaries.
  • Integrated game server SDK for game server lifecycle managements, including health checking, state management, configuration and more.
  • Autoscaling capabilities to ensure players always have a game server available to play on.
  • Out of the box metrics and log aggregation to track and visualise what is happening across all your game server sessions.
  • Modular architecture that can be tailored to your specific multiplayer game mechanics.
  • Game server scheduling and allocation strategies to ensure cost optimisation across cloud and on-premise environments.

Code of Conduct

Participation in this project comes under the Contributor Covenant Code of Conduct

What Next?

  • Review our Prerequisite Knowledge. Especially if the above sounds fantastic, but you aren’t yet familiar with technology like Kubernetes or terms such as “Game Servers”.
  • Have a look at our installation guides, for setting up a Kubernetes cluster and installing Code Blind on it.
  • Go through our Quickstart Guides to take you through setting up a simple game server on Code Blind.

2 - Prerequisite Knowledge

Foundational knowledge you should know before starting working with Code Blind.

Code Blind is built on top of the foundation of multiple open source projects, as well as utilising several architectural patterns across both distributed and multiplayer game systems – which can make it complicated to get started with, if they are things you are not familiar with or have experience with already.

To make getting started easier to break down and digest, this guide was written to outline what concepts and technology that the documentation assumes that you have knowledge of, and the depth of that knowledge, as well as providing resource to help fill those knowledge gaps.

Docker and Containerisation

Docker and containerisation is the technological foundation of Code Blind, so if you aren’t familiar, we recommend you have knowledge in the following areas before getting started with Code Blind:

  • Containers as a concept
  • Running Docker containers
  • Building your own container
  • Registries as a concept
  • Pushing and pulling containers from a registry

Resources

The following resources are great for learning these concepts:

Kubernetes

Kubernetes builds on top of Docker to run containers at scale, on lots of machines. If you have yet to learn about Kubernetes, we recommend that you have knowledge in the following areas before getting started with Code Blind:

  • Kubernetes as a concept - you should take the basics tutorial
  • Pods
  • Deployments
  • Services
  • Creating a Deployment with a Service

Mappings in Code Blind

Code Blind extends the Kubernetes API to include new custom resources such as GameServer and Fleet. See the Reference documentation for more information.

Code Blind creates a backing Pod with the appropriate configuration parameters for each GameServer that is configured in a cluster. They both have the same name.

Resources

Dedicated Game Servers

Code Blind is a platform for dedicated game servers for multiplayer games. If “dedicated game servers” is a term that is not something you are familiar with, we recommend checking out some of the resources below, before getting started with Code Blind:

Resources

Game Engine

If you are building a multiplayer game, you will eventually need to understand how your game engine will integrate with Code Blind. There are multiple possible solutions, but the engines that have out of the box SDK’s for Code Blind are:

While you can integrate other engines with Code Blind, if you are new to developing on a game engine, you may want to start with the above.

3 - Install and configure Code Blind on Kubernetes

Instructions for creating a Kubernetes cluster and installing Code Blind.

Usage Requirements

Supported Container Architectures

The following container operating systems and architectures can be utilised with Code Blind:

OSArchitectureSupport
linuxamd64Stable
linuxarm64Alpha
windowsamd64Alpha

For all the platforms in Alpha, we would appreciate testing and bug reports on any issue found.

Code Blind and Kubernetes Supported Versions

Code Blind will support 3 releases of Kubernetes, targeting the newest version as being the latest available version in the GKE Rapid channel. However, we will ensure that at least one of the 3 versions chosen for each Code Blind release is supported by each of the major cloud providers (EKS and AKS). The vendored version of client-go will be aligned with the middle of the three supported Kubernetes versions. When a new version of Code Blind supports new versions of Kubernetes, it is explicitly called out in the release notes.

The following table lists recent Code Blind versions and their corresponding required Kubernetes versions:

Code Blind versionKubernetes version(s)
1.381.26, 1.27, 1.28
1.371.26, 1.27, 1.28
1.361.26, 1.27, 1.28
1.351.25, 1.26, 1.27
1.341.25, 1.26, 1.27
1.331.25, 1.26, 1.27
1.321.24, 1.25, 1.26
1.311.24, 1.25, 1.26
1.301.23, 1.24, 1.25
1.291.24
1.281.23
1.271.23
1.261.23
1.251.22
1.241.22
1.231.22
1.221.21
1.211.21

Best Practices

For detailed guides on best practices running Code Blind in production, see Best Practices.

3.1 - Create Kubernetes Cluster

Instructions for creating a Kubernetes cluster to install Code Blind on.

3.1.1 - Google Kubernetes Engine

Follow these steps to create a Google Kubernetes Engine (GKE) cluster for your Code Blind install.

Before you begin

Take the following steps to enable the Kubernetes Engine API:

  1. Visit the Kubernetes Engine page in the Google Cloud Platform Console.
  2. Create or select a project.
  3. Wait for the API and related services to be enabled. This can take several minutes.
  4. Enable billing for your project.
  • If you are not an existing GCP user, you may be able to enroll for a $300 US Free Trial credit.

Choosing a shell

To complete this quickstart, we can use either Google Cloud Shell or a local shell.

Google Cloud Shell is a shell environment for managing resources hosted on Google Cloud Platform (GCP). Cloud Shell comes preinstalled with the gcloud and kubectl command-line tools. gcloud provides the primary command-line interface for GCP, and kubectl provides the command-line interface for running commands against Kubernetes clusters.

If you prefer using your local shell, you must install the gcloud and kubectl command-line tools in your environment.

Cloud shell

To launch Cloud Shell, perform the following steps:

  1. Go to Google Cloud Platform Console
  2. From the top-right corner of the console, click the Activate Google Cloud Shell button: cloud shell
  3. A Cloud Shell session opens inside a frame at the bottom of the console. Use this shell to run gcloud and kubectl commands.

Local shell

To install gcloud and kubectl, perform the following steps:

  1. Install the Google Cloud SDK, which includes the gcloud command-line tool.
  2. Initialize some default configuration by running the following command.
    • When asked Do you want to configure a default Compute Region and Zone? (Y/n)?, enter Y and choose a zone in your geographical region of choice.
    gcloud init
    
  3. Install the kubectl command-line tool by running the following command:
    gcloud components install kubectl
    

Choosing a Regional or Zonal Cluster

You will need to pick a geographical region or zone where you want to deploy your cluster, and whether to create a regional or zonal cluster. We recommend using a Regional cluster, as the zonal GKE control plane can go down temporarily to adjust for cluster resizing, automatic upgrades and repairs.

After choosing a cluster type, choose a region or zone. The region you chose is COMPUTE_REGION below. (Note that if you chose a zone, replace --region=[COMPUTE_REGION] with --zone=[COMPUTE_ZONE] in commands below.)

Choosing a Release Channel and Optional Version

We recommend using the regular release channel, which offers a balance between stability and freshness. If you’d like to read more, see our guide on Release Channels. The release channel you chose is RELEASE_CHANNEL below.

(Optional) During cluster creation, to set a specific available version in the release channel, use the --cluster-version=[VERSION] flag, e.g. --cluster-version=1.27. Be sure to choose a version supported by Code Blind. (If you rely on release channels, the latest Code Blind release should be supported by the default versions of all channels.)

Choosing a GKE cluster mode

A cluster consists of at least one control plane machine and multiple worker machines called nodes. In Google Kubernetes Engine, nodes are Compute Engine virtual machine instances that run the Kubernetes processes necessary to make them part of the cluster.

Code Blind supports both GKE Standard mode and GKE Autopilot mode. Code Blind GameServer and Fleet manifests that work on Standard are compatible on Autopilot with some constraints, described in the following section. We recommend running GKE Autopilot clusters, if you meet the constraints.

You can’t convert existing Standard clusters to Autopilot; create new Autopilot clusters instead.

Code Blind on GKE Autopilot

Autopilot is GKE’s fully-managed mode. GKE configures, maintains, scales, and upgrades nodes for you, which can reduce your maintenance and operating overhead. You only pay for the resources requested by your running Pods, and you don’t pay for unused node capacity or Kubernetes system workloads.

This section describes the Code Blind-specific considerations in Autopilot clusters. For a general comparison between Autopilot and Standard, refer to Choose a GKE mode of operation.

Autopilot nodes are, by default, optimized for most workloads. If some of your workloads have broad compute requirements such as Arm architecture or a minimum CPU platform, you can also choose a compute class that meets that requirement. However, if you have specialized hardware needs that require fine-grained control over machine configuration, consider using GKE Standard.

Code Blind on Autopilot has pre-configured opinionated constraints. Evaluate whether these constraints impact your workloads:

  • Operating system: No Windows containers.
  • Resource requests: Autopilot has pre-determined minimum Pod resource requests. If your game servers require less than those minimums, use GKE Standard.
  • Scheduling strategy: Packed is supported, which is the Code Blind default. Distributed is not supported.
  • Host port policy: Dynamic is supported, which is the Code Blind default. Static and Passthrough are not supported.
  • Seccomp profile: Code Blind sets the seccomp profile to Unconfined to avoid unexpected container creation delays that might occur because Autopilot enables the RuntimeDefault seccomp profile.
  • Pod disruption policy: eviction.safe: Never is supported, which is the Code Blind default. eviction.safe: Always is supported. eviction.safe: OnUpgrade is not supported. If your game sessions exceed one hour, refer to Considerations for long sessions.

Choosing a GCP network

By default, gcloud and the Cloud Console use the VPC named default for all new resources. If you plan to create a dual-stack IPv4/IPv6 cluster cluster, special considerations need to be made. Dual-stack clusters require a dual-stack subnet, which are only supported in custom mode VPC networks. For a new dual-stack cluster, you can either:

  • create a new custom mode VPC,

  • or if you wish to continue using the default network, you must switch it to custom mode. After switching a network to custom mode, you will need to manually manage subnets within the default VPC.

Once you have a custom mode VPC, you will need to choose whether to use an existing subnet or create a new one - read VPC-native guide on creating a dual-stack cluster, but don’t create the cluster just yet - we’ll create the cluster later in this guide. To use the network and/or subnetwork you just created, you’ll need to add --network and --subnetwork, and for GKE Standard, possibly --stack-type and --ipv6-access-type, depending on whether you created the subnet simultaneously with the cluster.

Creating the firewall

We need a firewall to allow UDP traffic to nodes tagged as game-server via ports 7000-8000. These firewall rules apply to cluster nodes you will create in the next section.

gcloud compute firewall-rules create game-server-firewall \
  --allow udp:7000-8000 \
  --target-tags game-server \
  --description "Firewall to allow game server udp traffic"

Creating the cluster

Create a GKE cluster in which you’ll install Code Blind. You can use GKE Standard mode or GKE Autopilot mode.

Create a Standard mode cluster for Code Blind

Create the cluster:

gcloud container clusters create [CLUSTER_NAME] \
  --region=[COMPUTE_REGION] \
  --release-channel=[RELEASE_CHANNEL] \
  --tags=game-server \
  --scopes=gke-default \
  --num-nodes=4 \
  --enable-image-streaming \
  --machine-type=e2-standard-4

Replace the following:

  • [CLUSTER_NAME]: The name of the cluster you want to create
  • [COMPUTE_REGION]: The GCP region to create the cluster in, chosen above
  • [RELEASE_CHANNEL]: The GKE release channel, chosen above

Flag explanations:

  • --region: The compute region you chose above.
  • --release-channel: The release channel you chose above.
  • --tags: Defines the tags that will be attached to new nodes in the cluster. This is to grant access through ports via the firewall created above.
  • --scopes: Defines the Oauth scopes required by the nodes.
  • --num-nodes: The number of nodes to be created in each of the cluster’s zones. Default: 4. Depending on the needs of your game, this parameter should be adjusted.
  • --enable-image-streaming: Use Image streaming to pull container images, which leads to significant improvements in initialization times. Limitations apply to enable this feature.
  • --machine-type: The type of machine to use for nodes. Default: e2-standard-4. Depending on the needs of your game, you may wish to have smaller or larger machines.

(Optional) Creating a dedicated node pool

Create a dedicated node pool for the Code Blind resources to be installed in. If you skip this step, the Code Blind controllers will share the default node pool with your game servers, which is fine for experimentation but not recommended for a production deployment.

gcloud container node-pools create agones-system \
  --cluster=[CLUSTER_NAME] \
  --region=[COMPUTE_REGION] \
  --node-taints agones.dev/agones-system=true:NoExecute \
  --node-labels agones.dev/agones-system=true \
  --num-nodes=1 \
  --machine-type=e2-standard-4

Replace the following:

  • [CLUSTER_NAME]: The name of the cluster you created
  • [COMPUTE_REGION]: The GCP region to create the cluster in, chosen above

Flag explanations:

  • --cluster: The name of the cluster you created.
  • --region: The compute region you chose above.
  • --node-taints: The Kubernetes taints to automatically apply to nodes in this node pool.
  • --node-labels: The Kubernetes labels to automatically apply to nodes in this node pool.
  • --num-nodes: The number of nodes per cluster zone. For regional clusters, --num-nodes=1 creates one node in 3 separate zones in the region, giving you faster recovery time in the event of a node failure.
  • --machine-type: The type of machine to use for nodes. Default: e2-standard-4. Depending on the needs of your game, you may wish to have smaller or larger machines.

(Optional) Creating a metrics node pool

Create a node pool for Metrics if you want to monitor the Code Blind system using Prometheus with Grafana or Cloud Logging and Monitoring.

gcloud container node-pools create agones-metrics \
  --cluster=[CLUSTER_NAME] \
  --region=[COMPUTE_REGION] \
  --node-taints agones.dev/agones-metrics=true:NoExecute \
  --node-labels agones.dev/agones-metrics=true \
  --num-nodes=1 \
  --machine-type=e2-standard-4

Replace the following:

  • [CLUSTER_NAME]: The name of the cluster you created
  • [COMPUTE_REGION]: The GCP region to create the cluster in, chosen above

Flag explanations:

  • --cluster: The name of the cluster you created.
  • --region: The compute region you chose above.
  • --node-taints: The Kubernetes taints to automatically apply to nodes in this node pool.
  • --node-labels: The Kubernetes labels to automatically apply to nodes in this node pool.
  • --num-nodes: The number of nodes per cluster zone. For regional clusters, --num-nodes=1 creates one node in 3 separate zones in the region, giving you faster recovery time in the event of a node failure.
  • --machine-type: The type of machine to use for nodes. Default: e2-standard-4. Depending on the needs of your game, you may wish to have smaller or larger machines.

(Optional) Creating a node pool for Windows

If you run game servers on Windows, you need to create a dedicated node pool for those servers. Windows Server 2019 (WINDOWS_LTSC_CONTAINERD) is the recommended image for Windows game servers.

gcloud container node-pools create windows \
  --cluster=[CLUSTER_NAME] \
  --region=[COMPUTE_REGION] \
  --image-type WINDOWS_LTSC_CONTAINERD \
  --machine-type e2-standard-4 \
  --num-nodes=4

Replace the following:

  • [CLUSTER_NAME]: The name of the cluster you created
  • [COMPUTE_REGION]: The GCP region to create the cluster in, chosen above

Flag explanations:

  • --cluster: The name of the cluster you created.
  • --region: The compute region you chose above.
  • --image-type: The image type of the instances in the node pool - WINDOWS_LTSC_CONTAINERD in this case.
  • --machine-type: The type of machine to use for nodes. Default: e2-standard-4. Depending on the needs of your game, you may wish to have smaller or larger machines.
  • --num-nodes: The number of nodes per cluster zone. For regional clusters, --num-nodes=1 creates one node in 3 separate zones in the region, giving you faster recovery time in the event of a node failure.

Create an Autopilot mode cluster for Code Blind

  1. Choose a Release Channel (Autopilot clusters must be on a Release Channel).

  2. Create the cluster:

    gcloud container clusters create-auto [CLUSTER_NAME] \
      --region=[COMPUTE_REGION] \
      --release-channel=[RELEASE_CHANNEL] \
      --autoprovisioning-network-tags=game-server
    

Replace the following:

  • [CLUSTER_NAME]: The name of your cluster.
  • [COMPUTE_REGION]: the GCP region to create the cluster in.
  • [RELEASE_CHANNEL]: one of rapid, regular, or stable, chosen above. The default is regular.

Flag explanations:

  • --region: The compute region you chose above.
  • --release-channel: The release channel you chose above.
  • --autoprovisioning-network-tags: Defines the tags that will be attached to new nodes in the cluster. This is to grant access through ports via the firewall created above.

Setting up cluster credentials

gcloud container clusters create configurates credentials for kubectl automatically. If you ever lose those, run:

gcloud container clusters get-credentials [CLUSTER_NAME] --region=[COMPUTE_REGION]

Next Steps

3.1.2 - Amazon Elastic Kubernetes Service

Follow these steps to create an Amazon Elastic Kubernetes Service (EKS) cluster for your Code Blind install.

Create your EKS Cluster using the Getting Started Guide.

Possible steps are the following:

  1. Create new IAM role for cluster management.
  2. Run aws configure to authorize your awscli with proper AWS Access Key ID and AWS Secret Access Key.
  3. Create an example cluster:
eksctl create cluster \
--name prod \
--version 1.28 \
--nodegroup-name standard-workers \
--node-type t3.medium \
--nodes 3 \
--nodes-min 3 \
--nodes-max 4

Allowing UDP Traffic

For Code Blind to work correctly, we need to allow UDP traffic to pass through to our EKS cluster worker nodes. To achieve this, we must update the workers’ nodepool SG (Security Group) with the proper rule. A simple way to do that is:

  • Log in to the AWS Management Console
  • Go to the VPC Dashboard and select Security Groups
  • Find the Security Group for the workers nodepool, which will be named something like eksctl-[cluster-name]-nodegroup-[cluster-name]-workers/SG
  • Select Inbound Rules
  • Edit Rules to add a new Custom UDP Rule with a 7000-8000 port range and an appropriate Source CIDR range (0.0.0.0/0 allows all traffic)

Next Steps

3.1.3 - Azure Kubernetes Service

Follow these steps to create an Azure Kubernetes Service (AKS) cluster for your Code Blind install.

Choosing your shell

You can use either Azure Cloud Shell or install the Azure CLI on your local shell in order to install AKS in your own Azure subscription. Cloud Shell comes preinstalled with az and kubectl utilities whereas you need to install them locally if you want to use your local shell. If you use Windows 10, you can use the WIndows Subsystem for Windows as well.

Creating the AKS cluster

If you are using Azure CLI from your local shell, you need to log in to your Azure account by executing the az login command and following the login procedure.

Here are the steps you need to follow to create a new AKS cluster (additional instructions and clarifications are listed here):

# Declare necessary variables, modify them according to your needs
AKS_RESOURCE_GROUP=akstestrg     # Name of the resource group your AKS cluster will be created in
AKS_NAME=akstest                 # Name of your AKS cluster
AKS_LOCATION=westeurope          # Azure region in which you'll deploy your AKS cluster

# Create the Resource Group where your AKS resource will be installed
az group create --name $AKS_RESOURCE_GROUP --location $AKS_LOCATION

# Create the AKS cluster - this might take some time. Type 'az aks create -h' to see all available options

# The following command will create a four Node AKS cluster. Node size is Standard A1 v1 and Kubernetes version is 1.28.0. Plus, SSH keys will be generated for you, use --ssh-key-value to provide your values
az aks create --resource-group $AKS_RESOURCE_GROUP --name $AKS_NAME --node-count 4 --generate-ssh-keys --node-vm-size Standard_A4_v2 --kubernetes-version 1.28.0 --enable-node-public-ip

# Install kubectl
sudo az aks install-cli

# Get credentials for your new AKS cluster
az aks get-credentials --resource-group $AKS_RESOURCE_GROUP --name $AKS_NAME

Alternatively, you can use the Azure Portal to create a new AKS cluster (instructions).

Allowing UDP traffic

For Code Blind to work correctly, we need to allow UDP traffic to pass through to our AKS cluster. To achieve this, we must update the NSG (Network Security Group) with the proper rule. A simple way to do that is:

  • Log in to the Azure Portal
  • Find the resource group where the AKS(Azure Kubernetes Service) resources are kept, which should have a name like MC_resourceGroupName_AKSName_westeurope. Alternative, you can type az resource show --namespace Microsoft.ContainerService --resource-type managedClusters -g $AKS_RESOURCE_GROUP -n $AKS_NAME -o json | jq .properties.nodeResourceGroup
  • Find the Network Security Group object, which should have a name like aks-agentpool-********-nsg (ie. aks-agentpool-55978144-nsg for dns-name-prefix agones)
  • Select Inbound Security Rules
  • Select Add to create a new Rule with UDP as the protocol and 7000-8000 as the Destination Port Ranges. Pick a proper name and leave everything else at their default values

Alternatively, you can use the following command, after modifying the RESOURCE_GROUP_WITH_AKS_RESOURCES and NSG_NAME values:

az network nsg rule create \
  --resource-group RESOURCE_GROUP_WITH_AKS_RESOURCES \
  --nsg-name NSG_NAME \
  --name AgonesUDP \
  --access Allow \
  --protocol Udp \
  --direction Inbound \
  --priority 520 \
  --source-port-range "*" \
  --destination-port-range 7000-8000

Getting Public IPs to Nodes

Kubernetes version prior to 1.18.19, 1.19.11 and 1.20.7

To find a resource’s public IP, search for Virtual Machine Scale Sets -> click on the set name(inside MC_resourceGroupName_AKSName_westeurope group) -> click Instances -> click on the instance name -> view Public IP address.

To get public IP via API look here.

For more information on Public IPs for VM NICs, see this document.

Kubernetes version starting 1.18.19, 1.19.11 and 1.20.7

Virtual Machines public IP is available directly in Kubernetes EXTERNAL-IP.

Next Steps

3.1.4 - Minikube

Follow these steps to create a Minikube cluster for your Code Blind install.

Installing Minikube

First, install Minikube, which may also require you to install a virtualisation solution, such as VirtualBox as well.

Starting Minikube

Minikube will need to be started with the supported version of Kubernetes that is supported with Code Blind, via the --kubernetes-version command line flag.

Optionally, we also recommend starting with an agones profile, using -p to keep this cluster separate from any other clusters you may have running with Minikube.

minikube start --kubernetes-version v1.27.6 -p agones

Check the official minikube start reference for more options that may be required for your platform of choice.

Known working drivers

Other operating systems and drivers may work, but at this stage have not been verified to work with UDP connections via Code Blind exposed ports.

Linux (amd64)

  • Docker (default)
  • kvm2

Mac (amd64)

  • Docker (default)
  • Hyperkit

Windows (amd64)

If you have successfully tested with other platforms and drivers, please click “edit this page” in the top right hand side and submit a pull request to let us know.

Local connection workarounds

Depending on your operating system and virtualization platform that you are using with Minikube, it may not be possible to connect directly to a GameServer hosted on Code Blind as you would on a cloud hosted Kubernetes cluster.

If you are unable to do so, the following workarounds are available, and may work on your platform:

minikube ip

Rather than using the published IP of a GameServer to connect, run minikube ip -p agones to get the local IP for the minikube node, and connect to that address.

Create a service

This would only be for local development, but if none of the other workarounds work, creating a Service for the GameServer you wish to connect to is a valid solution, to tunnel traffic to the appropriate GameServer container.

Use the following yaml:

apiVersion: v1
kind: Service
metadata:
  name: agones-gameserver
spec:
  type: LoadBalancer
  selector:
    agones.dev/gameserver: ${GAMESERVER_NAME}
  ports:
  - protocol: UDP
    port: 7000 # local port
    targetPort: ${GAMESERVER_CONTAINER_PORT}

Where ${GAMESERVER_NAME} is replaced with the GameServer you wish to connect to, and ${GAMESERVER_CONTAINER_PORT} is replaced with the container port GameServer exposes for connection.

Running minikube service list -p agones will show you the IP and port to connect to locally in the URL field.

To connect to a different GameServer, run kubectl edit service agones-gameserver and edit the ${GAMESERVER_NAME} value to point to the new GameServer instance and/or the ${GAMESERVER_CONTAINER_PORT} value as appropriate.

Use a different driver

If you cannot connect through the Serviceor use other workarounds, you may want to try a different minikube driver, and if that doesn’t work, connection via UDP may not be possible with minikube, and you may want to try either a different local Kubernetes tool or use a cloud hosted Kubernetes cluster.

Next Steps

3.2 - Install Code Blind

Install Code Blind in your existing Kubernetes cluster.

If you have not yet created a cluster, follow the instructions for the environment where you will be running Code Blind.

3.2.1 - Install Code Blind using YAML

We can install Code Blind to the cluster using an install.yaml file.

Installing Code Blind

Installing Code Blind using the pre-generated install.yaml file is the quickest, simplest way to get Code Blind up and running in your Kubernetes cluster:

kubectl create namespace agones-system
kubectl apply --server-side -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/install/yaml/install.yaml

You can also find the install.yaml in the latest agones-install zip from the releases archive.

Customizing your install

To change the configurable parameters in the install.yaml file, you can use helm template to generate a custom file locally without needing to use helm to install Code Blind into your cluster.

The following example sets the featureGates and generateTLS helm parameters and creates a customized install-custom.yaml file (note that the pull command was introduced in Helm version 3):

helm pull --untar https://agones.dev/chart/stable/agones-1.38.0.tgz && \
cd agones && \
helm template agones-manual --namespace agones-system  . \
  --set agones.controller.generateTLS=false \
  --set agones.allocator.generateTLS=false \
  --set agones.allocator.generateClientTLS=false \
  --set agones.crds.cleanupOnDelete=false \
  --set agones.featureGates="PlayerTracking=true" \
  > install-custom.yaml

Uninstalling Code Blind

To uninstall/delete the Code Blind deployment and delete agones-system namespace:

kubectl delete fleets --all --all-namespaces
kubectl delete gameservers --all --all-namespaces
kubectl delete -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/install/yaml/install.yaml
kubectl delete namespace agones-system

Note: It may take a couple of minutes until all resources described in install.yaml file are deleted.

Next Steps

3.2.2 - Install Code Blind using Helm

Install Code Blind on a Kubernetes cluster using the Helm package manager.

Prerequisites

Helm 3

Installing the Chart

To install the chart with the release name my-release using our stable helm repository:

helm repo add agones https://agones.dev/chart/stable
helm repo update
helm install my-release --namespace agones-system --create-namespace agones/agones

We recommend installing Code Blind in its own namespaces, such as agones-system as shown above. If you want to use a different namespace, you can use the helm --namespace parameter to specify.

When running in production, Code Blind should be scheduled on a dedicated pool of nodes, distinct from where Game Servers are scheduled for better isolation and resiliency. By default Code Blind prefers to be scheduled on nodes labeled with agones.dev/agones-system=true and tolerates node taint agones.dev/agones-system=true:NoExecute. If no dedicated nodes are available, Code Blind will run on regular nodes, but that’s not recommended for production use. For instructions on setting up a dedicated node pool for Code Blind, see the Code Blind installation instructions for your preferred environment.

The command deploys Code Blind on the Kubernetes cluster with the default configuration. The configuration section lists the parameters that can be configured during installation.

Namespaces

By default Code Blind is configured to work with game servers deployed in the default namespace. If you are planning to use another namespace you can configure Code Blind via the parameter gameservers.namespaces.

For example to use default and xbox namespaces:

kubectl create namespace xbox
helm install my-release agones/agones --set "gameservers.namespaces={default,xbox}" --namespace agones-system

If you want to add a new namespace afterward upgrade your release:

kubectl create namespace ps4
helm upgrade my-release agones/agones --reuse-values --set "gameservers.namespaces={default,xbox,ps4}" --namespace agones-system

Uninstalling the Chart

To uninstall/delete the my-release deployment:

helm uninstall my-release --namespace=agones-system

RBAC

By default, agones.rbacEnabled is set to true. This enables RBAC support in Code Blind and must be true if RBAC is enabled in your cluster.

The chart will take care of creating the required service accounts and roles for Code Blind.

If you have RBAC disabled, or to put it another way, ABAC enabled, you should set this value to false.

Configuration

The following tables lists the configurable parameters of the Code Blind chart and their default values.

General

ParameterDescriptionDefault
agones.featureGatesA URL query encoded string of Flags to enable/disable e.g. Example=true&OtherThing=false. Any value accepted by strconv.ParseBool(string) can be used as a boolean value``
agones.rbacEnabledCreates RBAC resources. Must be set for any cluster configured with RBACtrue
agones.registerWebhooksRegisters the webhooks used for the admission controllertrue
agones.registerApiServiceRegisters the apiservice(s) used for the Kubernetes API extensiontrue
agones.registerServiceAccountsAttempts to create service accounts for the controllerstrue
agones.createPriorityClassAttempts to create priority classes for the controllerstrue
agones.priorityClassNameName of the priority classes to createagones-system
agones.requireDedicatedNodesForces Code Blind system components to be scheduled on dedicated nodes, only applies to the GKE Standard without node auto-provisioningfalse

Custom Resource Definitions

ParameterDescriptionDefault
agones.crds.installInstall the CRDs with this chart. Useful to disable if you want to subchart (since crd-install hook is broken), so you can copy the CRDs into your own chart.true
agones.crds.cleanupOnDeleteRun the pre-delete hook to delete all GameServers and their backing Pods when deleting the helm chart, so that all CRDs can be removed on chart deletiontrue
agones.crds.cleanupJobTTLThe number of seconds for Kubernetes to delete the associated Job and Pods of the pre-delete hook after it completes, regardless if the Job is successful or not. Set to 0 to disable cleaning up the Job or the associated Pods.60

Metrics

ParameterDescriptionDefault
agones.metrics.prometheusServiceDiscoveryAdds annotations for Prometheus ServiceDiscovery (and also Strackdriver)true
agones.metrics.prometheusEnabledEnables controller metrics on port 8080 and path /metricstrue
agones.metrics.stackdriverEnabledEnables Stackdriver exporter of controller metricsfalse
agones.metrics.stackdriverProjectIDThis overrides the default gcp project id for use with stackdriver``
agones.metrics.stackdriverLabelsA set of default labels to add to all stackdriver metrics generated in form of key value pair (key=value,key2=value2). By default metadata are automatically added using Kubernetes API and GCP metadata enpoint.``
agones.metrics.serviceMonitor.intervalDefault scraping interval for ServiceMonitor30s

Service Accounts

ParameterDescriptionDefault
agones.serviceaccount.controller.nameService account name for the controlleragones-controller
agones.serviceaccount.controller.annotationsAnnotations added to the Code Blind controller service account{}
agones.serviceaccount.sdk.nameService account name for the sdkagones-sdk
agones.serviceaccount.sdk.annotationsA map of namespaces to maps of Annotations added to the Code Blind SDK service account for the specified namespaces{}
agones.serviceaccount.allocator.nameService account name for the allocatoragones-allocator
agones.serviceaccount.allocator.annotationsAnnotations added to the Code Blind allocator service account{}

Container Images

ParameterDescriptionDefault
agones.image.registryGlobal image registry for all the Code Blind system imagesus-docker.pkg.dev/agones-images/release
agones.image.tagGlobal image tag for all images1.38.0
agones.image.controller.nameImage name for the controlleragones-controller
agones.image.controller.pullPolicyImage pull policy for the controllerIfNotPresent
agones.image.controller.pullSecretImage pull secret for the controller, allocator, sdk and ping image. Should be created both in agones-system and default namespaces``
agones.image.sdk.nameImage name for the sdkagones-sdk
agones.image.sdk.tagImage tag for the sdkvalue of agones.image.tag
agones.image.sdk.cpuRequestThe cpu request for sdk server container30m
agones.image.sdk.cpuLimitThe cpu limit for the sdk server container0 (none)
agones.image.sdk.memoryRequestThe memory request for sdk server container0 (none)
agones.image.sdk.memoryLimitThe memory limit for the sdk server container0 (none)
agones.image.sdk.alwaysPullTells if the sdk image should always be pulledfalse
agones.image.ping.nameImage name for the ping serviceagones-ping
agones.image.ping.tagImage tag for the ping servicevalue of agones.image.tag
agones.image.ping.pullPolicyImage pull policy for the ping serviceIfNotPresent
agones.image.extensions.nameImage name for extensionsagones-extensions
agones.image.extensions.pullPolicyImage pull policy for extensionsIfNotPresent

Code Blind Controller

ParameterDescriptionDefault
agones.controller.replicasThe number of replicas to run in the agones-controller deployment.2
agones.controller.pdb.minAvailableDescription of the number of pods from that set that must still be available after the eviction, even in the absence of the evicted pod. Can be either an absolute number or a percentage. Mutually Exclusive with maxUnavailable1
agones.controller.pdb.maxUnavailableDescription of the number of pods from that set that can be unavailable after the eviction. It can be either an absolute number or a percentage Mutually Exclusive with minAvailable``
agones.controller.http.portPort to use for liveness probe service and metrics8080
agones.controller.healthCheck.initialDelaySecondsInitial delay before performing the first probe (in seconds)3
agones.controller.healthCheck.periodSecondsSeconds between every liveness probe (in seconds)3
agones.controller.healthCheck.failureThresholdNumber of times before giving up (in seconds)3
agones.controller.healthCheck.timeoutSecondsNumber of seconds after which the probe times out (in seconds)1
agones.controller.resourcesController resource requests/limit{}
agones.controller.generateTLSSet to true to generate TLS certificates or false to provide your own certificatestrue
agones.controller.tlsCertCustom TLS certificate provided as a string``
agones.controller.tlsKeyCustom TLS private key provided as a string``
agones.controller.nodeSelectorController node labels for pod assignment{}
agones.controller.tolerationsController toleration labels for pod assignment[]
agones.controller.affinityController affinity settings for pod assignment{}
agones.controller.annotationsAnnotations added to the Code Blind controller pods{}
agones.controller.numWorkersNumber of workers to spin per resource type100
agones.controller.apiServerQPSMaximum sustained queries per second that controller should be making against API Server400
agones.controller.apiServerQPSBurstMaximum burst queries per second that controller should be making against API Server500
agones.controller.logLevelCode Blind Controller Log level. Log only entries with that severity and aboveinfo
agones.controller.persistentLogsStore Code Blind controller logs in a temporary volume attached to a container for debuggingtrue
agones.controller.persistentLogsSizeLimitMBMaximum total size of all Code Blind container logs in MB10000
agones.controller.disableSecretDisables the creation of any allocator secrets. If true, you MUST provide the {agones.releaseName}-cert secrets before installation.false
agones.controller.customCertSecretPathRemap cert-manager path to server.crt and server.key{}
agones.controller.allocationApiService.annotationsAnnotations added to the Code Blind apiregistration{}
agones.controller.allocationApiService.disableCaBundleDisable ca-bundle so it can be injected by cert-managerfalse
agones.controller.validatingWebhook.annotationsAnnotations added to the Code Blind validating webhook{}
agones.controller.validatingWebhook.disableCaBundleDisable ca-bundle so it can be injected by cert-managerfalse
agones.controller.mutatingWebhook.annotationsAnnotations added to the Code Blind mutating webhook{}
agones.controller.mutatingWebhook.disableCaBundleDisable ca-bundle so it can be injected by cert-managerfalse
agones.controller.allocationBatchWaitTimeWait time between each allocation batch when performing allocations in controller mode500ms
agones.controller.topologySpreadConstraintsEnsures better resource utilization and high availability by evenly distributing Pods in the agones-system namespace{}

Ping Service

ParameterDescriptionDefault
agones.ping.installWhether to install the ping servicetrue
agones.ping.replicasThe number of replicas to run in the deployment2
agones.ping.http.exposeExpose the http ping service via a Servicetrue
agones.ping.http.responseThe string response returned from the http serviceok
agones.ping.http.portThe port to expose on the service80
agones.ping.http.serviceTypeThe Service Type of the HTTP ServiceLoadBalancer
agones.ping.http.nodePortStatic node port to use for HTTP ping service. (Only applies when agones.ping.http.serviceType is NodePort.)0
agones.ping.http.loadBalancerIPThe Load Balancer IP of the HTTP Service load balancer. Only works if the Kubernetes provider supports this option.``
agones.ping.http.loadBalancerSourceRangesThe Load Balancer SourceRanges of the HTTP Service load balancer. Only works if the Kubernetes provider supports this option.[]
agones.ping.http.annotationsAnnotations added to the Code Blind ping http service{}
agones.ping.udp.exposeExpose the udp ping service via a Servicetrue
agones.ping.udp.rateLimitNumber of UDP packets the ping service handles per instance, per second, per sender20
agones.ping.udp.portThe port to expose on the service80
agones.ping.udp.serviceTypeThe Service Type of the UDP ServiceLoadBalancer
agones.ping.udp.nodePortStatic node port to use for UDP ping service. (Only applies when agones.ping.udp.serviceType is NodePort.)0
agones.ping.udp.loadBalancerIPThe Load Balancer IP of the UDP Service load balancer. Only works if the Kubernetes provider supports this option.``
agones.ping.udp.loadBalancerSourceRangesThe Load Balancer SourceRanges of the UDP Service load balancer. Only works if the Kubernetes provider supports this option.[]
agones.ping.udp.annotationsAnnotations added to the Code Blind ping udp service{}
agones.ping.healthCheck.initialDelaySecondsInitial delay before performing the first probe (in seconds)3
agones.ping.healthCheck.periodSecondsSeconds between every liveness probe (in seconds)3
agones.ping.healthCheck.failureThresholdNumber of times before giving up (in seconds)3
agones.ping.healthCheck.timeoutSecondsNumber of seconds after which the probe times out (in seconds)1
agones.ping.resourcesPing pods resource requests/limit{}
agones.ping.nodeSelectorPing node labels for pod assignment{}
agones.ping.tolerationsPing toleration labels for pod assignment[]
agones.ping.affinityPing affinity settings for pod assignment{}
agones.ping.annotationsAnnotations added to the Code Blind ping pods{}
agones.ping.updateStrategyThe strategy to apply to the allocator deployment{}
agones.ping.pdb.enabledSet to true to enable the creation of a PodDisruptionBudget for the ping deploymentfalse
agones.ping.pdb.minAvailableDescription of the number of pods from that set that must still be available after the eviction, even in the absence of the evicted pod. Can be either an absolute number or a percentage. Mutually Exclusive with maxUnavailable1
agones.ping.pdb.maxUnavailableDescription of the number of pods from that set that can be unavailable after the eviction. It can be either an absolute number or a percentage Mutually Exclusive with minAvailable``
agones.ping.topologySpreadConstraintsEnsures better resource utilization and high availability by evenly distributing Pods in the agones-system namespace{}

Allocator Service

ParameterDescriptionDefault
agones.allocator.apiServerQPSMaximum sustained queries per second that an allocator should be making against API Server400
agones.allocator.apiServerQPSBurstMaximum burst queries per second that an allocator should be making against API Server500
agones.allocator.remoteAllocationTimeoutRemote allocation call timeout.10s
agones.allocator.totalRemoteAllocationTimeoutTotal remote allocation timeout including retries.30s
agones.allocator.logLevelCode Blind Allocator Log level. Log only entries with that severity and aboveinfo
agones.allocator.installWhether to install the allocator servicetrue
agones.allocator.replicasThe number of replicas to run in the deployment3
agones.allocator.service.nameService name for the allocatoragones-allocator
agones.allocator.service.serviceTypeThe Service Type of the HTTP ServiceLoadBalancer
agones.allocator.service.clusterIPThe Cluster IP of the Code Blind allocator. If you want Headless Service for Code Blind Allocator, you can set None to clusterIP.``
agones.allocator.service.loadBalancerIPThe Load Balancer IP of the Code Blind allocator load balancer. Only works if the Kubernetes provider supports this option.``
agones.allocator.service.loadBalancerSourceRangesThe Load Balancer SourceRanges of the Code Blind allocator load balancer. Only works if the Kubernetes provider supports this option.[]
agones.allocator.service.annotationsAnnotations added to the Code Blind allocator service{}
agones.allocator.service.http.enabledIf true the allocator service will respond to REST requeststrue
agones.allocator.service.http.appProtocolThe appProtocol to set on the Service for the http allocation port. If left blank, no value is set.``
agones.allocator.service.http.portThe port that is exposed externally by the allocator service for REST requests443
agones.allocator.service.http.portNameThe name of exposed porthttp
agones.allocator.service.http.targetPortThe port that is used by the allocator pod to listen for REST requests. Note that the allocator server cannot bind to low numbered ports.8443
agones.allocator.service.http.nodePortIf the ServiceType is set to “NodePort”, this is the NodePort that the allocator http service is exposed on.30000-32767
agones.allocator.service.grpc.enabledIf true the allocator service will respond to gRPC requeststrue
agones.allocator.service.grpc.portThe port that is exposed externally by the allocator service for gRPC requests443
agones.allocator.service.grpc.portNameThe name of exposed port``
agones.allocator.service.grpc.appProtocolThe appProtocol to set on the Service for the gRPC allocation port. If left blank, no value is set.``
agones.allocator.service.grpc.nodePortIf the ServiceType is set to “NodePort”, this is the NodePort that the allocator gRPC service is exposed on.30000-32767
agones.allocator.service.grpc.targetPortThe port that is used by the allocator pod to listen for gRPC requests. Note that the allocator server cannot bind to low numbered ports.8443
agones.allocator.generateClientTLSSet to true to generate client TLS certificates or false to provide certificates in certs/allocator/allocator-client.default/*true
agones.allocator.generateTLSSet to true to generate TLS certificates or false to provide your own certificatestrue
agones.allocator.disableMTLSTurns off client cert authentication for incoming connections to the allocator.false
agones.allocator.disableTLSTurns off TLS security for incoming connections to the allocator.false
agones.allocator.disableSecretCreationDisables the creation of any allocator secrets. If true, you MUST provide the allocator-tls, allocator-tls-ca, and allocator-client-ca secrets before installation.false
agones.allocator.tlsCertCustom TLS certificate provided as a string``
agones.allocator.tlsKeyCustom TLS private key provided as a string``
agones.allocator.clientCAsA map of secret key names to allowed client CA certificates provided as strings{}
agones.allocator.tolerationsAllocator toleration labels for pod assignment[]
agones.allocator.affinityAllocator affinity settings for pod assignment{}
agones.allocator.annotationsAnnotations added to the Code Blind allocator pods{}
agones.allocator.resourcesAllocator pods resource requests/limit{}
agones.allocator.labelsLabels Added to the Code Blind Allocator pods{}
agones.allocator.readiness.initialDelaySecondsInitial delay before performing the first probe (in seconds)3
agones.allocator.readiness.periodSecondsSeconds between every liveness probe (in seconds)3
agones.allocator.readiness.failureThresholdNumber of times before giving up (in seconds)3
agones.allocator.nodeSelectorAllocator node labels for pod assignment{}
agones.allocator.serviceMetrics.nameSecond Service name for the allocatoragones-allocator-metrics-service
agones.allocator.serviceMetrics.annotationsAnnotations added to the Code Blind allocator second Service{}
agones.allocator.serviceMetrics.http.portThe port that is exposed within cluster by the allocator service for http requests8080
agones.allocator.serviceMetrics.http.portNameThe name of exposed porthttp
agones.allocator.allocationBatchWaitTimeWait time between each allocation batch when performing allocations in allocator mode500ms
agones.allocator.updateStrategyThe strategy to apply to the ping deployment{}
agones.allocator.pdb.enabledSet to true to enable the creation of a PodDisruptionBudget for the allocator deploymentfalse
agones.allocator.pdb.minAvailableDescription of the number of pods from that set that must still be available after the eviction, even in the absence of the evicted pod. Can be either an absolute number or a percentage. Mutually Exclusive with maxUnavailable1
agones.allocator.pdb.maxUnavailableDescription of the number of pods from that set that can be unavailable after the eviction. It can be either an absolute number or a percentage. Mutually Exclusive with minAvailable``
agones.allocator.topologySpreadConstraintsEnsures better resource utilization and high availability by evenly distributing Pods in the agones-system namespace{}

Extensions

ParameterDescriptionDefault
agones.extensions.http.portPort to use for liveness probe service and metrics8080
agones.extensions.healthCheck.initialDelaySecondsInitial delay before performing the first probe (in seconds)3
agones.extensions.healthCheck.periodSecondsSeconds between every liveness probe (in seconds)3
agones.extensions.healthCheck.failureThresholdNumber of times before giving up (in seconds)3
agones.extensions.healthCheck.timeoutSecondsNumber of seconds after which the probe times out (in seconds)1
agones.extensions.resourcesExtensions resource requests/limit{}
agones.extensions.generateTLSSet to true to generate TLS certificates or false to provide your own certificatestrue
agones.extensions.tlsCertCustom TLS certificate provided as a string``
agones.extensions.tlsKeyCustom TLS private key provided as a string``
agones.extensions.nodeSelectorExtensions node labels for pod assignment{}
agones.extensions.tolerationsExtensions toleration labels for pod assignment[]
agones.extensions.affinityExtensions affinity settings for pod assignment{}
agones.extensions.annotationsAnnotations added to the Code Blind extensions pods{}
agones.extensions.numWorkersNumber of workers to spin per resource type100
agones.extensions.apiServerQPSMaximum sustained queries per second that extensions should be making against API Server400
agones.extensions.apiServerQPSBurstMaximum burst queries per second that extensions should be making against API Server500
agones.extensions.logLevelCode Blind Extensions Log level. Log only entries with that severity and aboveinfo
agones.extensions.persistentLogsStore Code Blind extensions logs in a temporary volume attached to a container for debuggingtrue
agones.extensions.persistentLogsSizeLimitMBMaximum total size of all Code Blind container logs in MB10000
agones.extensions.disableSecretDisables the creation of any allocator secrets. If true, you MUST provide the {agones.releaseName}-cert secrets before installation.false
agones.extensions.customCertSecretPathRemap cert-manager path to server.crt and server.key{}
agones.extensions.allocationApiService.annotationsAnnotations added to the Code Blind apiregistration{}
agones.extensions.allocationApiService.disableCaBundleDisable ca-bundle so it can be injected by cert-managerfalse
agones.extensions.validatingWebhook.annotationsAnnotations added to the Code Blind validating webhook{}
agones.extensions.validatingWebhook.disableCaBundleDisable ca-bundle so it can be injected by cert-managerfalse
agones.extensions.mutatingWebhook.annotationsAnnotations added to the Code Blind mutating webhook{}
agones.extensions.mutatingWebhook.disableCaBundleDisable ca-bundle so it can be injected by cert-managerfalse
agones.extensions.allocationBatchWaitTimeWait time between each allocation batch when performing allocations in controller mode500ms
agones.extensions.pdb.minAvailableDescription of the number of pods from that set that must still be available after the eviction, even in the absence of the evicted pod. Can be either an absolute number or a percentage. Mutually Exclusive with maxUnavailable1
agones.extensions.pdb.maxUnavailableDescription of the number of pods from that set that can be unavailable after the eviction. It can be either an absolute number or a percentage Mutually Exclusive with minAvailable``
agones.extensions.replicasThe number of replicas to run in the deployment2
agones.extensions.topologySpreadConstraintsEnsures better resource utilization and high availability by evenly distributing Pods in the agones-system namespace{}

GameServers

ParameterDescriptionDefault
gameservers.namespacesa list of namespaces you are planning to use to deploy game servers["default"]
gameservers.minPortMinimum port to use for dynamic port allocation7000
gameservers.maxPortMaximum port to use for dynamic port allocation8000
gameservers.podPreserveUnknownFieldsDisable field pruning and schema validation on the Pod template for a GameServer definitionfalse

Helm Installation

ParameterDescriptionDefault
helm.installTestsAdd an ability to run helm test agones to verify the installationfalse

Specify each parameter using the --set key=value[,key=value] argument to helm install. For example,

helm install my-release --namespace agones-system \
  --set gameservers.minPort=1000,gameservers.maxPort=5000 agones

The above command will deploy Code Blind controllers to agones-system namespace. Additionally Code Blind will use a dynamic GameServers’ port allocation range of 1000-5000.

Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example,

helm install my-release --namespace agones-system -f values.yaml agones/agones

Helm test

This test would create a GameServer resource and delete it afterwards.

Check the Code Blind installation by running the following command:

helm test my-release -n agones-system

You should see a successful output similar to this :

NAME: my-release
LAST DEPLOYED: Wed Mar 29 06:13:23 2023
NAMESPACE: agones-system
STATUS: deployed
REVISION: 4
TEST SUITE:     my-release-test
Last Started:   Wed Mar 29 06:17:52 2023
Last Completed: Wed Mar 29 06:18:10 2023
Phase:          Succeeded

Controller TLS Certificates

By default agones chart generates tls certificates used by the admission controller, while this is handy, it requires the agones controller to restart on each helm upgrade command.

Manual

For most use cases the controller would have required a restart anyway (eg: controller image updated). However if you really need to avoid restarts we suggest that you turn off tls automatic generation (agones.controller.generateTLS to false) and provide your own certificates (certs/server.crt,certs/server.key).

Cert-Manager

Another approach is to use cert-manager.io solution for cluster level certificate management.

In order to use the cert-manager solution, first install cert-manager on the cluster. Then, configure an Issuer/ClusterIssuer resource and last configure a Certificate resource to manage controller Secret. Make sure to configure the Certificate based on your system’s requirements, including the validity duration.

Here is an example of using a self-signed ClusterIssuer for configuring controller Secret where secret name is my-release-cert or {{ template "agones.fullname" . }}-cert:

#!/bin/bash
# Create a self-signed ClusterIssuer
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}
EOF

# Create a Certificate with IP for the my-release-cert )
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-release-agones-cert
  namespace: agones-system
spec:
  dnsNames:
    - agones-controller-service.agones-system.svc
  secretName: my-release-agones-cert
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
EOF

After the certificates are generated, we will want to inject caBundle into the controller and extensions webhook and disable the controller and extensions secret creation through the following values.yaml file.:

agones:
  controller:
    disableSecret: true
    customCertSecretPath:
    - key: ca.crt
      path: ca.crt
    - key: tls.crt
      path: server.crt
    - key: tls.key
      path: server.key
    allocationApiService:
      annotations:
        cert-manager.io/inject-ca-from: agones-system/my-release-agones-cert
      disableCaBundle: true
    validatingWebhook:
      annotations:
        cert-manager.io/inject-ca-from: agones-system/my-release-agones-cert
      disableCaBundle: true
    mutatingWebhook:
      annotations:
        cert-manager.io/inject-ca-from: agones-system/my-release-agones-cert
      disableCaBundle: true
  extensions:
    disableSecret: true
    customCertSecretPath:
    - key: ca.crt
      path: ca.crt
    - key: tls.crt
      path: server.crt
    - key: tls.key
      path: server.key
    allocationApiService:
      annotations:
        cert-manager.io/inject-ca-from: agones-system/my-release-agones-cert
      disableCaBundle: true
    validatingWebhook:
      annotations:
        cert-manager.io/inject-ca-from: agones-system/my-release-agones-cert
      disableCaBundle: true
    mutatingWebhook:
      annotations:
        cert-manager.io/inject-ca-from: agones-system/my-release-agones-cert
      disableCaBundle: true

After copying the above yaml into a values.yaml file, use below command to install Code Blind:

helm install my-release --namespace agones-system --create-namespace --values values.yaml agones/agones

Reserved Allocator Load Balancer IP

In order to reuse the existing load balancer IP on upgrade or install the agones-allocator service as a LoadBalancer using a reserved static IP, a user can specify the load balancer’s IP with the agones.allocator.http.loadBalancerIP helm configuration parameter value. By setting the loadBalancerIP value:

  1. The LoadBalancer is created with the specified IP, if supported by the cloud provider.
  2. A self-signed server TLS certificate is generated for the IP, used by the agones-allocator service.

Next Steps

3.3 - Deploy Kubernetes cluster and install Code Blind using Terraform

Install a Kubernetes cluster and Code Blind declaratively using Terraform.

Prerequisites

  • Terraform v1.0.8
  • Access to the the Kubernetes hosting provider you are using (e.g. gcloud, awscli, or az utility installed)
  • Git

3.3.1 - Installing Code Blind on Google Kubernetes Engine using Terraform

You can use Terraform to provision a GKE cluster and install Code Blind on it.

Before you begin

Take the following steps to enable the Kubernetes Engine API:

  1. Visit the Kubernetes Engine page in the Google Cloud Platform Console.
  2. Create or select a project.
  3. Wait for the API and related services to be enabled. This can take several minutes.
  4. Enable billing for your project.
  • If you are not an existing GCP user, you may be able to enroll for a $300 US Free Trial credit.

Choosing a shell

To complete this quickstart, we can use either Google Cloud Shell or a local shell.

Google Cloud Shell is a shell environment for managing resources hosted on Google Cloud Platform (GCP). Cloud Shell comes preinstalled with the gcloud and kubectl command-line tools. gcloud provides the primary command-line interface for GCP, and kubectl provides the command-line interface for running commands against Kubernetes clusters.

If you prefer using your local shell, you must install the gcloud and kubectl command-line tools in your environment.

Cloud shell

To launch Cloud Shell, perform the following steps:

  1. Go to Google Cloud Platform Console
  2. From the top-right corner of the console, click the Activate Google Cloud Shell button: cloud shell
  3. A Cloud Shell session opens inside a frame at the bottom of the console. Use this shell to run gcloud and kubectl commands.
  4. Set a compute zone in your geographical region with the following command. The compute zone will be something like us-west1-a. A full list can be found here.
    gcloud config set compute/zone [COMPUTE_ZONE]
    

Local shell

To install gcloud and kubectl, perform the following steps:

  1. Install the Google Cloud SDK, which includes the gcloud command-line tool.
  2. Initialize some default configuration by running the following command.
    • When asked Do you want to configure a default Compute Region and Zone? (Y/n)?, enter Y and choose a zone in your geographical region of choice.
    gcloud init
    
  3. Install the kubectl command-line tool by running the following command:
    gcloud components install kubectl
    

Installation

An example configuration can be found here: Terraform configuration with Code Blind submodule.

Copy this file into a local directory where you will execute the terraform commands.

The GKE cluster created from the example configuration will contain 3 Node Pools:

  • "default" node pool with "game-server" tag, containing 4 nodes.
  • "agones-system" node pool for Code Blind Controller.
  • "agones-metrics" for monitoring and metrics collecting purpose.

Configurable parameters:

  • project - your Google Cloud Project ID (required)
  • name - the name of the GKE cluster (default is “agones-terraform-example”)
  • agones_version - the version of agones to install (an empty string, which is the default, is the latest version from the Helm repository)
  • machine_type - machine type for hosting game servers (default is “e2-standard-4”)
  • node_count - count of game server nodes for the default node pool (default is “4”)
  • enable_image_streaming - whether or not to enable image streaming for the "default" node pool (default is true)
  • zone - (Deprecated, use location) the name of the zone you want your cluster to be created in (default is “us-west1-c”)
  • network - the name of the VPC network you want your cluster and firewall rules to be connected to (default is “default”)
  • subnetwork - the name of the subnetwork in which the cluster’s instances are launched. (required when using non default network)
  • log_level - possible values: Fatal, Error, Warn, Info, Debug (default is “info”)
  • feature_gates - a list of alpha and beta version features to enable. For example, “PlayerTracking=true&ContainerPortAllocation=true”
  • gameserver_minPort - the lower bound of the port range which gameservers will listen on (default is “7000”)
  • gameserver_maxPort - the upper bound of the port range which gameservers will listen on (default is “8000”)
  • gameserver_namespaces - a list of namespaces which will be used to run gameservers (default is ["default"]). For example ["default", "xbox-gameservers", "mobile-gameservers"]
  • force_update - whether or not to force the replacement/update of resource (default is true, false may be required to prevent immutability errors when updating the configuration)
  • location - the name of the location you want your cluster to be created in (default is “us-west1-c”)
  • autoscale - whether you want to enable autoscale for the gameserver nodepool (default is false)
  • min_node_count - the minimum number of nodes for a nodepool when autoscale is enabled (default is “1”)
  • max_node_count - the maximum number of nodes for a nodepool when autoscale is enabled (default is “5”)

Creating the cluster

In the directory where you created module.tf, run:

terraform init

This will cause terraform to clone the Code Blind repository and use the ./install/terraform folder as the starting point of the Code Blind submodule, which contains all necessary Terraform configuration files.

Next, make sure that you can authenticate using gcloud:

gcloud auth application-default login

Option 1: Creating the cluster in the default VPC

To create your GKE cluster in the default VPC just specify the project variable.

terraform apply -var project="<YOUR_GCP_ProjectID>"

Option 2: Creating the cluster in a custom VPC

To create the cluster in a custom VPC you must specify the project, network and subnetwork variables.

terraform apply -var project="<YOUR_GCP_ProjectID>" -var network="<YOUR_NETWORK_NAME>" -var subnetwork="<YOUR_SUBNETWORK_NAME>"

To verify that the cluster was created successfully, set up your kubectl credentials:

gcloud container clusters get-credentials --zone us-west1-c agones-terraform-example

Then check that you have access to the Kubernetes cluster:

kubectl get nodes

You should have 6 nodes in Ready state.

Uninstall the Code Blind and delete GKE cluster

To delete all resources provisioned by Terraform:

terraform destroy -var project="<YOUR_GCP_ProjectID>"

Next Steps

3.3.2 - Installing Code Blind on AWS Elastic Kubernetes Service using Terraform

You can use Terraform to provision an EKS cluster and install Code Blind on it.

Installation

You can use Terraform to provision your Amazon EKS (Elastic Kubernetes Service) cluster and install Code Blind on it using the Helm Terraform provider.

An example of the EKS submodule config file can be found here: Terraform configuration with Code Blind submodule

Copy this file into a separate folder.

Configure your AWS CLI tool CLI configure:

aws configure

Initialise your terraform:

terraform init

Creating Cluster

By editing modules.tf you can change the parameters that you need to. For instance, the - machine_type variable.

Configurable parameters:

  • cluster_name - the name of the EKS cluster (default is “agones-terraform-example”)
  • agones_version - the version of agones to install (an empty string, which is the default, is the latest version from the Helm repository)
  • machine_type - EC2 instance type for hosting game servers (default is “t2.large”)
  • region - the location of the cluster (default is “us-west-2”)
  • node_count - count of game server nodes for the default node pool (default is “4”)
  • log_level - possible values: Fatal, Error, Warn, Info, Debug (default is “info”)
  • feature_gates - a list of alpha and beta version features to enable. For example, “PlayerTracking=true&ContainerPortAllocation=true”
  • gameserver_minPort - the lower bound of the port range which gameservers will listen on (default is “7000”)
  • gameserver_maxPort - the upper bound of the port range which gameservers will listen on (default is “8000”)
  • gameserver_namespaces - a list of namespaces which will be used to run gameservers (default is ["default"]). For example ["default", "xbox-gameservers", "mobile-gameservers"]
  • force_update - whether or not to force the replacement/update of resource (default is true, false may be required to prevent immutability errors when updating the configuration)

Now you can create an EKS cluster and deploy Code Blind on EKS:

terraform apply [-var agones_version="1.38.0"]

After deploying the cluster with Code Blind, you can get or update your kubeconfig by using:

aws eks --region us-west-2 update-kubeconfig --name agones-cluster

With the following output:

Added new context arn:aws:eks:us-west-2:601646756426:cluster/agones-cluster to /Users/user/.kube/config

Switch kubectl context to the recently created one:

kubectl config use-context arn:aws:eks:us-west-2:601646756426:cluster/agones-cluster

Check that you are authenticated against the recently created Kubernetes cluster:

kubectl get nodes

Uninstall the Code Blind and delete EKS cluster

Run the following commands to delete all Terraform provisioned resources:

terraform destroy -target module.helm_agones.helm_release.agones -auto-approve && sleep 60
terraform destroy

3.3.3 - Installing Code Blind on Azure Kubernetes Service using Terraform

You can use Terraform to provision an AKS cluster and install Code Blind on it.

Installation

Install az utility by following these instructions.

The example of AKS submodule configuration could be found here: Terraform configuration with Code Blind submodule

Copy module.tf file into a separate folder.

Log in to Azure CLI:

az login

Configure your terraform:

terraform init

Create a service principal and configure its access to Azure resources:

az ad sp create-for-rbac

Now you can deploy your cluster (use values from the above command output):

terraform apply -var client_id="<appId>" -var client_secret="<password>"

Once you created all resources on AKS you can get the credentials so that you can use kubectl to configure your cluster:

az aks get-credentials --resource-group agonesRG --name test-cluster

Check that you have access to the Kubernetes cluster:

kubectl get nodes

Configurable parameters:

  • log_level - possible values: Fatal, Error, Warn, Info, Debug (default is “info”)
  • cluster_name - the name of the AKS cluster (default is “agones-terraform-example”)
  • agones_version - the version of agones to install (an empty string, which is the default, is the latest version from the Helm repository)
  • machine_type - node machine type for hosting game servers (default is “Standard_D2_v2”)
  • disk_size - disk size of the node
  • region - the location of the cluster
  • node_count - count of game server nodes for the default node pool (default is “4”)
  • feature_gates - a list of alpha and beta version features to enable. For example, “PlayerTracking=true&ContainerPortAllocation=true”
  • gameserver_minPort - the lower bound of the port range which gameservers will listen on (default is “7000”)
  • gameserver_maxPort - the upper bound of the port range which gameservers will listen on (default is “8000”)
  • gameserver_namespaces - a list of namespaces which will be used to run gameservers (default is ["default"]). For example ["default", "xbox-gameservers", "mobile-gameservers"]
  • force_update - whether or not to force the replacement/update of resource (default is true, false may be required to prevent immutability errors when updating the configuration)

Uninstall the Code Blind and delete AKS cluster

Run next command to delete all Terraform provisioned resources:

terraform destroy

Reference

Details on how you can authenticate your AKS terraform provider using official instructions.

Next Steps

3.4 - Confirming Code Blind Installation

Verify Code Blind is installed and has started successfully.

To confirm Code Blind is up and running, run the following command:

kubectl describe --namespace agones-system pods

It should describe six pods created in the agones-system namespace, with no error messages or status. All Conditions sections should look like this:

Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True

All this pods should be in a RUNNING state:

kubectl get pods --namespace agones-system
NAME                                 READY   STATUS    RESTARTS   AGE
agones-allocator-5c988b7b8d-cgtbs    1/1     Running   0          8m47s
agones-allocator-5c988b7b8d-hhhr5    1/1     Running   0          8m47s
agones-allocator-5c988b7b8d-pv577    1/1     Running   0          8m47s
agones-controller-7db45966db-56l66   1/1     Running   0          8m44s
agones-ping-84c64f6c9d-bdlzh         1/1     Running   0          8m37s
agones-ping-84c64f6c9d-sjgzz         1/1     Running   0          8m47s

That’s it!

Now with Code Blind installed, you can utilise its Custom Resource Definitions to create resources of type GameServer, Fleet and more!

What’s next

3.5 - Upgrading Code Blind and Kubernetes

Strategies and techniques for managing Code Blind and Kubernetes upgrades in a safe manner.

Upgrading Code Blind

The following are strategies for safely upgrading Code Blind from one version to another. They may require adjustment to your particular game architecture but should provide a solid foundation for updating Code Blind safely.

The recommended approach is to use multiple clusters, such that the upgrade can be tested gradually with production load and easily rolled back if the need arises.

Upgrading Code Blind: Multiple Clusters

We essentially want to transition our GameServer allocations from a cluster with the old version of Code Blind, to a cluster with the upgraded version of Code Blind while ensuring nothing surprising happens during this process.

This also allows easy rollback to the previous infrastructure that we already know to be working in production, with minimal interruptions to player experience.

The following are steps to implement this:

  1. Create a new cluster of the same size or smaller as the current cluster.
  2. Install the new version of Code Blind on the new cluster.
  3. Deploy the same set of Fleets, GameServers and FleetAutoscalers from the old cluster into the new cluster.
  4. With your matchmaker, start sending a small percentage of your matched players’ game sessions to the new cluster.
  5. Assuming everything is working successfully on the new cluster, slowly increase the percentage of matched sessions to the new cluster, until you reach 100%.
  6. Once you are comfortable with the stability of the new cluster with the new Code Blind version, shut down the old cluster.
  7. Congratulations - you have now upgraded to a new version of Code Blind! 👍

Upgrading Code Blind: Single Cluster

If you are upgrading a single cluster, we recommend creating a maintenance window, in which your game goes offline for the period of your upgrade, as there will be a short period in which Code Blind will be non-responsive during the upgrade.

Installation with install.yaml

If you installed Code Blind with install.yaml, then you will need to delete the previous installation of Code Blind before upgrading to the new version, as we need to remove all of Code Blind before installing the new version.

  1. Start your maintenance window.
  2. Delete the current set of Fleets, GameServers and FleetAutoscalers in your cluster.
  3. Make sure to delete the same version of Code Blind that was previously installed, for example: kubectl delete -f https://raw.githubusercontent.com/googleforgames/agones/<old-release-version>/install/yaml/install.yaml
  4. Install Code Blind with install.yaml.
  5. Deploy the same set of Fleets, GameServers and FleetAutoscalers back into the cluster.
  6. Run any other tests to ensure the Code Blind installation is working as expected.
  7. Close your maintenance window.
  8. Congratulations - you have now upgraded to a new version of Code Blind! 👍

Installation with Helm

Helm features capabilities for upgrading to newer versions of Code Blind without having to uninstall Code Blind completely.

For details on how to use Helm for upgrades, see the helm upgrade documentation.

Given the above, the steps for upgrade are simpler:

  1. Start your maintenance window.
  2. Delete the current set of Fleets, GameServers and FleetAutoscalers in your cluster.
  3. Run helm upgrade with the appropriate arguments, such a --version, for your specific upgrade
  4. Deploy the same set of Fleets, GameServers and FleetAutoscalers back into the cluster.
  5. Run any other tests to ensure the Code Blind installation is working as expected.
  6. Close your maintenance window.
  7. Congratulations - you have now upgraded to a new version of Code Blind! 👍

Upgrading Kubernetes

The following are strategies for safely upgrading the underlying Kubernetes cluster from one version to another. They may require adjustment to your particular game architecture but should provide a solid foundation for updating your cluster safely.

The recommended approach is to use multiple clusters, such that the upgrade can be tested gradually with production load and easily rolled back if the need arises.

Code Blind has multiple supported Kubernetes versions for each version. You can stick with a minor Kubernetes version until it is not supported by Code Blind, but it is recommended to do supported minor (e.g. 1.12.1 ➡ 1.13.2) Kubernetes version upgrades at the same time as a matching Code Blind upgrades.

Patch upgrades (e.g. 1.12.1 ➡ 1.12.3) within the same minor version of Kubernetes can be done at any time.

Multiple Clusters

This process is very similar to the Upgrading Code Blind: Multiple Clusters approach above.

We essentially want to transition our GameServer allocations from a cluster with the old version of Kubernetes, to a cluster with the upgraded version of Kubernetes while ensuring nothing surprising happens during this process.

This also allows easy rollback to the previous infrastructure that we already know to be working in production, with minimal interruptions to player experience.

The following are steps to implement this:

  1. Create a new cluster of the same size or smaller as the current cluster, with the new version of Kubernetes
  2. Install the same version of Code Blind on the new cluster, as you have on the previous cluster.
  3. Deploy the same set of Fleets and/or GameServers from the old cluster into the new cluster.
  4. With your matchmaker, start sending a small percentage of your matched players’ game sessions to the new cluster.
  5. Assuming everything is working successfully on the new cluster, slowly increase the percentage of matched sessions to the new cluster, until you reach 100%.
  6. Once you are comfortable with the stability of the new cluster with the new Kubernetes version, shut down the old cluster.
  7. Congratulations - you have now upgraded to a new version of Kubernetes! 👍

Single Cluster

If you are upgrading a single cluster, we recommend creating a maintenance window, in which your game goes offline for the period of your upgrade, as there will be a short period in which Code Blind will be non-responsive during the node upgrades.

  1. Start your maintenance window.
  2. Scale your Fleets down to 0 and/or delete your GameServers. This is a good safety measure so there aren’t race conditions between the Code Blind controller being recreated and GameServers being deleted doesn’t occur, and GameServers can end up stuck in erroneous states.
  3. Start and complete your control plane upgrade(s).
  4. Start and complete your node upgrades.
  5. Scale your Fleets back up and/or recreate your GameServers.
  6. Run any other tests to ensure the Code Blind installation is still working as expected.
  7. Close your maintenance window.
  8. Congratulations - you have now upgraded to a new version of Kubernetes! 👍

4 - Getting Started

Quickstarts for getting up and running with Code Blind

4.1 - Quickstart: Create a Game Server

This guide covers how you can quickly get started using Code Blind to create GameServers.

Prerequisites

The following prerequisites are required to create a GameServer:

  1. A Kubernetes cluster with the UDP port range 7000-8000 open on each node.
  2. Code Blind controller installed in the targeted cluster
  3. kubectl properly configured
  4. Netcat which is already installed on most Linux/macOS distributions, for windows you can use WSL.

If you don’t have a Kubernetes cluster you can follow these instructions to create a cluster on Google Kubernetes Engine (GKE), Minikube or Azure Kubernetes Service (AKS), and install Code Blind.

For the purpose of this guide we’re going to use the simple-game-server example as the GameServer container. This example is a very simple UDP server written in Go. Don’t hesitate to look at the code of this example for more information.

Objectives

  • Create a GameServer in Kubernetes using Code Blind custom resource.
  • Get information about the GameServer such as IP address, port and state.
  • Connect to the GameServer.

1. Create a GameServer

Let’s create a GameServer using the following command :

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserver.yaml

You should see a successful output similar to this :

gameserver.agones.dev/simple-game-server-4ss4j created

This has created a GameServer record inside Kubernetes, which has also created a backing Pod to run our simple udp game server code in. If you want to see all your running GameServers you can run:

kubectl get gameservers

It should look something like this:

NAME                       STATE     ADDRESS       PORT   NODE     AGE
simple-game-server-7pjrq   Ready   35.233.183.43   7190   agones   3m

You can also see the Pod that got created by running kubectl get pods, the Pod will be prefixed by simple-game-server.

NAME                        READY     STATUS    RESTARTS   AGE
simple-game-server-7pjrq    2/2       Running   0          5m

As you can see above it says READY: 2/2 this means there are two containers running in this Pod, this is because Code Blind injected the SDK sidecar for readiness and health checking of your Game Server.

For the full details of the YAML file head to the GameServer Specification Guide

2. Fetch the GameServer Status

Let’s wait for the GameServer state to become Ready. You can use the watch tool to see the state change. If your operating system does not have watch, manually run kubectl describe gameserver until the state changes.

watch kubectl describe gameserver
Name:         simple-game-server-7pjrq
Namespace:    default
Labels:       <none>
Annotations:  agones.dev/sdk-version: 0.9.0-764fa53
API Version:  agones.dev/v1
Kind:         GameServer
Metadata:
  Creation Timestamp:  2019-02-27T15:06:20Z
  Finalizers:
    agones.dev
  Generate Name:     simple-game-server-
  Generation:        1
  Resource Version:  30377
  Self Link:         /apis/agones.dev/v1/namespaces/default/gameservers/simple-game-server-7pjrq
  UID:               3d7ac3e1-3aa1-11e9-a4f5-42010a8a0019
Spec:
  Container:  simple-game-server
  Health:
    Failure Threshold:      3
    Initial Delay Seconds:  5
    Period Seconds:         5
  Ports:
    Container Port:  7654
    Host Port:       7190
    Name:            default
    Port Policy:     Dynamic
    Protocol:        UDP
  Scheduling:        Packed
  Template:
    Metadata:
      Creation Timestamp:  <nil>
    Spec:
      Containers:
        Image:  us-docker.pkg.dev/codeblind/examples/simple-server:0.27
        Name:   simple-game-server
        Resources:
          Limits:
            Cpu:     20m
            Memory:  32Mi
          Requests:
            Cpu:     20m
            Memory:  32Mi
Status:
  Address:    35.233.183.43
  Node Name:  agones
  Ports:
    Name:  default
    Port:  7190
  State:   Ready
Events:
  Type    Reason          Age   From                   Message
  ----    ------          ----  ----                   -------
  Normal  PortAllocation  34s   gameserver-controller  Port allocated
  Normal  Creating        34s   gameserver-controller  Pod simple-game-server-7pjrq created
  Normal  Scheduled       34s   gameserver-controller  Address and port populated
  Normal  Ready           27s   gameserver-controller  SDK.Ready() executed

If you look towards the bottom, you can see there is a Status > State value. We are waiting for it to move to Ready, which means that the game server is ready to accept connections.

You might also be interested to see the Events section, which outlines when various lifecycle events of the GameServer occur. We can also see when the GameServer is ready on the event stream as well - at which time the Status > Address and Status > Ports > Port have also been populated, letting us know what IP and port our client can now connect to!

Let’s retrieve the IP address and the allocated port of your Game Server :

kubectl get gs

This should output your Game Server IP address and ports, eg:

NAME                       STATE   ADDRESS         PORT   NODE     AGE
simple-game-server-7pjrq   Ready   35.233.183.43   7190   agones   4m

3. Connect to the GameServer

You can now communicate with the Game Server :

nc -u {IP} {PORT}
Hello World !
ACK: Hello World !
EXIT

You can finally type EXIT which tells the SDK to run the Shutdown command, and therefore shuts down the GameServer.

If you run kubectl describe gameserver again - either the GameServer will be gone completely, or it will be in Shutdown state, on the way to being deleted.

Next Step

If you want to use your own GameServer container make sure you have properly integrated the Code Blind SDK.

4.2 - Quickstart: Create a Game Server Fleet

This guide covers how you can quickly get started using Code Blind to create a Fleet of warm GameServers ready for you to allocate out of and play on!

Prerequisites

The following prerequisites are required to create a GameServer:

  1. A Kubernetes cluster with the UDP port range 7000-8000 open on each node.
  2. Code Blind controller installed in the targeted cluster
  3. kubectl properly configured
  4. Netcat which is already installed on most Linux/macOS distributions, for windows you can use WSL.

If you don’t have a Kubernetes cluster you can follow these instructions to create a cluster on Google Kubernetes Engine (GKE), Minikube or Azure Kubernetes Service (AKS), and install Code Blind.

For the purpose of this guide we’re going to use the simple-game-server example as the GameServer container. This example is a very simple UDP server written in Go. Don’t hesitate to look at the code of this example for more information.

While not required, you may wish to go through the Create a Game Server quickstart before this one.

Objectives

  • Create a Fleet in Kubernetes using an Code Blind custom resource.
  • Scale the Fleet up from its initial configuration.
  • Request a GameServer allocation from the Fleet to play on.
  • Connect to the allocated GameServer.
  • Deploy a new GameServer configuration to the Fleet.

1. Create a Fleet

Let’s create a Fleet using the following command:

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/fleet.yaml

You should see a successful output similar to this :

fleet.agones.dev/simple-game-server created

This has created a Fleet record inside Kubernetes, which in turn creates two warm GameServers that are available to be allocated for a game session.

kubectl get fleet

It should look something like this:

NAME                 SCHEDULING   DESIRED   CURRENT   ALLOCATED   READY     AGE
simple-game-server   Packed       2         3         0           2         9m

You can also see the GameServers that have been created by the Fleet by running kubectl get gameservers, the GameServer will be prefixed by simple-game-server.

NAME                             STATE     ADDRESS            PORT   NODE      AGE
simple-game-server-llg4x-rx6rc   Ready     192.168.122.205    7752   minikube   9m
simple-game-server-llg4x-v6g2r   Ready     192.168.122.205    7623   minikube   9m

For the full details of the YAML file head to the Fleet Specification Guide

2. Fetch the Fleet status

Let’s wait for the two GameServers to become ready.

watch kubectl describe fleet simple-game-server
Name:         simple-game-server
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"agones.dev/v1","kind":"Fleet","metadata":{"annotations":{},"name":"simple-game-server","namespace":"default"},"spec":{"replicas":2,...
API Version:  agones.dev/v1
Kind:         Fleet
Metadata:
  Cluster Name:
  Creation Timestamp:  2018-07-01T18:55:35Z
  Generation:          1
  Resource Version:    24685
  Self Link:           /apis/agones.dev/v1/namespaces/default/fleets/simple-game-server
  UID:                 56710a91-7d60-11e8-b2dd-08002703ef08
Spec:
  Replicas:  2
  Strategy:
    Rolling Update:
      Max Surge:        25%
      Max Unavailable:  25%
    Type:               RollingUpdate
  Template:
    Metadata:
      Creation Timestamp:  <nil>
    Spec:
      Health:
      Ports:
        Container Port:  7654
        Name:            default
        Port Policy:     Dynamic
      Template:
        Metadata:
          Creation Timestamp:  <nil>
        Spec:
          Containers:
            Image:  us-docker.pkg.dev/codeblind/examples/simple-server:0.27
            Name:   simple-game-server
            Resources:
Status:
  Allocated Replicas:  0
  Ready Replicas:      2
  Replicas:            2
Events:
  Type    Reason                 Age   From              Message
  ----    ------                 ----  ----              -------
  Normal  CreatingGameServerSet  13s   fleet-controller  Created GameServerSet simple-game-server-wlqnd

If you look towards the bottom, you can see there is a section of Status > Ready Replicas which will tell you how many GameServers are currently in a Ready state. After a short period, there should be 2 Ready Replicas.

3. Scale up the Fleet

Let’s scale up the Fleet from 2 replicates to 5.

Run kubectl scale fleet simple-game-server --replicas=5 to change Replicas count from 2 to 5.

If we now run kubectl get gameservers we should see 5 GameServers prefixed by simple-game-server.

NAME                             STATE    ADDRESS           PORT    NODE       AGE
simple-game-server-sdhzn-kcmh6   Ready    192.168.122.205   7191    minikube   52m
simple-game-server-sdhzn-pdpk5   Ready    192.168.122.205   7752    minikube   53m
simple-game-server-sdhzn-r4d6x   Ready    192.168.122.205   7623    minikube   52m
simple-game-server-sdhzn-wng5k   Ready    192.168.122.205   7709    minikube   53m
simple-game-server-sdhzn-wnhsw   Ready    192.168.122.205   7478    minikube   52m

4. Allocate a Game Server from the Fleet

Since we have a fleet of warm gameservers, we need a way to request one of them for usage, and mark that it has players accessing it (and therefore, it should not be deleted until they are finished with it).

We can do the allocation of a GameServer for usage through a GameServerAllocation, which will both return to us the details of a GameServer (assuming one is available), and also move it to the Allocated state, which demarcates that it has players on it, and should not be removed until SDK.Shutdown() is called, or it is manually deleted.

It is worth noting that there is nothing specific that ties a GameServerAllocation to a fleet. A GameServerAllocation uses a label selector to determine what group of GameServers it will attempt to allocate out of. That being said, a Fleet and GameServerAllocation are often used in conjunction.

This example uses the label selector to specifically target the simple-game-server fleet that we just created.

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserverallocation.yaml -o yaml

For the full details of the YAML file head to the GameServerAllocation Specification Guide

You should get back a response that looks like the following:

apiVersion: allocation.agones.dev/v1
kind: GameServerAllocation
metadata:
  creationTimestamp: 2019-02-19T02:13:12Z
  name: simple-game-server-dph9b-hfk24
  namespace: default
spec:
  metadata: {}
  required:
    matchLabels:
      agones.dev/fleet: simple-game-server
  scheduling: Packed
status:
  address: 192.168.122.152
  gameServerName: simple-game-server-dph9b-hfk24
  nodeName: minikube
  ports:
  - name: default
    port: 7714
  state: Allocated

If you look at the status section, there are several things to take note of. The state value will tell if a GameServer was allocated or not. If a GameServer could not be found, this will be set to UnAllocated. If there are too many concurrent requests overwhelmed the system, state will be set to Contention even though there are available GameServers.

However, we see that the status.state value was set to Allocated. This means you have been successfully allocated a GameServer out of the fleet, and you can now connect your players to it!

You can see various immutable details of the GameServer in the status - the address, ports and the name of the GameServer, in case you want to use it to retrieve more details.

We can also check to see how many GameServers you have Allocated vs Ready with the following command (“gs” is shorthand for “gameserver”).

kubectl get gs

This will get you a list of all the current GameServers and their Status.State.

NAME                             STATE       ADDRESS           PORT   NODE      AGE
simple-game-server-sdhzn-kcmh6   Ready       192.168.122.205   7191   minikube  52m
simple-game-server-sdhzn-pdpk5   Ready       192.168.122.205   7752   minikube  53m
simple-game-server-sdhzn-r4d6x   Allocated   192.168.122.205   7623   minikube  52m
simple-game-server-sdhzn-wng5k   Ready       192.168.122.205   7709   minikube  53m
simple-game-server-sdhzn-wnhsw   Ready       192.168.122.205   7478   minikube  52m

A handy trick for checking to see how many GameServers you have Allocated vs Ready, run the following:

kubectl get gs

This will get you a list of all the current GameServers and their Status > State.

NAME                             STATE       ADDRESS          PORT   NODE        AGE
simple-game-server-tfqn7-c9tqz   Ready       192.168.39.150   7136   minikube    52m
simple-game-server-tfqn7-g8fhq   Allocated   192.168.39.150   7148   minikube    53m
simple-game-server-tfqn7-p8wnl   Ready       192.168.39.150   7453   minikube    52m
simple-game-server-tfqn7-t6bwp   Ready       192.168.39.150   7228   minikube    53m
simple-game-server-tfqn7-wkb7b   Ready       192.168.39.150   7226   minikube    52m

5. Scale down the Fleet

Not only can we scale our fleet up, but we can scale it down as well.

The nice thing about Code Blind is that it is smart enough to know when GameServers have been moved to Allocated and will automatically leave them running on scale down – as we assume that players are playing on this game server, and we shouldn’t disconnect them!

Let’s scale down our Fleet to 0 (yep! you can do that!), and watch what happens.

Run kubectl scale fleet simple-game-server --replicas=0 to change Replicas count from 5 to 0.

It may take a moment for all the GameServers to shut down, so let’s watch them all and see what happens:

watch kubectl get gs

Eventually, one by one they will be removed from the list, and you should simply see:

NAME                             STATUS      ADDRESS          PORT    NODE       AGE
simple-game-server-tfqn7-g8fhq   Allocated   192.168.39.150   7148    minikube   55m

That lone Allocated GameServer is left all alone, but still running!

If you would like, try editing the Fleet configuration replicas field and watch the list of GameServers grow and shrink.

6. Connect to the GameServer

Since we’ve only got one allocation, we’ll just grab the details of the IP and port of the only allocated GameServer:

kubectl get gameservers | grep Allocated | awk '{print $3":"$4 }'

This should output your Game Server IP address and port. (eg 10.130.65.208:7936)

You can now communicate with the GameServer:

nc -u {IP} {PORT}
Hello World !
ACK: Hello World !
EXIT

You can finally type EXIT which tells the SDK to run the Shutdown command, and therefore shuts down the GameServer.

If you run kubectl describe gs | grep State again - either the GameServer will be replaced with a new, Ready GameServer , or it will be in Shutdown state, on the way to being deleted.

Since we are running a Fleet, Code Blind will always do it’s best to ensure there are always the configured number of GameServers in the pool in either a Ready or Allocated state.

7. Deploy a new version of the GameServer on the Fleet

We can also change the configuration of the GameServer of the running Fleet, and have the changes roll out, without interrupting the currently Allocated GameServers.

Let’s take this for a spin! Run kubectl scale fleet simple-game-server --replicas=5 to return Replicas count back to 5.

Let’s also allocate ourselves a GameServer:

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserverallocation.yaml -o yaml

We should now have four Ready GameServers and one Allocated.

We can check this by running kubectl get gs.

NAME                             STATE       ADDRESS          PORT   NODE       AGE
simple-game-server-tfqn7-c9tz7   Ready       192.168.39.150   7136   minikube   5m
simple-game-server-tfqn7-g8fhq   Allocated   192.168.39.150   7148   minikube   5m
simple-game-server-tfqn7-n0wnl   Ready       192.168.39.150   7453   minikube   5m
simple-game-server-tfqn7-hiiwp   Ready       192.168.39.150   7228   minikube   5m
simple-game-server-tfqn7-w8z7b   Ready       192.168.39.150   7226   minikube   5m

In production, we’d likely be changing a containers > image configuration to update our Fleet to run a new game server process, but to make this example simple, change containerPort from 7654 to 6000.

Run kubectl edit fleet simple-game-server, and make the necessary changes, and then save and exit your editor.

This will start the deployment of a new set of GameServers running with a Container Port of 6000.

Run kubectl describe gs | grep "Container Port" until you can see that there is one with a containerPort of 7654, which is the Allocated GameServer, and four instances with a containerPort of 6000 which is the new configuration. You can also run kubectl get gs and look at the Age column to see that one GameServer is much older than the other four.

You have now deployed a new version of your game!

Next Steps

4.3 - Quickstart: Create a Fleet Autoscaler

This guide covers how you can quickly get started using Code Blind to create a Fleet Autoscaler to manage your fleet size automatically, based on actual load.

Prerequisites

It is assumed that you have followed the instructions to Create a Game Server Fleet and you have a running fleet of game servers.

Objectives

  • Create a Fleet Autoscaler in Kubernetes using Code Blind custom resource.
  • Watch the Fleet scale up when allocating GameServers
  • Watch the Fleet scale down when shutting down allocated GameServers
  • Edit the autoscaler specification to apply live changes

1. Create a Fleet Autoscaler

Let’s create a Fleet Autoscaler using the following command :

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/fleetautoscaler.yaml

You should see a successful output similar to this :

fleetautoscaler.autoscaling.agones.dev/simple-game-server-autoscaler created

This has created a FleetAutoscaler record inside Kubernetes.

2. See the autoscaler status.

kubectl describe fleetautoscaler simple-game-server-autoscaler

It should look something like this:

Name:         simple-game-server-autoscaler
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"au
toscaling.agones.dev/v1","kind":"FleetAutoscaler","metadata":{"annotations":{},
"name":"simple-game-server-autoscaler","namespace":"default"},...
API Version:  autoscaling.agones.dev/v1
Kind:         FleetAutoscaler
Metadata:
  Cluster Name:
  Creation Timestamp:  2018-10-02T15:19:58Z
  Generation:          1
  Owner References:
    API Version:           autoscaling.agones.dev/v1
    Block Owner Deletion:  true
    Controller:            true
    Kind:                  Fleet
    Name:                  simple-game-server
    UID:                   9960762e-c656-11e8-933e-fa163e07a1d4
  Resource Version:        6123197
  Self Link:               /apis/autoscaling.agones.dev/v1/namespaces/default/fleetautoscalers/simple-game-server-autoscaler
  UID:                     9fd0efa1-c656-11e8-933e-fa163e07a1d4
Spec:
  Fleet Name:  simple-game-server
  Policy:
    Buffer:
      Buffer Size:   2
      Max Replicas:  10
      Min Replicas:  2
    Type:            Buffer
Status:
  Able To Scale:     true
  Current Replicas:  2
  Desired Replicas:  2
  Last Scale Time:   <nil>
  Scaling Limited:   false
Events:              <none>

You can see the status (able to scale, not limited), the last time the fleet was scaled (nil for never) and the current and desired fleet size.

The autoscaler works by changing the desired size, and the fleet creates/deletes game server instances to achieve that number. The convergence is achieved in time, which is usually measured in seconds.

3. Allocate a Game Server from the Fleet

If you’re interested in more details for game server allocation, you should consult the Create a Game Server Fleet page. In here we are only interested in triggering allocations to see the autoscaler in action.

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserverallocation.yaml -o yaml

You should get in return the allocated game server details, which should end with something like:

status:
  address: 34.94.118.237
  gameServerName: simple-game-server-v6jwb-6bzkz
  nodeName: gke-test-cluster-default-f11755a7-5km3
  ports:
  - name: default
    port: 7832
  state: Allocated

Note the address and port, you might need them later to connect to the server.

4. See the autoscaler in action

Now let’s wait a few seconds to allow the autoscaler to detect the change in the fleet and check again its status

kubectl describe fleetautoscaler simple-game-server-autoscaler

The last part should look something like this:

Spec:
  Fleet Name:  simple-game-server
  Policy:
    Buffer:
      Buffer Size:   2
      Max Replicas:  10
      Min Replicas:  2
    Type:            Buffer
Status:
  Able To Scale:     true
  Current Replicas:  3
  Desired Replicas:  3
  Last Scale Time:   2018-10-02T16:00:02Z
  Scaling Limited:   false
Events:
  Type    Reason            Age   From                        Message
  ----    ------            ----  ----                        -------
  Normal  AutoScalingFleet  2m    fleetautoscaler-controller  Scaling fleet simple-game-server from 2 to 3

You can see that the fleet size has increased, the autoscaler having compensated for the allocated instance. Last Scale Time has been updated, and a scaling event has been logged.

Double-check the actual number of game server instances and status by running

kubectl get gs

This will get you a list of all the current GameServers and their Status > State.

NAME                             STATE       ADDRESS        PORT     NODE        AGE
simple-game-server-mzhrl-hz8wk   Allocated   10.30.64.99    7131     minikube    5m
simple-game-server-mzhrl-k6jg5   Ready       10.30.64.100   7243     minikube    5m  
simple-game-server-mzhrl-n2sk2   Ready       10.30.64.168   7658     minikube    5m

5. Shut the allocated instance down

Since we’ve only got one allocation, we’ll just grab the details of the IP and port of the only allocated GameServer:

kubectl get gameservers | grep Allocated | awk '{print $3":"$4 }'

This should output your Game Server IP address and port. (eg 10.130.65.208:7936)

You can now communicate with the GameServer:

nc -u {IP} {PORT}
Hello World !
ACK: Hello World !
EXIT

You can finally type EXIT which tells the SDK to run the Shutdown command, and therefore shuts down the GameServer.

6. See the fleet scaling down

Now let’s wait a few seconds to allow the autoscaler to detect the change in the fleet and check again its status

kubectl describe fleetautoscaler simple-game-server-autoscaler

It should look something like this:

Spec:
  Fleet Name:  simple-game-server
  Policy:
    Buffer:
      Buffer Size:   2
      Max Replicas:  10
      Min Replicas:  2
    Type:            Buffer
Status:
  Able To Scale:     true
  Current Replicas:  3
  Desired Replicas:  2
  Last Scale Time:   2018-10-02T16:09:02Z
  Scaling Limited:   false
Events:
  Type    Reason            Age   From                        Message
  ----    ------            ----  ----                        -------
  Normal  AutoScalingFleet  9m    fleetautoscaler-controller  Scaling fleet simple-game-server from 2 to 3
  Normal  AutoScalingFleet  45s   fleetautoscaler-controller  Scaling fleet simple-game-server from 3 to 2

You can see that the fleet size has decreased, the autoscaler adjusting to game server instance being de-allocated, the Last Scale Time and the events have been updated. Note that simple-game-server game server instance you just closed earlier might stay a bit in ‘Unhealthy’ state (and its pod in ‘Terminating’ until it gets removed.

Double-check the actual number of game server instances and status by running

kubectl get gs

This will get you a list of all the current GameServers and their Status > State.

NAME                             STATE     ADDRESS        PORT    NODE       AGE
simple-game-server-mzhrl-k6jg5   Ready     10.30.64.100   7243    minikube   5m
simple-game-server-mzhrl-t7944   Ready     10.30.64.168   7561    minikube   5m

7. Change autoscaling parameters

We can also change the configuration of the FleetAutoscaler of the running Fleet, and have the changes applied live, without interruptions of service.

Run kubectl edit fleetautoscaler simple-game-server-autoscaler and set the bufferSize field to 5.

Let’s look at the list of game servers again. Run watch kubectl get gs until you can see that are 5 ready server instances:

NAME                             STATE     ADDRESS        PORT    NODE         AGE
simple-game-server-mzhrl-7jpkp   Ready     10.30.64.100   7019    minikube     5m
simple-game-server-mzhrl-czt8v   Ready     10.30.64.168   7556    minikube     5m
simple-game-server-mzhrl-k6jg5   Ready     10.30.64.100   7243    minikube     5m
simple-game-server-mzhrl-nb8h2   Ready     10.30.64.168   7357    minikube     5m
simple-game-server-mzhrl-qspb6   Ready     10.30.64.99    7859    minikube     5m
simple-game-server-mzhrl-zg9rq   Ready     10.30.64.99    7745    minikube     5m

Next Steps

Read the advanced Scheduling and Autoscaling guide, for more details on autoscaling.

If you want to use your own GameServer container make sure you have properly integrated the Code Blind SDK.

4.4 - Quickstart: Create a Fleet Autoscaler with Webhook Policy

This guide covers how you can create a webhook fleet autoscaler policy.

In some cases, your game servers may need to use custom logic for scaling your fleet that is more complex than what can be expressed using the Buffer policy in the fleetautoscaler. This guide shows how you can extend Code Blind with an autoscaler webhook to implement a custom autoscaling policy.

When you use an autoscaler webhook the logic computing the number of target replicas is delegated to an external HTTP/S endpoint, such as one provided by a Kubernetes deployment and service in the same cluster (as shown in the examples below). The fleetautoscaler will send a request to the webhook autoscaler’s /scale endpoint every sync period (currently 30s) with a JSON body, and scale the target fleet based on the data that is returned.

Chapter 1 Configuring HTTP fleetautoscaler webhook

Prerequisites

It is assumed that you have completed the instructions to Create a Game Server Fleet and have a running fleet of game servers.

Objectives

  • Run a fleet
  • Deploy the Webhook Pod and service for autoscaling
  • Create a Fleet Autoscaler with Webhook policy type in Kubernetes using Code Blind custom resource
  • Watch the Fleet scales up when allocating GameServers
  • Watch the Fleet scales down after GameServer shutdown

1. Deploy the fleet

Run a fleet in a cluster:

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/fleet.yaml

2. Deploy a Webhook service for autoscaling

In this step we would deploy an example webhook that will control the size of the fleet based on allocated gameservers portion in a fleet. You can see the source code for this example webhook server here. The fleetautoscaler would trigger this endpoint every 30 seconds. More details could be found also here. We need to create a pod which will handle HTTP requests with json payload FleetAutoscaleReview and return back it with FleetAutoscaleResponse populated.

The Scale flag and Replicas values returned in the FleetAutoscaleResponse tells the FleetAutoscaler what target size the backing Fleet should be scaled up or down to. If Scale is false - no scaling occurs.

Run next command to create a service and a Webhook pod in a cluster:

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/autoscaler-webhook/autoscaler-service.yaml

To check that it is running and liveness probe is fine:

kubectl describe pod autoscaler-webhook
Name:           autoscaler-webhook-86944884c4-sdtqh
Namespace:      default
Node:           gke-test-cluster-default-1c5dec79-h0tq/10.138.0.2
...
Status:         Running

3. Create a Fleet Autoscaler

Let’s create a Fleet Autoscaler using the following command:

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/webhookfleetautoscaler.yaml

You should see a successful output similar to this:

fleetautoscaler.autoscaling.agones.dev "webhook-fleet-autoscaler" created

This has created a FleetAutoscaler record inside Kubernetes. It has the link to Webhook service we deployed above.

4. See the fleet and autoscaler status.

In order to track the list of gameservers which run in your fleet you can run this command in a separate terminal tab:

 watch "kubectl get gs -n default"

In order to get autoscaler status use the following command:

kubectl describe fleetautoscaler webhook-fleet-autoscaler

It should look something like this:

Name:         webhook-fleet-autoscaler
Namespace:    default
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":
"autoscaling.agones.dev/v1","kind":"FleetAutoscaler","metadata":{"annotations"
:{},"name":"webhook-fleet-autoscaler","namespace":"default...
API Version:  autoscaling.agones.dev/v1
Kind:         FleetAutoscaler
etadata:
  Cluster Name:
  Creation Timestamp:  2018-12-22T12:52:23Z
  Generation:          1
  Resource Version:    2274579
  Self Link:           /apis/autoscaling.agones.dev/v1/namespaces/default/fleet
autoscalers/webhook-fleet-autoscaler
  UID:                 6d03eae4-05e8-11e9-84c2-42010a8a01c9
Spec:
  Fleet Name:  simple-game-server
  Policy:
    Type:  Webhook
    Webhook:
      Service:
        Name:       autoscaler-webhook-service
        Namespace:  default
        Path:       scale
      URL:
Status:
  Able To Scale:     true
  Current Replicas:  2
  Desired Replicas:  2
  Last Scale Time:   <nil>
  Scaling Limited:   false
Events:              <none>

You can see the status (able to scale, not limited), the last time the fleet was scaled (nil for never), current and desired fleet size.

The autoscaler makes a query to a webhoook service deployed on step 1 and on response changing the target Replica size, and the fleet creates/deletes game server instances to achieve that number. The convergence is achieved in time, which is usually measured in seconds.

5. Allocate Game Servers from the Fleet to trigger scale up

If you’re interested in more details for game server allocation, you should consult the Create a Game Server Fleet page. Here we only interested in triggering allocations to see the autoscaler in action.

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserverallocation.yaml -o yaml

You should get in return the allocated game server details, which should end with something like:

status:
  address: 34.94.118.237
  gameServerName: simple-game-server-v6jwb-6bzkz
  nodeName: gke-test-cluster-default-f11755a7-5km3
  ports:
  - name: default
    port: 7832
  state: Allocated

Note the address and port, you might need them later to connect to the server.

Run the kubectl command one more time so that we have both servers allocated:

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserverallocation.yaml -o yaml

6. Check new Autoscaler and Fleet status

Now let’s wait a few seconds to allow the autoscaler to detect the change in the fleet and check again its status

kubectl describe fleetautoscaler webhook-fleet-autoscaler

The last part should look similar to this:

Spec:
  Fleet Name:  simple-game-server
  Policy:
    Type:  Webhook
    Webhook:
      Service:
        Name:       autoscaler-webhook-service
        Namespace:  default
        Path:       scale
      URL:
Status:
  Able To Scale:     true
  Current Replicas:  4
  Desired Replicas:  4
  Last Scale Time:   2018-12-22T12:53:47Z
  Scaling Limited:   false
Events:
  Type    Reason            Age   From                        Message
  ----    ------            ----  ----                        -------
  Normal  AutoScalingFleet  35s   fleetautoscaler-controller  Scaling fleet simple-game-server from 2 to 4

You can see that the fleet size has increased in particular case doubled to 4 gameservers (based on our custom logic in our webhook), the autoscaler having compensated for the two allocated instances. Last Scale Time has been updated and a scaling event has been logged.

Double-check the actual number of game server instances and status by running:

 kubectl get gs -n default

This will get you a list of all the current GameServers and their Status > State.

NAME                     STATE       ADDRESS         PORT     NODE        AGE
simple-game-server-dmkp4-8pkk2   Ready       35.247.13.175   7386     minikube     5m
simple-game-server-dmkp4-b7x87   Allocated   35.247.13.175   7219     minikube     5m
simple-game-server-dmkp4-r4qtt   Allocated   35.247.13.175   7220     minikube     5m
simple-game-server-dmkp4-rsr6n   Ready       35.247.13.175   7297     minikube     5m

7. Check downscaling using Webhook Autoscaler policy

Based on our custom webhook deployed earlier, if the fraction of allocated replicas in whole Replicas count would be less than threshold (0.3) then the fleet would scale down by scaleFactor, in our example by 2.

Note that the example webhook server has a limitation that it would not decrease fleet replica count under minReplicasCount, which is equal to 2.

We need to run EXIT command on one gameserver (Use IP address and port of the allocated gameserver from the previous step) in order to decrease the number of allocated gameservers in a fleet (<0.3).

nc -u 35.247.13.175 7220
EXIT

Server would be in shutdown state. Wait about 30 seconds. Then you should see scaling down event in the output of next command:

kubectl describe fleetautoscaler webhook-fleet-autoscaler

You should see these lines in events:

  Normal   AutoScalingFleet  11m                fleetautoscaler-controller  Scaling fleet simple-game-server from 2 to 4
  Normal   AutoScalingFleet  1m                 fleetautoscaler-controller  Scaling fleet simple-game-server from 4 to 2

And get gameservers command output:

kubectl get gs -n default
NAME                             STATUS      ADDRESS          PORT     NODE       AGE
simple-game-server-884fg-6q5sk   Ready       35.247.117.202   7373     minikube   5m
simple-game-server-884fg-b7l58   Allocated   35.247.117.202   7766     minikube   5m

8. Cleanup

You can delete the autoscaler service and associated resources with the following commands.

kubectl delete -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/autoscaler-webhook/autoscaler-service.yaml

Removing the fleet:

kubectl delete -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/fleet.yaml

Chapter 2 Configuring HTTPS fleetautoscaler webhook with CA Bundle

Objectives

Using TLS and a certificate authority (CA) bundle we can establish trusted communication between Fleetautoscaler and an HTTPS server running the autoscaling webhook that controls the size of the fleet (Replicas count). The certificate of the autoscaling webhook must be signed by the CA provided in fleetautoscaler yaml configuration file. Using TLS eliminates the possibility of a man-in-the-middle attack between the fleetautoscaler and the autoscaling webhook.

1. Deploy the fleet

Run a fleet in a cluster:

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/fleet.yaml

2. Create X509 Root and Webhook certificates

The procedure of generating a Self-signed CA certificate is as follows:

The first step is to create the private root key:

openssl genrsa -out rootCA.key 2048

The next step is to self-sign this certificate:

openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem

This will start an interactive script that will ask you for various bits of information. Fill it out as you see fit.

Every webhook that you wish to install a trusted certificate will need to go through this process. First, just like with the root CA step, you’ll need to create a private key (different from the root CA):

openssl genrsa -out webhook.key 2048

Next create configuration file cert.conf for the certificate signing request:

[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = autoscaler-tls-service.default.svc
[v3_req]
keyUsage = digitalSignature
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = autoscaler-tls-service.default.svc

Generate the certificate signing request, use valid hostname which in this case will be autoscaler-tls-service.default.svc as Common Name (eg, fully qualified host name) as well as DNS.1 in the alt_names section of the config file.

Check the Kubernetes documentation to see how Services get assigned DNS entries.

openssl req -new -out webhook.csr -key webhook.key -config cert.conf

Once that’s done, you’ll sign the CSR, which requires the CA root key:

openssl x509 -req -in webhook.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out webhook.crt -days 500 -sha256 -extfile cert.conf -extensions v3_req

This would generate webhook.crt certificate

Add secret which later would be mounted to autoscaler-webhook-tls pod.

kubectl create secret tls autoscalersecret --cert=webhook.crt --key=webhook.key

You need to put Base64-encoded string into caBundle field in your fleetautoscaler yaml configuration:

base64 -i ./rootCA.pem

Copy the output of the command above and replace the caBundle field in your text editor (say vim) with the new value:

wget https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/webhookfleetautoscalertls.yaml
vim ./webhookfleetautoscalertls.yaml

3. Deploy a Webhook service for autoscaling

Run next command to create a service and a Webhook pod in a cluster:

kubectl apply -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/autoscaler-webhook/autoscaler-service-tls.yaml

To check that it is running and liveness probe is fine:

kubectl describe pod autoscaler-webhook-tls

Wait for the Running status results:

Name:               autoscaler-webhook-tls-f74c9bff7-ssrsc
Namespace:          default
...
Status:         Running

4. Create a Fleet Autoscaler

Let’s create a Fleet Autoscaler using the following command (caBundle should be set properly on Step 2):

kubectl apply -f ./webhookfleetautoscalertls.yaml

5. See the fleet and autoscaler status.

In order to track the list of gameservers which run in your fleet you can run this command in a separate terminal tab:

 watch "kubectl get gs -n default"

6. Allocate two Game Servers from the Fleet to trigger scale up

If you’re interested in more details for game server allocation, you should consult the Create a Game Server Fleet page. Here we only interested in triggering allocations to see the autoscaler in action.

for i in {0..1} ; do kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserverallocation.yaml -o yaml ; done

7. Check new Autoscaler and Fleet status

Now let’s wait a few seconds to allow the autoscaler to detect the change in the fleet and check again its status

kubectl describe fleetautoscaler  webhook-fleetautoscaler-tls

The last part should look similar to this:

Spec:
  Fleet Name:  simple-game-server
  Policy:
    Type:  Webhook
    Webhook:
      Ca Bundle:  LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN1RENDQWFBQ0NRQ29kcEFNbTlTd0pqQU5CZ2txaGtpRzl3MEJBUXNGQURBZU1Rc3dDUVlEVlFRR0V3SlYKVXpFUE1BMEdBMVVFQ3d3R1FXZHZibVZ6TUI0WERURTVNREV3TkRFeE5URTBORm9YRFRJeE1UQXlOREV4TlRFMApORm93SGpFTE1Ba0dBMVVFQmhNQ1ZWTXhEekFOQmdOVkJBc01Ca0ZuYjI1bGN6Q0NBU0l3RFFZSktvWklodmNOCkFRRUJCUUFEZ2dFUEFEQ0NBUW9DZ2dFQkFOQ0h5dndDOTZwZDlTdkFhMUIvRWg2ekcxeDBLS1dPaVhtNzhJcngKKzZ5WHd5YVpsMVo1cVExbUZoOThMSGVZUmQwWVgzRTJnelZ5bFpvUlUra1ZESzRUc0VzV0tNUFVpdVo0MUVrdApwbythbEN6alAyaXZzRGZaOGEvdnByL3dZZ2FrWGtWalBUaGpKUk9xTnFIdWROMjZVcUFJYnNOTVpoUkxkOVFFCnFLSjRPNmFHNVMxTVNqZFRGVHFlbHJiZitDcXNKaHltZEIzZmxGRUVvdXExSmoxS0RoQjRXWlNTbS9VSnpCNkcKNHUzY3BlQm1jTFVRR202ZlFHb2JFQSt5SlpMaEVXcXBrd3ZVZ2dCNmRzWE8xZFNIZXhhZmlDOUVUWGxVdFRhZwo1U2JOeTVoYWRWUVV3Z253U0J2djR2R0t1UUxXcWdXc0JyazB5Wll4Sk5Bb0V5RUNBd0VBQVRBTkJna3Foa2lHCjl3MEJBUXNGQUFPQ0FRRUFRMkgzaWJRcWYzQTNES2l1eGJISURkbll6TlZ2Z0dhRFpwaVZyM25ocm55dmxlNVgKR09hRm0rMjdRRjRWV29FMzZDTGhYZHpEWlM4bEpIY09YUW5KOU83Y2pPYzkxVmh1S2NmSHgwS09hU1oweVNrVAp2bEtXazlBNFdoNGE0QXFZSlc3Z3BUVHR1UFpydnc4VGsvbjFaWEZOYVdBeDd5RU5OdVdiODhoNGRBRDVaTzRzCkc5SHJIdlpuTTNXQzFBUXA0Q3laRjVyQ1I2dkVFOWRkUmlKb3IzM3pLZTRoRkJvN0JFTklZZXNzZVlxRStkcDMKK0g4TW5LODRXeDFUZ1N5Vkp5OHlMbXFpdTJ1aThjaDFIZnh0OFpjcHg3dXA2SEZLRlRsTjlBeXZUaXYxYTBYLwpEVTk1eTEwdi9oTlc0WHpuMDJHNGhrcjhzaUduSEcrUEprT3hBdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
      Service:    <nil>
      URL:        https://autoscaler-tls-service.default.svc:8000/scale
Events:
  Type    Reason            Age   From                        Message
  ----    ------            ----  ----                        -------
  Normal  AutoScalingFleet  5s   fleetautoscaler-controller  Scaling fleet simple-game-server from 2 to 4

You can see that the fleet size has increased in particular case doubled to 4 gameservers (based on our custom logic in our webhook), the autoscaler having compensated for the two allocated instances. Last Scale Time has been updated and a scaling event has been logged.

Double-check the actual number of game server instances and status by running:

 kubectl get gs -n default

This will get you a list of all the current GameServers and their Status > State.

NAME                     STATE       ADDRESS         PORT      NODE      AGE
simple-game-server-njmr7-2t4nx   Ready       35.203.159.68   7330      minikube   1m
simple-game-server-njmr7-65rp6   Allocated   35.203.159.68   7294      minikube   4m

8. Cleanup

You can delete the autoscaler service and associated resources with the following commands.

kubectl delete -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/autoscaler-webhook/autoscaler-service-tls.yaml

Removing x509 key secret:

kubectl delete secret autoscalersecret

Removing the fleet:

kubectl delete -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/fleet.yaml

Comments

Note that secure communication has been established and we can trust that communication between the fleetautoscaler and the autoscaling webhook. If you need to run the autoscaling webhook outside of the Kubernetes cluster, you can use another root certificate authority as long as you put it into the caBundle parameter in fleetautoscaler configuration (in pem format, base64-encoded).

Troubleshooting Guide

If you run into problems with the configuration of your fleetautoscaler and webhook service the easiest way to debug them is to run:

kubectl describe fleetautoscaler <FleetAutoScalerName>

and inspect the events at the bottom of the output.

Common error messages.

If you have configured the wrong service Path for the FleetAutoscaler you will see a message like

Error calculating desired fleet size on FleetAutoscaler simple-fleet-r7fdv-autoscaler. Error: bad status code 404 from the server: https://autoscaler-tls-service.default.svc:8000/scale

If you are using a hostname other than autoscaler-tls-service.default.svc as the Common Name (eg, fully qualified host name) when creating a certificate using openssl tool you will see a message like

Post https://autoscaler-tls-service.default.svc:8000/scale: x509: certificate is not valid for any names, but wanted to match autoscaler-tls-service.default.svc

If you see errors like the following in autoscaler-webhook-tls pod logs:

http: TLS handshake error from 10.48.3.125:33374: remote error: tls: bad certificate

Then there could be an issue with your ./rootCA.pem.

You can repeat the process from step 2, in order to fix your certificates setup.

Next Steps

Read the advanced Scheduling and Autoscaling guide, for more details on autoscaling.

If you want to use your own GameServer container make sure you have properly integrated the Code Blind SDK.

4.5 - Quickstart: Edit a Game Server

The following guide is for developers without Docker or Kubernetes experience, that want to use the simple-game-server example as a starting point for a custom game server.

This guide addresses Google Kubernetes Engine and Minikube. We would welcome a Pull Request to expand this to include other platforms as well.

Prerequisites

  1. A Go environment
  2. Docker
  3. Code Blind installed on GKE or Minikube
  4. kubectl properly configured

To install on GKE, follow the install instructions (if you haven’t already) at Setting up a Google Kubernetes Engine (GKE) cluster. Also complete the “Enabling creation of RBAC resources” and “Installing Code Blind” sets of instructions on the same page.

To install locally on Minikube, read Setting up a Minikube cluster. Also complete the “Enabling creation of RBAC resources” and “Installing Code Blind” sets of instructions on the same page.

Modify the code and push another new image

Modify the simple-game-server example source code

Modify the main.go file. For example:

Change the following line in main.go:

From:

respond(conn, sender, "ACK: "+txt+"\n")

To:

respond(conn, sender, "ACK: Echo says "+txt+"\n")

Build Server

Since Docker image is using Alpine Linux, the “go build” command has to include few more environment variables.

go get agones.dev/agones/pkg/sdk
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o bin/server -a -v main.go

Using Docker File

Create a new docker image

docker build -t gcr.io/[PROJECT_ID]/agones-simple-game-server:modified .

Note: you can change the image name “agones-simple-game-server” to something else.

If using GKE, push the image to GCP Registry

docker push gcr.io/[PROJECT_ID]/agones-simple-game-server:modified

Note: Review Authentication Methods for additional information regarding use of gcloud as a Docker credential helper and advanced authentication methods to the Google Container Registry.

If using Minikube, load the image into Minikube

minikube cache add gcr.io/[PROJECT_ID]/agones-agones-simple-game-server:modified

Modify gameserver.yaml

Modify the following line from gameserver.yaml to use the new configuration.

spec:
  containers:
  - name: agones-simple-game-server
    image: gcr.io/[PROJECT_ID]/agones-simple-game-server:modified

If using GKE, deploy Server to GKE

Apply the latest settings to the Kubernetes container.

gcloud config set container/cluster [CLUSTER_NAME]
gcloud container clusters get-credentials [CLUSTER_NAME]
kubectl apply -f gameserver.yaml

If using Minikube, deploy the Server to Minikube

kubectl apply -f gameserver.yaml

Check the GameServer Status

kubectl describe gameserver

Verify

Let’s retrieve the IP address and the allocated port of your Game Server:

kubectl get gs simple-game-server -o jsonpath='{.status.address}:{.status.ports[0].port}'

You can now communicate with the Game Server :

nc -u {IP} {PORT}
Hello World!
ACK:  Echo says  Hello World!
EXIT

You can finally type EXIT which tells the SDK to run the Shutdown command, and therefore shuts down the GameServer.

If you run kubectl describe gameserver again - either the GameServer will be gone completely, or it will be in Shutdown state, on the way to being deleted.

Next Steps

If you want to perform rolling updates of modified game servers, see Quickstart Create a Game Server Fleet.

5 - Guides

Guides for deeper integrations with Code Blind

5.1 - Feature Stages

This page provides a description of the various stages that Code Blind features can be in, and the relative maturity and support level expected for each level.

Supported Versions

Code Blind versions are expressed as x.y.z, where x is the major version, y is the minor version, and z is the patch version following Semantic Versioning terminology.

Code Blind Features

A feature within Code Blind can be in Alpha, Beta or Stable stage.

Feature Gates

Alpha and Beta features can be enabled or disabled through the agones.featureGates configuration option that can be found in the Helm configuration documentation.

The current set of alpha and beta feature gates:

Feature NameGateDefaultStageSince
Allocated GameServers are notified on relevant Fleet UpdatesFleetAllocationOverflowEnabledBeta1.37.0
CountsAndListsCountsAndListsDisabledAlpha1.37.0
GameServer player capacity filtering on GameServerAllocationsPlayerAllocationFilterDisabledAlpha1.14.0
Player TrackingPlayerTrackingDisabledAlpha1.6.0
DisableResyncOnSDKServerDisableResyncOnSDKServerDisabledAlpha1.37.0
Example Gate (not in use)ExampleDisabledNone0.13.0

Description of Stages

Alpha

An Alpha feature means:

  • Disabled by default.
  • Might be buggy. Enabling the feature may expose bugs.
  • Support for this feature may be dropped at any time without notice.
  • The API may change in incompatible ways in a later software release without notice.
  • Recommended for use only in short-lived testing clusters, due to increased risk of bugs and lack of long-term support.

Beta

A Beta feature means:

  • Enabled by default, but able to be disabled through a feature gate.
  • The feature is well tested. Enabling the feature is considered safe.
  • Support for the overall feature will not be dropped, though details may change.
  • The schema and/or semantics of objects may change in incompatible ways in a subsequent beta or stable releases. When this happens, we will provide instructions for migrating to the next version. This may require deleting, editing, and re-creating API objects. The editing process may require some thought. This may require downtime for applications that rely on the feature.
  • Recommended for only non-business-critical uses because of potential for incompatible changes in subsequent releases. If you have multiple clusters that can be upgraded independently, you may be able to relax this restriction.

Stable

A Stable feature means:

  • The feature is enabled and the corresponding feature gate no longer exists.
  • Stable versions of features will appear in released software for many subsequent versions.

Feature Stage Indicators

There are a variety of features with Code Blind, how can we determine what stage each feature is in?

Below are indicators for each type of functionality that can be used to determine the feature stage for a given aspect of Code Blind.

Custom Resource Definitions (CRDs)

This refers to Kubernetes resource for Code Blind, such as GameServer, Fleet and GameServerAllocation.

New CRDs

For new resources, the stage of the resource will be indicated by the apiVersion of the resource.

For example: apiVersion: "agones.dev/v1" is a stable resource, apiVersion: "agones.dev/v1beta1" is a beta stage resource, and apiVersion: "agones.dev/v1alpha1" is an alpha stage resource.

New CRD attributes

When alpha and beta attributes are added to an existing stable Code Blind CRD, we will follow the Kubernetes Adding Unstable Features to Stable Versions Guide to optimise on the least amount of breaking changes for users as attributes progress through feature stages.

alpha and beta attributes will be added to the existing CRD as optional and documented with their feature stage. Attempting to populate these alpha and beta attributes on an Code Blind CRD will return a validation error if their accompanying Feature Flag is not enabled.

alpha and beta attributes can be subject to change of name and structure, and will result in breaking changes before moving to a stable stage. These changes will be outlined in release notes and feature documentation.

Code Blind Game Server SDK

Any alpha or beta Game Server SDK functionality will be a subpackage of the sdk package. For example , functionality found in a sdk.alphav1 package should be considered at the alpha feature stage.

Only experimental functionality will be found in any alpha and beta SDK packages, and as such may change as development occurs.

As SDK features move to through feature stages towards stable, the previous version of the SDK API will remain for at least one release to enable easy migration to the more stable feature stage (i.e. from alpha -> beta, beta -> stable)

Any other SDK functionality not marked as alpha or beta is assumed to be stable.

REST & gRPC APIs

REST and gRPC API will have versioned paths where appropriate to indicate their feature stage.

For example, a REST API with a prefix of v1alpha1 is an alpha stage feature: http://api.example.com/v1alpha1/exampleaction.

Similar to the SDK, any alpha or beta gRPC functionality will be a subpackage of the main API package. For example, functionality found in a api.alphav1 package should be considered at the alpha feature stage.

5.2 - Best Practices

Best practices for running Code Blind in production.

Overview

Running Code Blind in production takes consideration, from planning your launch to figuring out the best course of action for cluster and Code Blind upgrades. On this page, we’ve collected some general best practices. We also have cloud specific pages for:

If you are interested in submitting best practices for your cloud prodiver / on-prem, please contribute!

Separation of Code Blind from GameServer nodes

When running in production, Code Blind should be scheduled on a dedicated pool of nodes, distinct from where Game Servers are scheduled for better isolation and resiliency. By default Code Blind prefers to be scheduled on nodes labeled with agones.dev/agones-system=true and tolerates the node taint agones.dev/agones-system=true:NoExecute. If no dedicated nodes are available, Code Blind will run on regular nodes. See taints and tolerations for more information about Kubernetes taints and tolerations.

If you are collecting Metrics using our standard Prometheus installation, see the installation guide for instructions on configuring a separate node pool for the agones.dev/agones-metrics=true taint.

See Creating a Cluster for initial set up on your cloud provider.

Redundant Clusters

Allocate Across Clusters

Code Blind supports Multi-cluster Allocation, allowing you to allocate from a set of clusters, versus a single point of potential failure. There are several other options for multi-cluster allocation:

Spread

You should consider spreading your game servers in two ways:

  • Across geographic fault domains (GCP regions, AWS availability zones, separate datacenters, etc.): This is desirable for geographic fault isolation, but also for optimizing client latency to the game server.
  • Within a fault domain: Kubernetes Clusters are single points of failure. A single misconfigured RBAC rule, an overloaded Kubernetes Control Plane, etc. can prevent new game server allocations, or worse, disrupt existing sessions. Running multiple clusters within a fault domain also allows for easier upgrades.

5.2.1 - Google Kubernetes Engine Best Practices

Best practices for running Code Blind on Google Kubernetes Engine (GKE).

Overview

On this page, we’ve collected several Google Kubernetes Engine (GKE) best practices.

Release Channels

Why?

We recommned using Release Channels for all GKE clusters. Using Release Channels has several advantages:

  • Google automatically manages the version and upgrade cadence for your Kubernetes Control Plane and its nodes.
  • Clusters on a Release Channel are allowed to use the No minor upgrades and No minor or node upgrades scope of maintenance exclusions - in other words, enrolling a cluster in a Release Channel gives you more control over node upgrades.
  • Clusters enrolled in rapid channel have access to the newest Kubernetes version first. Code Blind strives to support the newest release in rapid channel to allow you to test the newest Kubernetes soon after it’s available in GKE.

What channel should I use?

We recommend the regular channel, which offers a balance between stability and freshness. See this guide for more discussion.

If you need to disallow minor version upgrades for more than 6 months, consider choosing the freshest Kubernetes version possible: Choosing the freshest version on rapid or regular will extend the amount of time before your cluster reaches end of life.

What versions are available on a given channel?

You can query the versions available across different channels using gcloud:

gcloud container get-server-config \
  --region=[COMPUTE_REGION] \
  --flatten="channels" \
  --format="yaml(channels)"

Replace the following:

Managing Game Server Disruption on GKE

If your game session length is less than an hour, use the eviction API to configure your game servers appropriately - see Controlling Disruption.

For sessions longer than an hour, there are currently two possible approaches to manage disruption:

  • (GKE Standard/Autopilot) Blue/green deployment at the cluster level: If you are using an automated deployment process, you can:

    • create a new, green cluster within a release channel e.g. every week,
    • use maintenance exclusions to prevent node upgrades for 30d, and
    • scale the Fleet on the old, blue cluster down to 0, and
    • use multi-cluster allocation on Code Blind, which will then direct new allocations to the new green cluster (since blue has 0 desired), then
    • delete the old, blue cluster when the Fleet successfully scales down.
  • (GKE Standard only) Use node pool blue/green upgrades

5.3 - Code Blind Game Server Client SDKs

The SDKs are integration points for game servers with Code Blind itself.

Overview

The client SDKs are required for a game server to work with Code Blind.

The current supported SDKs are:

You can also find some externally supported SDKs in our Third Party Content.

The SDKs are relatively thin wrappers around gRPC generated clients, or an implementation of the REST API (exposed via grpc-gateway), where gRPC client generation and compilation isn’t well supported.

They connect to a small process that Code Blind coordinates to run alongside the Game Server in a Kubernetes Pod. This means that more languages can be supported in the future with minimal effort (but pull requests are welcome! 😊 ).

There is also local development tooling for working against the SDK locally, without having to spin up an entire Kubernetes infrastructure.

Connecting to the SDK Server

Starting with Code Blind 1.1.0, the port that the SDK Server listens on for incoming gRPC or HTTP requests is configurable. This provides flexibility in cases where the default port conflicts with a port that is needed by the game server.

Code Blind will automatically set the following environment variables on all game server containers:

  • AGONES_SDK_GRPC_PORT: The port where the gRPC server is listening (defaults to 9357)
  • AGONES_SDK_HTTP_PORT: The port where the grpc-gateway is listening (defaults to 9358)

The SDKs will automatically discover and connect to the gRPC port specified in the environment variable.

If your game server requires using a REST client, it is advised to use the port from the environment variable, otherwise your game server will not be able to contact the SDK server if it is configured to use a non-default port.

Function Reference

While each of the SDKs are canonical to their languages, they all have the following functions that implement the core responsibilities of the SDK.

For language specific documentation, have a look at the respective source (linked above), and the examples.

Calling any of state changing functions mentioned below does not guarantee that GameServer Custom Resource object would actually change its state right after the call. For instance, it could be moved to the Shutdown state elsewhere (for example, when a fleet scales down), which leads to no changes in GameServer object. You can verify the result of this call by waiting for the desired state in a callback to WatchGameServer() function.

Functions which changes GameServer state or settings are:

  1. Ready()
  2. Shutdown()
  3. SetLabel()
  4. SetAnnotation()
  5. Allocate()
  6. Reserve()
  7. Alpha().SetCapacity()
  8. Alpha().PlayerConnect()
  9. Alpha().PlayerDisconnect()
  10. Alpha().SetCounterCount()
  11. Alpha().IncrementCounter()
  12. Alpha().DecrementCounter()
  13. Alpha().SetCounterCapacity()
  14. Alpha().AppendListValue()
  15. Alpha().DeleteListValue()
  16. Alpha().SetListCapacity()

Lifecycle Management

Ready()

This tells Code Blind that the Game Server is ready to take player connections. Once a Game Server has specified that it is Ready, then the Kubernetes GameServer record will be moved to the Ready state, and the details for its public address and connection port will be populated.

While Code Blind prefers that Shutdown() is run once a game has completed to delete the GameServer instance, if you want or need to move an Allocated GameServer back to Ready to be reused, you can call this SDK method again to do this.

Health()

This sends a single ping to designate that the Game Server is alive and healthy. Failure to send pings within the configured thresholds will result in the GameServer being marked as Unhealthy.

See the gameserver.yaml for all health checking configurations.

Reserve(seconds)

With some matchmaking scenarios and systems it is important to be able to ensure that a GameServer is unable to be deleted, but doesn’t trigger a FleetAutoscaler scale up. This is where Reserve(seconds) is useful.

Reserve(seconds) will move the GameServer into the Reserved state for the specified number of seconds (0 is forever), and then it will be moved back to Ready state. While in Reserved state, the GameServer will not be deleted on scale down or Fleet update, and also it could not be Allocated using GameServerAllocation.

This is often used when a game server process must register itself with an external system, such as a matchmaker, that requires it to designate itself as available for a game session for a certain period. Once a game session has started, it should call SDK.Allocate() to designate that players are currently active on it.

Calling other state changing SDK commands such as Ready or Allocate will turn off the timer to reset the GameServer back to the Ready state or to promote it to an Allocated state accordingly.

Allocate()

With some matchmakers and game matching strategies, it can be important for game servers to mark themselves as Allocated. For those scenarios, this SDK functionality exists.

There is a chance that GameServer does not actually become Allocated after this call. Please refer to the general note in Function Reference above.

The agones.dev/last-allocated annotation will be set on the GameServer to an RFC3339 formatted timestamp of the time of allocation, even if the GameServer was already in an Allocated state.

Note that if using SDK.Allocate() in combination with GameServerAllocations, it’s possible for the agones.dev/last-allocated timestamp to move backwards if clocks are not synchronized between the Code Blind controller and the GameServer pod.

Shutdown()

This tells Code Blind to shut down the currently running game server. The GameServer state will be set Shutdown and the backing Pod will be Terminated.

It’s worth reading the Termination of Pods Kubernetes documentation, to understand the termination process, and the related configuration options.

As a rule of thumb, implement a graceful shutdown in your game sever process when it receives the TERM signal from Kubernetes when the backing Pod goes into Termination state.

Be aware that if you use a variation of System.exit(0) after calling SDK.Shutdown(), your game server container may restart for a brief period, inline with our Health Checking policies.

If the SDK server receives a TERM signal before calling SDK.Shutdown(), the SDK server will stay alive for the period of the terminationGracePeriodSeconds until SDK.Shutdown() has been called.

Configuration Retrieval

GameServer()

This returns most of the backing GameServer configuration and Status. This can be useful for instances where you may want to know Health check configuration, or the IP and Port the GameServer is currently allocated to.

Since the GameServer contains an entire PodTemplate the returned object is limited to that configuration that was deemed useful. If there are areas that you feel are missing, please file an issue or pull request.

The easiest way to see what is exposed, is to check the sdk.proto, specifically at the message GameServer.

For language specific documentation, have a look at the respective source (linked above), and the examples.

WatchGameServer(function(gameserver){…})

This executes the passed in callback with the current GameServer details whenever the underlying GameServer configuration is updated. This can be useful to track GameServer > Status > State changes, metadata changes, such as labels and annotations, and more.

In combination with this SDK, manipulating Annotations and Labels can also be a useful way to communicate information through to running game server processes from outside those processes. This is especially useful when combined with GameServerAllocation applied metadata.

Since the GameServer contains an entire PodTemplate the returned object is limited to that configuration that was deemed useful. If there are areas that you feel are missing, please file an issue or pull request.

The easiest way to see what is exposed, is to check the sdk.proto, specifically at the message GameServer.

For language specific documentation, have a look at the respective source (linked above), and the examples.

Metadata Management

SetLabel(key, value)

This will set a Label value on the backing GameServer record that is stored in Kubernetes.

To maintain isolation, the key value is automatically prefixed with the value “agones.dev/sdk-”. This is done for two main reasons:

  • The prefix allows the developer to always know if they are accessing or reading a value that could have come, or may be changed by the client SDK. Much like private vs public scope in a programming language, the Code Blind SDK only gives you access to write to part of the set of labels and annotations that exist on a GameServer.
  • The prefix allows for a smaller attack surface if the GameServer container gets compromised. Since the game container is generally externally exposed, and the Code Blind project doesn’t control the binary that is run within it, limiting exposure if the game server becomes compromised is worth the extra development friction that comes with having this prefix in place.

Setting GameServer labels can be useful if you want information from your running game server process to be observable or searchable through the Kubernetes API.

SetAnnotation(key, value)

This will set an Annotation value on the backing GameServer record that is stored in Kubernetes.

To maintain isolation, the key value is automatically prefixed with “agones.dev/sdk-” for the same reasons as in SetLabel(…) above. The isolation is also important as Code Blind uses annotations on the GameServer as part of its internal processing.

Setting GameServer annotations can be useful if you want information from your running game server process to be observable through the Kubernetes API.

Counters And Lists

The Counters and Lists features in the SDK offer a flexible configuration for tracking various entities like players, rooms, and sessions.

Declared keys and default values for Counters and Lists are specified in GameServer.Spec.Counters and GameServer.Spec.Lists respectively.

Modified Counter and List values and capacities will be updated in GameServer.Status.Counters and GameServer.Status.Lists respectively.

Counters

All functions will return an error if the specified key is not predefined in the GameServer.Spec.Counters resource configuration.

Note: For Counters, the default setting for the capacity is preset to 1000. It is recommended to avoid configuring the capacity to max(int64), as doing so could cause problems with JSON Patch operations.

Alpha().GetCounterCount(key)

This function retrieves either the GameServer.Status.Counters[key].Count or the SDK awaiting-batch value for a given key, whichever is most up to date.

Alpha().SetCounterCount(key, amount)

This function sets the value of GameServer.Status.Counters[key].Count for the given key to the passed in amount. This operation overwrites any previous values and the new value cannot exceed the Counter’s capacity.

Alpha().IncrementCounter(key, amount)

This function increments GameServer.Status.Counters[key].Count for the given key by the passed in non-negative amount. The function returns an error if the Counter is already at capacity (at time of operation), indicating no increment will occur.

Alpha().DecrementCounter(key, amount)

This function decreases GameServer.Status.Counters[key].Count for the given key by the passed in non-negative amount. It returns an error if the Counter’s count is already at zero.

Alpha().SetCounterCapacity(key, amount)

This function sets the maximum GameServer.Status.Counters[key].Capacity for the given key by the passed in non-negative amount. A capacity value of 0 indicates no capacity limit.

Alpha().GetCounterCapacity(key)

This function retrieves either the GameServer.Status.Counters[key].Capacity or the SDK awaiting-batch value for the given key, whichever is most up to date.

Lists

All functions will return an error if the specified key is not predefined in the GameServer.Spec.Lists resource configuration.

Alpha().AppendListValue(key, value)

This function appends the specified string value to the List in GameServer.Status.Lists[key].Values.

An error is returned if the string already exists in the list or if the list is at capacity.

Alpha().DeleteListValue(key, value)

This function removes the specified string value from the List in GameServer.Status.Lists[key].Values.

An error is returned if the string does not exist in the list.

Alpha().SetListCapacity(key, amount)

This function sets the maximum capacity for the List at GameServer.Status.Lists[key].Capacity.

The capacity value is required to be between 0 and 1000.

Alpha().GetListCapacity(key)

This function retrieves either the GameServer.Status.Lists[key].Capacity or the SDK awaiting-batch value for the given key, whichever is most up to date.

Alpha().GetListValues(key)

This function retrieves either the GameServer.Status.Lists[key].Values or the SDK awaiting-batch values array for the given key, whichever is most up to date.

Alpha().ListContains(key, value)

Convenience function, which returns if the specific string value exists in the results of Alpha().GetListValues(key).

Alpha().GetListLength(key)

Convenience function, which retrieves the length of the results of Alpha().GetListValues(key).

Player Tracking

Alpha().PlayerConnect(playerID)

This function increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs.

GameServer.Status.Players.Count and GameServer.Status.Players.IDs are then set to update the player count and id list a second from now, unless there is already an update pending, in which case the update joins that batch operation.

PlayerConnect() returns true and adds the playerID to the list of playerIDs if this playerID was not already in the list of connected playerIDs.

If the playerID exists within the list of connected playerIDs, PlayerConnect() will return false, and the list of connected playerIDs will be left unchanged.

An error will be returned if the playerID was not already in the list of connected playerIDs but the player capacity for the server has been reached. The playerID will not be added to the list of playerIDs.

Alpha().PlayerDisconnect(playerID)

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs.

GameServer.Status.Players.Count and GameServer.Status.Players.IDs are then set to update the player count and id list a second from now, unless there is already an update pending, in which case the update joins that batch operation.

PlayerDisconnect() will return true and remove the supplied playerID from the list of connected playerIDs if the playerID value exists within the list.

If the playerID was not in the list of connected playerIDs, the call will return false, and the connected playerID list will be left unchanged.

Alpha().SetPlayerCapacity(count)

Update the GameServer.Status.Players.Capacity value with a new capacity.

Alpha().GetPlayerCapacity()

This function retrieves the current player capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Alpha().GetPlayerCount()

This function retrieves the current player count. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Alpha().IsPlayerConnected(playerID)

This function returns if the playerID is currently connected to the GameServer. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Alpha().GetConnectedPlayers()

This function returns the list of the currently connected player ids. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Writing your own SDK

If there isn’t an SDK for the language and platform you are looking for, you have several options:

gRPC Client Generation

If client generation is well supported by gRPC, then generate client(s) from the proto files found in the proto/sdk, directory and look at the current sdks to see how the wrappers are implemented to make interaction with the SDK server simpler for the user.

REST API Implementation

If client generation is not well supported by gRPC, or if there are other complicating factors, implement the SDK through the REST HTTP+JSON interface. This could be written by hand, or potentially generated from the Swagger/OpenAPI Specifications.

Finally, if you build something that would be usable by the community, please submit a pull request!

SDK Conformance Test

There is a tool SDK server Conformance checker which will run Local SDK server and record all requests your client is performing.

In order to check that SDK is working properly you should write simple SDK test client which would use all methods of your SDK.

Also to test that SDK client is receiving valid Gameserver data, your binary should set the same Label value as creation timestamp which you will receive as a result of GameServer() call and Annotation value same as gameserver UID received by Watch gameserver callback.

Complete list of endpoints which should be called by your test client is the following:

ready,allocate,setlabel,setannotation,gameserver,health,shutdown,watch

In order to run this test SDK server locally use:

SECONDS=30 make run-sdk-conformance-local

Docker container would timeout in 30 seconds and give your the comparison of received requests and expected requests

For instance you could run Go SDK conformance test and see how the process goes:

SDK_FOLDER=go make run-sdk-conformance-test

In order to add test client for your SDK, write sdktest.sh and Dockerfile. Refer to Golang SDK Conformance testing directory structure.

Building the Tools

If you wish to build the binaries from source the make target build-agones-sdk-binary will compile the necessary binaries for all supported operating systems (64 bit windows, linux and osx).

You can find the binaries in the bin folder in `cmd/sdk-server` once compilation is complete.

See Developing, Testing and Building Code Blind for more details.

5.3.1 - Unreal Engine Game Server Client Plugin

This is the Unreal Engine Code Blind Game Server Client Plugin.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Additional methods have been added for ease of use (both of which are enabled by default):

  • Connect
    • will call /gameserver till a succesful response is returned and then call /ready.
    • disabled by setting bDisableAutoConnect to true.
    • An event is broadcast with the GameServer data once the /gameserver call succeeds.
  • Health
    • calls /health endpoint on supplied rate
    • enabled by default with 10 second rate
    • disabled by default by setting HealthRateSeconds to 0.

Both of the above are automatically kicked off in the BeginPlay of the component.

Download

Download the source from the Releases Page or directly from GitHub.

Resources

Unreal is a game engine that is used by anyone from hobbyists all the way through to huge AAA Game Stuidos.

With this in mind there is a vast amount to learn to run a production game using Unreal, even before you get to learning how it integrates with Code Blind. If you want to kick the tires with a starter project you will probably be fine with one of the starter projects out of the box.

However as your Unreal/Code Blind project gets more advanced you will want to understand more about the engine itself and how it can be used to integrate with this project. There will be different ways of interacting via in Play In Editor (PIE) versus running as an actual dedicated game server packaged into a container.

There are few helpful links for latest Unreal Engine 5:

If you use Unreal Engine 4, There are few helpful links for it:

Getting Started

This is a SDK inspired by the REST API to the Code Blind sidecars that allows engineers to communicate with the sidecar from either C++ or Blueprints.

Getting the Code

Easiest way to get this code is to clone the repository and drop the entire plugin folder into your own Plugins folder. This runs the plugin as a Project plugin rather than an engine plugin.

We could however turn this into a marketplace plugin that can be retrived from the marketplace directly into the UE editor.

Using C++ (UE5/UE4)

  • Add Plugin (in your own .uproject file)
  "Plugins": [
    {
      "Enabled": true,
      "Name": "Code Blind"
    }
  ],
  • Add Plugin (in your own *.Build.cs)
PublicDependencyModuleNames.AddRange(
    new[]
    {
        "Code Blind",
    });
  • Add component in header
#include "AgonesComponent.h"

UPROPERTY(EditAnywhere, BlueprintReadWrite)
UAgonesComponent* AgonesSDK;
  • Initialize component in GameMode
#include "AgonesComponent.h"
#include "Classes.h"

ATestGameMode::ATestGameMode()
{
	AgonesSDK = CreateDefaultSubobject<UAgonesComponent>(TEXT("AgonesSDK"));
}
  • Use the Code Blind component to call PlayerReady
void APlatformGameSession::PostLogin(APlayerController* NewPlayer)
{
  // Empty brances are for callbacks on success and errror.
  AgonesSDK->PlayerConnect("netspeak-player", {}, {});
}

Using Blueprints (UE5)

  • Add Component to your Blueprint GameMode component

  • This will automatically call /health every 10 seconds and once /gameserver calls are succesful it will call /ready.

  • Accessing other functionality of Code Blind can be done via adding a node in Blueprints. actions

Using Blueprints (UE4)

  • Add Component to your Blueprint GameMode component

  • This will automatically call /health every 10 seconds and once /gameserver calls are succesful it will call /ready.

  • Accessing other functionality of Code Blind can be done via adding a node in Blueprints. actions

Configuration Options

A number of options can be altered via config files in Unreal these are supplied via Game configuration eg. DefaultGame.ini.

[/Script/Code Blind.AgonesComponent]
HttpPort=1337
HealthRateSeconds=5.0
bDisableAutoConnect=true

Unreal Hooks

Within the Unreal GameMode and GameSession exist a number of useful existing funtions that can be used to fit in with making calls out to Code Blind.

A few examples are:

  • RegisterServer to call SetLabel, SetPlayerCapacity
  • PostLogin to call PlayerConnect
  • NotifyLogout to call PlayerDisconnect

5.3.2 - Unity Game Server Client SDK

This is the Unity version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Additional methods have been added for ease of use:

  • Connect

Installation

The client SDK code can be manually downloaded and added to your project hierarchy.

It can also be imported into your project via the Unity Package Manager (UPM). To do that, open your project’s manifest.json file, and add the following line to the dependencies section:

{
  "dependencies": {
        "com.googleforgames.agones": "https://github.com/googleforgames/agones.git?path=/sdks/unity",
...

If you want a specific release, the dependency can be pinned to that version. For example:

"com.googleforgames.agones": "https://github.com/googleforgames/agones.git?path=/sdks/unity#v1.38.0",

Download

Download the source directly from GitHub.

Prerequisites

  • Unity >= 2018.x (.NET 4.x)

Usage

Import this script to your unity project and attach it to GameObject.

To begin working with the SDK, get an instance of it.

var agones = agonesGameObject.GetComponent<Code Blind.AgonesSdk>();

To connect to the SDK server, either local or when running on Code Blind, run the async Connect() method. This will wait for up to 30 seconds if the SDK server has not yet started and the connection cannot be made, and will return false if there was an issue connecting.

bool ok = await agones.Connect();

To mark the game server as ready to receive player connections, call the async method Ready().

async void SomeMethod()
{
    bool ok = await agones.Ready();
}

To get the details on the backing GameServer call GameServer().

Will return null if there is an error in retrieving the GameServer record.

var gameserver = await agones.GameServer();

To mark the GameServer as Reserved for a duration call Reserve(TimeSpan duration).

ok = await agones.Reserve(duration);

To mark that the game session is completed and the game server should be shut down call Shutdown().

bool ok = await agones.Shutdown();

Similarly SetAnnotation(string key, string value) and SetLabel(string key, string value) are async methods that perform an action.

And there is no need to call Health(), it is automatically called.

To watch when the backing GameServer configuration changes call WatchGameServer(callback), where the delegate function callback will be executed every time the GameServer configuration changes.

agones.WatchGameServer(gameServer => Debug.Log($"Server - Watch {gameServer}"));

Player Tracking

To use alpha features use the AgonesAlphaSDK class.

var agones = agonesGameObject.GetComponent<Code Blind.AgonesAlphaSdk>();

Alpha: PlayerConnect

This method increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs. Returns true and adds the playerID to the list of playerIDs if the playerIDs was not already in the list of connected playerIDs.

bool ok = await agones.PlayerConnect(playerId);

Alpha: PlayerDisconnect

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs. Will return true and remove the supplied playerID from the list of connected playerIDs if the playerID value exists within the list.

bool ok = await agones.PlayerDisconnect(playerId);

Alpha: SetPlayerCapacity

Update the GameServer.Status.Players.Capacity value with a new capacity.

var capacity = 100;
bool ok = await agones.SetPlayerCapacity(capacity);

Alpha: GetPlayerCapacity

This function retrieves the current player capacity GameServer.Status.Players.Capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

long capacity = await agones.GetPlayerCapacity();

Alpha: GetPlayerCount

Returns the current player count

long count = await agones.GetPlayerCount();

Alpha: IsPlayerConnected

This returns if the playerID is currently connected to the GameServer. This is always accurate, even if the value hasn’t been updated to the GameServer status yet.

bool isConnected = await agones.IsPlayerConnected(playerId);

Alpha: GetConnectedPlayers

This returns a list of the playerIDs that are currently connected to the GameServer.

List<string> players = await agones.GetConnectedPlayers();

Settings

The properties for the Unity Code Blind SDK can be found in the Inspector.

  • Health Interval Second
    • Interval of the server sending a health ping to the Code Blind sidecar. (default: 5)
  • Health Enabled
    • Whether the server sends a health ping to the Code Blind sidecar. (default: true)
  • Log Enabled
    • Debug Logging Enabled. Debug logging for development of this Plugin. (default: false)

5.3.3 - C++ Game Server Client SDK

This is the C++ version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues

Installation

Download

Download the source from the Releases Page or directly from GitHub.

Building the Libraries from source

CMake is used to build SDK for all supported platforms (Linux/Window/macOS).

Prerequisites

  • CMake >= 3.15.0
  • Git
  • C++17 compiler

Dependencies

Code Blind SDK only depends on the gRPC library.

If CMake cannot find gRPC with find_package(), it downloads and builds gRPC. There are some extra prerequisites for OpenSSL on Windows, see documentation:

  • Perl
  • NASM

Note that OpenSSL is not used in Code Blind SDK, but it is required to have a successful build of gRPC.

Options

Following options are available:

  • AGONES_THIRDPARTY_INSTALL_PATH (default is CMAKE_INSTALL_PREFIX) - installation path for Code Blind prerequisites (used only if gRPC and Protobuf are not found by find_package)
  • AGONES_ZLIB_STATIC (default is ON) - use static version of zlib for gRPC

(Windows only):

  • AGONES_BUILD_THIRDPARTY_DEBUG (default is OFF) - build both debug and release versions of SDK’s prerequisites. Option is not used if you already have built gRPC.
  • AGONES_OPENSSL_CONFIG_STRING (default is VC-WIN64A) - arguments to configure OpenSSL build (documentation). Used only if OpenSSL and gRPC is built by Code Blind.

Linux / MacOS

mkdir -p .build
cd .build
cmake .. -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX=./install
cmake --build . --target install

Windows

Building with Visual Studio:

md .build
cd .build
cmake .. -G "Visual Studio 15 2017 Win64" -DCMAKE_INSTALL_PREFIX=./install
cmake --build . --config Release --target install

Building with NMake

md .build
cd .build
cmake .. -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./install
cmake --build . --target install

CMAKE_INSTALL_PREFIX may be skipped if it is OK to install Code Blind SDK to a default location (usually /usr/local or c:/Program Files/Code Blind).

CMake option -Wno-dev is specified to suppress CMP0048 deprecation warning for gRPC build.

If AGONES_ZLIB_STATIC is set to OFF, ensure that you have installed zlib. For Windows, it’s enough to copy zlib.dll near to gameserver executable. For Linux/Mac usually no actions are needed.

Usage

Using SDK

In CMake-based projects it’s enough to specify a folder where SDK is installed with CMAKE_PREFIX_PATH and use find_package(agones CONFIG REQUIRED) command. For example: cpp-simple. It may be useful to disable some protobuf warnings in your project.

Usage

The C++ SDK is specifically designed to be as simple as possible, and deliberately doesn’t include any kind of singleton management, or threading/asynchronous processing to allow developers to manage these aspects as they deem appropriate for their system.

We may consider these types of features in the future, depending on demand.

To begin working with the SDK, create an instance of it:

agones::SDK *sdk = new agones::SDK();

To connect to the SDK server, either local or when running on Code Blind, run the sdk->Connect() method. This will block for up to 30 seconds if the SDK server has not yet started and the connection cannot be made, and will return false if there was an issue connecting.

bool ok = sdk->Connect();

To send a health check call sdk->Health(). This is a synchronous request that will return false if it has failed in any way. Read GameServer Health Checking for more details on the game server health checking strategy.

bool ok = sdk->Health();

To mark the game server as ready to receive player connections, call sdk->Ready(). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Ready();
if (!status.ok()) { ... }

To mark the game server as allocated, call sdk->Allocate(). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Allocate();
if (!status.ok()) { ... }

To mark the game server as reserved, call sdk->Reserve(seconds). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Reserve(std::chrono::seconds(N));
if (!status.ok()) { ... }

To mark that the game session is completed and the game server should be shut down call sdk->Shutdown(). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->Shutdown();
if (!status.ok()) { ... }

To set a Label on the backing GameServer call sdk->SetLabel(key, value). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

grpc::Status status = sdk->SetLabel("test-label", "test-value");
if (!status.ok()) { ... }

To set an Annotation on the backing GameServer call sdk->SetAnnotation(key, value). This will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

For more information you can also look at the gRPC Status reference.

status = sdk->SetAnnotation("test-annotation", "test value");
if (!status.ok()) { ... }

To get the details on the backing GameServer call sdk->GameServer(&gameserver), passing in a agones::dev::sdk::GameServer* to push the results of the GameServer configuration into.

This function will return a grpc::Status object, from which we can call status.ok() to determine if the function completed successfully.

agones::dev::sdk::GameServer gameserver;
grpc::Status status = sdk->GameServer(&gameserver);
if (!status.ok()) {...}

To get updates on the backing GameServer as they happen, call sdk->WatchGameServer([](const agones::dev::sdk::GameServer& gameserver){...}).

This will call the passed in std::function synchronously (this is a blocking function, so you may want to run it in its own thread) whenever the backing GameServer is updated.

sdk->WatchGameServer([](const agones::dev::sdk::GameServer& gameserver){
  std::cout << "GameServer Update:\n"                                 //
            << "\tname: " << gameserver.object_meta().name() << "\n"  //
            << "\tstate: " << gameserver.status().state() << "\n"
            << std::flush;
});

Next Steps

5.3.4 - Go Game Server Client SDK

This is the Go version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount✔️
CountersSetCounterCount✔️
CountersIncrementCounter✔️
CountersDecrementCounter✔️
CountersSetCounterCapacity✔️
CountersGetCounterCapacity✔️
ListsAppendListValue✔️
ListsDeleteListValue✔️
ListsSetListCapacity✔️
ListsGetListCapacity✔️
ListsListContains✔️
ListsGetListLength✔️
ListsGetListValues✔️
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Installation

go get the source, directly from GitHub

Usage

Review the GoDoc for usage instructions

5.3.5 - C# Game Server Client SDK

This is the C# version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGetGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Download

Download the source directly from GitHub.

Install using NuGet

  • Download the nuget package directly
  • Install the latest version using the Package Manager: Install-Package AgonesSDK
  • Install the latest version using the .NET CLI: dotnet add package AgonesSDK

To select a specific version, append --version, for example: --version 1.8.0 to either commands.

Prerequisites

  • .Net Standard 2.0 compliant framework.

Usage

Reference the SDK in your project & create a new instance of the SDK wrapper:

Initialization

To use the AgonesSDK, you will need to import the namespace by adding using Code Blind; at the beginning of your relevant files.

var agones = new AgonesSDK();

Connection

To connect to the SDK server, either locally or when running on Code Blind, run the ConnectAsync() method. This will wait for up to 30 seconds if the SDK server has not yet started and the connection cannot be made, and will return false if there was an issue connecting.

bool ok = await agones.ConnectAsync();

Ready

To mark the game server as ready to receive player connections, call the async method ReadyAsync().

async void SomeMethod()
{
    var status = await agones.ReadyAsync();
}

Health

To send Health pings, call the async method HealthAsync()

await agones.HealthAsync();

GetGameServer

To get the details on the backing GameServer call GetGameServerAsync().

Will return null if there is an error in retrieving the GameServer record.

var gameserver = await agones.GetGameServerAsync();

Reserve

To mark the GameServer as Reserved for a duration call ReserveAsync(long duration).

long duration = 30;
var status = await agones.ReserveAsync(duration);

ShutDown

To mark that the game session is completed and the game server should be shut down call ShutdownAsync().

var status = await agones.ShutdownAsync();

SetAnnotation & SetLabel

Similarly SetAnnotation(string key, string value) and SetLabel(string key, string value) are async methods that perform an action & return a Status object.

WatchGameServer

To watch when the backing GameServer configuration changes call WatchGameServer(callback), where the delegate function callback of type Action<GameServer> will be executed every time the GameServer configuration changes. This process is non-blocking internally.

agonesSDK.WatchGameServer((gameServer) => { Console.WriteLine($"Server - Watch {gameServer}");});

Player Tracking

Alpha: PlayerConnect

This method increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs. Returns true and adds the playerID to the list of playerIDs if the playerIDs was not already in the list of connected playerIDs.

bool ok = await agones.Alpha().PlayerConnectAsync(playerId);

Alpha: PlayerDisconnect

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs. Will return true and remove the supplied playerID from the list of connected playerIDs if the playerID value exists within the list.

bool ok = await agones.Alpha().PlayerDisconnectAsync(playerId);

Alpha: SetPlayerCapacity

Update the GameServer.Status.Players.Capacity value with a new capacity.

var capacity = 100;
var status = await agones.Alpha().SetPlayerCapacityAsync(capacity);

Alpha: GetPlayerCapacity

This function retrieves the current player capacity GameServer.Status.Players.Capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

long cap = await agones.Alpha().GetPlayerCapacityAsync();

Alpha: GetPlayerCount

Returns the current player count

long count = await agones.Alpha().GetPlayerCountAsync();

Alpha: IsPlayerConnected

This returns if the playerID is currently connected to the GameServer. This is always accurate, even if the value hasn’t been updated to the GameServer status yet.

var playerId = "player1";
bool isConnected = await agones.Alpha().IsPlayerConnectedAsync(playerId);

Remarks

  • All requests other than ConnectAsync will wait for up to 15 seconds before giving up, time to wait can also be set in the constructor.
  • Default host & port are localhost:9357
  • Methods that do not return a data object such as GameServer will return a gRPC Grpc.Core.Status object. To check the state of the request, check Status.StatusCode & Status.Detail. Ex:
if(status.StatusCode == StatusCode.OK)
    //do stuff

5.3.6 - Node.js Game Server Client SDK

This is the Node.js version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGetGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Prerequisites

  • Node.js >= 10.13.0

Usage

Add the agones dependency to your project:

npm install @google-cloud/agones-sdk

If you need to download the source, rather than install from NPM, you can find it on GitHub.

To begin working with the SDK, create an instance of it.

const AgonesSDK = require('@google-cloud/agones-sdk');

let agonesSDK = new AgonesSDK();

To connect to the SDK server, either local or when running on Code Blind, run the async method sdk.connect(), which will resolve once connected or reject on error or if no connection can be made after 30 seconds.

await agonesSDK.connect();

To send a health check ping call health(errorCallback). The error callback is optional and if provided will receive an error whenever emitted from the health check stream.

agonesSDK.health((error) => {
	console.error('error', error);
});

To mark the game server as ready to receive player connections, call the async method ready(). The result will be an empty object in this case.

let result = await agonesSDK.ready();

Similarly shutdown(), allocate(), setAnnotation(key, value) and setLabel(key, value) are async methods that perform an action and return an empty result.

To get details of the backing GameServer call the async method getGameServer(). The result will be an object representing GameServer defined in `sdk.proto`.

let result = await agonesSDK.getGameServer();

To get updates on the backing GameServer as they happen, call watchGameServer(callback, errorCallback). The callback will be called with a parameter matching the result of getGameServer(). The error callback is optional and if provided will receive an error whenever emitted from the watch stream.

agonesSDK.watchGameServer((result) => {
	console.log('watch', result);
}, (error) => {
	console.error('error', error);
});

To mark the game server as reserved for a period of time, call the async method reserve(seconds). The result will be an empty object.

For more information, please read the SDK Overview, check out agonesSDK.js and also look at the Node.js example.

5.3.7 - Rust Game Server Client SDK

This is the Rust version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGameServer✔️
ConfigurationWatch✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounterCount
CountersSetCounterCount
CountersIncrementCounter
CountersDecrementCounter
CountersSetCounterCapacity
CountersGetCounterCapacity
ListsAppendListValue
ListsDeleteListValue
ListsSetListCapacity
ListsGetListCapacity
ListsListContains
ListsGetListLength
ListsGetListValues
Player TrackingGetConnectedPlayers✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingGetPlayerCount✔️
Player TrackingIsPlayerConnected✔️
Player TrackingPlayerConnect✔️
Player TrackingPlayerDisconnect✔️
Player TrackingSetPlayerCapacity✔️

Prerequisites

Usage

Add this crate to dependencies section in your Cargo.toml.

Also note that the SDK is async only, so you will need an async runtime to execute the futures exposed by the SDK. It is recommended to use tokio as the SDK already depends on tokio due to its choice of gRPC library, tonic.

[dependencies]
agones = "1.34.0"
tokio = { version = "1.32.0", features = ["macros", "sync"] }

To begin working with the SDK, create an instance of it.

use std::time::Duration;

#[tokio::main]
async fn main() {
    let mut sdk = agones::Sdk::new(None /* default port */, None /* keep_alive */)
        .await
        .expect("failed to connect to SDK server");
}

To send health checks, call sdk.health_check, which will return a tokio::sync::mpsc::Sender::<()> which will send a health check every time a message is posted to the channel.

let health = sdk.health_check();
if health.send(()).await.is_err() {
    eprintln!("the health receiver was closed");
}

To mark the game session as ready call sdk.ready().

sdk.ready().await?;

To mark the game server as reserved for a period of time, call sdk.reserve(duration).

sdk.reserve(Duration::new(5, 0)).await?;

To mark that the game session is completed and the game server should be shut down call sdk.shutdown().

if let Err(e) = sdk.shutdown().await {
    eprintln!("Could not run Shutdown: {}", e);
}

To set a Label on the backing GameServer call sdk.set_label(key, value).

sdk.set_label("test-label", "test-value").await?;

To set an Annotation on the backing GameServer call sdk.set_annotation(key, value).

sdk.set_annotation("test-annotation", "test value").await?;

To get details of the backing GameServer call sdk.get_gameserver().

The function will return an instance of agones::types::GameServer including GameServer configuration info.

let gameserver = sdk.get_gameserver().await?;

To get updates on the backing GameServer as they happen, call sdk.watch_gameserver.

This will stream updates and endlessly until the stream is closed, so it is recommended to push this into its own async task.

let _watch = {
    // We need to clone the SDK as we are moving it to another task
    let mut watch_client = sdk.clone();
    // We use a simple oneshot to signal to the task when we want it to shutdown
    // and stop watching the gameserver update stream
    let (tx, mut rx) = tokio::sync::oneshot::channel::<()>();

    tokio::task::spawn(async move {
        println!("Starting to watch GameServer updates...");
        match watch_client.watch_gameserver().await {
            Err(e) => eprintln!("Failed to watch for GameServer updates: {}", e),
            Ok(mut stream) => loop {
                tokio::select! {
                    // We've received a new update, or the stream is shutting down
                    gs = stream.message() => {
                        match gs {
                            Ok(Some(gs)) => {
                                println!("GameServer Update, name: {}", gs.object_meta.unwrap().name);
                                println!("GameServer Update, state: {}", gs.status.unwrap().state);
                            }
                            Ok(None) => {
                                println!("Server closed the GameServer watch stream");
                                break;
                            }
                            Err(e) => {
                                eprintln!("GameServer Update stream encountered an error: {}", e);
                            }
                        }

                    }
                    // The watch is being dropped so we're going to shutdown the task
                    // and the watch stream
                    _ = &mut rx => {
                        println!("Shutting down GameServer watch loop");
                        break;
                    }
                }
            },
        }
    });

    tx
};

For more information, please read the SDK Overview, check out agones sdk implementation and also look at the Rust example.

5.3.8 - REST Game Server Client API

This is the REST version of the Code Blind Game Server Client SDK.

Check the Client SDK Documentation for more details on each of the SDK functions and how to run the SDK locally.

SDK Functionality

AreaActionImplemented
LifecycleReady✔️
LifecycleHealth✔️
LifecycleReserve✔️
LifecycleAllocate✔️
LifecycleShutdown✔️
ConfigurationGetGameServer✔️
ConfigurationWatchGameServer✔️
MetadataSetAnnotation✔️
MetadataSetLabel✔️
CountersGetCounter✔️
CountersUpdateCounter✔️
ListsGetList✔️
ListsUpdateList✔️
ListsAddListValue✔️
ListsRemoveListValue✔️
Player TrackingGetPlayerCapacity✔️
Player TrackingSetPlayerCapacity✔️
Player TrackingPlayerConnect✔️
Player TrackingGetConnectedPlayers✔️
Player TrackingIsPlayerConnected✔️
Player TrackingGetPlayerCount✔️
Player TrackingPlayerDisconnect✔️

The REST API can be accessed from http://localhost:${AGONES_SDK_HTTP_PORT}/ from the game server process. AGONES_SDK_HTTP_PORT is an environment variable automatically set for the game server process by Code Blind to support binding the REST API to a dynamic port. It is advised to use the environment variable rather than a hard coded port; otherwise your game server will not be able to contact the SDK server if it is configured to use a non-default port.

Generally the REST interface gets used if gRPC isn’t well supported for a given language or platform.

Generating clients

While you can hand write REST integrations, we also have a set of generated OpenAPI/Swagger definitions available. This means you can use OpenAPI/Swagger tooling to generate clients as well, if you need them.

For example, to create a cpp client for the stable sdk endpoints (to be run in the agones home directory):

docker run --rm -v ${PWD}:/local swaggerapi/swagger-codegen-cli generate -i /local/sdks/swagger/sdk.swagger.json  -l cpprest -o /local/out/cpp

The same could be run for alpha.swagger.json and beta.swagger.json as required.

You can read more about OpenAPI/Swagger code generation in their Command Line Tool Documentation

Reference

Lifecycle Management

Ready

Call when the GameServer is ready to accept connections

  • Path: /ready
  • Method: POST
  • Body: {}
Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/ready

Health

Send a Empty every d Duration to declare that this GameServer is healthy

  • Path: /health
  • Method: POST
  • Body: {}
Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/health

Reserve

Move Gameserver into a Reserved state for a certain amount of seconds for the future allocation.

  • Path: /reserve
  • Method: POST
  • Body: {"seconds": "5"}
Example
curl -d '{"seconds": "5"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/reserve

Allocate

With some matchmakers and game matching strategies, it can be important for game servers to mark themselves as Allocated. For those scenarios, this SDK functionality exists.

Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/allocate

Shutdown

Call when the GameServer session is over and it’s time to shut down

  • Path: /shutdown
  • Method: POST
  • Body: {}
Example
curl -d "{}" -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/shutdown

Configuration Retrieval

GameServer

Call when you want to retrieve the backing GameServer configuration details

  • Path: /gameserver
  • Method: GET
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/gameserver

Response:

{
    "object_meta": {
        "name": "local",
        "namespace": "default",
        "uid": "1234",
        "resource_version": "v1",
        "generation": "1",
        "creation_timestamp": "1531795395",
        "annotations": {
            "annotation": "true"
        },
        "labels": {
            "islocal": "true"
        }
    },
    "status": {
        "state": "Ready",
        "address": "127.0.0.1",
        "ports": [
            {
                "name": "default",
                "port": 7777
            }
        ]
    }
}

Watch GameServer

Call this when you want to get updates of when the backing GameServer configuration is updated.

These updates will come as newline delimited JSON, send on each update. To that end, you will want to keep the http connection open, and read lines from the result stream and and process as they come in.

curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/watch/gameserver

Response:

{"result":{"object_meta":{"name":"local","namespace":"default","uid":"1234","resource_version":"v1","generation":"1","creation_timestamp":"1533766607","annotations":{"annotation":"true"},"labels":{"islocal":"true"}},"status":{"state":"Ready","address":"127.0.0.1","ports":[{"name":"default","port":7777}]}}}
{"result":{"object_meta":{"name":"local","namespace":"default","uid":"1234","resource_version":"v1","generation":"1","creation_timestamp":"1533766607","annotations":{"annotation":"true"},"labels":{"islocal":"true"}},"status":{"state":"Ready","address":"127.0.0.1","ports":[{"name":"default","port":7777}]}}}
{"result":{"object_meta":{"name":"local","namespace":"default","uid":"1234","resource_version":"v1","generation":"1","creation_timestamp":"1533766607","annotations":{"annotation":"true"},"labels":{"islocal":"true"}},"status":{"state":"Ready","address":"127.0.0.1","ports":[{"name":"default","port":7777}]}}}

The Watch GameServer stream is also exposed as a WebSocket endpoint on the same URL and port as the HTTP watch/gameserver API. This endpoint is provided as a convienence for streaming data to clients such as Unreal that support WebSocket but not HTTP streaming, and HTTP streaming should be used instead if possible.

An example command that uses the WebSocket endpoint instead of streaming over HTTP is:

curl -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Sec-WebSocket-Key: ExampleKey1234567890===" -H "Sec-WebSocket-Version: 13" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/watch/gameserver

The data returned from this endpoint is delimited by the boundaries of a WebSocket payload as defined by RFC 6455, section 5.2. When reading from this endpoint, if your WebSocket client does not automatically handle frame reassembly (e.g. Unreal), make sure to read to the end of the WebSocket payload (as defined by the FIN bit) before attempting to parse the data returned. This is transparent in most clients.

Metadata Management

Set Label

Apply a Label with the prefix “agones.dev/sdk-” to the backing GameServer metadata.

See the SDK SetLabel documentation for restrictions.

Example
curl -d '{"key": "foo", "value": "bar"}' -H "Content-Type: application/json" -X PUT http://localhost:${AGONES_SDK_HTTP_PORT}/metadata/label

Set Annotation

Apply an Annotation with the prefix “agones.dev/sdk-” to the backing GameServer metadata

Example
curl -d '{"key": "foo", "value": "bar"}' -H "Content-Type: application/json" -X PUT http://localhost:${AGONES_SDK_HTTP_PORT}/metadata/annotation

Counters and Lists

Counters

In all the Counter examples, we retrieve the counter under the key rooms as if it was previously defined in GameServer.Spec.counters[room].

For your own Counter REST requests, replace the value rooms with your own key in the path.

Alpha: GetCounter

This function retrieves a specified counter by its key, rooms, and returns its information.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/counters/rooms

Response:

{"name":"rooms", "count":"1", "capacity":"10"}
Alpha: UpdateCounter

This function updates the properties of the counter with the key rooms, such as its count and capacity, and returns the updated counter details.

Example
curl -d '{"count": "5", "capacity": "11"}' -H "Content-Type: application/json" -X PATCH http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/counters/rooms

Response:

{"name":"rooms", "count":"5", "capacity":"11"}

Lists

In all the List examples, we retrieve the list under the key players as if it was previously defined in GameServer.Spec.lists[players].

For your own List REST based requests, replace the value players with your own key in the path.

Alpha: GetList

This function retrieves the list’s properties with the key players, returns the list’s information.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players

Response:

{"name":"players", "capacity":"100", "values":["player0", "player1", "player2"]}     
Alpha: UpdateList

This function updates the list’s properties with the key players, such as its capacity and values, returns the updated list details. This will overwrite all existing List.Values with the update list request values. Use addValue or removeValue for modifying the List.Values field.

Example
curl -d '{"capacity": "120", "values": ["player3", "player4"]}' -H "Content-Type: application/json" -X PATCH http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players

Response:

{"name":"players", "capacity":"120", "values":["player3", "player4"]}
Alpha: AddListValue

This function adds a new value to a list with the key players and returns the list with this addition.

Example
curl -d '{"value": "player9"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players:addValue

Response:

{"name":"players", "capacity":"120", "values":["player3", "player4", "player9"]}
Alpha: RemoveListValue

This function removes a value from the list with the key players and returns updated list.

Example
curl -d '{"value": "player3"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/v1alpha1/lists/players:removeValue

Response:

{"name":"players", "capacity":"120", "values":["player4", "player9"]}

Player Tracking

Alpha: PlayerConnect

This function increases the SDK’s stored player count by one, and appends this playerID to GameServer.Status.Players.IDs.

Example
curl -d '{"playerID": "uzh7i"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/connect

Response:

{"bool":true}

Alpha: PlayerDisconnect

This function decreases the SDK’s stored player count by one, and removes the playerID from GameServer.Status.Players.IDs.

Example
curl -d '{"playerID": "uzh7i"}' -H "Content-Type: application/json" -X POST http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/disconnect

Response:

{"bool":true}

Alpha: SetPlayerCapacity

Update the GameServer.Status.Players.Capacity value with a new capacity.

Example
curl -d '{"count": 5}' -H "Content-Type: application/json" -X PUT http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/capacity

Alpha: GetPlayerCapacity

This function retrieves the current player capacity. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -d '{}' -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/capacity

Response:

{"count":"5"}

Alpha: GetPlayerCount

This function retrieves the current player count. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/count

Response:

{"count":"2"}

Alpha: IsPlayerConnected

This function returns if the playerID is currently connected to the GameServer. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/connected/uzh7i

Response:

{"bool":true}

Alpha: GetConnectedPlayers

This function returns the list of the currently connected player ids. This is always accurate from what has been set through this SDK, even if the value has yet to be updated on the GameServer status resource.

Example
curl -H "Content-Type: application/json" -X GET http://localhost:${AGONES_SDK_HTTP_PORT}/alpha/player/connected

Response:

{"list":["uzh7i","3zh7i"]}

5.3.9 - Local Development

Working against the SDK without having to run a full kubernetes stack

When the game server is running on Code Blind, the SDK communicates over TCP to a small gRPC server that Code Blind coordinated to run in a container in the same network namespace as it - usually referred to in Kubernetes terms as a “sidecar”.

Therefore, when developing locally, we also need a process for the SDK to connect to!

To do this, we can run the same binary (the SDK Server) that runs inside Code Blind, but pass in a flag to run it in “local mode”. Local mode means that the sidecar binary will not try to connect to anything, and will just send log messages to stdout and persist local state in memory so that you can see exactly what the SDK in your game server is doing, and can confirm everything works.

Running the SDK Server

To run the SDK Server, you will need a copy of the binary. This can either be done by downloading a prebuilt binary or running from source code. This guide will focus on running from the prebuilt binary, but details about running from source code can be found below.

To run the prebuilt binary, for the latest release, you will need to download agonessdk-server-1.38.0.zip , and unzip it. You will find the executables for the SDK server, for each type of operating system.

  • MacOS
    • sdk-server.darwin.amd64
    • sdk-server.darwin.arm64
  • Linux
    • sdk-server.linux.amd64
    • sdk-server.linux.arm64
  • Windows
    • sdk-server.windows.amd64.exe

Running In “Local Mode”

To run in local mode, pass the flag --local to the executable.

For example:

./sdk-server.linux.amd64 --local

You should see output similar to the following:

{"ctlConf":{"Address":"localhost","IsLocal":true,"LocalFile":"","Delay":0,"Timeout":0,"Test":"","GRPCPort":9357,"HTTPPort":9358},"message":"Starting sdk sidecar","severity":"info","source":"main","time":"2019-10-30T21:44:37.973139+03:00","version":"1.1.0"}
{"grpcEndpoint":"localhost:9357","message":"Starting SDKServer grpc service...","severity":"info","source":"main","time":"2019-10-30T21:44:37.974585+03:00"}
{"httpEndpoint":"localhost:9358","message":"Starting SDKServer grpc-gateway...","severity":"info","source":"main","time":"2019-10-30T21:44:37.975086+03:00"}
{"message":"Ready request has been received!","severity":"info","time":"2019-10-30T21:45:47.031989+03:00"}
{"message":"gameserver update received","severity":"info","time":"2019-10-30T21:45:47.03225+03:00"}
{"message":"Shutdown request has been received!","severity":"info","time":"2019-10-30T21:46:18.179341+03:00"}
{"message":"gameserver update received","severity":"info","time":"2019-10-30T21:46:18.179459+03:00"}

Enabling Feature Gates

For development and testing purposes, you might want to enable specific features gates in the local SDK Server.

To do this, you can either set the FEATURE_GATES environment variable or use the --feature-gates command line parameter like so, with the same format as utilised when configuring it on a Helm install.

For example:

./sdk-server.linux.amd64 --local --feature-gates Example=true

or

FEATURE_GATES=Example=true ./sdk-server.linux.amd64 --local

Providing your own GameServer configuration for local development

By default, the local sdk-server will create a default GameServer configuration that is used for GameServer() and WatchGameServer() SDK calls. If you wish to provide your own configuration, as either yaml or json, this can be passed through as either --file or -f along with the --local flag.

If the GamerServer configuration file is changed while the local server is running, this will be picked up by the local server, and will change the current active configuration, as well as sending out events for WatchGameServer(). This is a useful way of testing functionality, such as changes of state from Ready to Allocated in your game server code.

For example:

wget https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserver.yaml
./sdk-server.linux.amd64 --local -f ./gameserver.yaml
{"ctlConf":{"Address":"localhost","IsLocal":true,"LocalFile":"./gameserver.yaml","Delay":0,"Timeout":0,"Test":"","GRPCPort":9357,"HTTPPort":9358},"message":"Starting sdk sidecar","severity":"info","source":"main","time":"2019-10-30T21:47:45.742776+03:00","version":"1.1.0"}
{"filePath":"/Users/alexander.apalikov/Downloads/agonessdk-server-1.1.0/gameserver.yaml","message":"Reading GameServer configuration","severity":"info","time":"2019-10-30T21:47:45.743369+03:00"}
{"grpcEndpoint":"localhost:9357","message":"Starting SDKServer grpc service...","severity":"info","source":"main","time":"2019-10-30T21:47:45.759692+03:00"}
{"httpEndpoint":"localhost:9358","message":"Starting SDKServer grpc-gateway...","severity":"info","source":"main","time":"2019-10-30T21:47:45.760312+03:00"}

Changing State of a Local GameServer

Some SDK calls would change the GameServer state according to GameServer State Diagram. Also local SDK server would persist labels and annotations updates.

Here is a complete list of these commands: ready, allocate, setlabel, setannotation, shutdown, reserve.

For example call to Reserve() for 30 seconds would change the GameServer state to Reserve and if no call to Allocate() occurs it would return back to Ready state after this period.

All changes to the GameServer state could be observed and retrieved using Watch() or GameServer() methods using GameServer SDK.

Example of using HTTP gateway locally:

curl -X POST "http://localhost:9358/ready" -H "accept: application/json" -H "Content-Type: application/json" -d "{}"
{}
curl -GET "http://localhost:9358/gameserver" -H "accept: application/json"
{"object_meta":{"creation_timestamp":"-62135596800"},"spec":{"health":{}},"status":{"state":"Ready"}}
curl -X PUT "http://localhost:9358/metadata/label" -H "accept: application/json" -H "Content-Type: application/json" -d "{ \"key\": \"foo\", \"value\": \"bar\"}"
curl -GET "http://localhost:9358/gameserver" -H "accept: application/json"
{"object_meta":{"creation_timestamp":"-62135596800","labels":{"agones.dev/sdk-foo":"bar"}},"spec":{"health":{}},"status":{"state":"Ready"}}

Running Local Mode in a Container

Once you have your game server process in a container, you may also want to test the container build locally as well.

Since the production agones-sdk binary has the --local mode built in, you can also use the production container image locally as well!

Since the SDK and your game server container need to share a port on localhost, one of the easiest ways to do that is to have them both run using the host network, like so:

In one shell run:

docker run --network=host --rm us-docker.pkg.dev/agones-images/release/agones-sdk:1.38.0 --local

You should see a similar output to what you would if you were running the binary directly, i.e. outside a container.

Then in another shell, start your game server container:

docker run --network=host --rm <your image here>

If you want to mount a custom gameserver.yaml, this is also possible:

wget https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserver.yaml
# required so that the `agones` user in the container can read the file
chmod o+r gameserver.yaml
docker run --network=host --rm -v $(pwd)/gameserver.yaml:/tmp/gameserver.yaml us-docker.pkg.dev/agones-images/release/agones-sdk:1.38.0 --local -f /tmp/gameserver.yaml

If you run Docker on a OS that doesn’t run Docker natively or in a VM, such as on Windows or macOS, you may want to to run the ClientSDK and your game server container together with Docker Compose. To do so, create a docker-compose.yaml file setup with a network overlay shared between them:

version: '3'
services:
  gameserver:
    build: . # <path to build context>
    ports:
      - "127.0.0.1:7777:7777/udp"

  sdk-server:
    image: "us-docker.pkg.dev/agones-images/release/agones-sdk:1.38.0"
    command: --local -f /gs_config
    network_mode: service:gameserver # <shared network between sdk and game server>
    configs:
      - gs_config

configs:
  gs_config:
    file: ./gameserver.yaml

Run docker-compose

docker-compose up --build

Running from source code instead of prebuilt binary

If you wish to run from source rather than pre-built binaries, that is an available alternative. You will need Go installed and will need to clone the Code Blind GitHub repo.

Disclaimer: Code Blind is run and tested with the version of Go specified by the GO_VERSION variable in the project’s build Dockerfile. Other versions are not supported, but may still work.

Your cloned repository is best switched to the latest specific release’s branch/tag. For example:

git clone https://github.com/googleforgames/agones.git
cd agones
git checkout release-1.38.0

With Go installed and the Code Blind repository cloned, the SDK Server can be run with the following command (from the Code Blind clone directory):

go run cmd/sdk-server/main.go --local

Commandline flags (e.g. --local) are exactly the same as command line flags when utilising a pre-built binary.

Next Steps:

  • Learn how to connect your local development game server binary into a running Code Blind Kubernetes cluster for even more live development options with an out of cluster dev server.

5.4 - Windows Gameservers

Run GameServers on Kubernetes nodes with the Windows operating system.

Prerequisites

The following prerequisites are required to create a GameServer:

  1. A Kubernetes cluster with the UDP port range 7000-8000 open on each node.
  2. Code Blind controller installed in the targeted cluster
  3. kubectl properly configured
  4. Netcat which is already installed on most Linux/macOS distributions, for windows you can use WSL.

If you don’t have a Kubernetes cluster you can follow these instructions to create a cluster on Google Kubernetes Engine (GKE), Minikube or Azure Kubernetes Service (AKS), and install Code Blind.

For the purpose of this guide we’re going to use the simple-game-server example as the GameServer container. This example is a very simple UDP server written in Go. Don’t hesitate to look at the code of this example for more information.

Ensure that you have some nodes to your cluster that are running Windows.

Objectives

  • Create a GameServer on a Windows node.
  • Connect to the GameServer.

1. Create a GameServer

Create a GameServer using the following command:

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/simple-game-server/gameserver-windows.yaml

You should see a successful output similar to this:

gameserver.agones.dev/simple-game-server-4ss4j created

Verify that the GameServer becomes Ready by running:

kubectl get gameservers

It should look something like this:

NAME                       STATE     ADDRESS       PORT   NODE     AGE
simple-game-server-7pjrq   Ready   35.233.183.43   7190   agones   3m

Take a note of the Game Server IP address and ports.

For the full details of the YAML file head to the GameServer Specification Guide

2. Connect to the GameServer

You can now communicate with the Game Server:

nc -u {IP} {PORT}
Hello World !
ACK: Hello World !
EXIT

You can finally type EXIT which tells the SDK to run the Shutdown command, and therefore shuts down the GameServer.

If you run kubectl describe gameserver again - either the GameServer will be gone completely, or it will be in Shutdown state, on the way to being deleted.

Next Steps

5.5 - Fleet Updates

Common patterns and approaches for updating Fleets with newer and/or different versions of your GameServer configuration.

Rolling Update Strategy

When Fleets are edited and updated, the default strategy of Code Blind is to roll the new version of the GameServer out to the entire Fleet, in a step by step increment and decrement by adding a chunk of the new version and removing a chunk of the current set of GameServers.

This is done while ensuring that Allocated GameServers are not deleted until they are specifically shutdown through the game servers SDK, as they are expected to have players on them.

You can see this in the Fleet.Spec.Strategy reference, with controls for how much of the Fleet is incremented and decremented at one time:

  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%

So when a Fleet is edited (any field other than replicas, see note below), either through kubectl edit/apply or via the Kubernetes API, this performs the following operations:

  1. Adds the maxSurge number of GameServers to the Fleet.
  2. Shutdown the maxUnavailable number of GameServers in the Fleet, skipping Allocated GameServers.
  3. Repeat above steps until all the previous GameServer configurations have been Shutdown and deleted.

By default, a Fleet will wait for new GameSevers to become Ready during a Rolling Update before continuing to shutdown additional GameServers, only counting GameServers that are Ready as being available when calculating the current maxUnavailable value which controls the rate at which GameServers are updated. This ensures that a Fleet cannot accidentally have zero GameServers in the Ready state if something goes wrong during a Rolling Update or if GameServers have a long delay before moving to the Ready state.

Recreate Strategy

This is an optimal Fleet update strategy if you want to replace all GameServers that are not Allocated with a new version as quickly as possible.

You can see this in the Fleet.Spec.Strategy reference:

  strategy:
    type: Recreate

So when a Fleet is edited, either through kubectl edit/apply or via the Kubernetes API, this performs the following operations:

  1. Shutdown all GameServers in the Fleet that are not currently Allocated.
  2. Create the same number of the new version of the GameServers that were previously deleted.
  3. Repeat above steps until all the previous GameServer configurations have been Shutdown and deleted.

Two (or more) Fleets Strategy

If you want very fine-grained control over the rate that new versions of a GameServer configuration is rolled out, or if you want to do some version of A/B testing or smoke test between different versions, running two (or more) Fleets at the same time is a good solution for this.

To do this, create a second Fleet inside your cluster, starting with zero replicas. From there you can scale this newer Fleet up and the older Fleet down as required by your specific rollout strategy.

This also allows you to rollback if issues arise with the newer version, as you can delete the newer Fleet and scale up the old Fleet to its previous levels, resulting in minimal impact to the players.

GameServerAllocation Across Fleets

Since GameServerAllocation is powered by label selectors, it is possible to allocate across multiple fleets, and/or give preference to particular sets of GameServers over others. You can see details of this in the GameServerAllocation reference.

In a scenario where a new v2 version of a Fleet is being slowly scaled up in a separate Fleet from the previous v1 Fleet, we can specify that we prefer allocation to occur from the v2 Fleet, and if none are available, fallback to the v1 Fleet, like so:

apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  selectors:
    - matchLabels:
        agones.dev/fleet: v2
    - matchLabels:
        game: my-awesome-game
  
apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  # Deprecated, use field selectors instead.
  required:
    matchLabels:
      game: my-awesome-game
  # Deprecated, use field selectors instead.
  preferred:
    - matchLabels:
        agones.dev/fleet: v2
  

In this example, all GameServers have the label game: my-awesome-game, so the Allocation will search across both Fleets through that mechanism. The selectors label matching selector tells the allocation system to first search all GameServers with the v2 Fleet label, and if not found, search through the rest of the set.

The above GameServerAllocation can then be used while you scale up the v2 Fleet and scale down the original Fleet at the rate that you deem fit for your specific rollout.

Notifying GameServers on Fleet Update/Downscale

When Allocated GameServers are utilised for a long time, such as a Lobby GameServer, or a GameServer that is being reused multiple times in a row, it can be useful to notify an Allocated GameServer process when its backing Fleet has been updated. When an update occurs, the Allocated GameServer, may want to actively perform a graceful shutdown and release its resources such that it can be replaced by a new version, or similar actions.

To do this, we provide the ability to apply a user-provided set of labels and/or annotations to the Allocated GameServers when a Fleet update occurs that updates its GameServer template, or generally causes the Fleet replica count to drop below the number of currently Allocated GameServers.

This provides two useful capabilities:

  1. The GameServer SDK.WatchGameServer() command can be utilised to react to this annotation and/or label change to indicate the Fleet system change, and the game server binary could execute code accordingly.
  2. This can also be used to proactively update GameServer labels, to effect change in allocation strategy - such as preferring the newer GameServers when allocating, but falling back to the older version if there aren’t enough of the new ones yet spun up.

The labels and/or annotations are applied to GameServers in a Fleet in the order designated by their configured Fleet scheduling.

Example yaml configuration:

apiVersion: "agones.dev/v1"
kind: Fleet
metadata:
  name: simple-game-server
spec:
  replicas: 2
  allocationOverflow: # This specifies which annotations and/or labels are applied
    labels:
      mykey: myvalue
      version: "" # empty an existing label value, so it's no longer in the allocation selection
    annotations:
      event: overflow
  template:
    spec:
      ports:
        - name: default
          containerPort: 7654
      template:
        spec:
          containers:
            - name: simple-game-server
              image: us-docker.pkg.dev/agones-images/examples/simple-game-server:0.27

See the Fleet reference for more details.

5.6 - GameServer Health Checking

Health checking exists to track the overall healthy state of the GameServer, such that action can be taken when a something goes wrong or a GameServer drops into an Unhealthy state

Disabling Health Checking

By default, health checking is enabled, but it can be turned off by setting the spec.health.disabled property to true.

SDK API

The Health() function on the SDK object needs to be called at an interval less than the spec.health.periodSeconds threshold time to be considered before it will be considered a failure.

The health check will also need to have not been called a consecutive number of times (spec.health.failureThreshold), giving it a chance to heal if it there is an issue.

Health Failure Strategy

The following is the process for what happens to a GameServer when it is unhealthy.

  1. If the GameServer container exits with an error before the GameServer moves to Ready then, it is restarted as per the restartPolicy (which defaults to “Always”).
  2. If the GameServer fails health checking at any point, then it doesn’t restart, but moves to an Unhealthy state.
  3. If the GameServer container exits while in Ready, Allocated or Reserved state, it will be restarted as per the restartPolicy (which defaults to “Always”, since RestartPolicy is a Pod wide setting), but will immediately move to an Unhealthy state.
  4. If the SDK sidecar fails, then it will be restarted, assuming the RestartPolicy is Always/OnFailure.

Fleet Management of Unhealthy GameServers

If a GameServer moves into an Unhealthy state when it is not part of a Fleet, the GameServer will remain in the Unhealthy state until explicitly deleted. This is useful for debugging Unhealthy GameServers, or if you are creating your own GameServer management layer, you can explicitly choose what to do if a GameServer becomes Unhealthy.

If a GameServer is part of a Fleet, the Fleet management system will delete any Unhealthy GameServers and immediately replace them with a brand new GameServer to ensure it has the configured number of Replicas.

Configuration Reference

  # Health checking for the running game server
  health:
    # Disable health checking. defaults to false, but can be set to true
    disabled: false
    # Number of seconds after the container has started before health check is initiated. Defaults to 5 seconds
    initialDelaySeconds: 5
    # If the `Health()` function doesn't get called at least once every period (seconds), then
    # the game server is not healthy. Defaults to 5
    periodSeconds: 5
    # Minimum consecutive failures for the health probe to be considered failed after having succeeded.
    # Defaults to 3. Minimum value is 1
    failureThreshold: 3

See the full GameServer example for more details

Example

C++

For a configuration that requires a health ping every 5 seconds, the example below sends a request every 2 seconds to be sure that the GameServer is under the threshold.

void doHealth(agones::SDK *sdk) {
    while (true) {
        if (!sdk->Health()) {
            std::cout << "Health ping failed" << std::endl;
        } else {
            std::cout << "Health ping sent" << std::endl;
        }
        std::this_thread::sleep_for(std::chrono::seconds(2));
    }
}

int main() {
    agones::SDK *sdk = new agones::SDK();
    bool connected = sdk->Connect();
    if (!connected) {
        return -1;
    }
    std::thread health (doHealth, sdk);

    // ...  run the game server code

}

Full Game Server

Also look in the examples directory.

5.7 - Player Tracking

Track player connections, disconnections, counts and capacities through the Code Blind SDK

Managing GameServer Capacities

To track your GameServer current player capacity, Code Blind gives you the ability to both set an initial capacity at GameServer creation, as well be able to change it during the lifecycle of the GameServer through the Code Blind SDK.

To set the initial capacity, you can do so via GameServer.Spec.Players.InitialCapacity like so:

apiVersion: "agones.dev/v1"
kind: GameServer
metadata:
  name: "gs-example"
spec:
  # ...
  players:
    # set this GameServer's initial player capacity to 10
    initialCapacity: 10

From there, if you need to change the capacity of the GameSever as gameplay is in progress, you can also do so via SDK.Alpha().SetPlayerCapacity(count)

The current player capacity is represented in GameServer.Status.Players.Capacity resource value.

We can see this in action, when we look at the Status section of a GameServer resource , wherein the capacity has been set to 20:

...
Status:
  Address:    14.81.195.72
  Node Name:  gke-test-cluster-default-6cd0ba67-1mps
  Players:
    Capacity:  20
    Count:     0
    Ids:       <nil>
  Ports:
    Name:          gameport
    Port:          7983
  Reserved Until:  <nil>
  State:           Ready

From the SDK, the game server binary can also retrieve the current player capacity via SDK.Alpha().GetPlayerCapacity().

Connecting and Disconnecting Players

As players connect and disconnect from your game, the Player Tracking functions enable you to track which players are currently connected.

It assumed that each player that connects has a unique token that identifies them as a player.

When a player connects to the game server binary, calling SDK.Alpha().PlayerConnect(playerID) with the unique player token will register them as connected, and store their player id.

At disconnection time, call SDK.Alpha().PlayerDisconnect(playerID) , which will deregister them and remove their player id from the list.

Each of these playerIDs is stored on GameServer.Status.Players.IDs, and the current count of connected players can be seen in GameServer.Status.Players.Count.

You can see this in action below in the GameServer Status section, where there are 4 players connected:

...
Status:
  Address:    39.82.196.74
  Node Name:  gke-test-cluster-default-6cd0ba77-1mps
  Players:
    Capacity:  10
    Count:     4
    Ids:
      xy8a
      m0ux
      71nj
      lpq5
  Ports:
    Name:          gameport
    Port:          7166
  Reserved Until:  <nil>
  State:           Ready

Checking Player Data

Not only is the connected player data stored on the GameServer resource, it is also stored in memory within the SDK, so that it can be used from within the game server binary as a realtime, thread safe, registry of connected players.

Therefore, if you want to:

Next Steps

5.8 - Local Game Server

Register your local game server with Code Blind.

You can register a local game server with Code Blind. This means you can run an experimental build of your game server in the Code Blind environment without the need of packaging and deploying it to a fleet. This allows you to quickly iterate on your game server code while still being able to plugin to your Code Blind environment.

Register your server with Code Blind

To register your local game server you’ll need to know the IP address of the machine running it and the port. With that you’ll create a game server config like the one below.

apiVersion: "agones.dev/v1"
kind: GameServer
metadata:
  name: my-local-server
  annotations:
    # Causes Code Blind to register your local game server at 192.1.1.2, replace with your server's IP address.
    agones.dev/dev-address: "192.1.1.2"
spec:
  ports:
  - name: default
    portPolicy: Static
    hostPort: 17654
    containerPort: 17654
  # The following is ignored but required due to validation.
  template:
    spec:
      containers:
      - name: simple-game-server
        image: us-docker.pkg.dev/codeblind/examples/simple-server:0.27

Once you save this to a file make sure you have kubectl configured to point to your Code Blind cluster and then run kubectl apply -f dev-gameserver.yaml. This will register your server with Code Blind.

Local Game Servers has a few limitations:

  • PortPolicy must be Static.
  • The game server is not managed by Code Blind. Features like autoscaling, replication, etc are not available.

When you are finished working with your server, you can remove the registration with kubectl delete -f dev-gameserver.yaml

Next Steps:

  • Review the specification of GameServer.
  • Read about GameServer allocation.
  • Learn how to connect your local development game server binary into a running Code Blind Kubernetes cluster for even more live development options with an out of cluster dev server.

5.9 - Latency Testing with Multiple Clusters

When running multiple Code Blind clusters around the world, you may need to have clients determine which cluster to connect to based on latency.

To make latency testing easier, Code Blind installs with a simple ping service with both HTTP and UDP services that can be called for the purpose of timing how long the roundtrip takes for information to be returned from either of these services.

Installing

By default, Code Blind installs Kubernetes Services for both HTTP and the UDP ping endpoints. These can be disabled entirely, or disabled individually. See the Helm install guide for the parameters to pass through, as well as configuration options.

The ping services as all installed under the agones-system namespace.

HTTP Service

This exposes an endpoint that returns a simple text HTTP response on request to the root “/” path. By default this is ok, but it can be configured via the agones.ping.http.response parameter.

This could be useful for providing clusters with unique lookup names, such that clients are able to identify clusters from their responses.

To lookup the details of this service, run kubectl describe service agones-ping-http-service --namespace=agones-system

UDP Service

The UDP ping service is a rate limited UDP echo service that returns the udp packet that it receives to its designated sender.

Since UDP sender details can be spoofed, this service is rate limited to 20 requests per second, per sender address, per running instance (default is 2).

This rate limit can be raised or lowered via the Helm install parameter agones.ping.udp.rateLimit.

UDP packets are also limited to 1024 bytes in size.

To lookup the details of this service, run kubectl describe service agones-ping-udp-service --namespace=agones-system

Client side tooling

We deliberately didn’t provide any game client libraries, as all major languages and engines have capabilities to send HTTP requests as well as UDP packets.

5.10 - Metrics

Code Blind controller exposes metrics via OpenCensus. OpenCensus is a single distribution of libraries that collect metrics and distributed traces from your services, we only use it for metrics but it will allow us to support multiple exporters in the future.

We choose to start with Prometheus as this is the most popular with Kubernetes but it is also compatible with Cloud Monitoring. If you need another exporter, check the list of supported exporters. It should be pretty straightforward to register a new one. (GitHub PRs are more than welcome.)

We plan to support multiple exporters in the future via environment variables and helm flags.

Backend integrations

Prometheus

If you are running a Prometheus instance you just need to ensure that metrics and kubernetes service discovery are enabled. (helm chart values agones.metrics.prometheusEnabled and agones.metrics.prometheusServiceDiscovery). This will automatically add annotations required by Prometheus to discover Code Blind metrics and start collecting them. (see example)

If your Prometheus metrics collection agent requires that you scrape from the pods directly(such as with Google Cloud Managed Prometheus), then the metrics ports for the controller and allocator will both be named http and exposed on 8080. In the case of the allocator, the port name and number can be overriden with the agones.allocator.serviceMetrics.http.portName and agones.allocator.serviceMetrics.http.port helm chart values.

Prometheus Operator

If you have Prometheus operator installed in your cluster, just enable ServiceMonitor installation in values:

agones:
  metrics:
    serviceMonitor:
      enabled: true

Google Cloud Managed Service for Prometheus

Google Cloud Managed Service for Prometheus is a fully managed multi-cloud solution for Prometheus. If you wish to use Managed Prometheus with Code Blind, follow the Google Cloud Managed Service for Prometheus installation steps.

Google Cloud Monitoring (formerly Stackdriver)

We support the OpenCensus Stackdriver exporter. In order to use it you should enable Cloud Monitoring API in Google Cloud Console. Follow the Google Cloud Monitoring installation steps to see your metrics in Cloud Monitoring.

Metrics available

NameDescriptionType
agones_gameservers_countThe number of gameservers per fleet and statusgauge
agones_gameserver_allocations_duration_secondsThe distribution of gameserver allocation requests latencieshistogram
agones_gameservers_totalThe total of gameservers per fleet and statuscounter
agones_gameserver_player_connected_totalThe total number of players connected to gameservers (Only available when player tracking is enabled)gauge
agones_gameserver_player_capacity_totalThe available capacity for players on gameservers (Only available when player tracking is enabled)gauge
agones_fleets_replicas_countThe number of replicas per fleet (total, desired, ready, reserved, allocated)gauge
agones_fleet_autoscalers_able_to_scaleThe fleet autoscaler can access the fleet to scalegauge
agones_fleet_autoscalers_buffer_limitsThe limits of buffer based fleet autoscalers (min, max)gauge
agones_fleet_autoscalers_buffer_sizeThe buffer size of fleet autoscalers (count or percentage)gauge
agones_fleet_autoscalers_current_replicas_countThe current replicas count as seen by autoscalersgauge
agones_fleet_autoscalers_desired_replicas_countThe desired replicas count as seen by autoscalersgauge
agones_fleet_autoscalers_limitedThe fleet autoscaler is outside the limits set by MinReplicas and MaxReplicas.gauge
agones_gameservers_node_countThe distribution of gameservers per nodehistogram
agones_nodes_countThe count of nodes empty and with gameserversgauge
agones_gameservers_state_durationThe distribution of gameserver state duration in seconds. Note: this metric could have some missing samples by design. Do not use the _total counter as the real value for state changes.histogram
agones_k8s_client_http_request_totalThe total of HTTP requests to the Kubernetes API by status codecounter
agones_k8s_client_http_request_duration_secondsThe distribution of HTTP requests latencies to the Kubernetes API by status codehistogram
agones_k8s_client_cache_list_totalThe total number of list operations for client-go cachescounter
agones_k8s_client_cache_list_duration_secondsDuration of a Kubernetes list API call in secondshistogram
agones_k8s_client_cache_list_itemsCount of items in a list from the Kubernetes APIhistogram
agones_k8s_client_cache_watches_totalThe total number of watch operations for client-go cachescounter
agones_k8s_client_cache_last_resource_versionLast resource version from the Kubernetes APIgauge
agones_k8s_client_workqueue_depthCurrent depth of the work queuegauge
agones_k8s_client_workqueue_latency_secondsHow long an item stays in the work queuehistogram
agones_k8s_client_workqueue_items_totalTotal number of items added to the work queuecounter
agones_k8s_client_workqueue_work_duration_secondsHow long processing an item from the work queue takeshistogram
agones_k8s_client_workqueue_retries_totalTotal number of items retried to the work queuecounter
agones_k8s_client_workqueue_longest_running_processorHow long the longest running workqueue processor has been running in microsecondsgauge
agones_k8s_client_workqueue_unfinished_work_secondsHow long unfinished work has been sitting in the workqueue in secondsgauge

Dropping Metric Labels

When a Fleet or FleetAutoscaler is deleted from the system, Code Blind will automatically clear metrics that utilise their name as a label from the exported metrics, so the metrics exported do not continuously grow in size over the lifecycle of the Code Blind installation.

Dashboard

Grafana Dashboards

We provide a set of useful Grafana dashboards to monitor Code Blind workload, they are located under the grafana folder:

Dashboard screenshots :

grafana dashboard autoscalers

grafana dashboard controller

Installation

When operating a live multiplayer game you will need to observe performances, resource usage and availability to learn more about your system. This guide will explain how you can setup Prometheus and Grafana into your own Kubernetes cluster to monitor your Code Blind workload.

Before attemping this guide you should make sure you have kubectl and helm installed and configured to reach your kubernetes cluster.

Prometheus installation

Prometheus is an open source monitoring solution, we will use it to store Code Blind controller metrics and query back the data.

Let’s install Prometheus using the Prometheus Community Kubernetes Helm Charts repository.

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

helm upgrade --install --wait prom prometheus-community/prometheus --namespace metrics --create-namespace \
    --set server.global.scrape_interval=30s \
    --set server.persistentVolume.enabled=true \
    --set server.persistentVolume.size=64Gi \
    -f ./build/prometheus.yaml

For resiliency it is recommended to run Prometheus on a dedicated node which is separate from nodes where Game Servers are scheduled. If you use the above command, with our prometheus.yaml to set up Prometheus, it will schedule Prometheus pods on nodes tainted with agones.dev/agones-metrics=true:NoExecute and labeled with agones.dev/agones-metrics=true if available.

As an example, to set up a dedicated node pool for Prometheus on GKE, run the following command before installing Prometheus. Alternatively you can taint and label nodes manually.

gcloud container node-pools create agones-metrics --cluster=... --zone=... \
  --node-taints agones.dev/agones-metrics=true:NoExecute \
  --node-labels agones.dev/agones-metrics=true \
  --num-nodes=1 \
  --machine-type=e2-standard-4

By default we will disable the push gateway (we don’t need it for Code Blind) and other exporters.

The helm chart supports nodeSelector, affinity and toleration, you can use them to schedule Prometheus deployments on an isolated node(s) to have an homogeneous game servers workload.

This will install a Prometheus Server in your current cluster with Persistent Volume Claim (Deactivated for Minikube and Kind) for storing and querying time series, it will automatically start collecting metrics from Code Blind Controller.

Finally, to access Prometheus metrics, rules and alerts explorer use

kubectl port-forward deployments/prom-prometheus-server 9090 -n metrics

Now you can access the prometheus dashboard http://localhost:9090.

On the landing page you can start exploring metrics by creating queries. You can also verify what targets Prometheus currently monitors (Header Status > Targets), you should see Code Blind controller pod in the kubernetes-pods section.

Now let’s install some Grafana dashboards.

Grafana installation

Grafana is a open source time series analytics platform which supports Prometheus data source. We can also easily import pre-built dashboards.

First we will install Code Blind dashboard as config maps in our cluster.

kubectl apply -f ./build/grafana/

Now we can install the Grafana Community Kubernetes Helm Charts from their repository. (Replace <your-admin-password> with the admin password of your choice)

helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

helm upgrade --install --wait grafana grafana/grafana --namespace metrics \
  --set adminPassword=<your-admin-password> -f ./build/grafana.yaml

This will install Grafana with our prepopulated dashboards and prometheus datasource previously installed

Finally to access dashboards run

kubectl port-forward deployments/grafana 3000 -n metrics

Open a web browser to http://localhost:3000, you should see Code Blind dashboards after login as admin.

Google Cloud Managed Service for Prometheus installation

To collect Code Blind metrics using Managed Prometheus:

  • Follow the instructions to enable managed collection for a GKE cluster or non-GKE cluster.

  • Configure Managed Prometheus to scrape Code Blind by creating a PodMonitoring resource:

kubectl apply -n agones-system -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/build/prometheus-google-managed.yaml

To install Grafana using a Managed Prometheus backend:

Google Cloud Monitoring installation

In order to use Google Cloud Monitoring you must enable the Monitoring API in the Google Cloud Console. The Cloud Monitoring exporter uses a strategy called Application Default Credentials (ADC) to find your application’s credentials. Details can be found in Setting Up Authentication for Server to Server Production Applications.

You need to grant all the necessary permissions to the users (see Access Control Guide). The predefined role Monitoring Metric Writer contains those permissions. Use the following command to assign the role to your default service account.

gcloud projects add-iam-policy-binding [PROJECT_ID] --member serviceAccount:[PROJECT_NUMBER][email protected] --role roles/monitoring.metricWriter

Before proceeding, ensure you have created a metrics node pool as mentioned in the Google Cloud installation guide.

The default metrics exporter installed with Code Blind is Prometheus. If you are using the Helm installation, you can install or upgrade Code Blind to use Cloud Monitoring, using the following chart parameters:

helm upgrade --install --wait --set agones.metrics.stackdriverEnabled=true --set agones.metrics.prometheusEnabled=false --set agones.metrics.prometheusServiceDiscovery=false my-release-name agones/agones --namespace=agones-system

With this configuration only the Cloud Monitoring exporter would be used instead of Prometheus exporter.

Using Cloud Monitoring with Workload Identity

If you would like to enable Cloud Monitoring in conjunction with Workload Identity, there are a few extra steps you need to follow:

  1. When setting up the Google service account following the instructions for Authenticating to Google Cloud, create two IAM policy bindings, one for serviceAccount:PROJECT_ID.svc.id.goog[agones-system/agones-controller] and one for serviceAccount:PROJECT_ID.svc.id.goog[agones-system/agones-allocator].

  2. Pass parameters to helm when installing Code Blind to add annotations to the agones-controller and agones-allocator Kubernetes service accounts:

helm install my-release --namespace agones-system --create-namespace agones/agones --set agones.metrics.stackdriverEnabled=true --set agones.metrics.prometheusEnabled=false --set agones.metrics.prometheusServiceDiscovery=false --set agones.serviceaccount.allocator.annotations."iam\.gke\.io/gcp-service-account"="GSA_NAME@PROJECT_ID\.iam\.gserviceaccount\.com" --set agones.serviceaccount.allocator.labels."iam\.gke\.io/gcp-service-account"="GSA_NAME@PROJECT_ID\.iam\.gserviceaccount\.com" --set agones.serviceaccount.controller.annotations."iam\.gke\.io/gcp-service-account"="GSA_NAME@PROJECT_ID\.iam\.gserviceaccount\.com"

To verify that metrics are being sent to Cloud Monitoring, create a Fleet or a Gameserver and look for the metrics to show up in the Cloud Monitoring dashboard. Navigate to the Metrics explorer and search for metrics with the prefix agones/. Select a metric and look for data to be plotted in the graph to the right.

An example of a custom dashboard is:

cloud monitoring dashboard

Currently there exists only manual way of configuring Cloud Monitoring Dashboard. So it is up to you to set an Alignment Period (minimal is 1 minute), GroupBy, Filter parameters and other graph settings.

Troubleshooting

If you can’t see Code Blind metrics you should have a look at the controller logs for connection errors. Also ensure that your cluster has the necessary credentials to interact with Cloud Monitoring. You can configure stackdriverProjectID manually, if the automatic discovery is not working.

Permissions problem example from controller logs:

Failed to export to Stackdriver: rpc error: code = PermissionDenied desc = Permission monitoring.metricDescriptors.create denied (or the resource may not exist).

If you receive this error, ensure your service account has the role or corresponding permissions mentioned above.

5.11 - Access Code Blind via the Kubernetes API

It’s likely that we will want to programmatically interact with Code Blind. Everything that can be done via the kubectl and yaml configurations can also be done via the Kubernetes API.

Installing Code Blind creates several Custom Resource Definitions (CRD), which can be accessed and manipulated through the Kubernetes API.

The detailed list of Code Blind CRDs with their parameters could be found here - Code Blind CRD API Reference.

Kubernetes has multiple client libraries, however, at time of writing, only the Go and Python clients are documented to support accessing CRDs.

This can be found in the Accessing a custom resource section of the Kubernetes documentation.

At this time, we recommend interacting with Code Blind through the Go client that has been generated in this repository, but other methods may also work as well.

Go Client

Kubernetes Go Client tooling generates a Client for Code Blind that we can use to interact with the Code Blind installation on our Kubernetes cluster.

Authentication

This client uses the same authentication mechanisms as the Kubernetes API.

If you plan to run your code in the same cluster as the Code Blind install, have a look at the in cluster configuration example from the Kubernetes Client.

If you plan to run your code outside the Kubernetes cluster as your Code Blind install, look at the out of cluster configuration example from the Kubernetes client.

Example

The following is an example of a in-cluster configuration, that creates a Clientset for Code Blind and then creates a GameServer.

A full example code is available in the example folder.

package main

import (
	"fmt"
	"context"

	agonesv1 "agones.dev/agones/pkg/apis/agones/v1"
	"agones.dev/agones/pkg/client/clientset/versioned"
	"agones.dev/agones/pkg/util/runtime"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
)

func main() {
	config, err := rest.InClusterConfig()
	logger := runtime.NewLoggerWithSource("main")
	if err != nil {
		logger.WithError(err).Fatal("Could not create in cluster config")
	}

	// Access to standard Kubernetes resources through the Kubernetes Clientset
	// We don't actually need this for this example, but it's just here for
	// illustrative purposes
	kubeClient, err := kubernetes.NewForConfig(config)
	if err != nil {
		logger.WithError(err).Fatal("Could not create the kubernetes clientset")
	}

	// Access to the Code Blind resources through the Code Blind Clientset
	// Note that we reuse the same config as we used for the Kubernetes Clientset
	agonesClient, err := versioned.NewForConfig(config)
	if err != nil {
		logger.WithError(err).Fatal("Could not create the agones api clientset")
	}

	// Create a GameServer
	gs := &agonesv1.GameServer{ObjectMeta: metav1.ObjectMeta{GenerateName: "simple-game-server", Namespace: "default"},
		Spec: agonesv1.GameServerSpec{
			Container: "simple-game-server",
			Ports: []agonesv1.GameServerPort{{
				ContainerPort: 7654,
				HostPort:      7654,
				Name:          "gameport",
				PortPolicy:    agonesv1.Static,
				Protocol:      corev1.ProtocolUDP,
			}},
			Template: corev1.PodTemplateSpec{
				Spec: corev1.PodSpec{
					Containers: []corev1.Container{{Name: "simple-game-server", Image: "us-docker.pkg.dev/codeblind/examples/simple-server:0.27"}},
				},
			},
		},
	}
	newGS, err := agonesClient.AgonesV1().GameServers("default").Create(context.TODO(), gs, metav1.CreateOptions{})
	if err != nil {
		panic(err)
	}

	fmt.Printf("New game servers' name is: %s", newGS.ObjectMeta.Name)
}

In order to create GS using provided example, you can run it as a Kubernetes Job:

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/crd-client/create-gs.yaml --namespace agones-system
kubectl get pods --namespace agones-system
NAME                                 READY   STATUS      RESTARTS   AGE
create-gs-6wz86-7qsm5                0/1     Completed   0          6s
kubectl logs create-gs-6wz86-7qsm5  --namespace agones-system
{"message":"\u0026{0xc0001dde00 default}","severity":"info","source":"main","time":"2020-04-21T11:14:00.477576428Z"}
{"message":"New GameServer name is: helm-test-server-fxfgg","severity":"info","time":"2020-04-21T11:14:00.516024697Z"}

You have just created a GameServer using Kubernetes Go Client.

Best Practice: Using Informers and Listers

Almost all Kubernetes’ controllers and custom controllers utilise Informers and Listers to reduce the load on the Kubernetes’s control plane.

Repetitive, direct access of the Kubernetes control plane API can significantly reduce the performance of the cluster – and Informers and Listers help resolving that issue.

Informers and Listers reduce the load on the Kubernetes control plane by creating, using and maintaining an eventually consistent an in-memory cache. This can be watched and also queried with zero cost, since it will only read against its in-memory model of the Kubernetes resources.

Informer’s role and Lister’s role are different.

An Informer is the mechanism for watching a Kubernetes object’s event, such that when a Kubernetes object changes(e.g. CREATE,UPDATE,DELETE), the Informer is informed, and can execute a callback with the relevant object as an argument.

This can be very useful for building event based systems against the Kubernetes API.

A Lister is the mechanism for querying Kubernetes object’s against the client side in-memory cache. Since the Lister stores objects in an in-memory cache, queries against a come at practically no cost.

Of course, Code Blind itself also uses Informers and Listers in its codebase.

Example

The following is an example of Informers and Listers, that show the GameServer’s name & status & IPs in the Kubernetes cluster.

package main

import (
	"context"
	"time"

	"agones.dev/agones/pkg/client/clientset/versioned"
	"agones.dev/agones/pkg/client/informers/externalversions"
	"agones.dev/agones/pkg/util/runtime"
	"k8s.io/apimachinery/pkg/labels"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/cache"
)

func main() {
	config, err := rest.InClusterConfig()
	logger := runtime.NewLoggerWithSource("main")
	if err != nil {
		logger.WithError(err).Fatal("Could not create in cluster config")
	}
	kubeClient, err := kubernetes.NewForConfig(config)
	agonesClient, err := versioned.NewForConfig(config)
	if err != nil {
		logger.WithError(err).Fatal("Could not create the agones api clientset")
	}

	// Create InformerFactory which create the informer
	informerFactory := informers.NewSharedInformerFactory(kubeClient, time.Second*30)
	agonesInformerFactory := externalversions.NewSharedInformerFactory(agonesClient, time.Second*30)

	// Create Pod informer by informerFactory
	podInformer := informerFactory.Core().V1().Pods()

	// Create GameServer informer by informerFactory
	gameServers := agonesInformerFactory.Code Blind().V1().GameServers()
	gsInformer := gameServers.Informer()

	// Add EventHandler to informer
	// When the object's event happens, the function will be called
	// For example, when the pod is added, 'AddFunc' will be called and put out the "Pod Added"
	podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    func(new interface{}) { logger.Infof("Pod Added") },
		UpdateFunc: func(old, new interface{}) { logger.Infof("Pod Updated") },
		DeleteFunc: func(old interface{}) { logger.Infof("Pod Deleted") },
	})
	gsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
		AddFunc:    func(new interface{}) { logger.Infof("GameServer Added") },
		UpdateFunc: func(old, new interface{}) { logger.Infof("GameServer Updated") },
		DeleteFunc: func(old interface{}) { logger.Infof("GameServer Deleted") },
	})

	ctx := context.Background()

	// Start Go routines for informer
	informerFactory.Start(ctx.Done())
	agonesInformerFactory.Start(ctx.Done())
	// Wait until finish caching with List API
	informerFactory.WaitForCacheSync(ctx.Done())
	agonesInformerFactory.WaitForCacheSync(ctx.Done())

	// Create Lister which can list objects from the in-memory-cache
	podLister := podInformer.Lister()
	gsLister := gameServers.Lister()

	for {
		// Get List objects of Pods from Pod Lister
		p := podLister.Pods("default")
		// Get List objects of GameServers from GameServer Lister
		gs, err := gsLister.List(labels.Everything())
		if err != nil {
			panic(err)
		}
		// Show GameServer's name & status & IPs
		for _, g := range gs {
			a, err := p.Get(g.GetName())
			if err != nil {
				panic(err)
			}
			logger.Infof("------------------------------")
			logger.Infof("Name: %s", g.GetName())
			logger.Infof("Status: %s", g.Status.State)
			logger.Infof("External IP: %s", g.Status.Address)
			logger.Infof("Internal IP: %s", a.Status.PodIP)
		}
		time.Sleep(time.Second * 25)
	}
}

You can list GameServer’s name and status and IPs using Kubernetes Informers and Listers.

Direct Access to the REST API via Kubectl

If there isn’t a client written in your preferred language, it is always possible to communicate directly with Kubernetes API to interact with Code Blind.

The Kubernetes API can be authenticated and exposed locally through the kubectl proxy

For example:

kubectl proxy &
Starting to serve on 127.0.0.1:8001

List all Code Blind endpoints

curl http://localhost:8001/apis | grep agones -A 5 -B 5
{
    "name": "agones.dev",
    "versions": [
    {
        "groupVersion": "agones.dev/v1",
        "version": "v1"
    }
    ],
    "preferredVersion": {
    "groupVersion": "agones.dev/v1",
    "version": "v1"
    },
    "serverAddressByClientCIDRs": null
}

List Code Blind resources

curl http://localhost:8001/apis/agones.dev/v1
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "agones.dev/v1",
  "resources": [
    {
      "name": "gameservers",
      "singularName": "gameserver",
      "namespaced": true,
      "kind": "GameServer",
      "verbs": [
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "create",
        "update",
        "watch"
      ],
      "shortNames": [
        "gs"
      ]
    }
  ]
}

List all gameservers in the default namespace

curl http://localhost:8001/apis/agones.dev/v1/namespaces/default/gameservers
{
    "apiVersion": "agones.dev/v1",
    "items": [
        {
            "apiVersion": "agones.dev/v1",
            "kind": "GameServer",
            "metadata": {
                "annotations": {
                    "kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"agones.dev/v1\",\"kind\":\"GameServer\",\"metadata\":{\"annotations\":{},\"name\":\"simple-game-server\",\"namespace\":\"default\"},\"spec\":{\"containerPort\":7654,\"hostPort\":7777,\"portPolicy\":\"static\",\"template\":{\"spec\":{\"containers\":[{\"image\":\"us-docker.pkg.dev/codeblind/examples/simple-server:0.27\",\"name\":\"simple-game-server\"}]}}}}\n"
                },
                "clusterName": "",
                "creationTimestamp": "2018-03-02T21:41:05Z",
                "finalizers": [
                    "agones.dev"
                ],
                "generation": 0,
                "name": "simple-game-server",
                "namespace": "default",
                "resourceVersion": "760",
                "selfLink": "/apis/agones.dev/v1/namespaces/default/gameservers/simple-game-server",
                "uid": "692beea6-1e62-11e8-beb2-080027637781"
            },
            "spec": {
                "PortPolicy": "Static",
                "container": "simple-game-server",
                "containerPort": 7654,
                "health": {
                    "failureThreshold": 3,
                    "initialDelaySeconds": 5,
                    "periodSeconds": 5
                },
                "hostPort": 7777,
                "protocol": "UDP",
                "template": {
                    "metadata": {
                        "creationTimestamp": null
                    },
                    "spec": {
                        "containers": [
                            {
                                "image": "us-docker.pkg.dev/codeblind/examples/simple-server:0.27",
                                "name": "simple-game-server",
                                "resources": {}
                            }
                        ]
                    }
                }
            },
            "status": {
                "address": "192.168.99.100",
                "nodeName": "agones",
                "port": 7777,
                "state": "Ready"
            }
        }
    ],
    "kind": "GameServerList",
    "metadata": {
        "continue": "",
        "resourceVersion": "1062",
        "selfLink": "/apis/agones.dev/v1/namespaces/default/gameservers"
    }
}

Allocate a gameserver from a fleet named ‘simple-game-server’, with GameServerAllocation

curl -d '{"apiVersion":"allocation.agones.dev/v1","kind":"GameServerAllocation","spec":{"required":{"matchLabels":{"agones.dev/fleet":"simple-game-server"}}}}' -H "Content-Type: application/json" -X POST http://localhost:8001/apis/allocation.agones.dev/v1/namespaces/default/gameserverallocations
{
    "kind": "GameServerAllocation",
    "apiVersion": "allocation.agones.dev/v1",
    "metadata": {
        "name": "simple-game-server-v6jwb-cmdcv",
        "namespace": "default",
        "creationTimestamp": "2019-07-03T17:19:47Z"
    },
    "spec": {
        "multiClusterSetting": {
            "policySelector": {}
        },
        "required": {
            "matchLabels": {
                "agones.dev/fleet": "simple-game-server"
            }
        },
        "scheduling": "Packed",
        "metadata": {}
    },
    "status": {
        "state": "Allocated",
        "gameServerName": "simple-game-server-v6jwb-cmdcv",
        "ports": [
            {
                "name": "default",
                "port": 7445
            }
        ],
        "address": "34.94.118.237",
        "nodeName": "gke-test-cluster-default-f11755a7-5km3"
    }
}

You may wish to review the Code Blind Kubernetes API for the full data structure reference.

The Kubernetes API Concepts section may also provide the more details on the API conventions that are used in the Kubernetes API.

Next Steps

5.12 - Troubleshooting

Troubleshooting guides and steps.

Something went wrong with my GameServer

If there is something going wrong with your GameServer, there are a few approaches to determining the cause:

Run with the local SDK server

A good first step for seeing what may be going wrong is replicating the issue locally. To do this you can take advantage of the Code Blind local SDK server , with the following troubleshooting steps:

  1. Run your game server as a local binary against the local SDK server
  2. Run your game server container against the local SDK server. It’s worth noting that running with docker run --network=host ... can be an easy way to allow your game server container(s) access to the local SDK server)

At each stage, keep an eye on the logs of your game server binary, and the local SDK server, and ensure there are no system errors.

Run as a GameServer rather than a Fleet

A Fleet will automatically replace any unhealthy GameServer under its control - which can make it hard to catch all the details to determine the cause.

To work around this, instantiate a single instance of your game server as a
GameServer within your Code Blind cluster.

This GameServer will not be replaced if it moves to an Unhealthy state, giving you time to introspect what is going wrong.

Introspect with Kubernetes tooling

There are many Kubernetes tools that will help with determining where things have potentially gone wrong for your game server. Here are a few you may want to try.

kubectl describe

Depending on what is happening, you may want to run kubectl describe <gameserver name> to view the events that are associated with that particular GameServer resource. This can give you insight into the lifecycle of the GameServer and if anything has gone wrong.

For example, here we can see where the simple-game-server example has been moved to the Unhealthy state due to a crash in the backing GameServer Pod container’s binary.

kubectl describe gs simple-game-server-zqppv
Name:         simple-game-server-zqppv
Namespace:    default
Labels:       <none>
Annotations:  agones.dev/sdk-version: 1.0.0-dce1546
API Version:  agones.dev/v1
Kind:         GameServer
Metadata:
  Creation Timestamp:  2019-08-16T21:25:44Z
  Finalizers:
    agones.dev
  Generate Name:     simple-game-server-
  Generation:        1
  Resource Version:  1378575
  Self Link:         /apis/agones.dev/v1/namespaces/default/gameservers/simple-game-server-zqppv
  UID:               6818adc7-c06c-11e9-8dbd-42010a8a0109
Spec:
  Container:  simple-game-server
  Health:
    Failure Threshold:      3
    Initial Delay Seconds:  5
    Period Seconds:         5
  Ports:
    Container Port:  7654
    Host Port:       7058
    Name:            default
    Port Policy:     Dynamic
    Protocol:        UDP
  Scheduling:        Packed
  Template:
    Metadata:
      Creation Timestamp:  <nil>
    Spec:
      Containers:
        Image:  us-docker.pkg.dev/codeblind/examples/simple-server:0.27
        Name:   simple-game-server
        Resources:
          Limits:
            Cpu:     20m
            Memory:  32Mi
          Requests:
            Cpu:     20m
            Memory:  32Mi
Status:
  Address:    35.230.59.117
  Node Name:  gke-test-cluster-default-590db5e4-4s6r
  Ports:
    Name:          default
    Port:          7058
  Reserved Until:  <nil>
  State:           Unhealthy
Events:
  Type     Reason          Age   From                   Message
  ----     ------          ----  ----                   -------
  Normal   PortAllocation  72s   gameserver-controller  Port allocated
  Normal   Creating        72s   gameserver-controller  Pod simple-game-server-zqppv created
  Normal   Scheduled       72s   gameserver-controller  Address and port populated
  Normal   RequestReady    67s   gameserver-sidecar     SDK state change
  Normal   Ready           66s   gameserver-controller  SDK.Ready() complete
  Warning  Unhealthy       34s   health-controller      Issue with Gameserver pod

The backing Pod has the same name as the GameServer - so it’s also worth looking at the details and events for the Pod to see if there are any issues there, such as restarts due to binary crashes etc.

For example, you can see the restart count on the us-docker.pkg.dev/codeblind/examples/simple-server:0.27 container is set to 1, due to the game server binary crash

kubectl describe pod simple-game-server-zqppv
Name:               simple-game-server-zqppv
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               gke-test-cluster-default-590db5e4-4s6r/10.138.0.23
Start Time:         Fri, 16 Aug 2019 21:25:44 +0000
Labels:             agones.dev/gameserver=simple-game-server-zqppv
                    agones.dev/role=gameserver
Annotations:        agones.dev/container: simple-game-server
                    agones.dev/sdk-version: 1.0.0-dce1546
                    cluster-autoscaler.kubernetes.io/safe-to-evict: false
Status:             Running
IP:                 10.48.1.80
Controlled By:      GameServer/simple-game-server-zqppv
Containers:
  simple-game-server:
    Container ID:   docker://69eacd03cc89b0636b78abe47926b02183ba84d18fa20649ca443f5232511661
    Image:          us-docker.pkg.dev/codeblind/examples/simple-server:0.27
    Image ID:       docker-pullable://gcr.io/agones-images/simple-game-server@sha256:6a60eff5e68b88b5ce75ae98082d79cff36cda411a090f3495760e5c3b6c3575
    Port:           7654/UDP
    Host Port:      7058/UDP
    State:          Running
      Started:      Fri, 16 Aug 2019 21:26:22 +0000
    Last State:     Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Fri, 16 Aug 2019 21:25:45 +0000
      Finished:     Fri, 16 Aug 2019 21:26:22 +0000
    Ready:          True
    Restart Count:  1
    Limits:
      cpu:     20m
      memory:  32Mi
    Requests:
      cpu:        20m
      memory:     32Mi
    Liveness:     http-get http://:8080/gshealthz delay=5s timeout=1s period=5s #success=1 #failure=3
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from empty (ro)
  agones-gameserver-sidecar:
    Container ID:   docker://f3c475c34d26232e19b60be65b03bc6ce41931f4c37e00770d3ab4a36281d31c
    Image:          gcr.io/agones-mark/agones-sdk:1.0.0-dce1546
    Image ID:       docker-pullable://gcr.io/agones-mark/agones-sdk@sha256:4b5693e95ee3023a2b2e2099d102bb6bac58d4ce0ac472e58a09cee6d160cd19
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Fri, 16 Aug 2019 21:25:48 +0000
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:     30m
    Liveness:  http-get http://:8080/healthz delay=3s timeout=1s period=3s #success=1 #failure=3
    Environment:
      GAMESERVER_NAME:  simple-game-server-zqppv
      POD_NAMESPACE:    default (v1:metadata.namespace)
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from agones-sdk-token-vr6qq (ro)
Conditions:
  Type              Status
  Initialized       True
  Ready             True
  ContainersReady   True
  PodScheduled      True
Volumes:
  empty:
    Type:    EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:
  agones-sdk-token-vr6qq:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  agones-sdk-token-vr6qq
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age                   From                                             Message
  ----    ------     ----                  ----                                             -------
  Normal  Scheduled  2m32s                 default-scheduler                                Successfully assigned default/simple-game-server-zqppv to gke-test-cluster-default-590db5e4-4s6r
  Normal  Pulling    2m31s                 kubelet, gke-test-cluster-default-590db5e4-4s6r  pulling image "gcr.io/agones-mark/agones-sdk:1.0.0-dce1546"
  Normal  Started    2m28s                 kubelet, gke-test-cluster-default-590db5e4-4s6r  Started container
  Normal  Pulled     2m28s                 kubelet, gke-test-cluster-default-590db5e4-4s6r  Successfully pulled image "gcr.io/agones-mark/agones-sdk:1.0.0-dce1546"
  Normal  Created    2m28s                 kubelet, gke-test-cluster-default-590db5e4-4s6r  Created container
  Normal  Created    114s (x2 over 2m31s)  kubelet, gke-test-cluster-default-590db5e4-4s6r  Created container
  Normal  Started    114s (x2 over 2m31s)  kubelet, gke-test-cluster-default-590db5e4-4s6r  Started container
  Normal  Pulled     114s (x2 over 2m31s)  kubelet, gke-test-cluster-default-590db5e4-4s6r  Container image "us-docker.pkg.dev/codeblind/examples/simple-server:0.27" already present on machine

Finally, you can also get the logs of your GameServer Pod as well via kubectl logs <pod name> -c <game server container name>, for example:

kubectl logs simple-game-server-zqppv -c simple-game-server
2019/08/16 21:26:23 Creating SDK instance
2019/08/16 21:26:24 Starting Health Ping
2019/08/16 21:26:24 Starting UDP server, listening on port 7654
2019/08/16 21:26:24 Marking this server as ready

The above commands will only give the most recent container’s logs (so we won’t get the previous crash), but you can use kubectl logs --previous=true simple-game-server-zqppv -c simple-game-server to get the previous instance of the containers logs, or use your Kubernetes platform of choice’s logging aggregation tools to view the crash details.

kubectl events

The “Events” section that is seen at the bottom of a kubectl describe is backed an actual Event record in Kubernetes, which can be queried - and is general persistent for an hour after it is created.

Therefore, even a GameServer or Pod resource is no longer available in the system, its Events may well be.

kubectl get events can be used to see all these events. This can also be grepped with the GameServer name to see all events across both the GameServer and its backing Pod, like so:

kubectl get events | grep simple-game-server-v992s-jwpx2
2m47s       Normal   PortAllocation          gameserver/simple-game-server-v992s-jwpx2   Port allocated
2m47s       Normal   Creating                gameserver/simple-game-server-v992s-jwpx2   Pod simple-game-server-v992s-jwpx2 created
2m47s       Normal   Scheduled               pod/simple-game-server-v992s-jwpx2          Successfully assigned default/simple-game-server-v992s-jwpx2 to gke-test-cluster-default-77e7f57d-j1mp
2m47s       Normal   Scheduled               gameserver/simple-game-server-v992s-jwpx2   Address and port populated
2m46s       Normal   Pulled                  pod/simple-game-server-v992s-jwpx2          Container image "us-docker.pkg.dev/codeblind/examples/simple-server:0.27" already present on machine
2m46s       Normal   Created                 pod/simple-game-server-v992s-jwpx2          Created container simple-game-server
2m45s       Normal   Started                 pod/simple-game-server-v992s-jwpx2          Started container simple-game-server
2m45s       Normal   Pulled                  pod/simple-game-server-v992s-jwpx2          Container image "gcr.io/agones-images/agones-sdk:1.7.0" already present on machine
2m45s       Normal   Created                 pod/simple-game-server-v992s-jwpx2          Created container agones-gameserver-sidecar
2m45s       Normal   Started                 pod/simple-game-server-v992s-jwpx2          Started container agones-gameserver-sidecar
2m45s       Normal   RequestReady            gameserver/simple-game-server-v992s-jwpx2   SDK state change
2m45s       Normal   Ready                   gameserver/simple-game-server-v992s-jwpx2   SDK.Ready() complete
2m47s       Normal   SuccessfulCreate        gameserverset/simple-game-server-v992s      Created gameserver: simple-game-server-v992s-jwpx2

Other techniques

For more tips and tricks, the Kubernetes Cheatsheet: Interactive with Pods also provides more troubleshooting techniques.

How do I see the logs for Code Blind?

If something is going wrong, and you want to see the logs for Code Blind, there are potentially two places you will want to check:

  1. The controller: assuming you installed Code Blind in the agones-system namespace, you will find that there is a single pod called agones-controller-<hash> (where hash is the unique code that Kubernetes generates) that exists there, that you can get the logs from. This is the main controller for Code Blind, and should be the first place to check when things go wrong.
    1. To get the logs from this controller run:
      kubectl logs --namespace=agones-system agones-controller-<hash>
  2. The SDK server sidecar: Code Blind runs a small gRPC + http server for the SDK in a container in the same network namespace as the game server container to connect to via the SDK.
    The logs from this SDK server are also useful for tracking down issues, especially if you are having trouble with a particular GameServer.
    1. To find the Pod for the GameServer look for the pod with a name that is prefixed with the name of the owning GameServer. For example if you have a GameServer named simple-game-server, it’s pod could potentially be named simple-game-server-dnbwj.
    2. To get the logs from that Pod, we need to specify that we want the logs from the agones-gameserver-sidecar container. To do that, run the following:
      kubectl logs simple-game-server-dnbwj -c agones-gameserver-sidecar

Code Blind uses JSON structured logging, therefore errors will be visible through the "severity":"info" key and value.

Enable Debug Level Logging for the SDK Server

By default, the SDK Server binary is set to an Info level of logging.

You can use the sdkServer.logLevel to increase this to Debug levels, and see extra information about what is happening with the SDK Server that runs alonside your game server container(s).

See the GameServer reference for configuration details.

Enable Debug Level Logging for the Code Blind Controller

By default, the log level for the Code Blind controller is “info”. To get a more verbose log output, switch this to “debug” via the agones.controller.logLevel Helm Configuration parameters at installation.

The Feature Flag I enabled/disabled isn’t working as expected

It’s entirely possible that Alpha features may still have bugs in them (They are alpha after all 😃), but the first thing to check is what the actual Feature Flags states were passed to Code Blind are, and that they were set correctly.

The easiest way is to check the top info level log lines from the Code Blind controller.

For example:

$ kubectl logs -n agones-system agones-controller-7575dc59-7p2rg  | head
{"filename":"/home/agones/logs/agones-controller-20220615_211540.log","message":"logging to file","numbackups":99,"severity":"info","source":"main","time":"2022-06-15T21:15:40.309349789Z"}
{"logLevel":"info","message":"Setting LogLevel configuration","severity":"info","source":"main","time":"2022-06-15T21:15:40.309403296Z"}
{"ctlConf":{"MinPort":7000,"MaxPort":8000,"SidecarImage":"gcr.io/agones-images/agones-sdk:1.23.0","SidecarCPURequest":"30m","SidecarCPULimit":"0","SidecarMemoryRequest":"0","SidecarMemoryLimit":"0","SdkServiceAccount":"agones-sdk","AlwaysPullSidecar":false,"PrometheusMetrics":true,"Stackdriver":false,"StackdriverLabels":"","KeyFile":"/home/agones/certs/server.key","CertFile":"/home/agones/certs/server.crt","KubeConfig":"","GCPProjectID":"","NumWorkers":100,"APIServerSustainedQPS":400,"APIServerBurstQPS":500,"LogDir":"/home/agones/logs","LogLevel":"info","LogSizeLimitMB":10000},"featureGates":"Example=true\u0026NodeExternalDNS=true\u0026PlayerAllocationFilter=false\u0026PlayerTracking=false","message":"starting gameServer operator...","severity":"info","source":"main","time":"2022-06-15T21:15:40.309528802Z","version":"1.23.0"}
...

The ctlConf section has the full configuration for Code Blind as it was passed to the controller. Within that log line there is a featureGates key, that has the full Feature Gate configuration as a URL Query String (\u0026 is JSON for &), so you can see if the Feature Gates are set as expected.

I uninstalled Code Blind before deleted all my GameServers and now they won’t delete

Code Blind GameServers use Finalizers to manage garbage collection of the GameServers. This means that if the Code Blind controller doesn’t remove the finalizer for you (i.e. if it has been uninstalled), it can be tricky to remove them all.

Thankfully, if we create a patch to remove the finalizers from all GameServers, we can delete them with impunity.

A quick one liner to do this:

kubectl get gameserver -o name | xargs -n1 -P1 -I{} kubectl patch {} --type=merge -p '{"metadata": {"finalizers": []}}'

Once this is done, you can kubectl delete gs --all and clean everything up (if it’s not gone already).

I’m getting Forbidden errors when trying to install Code Blind

Ensure that you are running Kubernetes 1.12 or later, which does not require any special clusterrolebindings to install Code Blind.

If you want to install Code Blind on an older version of Kubernetes, you need to create a clusterrolebinding to add your identity as a cluster admin, e.g.

# Kubernetes Engine
kubectl create clusterrolebinding cluster-admin-binding \
  --clusterrole cluster-admin --user `gcloud config get-value account`
# Minikube
kubectl create clusterrolebinding cluster-admin-binding \
  --clusterrole=cluster-admin --serviceaccount=kube-system:default

On GKE, gcloud config get-value accounts will return a lowercase email address, so if you are using a CamelCase email, you may need to type it in manually.

I’m getting stuck in “Terminating” when I uninstall Code Blind

If you try to uninstall the agones-system namespace before you have removed all of the components in the namespace you may end up in a Terminating state.

kubectl get ns
NAME              STATUS        AGE                                                                                                                                                    
agones-system     Terminating   4d

Fixing this up requires us to bypass the finalizer in Kubernetes (article link), by manually changing the namespace details:

First get the current state of the namespace:

kubectl get namespace agones-system -o json >tmp.json

Edit the response tmp.json to remove the finalizer data, for example remove the following:

"spec": {
    "finalizers": [
        "kubernetes"
    ]
},

Open a new terminal to proxy traffic:

 kubectl proxy
Starting to serve on 127.0.0.1:8001

Now make an API call to send the altered namespace data:

 curl -k -H "Content-Type: application/json" -X PUT --data-binary @tmp.json http://127.0.0.1:8001/api/v1/namespaces/agones-system/finalize

You may need to clean up any other Code Blind related resources you have in your cluster at this point.

6 - Common Integration Patterns

Common patterns and integrations of external systems, such as matchmakers, with GameServer starting, allocating and shutdown.

6.1 - Matchmaker requests a GameServer from a Fleet

This is the preferred workflow for a GameServer, in which an external matchmaker requests an allocation from one or more Fleets using a GameServerAllocation.

Allocated Lifecycle Sequence Diagram

Sample GameServerAllocation

Since Code Blind will automatically add the label agones.dev/fleet to a GameServer of a given Fleet, we can use that label selector to target a specific Fleet by name. In this instance, we are targeting the Fleet xonotic.

apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  required:
    matchLabels:
      agones.dev/fleet: xonotic

Next Steps:

6.2 - Matchmaker requires game server process registration

A scenario in which a Matchmaker requires a game server process to register themselves with the matchmaker, and the matchmaker decides which GameServer players are sent to.

In this scenario, the GameServer process will need to self Allocate when informed by the matchmaker that players are being sent to them.

Reserved Lifecycle Sequence Diagram

Next Steps:

  • Read the various references, including the GameServer and Fleet reference materials.
  • Review the specifics of Health Checking.
  • See all the commands the Client SDK provides - we only show a few here!
  • If you aren’t familiar with the term Pod, this should provide a reference.

6.3 - Canary Testing a new Fleet

Run a small Fleet for the new version of your GameServer to ensure it works correctly, before rolling it out to all your players.

To canary release/test a new Fleet, we can run a small, fixed size Fleet of the new version of our GameServer, while also running the current stable production version.

Allocations can then prefer to come from the canary Fleet, but if all GameServers are already allocated from the canary Fleet, players will be allocated to the current stable Fleets.

Over time, if the monitoring of those playing on the canary Fleet is working as expected, the size of the canary Fleet can be grown until you feel confident in its stability.

Once confidence has been achieved, the configuration for stable Fleet can be updated to match the canary (usually triggering a rolling update). The canary Fleet can then be deleted or updated to a new testing version of the game server process.

Canary Fleet Diagram

Sample GameServerAllocation

To ensure we don’t have to change the Allocation system every time we have a canary Fleet, in this example, we will state that in our system, the label canary: "true" will be added to any canary Fleet in the cluster.

apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  preferred:
    - matchLabels:
        canary: "true"
  required:
    matchLabels:
      agones.dev/fleet: stable

The above Allocation will then preferentially choose the Fleet that has GameServers with the label and key value ofcanary:"true", if it exists, and has remaining Ready GameServers, and if not, will apply the Allocation to the Fleet named “stable”.

Next Steps

6.4 - Reusing Allocated GameServers for more than one game session

After a GameServer has completed a player session, return it back to the pool of Ready GameServers for reuse.

Having a GameServer terminate after a single player session is better for packing and optimisation of infrastructure usage, as well as safety to ensure the process returns to an absolute zero state.

However, depending on the GameServer startup time, or other factors there may be reasons you wish to reuse a GameServer for n number of sessions before finally shutting it down.

The “magic trick” to this is knowing that the GameServer process can call SDK.Ready() to return to a Ready state after the GameServer has been allocated.

It is then up to the game developer to ensure that the game server process returns to a zero state once a game session has been completed.

Reserved Lifecycle Sequence Diagram

Next Steps

  • Have a look at all commands the Client SDK provides.
  • If you aren’t familiar with the term Pod, this shouldw provide a reference.

6.5 - High Density GameServers

How to run multiple concurrent game sessions in a single GameServer process.

Depending on the setup and resource requirements of your game server process, sometimes it can be a more economical use of resources to run multiple concurrent game sessions from within a single GameServer instance.

The tradeoff here is that this requires more management on behalf of the integrated game server process and external systems, since it works around the common Kubernetes and/or Code Blind container lifecycle.

Utilising the new allocation gameServerState filter as well as the existing ability to edit the GameServer labels at both allocation time, and from within the game server process, via the SDK, means Code Blind is able to atomically remove a GameServer from the list of potentially allocatable GameServers at allocation time, and then return it back into the pool of allocatable GameServers if and when the game server process deems that is has room to host another game session.

High Density Allocation Diagram

Example GameServerAllocation

The below Allocation will first attempt to find a GameServer from the Fleet simple-udp that is already Allocated and also has the label agones.dev/sdk-gs-session-ready with the value of true.

The above condition indicates that the matching game server process behind the matched GameServer record is able to accept another game session at this time.

If an Allocated GameServer does not exist with the desired labels, then use the next selector to allocate a Ready GameServer from the simple-udp Fleet.

Whichever condition is met, once allocation is made against a GameServer, its label of agones.dev/sdk-gs-session-ready will be set to the value of false and it will no longer match the first selector, thereby removing it from any future allocations with the below schema.

It will then be up to the game server process to decide on if and when it is appropriate to set the agones.dev/sdk-gs-session-ready value back to true, thereby indicating that it can accept another concurrent gameplay session.

apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  selectors:
    - matchLabels:
        agones.dev/fleet: simple-udp
        agones.dev/sdk-gs-session-ready: "true" # this is important
      gameServerState: Allocated # new state filter: allocate from Allocated servers
    - matchLabels:
        agones.dev/fleet: simple-udp
      gameServerState: Ready # Allocate out of the Ready Pool (which would be default, so backward compatible)
  metadata:
    labels:
      agones.dev/sdk-gs-session-ready: "false" # this removes it from the pool

Consistency

Code Blind, and Kubernetes itself are built as eventually consistent, self-healing systems. To that end, it is worth noting that there may be minor delays between each of the operations in the above flow. For example, depending on the cluster load, it may take up to a second for an SDK driven label change on a GameServer record to be visible to the Code Blind allocation system. We recommend building your integrations with Code Blind with this in mind.

Next Steps

6.6 - Allocating based on GameServer Player Capacity

Find a GameServer that has room for a specific number of players.

Using this approach, we are able to be able to make a request that is akin to: “Find me a GameServer that is already allocated, with room for n number of players, and if one is not available, allocate me a Ready GameServer”.

Common applications of this type of allocation are Lobby servers where players await matchmaking, or a persistent world server where players connect and disconnect from a large map.

Player Capacity Allocation Diagram

Example GameServerAllocation

The below allocation will attempt to find an already Allocated GameServer from the Fleet “lobby” with room for 10 to 15 players, and if it cannot find one, will allocate a Ready one from the same Fleet.

apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  selectors:
    - matchLabels:
        agones.dev/fleet: lobby
      gameServerState: Allocated
      players:
        minAvailable: 10
        maxAvailable: 15
    - matchLabels:
        agones.dev/fleet: lobby

Next Steps

  • Have a look at all commands the Client SDK provides.
  • Check all the options available on GameServerAllocation.
  • If you aren’t familiar with the term Pod, this should provide a reference.

7 - Tutorials

Code Blind Tutorials

7.1 - Tutorial Build and Run a Simple Gameserver (Rust)

This tutorial describes how to use the Code Blind Rust SDK in a simple Rust gameserver.

Objectives

  • Run a simple gameserver
  • Understand how the simple gameserver uses the Code Blind Rust SDK
  • Build a customized version of the simple gameserver
  • Run your customized simple gameserver

Prerequisites

  1. Docker
  2. Code Blind installed on GKE
  3. kubectl properly configured
  4. A local copy of the Code Blind repository
  5. A repository for Docker images, such as Docker Hub or GC Container Registry

To install on GKE, follow the install instructions (if you haven’t already) at Setting up a Google Kubernetes Engine (GKE) cluster. Also complete the “Installing Code Blind” instructions on the same page.

While not required, you may wish to review the Create a Game Server, Create a Game Server Fleet, and/or Edit a Game Server quickstarts.

1. Run the simple gameserver

First, run the pre-built version of the simple gameserver and take note of the name that was created:

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/rust-simple/gameserver.yaml
GAMESERVER_NAME=$(kubectl get gs -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')

The game server sets up the Code Blind SDK, calls sdk.ready() to inform Code Blind that it is ready to serve traffic, prints a message every 10 seconds, and then calls sdk.shutdown() after a minute to indicate that the gameserver is going to exit.

You can follow along with the lifecycle of the gameserver by running

kubectl logs ${GAMESERVER_NAME} rust-simple -f

which should produce output similar to

Rust Game Server has started!
Creating SDK instance
Setting a label
Starting to watch GameServer updates...
Health ping sent
Setting an annotation
Marking server as ready...
...marked Ready
Getting GameServer details...
GameServer name: rust-simple-txsc6
Running for 0 seconds
GameServer Update, name: rust-simple-txsc6
GameServer Update, state: Scheduled
GameServer Update, name: rust-simple-txsc6
GameServer Update, state: Scheduled
GameServer Update, name: rust-simple-txsc6
GameServer Update, state: RequestReady
GameServer Update, name: rust-simple-txsc6
GameServer Update, state: Ready
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 10 seconds
GameServer Update, name: rust-simple-txsc6
GameServer Update, state: Ready
...
Shutting down after 60 seconds...
...marked for Shutdown
Running for 60 seconds
Health ping sent
GameServer Update, name: rust-simple-txsc6
GameServer Update, state: Shutdown
GameServer Update, name: rust-simple-txsc6
GameServer Update, state: Shutdown
...

If everything goes as expected, the gameserver will exit automatically after about a minute.

In some cases, the gameserver goes into an unhealthy state, in which case it will be restarted indefinitely. If this happens, you can manually remove it by running

kubectl delete gs ${GAMESERVER_NAME}

2. Build a simple gameserver

Change directories to your local agones/examples/rust-simple directory. To experiment with the SDK, open up main.rs in your favorite editor and change the interval at which the gameserver calls sdk.health() from 2 seconds to 20 seconds by modifying the line in the thread assigned to let _health to be

thread::sleep(Duration::from_secs(20));

Next build a new docker image by running

cd examples/rust-simple
REPOSITORY=<your-repository> # e.g. gcr.io/agones-images
make build-image REPOSITORY=${REPOSITORY}

The multi-stage Dockerfile will pull down all of the dependencies needed to build the image. Note that it is normal for this to take several minutes to complete.

Once the container has been built, push it to your repository

docker push ${REPOSITORY}/rust-simple-server:0.4

3. Run the customized gameserver

Now it is time to deploy your newly created gameserver container into your Code Blind cluster.

First, you need to edit examples/rust-simple/gameserver.yaml to point to your new image:

containers:
- name: rust-simple
  image: $(REPOSITORY)/rust-simple-server:0.4
  imagePullPolicy: Always

Then, deploy your gameserver

kubectl create -f gameserver.yaml
GAMESERVER_NAME=$(kubectl get gs -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')

Again, follow along with the lifecycle of the gameserver by running

kubectl logs ${GAMESERVER_NAME} rust-simple -f

which should produce output similar to

Rust Game Server has started!
Creating SDK instance
Setting a label
Starting to watch GameServer updates...
Health ping sent
Setting an annotation
Marking server as ready...
...marked Ready
Getting GameServer details...
GameServer name: rust-simple-z6lz8
Running for 0 seconds
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: Scheduled
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: RequestReady
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: RequestReady
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: Ready
Running for 10 seconds
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: Ready
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: Unhealthy
Health ping sent
Running for 20 seconds
Running for 30 seconds
Health ping sent
Running for 40 seconds
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: Unhealthy
Running for 50 seconds
Health ping sent
Shutting down after 60 seconds...
...marked for Shutdown
Running for 60 seconds
Running for 70 seconds
GameServer Update, name: rust-simple-z6lz8
GameServer Update, state: Unhealthy
Health ping sent
Running for 80 seconds
Running for 90 seconds
Health ping sent
Rust Game Server finished.

with the slower healthcheck interval, the gameserver gets automatically marked an Unhealthy by Code Blind.

To finish, clean up the gameserver by manually removing it

kubectl delete gs ${GAMESERVER_NAME}

7.2 - Tutorial Build and Run a Simple Gameserver (node.js)

This tutorial describes how to use the Code Blind node.js SDK in a simple node.js gameserver.

Objectives

  • Run a simple gameserver
  • Understand how the simple gameserver uses the Code Blind node.js SDK
  • Build a customized version of the simple gameserver
  • Run your customized simple gameserver

Prerequisites

  1. Docker
  2. Code Blind installed on GKE
  3. kubectl properly configured
  4. A local copy of the Code Blind repository
  5. A repository for Docker images, such as Docker Hub or GC Container Registry

To install on GKE, follow the install instructions (if you haven’t already) at Setting up a Google Kubernetes Engine (GKE) cluster. Also complete the “Installing Code Blind” instructions on the same page.

While not required, you may wish to review the Create a Game Server, Create a Game Server Fleet, and/or Edit a Game Server quickstarts.

1. Run the simple gameserver

First, run the pre-built version of the simple gameserver and take note of the name that was created:

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/nodejs-simple/gameserver.yaml
GAMESERVER_NAME=$(kubectl get gs -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')

The game server sets up the Code Blind SDK, calls sdk.ready() to inform Code Blind that it is ready to serve traffic, prints a message every 10 seconds, and then calls sdk.shutdown() after a minute to indicate that the gameserver is going to exit.

You can follow along with the lifecycle of the gameserver by running

kubectl logs ${GAMESERVER_NAME} nodejs-simple -f

which should produce output similar to

> @ start /home/server/examples/nodejs-simple
> node src/index.js

node.js Game Server has started!
Setting a label
(node:20) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
Setting an annotation
Marking server as ready...
...marked Ready
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: Scheduled
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: RequestReady
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: RequestReady
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: Ready
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 10 seconds!
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 20 seconds!
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: Ready
Running for 30 seconds!
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 40 seconds!
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 50 seconds!
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: Ready
Running for 60 seconds!
Shutting down after 60 seconds...
...marked for Shutdown
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: Shutdown
Health ping sent
GameServer Update:
	name: nodejs-simple-9bw4g 
	state: Shutdown
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 70 seconds!
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 80 seconds!
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 90 seconds!

If everything goes as expected, the gameserver will exit automatically after about a minute.

In some cases, the gameserver goes into an unhealthy state, in which case it will be restarted indefinitely. If this happens, you can manually remove it by running

kubectl delete gs ${GAMESERVER_NAME}

2. Build a simple gameserver

Change directories to your local agones/examples/nodejs-simple directory. To experiment with the SDK, open up src/index.js in your favorite editor and change the interval at which the gameserver calls sdk.health() from 2 seconds to 20 seconds by modifying the lines in the health ping handler to be

setInterval(() => {
	agonesSDK.health();
	console.log('Health ping sent');
}, 20000);

Next build a new docker image by running

cd examples/nodejs-simple
REPOSITORY=<your-repository> # e.g. gcr.io/agones-images
make build REPOSITORY=${REPOSITORY}

Once the container has been built, push it to your repository

docker push ${REPOSITORY}/nodejs-simple-server:0.1

3. Run the customized gameserver

Now it is time to deploy your newly created gameserver container into your Code Blind cluster.

First, you need to edit examples/nodejs-simple/gameserver.yaml to point to your new image:

containers:
- name: nodejs-simple
  image: $(REPOSITORY)/nodejs-simple-server:0.1
  imagePullPolicy: Always

Then, deploy your gameserver

kubectl create -f gameserver.yaml
GAMESERVER_NAME=$(kubectl get gs -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')

Again, follow along with the lifecycle of the gameserver by running

kubectl logs ${GAMESERVER_NAME} nodejs-simple -f

which should produce output similar to

> @ start /home/server/examples/nodejs-simple
> node src/index.js

node.js Game Server has started!
Setting a label
(node:20) [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead.
Setting an annotation
Marking server as ready...
...marked Ready
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: Scheduled
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: Scheduled
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: RequestReady
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: Ready
Running for 10 seconds!
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: Unhealthy
Health ping sent
Running for 20 seconds!
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: Unhealthy
Running for 30 seconds!
Health ping sent
Running for 40 seconds!
Running for 50 seconds!
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: Unhealthy
Health ping sent
Shutting down after 60 seconds...
...marked for Shutdown
Running for 60 seconds!
Running for 70 seconds!
Health ping sent
Running for 80 seconds!
GameServer Update:
	name: nodejs-simple-qkpqn 
	state: Unhealthy
Running for 90 seconds!

with the slower healthcheck interval, the gameserver gets automatically marked an Unhealthy by Code Blind.

To finish, clean up the gameserver by manually removing it

kubectl delete gs ${GAMESERVER_NAME}

7.3 - Tutorial Build and Run a Simple Gameserver (C++)

This tutorial describes how to use the Code Blind C++ SDK in a simple C++ gameserver.

Objectives

  • Run a simple gameserver
  • Understand how the simple gameserver uses the Code Blind C++ SDK
  • Build a customized version of the simple gameserver
  • Run your customized simple gameserver

Prerequisites

  1. Docker
  2. Code Blind installed on GKE
  3. kubectl properly configured
  4. A local copy of the Code Blind repository
  5. A repository for Docker images, such as Docker Hub or GC Container Registry

To install on GKE, follow the install instructions (if you haven’t already) at Setting up a Google Kubernetes Engine (GKE) cluster. Also complete the “Installing Code Blind” instructions on the same page.

While not required, you may wish to review the Create a Game Server, Create a Game Server Fleet, and/or Edit a Game Server quickstarts.

1. Run the simple gameserver

First, run the pre-built version of the simple gameserver and take note of the name that was created:

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/cpp-simple/gameserver.yaml
GAMESERVER_NAME=$(kubectl get gs -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')

The game server sets up the Code Blind SDK, calls SDK::Ready() to inform Code Blind that it is ready to serve traffic, prints a message every 10 seconds, and then calls SDK::Shutdown() after a minute to indicate that the gameserver is going to exit.

You can follow along with the lifecycle of the gameserver by running

kubectl logs ${GAMESERVER_NAME} cpp-simple -f

which should produce output similar to

C++ Game Server has started!
Getting the instance of the SDK!
Attempting to connect...
...handshake complete.
Setting a label
Starting to watch GameServer updates...
Health ping sent
Setting an annotation
Marking server as ready...
...marked Ready
Getting GameServer details...
GameServer name: cpp-simple-tlgzp
Running for 0 seconds !
GameServer Update:
	name: cpp-simple-tlgzp
	state: Scheduled
GameServer Update:
	name: cpp-simple-tlgzp
	state: RequestReady
GameServer Update:
	name: cpp-simple-tlgzp
	state: RequestReady
GameServer Update:
	name: cpp-simple-tlgzp
	state: Ready
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Health ping sent
Running for 10 seconds !
Health ping sent
...
GameServer Update:
	name: cpp-simple-2mtdc
	state: Ready
Shutting down after 60 seconds...
...marked for Shutdown
Running for 60 seconds !
Health ping sent
GameServer Update:
	name: cpp-simple-2mtdc
	state: Shutdown
GameServer Update:
	name: cpp-simple-2mtdc
	state: Shutdown
Health ping failed
Health ping failed
Health ping failed
Health ping failed
Running for 70 seconds !
Health ping failed
Health ping failed
Health ping failed
Health ping failed
Health ping failed
Running for 80 seconds !
Health ping failed
Health ping failed
Health ping failed
Health ping failed
Health ping failed

If everything goes as expected, the gameserver will exit automatically after about a minute.

In some cases, the gameserver goes into an unhealthy state, in which case it will be restarted indefinitely. If this happens, you can manually remove it by running

kubectl delete gs ${GAMESERVER_NAME}

2. Run a fleet of simple gameservers

Next, run a fleet of gameservers

kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.38.0/examples/cpp-simple/fleet.yaml
FLEET_NAME=$(kubectl get fleets -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')

You can again inspect the output of an individual gameserver (which will look the same as above), but what is more interesting is to watch the set of all gameservers over time. Each gameserver exits after about a minute, but a fleet is responsible for keeping a sufficient number of gameservers in the Ready state. So as each gameserver exits, it is replaced by a new one. You can see this in action by running

watch "kubectl get gameservers"

which should show how gameservers are constantly transitioning from Scheduled to Ready to Shutdown before disappearing.

When you are finished watching the fleet produce new gameservers you should remove the fleet by running

kubectl delete fleet ${FLEET_NAME}

3. Build a simple gameserver

Change directories to your local agones/examples/cpp-simple directory. To experiment with the SDK, open up server.cc in your favorite editor and change the interval at which the gameserver calls SDK::Health from 2 seconds to 20 seconds by modifying the line in DoHealth to be

std::this_thread::sleep_for(std::chrono::seconds(20));

Next build a new docker image by running

cd examples/cpp-simple
REPOSITORY=<your-repository> # e.g. gcr.io/agones-images
make build REPOSITORY=${REPOSITORY}

The multi-stage Dockerfile will pull down all of the dependencies needed to build the image. Note that it is normal for this to take several minutes to complete.

Once the container has been built, push it to your repository

docker push ${REPOSITORY}/cpp-simple-server:0.6

4. Run the customized gameserver

Now it is time to deploy your newly created gameserver container into your Code Blind cluster.

First, you need to edit examples/cpp-simple/gameserver.yaml to point to your new image:

containers:
- name: cpp-simple
  image: $(REPOSITORY)/cpp-simple-server:0.6
  imagePullPolicy: Always # add for development

Then, deploy your gameserver

kubectl create -f gameserver.yaml
GAMESERVER_NAME=$(kubectl get gs -o go-template --template '{{range .items}}{{.metadata.name}}{{"\n"}}{{end}}')

Again, follow along with the lifecycle of the gameserver by running

kubectl logs ${GAMESERVER_NAME} cpp-simple -f

which should produce output similar to

C++ Game Server has started!
Getting the instance of the SDK!
Attempting to connect...
...handshake complete.
Setting a label
Health ping sent
Starting to watch GameServer updates...
Setting an annotation
Marking server as ready...
...marked Ready
Getting GameServer details...
GameServer name: cpp-simple-f255n
Running for 0 seconds !
GameServer Update:
	name: cpp-simple-f255n
	state: Scheduled
GameServer Update:
	name: cpp-simple-f255n
	state: Scheduled
GameServer Update:
	name: cpp-simple-f255n
	state: RequestReady
GameServer Update:
	name: cpp-simple-f255n
	state: Ready
Running for 10 seconds !
GameServer Update:
	name: cpp-simple-f255n
	state: Unhealthy
Health ping sent
Running for 20 seconds !
GameServer Update:
	name: cpp-simple-f255n
	state: Unhealthy
Running for 30 seconds !
Health ping sent
Running for 40 seconds !
Running for 50 seconds !
GameServer Update:
	name: cpp-simple-f255n
	state: Unhealthy
Health ping sent
Shutting down after 60 seconds...
...marked for Shutdown
Running for 60 seconds !
Running for 70 seconds !
Health ping sent
Running for 80 seconds !
GameServer Update:
	name: cpp-simple-f255n
	state: Unhealthy
Running for 90 seconds !
Health ping sent

with the slower healthcheck interval, the gameserver gets automatically marked an Unhealthy by Code Blind.

To finish, clean up the gameserver by manually removing it

kubectl delete gs ${GAMESERVER_NAME}

8 - Reference

Reference documentation for Code Blind Custom Resource Definitions

8.1 - GameServer Specification

Like any other Kubernetes resource you describe a GameServer’s desired state via a specification written in YAML or JSON to the Kubernetes API. The Code Blind controller will then change the actual state to the desired state.

A full GameServer specification is available below and in the example folder for reference :

apiVersion: "agones.dev/v1"
kind: GameServer
# GameServer Metadata
# https://v1-27.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta
metadata:
  # generateName: "gds-example" # generate a unique name, with the given prefix
  name: "gds-example" # set a fixed name
spec:
  # if there is more than one container, specify which one is the game server
  container: example-server
  # Array of ports that can be exposed as direct connections to the game server container
  ports:
    # name is a descriptive name for the port
  - name: default
    # portPolicy has three options:
    # - "Dynamic" (default) the system allocates a free hostPort for the gameserver, for game clients to connect to
    # - "Static", user defines the hostPort that the game client will connect to. Then onus is on the user to ensure that the
    # port is available. When static is the policy specified, `hostPort` is required to be populated
    # - "Passthrough" dynamically sets the `containerPort` to the same value as the dynamically selected hostPort.
    #      This will mean that users will need to lookup what port has been opened through the server side SDK.
    portPolicy: Static
    # The name of the container to open the port on. Defaults to the game server container if omitted or empty.
    container: simple-game-server
    # the port that is being opened on the game server process
    containerPort: 7654
    # the port exposed on the host, only required when `portPolicy` is "Static". Overwritten when portPolicy is "Dynamic".
    hostPort: 7777
    # protocol being used. Defaults to UDP. TCP and TCPUDP are other options
    # - "UDP" (default) use the UDP protocol
    # - "TCP", use the TCP protocol
    # - "TCPUDP", uses both TCP and UDP, and exposes the same hostPort for both protocols.
    #       This will mean that it adds an extra port, and the first port is set to TCP, and second port set to UDP
    protocol: UDP
  # Health checking for the running game server
  health:
    # Disable health checking. defaults to false, but can be set to true
    disabled: false
    # Number of seconds after the container has started before health check is initiated. Defaults to 5 seconds
    initialDelaySeconds: 5
    # If the `Health()` function doesn't get called at least once every period (seconds), then
    # the game server is not healthy. Defaults to 5
    periodSeconds: 5
    # Minimum consecutive failures for the health probe to be considered failed after having succeeded.
    # Defaults to 3. Minimum value is 1
    failureThreshold: 3
  # Parameters for game server sidecar
  sdkServer:
    # sdkServer log level parameter has three options:
    #  - "Info" (default) The SDK server will output all messages except for debug messages
    #  - "Debug" The SDK server will output all messages including debug messages
    #  - "Error" The SDK server will only output error messages
    logLevel: Info
    # grpcPort and httpPort control what ports the sdkserver listens on.
    # Starting with Code Blind 1.2 the default grpcPort is 9357 and the default
    # httpPort is 9358. In earlier releases, the defaults were 59357 and 59358
    # respectively but as these were in the ephemeral port range they could
    # conflict with other TCP connections.
    grpcPort: 9357
    httpPort: 9358
  # [Stage:Alpha]
  # [FeatureFlag:PlayerTracking]
  # Players provides the configuration for player tracking features.
  # Commented out since Alpha, and disabled by default
  # players:
  #   # set this GameServer's initial player capacity
  #   initialCapacity: 10
  #
  # [Stage:Alpha]
  # [FeatureFlag:CountsAndLists]
  # Counts and Lists provides the configuration for generic (player, room, session, etc.) tracking features.
  # Commented out since Alpha, and disabled by default
  # counters: # counters are int64 counters that can be incremented and decremented by set amounts. Keys must be declared at GameServer creation time.
  #   games: # arbitrary key.
  #     count: 1 # initial value.
  #     capacity: 100 # (Optional) Defaults to 1000 and setting capacity to max(int64) may lead to issues and is not recommended. See GitHub issue https://github.com/googleforgames/agones/issues/3636 for more details.
  #   sessions:
  #     count: 1
  # lists: # lists are lists of values stored against this GameServer that can be added and deleted from. Keys must be declared at GameServer creation time.
  #   players: # an empty list, with a capacity set to 10.
  #     capacity: 10 # capacity value, defaults to 1000.
  #   rooms:
  #     capacity: 333
  #     values: # initial set of values in a list.
  #       - room1
  #       - room2
  #       - room3
  #  
  # Pod template configuration
  # https://v1-27.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplate-v1-core
  template:
    # pod metadata. Name & Namespace is overwritten
    metadata:
      labels:
        myspeciallabel: myspecialvalue
    # Pod Specification
    spec:
      containers:
      - name: simple-game-server
        image:  us-docker.pkg.dev/codeblind/examples/simple-server:0.27
        imagePullPolicy: Always
      # nodeSelector is a label that can be used to tell Kubernetes which host
      # OS to use. For Windows game servers uncomment the nodeSelector
      # definition below.
      # Details: https://kubernetes.io/docs/setup/production-environment/windows/user-guide-windows-containers/#ensuring-os-specific-workloads-land-on-the-appropriate-container-host
      # nodeSelector:
      #   kubernetes.io/os: windows

Since Code Blind defines a new Custom Resources Definition (CRD) we can define a new resource using the kind GameServer with the custom group agones.dev and API version v1.

You can use the metadata field to target a specific namespaces but also attach specific annotations and labels to your resource. This is a very common pattern in the Kubernetes ecosystem.

The length of the name field of the Gameserver should not exceed 63 characters.

The spec field is the actual GameServer specification and it is composed as follow:

  • container is the name of container running the GameServer in case you have more than one container defined in the pod. If you do, this is a mandatory field. For instance this is useful if you want to run a sidecar to ship logs.
  • ports are an array of ports that can be exposed as direct connections to the game server container
    • name is an optional descriptive name for a port
    • portPolicy has three options: - Dynamic (default) the system allocates a random free hostPort for the gameserver, for game clients to connect to. - Static, user defines the hostPort that the game client will connect to. Then onus is on the user to ensure that the port is available. When static is the policy specified, hostPort is required to be populated. - Passthrough dynamically sets the containerPort to the same value as the dynamically selected hostPort. This will mean that users will need to lookup what port to open through the server side SDK before starting communications.
    • container (Alpha) the name of the container to open the port on. Defaults to the game server container if omitted or empty.
    • containerPort the port that is being opened on the game server process, this is a required field for Dynamic and Static port policies, and should not be included in Passthrough configuration.
    • protocol the protocol being used. Defaults to UDP. TCP and TCPUDP are other options.
  • health to track the overall healthy state of the GameServer, more information available in the health check documentation.
  • sdkServer defines parameters for the game server sidecar
    • logging field defines log level for SDK server. Defaults to “Info”. It has three options:
      • “Info” (default) The SDK server will output all messages except for debug messages
      • “Debug” The SDK server will output all messages including debug messages
      • “Error” The SDK server will only output error messages
    • grpcPort the port that the SDK Server binds to for gRPC connections
    • httpPort the port that the SDK Server binds to for HTTP gRPC gateway connections
  • players (Alpha, behind “PlayerTracking” feature gate), sets this GameServer’s initial player capacity
  • counters (Alpha, requires “CountsAndLists” feature flag) are int64 counters with a default capacity of 1000 that can be incremented and decremented by set amounts. Keys must be declared at GameServer creation time. Note that setting the capacity to max(int64) may lead to issues.
  • lists (Alpha, requires “CountsAndLists” feature flag) are lists of values stored against this GameServer that can be added and deleted from. Key must be declared at GameServer creation time.
  • template the pod spec template to run your GameServer containers, see for more information.

Stable Network ID

If you want to connect to a GameServer from within your Kubernetes cluster via a convention based DNS entry, each Pod attached to a GameServer automatically derives its hostname from the name of the GameServer.

To create internal DNS entries within the cluster, a group of Pods attached to GameServers can use a Headless Service to control the domain of the Pods, along with providing a subdomain value to the GameServer PodTemplateSpec to provide all the required details such that Kubernetes will create a DNS record for each Pod behind the Service.

You are also responsible for setting the labels on the GameServer.Spec.Template.Metadata to set the labels on the created Pods and creating the Headless Service responsible for the network identity of the pods, Code Blind will not do this for you, as a stable DNS record is not required for all use cases.

To ensure that the hostName value matches RFC 1123, any . values in the GameServer name are replaced by - when setting the underlying Pod.Spec.HostName value.

GameServer State Diagram

The following diagram shows the lifecycle of a GameServer.

Game Servers are created through Kubernetes API (either directly or through a Fleet) and their state transitions are orchestrated by:

  • GameServer controller, which allocates ports, launches Pods backing game servers and manages their lifetime
  • Allocation controller, which marks game servers as Allocated to handle a game session
  • SDK, which manages health checking and shutdown of a game server session

GameServer State Diagram

Primary Address vs Addresses

GameServer.Status has two fields which reflect the network address of the GameServer: address and addresses. The address field is a policy-based choice of “primary address” that will work for many use cases, and will always be one of the addresses. The addresses field contains every address in the Node.Status.addresses, representing all known ways to reach the GameServer over the network.

To choose address from addresses, Code Blind looks for the following address types, in highest to lowest priorty:

  • ExternalDNS
  • ExternalIP
  • InternalDNS
  • InternalIP

e.g. if any ExternalDNS address is found in the respective Node, it is used as the address.

The policy for address will work for many use-cases, but for some advanced cases, such as IPv6 enablement, you may need to evaluate all addresses and pick the addresses that best suits your needs.

8.2 - Fleet Specification

A Fleet is a set of warm GameServers that are available to be allocated from.

To allocate a GameServer from a Fleet, use a GameServerAllocation.

Like any other Kubernetes resource you describe a Fleet’s desired state via a specification written in YAML or JSON to the Kubernetes API. The Code Blind controller will then change the actual state to the desired state.

A full Fleet specification is available below and in the example folder for reference :

apiVersion: "agones.dev/v1"
kind: Fleet
# Fleet Metadata
# https://v1-27.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta
metadata:
  name: fleet-example
spec:
  # the number of GameServers to keep Ready or Allocated in this Fleet
  replicas: 2
  # defines how GameServers are organised across the cluster.
  # Options include:
  # "Packed" (default) is aimed at dynamic Kubernetes clusters, such as cloud providers, wherein we want to bin pack
  # resources
  # "Distributed" is aimed at static Kubernetes clusters, wherein we want to distribute resources across the entire
  # cluster
  scheduling: Packed
  # a GameServer template - see:
  # https://agones.dev/docs/reference/gameserver/ for all the options
  strategy:
    # The replacement strategy for when the GameServer template is changed. Default option is "RollingUpdate",
    # "RollingUpdate" will increment by maxSurge value on each iteration, while decrementing by maxUnavailable on each
    # iteration, until all GameServers have been switched from one version to another.
    # "Recreate" terminates all non-allocated GameServers, and starts up a new set with the new details to replace them.
    type: RollingUpdate
    # Only relevant when `type: RollingUpdate`
    rollingUpdate:
      # the amount to increment the new GameServers by. Defaults to 25%
      maxSurge: 25%
      # the amount to decrements GameServers by. Defaults to 25%
      maxUnavailable: 25%
  # [Stage:Beta]
  # [FeatureFlag:FleetAllocationOverflow]
  # Labels and/or Annotations to apply to overflowing GameServers when the number of Allocated GameServers is more
  # than the desired replicas on the underlying `GameServerSet`
  allocationOverflow:
    labels:
      mykey: myvalue
      version: "" # empty an existing label value
    annotations:
      otherkey: setthisvalue
  #
  # [Stage:Alpha]
  # [FeatureFlag:CountsAndLists]
  # Which gameservers in the Fleet are most important to keep around - impacts scale down logic.
  # priorities:
  # - type: Counter # Sort by a “Counter”
  #   key: player # The name of the Counter. No impact if no GameServer found.
  #   order: Descending # Default is "Ascending" so smaller capacity will be removed first on down scaling.
  # - type: List # Sort by a “List”
  #   key: room # The name of the List. No impact if no GameServer found.
  #   order: Ascending # Default is "Ascending" so smaller capacity will be removed first on down scaling.
  #      
  template:
    # GameServer metadata
    metadata:
      labels:
        foo: bar
    # GameServer specification
    spec:
      ports:
      - name: default
        portPolicy: Dynamic
        containerPort: 26000
      health:
        initialDelaySeconds: 30
        periodSeconds: 60
      # Parameters for game server sidecar
      sdkServer:
        logLevel: Info
        grpcPort: 9357
        httpPort: 9358
      # The GameServer's Pod template
      template:
        spec:
          containers:
          - name: simple-game-server
            image: us-docker.pkg.dev/codeblind/examples/simple-server:0.27

Since Code Blind defines a new Custom Resources Definition (CRD) we can define a new resource using the kind Fleet with the custom group agones.dev and API version v1.

You can use the metadata field to target a specific namespaces but also attach specific annotations and labels to your resource. This is a very common pattern in the Kubernetes ecosystem.

The length of the name field of the fleet should be at most 63 characters.

The spec field is the actual Fleet specification and it is composed as follow:

  • replicas is the number of GameServers to keep Ready or Allocated in this Fleet
  • scheduling defines how GameServers are organised across the cluster. Affects backing Pod scheduling, as well as scale down mechanics. “Packed” (default) is aimed at dynamic Kubernetes clusters, such as cloud providers, wherein we want to bin pack resources. “Distributed” is aimed at static Kubernetes clusters, wherein we want to distribute resources across the entire cluster. See Scheduling and Autoscaling for more details.
  • strategy is the GameServer replacement strategy for when the GameServer template is edited.
    • type is replacement strategy for when the GameServer template is changed. Default option is “RollingUpdate”, but “Recreate” is also available.
      • RollingUpdate will increment by maxSurge value on each iteration, while decrementing by maxUnavailable on each iteration, until all GameServers have been switched from one version to another.
      • Recreate terminates all non-allocated GameServers, and starts up a new set with the new GameServer configuration to replace them.
    • rollingUpdate is only relevant when type: RollingUpdate
      • maxSurge is the amount to increment the new GameServers by. Defaults to 25%
      • maxUnavailable is the amount to decrements GameServers by. Defaults to 25%
  • allocationOverflow (Beta, requires FleetAllocationOverflow flag) The labels and/or Annotations to apply to GameServers when the number of Allocated GameServers exceeds the desired replicas in the underlying GameServerSet.
    • labels the map of labels to be applied
    • annotations the map of annotations to be applied
    • Fleet's Scheduling Strategy: The GameServers associated with the GameServerSet are sorted based on either Packed or Distributed strategy.
      • Packed: Code Blind maximizes resource utilization by trying to populate nodes that are already in use before allocating GameServers to other nodes.
      • Distributed: Code Blind employs this strategy to spread out GameServer allocations, ensuring an even distribution of GameServers across the available nodes.
  • priorities: (Alpha, requires CountsAndLists feature flag): Defines which gameservers in the Fleet are most important to keep around - impacts scale down logic.
    • type: Sort by a “Counter” or a “List”.
    • key: The name of the Counter or List. If not found on the GameServer, has no impact.
    • order: Order: Sort by “Ascending” or “Descending”. “Descending” a bigger Capacity is preferred. “Ascending” would be smaller Capacity is preferred.
  • template a full GameServer configuration template. See the GameServer reference for all available fields.

Fleet Scale Subresource Specification

Scale subresource is defined for a Fleet. Please refer to Kubernetes docs.

You can use the following command to scale the fleet with name simple-game-server:

kubectl scale fleet simple-game-server --replicas=10
fleet.agones.dev/simple-game-server scaled

You can also use Kubernetes API to get or update the Replicas count:

curl http://localhost:8001/apis/agones.dev/v1/namespaces/default/fleets/simple-game-server/scale
{
  "kind": "Scale",
  "apiVersion": "autoscaling/v1",
  "metadata": {
    "name": "simple-game-server",
    "namespace": "default",
    "selfLink": "/apis/agones.dev/v1/namespaces/default/fleets/simple-game-server/scale",
    "uid": "4dfaa310-2566-11e9-afd1-42010a8a0058",
    "resourceVersion": "292652",
    "creationTimestamp": "2019-01-31T14:41:33Z"
  },
  "spec": {
    "replicas": 10
  },
  "status": {
    "replicas": 10
  }

Also exposing a Scale subresource would allow you to configure HorizontalPodAutoscaler and PodDisruptionBudget for a fleet in the future. However these features have not been tested, and are not currently supported - but if you are looking for these features, please be sure to let us know in the ticket.

8.3 - GameServerAllocation Specification

A GameServerAllocation is used to atomically allocate a GameServer out of a set of GameServers. This could be a single Fleet, multiple Fleets, or a self managed group of GameServers.

Allocation is the process of selecting the optimal GameServer that matches the filters defined in the GameServerAllocation specification below, and returning its details.

A successful Alloction moves the GameServer to the Allocated state, which indicates that it is currently active, likely with players on it, and should not be removed until SDK.Shutdown() is called, or it is explicitly manually deleted.

A full GameServerAllocation specification is available below and in the example folder for reference:

apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  # GameServer selector from which to choose GameServers from.
  # Defaults to all GameServers.
  # matchLabels, matchExpressions, gameServerState and player filters can be used for filtering.
  # See: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more details on label selectors.
  # An ordered list of GameServer label selectors.
  # If the first selector is not matched, the selection attempts the second selector, and so on.
  # This is useful for things like smoke testing of new game servers.
  selectors:
    - matchLabels:
        agones.dev/fleet: green-fleet
        # [Stage:Alpha]
        # [FeatureFlag:PlayerAllocationFilter]
      players:
        minAvailable: 0
        maxAvailable: 99
    - matchLabels:
        agones.dev/fleet: blue-fleet
    - matchLabels:
        game: my-game
      matchExpressions:
        - {key: tier, operator: In, values: [cache]}
      # Specifies which State is the filter to be used when attempting to retrieve a GameServer
      # via Allocation. Defaults to "Ready". The only other option is "Allocated", which can be used in conjunction with
      # label/annotation/player selectors to retrieve an already Allocated GameServer.
      gameServerState: Ready
      # [Stage:Alpha]
      # [FeatureFlag:CountsAndLists]
      # counters: # selector for counter current values of a GameServer count
      #   rooms:
      #     minCount: 1 # minimum value. Defaults to 0.
      #     maxCount: 5 # maximum value. Defaults to max(int64)
      #     minAvailable: 1 # minimum available (current capacity - current count). Defaults to 0.
      #     maxAvailable: 10 # maximum available (current capacity - current count) Defaults to max(int64)
      # lists:
      #   players:
      #     containsValue: "x6k8z" # only match GameServers who has this value in the list. Defaults to "", which is all.
      #     minAvailable: 1 # minimum available (current capacity - current count). Defaults to 0.
      #     maxAvailable: 10 # maximum available (current capacity - current count) Defaults to 0, which translates to max(int64)
      # [Stage:Alpha]      
      # [FeatureFlag:PlayerAllocationFilter]
      # Provides a filter on minimum and maximum values for player capacity when retrieving a GameServer
      # through Allocation. Defaults to no limits.
      players:
        minAvailable: 0
        maxAvailable: 99
  # defines how GameServers are organised across the cluster.
  # Options include:
  # "Packed" (default) is aimed at dynamic Kubernetes clusters, such as cloud providers, wherein we want to bin pack
  # resources
  # "Distributed" is aimed at static Kubernetes clusters, wherein we want to distribute resources across the entire
  # cluster
  scheduling: Packed
  # Optional custom metadata that is added to the game server at allocation
  # You can use this to tell the server necessary session data
  metadata:
    labels:
      mode: deathmatch
    annotations:
      map:  garden22
    # [Stage:Alpha]
    # [FeatureFlag:CountsAndLists]
    # The first Priority on the array of Priorities is the most important for sorting. The allocator will
    # use the first priority for sorting GameServers by available Capacity in the Selector set. Acts as a
    # tie-breaker after sorting the game servers by State and Strategy Packed. Impacts which GameServer
    # is checked first. Optional.
    # priorities:
    # - type: List  # Whether a Counter or a List.
    #   key: rooms  # The name of the Counter or List.
    #   order: Ascending  # "Ascending" lists smaller available capacity first.
    # [Stage:Alpha]
    # [FeatureFlag:CountsAndLists]
    # Counter actions to perform during allocation. Optional.
    # counters:
    #   rooms:
    #     action: increment # Either "Increment" or "Decrement" the Counter’s Count.
    #     amount: 1 # Amount is the amount to increment or decrement the Count. Must be a positive integer.
    #     capacity: 5 # Amount to update the maximum capacity of the Counter to this number. Min 0, Max int64.
    # List actions to perform during allocation. Optional.
    # lists:
    #   players:
    #     addValues: # appends values to a List’s Values array. Any duplicate values will be ignored
    #       - x7un
    #       - 8inz
    #     capacity: 40 # Updates the maximum capacity of the Counter to this number. Min 0, Max 1000.
  
apiVersion: "allocation.agones.dev/v1"
kind: GameServerAllocation
spec:
  # Deprecated, use field selectors instead.
  # GameServer selector from which to choose GameServers from.
  # Defaults to all GameServers.
  # matchLabels, matchExpressions, gameServerState and player filters can be used for filtering.
  # See: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ for more details on label selectors.
  # Deprecated, use field selectors instead.
  required:
    matchLabels:
      game: my-game
    matchExpressions:
      - {key: tier, operator: In, values: [cache]}
    # Specifies which State is the filter to be used when attempting to retrieve a GameServer
    # via Allocation. Defaults to "Ready". The only other option is "Allocated", which can be used in conjunction with
    # label/annotation/player selectors to retrieve an already Allocated GameServer.
    gameServerState: Ready
    # [Stage:Alpha]
    # [FeatureFlag:PlayerAllocationFilter]
    # Provides a filter on minimum and maximum values for player capacity when retrieving a GameServer
    # through Allocation. Defaults to no limits.
    players:
      minAvailable: 0
      maxAvailable: 99
  # Deprecated, use field selectors instead.
  # An ordered list of preferred GameServer label selectors
  # that are optional to be fulfilled, but will be searched before the `required` selector.
  # If the first selector is not matched, the selection attempts the second selector, and so on.
  # If any of the preferred selectors are matched, the required selector is not considered.
  # This is useful for things like smoke testing of new game servers.
  # This also support matchExpressions, gameServerState and player filters.
  preferred:
    - matchLabels:
        agones.dev/fleet: green-fleet
      # [Stage:Alpha]
      # [FeatureFlag:PlayerAllocationFilter]
      players:
        minAvailable: 0
        maxAvailable: 99
    - matchLabels:
        agones.dev/fleet: blue-fleet
  # defines how GameServers are organised across the cluster.
  # Options include:
  # "Packed" (default) is aimed at dynamic Kubernetes clusters, such as cloud providers, wherein we want to bin pack
  # resources
  # "Distributed" is aimed at static Kubernetes clusters, wherein we want to distribute resources across the entire
  # cluster
  scheduling: Packed
  # Optional custom metadata that is added to the game server at allocation
  # You can use this to tell the server necessary session data
  metadata:
    labels:
      mode: deathmatch
    annotations:
      map:  garden22
  

The spec field is the actual GameServerAllocation specification, and it is composed as follows:

  • Deprecated, use selectors instead. If selectors is set, this field will be ignored. required is a GameServerSelector (matchLabels. matchExpressions, gameServerState and player filters) from which to choose GameServers from.
  • Deprecated, use selectors instead. If selectors is set, this field will be ignored. preferred is an ordered list of preferred GameServerSelector that are optional to be fulfilled, but will be searched before the required selector. If the first selector is not matched, the selection attempts the second selector, and so on. If any of the preferred selectors are matched, the required selector is not considered. This is useful for things like smoke testing of new game servers.
  • selectors is an ordered list of GameServerSelector. If the first selector is not matched, the selection attempts the second selector, and so on. This is useful for things like smoke testing of new game servers.
  • matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is “key”, the operator is “In”, and the values array contains only “value”. The requirements are ANDed. Optional.
  • matchExpressions is a list of label selector requirements. The requirements are ANDed. Optional.
  • gameServerState GameServerState specifies which State is the filter to be used when attempting to retrieve a GameServer via Allocation. Defaults to “Ready”. The only other option is “Allocated”, which can be used in conjunction with label/annotation/player selectors to retrieve an already Allocated GameServer.
  • counters (Alpha, “CountsAndLists” feature flag) enables filtering based on game server Counter status, such as the minimum and maximum number of active rooms. This helps in selecting game servers based on their current activity or capacity. Optional.
  • lists (Alpha, “CountsAndLists” feature flag) enables filtering based on game server List status, such as allowing for inclusion or exclusion of specific players. Optional.
  • scheduling defines how GameServers are organised across the cluster, in this case specifically when allocating GameServers for usage. “Packed” (default) is aimed at dynamic Kubernetes clusters, such as cloud providers, wherein we want to bin pack resources. “Distributed” is aimed at static Kubernetes clusters, wherein we want to distribute resources across the entire cluster. See Scheduling and Autoscaling for more details.
  • metadata is an optional list of custom labels and/or annotations that will be used to patch the game server’s metadata in the moment of allocation. This can be used to tell the server necessary session data
  • priorities (Alpha, requires CountsAndLists feature flag) manages counters and lists for game servers, setting limits on room counts and player inclusion/exclusion.
  • counters (Alpha, “CountsAndLists” feature flag) Counter actions to perform during allocation.
  • lists (Alpha, “CountsAndLists” feature flag) List actions to perform during allocation.

Once created the GameServerAllocation will have a status field consisting of the following:

  • State is the current state of a GameServerAllocation, e.g. Allocated, or UnAllocated
  • GameServerName is the name of the game server attached to this allocation, once the state is Allocated
  • Ports is a list of the ports that the game server makes available. See the GameServer Reference for more details.
  • Address is the primary network address where the game server can be reached.
  • Addresses is an array of all network addresses where the game server can be reached. It is a copy of the Node.Status.addresses field for the node the GameServer is scheduled on.
  • NodeName is the name of the node that the gameserver is running on.
  • Source is “local” unless this allocation is from a remote cluster, in which case Source is the endpoint of the remote agones-allocator. See Multi-cluster Allocation for more details.
  • Metadata conststs of:
    • Labels containing the labels of the game server at allocation time.
    • Annotations containing the annotations of the underlying game server at allocation time.

Each GameServerAllocation will allocate from a single namespace. The namespace can be specified outside of the spec, either with the --namespace flag when using the command line / kubectl or in the url when using an API call. If not specified when using the command line, the namespace will be automatically set to default.

Next Steps:

8.4 - Fleet Autoscaler Specification

A FleetAutoscaler’s job is to automatically scale up and down a Fleet in response to demand.

A full FleetAutoscaler specification is available below and in the example folder for reference, but here are several examples that show different autoscaling policies.

Ready Buffer Autoscaling

Fleet autoscaling with a buffer can be used to maintain a configured number of game server instances ready to serve players based on number of allocated instances in a Fleet. The buffer size can be specified as an absolute number or a percentage of the desired number of Ready game server instances over the Allocated count.

apiVersion: "autoscaling.agones.dev/v1"
kind: FleetAutoscaler
# FleetAutoscaler Metadata
# https://v1-27.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta
metadata:
  name: fleet-autoscaler-example
spec:
  # The name of the fleet to attach to and control. Must be an existing Fleet in the same namespace
  # as this FleetAutoscaler
  fleetName: fleet-example
  # The autoscaling policy
  policy:
    # type of the policy. for now, only Buffer is available
    type: Buffer
    # parameters of the buffer policy
    buffer:
      # Size of a buffer of "ready" game server instances
      # The FleetAutoscaler will scale the fleet up and down trying to maintain this buffer, 
      # as instances are being allocated or terminated
      # it can be specified either in absolute (i.e. 5) or percentage format (i.e. 5%)
      bufferSize: 5
      # minimum fleet size to be set by this FleetAutoscaler. 
      # if not specified, the actual minimum fleet size will be bufferSize
      minReplicas: 10
      # maximum fleet size that can be set by this FleetAutoscaler
      # required
      maxReplicas: 20
  # The autoscaling sync strategy
  sync:
    # type of the sync. for now, only FixedInterval is available
    type: FixedInterval
    # parameters of the fixedInterval sync
    fixedInterval:
      # the time in seconds between each auto scaling
      seconds: 30

Counter and List Autoscaling

A Counter based autoscaler can be used to autoscale GameServers based on a Count and Capacity set on each of the GameServers in a Fleet to ensure there is always a buffer of total capacity available.

For example, if you have a game server that can support 10 rooms, and you want to ensure that there are always at least 5 rooms available, you could use a counter-based autoscaler with a buffer size of 5. The autoscaler would then scale the Fleet up or down based on the difference between the count of rooms across the Fleet and the capacity of rooms across the Fleet to ensure the buffer is maintained.

Counter-based FleetAutoscaler specification below and in the example folder:

apiVersion: autoscaling.agones.dev/v1
kind: FleetAutoscaler
metadata:
  name: fleet-autoscaler-counter
spec:
  fleetName: fleet-example
  policy:
    type: Counter  # Counter based autoscaling
    counter:
      # Key is the name of the Counter. Required field.
      key: players
      # BufferSize is the size of a buffer of counted items that are available in the Fleet (available capacity).
      # Value can be an absolute number (ex: 5) or a percentage of the Counter available capacity (ex: 5%).
      # An absolute number is calculated from percentage by rounding up. Must be bigger than 0. Required field.
      bufferSize: 5
      # MinCapacity is the minimum aggregate Counter total capacity across the fleet.
      # If BufferSize is specified as a percentage, MinCapacity is required and cannot be 0.
      # If non zero, MinCapacity must be smaller than MaxCapacity and must be greater than or equal to BufferSize.
      minCapacity: 10
      # MaxCapacity is the maximum aggregate Counter total capacity across the fleet.
      # MaxCapacity must be greater than or equal to both MinCapacity and BufferSize. Required field.
      maxCapacity: 100

A List based autoscaler can be used to autoscale GameServers based on the List length and Capacity set on each of the GameServers in a Fleet to ensure there is always a buffer of total capacity available.

For example, if you have a game server that can support 10 players, and you want to ensure that there are always room for at least 5 players across GameServers in a Fleet, you could use a list-based autoscaler with a buffer size of 5. The autoscaler would then scale the Fleet up or down based on the difference between the total length of the players and the total players capacity across the Fleet to ensure the buffer is maintained.

List-based FleetAutoscaler specification below and in the example folder:

apiVersion: autoscaling.agones.dev/v1
kind: FleetAutoscaler
metadata:
  name: fleet-autoscaler-list
spec:
  fleetName: fleet-example
  policy:
    type: List  # List based autoscaling.
    list:
      # Key is the name of the List. Required field.
      key: rooms
      # BufferSize is the size of a buffer based on the List capacity that is available over the current
      # aggregate List length in the Fleet (available capacity).
      # It can be specified either as an absolute value (i.e. 5) or percentage format (i.e. 5%).
      # Must be bigger than 0. Required field.
      bufferSize: 5
      # MinCapacity is the minimum aggregate List total capacity across the fleet.
      # If BufferSize is specified as a percentage, MinCapacity is required must be greater than 0.
      # If non-zero, MinCapacity must be smaller than MaxCapacity and must be greater than or equal to BufferSize.
      minCapacity: 10
      # MaxCapacity is the maximum aggregate List total capacity across the fleet.
      # MaxCapacity must be greater than or equal to both MinCapacity and BufferSize. Required field.
      maxCapacity: 100

Webhook Autoscaling

A webhook-based FleetAutoscaler can be used to delegate the scaling logic to a separate http based service. This can be useful if you want to use a custom scaling algorithm or if you want to integrate with other systems. For example, you could use a webhook-based FleetAutoscaler to scale your fleet based on data from a match-maker or player authentication system or a combination of systems.

Webhook based autoscalers have the added benefit of being able to scale a Fleet to 0 replicas, since they are able to scale up on demand based on an external signal before a GameServerAllocation is executed from a match-maker or similar system.

In order to define the path to your Webhook you can use either URL or service. Note that caBundle parameter is required if you use HTTPS for webhook FleetAutoscaler, caBundle should be omitted if you want to use HTTP webhook server.

For Webhook FleetAutoscaler below and in example folder:

apiVersion: "autoscaling.agones.dev/v1"
kind: FleetAutoscaler
metadata:
  name: webhook-fleet-autoscaler
spec:
  fleetName: simple-game-server
  policy:
    # type of the policy - this example is Webhook
    type: Webhook
    # parameters for the webhook policy - this is a WebhookClientConfig, as per other K8s webhooks
    webhook:
      # use a service, or URL
      service:
        name: autoscaler-webhook-service
        namespace: default
        path: scale
      # optional for URL defined webhooks
      # url: ""
      # caBundle:  optional, used for HTTPS webhook type
  # The autoscaling sync strategy
  sync:
    # type of the sync. for now, only FixedInterval is available
    type: FixedInterval
    # parameters of the fixedInterval sync
    fixedInterval:
      # the time in seconds between each auto scaling
      seconds: 30

See the Webhook Endpoint Specification for the specification of the incoming and outgoing JSON packet structure for the webhook endpoint.

Spec Field Reference

The spec field of the FleetAutoscaler is composed as follows:

  • fleetName is name of the fleet to attach to and control. Must be an existing Fleet in the same namespace as this FleetAutoscaler.
  • policy is the autoscaling policy
    • type is type of the policy. “Buffer” and “Webhook” are available
    • buffer parameters of the buffer policy type
      • bufferSize is the size of a buffer of “ready” and “reserved” game server instances. The FleetAutoscaler will scale the fleet up and down trying to maintain this buffer, as instances are being allocated or terminated. Note that “reserved” game servers could not be scaled down. It can be specified either in absolute (i.e. 5) or percentage format (i.e. 5%)
      • minReplicas is the minimum fleet size to be set by this FleetAutoscaler. if not specified, the minimum fleet size will be bufferSize if absolute value is used. When bufferSize in percentage format is used, minReplicas should be more than 0.
      • maxReplicas is the maximum fleet size that can be set by this FleetAutoscaler. Required.
    • webhook parameters of the webhook policy type
      • service is a reference to the service for this webhook. Either service or url must be specified. If the webhook is running within the cluster, then you should use service. Port 8000 will be used if it is open, otherwise it is an error.
        • name is the service name bound to Deployment of autoscaler webhook. Required (see example) The FleetAutoscaler will scale the fleet up and down based on the response from this webhook server
        • namespace is the kubernetes namespace where webhook is deployed. Optional If not specified, the “default” would be used
        • path is an optional URL path which will be sent in any request to this service. (i. e. /scale)
        • port is optional, it is the port for the service which is hosting the webhook. The default is 8000 for backward compatibility. If given, it should be a valid port number (1-65535, inclusive).
      • url gives the location of the webhook, in standard URL form ([scheme://]host:port/path). Exactly one of url or service must be specified. The host should not refer to a service running in the cluster; use the service field instead. (optional, instead of service)
      • caBundle is a PEM encoded certificate authority bundle which is used to issue and then validate the webhook’s server certificate. Base64 encoded PEM string. Required only for HTTPS. If not present HTTP client would be used.
    • Note: only one buffer or webhook could be defined for FleetAutoscaler which is based on the type field.
    • counter parameters of the counter policy type
      • counter contains the settings for counter-based autoscaling:
        • key is the name of the counter to use for scaling decisions.
        • bufferSize is the size of a buffer of counted items that are available in the Fleet (available capacity). Value can be an absolute number or a percentage of desired game server instances. An absolute number is calculated from percentage by rounding up. Must be bigger than 0.
        • minCapacity is the minimum aggregate Counter total capacity across the fleet. If zero, MinCapacity is ignored. If non zero, MinCapacity must be smaller than MaxCapacity and bigger than BufferSize.
        • maxCapacity is the maximum aggregate Counter total capacity across the fleet. It must be bigger than both MinCapacity and BufferSize.
    • list parameters of the list policy type
      • list contains the settings for list-based autoscaling:
        • key is the name of the list to use for scaling decisions.
        • bufferSize is the size of a buffer based on the List capacity that is available over the current aggregate List length in the Fleet (available capacity). It can be specified either as an absolute value or percentage format.
        • minCapacity is the minimum aggregate List total capacity across the fleet. If zero, it is ignored. If non zero, it must be smaller than MaxCapacity and bigger than BufferSize.
        • maxCapacity is the maximum aggregate List total capacity across the fleet. It must be bigger than both MinCapacity and BufferSize. Required field.
  • sync is autoscaling sync strategy. It defines when to run the autoscaling
    • type is type of the sync. For now only “FixedInterval” is available
    • fixedInterval parameters of the fixedInterval sync
      • seconds is the time in seconds between each autoscaling

Webhook Endpoint Specification

A webhook based FleetAutoscaler sends an HTTP POST request to the webhook endpoint every sync period (default is 30s) with a JSON body, and scale the target fleet based on the data that is returned.

The JSON payload that is sent is a FleetAutoscaleReview data structure and a FleetAutoscaleResponse data structure is expected to be returned.

The FleetAutoscaleResponse’s Replica field is used to set the target Fleet count with each sync interval, thereby providing the autoscaling functionality.

// FleetAutoscaleReview is passed to the webhook with a populated Request value,
// and then returned with a populated Response.
type FleetAutoscaleReview struct {
	Request  *FleetAutoscaleRequest  `json:"request"`
	Response *FleetAutoscaleResponse `json:"response"`
}

type FleetAutoscaleRequest struct {
	// UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are
	// otherwise identical (parallel requests, requests when earlier requests did not modify etc)
	// The UID is meant to track the round trip (request/response) between the Autoscaler and the WebHook, not the user request.
	// It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.
	UID types.UID `json:"uid""`
	// Name is the name of the Fleet being scaled
	Name string `json:"name"`
	// Namespace is the namespace associated with the request (if any).
	Namespace string `json:"namespace"`
	// The Fleet's status values
	Status v1.FleetStatus `json:"status"`
}

type FleetAutoscaleResponse struct {
	// UID is an identifier for the individual request/response.
	// This should be copied over from the corresponding FleetAutoscaleRequest.
	UID types.UID `json:"uid"`
	// Set to false if no scaling should occur to the Fleet
	Scale bool `json:"scale"`
	// The targeted replica count
	Replicas int32 `json:"replicas"`
}

// FleetStatus is the status of a Fleet
type FleetStatus struct {
	// Replicas the total number of current GameServer replicas
	Replicas int32 `json:"replicas"`
	// ReadyReplicas are the number of Ready GameServer replicas
	ReadyReplicas int32 `json:"readyReplicas"`
	// ReservedReplicas are the total number of Reserved GameServer replicas in this fleet.
	// Reserved instances won't be deleted on scale down, but won't cause an autoscaler to scale up.
	ReservedReplicas int32 `json:"reservedReplicas"`
	// AllocatedReplicas are the number of Allocated GameServer replicas
	AllocatedReplicas int32 `json:"allocatedReplicas"`
}

For Webhook Fleetautoscaler Policy either HTTP or HTTPS could be used. Switching between them occurs depending on https presence in URL or by the presence of caBundle. The example of the webhook written in Go could be found here.

It implements the scaling logic based on the percentage of allocated gameservers in a fleet.

8.5 - Code Blind Kubernetes API

Detailed list of Code Blind Custom Resource Definitions available

Packages:

allocation.agones.dev/v1

Package v1 is the v1 version of the API.

Resource Types:

GameServerAllocation

GameServerAllocation is the data structure for allocating against a set of GameServers, defined selectors selectors

FieldDescription
apiVersion
string
allocation.agones.dev/v1
kind
string
GameServerAllocation
metadata
Kubernetes meta/v1.ObjectMeta
Refer to the Kubernetes API documentation for the fields of the metadata field.
spec
GameServerAllocationSpec


multiClusterSetting
MultiClusterSetting

MultiClusterPolicySelector if specified, multi-cluster policies are applied. Otherwise, allocation will happen locally.

required
GameServerSelector

Deprecated: use field Selectors instead. If Selectors is set, this field is ignored. Required is the GameServer selector from which to choose GameServers from. Defaults to all GameServers.

preferred
[]GameServerSelector

Deprecated: use field Selectors instead. If Selectors is set, this field is ignored. Preferred is an ordered list of preferred GameServer selectors that are optional to be fulfilled, but will be searched before the required selector. If the first selector is not matched, the selection attempts the second selector, and so on. If any of the preferred selectors are matched, the required selector is not considered. This is useful for things like smoke testing of new game servers.

priorities
[]Priority
(Optional)

(Alpha, CountsAndLists feature flag) The first Priority on the array of Priorities is the most important for sorting. The allocator will use the first priority for sorting GameServers by available Capacity in the Selector set. Acts as a tie-breaker after sorting the game servers by State and Strategy Packed. Impacts which GameServer is checked first.

selectors
[]GameServerSelector

Ordered list of GameServer label selectors. If the first selector is not matched, the selection attempts the second selector, and so on. This is useful for things like smoke testing of new game servers. Note: This field can only be set if neither Required or Preferred is set.

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”.

metadata
MetaPatch

MetaPatch is optional custom metadata that is added to the game server at allocation You can use this to tell the server necessary session data

counters
map[string]agones.dev/agones/pkg/apis/allocation/v1.CounterAction
(Optional)

(Alpha, CountsAndLists feature flag) Counter actions to perform during allocation.

lists
map[string]agones.dev/agones/pkg/apis/allocation/v1.ListAction
(Optional)

(Alpha, CountsAndLists feature flag) List actions to perform during allocation.

status
GameServerAllocationStatus

CounterAction

(Appears on: GameServerAllocationSpec)

CounterAction is an optional action that can be performed on a Counter at allocation.

FieldDescription
action
string
(Optional)

Action must to either “Increment” or “Decrement” the Counter’s Count. Must also define the Amount.

amount
int64
(Optional)

Amount is the amount to increment or decrement the Count. Must be a positive integer.

capacity
int64
(Optional)

Capacity is the amount to update the maximum capacity of the Counter to this number. Min 0, Max int64.

CounterSelector

(Appears on: GameServerSelector)

CounterSelector is the filter options for a GameServer based on the count and/or available capacity.

FieldDescription
minCount
int64
(Optional)

MinCount is the minimum current value. Defaults to 0.

maxCount
int64
(Optional)

MaxCount is the maximum current value. Defaults to 0, which translates as max(in64).

minAvailable
int64
(Optional)

MinAvailable specifies the minimum capacity (current capacity - current count) available on a GameServer. Defaults to 0.

maxAvailable
int64
(Optional)

MaxAvailable specifies the maximum capacity (current capacity - current count) available on a GameServer. Defaults to 0, which translates to max(int64).

GameServerAllocationSpec

(Appears on: GameServerAllocation)

GameServerAllocationSpec is the spec for a GameServerAllocation

FieldDescription
multiClusterSetting
MultiClusterSetting

MultiClusterPolicySelector if specified, multi-cluster policies are applied. Otherwise, allocation will happen locally.

required
GameServerSelector

Deprecated: use field Selectors instead. If Selectors is set, this field is ignored. Required is the GameServer selector from which to choose GameServers from. Defaults to all GameServers.

preferred
[]GameServerSelector

Deprecated: use field Selectors instead. If Selectors is set, this field is ignored. Preferred is an ordered list of preferred GameServer selectors that are optional to be fulfilled, but will be searched before the required selector. If the first selector is not matched, the selection attempts the second selector, and so on. If any of the preferred selectors are matched, the required selector is not considered. This is useful for things like smoke testing of new game servers.

priorities
[]Priority
(Optional)

(Alpha, CountsAndLists feature flag) The first Priority on the array of Priorities is the most important for sorting. The allocator will use the first priority for sorting GameServers by available Capacity in the Selector set. Acts as a tie-breaker after sorting the game servers by State and Strategy Packed. Impacts which GameServer is checked first.

selectors
[]GameServerSelector

Ordered list of GameServer label selectors. If the first selector is not matched, the selection attempts the second selector, and so on. This is useful for things like smoke testing of new game servers. Note: This field can only be set if neither Required or Preferred is set.

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”.

metadata
MetaPatch

MetaPatch is optional custom metadata that is added to the game server at allocation You can use this to tell the server necessary session data

counters
map[string]agones.dev/agones/pkg/apis/allocation/v1.CounterAction
(Optional)

(Alpha, CountsAndLists feature flag) Counter actions to perform during allocation.

lists
map[string]agones.dev/agones/pkg/apis/allocation/v1.ListAction
(Optional)

(Alpha, CountsAndLists feature flag) List actions to perform during allocation.

GameServerAllocationState (string alias)

(Appears on: GameServerAllocationStatus)

GameServerAllocationState is the Allocation state

GameServerAllocationStatus

(Appears on: GameServerAllocation)

GameServerAllocationStatus is the status for an GameServerAllocation resource

FieldDescription
state
GameServerAllocationState

GameServerState is the current state of an GameServerAllocation, e.g. Allocated, or UnAllocated

gameServerName
string
ports
[]GameServerStatusPort
address
string
addresses
[]Kubernetes core/v1.NodeAddress
nodeName
string
source
string

If the allocation is from a remote cluster, Source is the endpoint of the remote agones-allocator. Otherwise, Source is “local”

metadata
GameServerMetadata

GameServerMetadata

(Appears on: GameServerAllocationStatus)

GameServerMetadata is the metadata from the allocated game server at allocation time

FieldDescription
labels
map[string]string
annotations
map[string]string

GameServerSelector

(Appears on: GameServerAllocationSpec)

GameServerSelector contains all the filter options for selecting a GameServer for allocation.

FieldDescription
LabelSelector
Kubernetes meta/v1.LabelSelector

(Members of LabelSelector are embedded into this type.)

See: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/

gameServerState
GameServerState

GameServerState specifies which State is the filter to be used when attempting to retrieve a GameServer via Allocation. Defaults to “Ready”. The only other option is “Allocated”, which can be used in conjunction with label/annotation/player selectors to retrieve an already Allocated GameServer.

players
PlayerSelector
(Optional)

[Stage:Alpha] [FeatureFlag:PlayerAllocationFilter] Players provides a filter on minimum and maximum values for player capacity when retrieving a GameServer through Allocation. Defaults to no limits.

counters
map[string]agones.dev/agones/pkg/apis/allocation/v1.CounterSelector
(Optional)

(Alpha, CountsAndLists feature flag) Counters provides filters on minimum and maximum values for a Counter’s count and available capacity when retrieving a GameServer through Allocation. Defaults to no limits.

lists
map[string]agones.dev/agones/pkg/apis/allocation/v1.ListSelector
(Optional)

(Alpha, CountsAndLists feature flag) Lists provides filters on minimum and maximum values for List capacity, and for the existence of a value in a List, when retrieving a GameServer through Allocation. Defaults to no limits.

ListAction

(Appears on: GameServerAllocationSpec)

ListAction is an optional action that can be performed on a List at allocation.

FieldDescription
addValues
[]string
(Optional)

AddValues appends values to a List’s Values array. Any duplicate values will be ignored.

capacity
int64
(Optional)

Capacity updates the maximum capacity of the Counter to this number. Min 0, Max 1000.

ListSelector

(Appears on: GameServerSelector)

ListSelector is the filter options for a GameServer based on List available capacity and/or the existence of a value in a List.

FieldDescription
containsValue
string
(Optional)

ContainsValue says to only match GameServers who has this value in the list. Defaults to “”, which is all.

minAvailable
int64
(Optional)

MinAvailable specifies the minimum capacity (current capacity - current count) available on a GameServer. Defaults to 0.

maxAvailable
int64
(Optional)

MaxAvailable specifies the maximum capacity (current capacity - current count) available on a GameServer. Defaults to 0, which is translated as max(int64).

MetaPatch

(Appears on: GameServerAllocationSpec)

MetaPatch is the metadata used to patch the GameServer metadata on allocation

FieldDescription
labels
map[string]string
annotations
map[string]string

MultiClusterSetting

(Appears on: GameServerAllocationSpec)

MultiClusterSetting specifies settings for multi-cluster allocation.

FieldDescription
enabled
bool
policySelector
Kubernetes meta/v1.LabelSelector

PlayerSelector

(Appears on: GameServerSelector)

PlayerSelector is the filter options for a GameServer based on player counts

FieldDescription
minAvailable
int64
maxAvailable
int64

autoscaling.agones.dev/v1

Package v1 is the v1 version of the API.

Resource Types:

FleetAutoscaler

FleetAutoscaler is the data structure for a FleetAutoscaler resource

FieldDescription
apiVersion
string
autoscaling.agones.dev/v1
kind
string
FleetAutoscaler
metadata
Kubernetes meta/v1.ObjectMeta
Refer to the Kubernetes API documentation for the fields of the metadata field.
spec
FleetAutoscalerSpec


fleetName
string
policy
FleetAutoscalerPolicy

Autoscaling policy

sync
FleetAutoscalerSync
(Optional)

Sync defines when FleetAutoscalers runs autoscaling

status
FleetAutoscalerStatus

BufferPolicy

(Appears on: FleetAutoscalerPolicy)

BufferPolicy controls the desired behavior of the buffer policy.

FieldDescription
maxReplicas
int32

MaxReplicas is the maximum amount of replicas that the fleet may have. It must be bigger than both MinReplicas and BufferSize

minReplicas
int32

MinReplicas is the minimum amount of replicas that the fleet must have If zero, it is ignored. If non zero, it must be smaller than MaxReplicas and bigger than BufferSize

bufferSize
k8s.io/apimachinery/pkg/util/intstr.IntOrString

BufferSize defines how many replicas the autoscaler tries to have ready all the time Value can be an absolute number (ex: 5) or a percentage of desired gs instances (ex: 15%) Absolute number is calculated from percentage by rounding up. Example: when this is set to 20%, the autoscaler will make sure that 20% of the fleet’s game server replicas are ready. When this is set to 20, the autoscaler will make sure that there are 20 available game servers Must be bigger than 0 Note: by “ready” we understand in this case “non-allocated”; this is done to ensure robustness and computation stability in different edge case (fleet just created, not enough capacity in the cluster etc)

CounterPolicy

(Appears on: FleetAutoscalerPolicy)

CounterPolicy controls the desired behavior of the Counter autoscaler policy.

FieldDescription
key
string

Key is the name of the Counter. Required field.

maxCapacity
int64

MaxCapacity is the maximum aggregate Counter total capacity across the fleet. MaxCapacity must be bigger than both MinCapacity and BufferSize. Required field.

minCapacity
int64

MinCapacity is the minimum aggregate Counter total capacity across the fleet. If zero, MinCapacity is ignored. If non zero, MinCapacity must be smaller than MaxCapacity and bigger than BufferSize.

bufferSize
k8s.io/apimachinery/pkg/util/intstr.IntOrString

BufferSize is the size of a buffer of counted items that are available in the Fleet (available capacity). Value can be an absolute number (ex: 5) or a percentage of desired gs instances (ex: 5%). An absolute number is calculated from percentage by rounding up. Must be bigger than 0. Required field.

FixedIntervalSync

(Appears on: FleetAutoscalerSync)

FixedIntervalSync controls the desired behavior of the fixed interval based sync.

FieldDescription
seconds
int32

Seconds defines how often we run fleet autoscaling in seconds

FleetAutoscaleRequest

(Appears on: FleetAutoscaleReview)

FleetAutoscaleRequest defines the request to webhook autoscaler endpoint

FieldDescription
uid
k8s.io/apimachinery/pkg/types.UID

UID is an identifier for the individual request/response. It allows us to distinguish instances of requests which are otherwise identical (parallel requests, requests when earlier requests did not modify etc) The UID is meant to track the round trip (request/response) between the Autoscaler and the WebHook, not the user request. It is suitable for correlating log entries between the webhook and apiserver, for either auditing or debugging.

name
string

Name is the name of the Fleet being scaled

namespace
string

Namespace is the namespace associated with the request (if any).

status
FleetStatus

The Fleet’s status values

FleetAutoscaleResponse

(Appears on: FleetAutoscaleReview)

FleetAutoscaleResponse defines the response of webhook autoscaler endpoint

FieldDescription
uid
k8s.io/apimachinery/pkg/types.UID

UID is an identifier for the individual request/response. This should be copied over from the corresponding FleetAutoscaleRequest.

scale
bool

Set to false if no scaling should occur to the Fleet

replicas
int32

The targeted replica count

FleetAutoscaleReview

FleetAutoscaleReview is passed to the webhook with a populated Request value, and then returned with a populated Response.

FieldDescription
request
FleetAutoscaleRequest
response
FleetAutoscaleResponse

FleetAutoscalerPolicy

(Appears on: FleetAutoscalerSpec)

FleetAutoscalerPolicy describes how to scale a fleet

FieldDescription
type
FleetAutoscalerPolicyType

Type of autoscaling policy.

buffer
BufferPolicy
(Optional)

Buffer policy config params. Present only if FleetAutoscalerPolicyType = Buffer.

webhook
WebhookPolicy
(Optional)

Webhook policy config params. Present only if FleetAutoscalerPolicyType = Webhook.

counter
CounterPolicy
(Optional)

[Stage:Alpha] [FeatureFlag:CountsAndLists] Counter policy config params. Present only if FleetAutoscalerPolicyType = Counter.

list
ListPolicy
(Optional)

[Stage:Alpha] [FeatureFlag:CountsAndLists] List policy config params. Present only if FleetAutoscalerPolicyType = List.

FleetAutoscalerPolicyType (string alias)

(Appears on: FleetAutoscalerPolicy)

FleetAutoscalerPolicyType is the policy for autoscaling for a given Fleet

FleetAutoscalerSpec

(Appears on: FleetAutoscaler)

FleetAutoscalerSpec is the spec for a Fleet Scaler

FieldDescription
fleetName
string
policy
FleetAutoscalerPolicy

Autoscaling policy

sync
FleetAutoscalerSync
(Optional)

Sync defines when FleetAutoscalers runs autoscaling

FleetAutoscalerStatus

(Appears on: FleetAutoscaler)

FleetAutoscalerStatus defines the current status of a FleetAutoscaler

FieldDescription
currentReplicas
int32

CurrentReplicas is the current number of gameserver replicas of the fleet managed by this autoscaler, as last seen by the autoscaler

desiredReplicas
int32

DesiredReplicas is the desired number of gameserver replicas of the fleet managed by this autoscaler, as last calculated by the autoscaler

lastScaleTime
Kubernetes meta/v1.Time
(Optional)

lastScaleTime is the last time the FleetAutoscaler scaled the attached fleet,

ableToScale
bool

AbleToScale indicates that we can access the target fleet

scalingLimited
bool

ScalingLimited indicates that the calculated scale would be above or below the range defined by MinReplicas and MaxReplicas, and has thus been capped.

FleetAutoscalerSync

(Appears on: FleetAutoscalerSpec)

FleetAutoscalerSync describes when to sync a fleet

FieldDescription
type
FleetAutoscalerSyncType

Type of autoscaling sync.

fixedInterval
FixedIntervalSync
(Optional)

FixedInterval config params. Present only if FleetAutoscalerSyncType = FixedInterval.

FleetAutoscalerSyncType (string alias)

(Appears on: FleetAutoscalerSync)

FleetAutoscalerSyncType is the sync strategy for a given Fleet

ListPolicy

(Appears on: FleetAutoscalerPolicy)

ListPolicy controls the desired behavior of the List autoscaler policy.

FieldDescription
key
string

Key is the name of the List. Required field.

maxCapacity
int64

MaxCapacity is the maximum aggregate List total capacity across the fleet. MaxCapacity must be bigger than both MinCapacity and BufferSize. Required field.

minCapacity
int64

MinCapacity is the minimum aggregate List total capacity across the fleet. If zero, it is ignored. If non zero, it must be smaller than MaxCapacity and bigger than BufferSize.

bufferSize
k8s.io/apimachinery/pkg/util/intstr.IntOrString

BufferSize is the size of a buffer based on the List capacity that is available over the current aggregate List length in the Fleet (available capacity). It can be specified either as an absolute value (i.e. 5) or percentage format (i.e. 5%). Must be bigger than 0. Required field.

WebhookPolicy

(Appears on: FleetAutoscalerPolicy)

WebhookPolicy controls the desired behavior of the webhook policy. It contains the description of the webhook autoscaler service used to form url which is accessible inside the cluster

FieldDescription
url
string
(Optional)

url gives the location of the webhook, in standard URL form (scheme://host:port/path). Exactly one of url or service must be specified.

The host should not refer to a service running in the cluster; use the service field instead. The host might be resolved via external DNS in some apiservers (e.g., kube-apiserver cannot resolve in-cluster DNS as that would be a layering violation). host may also be an IP address.

Please note that using localhost or 127.0.0.1 as a host is risky unless you take great care to run this webhook on all hosts which run an apiserver which might need to make calls to this webhook. Such installs are likely to be non-portable, i.e., not easy to turn up in a new cluster.

The scheme must be “https”; the URL must begin with “https://”.

A path is optional, and if present may be any string permissible in a URL. You may use the path to pass an arbitrary string to the webhook, for example, a cluster identifier.

Attempting to use a user or basic auth e.g. “user:password@” is not allowed. Fragments (“#…”) and query parameters (“?…”) are not allowed, either.

service
Kubernetes admissionregistration/v1.ServiceReference
(Optional)

service is a reference to the service for this webhook. Either service or url must be specified.

If the webhook is running within the cluster, then you should use service.

caBundle
[]byte
(Optional)

caBundle is a PEM encoded CA bundle which will be used to validate the webhook’s server certificate. If unspecified, system trust roots on the apiserver are used.


multicluster.agones.dev/v1

Package v1 is the v1 version of the API.

Resource Types:

GameServerAllocationPolicy

GameServerAllocationPolicy is the Schema for the gameserverallocationpolicies API

FieldDescription
apiVersion
string
multicluster.agones.dev/v1
kind
string
GameServerAllocationPolicy
metadata
Kubernetes meta/v1.ObjectMeta
Refer to the Kubernetes API documentation for the fields of the metadata field.
spec
GameServerAllocationPolicySpec


priority
int32
weight
int
connectionInfo
ClusterConnectionInfo

ClusterConnectionInfo

(Appears on: GameServerAllocationPolicySpec)

ClusterConnectionInfo defines the connection information for a cluster

FieldDescription
clusterName
string

Optional: the name of the targeted cluster

allocationEndpoints
[]string

The endpoints for the allocator service in the targeted cluster. If the AllocationEndpoints is not set, the allocation happens on local cluster. If there are multiple endpoints any of the endpoints that can handle allocation request should suffice

secretName
string

The name of the secret that contains TLS client certificates to connect the allocator server in the targeted cluster

namespace
string

The cluster namespace from which to allocate gameservers

serverCa
[]byte

The PEM encoded server CA, used by the allocator client to authenticate the remote server.

ConnectionInfoIterator

ConnectionInfoIterator an iterator on ClusterConnectionInfo

FieldDescription
currPriority
int

currPriority Current priority index from the orderedPriorities

orderedPriorities
[]int32

orderedPriorities list of ordered priorities

priorityToCluster
map[int32]map[string][]*agones.dev/agones/pkg/apis/multicluster/v1.GameServerAllocationPolicy

priorityToCluster Map of priority to cluster-policies map

clusterBlackList
map[string]bool

clusterBlackList the cluster blacklist for the clusters that has already returned

GameServerAllocationPolicySpec

(Appears on: GameServerAllocationPolicy)

GameServerAllocationPolicySpec defines the desired state of GameServerAllocationPolicy

FieldDescription
priority
int32
weight
int
connectionInfo
ClusterConnectionInfo

agones.dev/v1

Package v1 is the v1 version of the API.

Resource Types:

Fleet

Fleet is the data structure for a Fleet resource

FieldDescription
apiVersion
string
agones.dev/v1
kind
string
Fleet
metadata
Kubernetes meta/v1.ObjectMeta
Refer to the Kubernetes API documentation for the fields of the metadata field.
spec
FleetSpec


replicas
int32

Replicas are the number of GameServers that should be in this set. Defaults to 0.

allocationOverflow
AllocationOverflow
(Optional)

[Stage: Beta] [FeatureFlag:FleetAllocationOverflow] Labels and/or Annotations to apply to overflowing GameServers when the number of Allocated GameServers is more than the desired replicas on the underlying GameServerSet

strategy
Kubernetes apps/v1.DeploymentStrategy

Deployment strategy

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”.

priorities
[]Priority
(Optional)

(Alpha, CountsAndLists feature flag) The first Priority on the array of Priorities is the most important for sorting. The Fleetautoscaler will use the first priority for sorting GameServers by total Capacity in the Fleet and acts as a tie-breaker after sorting the game servers by State and Strategy. Impacts scale down logic.

template
GameServerTemplateSpec

Template the GameServer template to apply for this Fleet

status
FleetStatus

GameServer

GameServer is the data structure for a GameServer resource. It is worth noting that while there is a GameServerStatus Status entry for the GameServer, it is not defined as a subresource - unlike Fleet and other Code Blind resources. This is so that we can retain the ability to change multiple aspects of a GameServer in a single atomic operation, which is particularly useful for operations such as allocation.

FieldDescription
apiVersion
string
agones.dev/v1
kind
string
GameServer
metadata
Kubernetes meta/v1.ObjectMeta
Refer to the Kubernetes API documentation for the fields of the metadata field.
spec
GameServerSpec


container
string

Container specifies which Pod container is the game server. Only required if there is more than one container defined

ports
[]GameServerPort

Ports are the array of ports that can be exposed via the game server

health
Health

Health configures health checking

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”

sdkServer
SdkServer

SdkServer specifies parameters for the Code Blind SDK Server sidecar container

template
Kubernetes core/v1.PodTemplateSpec

Template describes the Pod that will be created for the GameServer

players
PlayersSpec
(Optional)

(Alpha, PlayerTracking feature flag) Players provides the configuration for player tracking features.

counters
map[string]agones.dev/agones/pkg/apis/agones/v1.CounterStatus
(Optional)

(Alpha, CountsAndLists feature flag) Counters provides the configuration for tracking of int64 values against a GameServer. Keys must be declared at GameServer creation time.

lists
map[string]agones.dev/agones/pkg/apis/agones/v1.ListStatus
(Optional)

(Alpha, CountsAndLists feature flag) Lists provides the configuration for tracking of lists of up to 1000 values against a GameServer. Keys must be declared at GameServer creation time.

eviction
Eviction
(Optional)

Eviction specifies the eviction tolerance of the GameServer. Defaults to “Never”.

status
GameServerStatus

GameServerSet

GameServerSet is the data structure for a set of GameServers. This matches philosophically with the relationship between Deployments and ReplicaSets

FieldDescription
apiVersion
string
agones.dev/v1
kind
string
GameServerSet
metadata
Kubernetes meta/v1.ObjectMeta
Refer to the Kubernetes API documentation for the fields of the metadata field.
spec
GameServerSetSpec


replicas
int32

Replicas are the number of GameServers that should be in this set

allocationOverflow
AllocationOverflow
(Optional)

[Stage: Beta] [FeatureFlag:FleetAllocationOverflow] Labels and Annotations to apply to GameServers when the number of Allocated GameServers drops below the desired replicas on the underlying GameServerSet

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”.

priorities
[]Priority
(Optional)

(Alpha, CountsAndLists feature flag) The first Priority on the array of Priorities is the most important for sorting. The Fleetautoscaler will use the first priority for sorting GameServers by total Capacity in the Fleet and acts as a tie-breaker after sorting the game servers by State and Strategy. Impacts scale down logic.

template
GameServerTemplateSpec

Template the GameServer template to apply for this GameServerSet

status
GameServerSetStatus

AggregatedCounterStatus

(Appears on: FleetStatus, GameServerSetStatus)

AggregatedCounterStatus stores total and allocated Counter tracking values

FieldDescription
allocatedCount
int64
allocatedCapacity
int64
count
int64
capacity
int64

AggregatedListStatus

(Appears on: FleetStatus, GameServerSetStatus)

AggregatedListStatus stores total and allocated List tracking values

FieldDescription
allocatedCount
int64
allocatedCapacity
int64
count
int64
capacity
int64

AggregatedPlayerStatus

(Appears on: FleetStatus, GameServerSetStatus)

AggregatedPlayerStatus stores total player tracking values

FieldDescription
count
int64
capacity
int64

AllocationOverflow

(Appears on: FleetSpec, GameServerSetSpec)

AllocationOverflow specifies what labels and/or annotations to apply on Allocated GameServers if the desired number of the underlying GameServerSet drops below the number of Allocated GameServers attached to it.

FieldDescription
labels
map[string]string
(Optional)

Labels to be applied to the GameServer

annotations
map[string]string
(Optional)

Annotations to be applied to the GameServer

CounterStatus

(Appears on: GameServerSpec, GameServerStatus)

CounterStatus stores the current counter values and maximum capacity

FieldDescription
count
int64
capacity
int64

Eviction

(Appears on: GameServerSpec, GameServerStatus)

Eviction specifies the eviction tolerance of the GameServer

FieldDescription
safe
EvictionSafe

Game server supports termination via SIGTERM: - Always: Allow eviction for both Cluster Autoscaler and node drain for upgrades - OnUpgrade: Allow eviction for upgrades alone - Never (default): Pod should run to completion

EvictionSafe (string alias)

(Appears on: Eviction)

EvictionSafe specified whether the game server supports termination via SIGTERM

FleetSpec

(Appears on: Fleet)

FleetSpec is the spec for a Fleet

FieldDescription
replicas
int32

Replicas are the number of GameServers that should be in this set. Defaults to 0.

allocationOverflow
AllocationOverflow
(Optional)

[Stage: Beta] [FeatureFlag:FleetAllocationOverflow] Labels and/or Annotations to apply to overflowing GameServers when the number of Allocated GameServers is more than the desired replicas on the underlying GameServerSet

strategy
Kubernetes apps/v1.DeploymentStrategy

Deployment strategy

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”.

priorities
[]Priority
(Optional)

(Alpha, CountsAndLists feature flag) The first Priority on the array of Priorities is the most important for sorting. The Fleetautoscaler will use the first priority for sorting GameServers by total Capacity in the Fleet and acts as a tie-breaker after sorting the game servers by State and Strategy. Impacts scale down logic.

template
GameServerTemplateSpec

Template the GameServer template to apply for this Fleet

FleetStatus

(Appears on: Fleet, FleetAutoscaleRequest)

FleetStatus is the status of a Fleet

FieldDescription
replicas
int32

Replicas the total number of current GameServer replicas

readyReplicas
int32

ReadyReplicas are the number of Ready GameServer replicas

reservedReplicas
int32

ReservedReplicas are the total number of Reserved GameServer replicas in this fleet. Reserved instances won’t be deleted on scale down, but won’t cause an autoscaler to scale up.

allocatedReplicas
int32

AllocatedReplicas are the number of Allocated GameServer replicas

players
AggregatedPlayerStatus
(Optional)

[Stage:Alpha] [FeatureFlag:PlayerTracking] Players are the current total player capacity and count for this Fleet

counters
map[string]agones.dev/agones/pkg/apis/agones/v1.AggregatedCounterStatus
(Optional)

(Alpha, CountsAndLists feature flag) Counters provides aggregated Counter capacity and Counter count for this Fleet.

lists
map[string]agones.dev/agones/pkg/apis/agones/v1.AggregatedListStatus
(Optional)

(Alpha, CountsAndLists feature flag) Lists provides aggregated List capacityv and List values for this Fleet.

GameServerPort

(Appears on: GameServerSpec)

GameServerPort defines a set of Ports that are to be exposed via the GameServer

FieldDescription
name
string

Name is the descriptive name of the port

portPolicy
PortPolicy

PortPolicy defines the policy for how the HostPort is populated. Dynamic port will allocate a HostPort within the selected MIN_PORT and MAX_PORT range passed to the controller at installation time. When Static portPolicy is specified, HostPort is required, to specify the port that game clients will connect to

container
string
(Optional)

Container is the name of the container on which to open the port. Defaults to the game server container.

containerPort
int32

ContainerPort is the port that is being opened on the specified container’s process

hostPort
int32

HostPort the port exposed on the host for clients to connect to

protocol
Kubernetes core/v1.Protocol

Protocol is the network protocol being used. Defaults to UDP. TCP and TCPUDP are other options.

GameServerSetSpec

(Appears on: GameServerSet)

GameServerSetSpec the specification for GameServerSet

FieldDescription
replicas
int32

Replicas are the number of GameServers that should be in this set

allocationOverflow
AllocationOverflow
(Optional)

[Stage: Beta] [FeatureFlag:FleetAllocationOverflow] Labels and Annotations to apply to GameServers when the number of Allocated GameServers drops below the desired replicas on the underlying GameServerSet

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”.

priorities
[]Priority
(Optional)

(Alpha, CountsAndLists feature flag) The first Priority on the array of Priorities is the most important for sorting. The Fleetautoscaler will use the first priority for sorting GameServers by total Capacity in the Fleet and acts as a tie-breaker after sorting the game servers by State and Strategy. Impacts scale down logic.

template
GameServerTemplateSpec

Template the GameServer template to apply for this GameServerSet

GameServerSetStatus

(Appears on: GameServerSet)

GameServerSetStatus is the status of a GameServerSet

FieldDescription
replicas
int32

Replicas is the total number of current GameServer replicas

readyReplicas
int32

ReadyReplicas is the number of Ready GameServer replicas

reservedReplicas
int32

ReservedReplicas is the number of Reserved GameServer replicas

allocatedReplicas
int32

AllocatedReplicas is the number of Allocated GameServer replicas

shutdownReplicas
int32

ShutdownReplicas is the number of Shutdown GameServers replicas

players
AggregatedPlayerStatus
(Optional)

[Stage:Alpha] [FeatureFlag:PlayerTracking] Players is the current total player capacity and count for this GameServerSet

counters
map[string]agones.dev/agones/pkg/apis/agones/v1.AggregatedCounterStatus
(Optional)

(Alpha, CountsAndLists feature flag) Counters provides aggregated Counter capacity and Counter count for this GameServerSet.

lists
map[string]agones.dev/agones/pkg/apis/agones/v1.AggregatedListStatus
(Optional)

(Alpha, CountsAndLists feature flag) Lists provides aggregated List capacity and List values for this GameServerSet.

GameServerSpec

(Appears on: GameServer, GameServerTemplateSpec)

GameServerSpec is the spec for a GameServer resource

FieldDescription
container
string

Container specifies which Pod container is the game server. Only required if there is more than one container defined

ports
[]GameServerPort

Ports are the array of ports that can be exposed via the game server

health
Health

Health configures health checking

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”

sdkServer
SdkServer

SdkServer specifies parameters for the Code Blind SDK Server sidecar container

template
Kubernetes core/v1.PodTemplateSpec

Template describes the Pod that will be created for the GameServer

players
PlayersSpec
(Optional)

(Alpha, PlayerTracking feature flag) Players provides the configuration for player tracking features.

counters
map[string]agones.dev/agones/pkg/apis/agones/v1.CounterStatus
(Optional)

(Alpha, CountsAndLists feature flag) Counters provides the configuration for tracking of int64 values against a GameServer. Keys must be declared at GameServer creation time.

lists
map[string]agones.dev/agones/pkg/apis/agones/v1.ListStatus
(Optional)

(Alpha, CountsAndLists feature flag) Lists provides the configuration for tracking of lists of up to 1000 values against a GameServer. Keys must be declared at GameServer creation time.

eviction
Eviction
(Optional)

Eviction specifies the eviction tolerance of the GameServer. Defaults to “Never”.

GameServerState (string alias)

(Appears on: GameServerSelector, GameServerStatus)

GameServerState is the state for the GameServer

GameServerStatus

(Appears on: GameServer)

GameServerStatus is the status for a GameServer resource

FieldDescription
state
GameServerState

GameServerState is the current state of a GameServer, e.g. Creating, Starting, Ready, etc

ports
[]GameServerStatusPort
address
string
addresses
[]Kubernetes core/v1.NodeAddress
(Optional)

Addresses is the array of addresses at which the GameServer can be reached; copy of Node.Status.addresses.

nodeName
string
reservedUntil
Kubernetes meta/v1.Time
players
PlayerStatus
(Optional)

[Stage:Alpha] [FeatureFlag:PlayerTracking]

counters
map[string]agones.dev/agones/pkg/apis/agones/v1.CounterStatus
(Optional)

(Alpha, CountsAndLists feature flag) Counters and Lists provides the configuration for generic tracking features.

lists
map[string]agones.dev/agones/pkg/apis/agones/v1.ListStatus
(Optional)
eviction
Eviction
(Optional)

Eviction specifies the eviction tolerance of the GameServer.

GameServerStatusPort

(Appears on: GameServerAllocationStatus, GameServerStatus)

GameServerStatusPort shows the port that was allocated to a GameServer.

FieldDescription
name
string
port
int32

GameServerTemplateSpec

(Appears on: FleetSpec, GameServerSetSpec)

GameServerTemplateSpec is a template for GameServers

FieldDescription
metadata
Kubernetes meta/v1.ObjectMeta
Refer to the Kubernetes API documentation for the fields of the metadata field.
spec
GameServerSpec


container
string

Container specifies which Pod container is the game server. Only required if there is more than one container defined

ports
[]GameServerPort

Ports are the array of ports that can be exposed via the game server

health
Health

Health configures health checking

scheduling
agones.dev/agones/pkg/apis.SchedulingStrategy

Scheduling strategy. Defaults to “Packed”

sdkServer
SdkServer

SdkServer specifies parameters for the Code Blind SDK Server sidecar container

template
Kubernetes core/v1.PodTemplateSpec

Template describes the Pod that will be created for the GameServer

players
PlayersSpec
(Optional)

(Alpha, PlayerTracking feature flag) Players provides the configuration for player tracking features.

counters
map[string]agones.dev/agones/pkg/apis/agones/v1.CounterStatus
(Optional)

(Alpha, CountsAndLists feature flag) Counters provides the configuration for tracking of int64 values against a GameServer. Keys must be declared at GameServer creation time.

lists
map[string]agones.dev/agones/pkg/apis/agones/v1.ListStatus
(Optional)

(Alpha, CountsAndLists feature flag) Lists provides the configuration for tracking of lists of up to 1000 values against a GameServer. Keys must be declared at GameServer creation time.

eviction
Eviction
(Optional)

Eviction specifies the eviction tolerance of the GameServer. Defaults to “Never”.

Health

(Appears on: GameServerSpec)

Health configures health checking on the GameServer

FieldDescription
disabled
bool

Disabled is whether health checking is disabled or not

periodSeconds
int32

PeriodSeconds is the number of seconds each health ping has to occur in

failureThreshold
int32

FailureThreshold how many failures in a row constitutes unhealthy

initialDelaySeconds
int32

InitialDelaySeconds initial delay before checking health

ListStatus

(Appears on: GameServerSpec, GameServerStatus)

ListStatus stores the current list values and maximum capacity

FieldDescription
capacity
int64
values
[]string

PlayerStatus

(Appears on: GameServerStatus)

PlayerStatus stores the current player capacity values

FieldDescription
count
int64
capacity
int64
ids
[]string

PlayersSpec

(Appears on: GameServerSpec)

PlayersSpec tracks the initial player capacity

FieldDescription
initialCapacity
int64

PortPolicy (string alias)

(Appears on: GameServerPort)

PortPolicy is the port policy for the GameServer

Priority

(Appears on: FleetSpec, GameServerAllocationSpec, GameServerSetSpec)

Priority is a sorting option for GameServers with Counters or Lists based on the Capacity.

FieldDescription
type
string

Type: Sort by a “Counter” or a “List”.

key
string

Key: The name of the Counter or List. If not found on the GameServer, has no impact.

order
string

Order: Sort by “Ascending” or “Descending”. “Descending” a bigger Capacity is preferred. “Ascending” would be smaller Capacity is preferred.

SdkServer

(Appears on: GameServerSpec)

SdkServer specifies parameters for the Code Blind SDK Server sidecar container

FieldDescription
logLevel
SdkServerLogLevel

LogLevel for SDK server (sidecar) logs. Defaults to “Info”

grpcPort
int32

GRPCPort is the port on which the SDK Server binds the gRPC server to accept incoming connections

httpPort
int32

HTTPPort is the port on which the SDK Server binds the HTTP gRPC gateway server to accept incoming connections

SdkServerLogLevel (string alias)

(Appears on: SdkServer)

SdkServerLogLevel is the log level for SDK server (sidecar) logs


Generated with gen-crd-api-reference-docs.

9 - Examples

List of available code examples

Configuration files

These are full examples for each of the resource types of Code Blind

Game server implementations

These are all examples of simple game server implementations, that integrate the Code Blind game server SDK.

  • Simple gameserver (Go) - simple server that responds to TCP connections or UDP packets on the same port.
  • CPP Simple (C++) - C++ example that starts up, stays healthy and then shuts down after 60 seconds.
  • Node.js Simple (Node.js) - A simple Node.js example that marks itself as ready, sets some labels and then shutsdown.
  • Rust Simple (Rust) - A simple Rust example that marks itself as ready, sets some labels and then shutsdown.
  • Unity Simple (Unity3d) - This is a very simple “unity server” that doesn’t do much other than show how the SDK works in Unity.
  • Xonotic - Wraps the SDK around the open source FPS game Xonotic and hosts it on Code Blind.
  • SuperTuxKart - Wraps the SDK around the open source racing game SuperTuxKart, and hosts it on Code Blind.

Building on top of Code Blind

Game Server Allocation

Integrations with other projects

10 - Advanced

Advanced Guides, Techniques and walk-throughs

10.1 - Scheduling and Autoscaling

Scheduling and autoscaling go hand in hand, as where in the cluster GameServers are provisioned impacts how to autoscale fleets up and down (or if you would even want to)

Cluster Autoscaler

Kubernetes has a cluster node autoscaler that works with a wide variety of cloud providers.

The default scheduling strategy (Packed) is designed to work with the Kubernetes autoscaler out of the box.

The autoscaler will automatically add Nodes to the cluster when GameServers don’t have room to be scheduled on the clusters, and then scale down when there are empty Nodes with no GameServers running on them.

This means that scaling Fleets up and down can be used to control the size of the cluster, as the cluster autoscaler will adjust the size of the cluster to match the resource needs of one or more Fleets running on it.

To enable and configure autoscaling on your cloud provider, check their connector implementation, or their cloud specific documentation.

Google Kubernetes Engine

Amazon Elastic Kubernetes Service

Azure Kubernetes Service

Fleet Autoscaling

Fleet autoscaling is the only type of autoscaling that exists in Code Blind. It is currently available as a buffer autoscaling strategy or as a webhook driven strategy, such that you can provide your own autoscaling logic.

Have a look at the Create a Fleet Autoscaler quickstart, the Create a Webhook Fleet Autoscaler quickstart, and the Fleet Autoscaler Specification for details.

Autoscaling Concepts

To facilitate autoscaling, we need to combine several concepts and functionality, as described below.

Allocation Scheduling

Allocation scheduling refers to the order in which GameServers, and specifically their backing Pods are chosen from across the Kubernetes cluster within a given Fleet when allocation occurs.

Pod Scheduling

Each GameServer is backed by a Kubernetes Pod. Pod scheduling refers to the strategy that is in place that determines which node in the Kubernetes cluster the Pod is assigned to, when it is created.

Fleet Scale Down Strategy

Fleet Scale Down strategy refers to the order in which the GameServers that belong to a Fleet are deleted, when Fleets are shrunk in size.

Fleet Scheduling

There are two scheduling strategies for Fleets - each designed for different types of Kubernetes Environments.

Packed

apiVersion: "agones.dev/v1"
kind: Fleet
metadata:
  name: simple-game-server
spec:
  replicas: 100
  scheduling: Packed
  template:
    spec:
      ports:
      - containerPort: 7654
      template:
        spec:
          containers:
          - name: simple-game-server
            image: us-docker.pkg.dev/codeblind/examples/simple-server:0.27

This is the default Fleet scheduling strategy. It is designed for dynamic Kubernetes environments, wherein you wish to scale up and down as load increases or decreases, such as in a Cloud environment where you are paying for the infrastructure you use.

It attempts to pack as much as possible into the smallest set of nodes, to make scaling infrastructure down as easy as possible.

This affects the Cluster autoscaler, Allocation Scheduling, Pod Scheduling and Fleet Scale Down Scheduling.

Cluster Autoscaler

When using the “Packed” strategy, Code Blind will ensure that the Cluster Autoscaler doesn’t attempt to evict and move GameServer Pods onto new Nodes during gameplay.

If a gameserver can tolerate being evicted (generally in combination with setting an appropriate graceful termination period on the gameserver pod) and you want the Cluster Autoscaler to compact your cluster by evicting game servers when it would allow the Cluster Autoscaler to reduce the number of nodes in the cluster, Controlling Disruption describes how to choose the .eviction setting appropriate for your GameServer or Fleet.

Allocation Scheduling Strategy

Under the “Packed” strategy, allocation will prioritise allocating GameServers to nodes that are running on Nodes that already have allocated GameServers running on them.

Pod Scheduling Strategy

Under the “Packed” strategy, Pods will be scheduled using the PodAffinity with a preferredDuringSchedulingIgnoredDuringExecution affinity with hostname topology. This attempts to group together GameServer Pods within as few nodes in the cluster as it can.

Fleet Scale Down Strategy

With the “Packed” strategy, Fleets will remove Ready GameServers from Nodes with the least number of Ready and Allocated GameServers on them. Attempting to empty Nodes so that they can be safely removed.

Distributed

apiVersion: "agones.dev/v1"
kind: Fleet
metadata:
  name: simple-game-server
spec:
  replicas: 100
  scheduling: Distributed
  template:
    spec:
      ports:
      - containerPort: 7654
      template:
        spec:
          containers:
          - name: simple-game-server
            image: us-docker.pkg.dev/codeblind/examples/simple-server:0.27

This Fleet scheduling strategy is designed for static Kubernetes environments, such as when you are running Kubernetes on bare metal, and the cluster size rarely changes, if at all.

This attempts to distribute the load across the entire cluster as much as possible, to take advantage of the static size of the cluster.

This affects Allocation Scheduling, Pod Scheduling and Fleet Scale Down Scheduling.

Cluster Autoscaler

Since this strategy is not aimed at clusters that autoscale, this strategy does nothing for the cluster autoscaler.

Allocation Scheduling Strategy

Under the “Distributed” strategy, allocation will prioritise allocating GameServers to nodes that have the least number of allocated GameServers on them.

Pod Scheduling Strategy

Under the “Distributed” strategy, Pod scheduling is provided by the default Kubernetes scheduler, which will attempt to distribute the GameServer Pods across as many nodes as possible.

Fleet Scale Down Strategy

With the “Distributed” strategy, Fleets will remove Ready GameServers from Nodes with at random, to ensure a distributed load is maintained.

10.2 - High Availability Code Blind

Learn how to configure your Code Blind services for high availability and resiliancy to disruptions.

High Availability for Code Blind Controller

The agones-controller responsibility is split up into agones-controller, which enacts the Code Blind control loop, and agones-extensions, which acts as a service endpoint for webhooks and the allocation extension API. Splitting these responsibilities allows the agones-extensions pod to be horizontally scaled, making the Code Blind control plane highly available and more resiliant to disruption.

Multiple agones-controller pods enabled, with a primary controller selected via leader election. Having multiple agones-controller minimizes downtime of the service from pod disruptions such as deployment updates, autoscaler evictions, and crashes.

Extension Pod Configrations

The agones-extensions binary has a similar helm configuration to agones-controller, see here. If you previously overrode agones.controller.* settings, you may need to override the same agones.extensions.* setting.

To change controller.numWorkers to 200 from 100 values and through the use of helm --set, add the follow to the helm command:

 ...
 --set agones.controller.numWorkers=200
 ...

An important configuration to note is the PodDisruptionBudget fields, agones.extensions.pdb.minAvailable and agones.extensions.pdb.maxUnavailable. Currently, the agones.extensions.pdb.minAvailable field is set to 1.

Deployment Considerations

Leader election will automatically be enabled and agones.controller.replicas is > 1. agones.controller.replicas defaults to 2.

The default configuration now deploys 2 agones-controller pods and 2 agones-extensions pods, replacing the previous single agones-controller pod setup. For example:

NAME                                 READY   STATUS    RESTARTS   AGE
agones-allocator-78c6b8c79-h9nqc     1/1     Running   0          23h
agones-allocator-78c6b8c79-l2bzp     1/1     Running   0          23h
agones-allocator-78c6b8c79-rw75j     1/1     Running   0          23h
agones-controller-fbf944f4-vs9xx     1/1     Running   0          23h
agones-controller-fbf944f4-sjk3t     1/1     Running   0          23h
agones-extensions-5648fc7dcf-hm6lk   1/1     Running   0          23h
agones-extensions-5648fc7dcf-qbc6h   1/1     Running   0          23h
agones-ping-5b9647874-2rrl6          1/1     Running   0          27h
agones-ping-5b9647874-rksgg          1/1     Running   0          27h

The number of replicas for agones-extensions can be set using helm variable agones.extensions.replicas, but the default is 2.

We expect the aggregate memory consumption of the pods will be slightly higher than the previous singleton pod, but as the responsibilities are now split across the pods, the aggregate CPU consumption should also be similar.

Feature Design

Please see HA Code Blind.

10.3 - Controlling Disruption

Game servers running on Code Blind may be disrupted by Kubernetes; learn how to control disruption of your game servers.

Disruption in Kubernetes

A Pod in Kubernetes may be disrupted for involuntary reasons, e.g. hardware failure, or voluntary reasons, such as when nodes are drained for upgrades.

By default, Code Blind assumes your game server should never be disrupted voluntarily and configures the Pod appropriately - but this isn’t always the ideal setting. Here we discuss how Code Blind allows you to control the two most significant sources of voluntary Pod evictions, node upgrades and Cluster Autoscaler, using the eviction API on the GameServer object.

Benefits of Allowing Voluntary Disruption

It’s not always easy to write your game server in a way that allows for disruption, but it can have major benefits:

  • Compaction of your cluster using Cluster Autoscaler can lead to considerable cost savings for your infrastructure.
  • Allowing automated node upgrades can save you management toil, and lowers the time it takes to patch security vulnerabilites.

Considerations

When discussing game server pod disruption, it’s important to keep two factors in mind:

  • TERM signal: Is your game server tolerant of graceful termination? If you wish to support voluntary disruption, your game server must handle the TERM signal (even if it runs to completion after receiving TERM).
  • Termination Grace Period: After receiving TERM, how long does your game server need to run? If you run to completion after receiving TERM, this is equivalent to the session length - if not, you can think of this as the cleanup time. In general, we bucket the grace period into “less than 10 minutes”, “10 minutes to an hour”, and “greater than an hour”. (See below if you are curious about grace period considerations.)

eviction API

The eviction API is specified as part of the GameServerSpec, like:

apiVersion: "agones.dev/v1"
kind: GameServer
metadata:
  name: "simple-game-server"
spec:
  eviction:
    safe: Always
  template:
    [...]

You can set eviction.safe based on your game server’s tolerance for disruption and session length, based on the following diagram:

Eviction Decision Diagram

In words:

  • Does the game server support TERM and terminate within ten minutes?
    • Yes to both: Set safe: Always, and set terminationGracePeriodSeconds to the session length or cleanup time.
    • No to either: Does the game server support TERM and terminate within an hour?
      • Yes to both: Set safe: OnUpgrade, and configure terminationGracePeriodSeconds to the session length or cleanup time.
      • No to either: Set safe: Never. If your game server does not terminate within an hour, see below.

What’s special about ten minutes and one hour?

  • Ten minutes: Cluster Autoscaler respects ten minutes of graceful termination on scale-down. On some cloud products, you can configure --max-graceful-termination-sec to change this, but it is not advised: Cluster Autoscaler is currently only capable of scaling down one node at a time, and larger graceful termination windows slow this down farther (see autoscaler#5079). If the ten minute limit does not apply to you, generally you should choose between safe: Always (for sessions less than an hour), or see below.

  • One hour: On many cloud products, PodDisruptionBudget can only block node upgrade evictions for a certain period of time - on GKE this is 1h. After that, the PDB is ignored, or the node upgrade fails with an error. Controlling Pod disruption for longer than one hour requires cluster configuration changes outside of Code Blind - see below.

Considerations for long sessions

Outside of Cluster Autoscaler, the main source of disruption for long sessions is node upgrade. On some cloud products, such as GKE Standard, node upgrades are entirely within your control. On others, such as GKE Autopilot, node upgrade is automatic. Typical node upgrades use an eviction based, rolling recreate strategy, and may not honor PodDisruptionBudget for longer than an hour. See Best Practices for information specific to your cloud product.

Implementation / Under the hood

Each option uses a slightly different permutation of:

As a quick reference:

evictions.safe settingsafe-to-evict pod annotationagones.dev/safe-to-evict label
Never (default)falsefalse (matches PDB)
OnUpdatefalsetrue (does not match PDB)
Alwaystruetrue (does not match PDB)

Further Reading

10.4 - Limiting CPU & Memory

Kubernetes natively has inbuilt capabilities for requesting and limiting both CPU and Memory usage of running containers.

As a short description:

  • CPU Requests are limits that are applied when there is CPU congestion, and as such can burst above their set limits.
  • CPU Limits are hard limits on how much CPU time the particular container gets access to.

This is useful for game servers, not just as a mechanism to distribute compute resources evenly, but also as a way to advice the Kubernetes scheduler how many game server processes it is able to fit into a given node in the cluster.

It’s worth reading the Managing Compute Resources for Containers Kubernetes documentation for more details on “requests” and “limits” to both CPU and Memory, and how to configure them.

GameServers

Since the GameServer specification provides a full PodSpecTemplate, we can take advantage of both resource limits and requests in our GameServer configurations.

For example, to set a CPU limit on our GameServer configuration of 250m/0.25 of a CPU, we could do so as followed:

apiVersion: "agones.dev/v1"
kind: GameServer
metadata:
  name: "simple-game-server"
spec:
  ports:
  - name: default
    containerPort: 7654
  template:
    spec:
      containers:
      - name: simple-game-server
        image: us-docker.pkg.dev/codeblind/examples/simple-server:0.27
        resources:
          limits:
            cpu: "250m" #this is our limit here

If you do not set a limit or request, the default is set by Kubernetes at a 100m CPU request.

SDK GameServer sidecar

You may also want to tweak the CPU request or limits on the SDK GameServer sidecar process that spins up alongside each game server container.

You can do this through the Helm configuration when installing Code Blind.

By default, this is set to having a CPU request value of 30m, with no hard CPU limit. This ensures that the sidecar always has enough CPU to function, but it is configurable in case a lower, or higher value is required on your clusters, or if you desire hard limit.

10.5 - Out of Cluster Dev Server

Running and debugging server binary locally while connected to a full kubernetes stack

This section builds upon the topics discussed in local SDK Server, Local Game Server, and GameServer allocation (discussed here, here, and here). Having a firm understanding of those concepts will be necessary for running an “out of cluster” local server.

Running an “out of cluster” dev server combines the best parts of local debugging and being a part of a cluster. A developer will be able to run a custom server binary on their local machine, even within an IDE with breakpoints. The server would also be allocatable within a cluster, allowing integration with the project’s full stack for handling game server lifetime.

For each run, the only manual steps required by the developer is to manually run the local SDK Server and to run their custom gameplay binary (each can easily be reused/restarted). All other state progression will be automatically handled by the custom gameplay server (calling the SDK API), the SDK Server (handling the SDK calls), the cluster GameServer Controller (progressing specific states), and the cluster’s allocation system (whether be through GameServerAllocation or via the Allocator Service) – just as it would when running in a pod in a cluster!

Out of cluster development is a fantastic option during early prototyping, as it can (optionally) all be run on a single machine with tools such as Minikube.

The name “out of cluster” is to contrast InClusterConfig which is used in the internal golang kubeconfig API.

Prerequisite steps

To be able to run an “out of cluster” local game server, one needs to first complete a few prerequisite steps.

Cluster created

First, a cluster must have been created that the developer has access to through commands like kubectl. This cluster could be running on a provider or locally (e.g. on Minikube). See Create Kubernetes Cluster for more details on how to create a cluster, if not already done so.

Code Blind GameServer resource created

Out of cluster dev servers make use of local dev servers. Follow the instructions there to create a GameServer resource for use with a local game server. Note that the metadata:annotations:agones.dev/dev-address should be updated to point to the local machine, more details below around port forwarding.

SDK Server available

An “out of cluster” dev server requires the need to also run the SDK Server locally.

When a GameServer runs normally in a prod-like environment, the Code Blind cluster controller will handle initializing the containers which contain the SDK Server and the game server binary. The game server binary will be able to connect over gRPC to the SDK Server running in the sidecar container. When the game server binary makes SDK calls (e.g. SDK.Ready()), those get sent to the SDK Server via gRPC and the SDK Server as able to modify the GameServer resource in the cluster. When the GameServer resource gets modified (either by the Code Blind cluster controller, by the Code Blind Allocation Service, or by the K8s API), the SDK Server is monitoring and sends update events over gRPC to the SDK API, resulting in a callback in the game server binary logic.

The goal of an “out of cluster” dev server is to keep all this prod-like functionality, even in a debuggable context. To do so, the developer must run the SDK Server locally such that the (also local) game server binary can connect via gRPC. Instructions for downloading and running the SDK Server can be found here. However, instead of using --local or --file, the SDK Server will need to be run in “out of cluster” mode by providing a kubeconfig file to connect to the cluster. This section is focusing on getting the SDK Server ready to run locally, more detail about running it can be found below.

Game server binary available

When running Code Blind normally, the game server binary is inside a prebuilt docker image which is loaded into a container in a GameServer’s pod. This can either be a custom, developer-created, docker image and contained binary or a sample image/binary from an external source. This document will use the sample simple-game-server, which follows suit from various other documentation pages (e.g. Quickstart: Create a Game Server).

The simple-game-server can be run from the docker image us-docker.pkg.dev/codeblind/examples/simple-server:0.27. The game server binary can either be run within a docker container or run locally, so long as all ports are published/forward – more on this below.

Alternatively, the simple-game-server can also be run from source code; see examples/simple-game-server/main.go. More details about running from source can be found here.

Disclaimer: Code Blind is run and tested with the version of Go specified by the GO_VERSION variable in the project’s build Dockerfile. Other versions are not supported, but may still work.

If a developer has their own game server logic, written in the language of their choice, that would be perfectly fine. A custom game server can be similarly run within a docker container, run directly on commandline, or run via an IDE/debugger.

Forwarded Ports

As the game server binary will be run on the developer’s machine and a requesting client will attempt to connect to the game server via the GameServer’s metadata:annotations:agones.dev/dev-address and spec:ports:hostPort fields, the developer needs to ensure that connection can take place.

If the game server binary and the arbitrary connecting client logic are both on the same network, then connecting should work without any extra steps. However, if the developer has a more complicated network configuration or if they are attempting to connect over the public internet, extra steps may be required.

Obviously, this document does not know what every developer’s specific network configuration is, how their custom game client(s) work, their development environment, and/or various other factors. The developer will need to figure out which steps are necessary for their specific configuration.

If attempting to connect via the internet, the developer needs to set the GameServer’s metadata:annotations:agones.dev/dev-address field to their public IP. This can be found by going to whatsmyip.org or whatismyip.com in a web browser.

The GameServer’s spec:ports:hostPort/spec:ports:containerPort should be set to whichever port the game server binary’s logic will bind to – the port used by simple-game-server is 7654 (by default). The local network’s router must also be configured to forward this port to the desired machine; allowing inbound external requests (from the internet) to be directed to the machine on the network that is running the game server.

If the SDK Server is run on the same machine as the game server binary, no extra steps are necessary for the two to connect. By default, the SDK API (in the game server binary) will attempt to gRPC connect to the SDK Server on localhost on the port 9357. If the SDK Server is run on another machine, or if the SDK Server is set to use different ports (e.g. via commandline arguments), the developer will need to also take appropriate steps to ensure that the game server can connect to the SDK Server. As discussed further below running the SDK Server with --address 0.0.0.0 can be quite helpful with various setups.

If the developer is running the SDK Server or the game server binary within docker container(s), then publishing ports and/or connecting to a docker network may be necessary. Again, these configurations can vary quite dramatically and the developer will need to find the necessary steps for their specific setup.

Running “out of cluster” local game server

Now that all prerequisite steps have been completed, the developer should have:

Optional GameServer state monitoring

A helpful (optional) step to see progress when running is to watch the GameServer resource.

This can be done with the command:

kubectl get --watch -n default gs my-local-server

It may be necessary to replace default and my-local-server with whichever namespace/name values are used by the dev GameServer created above).

With this command running, the terminal will automatically show updates to the GameServer’s state – however, this is not necessary to proceed.

Running SDK Server locally

The first step is to run the SDK Server, making it available for the (later run) game server binary to connect. Here is a sample command to run the SDK Server, with each argument discussed after.

./sdk-server.linux.amd64 \
  --gameserver-name my-local-server \
  --pod-namespace default \
  --kubeconfig "$HOME/.kube/config" \
  --address 0.0.0.0 \
  --graceful-termination false
  • --gameserver-name is a necessary arg, passed instead of the GAMESERVER_NAME enviroment variable.
    • It is set to the name of the dev GameServer k8s resource.
    • It tells the SDK Sever which resource to read/write to on the k8s cluster.
    • This example value of my-local-server matches to the instructions for setting up a Local Game Server.
  • --pod-namespace is a necessary arg, passed instead of the POD_NAMESPACE enviroment variable.
    • It is set set to the namespace which the dev GameServer resides in.
    • It tells the SDK Sever which namespace to look under for the GameServer to read/write to on the k8s cluster.
    • This example value of default is used as most instructions in this documentation assumes GameServers to be created in the default namespace.
  • --kubeconfig tells the SDK Server how to connect to the k8s cluster.
    • This actually does not trigger any special flow (unlike --local or --file). The SDK Server will run just as it would when created in a sidecar container in a k8s cluster.
    • Passing this argument simply provides where to connect along with the credentials to do so.
    • This example value of "$HOME/.kube/config" is the default location for k8s authentication information. This requires the developer be logged in via kubectl and have the desired cluster selected via kubectl config use-context.
  • --address specifies the binding IP address for the SDK Server’s SDK API.
    • By default, the binding address is localhost. This may be difficult for some development setups.
    • Overriding this value changes which IP address(es) the server will bind to for receiving gRPC/REST SDK API calls.
    • This example value of 0.0.0.0 sets the SDK Server to receive API calls that are sent to any IP address (that reach the machine).
  • --graceful-termination set to false will disable some smooth state transitions when exiting.
    • By default, the SDK Server will wait until the GameServer has reached the Shutdown state before exiting (“graceful termination”).
    • This will cause the SDK Server to hang (waiting on state update) when attempting to terminate (e.g. with ^C).
    • When running binaries in a development context, quickly exiting and restarting the SDK Server is handy.

This can easily be terminated with ^C and restarted as necessary. Note that terminating the SDK Server while the game server binary (discussed in the next section) is using it may result in failure to update/watch GameServer state and may result in a runtime error in the game server binary.

Running game server binary locally

Now that the SDK Server is running locally with k8s credentials, the game server binary can run in an integrated fashion. The game server binary’s SDK calls will reach the local SDK Server, which will then interact with the GameServer resource on the k8s cluster.

Again, this document will make use of simple-game-server via its docker image, but running directly or use of a custom game server binary is just as applicable. Run the game server binary with the command:

docker run --rm --network="host" us-docker.pkg.dev/codeblind/examples/simple-server:0.27

The --rm flag will nicely autoclean up the docker container after exiting. The --network="host" flag will tell the docker container to use the host’s network stack directly; this allows calls to localhost to reach the SDK Server. The commands and flags used will likely differ if running a custom game server binary.

If the earlier kubectl get --watch command was run, it will now show the GameServer progressed to the RequestReady state, which will automatically be progressed to the Ready state by the Code Blind controller on the cluster.

The GameServer state can further be modified by SDK calls, gRPC/REST calls, allocation via either GameServerAllocation or Allocator Service, K8s API calls, etc. These changes will be shown by the kubectl get --watch command. These changes will also be picked up by the game server binary, if there is a listener registered through the SDK API. This means that this GameServer can be allocated just as it would be when running completely on k8s, but it can be locally debugged.

If the server crashes or is killed by the developer, it can easily be restarted. This can be done without restarting the SDK Server or any other manual intevention with the GameServer resource. Naturally, this may have implications on any connected clients, but that is project specific and left to the developer to handle.

10.6 - Allocator Service

Code Blind provides an mTLS based allocator service that is accessible from outside the cluster using a load balancer. The service is deployed and scales independent to Code Blind controller.

To allocate a game server, Code Blind provides a gRPC and REST service with mTLS authentication, called agones-allocator that can be used instead of GameServerAllocations.

Both gRPC and REST are accessible through a Kubernetes service that can be externalized using a load balancer. By default, gRPC and REST are served from the same port. However, either service can be disabled or the services can be served from separate ports using the helm configuration.

For requests to either service to succeed, a client certificate must be provided that is in the authorization list of the allocator service. The remainder of this article describes how to manually make a successful allocation request using the API.

The guide assumes you have command line tools installed for jq, go and openssl.

GameServerAllocation vs Allocator Service

There are several reasons you may prefer to use the Allocator Service over the GameServerAllocation custom resource definition, depending on your architecture and requirements:

  • A requirement to do multi-cluster allocation.
  • Want to create Allocations from outside the Code Blind Kubernetes cluster.
  • Prefer SSL based authentication over Kubernetes RBAC.
  • Prefer a gRPC or REST based API over an integration with the Kubernetes API.

Find the external IP

The service is hosted under the same namespace as the Code Blind controller. To find the external IP of your allocator service, replace agones-system namespace with the namespace to which Code Blind is deployed and execute the following command:

kubectl get service agones-allocator -n agones-system

The output of the command should look like:

NAME                        TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)            AGE
agones-allocator            LoadBalancer   10.55.251.73    34.82.195.204   443:30250/TCP      7d22h

Server TLS certificate

If the agones-allocator service is installed as a LoadBalancer using a reserved IP, a valid self-signed server TLS certificate is generated using the IP provided. Otherwise, the server TLS certificate should be replaced. If you installed Code Blind using helm, you can easily reconfigure the allocator service with a preset IP address by setting the agones.allocator.service.loadBalancerIP parameter to the address that was automatically assigned to the service and helm upgrade:

EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
helm upgrade my-release agones/agones -n agones-system --wait \
   --set agones.allocator.service.loadBalancerIP=${EXTERNAL_IP} \
   ...

Another approach is to replace the default server TLS certificate with a certificate with CN and subjectAltName. There are multiple approaches to generate a certificate. Code Blind recommends using cert-manager.io solution for cluster level certificate management.

In order to use the cert-manager solution, first install cert-manager on the cluster. Then, configure an Issuer/ClusterIssuer resource and last configure a Certificate resource to manage allocator-tls Secret. Make sure to configure the Certificate based on your system’s requirements, including the validity duration.

Here is an example of using a self-signed ClusterIssuer for configuring allocator-tls Secret:

#!/bin/bash
# Create a self-signed ClusterIssuer
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: selfsigned
spec:
  selfSigned: {}
EOF

EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

# for EKS use hostname
# HOST_NAME=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')

# Create a Certificate with IP for the allocator-tls secret
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: allocator-tls
  namespace: agones-system
spec:
  commonName: ${EXTERNAL_IP}
  ipAddresses:
    - ${EXTERNAL_IP}
  secretName: allocator-tls
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
EOF

# Wait for the allocator-tls Secret
sleep 1
TLS_CA_VALUE=$(kubectl get secret allocator-tls -n agones-system -ojsonpath='{.data.ca\.crt}')

# Add ca.crt to the allocator-tls-ca Secret
kubectl get secret allocator-tls-ca -o json -n agones-system | jq '.data["tls-ca.crt"]="'${TLS_CA_VALUE}'"' | kubectl apply -f -
echo $TLS_CA_VALUE | base64 -d > ca.crt
# In case of MacOS
# echo $TLS_CA_VALUE | base64 -D > ca.crt

Bring Your Own Certificates (advanced)

If you would like to completely manage the tls secrets outside of helm, you can create them in the namespace where agones is going to be installed, and then set the helm value agones.allocator.disableSecretCreation to true. This method will also work with the cert-manager method, as long as your certificate and secret are created ahead of time, and you populate the allocator-tls-ca and allocator-client-ca yourself.

Client Certificate

Because agones-allocator uses an mTLS authentication mechanism, a client must provide a certificate that is accepted by the server.

If Code Blind is installed using Helm, you can leverage a default client secret, allocator-client.default, created in the game server namespace and allowlisted in allocator-client-ca Kubernetes secret. You can extract and use that secret for client side authentication, by following the allocation example.

Otherwise, here is an example of generating a client certificate using openssl.

#!/bin/bash

EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout client.key -out client.crt -addext 'subjectAltName=IP:'${EXTERNAL_IP}''

CERT_FILE_VALUE=$(cat client.crt | base64 -w 0)

# In case of MacOS
# CERT_FILE_VALUE=$(cat client.crt | base64)

# allowlist client certificate
kubectl get secret allocator-client-ca -o json -n agones-system | jq '.data["client_trial.crt"]="'${CERT_FILE_VALUE}'"' | kubectl apply -f -

The last command creates a new entry in the secret data map for allocator-client-ca for the client CA. This is for the agones-allocator service to accept the newly generated client certificate.

Send allocation request

After setting up agones-allocator with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. Make sure you have a fleet with ready game servers in the game server namespace.

Set the environment variables and store the client secrets before allocating using gRPC or REST APIs:

NAMESPACE=default # replace with any namespace
EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
KEY_FILE=client.key
CERT_FILE=client.crt
TLS_CA_FILE=ca.crt

# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following.
# In case of MacOS replace "base64 -d" with "base64 -D"
kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}"
kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}"
kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}"

Using gRPC

To start, take a look at the allocation gRPC client examples in golang and C# languages. In the following, the golang gRPC client example is used to allocate a Game Server in the default namespace.

#!/bin/bash

go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \
    --port 443 \
    --namespace ${NAMESPACE} \
    --key ${KEY_FILE} \
    --cert ${CERT_FILE} \
    --cacert ${TLS_CA_FILE}

Using REST

#!/bin/bash

curl --key ${KEY_FILE} \
     --cert ${CERT_FILE} \
     --cacert ${TLS_CA_FILE} \
     -H "Content-Type: application/json" \
     --data '{"namespace":"'${NAMESPACE}'"}' \
     https://${EXTERNAL_IP}/gameserverallocation \
     -X POST

You should expect to see the following output:

{"gameServerName":"game-server-name","ports":[{"name":"default","port":7463}],"address":"1.2.3.4","nodeName":"node-name"}

Sending Data to the Game Server

The service accepts a metadata field, which can be used to apply labels and annotations to the allocated GameServer. The old metaPatch fields is now deprecated, but can still be used for compatibility. If both metadata and metaPatch fields are set, metaPatch is ignored.

Secrets Explained

agones-allocator has a dependency on three Kubernetes secrets:

  1. allocator-tls - stores the server certificate.
  2. allocator-client-ca - stores the allocation authorized client CA for mTLS to allowlist client certificates.
  3. allocator-tls-ca (optional) - stores allocator-tls CA.

The separation of CA secret from the private secret is for the security reason to avoid reading the private secret, while retrieving the allocator CA that is used by the allocation client to validate the server. It is optional to set or maintain the allocator-tls-ca secret.

Troubleshooting

If you encounter problems, explore the following potential root causes:

  1. Check server certificate - Using openssl you can get the certificate chain for the server.

    EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
    openssl s_client -connect ${EXTERNAL_IP}:443
    
    • Inspect the server certificate by storing the certificate returned, under Server certificate and validating using openssl x509 -in tls.crt -text -noout.
    • Make sure the certificate is not expired and the Subject Alternative Name is set.
    • If the issuer is CN = allocation-ca, the certificate is generated using Code Blind helm installation.
  2. Check client certificate

    • You may get an error such as rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection closed, make sure your client certificate is allowlisted by being added to allocator-client-ca.
    kubectl get secret allocator-client-ca -o json -n agones-system
    
    • If the server certificate is not accepted by the client, you may get an error such as rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority", depending on the client. In this case, verify that the TLS CA file matches the server certificate.
    kubectl get secret allocator-tls -n agones-system -ojsonpath="{.data.tls\.crt}" | base64 -d > tls.crt
    openssl verify -verbose -CAfile ca.crt tls.crt
    tls.crt: OK
    
  3. Make sure the service is up and running.

    kubectl get pod -n agones-system | grep agones-allocator
    agones-allocator-59b4f6b5c6-86j62      1/1     Running     0          6m36s
    agones-allocator-59b4f6b5c6-kbqrq      1/1     Running     0          6m45s
    agones-allocator-59b4f6b5c6-trbkl      1/1     Running     0          6m28s
    
    kubectl get service agones-allocator -n agones-system
    agones-allocator   LoadBalancer   10.55.248.14   34.82.195.204    443:32468/TCP   6d23h
    

API Reference

The AllocationService API is located as a gRPC service here. Additionally, the REST API is available as a Swagger API.

10.7 - Multi-cluster Allocation

In order to allow allocation from multiple clusters, Code Blind provides a mechanism to set redirect rules for allocation requests to the right cluster.

There may be different types of clusters, such as on-premise, and Google Kubernetes Engine (GKE), used by a game to help with the cost-saving and availability. For this purpose, Code Blind provides a mechanism to define priorities on the clusters. Priorities are defined on GameServerAllocationPolicy agones CRD. A matchmaker can enable the multi-cluster rules on a request and target agones-allocator endpoint in any of the clusters and get resources allocated on the cluster with the highest priority. If the cluster with the highest priority is overloaded, the allocation request is redirected to the cluster with the next highest priority.

The remainder of this article describes how to enable multi-cluster allocation.

Define Cluster Priority

GameServerAllocationPolicy is the CRD defined by Code Blind for setting multi-cluster allocation rules. In addition to cluster priority, it describes the connection information for the target cluster, including the game server namespace, agones-allocator endpoint and client K8s secrets name for redirecting the allocation request. Game servers will be allocated from clusters with the lowest priority number. If there are no available game servers available in clusters with the lowest priority number, they will be allocated from clusters with the next lowest priority number. For clusters with the same priority, the cluster is chosen with a probability relative to its weight.

Here is an example of setting the priority for a cluster and it’s connection rules. One such resource should be defined per cluster.

In the following example the policy is defined for cluster B in cluster A.

cat <<EOF | kubectl apply -f -
apiVersion: multicluster.agones.dev/v1
kind: GameServerAllocationPolicy
metadata:
  name: allocator-cluster-b
  namespace: cluster-a-ns
spec:
  connectionInfo:
    allocationEndpoints:
    - 34.82.195.204
    clusterName: "clusterB"
    namespace: cluster-b-ns
    secretName: allocator-client-to-cluster-b
    serverCa: c2VydmVyQ0E=
  priority: 1
  weight: 100
EOF

To define the local cluster priority a GameServerAllocationPolicy should be defined without an allocationEndpoints field. If the local cluster priority is not defined, the allocation from the local cluster happens only if allocation from other clusters with the existing allocation rules is unsuccessful.

Allocation requests with multi-cluster allocation enabled but with only the local cluster available (e.g. in development) must have a local cluster priority defined, or the request fails with the error “no multi-cluster allocation policy is specified”.

The namespace field in connectionInfo is the namespace that the game servers will be allocated in, and must be a namespace in the target cluster that has been previously defined as allowed to host game servers. The Namespace specified in the allocation request (below) is used to refer to the namespace that the GameServerAllocationPolicy itself is located in.

serverCa is the server TLS CA public certificate, set only if the remote server certificate is not signed by a public CA (e.g. self-signed). If this field is not specified, the certificate can also be specified in a field named ca.crt of the client secret (the secret referred to in the secretName field).

Establish trust

To accept allocation requests from other clusters, agones-allocator for cluster B should be configured to accept the client’s certificate from cluster A and the cluster A’s client should be configured to accept the server TLS certificate, if it is not signed by a public Certificate Authority (CA).

Follow the steps to configure the agones allocator gRPC service. The client certificate pair in the mentioned document is stored as a K8s secret. Here are the secrets to set:

1.Client certificate to talk to other clusters:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: allocator-client-to-cluster-b
  namespace: cluster-a-ns
type: Opaque
data:
  tls.crt: <REDACTED>
  tls.key: <REDACTED>
  ca.crt: <REDACTED>
EOF

The certificates are base 64 string of the certificate file e.g. cat ${CERT_FILE} | base64 -w 0

Code Blind recommends using cert-manager.io solution for generating client certificates.

2.Add client CA to the list of authorized client certificates by agones-allocator in the targeted cluster.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: allocator-client-ca
  namespace: agones-system
type: Opaque
data:
  client1.crt: <REDACTED>
  client2.crt: <REDACTED>
  clientN.crt: <REDACTED>
EOF

Allocate multi-cluster

To enable multi-cluster allocation, set multiClusterSetting.enabled to true in allocation.proto and send allocation requests. For more information visit agones-allocator. In the following, using allocator-client sample, a multi-cluster allocation request is sent to the agones-allocator service. If the allocation succeeds, the AllocationResponse will contain a Source field which indicates the endpoint of the remote agones-allocator.

Set the environment variables and store the client secrets before allocating using gRPC or REST APIs

#!/bin/bash

NAMESPACE=default # replace with any namespace
EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
KEY_FILE=client.key
CERT_FILE=client.crt
TLS_CA_FILE=ca.crt

# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following.
# In case of MacOS replace "base64 -d" with "base64 -D"
kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}"
kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}"
kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}"
#!/bin/bash

go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \
    --namespace ${NAMESPACE} \
    --key ${KEY_FILE} \
    --cert ${CERT_FILE} \
    --cacert ${TLS_CA_FILE} \
    --multicluster true

If using REST use

#!/bin/bash

curl --key ${KEY_FILE} \
     --cert ${CERT_FILE} \
     --cacert ${TLS_CA_FILE} \
     -H "Content-Type: application/json" \
     --data '{"namespace":"'${NAMESPACE}'", "multiClusterSetting":{"enabled":true}}' \
     https://${EXTERNAL_IP}/gameserverallocation \
     -X POST
     

Troubleshooting

If you encounter problems, explore the following potential root causes:

  1. Make sure single cluster allocation works for each cluster using this troubleshooting.

  2. For each cluster, make sure there is a GameServerAllocationPolicy resource defined in the game server cluster.

  3. Inspect the .spec.connectionInfo for GameServerAllocationPolicy for each cluster. Use the cluster connection information in that field to verify that single cluster allocation works. Use the information to verify the connection:

POLICY_NAME=<policy-name>
POLICY_NAMESPACE=<policy-namespace>

NAMESPACE=$(kubectl get gameserverallocationpolicy ${POLICY_NAME} -n ${POLICY_NAMESPACE} -ojsonpath={.spec.connectionInfo.namespace})
EXTERNAL_IP=$(kubectl get gameserverallocationpolicy ${POLICY_NAME} -n ${POLICY_NAMESPACE} -ojsonpath={.spec.connectionInfo.allocationEndpoints\[0\]})
CLIENT_SECRET_NAME=$(kubectl get gameserverallocationpolicy ${POLICY_NAME} -n ${POLICY_NAMESPACE} -ojsonpath={.spec.connectionInfo.secretName})

KEY_FILE=client.key
CERT_FILE=client.crt
TLS_CA_FILE=ca.crt

# In case of MacOS replace "base64 -d" with "base64 -D"
kubectl get secret "${CLIENT_SECRET_NAME}" -n "${POLICY_NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}"
kubectl get secret "${CLIENT_SECRET_NAME}" -n "${POLICY_NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}"
kubectl get secret "${CLIENT_SECRET_NAME}" -n "${POLICY_NAMESPACE}" -ojsonpath="{.data.ca\.crt}" | base64 -d > "${TLS_CA_FILE}"
#!/bin/bash

go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \
    --port 443 \
    --namespace ${NAMESPACE} \
    --key ${KEY_FILE} \
    --cert ${CERT_FILE} \
    --cacert ${TLS_CA_FILE}

10.8 - GameServer Pod Service Accounts

RBAC permissions and service accounts for the GameServer Pod.

Default Settings

By default, Code Blind sets up service accounts and sets them appropriately for the Pods that are created for GameServers.

Since Code Blind provides GameServer Pods with a sidecar container that needs access to Code Blind Custom Resource Definitions, Pods are configured with a service account with extra RBAC permissions to ensure that it can read and modify the resources it needs.

Since service accounts apply to all containers in a Pod, Code Blind will automatically overwrite the mounted key for the service account in the container that is running the dedicated game server in the backing Pod. This is done since game server containers are exposed publicly, and generally don’t require the extra permissions to access aspects of the Kubernetes API.

Bringing your own Service Account

If needed, you can provide your own service account on the Pod specification in the GameServer configuration.

For example:

apiVersion: "agones.dev/v1"
kind: GameServer
metadata:
  generateName: "simple-game-server-"
spec:
  ports:
  - name: default
    containerPort: 7654
  template:
    spec:
      serviceAccountName: my-special-service-account # a custom service account
      containers:
      - name: simple-game-server
        image: us-docker.pkg.dev/codeblind/examples/simple-server:0.27

If a service account is configured, the mounted key is not overwritten, as it assumed that you want to have full control of the service account and underlying RBAC permissions.

11 - Frequently Asked Questions

Architecture

What is the relationship between a Kubernetes Pod and an Code Blind GameServer?

Code Blind creates a backing Pod with the appropriate configuration parameters for each GameServer that is configured in the cluster. They both have the same name if you are ever looking to match one to the other.

Can I reuse a GameServer for multiple game sessions?

Yes.

Code Blind is inherently un-opinionated about the lifecycle of your game. When you call SDK.Allocate() you are protecting that GameServer instance from being scaled down for the duration of the Allocation. Typically, you would run one game session within a single allocation. However, you could allocate, and run N sessions on a single GameServer, and then de-allocate/shutdown at a later time.

How can I return an Allocated GameServer to the Ready state?

If you wish to return an Allocated GameServer to the Ready state, you can use the SDK.Ready() command whenever it makes sense for your GameServer to return to the pool of potentially Allocatable and/or scaled down GameServers.

Have a look at the integration pattern “Reusing Allocated GameServers for more than one game session” for more details.

Integration

What steps do I need to take to integrate my GameServer?

  1. Integrate your game server binary with the Code Blind SDK, calling the appropriate lifecycle event hooks.
  2. Containerize your game server binary with Docker
  3. Publish your Docker image in a container registry/repository.
  4. Create a gameserver.yaml file for your container image.
  5. Test your gameserver.yaml file.
  6. Consider utilizing Fleets. and Autoscalers for deploying at scale.

What are some common patterns for integrating the SDK with a Game Server Binary?

  • In-Engine
    • Integrate the SDK directly with the dedicated game server, such that it is part of the same codebase.
  • Sidecar
    • Use a Kubernetes sidecar pattern to run the SDK in a separate process that runs alongside your game server binary, and can share the disk and network namespace. This game server binary could expose its own API, or write to a shared file, that the sidecar process integrates with, and can then communicate back to Code Blind through the SDK.
  • Wrapper
    • Write a process that wraps the game server binary, and intercepts aspects such as the foreground log output, and use that information to react and communicate with Code Blind appropriately. This can be particularly useful for legacy game servers or game server binaries wherein you do not have access to the original source. You can see this in both the Xonotic and SuperTuxKart examples.

What if my engine / language of choice does not have a supported SDK, what can I do?

Either utilise the REST API, which can be generated from the Swagger specification, or generate your own gRPC client from the proto file.

Game Server SDKs are a thin wrapper around either REST or gRPC clients, depending on language or platform, and can be used as examples.

How can I pass data to my Game Server binary on Allocation?

A GameServerAllocation has a spec.metadata section, that will apply any configured Labels and/or Annotations to a requested GameServer at Allocation time.

The game server binary can watch for the state change to Allocated, as well as changes to the GameServer metadata, through SDK.WatchGameServer().

Combining these two features allows you to pass information such as map data, gameplay metadata and more to a game server binary at Allocation time, through Code Blind functionality.

Do note, that if you wish to have either the labels or annotations on the GameServer that are set via a
GameServerAllocation to be editable by the game server binary with the Code Blind SDK, the label key will need to be prefixed with agones.dev/sdk-. See SDK.SetLabel() and SDK.SetAnnotation() for more information.

How can I expose information from my game server binary to an external service?

The Code Blind game server SDK allows you to set custom Labels and Annotations through the SDK.SetLabel() and SDK.SetAnnotation() functionality respectively.

This information is then queryable via the Kubernetes API, and can be used for game specific, custom integrations.

If my game server requires more states than what Code Blind provides (e.g. Ready, Allocated, Shutdown, etc), can I add my own?

If you want to track custom game server states, then you can utilise the game server client SDK SDK.SetLabel() and SDK.SetAnnotation() functionality to expose these custom states to outside systems via your own labels and annotations.

This information is then queryable via the Kubernetes API, and can be used for game specific state integrations with systems like matchmakers and more.

Custom labels could also potentially be utilised with GameServerAllocation required and/or preferred label selectors, to further refine Ready GameServer selection on Allocation.

Scaling

How large can an Code Blind cluster be? / How many GameServers can be supported in a single cluster?

The answer to this question is “it depends” 😁.

As a rule of thumb, we recommend clusters no larger than 500 nodes, based on production workloads.

That being said, this is highly dependent on Kubernetes hosting platform, control plane resources, node resources, requirements of your game server, game server session length, node spin up time, etc, and therefore you should run your own load tests against your hosting provider to determine the optimal cluster size for your game.

We recommend running multiple clusters for your production GameServer workloads, to spread the load and provide extra redundancy across your entire game server fleet.

Network

How are IP addresses allocated to GameServers?

Each GameServer inherits the IP Address of the Node on which it resides. If it can find an ExternalIP address on the Node (which it should if it’s a publicly addressable Node), that it utilised, otherwise it falls back to using the InternalIP address.

How do I use the DNS name of the Node?

If the Kubernetes nodes have an ExternalDNS record, then it will be utilised as the GameServer address preferentially over the ExternalIP node record.

How is traffic routed from the allocated Port to the GameServer container?

Traffic is routed to the GameServer Container utilising the hostPort field on a Pod’s Container specification.

This opens a port on the host Node and routes traffic to the container via iptables or ipvs, depending on host provider and/or network overlay.

In worst case scenarios this routing can add an extra 0.5ms latency to UDP packets, but that is extremely rare.

Why did you use hostPort and not hostNetwork for your networking?

The decision was made not to use hostNetwork, as the benefits of having isolated network namespaces between game server processes give us the ability to run sidecar containers, and provides an extra layer of security to each game server process.

Performance

How big an image can I use for my GameServer?

We routinely see users running container images that are multiple GB in size.

The only downside to larger images, is that they can take longer to first load on a Kubernetes node, but that can be managed by your Fleet and Fleet Autoscaling configuration to ensure this load time is taken into account on a new Node’s container initial load.

How quickly can Code Blind spin up new GameServer instances?

When running Code Blind on GKE, we have verified that an Code Blind cluster can start up to 10,000 GameServer instances per minute (not including node creation).

This number could vary depending on the underlying scaling capabilities of your cloud provider, Kubernetes cluster configuration, and your GameServer Ready startup time, and therefore we recommend you always run your own load tests for your specific game and game server containers.

Architecture

Can’t we use a Deployment or a StatefulSet for game server workloads?

Kubernetes Deployments were built for unordered, stateless workloads. That is, workloads that are essentially homogeneous between each instance, and therefore it doesn’t matter which order they are scaled up, or scaled down.

A set of web servers behind the same load balancer are a perfect example of this. The configuration and application code between instances is the same, and as long as there are enough replicas to handle the requests coming through a load balancer, if we scale from 10 to 5, it doesn’t matter which ones are removed and in which order.

Kubernetes StatefulSets were built for ordered, stateful workloads. That is, workloads in which each instance is essentially heterogeneous, and for reliability and predictability it’s extremely important that scale up happens in order (0 ,1, 2, 3) and scaling down happens in reverse (3, 2, 1, 0).

Databases are a great use case for a StatefulSet, since (depending on the database), instance 0 may be the primary, and instances 1, 2, 3+ may be replicas. Knowing that the order of scale up and down is completely reliable to both ensure that the correct disk image is in place, but also allow for appropriate synchronisation between a primaries and/or replicas can occur, and no downtime occurs.

Dedicated, authoritative game server workloads are sometimes stateful, and while not ordered/unordered, game servers are prioritised for both scale down and allocation for player usage.

Game servers are sometimes stateful, because their state only matters if players are playing on them. If no players are playing on a game server, then it doesn’t matter if it gets shut down, or replaced, since nobody will notice. But they are stateful (most often in-memory state, for the game simulation) when players are playing on them, and therefore can’t be shutdown while that is going on.

Game Server workloads are also prioritised, in that the order of demarcating game servers for player connection and also on game server scale down impact optimal usage of the hosting infrastructure.

For example, in Cloud based workloads, you will want to pack the game servers that have players on them as tightly as possible across as few Nodes as possible, while on scale down, will want to prioritise removing game servers from
Nodes that are the most empty to create empty Nodes that then can be deleted - thereby using the least amount of infrastructure as possible.

So while one might be able to use Deployments and/or StatefulSets to run game server workloads, it will be extremely hard (impossible? 🤔) to run as optimally as a tailored solution such as Code Blind.

Ecosystem

Is there an example of Code Blind and other project working together?

Yes! There are several! Check out both our official and third party examples!

12 - Third Party Content

Content created by our community

12.1 - Third Party Videos and Presentations

Community contributed videos and presentations on Code Blind.

Presentations

Large-Scale Multiplayer Gaming on Kubernetes (Cloud Next ‘19)

Code Blind: Scaling Multiplayer Game Servers with Open Source (GDC 2019)

Intro to Code Blind: Scaling Multiplayer Game Servers with Kubernetes (Kubecon, 2018)

Google Cloud Next ‘18 London: Carl Dionne, Development Director at Ubisoft Montreal

Code Blind: Scaling Multiplayer Dedicated Game Servers with Kubernetes (Cloud Next ‘18)

Screencasts

Code Blind: How Do I Docker and Kubernetes? (Part 1 & Part 2)

12.2 - Third Party Examples

Community contributed Dedicated Game Server examples on Code Blind.

Integrations with other projects

Minetest

  • Minetest is a free and open-source sandbox video game available for Linux, FreeBSD, Microsoft Windows, MacOS, and Android. Minetest game play is very similar to that of Minecraft. Players explore a blocky 3D world, discover and extract raw materials, craft tools and items, and build structures and landscapes.
  • Minetest server for Code Blind is an example of the Minetest server hosted on Kubernetes using Code Blind. It wraps the Minetest server with a Go binary, and introspects stdout to provide the event hooks for the SDK integration. The wrapper is from Xonotic Example with a few changes to look for the Minetest ready output message.

You will need to download the Minetest client separately to play.

Quilkin

You will need to download the Xonotic client to interact with the demo.

Shulker

  • Shulker is a Kubernetes operator for managing complex and dynamic Minecraft infrastructure at scale, including game servers and proxies.
  • It builds on top Code Blind GameServer and Fleet primitives to provide simplified abstractions specifically tailored to orchestrating Minecraft workloads.

Shulker requires you to have a genuine Minecraft account. You’ll need to purchase the game to test the “Getting Started” example.

12.3 - Third Party Podcasts

Community contributed podcasts on Code Blind.

2019

2018

12.4 - Third Party Libraries and Tools

Community contributed libraries and tools on Code Blind.

Client SDKs

Messaging

Libraries or applications that implement messaging systems.

Controllers

Allocation

Development Tools

  • Minikube Code Blind Cluster - Automates the creation of a complete Kubernetes/Code Blind Cluster locally, using Xonotic as a sample gameserver. Intended to provide a local environment for developers which approximates a production Code Blind deployment.

13 - Documentation Editing and Contribution

How to contribute to the docs

Writing Documentation

We welcome contributions to documentation!

Running the site

If you clone the repository and run make site-server from the build directory, this will run the live hugo server, running as a showing the next version’s content and upcoming publishedDate’d content as well. This is likely to be the next releases’ version of the website.

To show the current website’s view, run it as make site-server ENV=RELEASE_VERSION=1.38.0 ARGS=

Platform

This site and documentation is built with a combination of Hugo, static site generator, with the Docsy theme for open source documentation.

Documentation for upcoming features

Since we may want to update the existing documentation for a given release, when writing documentation, you will likely want to make sure it doesn’t get published until the next release.

There are two approaches for hiding documentation for upcoming features, such that it only gets published when the version is incremented on the release:

At the Page Level

Use publishDate and expiryDate in Hugo Front Matter to control when the page is displayed (or hidden). You can look at the Release Calendar to align it with the next release candidate release date - or whichever release is most relevant.

Within a Page

We have a feature shortcode that can be used to show, or hide sections of pages based on the current semantic version (set in config.toml, and overwritable by env variable).

For example, to show a section only from 1.24.0 forwards:

{{% feature publishVersion="1.24.0" %}}
  This is my special content that should only display >= 1.24.0
{{% /feature %}}

or to hide a section from 1.24.0 onward:

{{% feature expiryVersion="1.24.0" %}}
  This is my special content that will be hidden >= 1.24.0
{{% /feature %}}

Regenerate Diagrams

To regenerate the PlantUML or Dot diagrams, delete the .png version of the file in question from /static/diagrams/, and run make site-images.