Jonathan Peppers


Xamarin, C# nerd, at Microsoft

Deploying a Minecraft Server on Azure with Docker

This week at Microsoft is "One Week"--an all-week Hackathon where we are allowed to work on open source projects for fun. Sounds great to me!

For one thing what I wanted to work on, I wanted to focus on a project different than what I have been doing with the Xamarin.Android team. Something using Windows Azure, Docker, and possibly a programming lanaguage other than C#. I have been playing with an open source server for Minecraft: Pocket Edition named Nukkit, which seemed like a fun project to "hack" on. It is written in Java by Minecraft enthusiasts (as you can imagine), and is derived from a server built for the PC version of Minecraft named Bukkit. Nukkit is the best software I've found to support Minecraft: PE, although it is in very much an experiment state. Supporting PE is a requirement, as my niece, nephew, and son all play this game on only mobile devices.

Build Nukkit from Source

I did all of this on OS X, but I assume it would also work on Windows. Begin by cloning (or forking) their repo from Github. The next thing you will need is Maven, if you do not already have it. I installed it with Homebrew via a quick brew install maven command.

Next, it is just a couple shell commands to get Nukkit to compile:

This will build nukkit-1.0-SNAPSHOT.jar to the target directory.

If you haven't ever looked at Maven before, it is yet another build format for Java. A single file pom.xml configures the build, and is a fairly straightforward XML format. If you want to work with the project in an IDE, I would recommend the free Community edition of IntelliJ, and all you need to do is open the root directory cloned from Github. You should see a warning/tip prompting that pom.xml is not managed by IntelliJ, if you click it that seemed to be all I needed to work on the project and compile within IntelliJ.

NOTE: if you don't want to compile from source, you can also grab a build of nukkit.jar from their public Jenkins CI.

Running Nukkit

From here, I would recommend moving nukkit-1.0-SNAPSHOT.jar to its own directory somewhere, I chose ~/minecraft. To run Nukkit, just do:

After Nukkit is up and running, there is a nice command prompt for issuing commands within the game. Type help to see a list of them; stop shuts down the server. To join the server, if you have a device on the local network, you should see the server listed just as it does for a P2P game. Complete magic!

From what I have seen, Nukkit stores its data in a few files/directories as follows:

  • nukkit.yml - one of the main configuration files
  • server.properties - another main configuration file (I do not know why there are two)
  • worlds directory - structure of one or more "worlds" saved on the server
  • players directory - stores data about each joined player in a PLAYER_NAME.dat file
  • plugins directory - for custom plugins to be installed (more in this later)
  • logs directory - crash reports and console output

There are also a few other files in here, but these are the ones that seem important.

Basic Nukkit Configuration

If you have ever been around kids playing Minecraft: PE, they pretty much only play Creative mode. Let's modify our server.properties file to make that happen:

motd is the name of the server that appears in the Minecraft UI, so make sure to put something entertaining. gamemode is either 1 for Creative or 2 for Survival.

Installing Nukkit Plugins

It turns out that the basic functionaliy of Nukkit is missing a few things, but they have luckily included a plugin model to extend Nukkit. I used two plugins that address mostly what we wanted:

  • EssentialsNK - adds way more commands to the command prompt
  • MobPlugin - makes animals/enemies actually move

You can find all kinds of plugins for Nukkit here, but your mileage may vary.

To use these plugins, you can either get them to comple from source via mvn package, or find a download/Github release that contains their resulting jar files. To install a plugin, just drop the jar file into the plugins directory. You should see a message such as Enabling EssentialsNK v1.0.8 when running Nukkit.

More Worlds!

After getting your custom server to work, the first thing you need is to actually get levels your target audience has never seen before. There are plenty of Minecraft levels/maps out there on the internet, but searching Minecraft on Google can lead you to a wasteland full of malware--be careful. A few of the sites I would recommend that have working maps are at Curse and Minecraft Maps. The download links at Minecraft Maps send you through some terrible ads, but the maps themselves are very good.

