We are wrapping up a fun Hudson setup at work and I wanted to share our experience at a high level. Hudson is an “Extensible continuous integration server” that is used by a huge variety of companies for projects of all types. Hudson is definitely geared for Java projects out of the box but is very flexible and can be a huge help even on very non-Java code. I have configured a few different environments like this and like Hudson the most for a few reasons:
- Overall it is very polished and bug free. I suspect it being built by Sun for use on huge projects helps it here.
- The user base is sufficiently large so it is easy to find help and there are literally hundreds of plugins covering all sorts of interactions with different languages, version control systems, ticketing systems, etc. It is also possible to write your own plugins.
- It has virtually no dependencies outside of Java. Even for persistence it needs nothing as it uses intuitive flat file structure for storing everything. This also makes it very easy to migrate to a new server.
- The configuration of slave nodes and jobs are as straight forward as it gets.
- It lets you have as much control as you want, dropping to shell scripts is no problem at all.
In general continuous builds can make a huge difference for a development team. Everyone can move more quickly and they are completely worth the time and effort to setup right.
Our Project
We are working with a large C project that must be built and tested on many platforms. Building alone must be done on Windows 32, Windows 64, Linux 32, Linux 64, and OSX 32. Testing must be done on at least one of each architecture with additional rounds to cover different OS and GPU combinations. This variety of platforms takes a lot of time to test and it simply does not make sense to try and stay on top of the combinations with raw manpower.
The unique part of our testing is that it must be done on a machine with an Nvidia GPU. You will see below that we are using Citrix XenServer Virtual Machines for building but could not do the same for testing since VMs do not have native access to the GPU (with one exception that I know of). More notes about that later.
We build with standard Make and have to use a variety of compilers (gcc, g++, nvcc) to get everything generated.
The Build Machine Hardware
My goal with the build machine was to keep it cheap (under $1500) and to have at least 8 cores for doing builds. Our project can take awhile to build due to its size and number of dependencies and the best way to speed it up is raw CPU with usage of the “-j” argument in Make.
That said, these are the parts I went with. I didn’t shop around and used Newegg for everything for the tracking and RMA convenience. The only exception was a dual molex to 8 pin power adapter that I picked up at the nearby Microcenter for about $15.
| Part |
Price |
| NORCO RPC-450 Black 4U Rackmount Case |
$69.99 |
| TYAN S7002G2NR-LE Dual LGA 1366 |
$254.99 |
| 2 x Xeon E5506 |
$473.98 |
| Diablotek PHD 750W |
$79.99 |
| WINTEC 6GB (3 x 2GB) DDR3 1333 |
$229.99 |
| 6 x Seagate Barracuda 7200.12 ST3160318AS 160GB 7200 |
$227.94 |
| LITE-ON Black 18x DVD-ROM |
$19.99 |
| Shipping for all the above. |
$51.86 |
Some notes about these parts:
- If I had upgraded anything it would have been bumping the Xeons up to something with hyperthreading, but that would have broken my spending limit.
- Hard drives setup as 1 for XenServer host, a 4 disk raid 10 (using mdadm on the XenServer host), and a spare drive for the raid 10. The board only has 6 SATA ports so the 6th went to the external DVD-ROM.
- Tyan sells some awesome 2 socket boards if you can spend a little more money including ones with LSI raid controllers, more SATA ports, and up to 4 x16 PCIe 2.0 slots for running loads of GPUs.
- When buying bigger, multi-socket boards like this make sure your power supply has all the connectors needed or that you can buy adapters to compensate. The board above needs a 24pin and 2 x 8pin plugs (1 per CPU) from the power supply.
- The first TYAN board gave a code FF on power up and I had to RMA it. The second board worked without any trouble. When buying parts like this plan on having to RMA something. If you need a machine fast it is probably best to stick with a vendor like Dell.
Virtual Machines
On the hardware mentioned above we are running 4 virtual machines using XenServer. I settled on XenServer because it is free and full featured. My first attempt was VMWare Server running on Fedora but it is full of bugs and limitations (only saw one of the Xeons, and only allows 2 cores per VM). Their bare metal hypervisor (called ESXi) is supposedly better but it has strict hardware requirements and I wasn’t feeling very confident about VMWare after trying the VMWare Server product.
XenServer is a bare metal hypervisor, a very minimal Linux distribution. It does have a very complete command line interface for interacting with VMs and it does at least ship with mdadm so you can setup software raid arrays to run VMs on. The more feature rich, GUI-based administration app for working with the VMs only runs on Windows unfortunately and it connects to a running XenServer hypervisor. Most things can be done through the Windows application and anything more sophisticated can likely be done through the command line with shell scripts. This Guide has straight forward instructions for getting things running.
Once I settled on XenServer this part was very smooth. I made sure to fire up sshd and VNC Server on all virtual machines so I rarely have to use that Windows-only management application.
The 4 Virtual Machines are handling our builds for Windows and Linux, 32bit and 64bit for each. Each VM has access to all 8 Xeon cores and I staggered the polling frequency for each build in Hudson to avoid fights over CPU resources.
We are not able to use these for testing as we need native access to the GPU. The Parallels Extreme Workstation is another hypervisor that does support native access to certain cards but it is expensive and has very specific hardware requirements. I assumed my pieced together Newegg server would not be a good fit or at the very least would ensure I couldn’t get decent support from Parallels.
Hudson Setup
Hudson is definitely optimized for Java projects but we are having great success using it on our C project. Without going into too much detail, here are the general pieces of our setup. If anybody else is setting up something similar I am happy to answer any questions.
- Using the shell script option for all build steps.
- Using SSH for ALL slave nodes including Windows. For Windows we are using cygwin to install and run sshd. This has many advantages including being able to pretend you aren’t having to use Windows. More practically this lets you write your Windows scripts in good old bash using the standard Linux tools instead of having to suffer great pain with .bat files. The great part is this means you can often use common scripts regardless of slave OS.
- Using a separate job for each build and test environment.
- Build jobs are architecture specific (e.g. Linux 32bit)
- Test jobs are OS and device specific (e.g. Fedora 10 32bit running card X) and dependent on successful build jobs of compatible architecture. As an example, when the Linux 32bit build completes separate test jobs start up for Fedora 10 32bit card X, CentOS 32bit card Y, etc.
- We have a custom test harness that consists of shell scripts which compare GPU results against CPU results in backgrounded processes. When our test jobs complete a separate script parses this output and generates JUnit-compatible XML reports which Hudson reads. Hudson thinks they are JUnit and provides great trends, graphs, and data about these tests. We background the testing processes so that if they seg fault or time out we can kill them in the main script that is running all of the tests without having to stop testing altogether for that build.
- We have all of our various scripts stored on the master and the first step of the slaves is to scp over the latest versions of these scripts. This makes things much easier to maintain and ensures you only need to add/update scripts in one place.
- Using the Log Parser Plugin to fail builds. This is a great plugin that makes it trivially easy to indicate what indicates a failure in your build output and more generally is great for grouping your output into different categories.
- We tarball the compiled code and include that as an artifact on the last 10 builds of each architecture so that a clean build is always only a click away.
- Using ViewVC and the corresponding plugin to link all change logs in Hudson to the specific diffs. Hudson has plugins for more sophisticated repo browsers like Fisheye but ViewVC is free and functional enough.
- Our Mac builds and testing are handled by the developer laptops in the office. Our laptops are all listed as slaves and Hudson will snag one for usage when it sees one on the LAN. Eventually we will probably grab a Mac Mini to handle this.
Conclusion
Hudson is a great tool. The above setup was a fair amount of work to setup but will be a big help to our development team. We have the comfort of knowing builds and testing are happening constantly and have easy access to change logs, build histories, testing trends, stable build tarballs, build timings per OS, and all sorts of useful information and validation. We are tweaking it and making it better every time someone using it thinks of a change or some new information that would be helpful. I am very happy with the end result and feel that Hudson is flexible enough that we won’t ever outgrow what it can help with.