Docker, Minecraft and Bukkit…err CanaryMod

Last Friday was a “Spring Holiday” at Jive but all my little dudes were in school so I figured it’d be a great day to go into work and actually try to do something technical for a change of pace. My oldest has become completely absorbed in Minecraft and offered up that he’d really like to learn how to program mods a couple weeks ago so we bought the Kindle version of this book, which suggested the use of Bukkit. Simultaneously, I really wanted to get my hands a teensy bit dirty with Docker so I spent the time on Friday trying to get the two to place nicely together so that I could deploy a Docker image of Bukkit to my hosted server. Sidenote: I’m well aware of the fact that there are tons of Docker images already built for Minecraft, I personally learn faster and better by doing rather than reading.

My personal laptop is running OSX so the first order of business was getting Docker installed there. Docker doesn’t actually natively run on Macs so I installed boot2docker, which I think is a little bit Inception-ish (VM inside of a VM?) but seems to work just fine. After that I ran through this tutorial:

https://www.docker.com/tryit/

which was / is really well done. Not sure if there’s a tool that they used to build the interactive console but either way, great work on their part. I got my Docker.io account setup (https://hub.docker.com/u/ajohnson1200/) and then started down the path of trying to create a Docker image for Bukkit.

As I was investigating Bukkit as a development server I came across this presentation:

which forced me to call an audible at the line, didn’t look like Bukkit was going to be a valid platform to develop on. Turns out that the book I mentioned above has a second version, which instead of using Bukkit, uses CanaryMod, which does allow for custom Java plugins and doesn’t have licensing problems like Bukkit.

After going through the tutorial (which was really basic), my first thought / approach was to spin up a simple image so I started using this one:

https://registry.hub.docker.com/u/tianon/true/

and then using the console to install packages into that and to then save and publish it. But that was a rookie move, Docker has a much better way of creating images via Dockerfile, where you write a recipe for the image that you want to create and then let Docker build that from the recipe. So then I spent a bunch of time looking at some of the existing Minecraft (Bukkit or CanaryMod) Docker images that exist in the wild, examples:

https://github.com/mbrevoort/docker-minecraft-bukkit
https://github.com/tclavier/docker-scriptcraft
https://github.com/chrisabrams/docker-minecraft-with-bukkit

all of which were helpful. Became obvious that I’d use a Dockerfile and some kind of startup script and so after thinking about everything for a bit I ended up with a Dockerfile that looks like this:

from dockerfile/java:oracle-java8
env DEBIAN_FRONTEND noninteractive

add canarymod.jar /opt/minecraft/
add start /start
run chmod +x /start

volume ["/minecraft/"]
cmd /start

First line indicates that I want to base my image on the Java8 image (right?), second line puts the OS into noninteractive mode as documented here. Next I take the jar file that I downloaded from the CanaryMod site and add it to /opt/minecraft and alias my startup script to /start and give it the proper permissions. I create a mount point using the volume instruction which I’ll use later as the place that CanaryMod will start it’s data and then finally a cmd instruction to initialize the container, which leads to… a startup script that looks like this:
#!/bin/bash
set -x
set -e
cp /opt/minecraft/canarymod.jar /minecraft
cd /minecraft

/usr/bin/java -Xmx4096M -jar canarymod.jar
First two lines I copy / pasted from another startup script without knowing what they did but just read up on them via this nice blog post so basically we have some nice defaults for the startup script. Next, I copy the jar from the location that it exists in the image into the volume defined in the Dockerfile and then startup the app on the last line. When CanaryMod starts up, it’ll automatically create a bunch of directories and a couple of files:

canarymod.jar config db dbadapters eula.txt lang logs pluginlangs plugins usercache.json worlds

one of which you’ll need to change (eula.txt) before it’ll complete the startup process. I left that as a manual process that you’ll need to do if you use my Docker image (which you shouldn’t, mine is very basic). Once I had those written I built the image:
docker build -t mycanaryapp .
and then I decided that I didn’t like the name so I changed the name:
docker tag mycanaryapp:latest ajohnson1200/canarymod:latest
and then I pushed it to the Docker repo:
docker push ajohnson1200/canarymod
After that, I logged into my hosted server (which has been running for years on Rimuhosting) and attempted to install Docker. Docker wouldn’t work with the kernel I had installed so I had to update that and then got Docker installed in a couple minutes, pulled my image:
docker pull ajohnson1200/canarymod
and then started up the container:
docker run -v /usr/hosts/canarymod:/minecraft -p 25566:25566 --rm ajohnson1200/canarymod
binding the external port 25566 to the same internal port since I another Minecraft server already running on 25565 although now that I think about it, I think I could run CanaryMod inside the container on 25565 and just bind the external port to 25566 and not have to change the config that lives inside /minecraft. I’ll have to check that out.

Not sure if I took the right approach or not but the big thing that I didn’t spend time thinking about was storing data inside of the container instead of persisting the data on the volume outside of the container. I *think* that they way I’ve done it actually makes it possible to continually update the CanaryMod distribution while not touching the data that it stores, which is nice.

All in all, a fun exercise and I learned a bunch. If you’re looking for a CanaryMod image, don’t use mine. 🙂

Other links:

Leave a Reply

Your email address will not be published. Required fields are marked *