How to Dockerize EnvyMUD
By Zachary FlowerAbout 13 years ago, I published a post on my blog titled How to Compile Envy MUD (while my archive page has it listed as the first post, it’s actually just the oldest one I’ve retained after years of prior publishing). While the process of compiling Envy MUD (or EnvyMUD) hasn’t changed much in the last decade plus, our ability to actually launch it has improved in some pretty meaningful ways.
That’s right, I’m talking containers, people!
I’m a fan of Docker. It’s a simple way to consistently build and deploy applications—and, sometimes more importantly, development environments—but what makes is particularly useful (at least to me) is its ability to spin up a decades old codebase with minimum overhead.
Enter the EnvyMUD Container
While this post isn’t technically a Docker tutorial itself, I should probably touch on some of the basics of getting started. At the root of any Docker container is the Dockerfile, a source file that defines exactly how a container should be built and run. As an example, here is a Dockerfile that I wrote to run EnvyMUD (using the source code fixes outlined in my 2011 post linked above) and placed at the root of the project directory:
# Use an official gcc runtime as a parent image
FROM gcc:latest
# Set the working directory in the container to /app
WORKDIR /app
# Copy the current directory contents into the container at /app
COPY . /app
# Install make
RUN apt-get update && apt-get install -y make bc
# Set the working directory in the container to /app/src
WORKDIR /app/src
# Run make
RUN make
# Expose port 4000
EXPOSE 4000
# Expose the player directory externally
VOLUME /app/player
# Run the program output from the previous step
CMD ["./startupSH"]
While it should be pretty self-explanatory (especially with the comments), let’s do the proper tutorial thing and break down each directive and explain what they do:
FROM gcc:latest
- This is often the very first line in any Dockerfile, and it defines a base container with which to build a new one off of. You can inherit from multiple containers, and there are security concerns that you should always be mindful of when using public containers, but both of those are beyond the scope of this post. For our purposes, we are going to inherit from the latest release of the officialgcc
container, which—as you can guess—comes with thegcc
compiler installed.WORKDIR /app
- Next, we need to set the working directory in our container. When defined,WORKDIR
simply indicates what directory you will be in when running any future commands.COPY . /app
- Here’s where the actual fun starts coming in. The next thing we are going to do is copy our entire Envy project directory (which is where the Dockerfile has been placed), into our/app
directory.RUN apt-get update && apt-get install -y make bc
- Next we need to install a few extra dependencies. As you can see, thegcc
base image is running on top of some sort of Debian-based operating system (indicated by theapt-get
command. The two things we need to install in addition to what ships with the base image aremake
(to execute ourMakefile
andbc
to do some basic math in the includedstartupSH
script… more on both of these later).WORKDIR /app/src
- Now it’s time to start mucking with the code. To do that, we first need to change our working directory to the./src
directory that we copied into the/app
directory a few steps above.RUN make
- Once in the proper directory, it’s time to compile the code. This is done with aRUN
directive, which executes a commend from inside the container (which, in this case, ismake
).EXPOSE 4000
- Getting to our finish line, we have to do some exposures. The first one is exposing port 4000, which is what EnvyMUD will listen on (and that you will connect to using your favorite MUD client… or justtelnet
… your choice).VOLUME /app/player
- The next thing we want to expose is the/app/player
directory, because what happens in the container stays in the container unless you map it to something externally. In the case of both theEXPOSE
andVOLUME
directives, we’re identifying the exposures, but they won’t take effect until we actually run the container. More on that below.CMD ["./startupSH"]
- The last piece of any Dockerfile is the command that gets run. Generally speaking, this is supposed to be a command that is not run in the background, so no daemons people. It must be run in the foreground so Docker knows when something happens (like it crashes or shuts down). ThestartupSH
command is a script that is shipped with the EnvyMUD source code, and handles things like logging, in-game reboots, and core dumps.
If You Build It…
Okay, we’re done, but just with the Dockerfile. All this does is define our container, it doesn’t actually do anything until we build it. To do that, all we have to do is run the docker build
command:
$ docker build -t envy .
I want to call out one thing: the -t
flag. This means to tag
the build, which creates an easier-to-remember reference for us to use in the next step, which is actually running our container.
… It Will Run
So we’ve got a container build… now what?
Time to game, right?!
Not quite. Now we next have to actually run it. I want to call out that the example I’m going to present will be focused on running a container in the foreground. Restarts, crash recovery, backgrounding, etc. are not going to be covered in this particular post (although I plan on writing more about how to deploy a Docker-based MUD in the future).
To run our container, we need to execute the following command:
$ docker run -v ./player:/app/player -p 4000:4000 envy
As above, let’s break down what’s actually happening here:
- The
-v
flag identifies a volume. Since we are running our container from the root of our project directory, we will be mapping the./player
directory into the aforementioned/app/player
directory in the container. This will not only preserve the player files (where individual player data is stored) outside of the container (in the event of a crash, or accidental deletion), but will also allow us to edit the player files without having to log into the container itself (which is useful when making yourself an immortal). - The
-p
flag identifies a port to expose, which in our case is port4000
. While we can absolutely map an external port to the container’s internal port4000
, for our purposes we are just going to map4000
directly into the container.
Time to Test
Alright, so we’ve written our Dockerfile, built the container, and are now running it.
Now is it time to game?!
Yes. Yes it is.
To connect to our running game, I’m going to use the extremely basic telnet
command (which may or may not even be installed on your operating system right now). To do that, all we have to do is execute the following command:
$ telnet localhost 4000
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
__ ____ ./| ./\ ____ ____
Based on / __|_/"`/ /|_______/ / | '"\_|_ @ \ By:
Envy 2.0 / /#/ | @/ _@@@@ \ @/ \#\ @ \ The EnvyMud
( see 'help / @/##/ \| .\ @ /. > \##\ @ \ Staff
envy' & / @ |###| | < .!| |!.| |##\ @ \ ( see 'help
'help | @ |###| |@ \ \ / /\ |###| @@| wizlist' )
merc' ) |@@ |###_\___ |@| \(o o)/\@\ |###| @|
|@@ .\/'/'/'/\__ \ \_\___/_/\@ \ ________/####| __@|__
|@@ | \_\_\_\ |\ \@\ | | |\@ \ | |###/ / /
|@@ | \)\)\)__| \\@ \| | |/\ @@\| |#/ / /
| @ | |_____| \@ | | |/ \ @@| | \/ /@| Mud Version
\ @ | _____| |\ \| | \/ \ / | / @| 2.0.0
DikuMud \ | |_____|__ | \ | \/ / | @|
was created \| || \ | / | |\ @@|
by: Han Henrik |___________|| ____\___| \______/___|______|@ \@|
Staerfeldt, Katja | \ / | | | | _ \ @@ \ Kahn,
Nyboe, Tom Madsen, | \/ | | | | | | | @@ \ Hatchet,
Michael Seifert, | |\ /| | |_| | |_| | @@ \ and
and Sebastian Hammer |___| \__/ |___|\_______/|_________/ | Staff
( see 'help diku' ) email: envy@uclink3.berkeley.edu
WWW Home Page: http://www.teleport.com/-morpheus/envy.html
\_tilde
State thy name:
And that’s it! Enjoy your new/old containerized MUD.