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 Agones 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 GameServers 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.


Last modified June 13, 2024: Change Slack channel description from #developers to #development (#3868) (b37fc35)