Rob's Ramblings

Sunday 29 March 2015

Minecraft, Spigot, Bungeecord and Docker!

Tiddler has lately got really "into" Minecraft, spending rather too much time watching YouTube videos (Thanks, Stampy!) and creating her own worlds..

From my point of view, I quite like the idea of being able to add custom mods, and the (mostly) friendly and open ecosystem that has developed around it.  It reminds me very much of my time spent in, and writing patches for, WorldsAway, oh god, nearly 20 years ago... That was a fun time... it's a pity they didn't operate a bug bounty - I found quite a few!

Anyway, back to Minecraft.  She wanted to play with me, so we started off by my downloading and running the official "vanilla" server on one of the linux boxes.  It works, but is limited.  So I go find out that there is lots of alternate server software available. We end up with a two or three on different ports.  But the little box I was running it on really couldn't cope - a low end pentium that usually just ran an asterisk server!

So lets upgrade.  An impulse eBay purchase a few months back had bought me a Dell Poweredge 1950.  A rack server, which was currently sitting idle after an initial play.  With Dual 4-core Xenons and plenty of RAM, it was obsolete, but still probably the most powerful machine in the house! It was just itching to be used.

Now, with the prospect of letting the public into the minecraft server at some point, and with an urge to "do things properly", I wanted some form of isolation for the server processes.  Running a full blown VPS or virtual machine felt a little overkill, so I started looking around at what was recommended.  Now, years back, I used to use jails on FreeBSD, and it seems that the current flavour of the month builds on that concept, and is called Docker.

Now Docker does seem like the bees knees.  Reasonable isolation of a process with very little overhead, and in an ostensibly portable fashion  Let's use that then.

Several days of playing and one steep climb up the learning curve later and I'm pretty familiar with the basics.

Now... Minecraft. ... How to do multiple servers or worlds easily. This brought me to Bungeecord.  It is basically a proxy, that will control and switch users between servers.  That's better - no need to set up an entry on each client for each server - just do the one, and we can change servers on-the-fly once logged in, and I control all the config from the server itself.  This seems to be closely tied to the Spigot server sofware, so we might as well use that for the servers.

OK. The configuration.....

Firstly, we'll use data containers for all the, well, data.  This means I can delete and re-run things without having to re-configure the servers all the time!

We're going to be supporting multiple worlds, so in this example, we will call them simply world0, world1, world2, etc.  You may wish to use more descriptive names.

Create a data container..

sudo docker run -t -i --name world0-data -v /minecraft busybox true

Create a server..

sudo docker run -t -i --volumes-from world0-data -e EULA=true --name world0-server  nimmis/spigot

That should all fire up and eventually leave you at a > prompt - type stop to exit.

Now the more discerning among you will have noticed that we didn't specify any ports to reveal on that run command, so nothing will be able to connect to the server!  I know; that's deliberate.  We don't want just anybody connecting, after all.

Next, we need to adjust the config on the server, because bungeecord requires the individual servers to be in offline mode - it handles the authentication itself.   The config will be in /minecraft/, which is within the data container, so it's easy to create temporary access

sudo docker run --rm -t -i --volumes-from world0-data centos /bin/bash

cd /minecraft


You can also use the same technique to install any mods, ready-made maps, or other customisations.  If you need to install any extra programs into the container to allow you to fetch these, e.g. wget, ftp, scp, etc., then that's fine - you are working in a temporary container, and you will not affect the programme container, keeping it lean.  Only items you place into /minecraft - the data container - will be preserved!

Repeat for as many worlds/servers as you wish to create!

Now to use bungeecord to link them all together,

Create a data container for that too..

sudo docker run -ti --name bungee-data -v /bungeecord busybox true

And then create the server container..

sudo docker run -ti -p 25565:25565 --volumes-from bungee-data -e EULA=true \
  --name bungee --link world0-server:world0 \
  --link world1-server:world1 --link world2-server:world2 \

This time we are exposing a port to the world!

Make sure you add a --link for each server you created and wish to give access to.

After it fires up, and finishes creating everything, key "stop" to exit.  The rehf27/bungeecord image drops to a bash prompt on exit, so you can edit the config directly here.

vi /bungeecord/config.yml

If you need to do anything more exiting, or adjust things later, you can use the same technique as before to access the configuration from a temporary container -

sudo docker run --rm -t -i --volumes-from bungee-data centos /bin/bash

So, edit Bungeecord's config file, and change as appropriate.  The main things to alter are the listener ports - change to 25565 (as per the run command above) and the list of servers:

    motd: '&1Lobby Server'
    address: world0:25565
    restricted: false
    motd: '&1A New World'
    address: world1:25565
    restricted: false
    motd: '&1Another World'
    address: world2:25565
    restricted: false

- the "world0", "world1", etc in the address field comes from the alias, the part after the colon in the --link options on the run command.  Each of these is placed in the /etc/hosts file on this container, so that you can locate them, and specifying the link grants the access, so you don't need to open ports!

Save, and exit.

Now. start all your servers if they are not already

sudo docker start world0-server
sudo docker start world1-server
sudo docker start world2-server
sudo docker start bungee

Then point your minecraft client at your server IP, and have fun!

If you add or remove worlds later, you will need to crate a new bungeecord container with the new links.  Delete the current one..

sudo docker rm bungee

and create the new one, eg..

sudo docker run -ti -p 25565:25565 --volumes-from bungee-data -e EULA=true \
  --name bungee --link world0-server:world0 \
  --link world2-server:world2 \
  --link world3-server:world3 --link world4-server:world4 \

Because we used a data container, all existing configuration, mods, plugins, etc., will be preserved!  You can open a temporary container to edit the config file for the changes to the list of hosts just as we did before.

One caveat - stopping and starting a server will give it a different private IP address, which means the bungeecord server won't be able to connect to it - you will have to restart the bungee server to have it update things.  (There may be an option somewhere to preserve the private IP assigned to a container, but I've not yet looked for it.  If there is, use it!)

This took me several days of fun and games to tie down; I hope it's useful to others.  

A quick PS - I've not gone into depth on how to configure Spigot and Bungeecord;  just the essentials relating to their interaction within docker.  There are many guides and help resources out there that will assist you there.

For those of you that outgrow a single physical server, having your individual services Dockerised (is that a word yet?) means you can easily transfer them to another server with virtually no changes!  This useful link shows how to configure "one" docker across multiple physical hosts!

There also seems to have been work to add new --links to running containers; with a little bit of extra code to  interrogate the environment, this should make it possible to dynamically create the Bungeecord config file (or the hosts list at least) and avoid some of the additional configuration steps necessary to add new servers.

There are lots of ways to improve all this, but the aim of this write-up was to show how I got things working in the first place! :-)