One of the major announcements last week at DockerCon 2017 was LinuxKit, a framework for creating minimal Linux OS images purpose built for containers. Docker has been using the tools that make up LinuxKit for some time and the products derived from the tooling include Docker for Mac.
Sounds cool, and the best way to learn about a tool is to dive into using it! Given the extra time that I had on the plane home from Austin I did just that and would like to share with you an easy way to get started using LinuxKit.
To get going you’ll need a few things:
A 2010 or later Mac (a CPU that supports EPT)
OS X 10.10.3 or later
A Git client
Docker running (In my case, 17.04.0-ce-mac7 (16352))
Let’s get started!
First, we’ll need to install xhyve. Xhyve is a hypervisor which is built on top of OS X’s Hypervisor.framework that allows us to run virtual machines in user space. It is what Docker for Mac uses under the hood! There are a couple ways to do this, the easiest is to use Homebrew. Fire up your favorite terminal and install:
Once you have that complete, see if it works by
If you get a response with the various available flags, you are ready for the next step.
Building the moby tool
The next step in getting started is to build the moby tool. This tool is what will provide the functionality to read in the yaml we will specify later, execute the various docker commands to build the Linux OS, and if you’d like, run the image. In this example, we’ll be using xhyve to run the image rather than project moby’s HyperKit. I found that building the kernel and initrd images takes quite a bit less time than a qcow image, so if you need to iterate quickly xhyve is the way to go.
First, cd into whatever workspace you’d like to use, and clone the LinuxKit repo:
Then, make and install the moby binary:
Once you have this complete, you should be ready to build your first Linux image using LinuxKit.
Customizing and Building a Linux image
With the prerequisites out of the way, let’s build our first image. The LinuxKit project includes a few examples, some of which were demoed at DockerCon. Rather than getting super complicated out of the gate, let’s build a simple image that fires up a redis instance on boot.
First, you’ll need to start with a yaml file that describes your Linux image. Pulling from the examples, we’ll take the base docker image and add an entry for redis:
Let’s quickly examine this file. Without getting into all of the details (which are available on the LinuxKit git repo) we’ll focus on the major blocks. The beginning of the file spells out the “base” Linux docker image that defines the kernel and kernel command line options. Next, the file describes how the base OCI complaint LinuxKit images that are required for init. After that, the file describes how more base images that will be run by runc sequentially before any other services are started. Next up are the services (again OCI compliant images) that will be started by containerd, which are meant to remain running. And last, the output files which moby will create as part of the build process. With the base file created, let’s build our image!
You’ll see output that looks like this:
And that’s it! Here are the files that are created:
The raw kernel image (linux-redis-bzImage)
The init ramdisk (linux-redis-initrd.img)
The commandline options you'll need to provide to xhyve in a file
Running the LinuxKit output with xhyve
Now that we’ve built the image, let’s run it! First, we’ll have to create a script that tells xhyve how to instantiate the image as a virtual machine. Now, I did say before that if you wanted to, you could have set the moby tool to output a qcow image, and then use moby run to run the image as a VM. I’d rather use xhyve, as this is how other non-LinuxKit operating systems can be run on the Hypervisor.framework. Let’s do it.
We’ll need one thing to run our image: A script that defines to xhyve the parameters for building our virtual machine. The main items needed in this script are what we got from the moby build process. Here’s and example to fire up our redis server:
Once you have the file created, make it executable and run it. A couple things to note at this point:
If you have any VPN running to secure your connection to a wireless network, etc., shut it down. There are some known issues with routing traffic when the VPN is up. There may be a fix on the interwebs for this, didn’t have time to research the proper route which needs to be added to operate with VPN networking in place on OS X.
You’ll need to execute the script with superuser privileges if you are going to network the virtual machine.
That done, let’s run it:
Once you run this, a bunch of output will fly by as the virtual machine is run. At the end, you’ll get a command line prompt on your newly minted VM!
Let’s see if the redis server is running:
Looks like our machine is listening on 6379, the redis port. Now, let’s see if that is exposed properly on the network and reachable. First, find the IP address of your VM:
Our IP is 192.168.64.17. From the Mac OS X host, we’ll use netcat and test the connection and server status:
Once you are done poking around, issue halt to shutdown the VM:
Success! We have a working LinuxKit image that is running a userland VM on OS X! Very cool. This is just the beginning. You can use the templates to create other images, customizing them to your liking. Also, don’t need to use redis. Try using your own docker images with your own services, and stand up the VMs.
In future posts, I’ll explore how to use the other Moby Project tools, like InfraKit and HyperKit, leveraging these to stand up LinuxKit OS images to provide more than just a quick testbed.
Oh, and take a look at the size of the two files created by LinuxKit and represents our Linux OS :)
This post originally appeared on the Nebulaworks website.