Minecraft and IBM Cloud, Part 1
Running Minecraft servers within Docker
This content is part # of # in the series: Minecraft and IBM Cloud, Part 1
This content is part of the series:Minecraft and IBM Cloud, Part 1
Stay tuned for additional content in this series.
What single Java™ program has been used more often than any other Java program by a whole order of magnitude? Hint: It has sold over 70 million copies since it was first released in beta form in 2009. Microsoft recently purchased the company that created this program for $2.5 billion. An enormous development ecosystem has grown up around this program, and hundreds of thousands of people have been inspired to learn programming in Java — just so they can develop extensions for this program.
As you probably guessed from the title, that program is the game Minecraft, and amazingly, those hundreds of thousands of developers are predominately children and teens. Minecraft has been a phenomenal success in the marketplace, and part of that is due to its unique structure: you purchase the client (the face of the game) commercially, but the servers are open for extension. Anyone can start up their own game server — leading to thousands of different options as people create unique communities of Minecraft builders across the Internet based on their interests and likes and dislikes.
But not only is the server open for download to start your own server, it's also open for extension by people who want to build their own add-ons to the game. In this series, I'm going to show how to use plugin development for Minecraft to teach some specific development technologies and principles.
You'll see how you can easily test the plugins you write locally, and then take the exact same configuration and run it unchanged on a cloud server that your friends ("beta testers") can get to and try out. You'll do this by using two specific technologies — Docker and IBM Cloud Containers — and along the way you'll see how to also set up Eclipse and the IBM Cloud DevOps Delivery Pipeline service to build your plugins and even how to set up team development with git.
Introduction to Docker, IBM Cloud, and containers
Before I get too far, I'll decode what I just said for any readers who may be familiar with Minecraft and Java, but not Docker and IBM Cloud. In this first tutorial, I'm going to focus on how to set up a local Minecraft server within Docker, so let's start with an introduction to Docker.
Docker is probably best understood by an analogy. Docker is often referred to as "a shipping container for code." So what does that mean? Think about what a shipping container is — you see them all the time — they're those rectangular metal boxes that you see on trucks, train cars, and especially on massive ships called container ships that carry hundreds of them at a time between ports.
Shipping containers changed the way that things are moved between countries. So let's say you're manufacturing smartphones. You want to move thousands of them from your factory in Korea to an electronics store in Toronto, Canada. In the past, those boxes of phones would have been packed and unpacked perhaps dozens of times as the boxes were moved from truck to truck and ship to ship, with lots of stops in warehouses along the way. Every time a box was stacked and unstacked, there was a chance for it to be damaged or lost. What's more, the work of stacking and unstacking is time-consuming and costly. Today, boxes are instead loaded into a big steel container at the factory once, and the container is moved by cranes from truck to ship to truck to the electronics store — all without being unpacked or opened at any time.
Docker does the same thing for code. The way we build and run code (such as Java) today is that you develop your code in a development environment. You then move the source code to another system where it's compiled and "built" into a runnable system (a WAR file or JAR file). However, that runnable code needs a lot of infrastructure (such as a particular Java runtime, maybe a particular application server, and a bunch of open-source JAR files) in order to run. So all of those things need to be moved and reassembled again and again in your unit test server, in your system test server, and in each of your production servers. Each stage introduces the possibility that something can go wrong in the "packing and unpacking" step. You could choose the wrong version of something or you could forget something along the way — leading to bugs that are sometimes very hard to track down.
Docker allows you to package an entire running system — all the way down to the operating system — into a single, lightweight package (not surprisingly, called a container) that you can then move from place to place. Nothing is ever left out, and nothing is ever broken as it moves around.
IBM Cloud comes into this by enabling you to take your Docker containers and run them not only on your local machine, but also in the cloud for others to access. IBM Cloud Containers, based on Kubernetes, provide an easy path for you to do that.
In this tutorial series the first two things we're going to focus on are how to make a Minecraft server run locally in Docker, and then how to make that same Docker container run in IBM Cloud.
Purchase and install the Minecraft client
As I mentioned earlier, the genius of Minecraft is that they sell the client to millions of players, and have opened up the source code so users can expand on the game and set up Minecraft servers for multi-user game play. The first step you have to take is to purchase and install the Minecraft client. You can find instructions for how to install the client on most operating systems on the Minecraft site.
We'll be developing and running our server in Ubuntu, but you can run the Minecraft client on any supported operating system. All you need to know is the IP address of the server, and your client needs to able to contact that IP address. In fact, I purchased the native Mac Minecraft client and then connected to my development servers running inside a VMWare virtual machine on my desktop from that client.
Install and configure Docker
The next thing you need to do is download and configure Docker and the other pieces of software that you'll need to make this entire set of instructions work. Let's start with Docker. In this series I'm going to show you how to install and configure it for Ubuntu Linux 16.04.2 LTS. If you want to do this on a different operating system, refer to the Docker install instructions for the appropriate platform.
Although we're using Ubuntu 16.04.2 LTS, you could use any operating system that supports both Docker and the Cloud Foundry tools from Cloud Foundry and IBM, which includes both Windows and Linux. I recommend using Ubuntu because in my experience it's both cleaner and simpler to do this kind of development directly in Linux. Luckily, it's easy to download and create a Linux image in either VMWare or VirtualBox in many other operating systems, so I'd recommend you do that as your base. When you do that (or if you're just installing Ubuntu Linux on your hardware), I'd also recommend that you configure it to use at least 2GB of RAM and 2 cores — anything less than that may cause some of the builds we'll do later to fail.
In this example, I'll show you how to configure and run Docker inside an Ubuntu image that runs within a VMWare environment. If you choose to use Virtualbox or another Hypervisor, some of the tasks that happen outside of Ubuntu will be slightly different.
Install Docker in Ubuntu
- Enter the following commands into the "terminal" application in
Ubuntu to install the latest edition of Docker Community Edition:
sudo apt-get -y install \ apt-transport-https \ ca-certificates \ curl
- The first command sets up your Linux environment with a few needed
updates, including the ever-helpful
curlcommand. The next few commands modify your apt-get installation with information on a new repository hosted at docker.com that allow you to install and update Docker from the Ubuntu
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" sudo apt-get update
- Now that your repository is set up and your Ubuntu system is up to
date, the next command will download Docker and install it into
sudo apt-get -y install docker-ce
- To validate your Docker installation, try the following command:
sudo docker run hello-world
If you see a message indicating that your Docker installation is working successfully, you are done! If not, you may want to go back and try running the previous steps again.
Install the IBM Cloud command line tools
Next you need to install the IBM Cloud command line tools. We won't use those in this first tutorial, but we'll begin using them in the third tutorial, and it's a good idea to install them now while you're setting up your Ubuntu image. If you're on any other platform, you'll need to go to http://clis.ng.bluemix.net/ui/home.html to install the correct tools in your browser, but this procedure will work in Ubuntu.
- At the command line, type the following line to download the IBM Cloud
command line tools:
wget "http://public.dhe.ibm.com/cloud/bluemix/cli/bluemix-cli/Bluemix_CLI_0.5.4_amd64.tar.gz" -O Bluemix_CLI.tar.gz
- Type the following at the command line to extract the tools:
tar -xvf Bluemix_CLI.tar.gz
- Next we need to run the installation script to install the IBM Cloud CLI
cd Bluemix_CLI sudo ./install_bluemix_cli
- After you’ve installed the IBM Cloud Command line tools, you need to
install the IBM Cloud container service plugin in order to start and
manage Kubernetes clusters. Do this with the following command:
bx plugin install container-service -r Bluemix
- Finally, you’ll need to install the kubectl APIs. Do this with the
curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl sudo chmod +x ./kubectl sudo mv ./kubectl /usr/local/bin/kubectl
- Now that you've downloaded and installed both Docker and the IBM Cloud
tools for Kubernetes (which will allow you to do some interesting
things with both!), you're ready to download the sample code that
we'll examine in the rest of this tutorial and in the following two
tutorials. The sample code is available in git, and you can download
it with the following command:
git clone https://github.com/kgb1001001/minecraft-project.git
About the basic Minecraft Dockerfile
You can run Docker in a couple of different ways. It's possible to simply start a container in Docker and begin using it, then start making changes to the running container and save those changes as a new image file. However, you'll remember that one of the reasons we're interested in Docker is to avoid the problems created by making manual changes, so that approach doesn't really help us! Instead, we're going to use a different approach that Docker also supports, which is the Dockerfile. A Dockerfile is a set of commands that builds up an image in a repeatable way. That way, if you execute the Dockerfile on different machines you always get exactly the same result. Think of it like a "bill of lading" that tells you what's inside your Docker image (and thus inside the containers you're building).
The purpose of our first Docker file is simply to see how easy it is to get a Minecraft server up and running locally in Docker. So let's look at the Dockerfile first. To view the Dockerfile, type the following commands:
cd minecraft-project cat minecraft/Dockerfile
The contents of the Dockerfile are shown below. Let's walk through the pieces one at a time.
# Version 0.0.3 # This Docker file builds a basic minecraft server # directly from the default minecraft server from Mojang # FROM ubuntu:16.04 MAINTAINER Kyle Brown “email@example.com” RUN apt-get update RUN apt-get install -y default-jdk RUN apt-get install -y wget RUN mkdir minecraft RUN wget -O minecraft/minecraft_server.jar \ https://s3.amazonaws.com/Minecraft.Download/versions/1.12/minecraft_server.1.12.jar RUN echo "eula=true" > eula.txt CMD java -Xms512m -Xmx1024m -jar minecraft/minecraft_server.jar nogui EXPOSE 25565
- The first command in the Dockerfile (after some helpful comments
prefaced by hashtags) is
FROM. It shows what image this Docker image is to be built off of. One of the great things about Docker is that you build images from other images, so if someone else wants to take your image and extend it in some way, it's easy to do that within certain limits. In our case, we're building the Docker image off of the latest Ubuntu 16.04 Image from the Dockerhub repository.
MAINTAINERsimply says who is responsible for writing and maintaining this file.
- The next three commands are the interesting ones. A
RUNcommand is exactly the same as executing a command at the Linux command line. So what I'm doing in the next three commands is updating the current Ubuntu image to the latest set of fixes and updates from Ubuntu, and then installing the default Java JDK and the wget utility. Next, I create a directory named minecraft and download the minecraft_server.jar file from the Amazon S3 storage site where it is hosted and place it in the minecraft directory.
- My next command creates a file named eula.txt that shows that the user of this server has accepted the EULA agreement.
- Now we get to the heart of what we want to do. The
CMDinstruction executes whatever comes after the
CMDkeyword when the Docker image created by this Dockerfile is run. That's important — the
RUNinstruction executes immediately when the Dockerfile is built or turned from a Dockerfile of instructions into a final Docker image. The
CMDinstruction doesn't run until you actually start up the container that is created from this image.
- The final instruction is
EXPOSEkeyword indicates which TCP/IP ports viewed from inside the Docker container should be accessible from outside the Docker container when it runs. By default you can't access any of the ports inside the Docker container — this means it's secure by default and you have to choose what you want to expose and when.
As I mentioned earlier, there are two steps to get a running container: you build the Dockerfile into an image, and then run the image as a container. But first you have to decide on a name for your Docker repository. Usually I use the same as my login name in Ubuntu, so whenever you see <repositoryname> in later instructions, replace it with your login id (for instance, mine is "kbrown").
- Let's start by issuing the following commands at the terminal:
cd minecraft sudo docker build -t="<repositoryname>/minecraft112" .
Note the important dot (.) at the end of the last command. This command creates a brand new image from the instructions inside the Dockerfile found in the current (".") directory — the same Dockerfile we were looking at earlier.
This results in all of the steps being executed one at a time, with a lot of intermediate information about the steps being output as a result. This command can take several minutes or more depending upon the speed of your network connection because many of the commands in the Dockerfile (such as the apt-get commands) need to download new versions of software into your Docker image. If it all works correctly, the last line you should see after the successful execution of
RUN java -jar minecraft/BuildTools.jaris:
Successfully built 764c25d251f6
Note that the 12-digit id on the end of your message will be different, but you do want to see a "Successfully built" message.
- Now the moment of truth comes — we're ready to see if you can
run a Minecraft server inside Docker! To try that, issue the following
command, again substituting your login name for
sudo docker run -i -t -p=25565:25565 <directory>/minecraft112
When it's done, you should see a message like this at the bottom of the console:
[14:10:43] [Server thread/INFO]: Done (6.086s)! For help, type "help" or "?"
- We're almost to the point that we can try out our new Minecraft server running in Docker, but there's one more piece of information we need to find out. When you run a virtual machine in VMWare, it runs at a network address that is visible to your host machine but not to the rest of the world. However, in order to contact any programs running at that network address you have to first find it. Because we're occupying our current terminal window by running Minecraft inside Docker, open a second one by going to the Ubuntu Terminal's File menu and selecting Open Terminal.
- In the second terminal window, type the following command:
ifconfig eth0 | grep “inet addr”
When that command executes you'll see something like the following output:
inet addr:172.16.103.242 Bcast:172.16.103.255 Mask:255.255.255.0
The Internet address immediately following
inet addr:is the one that we'll use in our Minecraft client out on the host operating system to contact our new server. Note that if you’re using a newer version of VMWare, instead of
eth0the default network adapter is called
ens33. If running this command gives you a “device not found” error, change the first command to:
Ifconfig ens33 | grep "inet addr"
Set up the Minecraft client
OK, now comes the fun part!
- Start up your Minecraft client, and click the big play button on the first screen (the one that gives you information about upcoming releases, and so on).
- In the Minecraft startup screen that follows, choose Multiplayer.
- On the "Play Multiplayer" screen that follows, click Add Server.
- On the Add (or Edit) Server Info screen, enter the IP address that you
found in your VM earlier, and make sure to add
:25565at the end of the server IP network address (25565 is the port that we expose from our Docker container).
- Click Done, then on the "Play Multiplayer" screen,
click Join Server for your new server.
You should be running on your new local server in Docker!
- After you've played around for awhile, and perhaps built a few things and slain a few monsters in Minecraft, you can go to the Ubuntu Terminal window that you're running your server in (in Docker) and press control+C to end the Docker container.
About the Spigot Dockerfile
It's certainly fun to be able to host your own local Minecraft server as shown above, but that's not really the point of this tutorial series. The problem is that Minecraft as it comes directly from Mojang is not very extensible on the server side. What I want to show you in this series is how to take advantage of some of the Watson services provided by IBM Cloud, which means we need a Minecraft server that's open for modification. In 2010 a group of developers built an API for exactly that kind of server — they called that the Bukkit API, and it allows you to build your extensions as Minecraft plugins. There have been several implementations of the Bukkit API, but the most common one is the Spigot implementation. Spigot is not only the most widely modded Minecraft server version, but it has been improved in several ways to achieve more efficiency and better performance.
We'll use Spigot as our server, so rather than basing the rest of our examples on the generic Minecraft Server in our previous example, we'll use the Spigot server going forward. Let's examine the Dockerfile shown below and walk through the differences to see how this would be done.
Note that the commands in this Dockerfile are adapted from the build instructions found on the Spigot website. If you run into issues with this Dockerfile, possibly caused by changes, refer back to that page — it's the authoritative source of information about how to build Spigot servers.
Let's look at the new Dockerfile:
# Version 0.0.3 # This version builds a spigot server # using the recommended build strategy for spigot # This is advantageous in that it's better for plugin development # and fits well with the Docker approach # FROM ubuntu:16.04 MAINTAINER Kyle Brown “firstname.lastname@example.org” RUN apt-get update RUN apt-get install -y git RUN apt-get install -y default-jdk RUN apt-get install -y wget RUN mkdir minecraft RUN wget “https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" -O minecraft/BuildTools.jar RUN git config --global core.autocrlf input RUN java -jar minecraft/BuildTools.jar –rev 1.12 RUN echo "eula=true" > eula.txt CMD java -XX:MaxPermSize=128M -Xms512m -Xmx1024m -jar spigot-1.12.jar nogui EXPOSE 25565
Several aspects of this file should look familiar to you because they're the same as in the basic Minecraft server example we looked at earlier. For instance, the commands to update Ubuntu Linux and install the default JDK are the same. However, this Dockerfile also installs the git tool, which we've already used in Linux.
The real change happens after the line that performs the
to fetch the file from the SpitgotMC servers (instead of the Amazon S3
servers hosting the vanilla Minecraft server). What we fetch back from the
SpigotMC server is not a simple JAR file that we then run, but an
intermediate file that we use to actually build the final JAR file. This
has the advantage of allowing us to always obtain the most recent version
of the Spigot server without having to change additional code in our
Dockerfile. We also have to make some changes to the default behavior of
git, which is used in the build process, in order to deal with the
differences in the way carriage returns/line feeds are handled between
MicrosoftWindows (where most Spigot development occurs) and Linux.
In order to see how the build process is different, issue the following commands in the same directory from which you ran the Minecraft container earlier.
cd ../spigot sudo docker build -t="<repositoryname>/spigot112” .
Note that it will take you a long time to build this file! But the good news is that the layered nature of Docker is going to mean that Docker doesn't have to build all of the intermediate steps every time. In order to see what I mean, execute that command one more time. It should happen much faster! The reason is found in the output:
Step 7 : RUN wget "https://hub.spigotmc.org/jenkins/job/BuildTools/lastSuccessfulBuild/artifact/target/BuildTools.jar" -O minecraft/BuildTools.jar ---> Using cache ---> fdf1e118f298 Step 8 : RUN git config --global core.autocrlf input ---> Using cache ---> 5a130309c12c Step 9 : RUN java -jar minecraft/BuildTools.jar ---> Using cache ---> 516e9fb02428
Note that after each command it says
Using cache. When Docker
executes a step in a Dockerfile, it caches the result as a new image. In
Part 2, when we start building plugins for Spigot, you'll see
that we don't have go through the time-consuming parts of the build
process — updating Ubuntu and building the Spigot file —
To see if you've been successful, issue the following command:
sudo docker run -i -t -p=25565:25565 <repositoryname>/spigot112
If you see a message like this:
[21:16:17 INFO]: Done (11.170s)! For help, type "help" or "?"
You have successfully built your second Minecraft server — this one extensible! Try connecting your Minecraft client to the new server (it's at the same network address as the previous example, so you don't have to change that) and see if everything works.
You've now completed part one Part 1 of this tutorial series. You've learned how Docker works, and how it's useful for Minecraft developers, and you've seen some detailed examples of a couple of Dockerfiles for creating local Minecraft servers. In Part 2, you'll learn how to create plugins for the Spigot server and you'll see an example of one running inside the server.
- Learn to Program with Minecraft Plugins
- Best practices for writing Dockerfiles
- Spigot community wiki
- IBM Cloud essentials course
- More how-tos on Docker
- More how-tos by Kyle Brown