When you download a map, it will be a huge zip file. I've found only some of the files actually matter. I'll walkthrough setting up a map I downloaded from Curse here, named Diversity 2. Make a directory at NUKKIT_ROOT/worlds/diversity and copy level.dat and the region directory from the zip file into it.

Next we will have to make some changes to our nukkit.yml and server.properties files:

From here, launch your server and connect from your mobile device. You should be dropped into the world you just downloaded from the Internet, not too shabby!

To add multiple worlds, repeat the process above and add additional lines to your nukkit.yml file. If you want to make new worlds from scratch, just add the names to the yml file and Nukkit will create the directories as needed. To traverse between worlds while your server is running, I use a few commands that are provided by the EssentialsNK plugin we installed earlier:

  • op PLAYER_NAME - sometimes needed, depending on the command. This makes a player an "admin".
  • sudo PLAYER_NAME world WORLD_NAME - sudo allows the server to run a command as player, while world teleports the player between worlds

This is pretty cool, since there are not really any ways to have multiple "worlds" at once within a single connected Minecraft P2P game between two mobile devices. Multiple players can join the server and do their own thing in separate worlds. It is also good to know that these commands can also be run by players in chat by typing /world WORLD_NAME, for example. Trust me, all this stuff will blow kids' minds. It is an amazing way to get them into pseudo-programming (via a CLI), and they can start to see how programming could be fun!

Legacy Maps

Some maps you will find on the Internet are in a different format than mentioned above. If Nukkit finds one of these maps, it will run through an import process, probably print a lot of errors, and then finally migrate the world to the new format. These older maps will have a db directory and a single level.dat file. Install them into the worlds directory just as you did before. After migration, Nukkit will move them to LEVEL_NAME.old and keep the migrated level in a LEVEL_NAME folder. If you need to migrate a level, I would recommend doing it locally and put the migrated level files in your docker image--now let's move onto talking about docker.

Running it on Windows Azure

So far, all of this was running on my local dev environment. It is not too much of a step to lift our server to Windows Azure and get it running on the public Internet. NOTE: if you want to run Nukkit for an extended time on a public server, some secure precautions might be needed here. I might write a future post about this if I figure something out.

First, create a Linux VM of your choice, but I just chose the Ubuntu Server 16.04 LTS image. Make sure you can ssh into the VM and are comfortable running commands. Next, let's install Docker via sudo apt install docker.io. This is really all we'll need to install here, if you use other distros you may need Java, etc.

Azure should have already created a Network Security Group for your VM, we will need to open the 19132 port for Nukkit to operate under Inbound Security Rules:

Inbound Security Rules

NOTE: that I have no idea if any of this is secure/best practice. I recommend just shutting down the VM when you are not using it.

Dockerizing Nukkit

Nukkit is a perfect example of an application that can be "dockerized". It has no dependencies, and just needs to use the local file system. Overall it was a great start for me to learn about docker. If you have not used or heard about docker, it is a way to bundle up your application and its dependencies into an isolated container (or group of containers). This sets up our application to have its own file system in a sandbox isolated from other containers on the same machine.

If you've not used Docker before, go ahead and install the desktop/dev environment over at docker.com.

Next, create a new empty directory to start developing your Docker image. I used a Github repo for this, which made a lot of sense for me. Drop nukkit-1.0-SNAPSHOT.jar, nukkit.yml, server.properties, and any plugin jars desired into the directory.

Next let's write our first Dockerfile, a plain text file named as such to configure our image:

To break down what we did here:

  • FROM anapsix/alpine-java - bases our image on an existing image on DockerHub
  • MAINTAINER Jon - just put your name, I guess
  • COPY src destination - to build up the file structure of our image
  • WORKDIR /home/ - set the working directory
  • CMD java -jar /home/nukkit-1.0-SNAPSHOT.jar - launch Nukkit

I have no idea if this is best practice, but I arranged the following file structure within my image as follows:

  • /home/nukkit-1.0-SNAPSHOT.jar
  • /home/nukkit.yml
  • /home/server.properties
  • /home/worlds
  • /home/plugins/EssentialsNK-1.0.8.jar
  • /home/plugins/MobPlugin-1.1-DEV.jar

