Skip to content

Commit

Permalink
Quick start to create game server & details spec guide
Browse files Browse the repository at this point in the history
  • Loading branch information
Cyril TOVENA committed Mar 1, 2018
1 parent 09cf3cb commit b8a29b5
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 7 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ Documentation and usage guides on how to develop and host dedicated game servers
More documentation forthcoming.

### Quickstarts:
- Create a Game Server (forthcoming)
- [Create a Game Server](./docs/create_gameserver.md)

### Guides
- [Game Server Specification](./docs/gameserver_spec.md)
- Integrating the C++ SDK (forthcoming)
- [GameServer Health Checking](./docs/health_checking.md)

Expand Down
150 changes: 150 additions & 0 deletions docs/create_gameserver.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Quickstart Create a Game Server

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

## Objectives

- Create a GameServer in kubernetes using Agones custom ressources.
- Get informations about the GameServer such as IP address, port and state.
- Connect to the GameServer.

## 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. Agones 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](https://docs.microsoft.com/en-us/windows/wsl/install-win10).

>NOTE: Agones required Kubernetes versions 1.9 with role-based access controls (RBAC) and MutatingAdmissionWebhook features activated. To check your version, enter `kubectl version`.
If you don't have a Kubernetes cluster you can follow [these instructions](installing_agones.md) to create a cluster on Google Kubernetes Engine (GKE) or Minikube, and install Agones.

For the purpose of this guide we're going to use the [simple-udp](../examples/simple-udp/) example as the GameServer container. This example is very simple UDP server written in Go. Don't hesitate to look at the code of this example for more information.

### 1. Create a GameServer

Let's create a GameServer using the following command :

```
kubectl apply -f https://github.com/googlecloudplatform/agones/blob/master/examples/simple-udp/server/gameserver.yaml
```

You should see a successful ouput similar to this :

```
gameserver "simple-udp" created
```

This has created a GameServer record inside Kubernetes, which has also created a backing [Pod](https://kubernetes.io/docs/concepts/workloads/pods/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 AGE
simple-udp 5m
```

You can also see the [Pod](https://kubernetes.io/docs/concepts/workloads/pods/pod/) that got created by running `kubectl get pods`, the Pod will be prefixed by `simple-udp`.

```
NAME READY STATUS RESTARTS AGE
simple-udp-vwxpt 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 Agones 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](./gameserver_spec.md)

### 2. Fetch the GameServer Status

Let's wait for the GameServer state to become `Ready`:

```
watch kubectl describe gameserver
```

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 `GameSever` occur. We can also see when the `GameServer` is ready on the event stream as well - at which time the `Status > Address` and `Status > Port` have also been populated, letting us know what IP and port our client can now connect to!

```
Name: simple-udp
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"stable.agones.dev/v1alpha1","kind":"GameServer","metadata":{"annotations":{},"name":"simple-udp","namespace":"default"},"spec":{"contain...
API Version: stable.agones.dev/v1alpha1
Kind: GameServer
Metadata:
Cluster Name:
Creation Timestamp: 2018-03-01T20:43:36Z
Finalizers:
stable.agones.dev
Generation: 0
Resource Version: 6238
Self Link: /apis/stable.agones.dev/v1alpha1/namespaces/default/gameservers/simple-udp
UID: 372468b6-1d91-11e8-926e-fa163e0980b0
Spec:
Port Policy: dynamic
Container: simple-udp
Container Port: 7654
Health:
Failure Threshold: 3
Initial Delay Seconds: 5
Period Seconds: 5
Host Port: 7211
Protocol: UDP
Template:
Metadata:
Creation Timestamp: <nil>
Spec:
Containers:
Image: gcr.io/agones-images/udp-server:0.1
Name: simple-udp
Resources:
Status:
Address: 10.130.65.212
Node Name: dev-worker-03
Port: 7211
State: Ready
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal PortAllocation 11s gameserver-controller Port allocated
Normal Creating 11s gameserver-controller Pod simple-udp-vwxpt created
Normal Starting 11s gameserver-controller Synced
Normal Ready 4s gameserver-controller Address and Port populated
```


Then retrieve the IP address and the allocated port of your Game Server :

```
kubectl get gs simple-udp -o jsonpath='{.status.address}:{.status.port}'
```

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

### 3. Connect to the GameServer

You can now communicate with the Game Server :

```
nc -u 10.130.65.208 7936
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`.


## Next Step

If you want to use your own GameServer container make sure you have properly integrated the [Agones SDK](../sdks/).
45 changes: 45 additions & 0 deletions docs/gameserver_spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# GameServer Specification

Like any other kubernetes ressources you describe a GameServer desired state via a specification written in YAML or JSON to the kubernetes API. The Agones controller will then change the actual state to the desired state.

A full GameServer specification is available below and in the [example folder](../examples/gameserver.yaml) for reference :

```
apiVersion: "stable.agones.dev/v1alpha1"
kind: GameServer
metadata:
name: "gds-example"
spec:
container: example-server
portPolicy: "static"
containerPort: 7654
hostPort: 7777
protocol: UDP
health:
disabled: false
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 3
template:
metadata:
labels:
myspeciallabel: myspecialvalue
spec:
containers:
- name: example-server
image: gcr.io/agones/test-server:0.1
imagePullPolicy: Always
```

Since Agones defines a new [Custom Ressources Definition (CRD)](https://kubernetes.io/docs/concepts/api-extension/custom-resources/) we can define a new ressource using the kind `GameServer` with the custom group `stable.agones.dev` and API version `v1alpha1`.

You can use the metadata field to target a specific [namespaces](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/) but also attach specific [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) and [labels](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/) to your ressource. This is a very common pattern in the kubernetes ecosystem.

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](https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/). If you do, this is a mandatory field. For instance this is useful if you want to run a sidecar to ship logs.
- `portPolicy` has two options `dynamic` (default) the system allocates a free hostPort for the gameserver, for game clients to connect to. And `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.
- `containerPort` the port that is being opened on the game server process, this is required field.
- `protocol` the protocol being used. Defaults to UDP. TCP is the only other option.
- `health` to track the overall healthy state of the GameServer, more information available in the [health check documentation](./health_checking.md).
- `template` the [pod spec template](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.9/#pod-v1-core) to run you GameServer containers, [see](https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/#pod-templates) for more
6 changes: 2 additions & 4 deletions examples/simple-udp/server/gameserver.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ kind: GameServer
metadata:
name: "simple-udp"
spec:
portPolicy: "static"
portPolicy: "dynamic"
containerPort: 7654
hostPort: 7777
template:
spec:
containers:
- name: simple-udp
image: gcr.io/agones-images/udp-server:0.1
# imagePullPolicy: Always # add for development
image: gcr.io/agones-images/udp-server:0.1
5 changes: 3 additions & 2 deletions examples/simple-udp/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"log"
"net"
"os"
"strings"

"time"

Expand Down Expand Up @@ -66,7 +67,7 @@ func main() {
log.Fatalf("Could not read from udp stream: %v", err)
}

txt := string(b[:n]) // trim the empty bytes
txt := strings.TrimSpace(string(b[:n]))
log.Printf("Received packet from %v: %v", sender.String(), txt)
switch txt {
// shuts down the gameserver
Expand All @@ -87,7 +88,7 @@ func main() {
}

// echo it back
ack := "ACK: " + txt
ack := "ACK: " + txt + "\n"
if _, err = conn.WriteTo([]byte(ack), sender); err != nil {
log.Fatalf("Could not write to udp stream: %v", err)
}
Expand Down

0 comments on commit b8a29b5

Please sign in to comment.