OpenArena game
Teeworlds game
We have created two demos for you to test this project with, both of which are related games. We have modified the Docker images of the popular open source games OpenArena and Teeworlds, both of which can easily be used to demonstrate this project. You can find the modified Docker image for OpenArena here and for Teeworlds here. Both repos have been configured with Docker Hub automated build. In this way, every push to GitHub modifies the images on Docker Hub (OpenArena and Teeworlds).
The steps to run the demo for both games are pretty similar. The big difference is that in the case of the OpenArena game we have taken the game's files (executables, assets, everything) outside the Docker image, in order to save on its final size. We will use an Azure File share to store the files. This share will be volume mounted
from the running container when it's created. So, here are the steps that you could use if you wanted to set up a quick demo of the project:
- (OpenArena game only) You need to create an Azure Storage account to store the game's files. Use the following script from either Azure CLI or Azure Cloud Shell:
# Change these parameters as needed
ACI_PERS_STORAGE_ACCOUNT_NAME=openarena$RANDOM
ACI_PERS_LOCATION=westeurope # for better performance, choose the location that your Azure Container Instances will be deployed
ACI_PERS_SHARE_NAME=openarenadata
ACI_PERS_RESOURCE_GROUP=acimanagement #resource group where you want to deploy your Azure Files share. You can use the same one as your Functions
# Create the storage account with the provided parameters
az storage account create \
--resource-group $ACI_PERS_RESOURCE_GROUP \
--name $ACI_PERS_STORAGE_ACCOUNT_NAME \
--location $ACI_PERS_LOCATION \
--sku Standard_LRS
# Export the connection string as an environment variable. The following 'az storage share create' command
# references this environment variable when creating the Azure file share.
export AZURE_STORAGE_CONNECTION_STRING=`az storage account show-connection-string --resource-group $ACI_PERS_RESOURCE_GROUP --name $ACI_PERS_STORAGE_ACCOUNT_NAME --output tsv`
# Create the file share
az storage share create -n $ACI_PERS_SHARE_NAME
# Get Storage credentials
STORAGE_ACCOUNT=$(az storage account list --resource-group $ACI_PERS_RESOURCE_GROUP --query "[?contains(name,'$ACI_PERS_STORAGE_ACCOUNT_NAME')].[name]" --output tsv)
echo $STORAGE_ACCOUNT
STORAGE_KEY=$(az storage account keys list --resource-group $ACI_PERS_RESOURCE_GROUP --account-name $STORAGE_ACCOUNT --query "[0].value" --output tsv)
echo $STORAGE_KEY
You can also find this script in the various
folder here.
Keep the STORAGE_ACCOUNT and STORAGE_KEY credentials handy as you will need them when you will deploy your Container Group for OpenArena game.
- (OpenArena game only) Download the game and place all its files onto the Azure File share you created. You can
- download the files locally and use Azcopy or Azure Storage Explorer to upload them on the share
- mount the share locally or on an Azure VM, download the files and transfer them via SMB (this proved to be must faster in our tests). You can use this command on Windows
or this one on Linuxnet use Z: \\accountname.file.core.windows.net\openarenadata /u:accountname key_ending_in==
Bear in mind that in the end, OpenArena files should exist directly in the path you specify. For instance, if you selectedsudo mount -t cifs //accountname.file.core.windows.net/openarenadata /path -o vers=3.0,username=accountname,password=key_ending_in==,dir_mode=0777,file_mode=0777
/path
as the mount folder, thepak0.pk3
file's full path should be/path/baseoa/pak0.pk3
. To do this on Linux, you could tryunzip path/to/openarena.zip -d /temppath cd /temppath cd openarena-0.8.8 mv * /path
- Download the game of your choice. For OpenArena check here and for Teeworlds check here for download links.
- Deploy the project in your Azure subscription. You can use one-click deployment, as described in README.md.
- Deploy the Event Grid subscription for the ACIMonitor Function. You can create the subscription by visiting the
ACIMonitor
Function page on the Azure portal (check here for instructions). Once you get the webhook URL, you can also use the deploy.eventgridsubscription.json file to deploy the Event Grid subscription. To do that, go to the Azure portal and ask to create aTemplate Deployment
resource. When you deploy your Event Grid suscription, make sure that you're monitoring all events on either the Resource Group you're planning to create your Container Instances on or your entire subscription. - Call the ACICreate Function to create an Azure Container Instance with the image of your game. You can get Function's URL (including the key) from the Azure Portal (instructions) and use the provided Postman files (located here) to begin. There are two POSTMAN exported collections, one for each game.
For the OpenArena you can use the following POST body. Make sure you change the values
resourceGroup
,containerGroupName
,containers[0].name
,containers[0].volumeMounts.name
,containerGroup.volumes[0].name
,containerGroup.ipAddress.dnsNameLabel
as well as the values incontainerGroup.volumes[0].azureFile
object.
{
"resourceGroup": "acimanagement",
"containerGroupName": "openarenaserver1",
"containerGroup" : {
"location": "westeurope",
"containers": [{
"name": "openarenaserver1",
"image": "dgkanatsios/docker_openarena",
"environmentVariables": [{
"name":"SERVER_NAME",
"value":"AzureOpenArena1"
},{
"name":"OA_STARTMAP",
"value":"dm4ish"
},{
"name":"OA_PORT",
"value":"27960"
}],
"resources": {
"requests": {
"memoryInGB": 0.5,
"cpu": 1
}
},
"ports": [{
"protocol": "udp",
"port": 27950
},{
"protocol": "udp",
"port": 27960
}],
"volumeMounts":[{
"name":"openarenadatavolume",
"mountPath": "/data",
"readOnly": false
}
]
}],
"ipAddress": {
"ports": [{
"protocol": "udp",
"port": 27950
},{
"protocol": "udp",
"port": 27960
}],
"type": "Public",
"dnsNameLabel": "customDNSName"
},
"osType": "Linux",
"volumes": [{
"name": "openarenadatavolume",
"azureFile": {
"shareName": "openarenadata",
"storageAccountName": "storage account name",
"storageAccountKey": "account key ending in=="
}
}]
}
}
For teeworlds you could use this script, modify the resourceGroup
, containerGroupName
, containers[0].name
, containerGroup.ipAddress.dnsNameLabel
values as well as the SERVER_NAME
environment variable.
{
"resourceGroup": "teeworlds",
"containerGroupName": "teeserver1",
"containerGroup" : {
"location": "westeurope",
"containers": [{
"name": "teeserver1",
"image": "dgkanatsios/docker-teeworlds",
"environmentVariables": [{
"name":"SERVER_NAME",
"value":"Azure-Dimitris-1"
}],
"resources": {
"requests": {
"memoryInGB": 0.5,
"cpu": 1
}
},
"ports": [{
"protocol": "udp",
"port": 8303
}]
}],
"ipAddress": {
"ports": [{
"protocol": "udp",
"port": 8303
}],
"type": "Public",
"dnsNameLabel": "customDNSName"
},
"osType": "Linux"
}
}
As you can easily notice, OpenArena requires port 27960/udp and Teeworlds requires port 8303/udp in order to function correctly.
- Once your Docker container is deployed, you will see a new entry in your Azure Table. The Storage account that contains this Table should have a name similar to
RANDOM_STRINGacidetails
whereas the actual table name isACIDetails
. You can use Azure Storage Explorer to monitor it. Container should be in the Creating state. - After a couple of minutes the instance should be running, so you'll see in the table storage that it has transitioned to the Running state, having a Public IP.
- Call the ACIList Function to see your Running Container Instances. If result is something like the below, you have successfully set up your OpenArena/Teeworlds game server on Azure Container Instances!
[
{
"resourceGroup": "teeworlds",
"containerGroupName": "teeserver1",
"PublicIP": "168.63.121.114",
"ActiveSessions": 0
}
]
- You can use this IP to connect to the game server you just set up. Start the game client and connect to this IP.
- If you now check your Table Storage (or call the ACIList Function again), you should see that ActiveSessions for the server you connected to are equal to 1. This number should increase as more clients connect to your container instance. This happens because both game images are configured to call the ACISetSessions Function when a user connects/disconnect to the server. The way this is done is pretty basic (and error-prone), since game servers log everything (including connections/disconnections) to STDOUT, we're just filtering it to a custom shell script and increasing a value in a text file. To see more details about it, check the
stdoutprocessor.sh
file in both Docker images. In a production environment, game server itself should call the ACISetSessions URL. - To get the logs from your game server, use the ACIDetails Function. If you omit the
type:"logs"
from the POST body, you should see the Azure Resource details for your container. - Now, suppose that you do not need this container instance any more. You may call ACIDelete Function to delete it at once, but there may be players that are currently playing the game. Of course, you do not want to ruin their experience, right? What you can do is call the ACISetState Function and set the container's state to
MarkedForDeletion
. That would be the POST body:
{
resourceGroup: "teworlds",
containerGroupName: "teeserver1",
state:"MarkedForDeletion"
}
Of course, we suppose that since it's MarkedForDeletion
, no other games will be scheduled on this container. This container will not be reported when ACIList is called (since it retrieves info only about Running
Container Instances).
- The ACIGC Function, which is being triggered in specific time intervals, will eventually kick-in and delete this Container Group resource since a) it's 'MarkedForDeletion' and b) it has 0 active sessions (we suppose that the game session has ended or players have left the server).
- To cleanup the resources you deployed for this demo, you should delete the Resource Group(s) where you deployed your resources as well as your Event Grid Subscriptions. To find these, use this article.