Over the weekend I discovered this really cool Github project by Paolo Mainardi called additronk8s which is a retro DOS game engine (using DOSBox) built as a Kubernetes (K8s) custom controller and implemented in Javascript. Having spent quite a bit of time last year building out our VMworld 2019 demo which ran a number of MSDOS games on ESX 3.0 running on VMware Cloud on AWS, I definitely had to give this project a try!
In addition to having some fun playing with K8s, this solution was also quite interesting from the techniques that were used, here is a description from Paolo's own words:
One of the goal of this project was to use just Kubernetes API without any external dependency (neither the storage), in fact is noteworthy that ConfigMaps are (ab)used as a persistent storage layer, using a simple technique of split/merge parts of files to save the games.
After poking around the repository, I found that it was not very intuitive to get started. In fact, it took me some time to figure out everything and lots of trial/error. I eventually got everything working and successfully deployed several DOS games to my Tanzu Kubernetes Grid (TKG) Cluster which I had running in my homelab. Below are the detailed instructions on how to quickly get this solution stood up which just requires a vanilla K8s deployment.
Step 1 - Clone the additronk8s repository:
git clone https://github.com/paolomainardi/additronk8s-retrogames-kubernetes-controller.git
cd additronk8s-retrogames-kubernetes-controller
Step 2 - Deploy the retro game controller engine to your K8s cluster by running the following command:
kubectl apply -f k8s/manifests/namespace.yml
kubectl -n games apply -f k8s/manifests/crd-game-controller.yml
kubectl -n games apply -f k8s/manifests/game-controller-sa.yml
kubectl -n games apply -f k8s/manifests/game-controller-cluster-role.yml
kubectl -n games apply -f k8s/manifests/game-controller-cluster-role-binding.yml
kubectl -n games apply -f k8s/manifests/game-controller.yaml
Step 3 - You can download any compatible DOSBox game online such as using https://dosgames.com and host it on your own HTTP endpoint. In this example, I will be using F-Tetris.
Step 4 - Next, we need to create a game deployment manifest that contains details about the game we plan to deploy. Use a descriptive filename along with updating the name attributes within the file which will show up in your K8s deployment. You will need to specify the zipURL property which should be an HTTP endpoint where your game zip file is hosted along with the dir property which is the name of the directory upon unzipping the file (use "." for no subdirectory) and lastly the exe property which is the name of the DOS executable.
cat > tetris.yaml <<EOF apiVersion: retro.sparkfabrik.com/v1 kind: Game metadata: name: tetris namespace: games spec: name: "Tetris" zipUrl: "http://192.168.30.6/f-tetris.zip" dir: "." exe: "F-TETRIS.EXE" EOF
Step 5 - Now we are ready to deploy our game by running the following command:
kubectl apply -f tetris.yaml
We can verify that the deployment was successful by running the following command and ensuring we see Status=Runningn for our game pod which will be named based on the spec name as shown in the screenshot below:
kubectl -n games get all
You can also tail the logs of the game-controller to ensure that it was successful in processing the location of your game zip file.
kubectl -n games logs deployment/game-controller -f
Step 6 - Before we can start playing our game, we need to setup a port forward by specifying the name of our service. In this case, it is svc/tetris or whatever name you had used in your deployment spec and we will need to forward both port 8080/8081.
kubectl -n games port-forward svc/tetris 8080:8080 8081:8081
Note: If you have the ability to provision a load balancer within your Kubernetes cluster, you can modify the Tetris service by running the following command: kubectl -n games patch svc tetris -p '{"spec": "type":"LoadBalancer"}' which will patch the service from type ClusterIP to LoadBalancer. Then you can retrieve the external address by running the following command: kubectl -n games get svc
Step 7 - Finally, open a web browser to localhost:8080 and you should see the DOSBox console and simply type the name of the DOS executable name to load the game:
What favorite DOS game will you be playing err working on? 😀
Dz says
I could not make it past
```
Reading cloud floppy disks of "Tetris" in progress... 💾
Reading cloud floppy disks of "Tetris" in progress... 💾
Reading cloud floppy disks of "Tetris" in progress... 💾
```
zip files is downloaded from http server for sure. I can see that with tcpdump. any hints?
Paolo Mainardi says
This is strange, it seems that is failing to download the file, but in that case the controller should warn you: https://github.com/paolomainardi/additronk8s-retrogames-kubernetes-controller/commit/b2796f620a972097d483fe2d7e8a8637e290f63e
Where is located the file ? You have 2 options:
1. GCP GS bucket (file must be publicly accessible)
2. HTTP(s) endpoint with a valid ssl certificate
dz says
I see from tcpdump of an HTTP server that file is being downloaded.
But there's no other signs anywhere about anything.
If I provide the wrong filename or http, it throws an error, so, the download part works for sure.
But i never see the configmap being created for the game or anything like that.
Paolo Mainardi says
Interesting! Which kind of k8s cluster and version are you deploying to ?
Can you try to run a `make` to test it against a local k3d cluster ?
dz says
it's TKG. let me try the k3d see if it's any different.
I've just tried to create a crb and give it cluster-admin level access. no difference.
From what I understand it's never getting past ->
const configmaps = await createConfigMapsSpec(files, gameObject);
dz says
same results with k3d.
Paolo Mainardi says
I think that i've found the cause, rewrote the remote url handling and now should way more consistent and reliable.
You can download again the repo here: https://github.com/paolomainardi/additronk8s-retrogames-kubernetes-controller skipping the steps 1,2,3,4 because now the images are on the dockerhub.
Let me know if it works.
dz says
it's much better, now I am getting the following errors from:
kk describe pod/tetris-6677ddc97d-r5p6w -n games
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 2m34s default-scheduler Successfully assigned games/tetris-6677ddc97d-r5p6w to k3d-retrogames-k8s-dev-server-0
Normal Pulled 2m6s kubelet Successfully pulled image "paolomainardi/additronk8s-game-engine:latest" in 27.622876051s
Normal Pulled 2m5s kubelet Successfully pulled image "paolomainardi/additronk8s-game-engine:latest" in 435.451046ms
Normal Pulled 112s kubelet Successfully pulled image "paolomainardi/additronk8s-game-engine:latest" in 445.792611ms
Normal Pulled 84s kubelet Successfully pulled image "paolomainardi/additronk8s-game-engine:latest" in 476.215216ms
Normal Created 84s (x4 over 2m6s) kubelet Created container hydrate-game
Normal Started 84s (x4 over 2m6s) kubelet Started container hydrate-game
Warning BackOff 46s (x8 over 2m4s) kubelet Back-off restarting failed container
Normal Pulling 33s (x5 over 2m34s) kubelet Pulling image "paolomainardi/additronk8s-game-engine:latest"
dz says
ok, actually following the guide from github and used quake example i got it working, now checking the port forwarding.. will update shortly.
Paolo Mainardi says
Hi Wililams! Thanks a lot for this great write-up, i've made some changes to make it easier to run, moved the docker images on the dockerhub and added a working example. So the steps "1,2,3,4" are not more needed, if you can update the blog post according to this would be great.
Thanks!
dz says
works like a charm! thanks!
William Lam says
Step 1 is still needed since that clones the repo 🙂 but yes, the remainder steps of building the containers are no longer required and blog post has been updated.
Paolo Mainardi says
Thanks a lot William!
Bharath S says
Awesome!! Work!! I enjoyed learning lots the fun way