To build the image, navigate to this directory and run docker build . -t nukkit. This will build an image named nukkit that is a self-contained copy of all of these files.

To run a container with this image, run docker run -it -p 19132:19132/udp --name nukkit nukkit. This will run a container named nukkit interactively, open the UDP port 19132, and use the nukkit image. This will appear similar to when you ran Nukkit normally, except it is now running inside a nice docker sandboxed environment. You can now run docker ps to view running containers, and you can delete the container via docker rm nukkit. The only drawback here is if we destroy the container and recreate it, we lose any state we have of the file system.

So how to persist files in Docker?

Docker has the concept of volumes for persisting data. You can map these to custom directories, but I preferred to just let docker manage the volumes as follows:

  • docker volume create --name worlds
  • docker volume create --name logs
  • docker volume create --name players
  • docker volume create --name plugins

Then we can modify our docker command a bit to use these volumes:

Docker puts these volumes physically on the host machine at /var/lib/docker/volumes/VOLUME_NAME/_data, so keep this in mind if you want to take a look at these files. Inside the running container, docker will map the /home/worlds directory and other directories to the physical location on the host machine.

Deploy our Docker Image to Azure

So far we've deployed our docker container to a local dev environment--we need this on the Internet, right? There are many ways to make this happen: DC/OS, Kubernetes, Docker Swarm, an Azure Container Registry, etc., but I wanted to give a walkthrough of the absolute simplest way. If you are somewhat familiar with ssh, you may have used a feature called port forwarding. When an ssh connection is established, you have the option to forward a port on localhost to a remote port on the destination of the ssh connection. Luckily, the docker CLI also has a mode where it can remotely administrate docker running on another machine over a network. We can use the two features together to run docker commands locally against our VM running in Azure.

To do this, first we need to restart the docker daemon on the VM. By default, the docker daemon does not allow remote administration, so we have to enable it:

Note, that this port is not open on the public Internet. We are opening the port on localhost on the VM, so that we can use SSH tunneling remotely to get to it. From here, switch to a command prompt on your local dev machine:

After running this command, you can connect locally to port 2375, it will route the traffic over the SSH tunnel and arrive at localhost inside the VM. This isn't a thing that Windows devs are used to, but you can still do all of this on Windows using Putty. SSH tunneling has been around for a long time, but if you are uncomfortable using it take a look at the Azure Container Registry instead. This is an Azure service where you can push docker images to (a private DockerHub), then pull them down to run inside the VM.

Once the SSH tunnel is established, you can set the environment variable DOCKER_HOST to :2375. After the env variable is set, any future docker commands in your terminal will talk to 2375, which happens to be our VM running on Azure. If you want to test this is working, try running docker info and you should see some information about the machine the docker cli is talking to.

To bring this all together, I wrote a tiny shell script so I don't have to run all these commands manually:

Once your VM is running the docker image publicly, you can connect to it from Minecraft: PE by going to the Friends tab, then clicking the little server button on the right. Fill out the IP and name the connection is all you need to do in order to connect:

Minecraft Connection Screen

Bugs in Nukkit

Nukkit is a bit buggy, but don't worry it is not going to harm anyone's Minecraft game on their device. The worst I've seen happen is Minecraft crash on iOS, and simply restarting the app gets you back to where you were. The biggest issue right now, is that in Creative mode, players inventories are sometimes completely empty. One workaround is to use EssentialsNK to give players' specific items, but that is alot of commands if a kid wants every item.

But wait, Nukkit is on Github! I took a look and there was already a PR about this issue. If you have already built Nukkit from source, it should be trivial to pull in this change and deploy a new docker container.

Conclusion

Hopefully this post gets you some ideas on how you can get your own kids interested in programming: they are most likely already Minecraft fanatics. At the very least, they will get into using a pseudo-CLI with in-game chat in Minecraft. If they get really into it, they could move from downloading new levels, extracting them, and restarting the server--to building full-blown plugins in Java. Now get out there and build some blocks in the cloud!

PS - the best level we've found yet: the Chambers.


comments powered by Disqus