# Run & Build GameServer(Rust)- GKE+Agones Platform

Agones is a library for hosting, running and scaling dedicated game servers on Kubernetes. It 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. It replaces bespoke or proprietary cluster management and game server scaling solutions with an open source solution that can be utilised 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, Agones 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.

This blog post would cover about how to use the Agones Rust SDK in a simple Rust game server. We would be leveraging managed kubernetes offering from Google Cloud Platform(Google Kubernetes Engine) with Agones installed on top of that.

Below are some of the prerequisites before you can start building and running a simple game server using Rust SDK for Agones.

1. [Docker](https://www.docker.com/get-started/)
    
2. Agones installed on GKE
    
3. kubectl properly configured
    
4. A local copy of the [Agones repository](https://github.com/googleforgames/agones/tree/release-1.27.0)
    
5. A repository for Docker images, such as [Docker Hub](https://hub.docker.com/) or [GC Container Registry](https://cloud.google.com/container-registry/)
    

Follow these steps to create a [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine/) cluster for your Agones install.

# **Before you begin**

Take the following steps to enable the Kubernetes Engine API:

1. Visit the [Kubernetes Engine](https://console.cloud.google.com/kubernetes/list) 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](https://support.google.com/cloud/answer/6293499#enable-billing) for your project.
    

# **Choosing a shell**

We can use either [Google Cloud Shell](https://cloud.google.com/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`](https://cloud.google.com/sdk/gcloud/) and [`kubectl`](https://kubernetes.io/docs/user-guide/kubectl-overview/) 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](https://console.cloud.google.com/home/dashboard).
    
2. From the top-right corner of the console, click the **Activate Google Cloud Shell** button.
    

![](https://miro.medium.com/v2/resize:fit:88/1*kv039Ge6-J7MrcmvWTd3NQ.png align="left")

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. An example compute zone is `us-west1-a`.

```bash
gcloud config set compute/zone [COMPUTE_ZONE]
```

# **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.

```bash
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**

A [cluster](https://cloud.google.com/kubernetes-engine/docs/concepts/cluster-architecture) consists of at least one *control plane* machine and multiple worker machines called *nodes*. In Google Kubernetes Engine, nodes are [Compute Engine virtual machine](https://cloud.google.com/compute/docs/instances/) instances that run the Kubernetes processes necessary to make them part of the cluster.

```bash
gcloud container clusters create [CLUSTER_NAME] --cluster-version=1.23 \
  --tags=game-server \
  --scopes=gke-default \
  --num-nodes=4 \
  --no-enable-autoupgrade \ 
  --enable-image-streaming \
  --machine-type=e2-standard-4
```

# **(Optional) Creating a dedicated node pool**

Create a [dedicated node pool](https://cloud.google.com/kubernetes-engine/docs/concepts/node-pools) for the Agones resources to be installed in. If you skip this step, the Agones controllers will share the default node pool with your game servers, which is fine for experimentation but not recommended for a production deployment.

```bash
gcloud container node-pools create agones-system \
  --cluster=[CLUSTER_NAME] \
  --no-enable-autoupgrade \
  --node-taints agones.dev/agones-system=true:NoExecute \
  --node-labels agones.dev/agones-system=true \
  --num-nodes=1
```

# **(Optional) Creating a metrics node pool**

Create a node pool for [Metrics](https://agones.dev/site/docs/guides/metrics/) if you want to monitor the Agones system using Prometheus with Grafana or Cloud Logging and Monitoring.

```bash
gcloud container node-pools create agones-metrics \
  --cluster=[CLUSTER_NAME] \
  --no-enable-autoupgrade \
  --node-taints agones.dev/agones-metrics=true:NoExecute \
  --node-labels agones.dev/agones-metrics=true \
  --num-nodes=1
```

# **Setting up cluster credentials**

Finally, let’s tell `gcloud` that we are speaking with this cluster, and get auth credentials for `kubectl` to use.

```bash
gcloud config set container/cluster [CLUSTER_NAME]
gcloud container clusters get-credentials [CLUSTER_NAME]
```

# **Install Agones using Helm**

Install Agones on a [Kubernetes](http://kubernetes.io/) cluster using the [Helm](https://helm.sh/) package manager.

# **Prerequisites**

* [Helm](https://helm.sh/) package manager 3.2.3+
    
* [Supported Kubernetes Cluster](https://agones.dev/site/docs/installation/#usage-requirements)
    

# **Helm 3**

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

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

When running in production, Agones should be scheduled on a dedicated pool of nodes, distinct from where Game Servers are scheduled for better isolation and resiliency. By default Agones prefers to be scheduled on nodes labeled with [`agones.dev/agones-system=true`](http://agones.dev/agones-system=true) and tolerates node taint [`agones.dev/agones-system=true:NoExecute`](http://agones.dev/agones-system=true:NoExecute). If no dedicated nodes are available, Agones will run on regular nodes, but that’s not recommended for production use.

# **Namespaces**

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

For example to use `default` **and** `xbox` namespaces:

```bash
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:

```bash
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:

```bash
helm uninstall my-release --namespace=agones-systemRBAC
```

By default, `agones.rbacEnabled` is set to true. This enables RBAC support in Agones 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 Agones.

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

The above command will deploy Agones controllers to `agones-system` namespace. Additionally Agones 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,

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

# **Helm test**

Check the Agones installation by running the following command:

```bash
helm test my-release --cleanup
```

```plaintext
RUNNING: agones-test
PASSED: agones-test
```

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

# **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](http://cert-manager.io) solution for cluster level certificate management.

In order to use the cert-manager solution, first [install cert-manager](https://cert-manager.io/docs/installation/kubernetes/) on the cluster. Then, [configure](https://cert-manager.io/docs/configuration/) an `Issuer`/`ClusterIssuer` resource and last [configure](https://cert-manager.io/docs/usage/certificate/) 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`:

```bash
#!/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
```

```bash
# 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-cert
  namespace: agones-system
spec:
  dnsNames:
    - agones-controller-service.agones-system.svc
  secretName: my-release-cert
  issuerRef:
    name: selfsigned
    kind: ClusterIssuer
EOF
```

After the certificates are generated, we will want to [inject caBundle](https://cert-manager.io/docs/concepts/ca-injector/) into controller webhook and disable controller secret creation by setting the following:

```bash
helm install my-release \
  --set agones.controller.disableSecret=true \
  --set agones.controller.customCertSecretPath[0].key='ca.crt',customCertSecretPath[0].path='ca.crt'
  --set agones.controller.customCertSecretPath[1].key='tls.crt',customCertSecretPath[1].path='server.crt'
  --set agones.controller.customCertSecretPath[2].key='tls.key',customCertSecretPath[2].path='server.key'
  --set agones.controller.allocationApiService.annotations={'cert-manager.io/inject-ca-from': 'agones-system/my-release-cert'} \
  --set agones.controller.allocationApiService.disableCaBundle=true \
  --set agones.controller.validatingWebhook.annotations={'cert-manager.io/inject-ca-from': 'agones-system/my-release-cert'} \
  --set agones.controller.validatingWebhook.disableCaBundle=true \
  --set agones.controller.mutatingWebhook.annotations={'cert-manager.io/inject-ca-from': 'agones-system/my-release-cert'} \
  --set agones.controller.mutatingWebhook.disableCaBundle=true \
  --namespace agones-system --create-namespace  \
  agones/agones
```

# **Run the simple gameserver**

First, run the pre-built version of the simple gameserver and take note of the name that was created:

```bash
kubectl create -f https://raw.githubusercontent.com/googleforgames/agones/release-1.27.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 Agones SDK, calls `sdk.ready()` to inform Agones 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

```bash
kubectl logs ${GAMESERVER_NAME} rust-simple -f
```

which should produce output similar to

```plaintext
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

```bash
kubectl delete gs ${GAMESERVER_NAME}
```

# **Build a simple gameserver**

Change directories to your local agones/examples/rust-simple directory. To experiment with the SDK, open up [`main.rs`](http://main.rs) in your favorite editor and change the interval at which the gameserver calls [`sdk.health`](http://sdk.health)`()` from 2 seconds to 20 seconds by modifying the line in the thread assigned to `let _health` to be

```bash
thread::sleep(Duration::from_secs(20));
```

Next build a new docker image by running

```bash
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

```bash
docker push ${REPOSITORY}/rust-simple-server:0.4
```

# **Run the customized gameserver**

Now it is time to deploy your newly created gameserver container into your Agones cluster.

First, you need to edit `examples/rust-simple/gameserver.yaml` to point to your new image:

```bash
containers:
- name: rust-simple
  image: $(REPOSITORY)/rust-simple-server:0.4
  imagePullPolicy: Always
```

Then, deploy your gameserver

```bash
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

```bash
kubectl logs ${GAMESERVER_NAME} rust-simple -f
```

which should produce output similar to

```plaintext
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 Agones.

To finish, clean up the gameserver by manually removing it

```bash
kubectl delete gs ${GAMESERVER_NAME}
```
