Migrating Existing Services to Docker, Part One

I've spent the last 2 weeks or so 'revamping' my web stack. Before, the whole thing looked something like the bowl of spaghetti above when drawn out. I had 4 VPSs, running about 10 different applications, so my server utilization was terrible. I even had one server that was only running HAProxy.

Step 1. The Audit

The first, and probably the most important thing to do when you're considering migrating any servers is to perform an audit. See what the servers are actually running, and verify that you know what you'll need to move so your services won't unexpectedly go down. For me, this step took a little while, since I painstakingly audited each server. I made a list of running services on each, and verified that I knew where to find configuration files and anything else required to duplicate the services.

As I went along, I kept a simple text file going, which listed hostnames and what services were running on them, like so:

warwick:
  - akpwebdesign.com website
  - [customer] website
  - Teamspeak 3
  ...
leblanc:
  - HAProxy
  - nothing else?? really?
AKP48:
  - AKP48 (Node.js)
  - Bravify version tracker (Node.js)
  - LoL static data (Node.js)
  ...
irc:
  - weechat
  - znc

Once I had a list of all the various services I was currently running, I noticed that I had some services running that I simply didn't use anymore, so I went through the list and removed those. The next step for me, was deciding which service I wanted to migrate first. I decided to move the Teamspeak server first, as it was the application I was most alright with experiencing some downtime if necessary.

2. Migrating the first service to Docker

In order to move forward, I needed to either find or create a Dockerfile for Teamspeak 3. I searched around a bit, but ended up not really finding anything premade that fit my needs, so I settled for creating my own from bits and pieces of Dockerfiles I found in my search. Here's what I created:

Dockerfile

FROM akpwebdesign/alpine-glibc

ADD http://dl.4players.de/ts/releases/3.0.12.4/teamspeak3-server_linux_amd64-3.0.12.4.tar.bz2 /tmp/teamspeak.tar.bz2

RUN mkdir /opt && \
  tar xjf /tmp/teamspeak.tar.bz2 -C /opt && \
  mv /opt/teamspeak3-server_* /opt/teamspeak && \
  rm /tmp/teamspeak.tar.bz2 

EXPOSE 9987/udp 10011 30033

ENTRYPOINT ["/opt/teamspeak/ts3server_minimal_runscript.sh"]

Basically all that Dockerfile does is starts from a fork of Alpine Linux that contains a functioning glibc (because Teamspeak 3 requires it), downloads the Teamspeak server files to the right location, then exposes the proper ports (for us to bind to later), and sets the command that Docker will run when the container is started. Once you've built this image (using docker build -t [username]/teamspeak3 . in the folder that this Dockerfile is contained), you can start the image and have a brand new Teamspeak server running in moments. Note: A previous version of this post used orimani/alpine-glibc as the glibc-compatible Alpine image, but the author of that image has since taken it down.

The next step for me was creating a start script for the Teamspeak server, as I want it to run separately from the rest of my stack (if I restart my web servers, there's no reason for Teamspeak to go down.)

For this, I created a really simple Bash script that starts the server and sets a flag so that Docker will always restart it if the container crashes or if the host restarts for any reason. The script also sets up port forwarding from the host network to the Docker container, gives the container a name (for easier administration later), and sets up Volumes for each of the folders that Teamspeak stores configuration information in, so that I can persist the Teamspeak configuration across container restarts and rebuilds. This also allows me to migrate the data from my old Teamspeak server much more easily.

start.sh

docker run -d --restart=always --name teamspeak3 \
	-v /srv/teamspeak/files/:/opt/teamspeak/files/ \
	-v /srv/teamspeak/logs/:/opt/teamspeak/logs/ \
	-v /srv/teamspeak/ts3server.sqlitedb:/opt/teamspeak/ts3server.sqlitedb \
	-p 9987:9987/udp -p 10011:10011 -p 30033:30033 \
	[username]/teamspeak3:latest

Once I created the startup script, I tested the script before migrating my data over from the old server. I realized that when the host folders (under /srv/teamspeak) don't exist, Docker will create them, but they will be owned by root. If you don't want this, be sure to create your folders before you start your Docker container with volumes set up.

After I verified that the container was able to successfully load, I migrated over the data from my old server using SFTP, started the container pointing at my migrated data, changed my DNS records, and waited about an hour (the TTL on my DNS records) before shutting down the old Teamspeak server for good. Clients that were connected to the old server were disconnected and reconnected to the new host in about 2 seconds, and the Teamspeak migration was complete.

3. Next Steps

As I'm sure you've noticed, we've now successfully moved only one of the many services that were running on my servers. In the next post, I'll go over the process of using docker-compose to migrate various web services quickly and easily.

Austin Peterson

Read more posts by this author.