We make heavy use of Docker internally for its fantastic tooling to help streamline and focus our processes that depend on containers. Recently we found the need to begin hacking on the Docker source to extend its APIs for improving our internal CI/CD workflow. During the development of our custom Docker APIs, I felt that the approach described in Docker’s documentation for setting up a development environment was great for those starting out with contributing, but it didn’t quite fit our needs for the following reasons:
- all build-related processes are performed inside of a container, which didn’t exactly fit with our existing CM flow;
- waiting for the dev container to build fresh slowed us down and bloated our dev environment by requiring Docker to build Docker source;
- the development lifecycle is slightly encumbered due to the nature of needing the code to build Docker inside of a container – which can be solved using volumes, however we already had an established development workflow that uses Vagrant; and
- when we’re ready to test in a production-like environment with our custom-built Docker, we needed an easy way to build and deploy in our internal cloud – we already had an established workflow for this, so consistency was important
To help alleviate our issues above, I decided to move the build steps outside of the dev container and directly into a RHEL 7.2 VM that we use for development (provisioned by Vagrant) – I use a Mac locally, but if you’re using RHEL directly you don’t necessarily need to use a VM. Please keep in mind that the process I use and describe below is for our internal workflows, and if you intend on contributing to Docker then you should be familiar with and follow Docker’s “Code contribution workflow” (https://docs.docker.com/opensource/code/).
The entire process to setup a development environment pretty much mirrors what happens during the building of Docker’s development container. It will install various package dependencies, statically build a few required libraries, and set up the environment for building with Go – the steps are easily repeatable and automated using your favorite CM tool.
Install dependencies
Install the required dependencies. You may need to tweak these depending on your environment.
yum install -y golang btrfs-progs btrfs-progs-devel glibc-static
Build statically linked libraries
Here we grab the source for lvm2 and sqlite3 so we can build them manually while enabling them for static linking.
git clone -b v2_02_103 https://git.fedorahosted.org/git/lvm2.git /usr/local/lvm2
cd /usr/local/lvm2
./configure --enable-static_link && make device-mapper && make install_device-mapper
mkdir /usr/src/sqlite3
curl -sSL https://www.sqlite.org/2015/sqlite-autoconf-3081002.tar.gz | tar -v -C /usr/src/sqlite3 -xz
cd /usr/src/sqlite3/sqlite-autoconf-3081002/
./configure --enable-static && make && make install
ldconfig
Prep environment for building Go
In this step, we’re going to grab Docker source from github and place it in the standard spot for Go programs. Feel free to use your own forked code and relocate the source to where it makes sense in your environment.
# grab docker source
mkdir -p /go/src/github.com/docker
git clone -b v1.9.1 https://github.com/docker/docker /go/src/github.com/docker/docker
Build docker
And the final step is to build the docker binary from the source we cloned in the previous step.
cd /go/src/github.com/docker/docker
GOPATH=/go:/go/src/github.com/docker/docker/vendor hack/make.sh binary
and the resulting binary should be available at bundles/1.9.1/binary/docker-1.9.1.
Wrapper script
I use a script similar to the following to quickly kick off various tasks while developing.
#!/bin/bash
export GOPATH=/go:/go/src/github.com/docker/docker/vendor
cd /go/src/github.com/docker/docker
hack/make.sh $@
and use it for building, linting, validating, running tests, etc. To find the available process you can check the docker/hack/make directory as most of the scripts in there are actual commands that can be used with the provided make.sh or using the wrapper above.
./build_wrapper.sh validate-lint
./build_wrapper.sh validate-gofmt
TESTFLAGS='-test.run ^TestBuild$' ./build_wrapper.sh test-unit
./build_wrapper.sh binary