Vagrant and Oh-My-Vagrant on RHEL7

My employer keeps paying me, which I appreciate, so it’s good to spend some time to make sure RHEL7 customers get a great developer experience! So here’s how to make vagrant, vagrant-libvirt and Oh-My-Vagrant work on RHEL 7+. The same steps should work for CentOS 7+.

I’ll first paste the commands you need to run, and then I’ll explain what’s happening for those that are interested:

# run these commands, and then get hacking!
# this requires the rhel-7-server-optional-rpms repo enabled
sudo subscription-manager repos --enable rhel-7-server-optional-rpms
sudo yum install -y gcc ruby-devel libvirt-devel libvirt qemu-kvm
sudo systemctl start libvirtd.service
wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.4_x86_64.rpm
sudo yum install -y vagrant_1.7.4_x86_64.rpm
vagrant plugin install vagrant-libvirt
wget https://copr.fedoraproject.org/coprs/purpleidea/vagrant-libvirt/repo/epel-7/purpleidea-vagrant-libvirt-epel-7.repo
sudo cp -a purpleidea-vagrant-libvirt-epel-7.repo /etc/yum.repos.d/
sudo yum install -y vagrant-libvirt    # noop plugin for oh-my-vagrant dependency
wget https://copr.fedoraproject.org/coprs/purpleidea/oh-my-vagrant/repo/epel-7/purpleidea-oh-my-vagrant-epel-7.repo
sudo cp -a purpleidea-oh-my-vagrant-epel-7.repo /etc/yum.repos.d/
sudo yum install -y oh-my-vagrant
. /etc/profile.d/oh-my-vagrant.sh # logout/login or source

Let’s go through it line by line.

sudo subscription-manager repos --enable rhel-7-server-optional-rpms

Make sure you have the optional repos enabled, which are needed for the ruby-devel package.

sudo yum install -y gcc ruby-devel libvirt-devel libvirt
sudo systemctl start libvirtd.service

Other than the base os, these are the dependencies you’ll need. If you have some sort of super minimal installation, and find that there is another dependency needed, please let me know and I’ll update this article. Usually libvirt is already installed, and libvirtd is started, but this includes those two operations in case they are needed.

wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.4_x86_64.rpm
sudo yum install -y vagrant_1.7.4_x86_64.rpm

Vagrant has finally landed in Fedora 22, but unfortunately it’s not in RHEL or any of the software collections yet. As a result, we install it from the upstream.

vagrant plugin install vagrant-libvirt

Similarly, vagrant-libvirt hasn’t been packaged for RHEL either, so we’ll install it into the users home directory via the vagrant plugin system.

wget https://copr.fedoraproject.org/coprs/purpleidea/vagrant-libvirt/repo/epel-7/purpleidea-vagrant-libvirt-epel-7.repo
sudo cp -a purpleidea-vagrant-libvirt-epel-7.repo /etc/yum.repos.d/
sudo yum install -y vagrant-libvirt    # noop plugin for oh-my-vagrant dependency

Since there isn’t a vagrant-libvirt RPM, and because the RPM’s for Oh-My-Vagrant depend on that “requires” to install correctly, I built an empty vagrant-libvirt RPM so that Oh-My-Vagrant thinks the dependency has been met in system wide RPM land, when it’s actually been met in the user specific home directory space. I couldn’t think of a better way to do this, and as a result, you get to read about the exercise that prompted my recent “empty RPM” article.

wget https://copr.fedoraproject.org/coprs/purpleidea/oh-my-vagrant/repo/epel-7/purpleidea-oh-my-vagrant-epel-7.repo
sudo cp -a purpleidea-oh-my-vagrant-epel-7.repo /etc/yum.repos.d/
sudo yum install -y oh-my-vagrant

This last part installs Oh-My-Vagrant from the COPR. There is no “dnf enable” command in RHEL, so we manually wget the repo file into place.

. /etc/profile.d/oh-my-vagrant.sh # logout/login or source

Lastly if you’d like to reuse your current terminal session, source the /etc/profile.d/ file that is installed, otherwise close and reopen your terminal.

You’ll need to do an omv init at least once to make sure all the user plugins are installed, and you should be ready for your first vagrant up! Please note, that the above process definitely includes some dirty workarounds until vagrant is more easily consumable in RHEL, but I wanted to get you hacking earlier rather than later!

I hope this article helps you hack it out in RHEL land, be sure to read about how to build your own custom RHEL vagrant boxes too!

Happy Hacking,

James

Making an empty RPM

I am definitely not an RPM expert, in fact, I’m afraid of it, but with recent tools such as COPR, and my glorious Makefile, some aspects of it have become palatable. This post will be about a recent journey I had building the most useless RPM ever.

A video of what my work building this RPM looked like.

A video of my journey building this RPM.

Because of reasons, I wanted to satisfy an RPM dependency for a package that I wanted to install without rebuilding that RPM. As a result, I wanted to build as small an RPM as possible. This took me down a much longer path than I thought it would.

Step 1: The empty spec file

I thought this would be easy. It turns out it was not. Here’s what happened…

james@computer:/tmp/rpmbuild$ cat vagrant-libvirt.spec
%global project_version 0.0.24

Name:       vagrant-libvirt
Version:    0.0.24
Release:    noop
Summary:    A fake vagrant-libvirt RPM
License:    AGPLv3+
BuildArch:  noarch

Requires:   vagrant >= 1.6.5

%description
A fake vagrant-libvirt RPM

%prep
%setup -c -q -T -D -a 0

%build

%install

%files

%changelog
james@computer:/tmp/rpmbuild$ rpmbuild -bs vagrant-libvirt.spec 
error: No "Source:" tag in the spec file

Amazingly, rpmbuild fails to build without specifying a Source0 directive. Gah… As an aside, yes the License field was also required, or it won’t build either! So let’s create a dummy RPM to use as the source!

Step 2: The empty tarball

james@computer:/tmp/rpmbuild$ tar -cjf vagrant-libvirt-noop.tar.bz2
tar: Cowardly refusing to create an empty archive
Try 'tar --help' or 'tar --usage' for more information.

Apparently tar doesn’t want to cooperate either! Maybe these utilities have some sort of ingrained existential fear of nothingness? I can work around this though.

Step 3: The empty file

james@computer:/tmp/rpmbuild$ echo hello > README
james@computer:/tmp/rpmbuild$ tar -cjf vagrant-libvirt-noop.tar.bz2 README
james@computer:/tmp/rpmbuild$ echo $?
0
james@computer:/tmp/rpmbuild$ cat vagrant-libvirt.spec
%global project_version 0.0.24

Name:       vagrant-libvirt
Version:    0.0.24
Release:    noop
Summary:    A fake vagrant-libvirt RPM
License:    AGPLv3+
Source0:    vagrant-libvirt-noop.tar.bz2
BuildArch:  noarch

Requires:   vagrant >= 1.6.5

%description
A fake vagrant-libvirt RPM

%prep
%setup -c -q -T -D -a 0

%build

%install

%files

%changelog

Okay great! Now to build the RPM…

Step 4: The empty RPM

james@computer:/tmp/rpmbuild$ mkdir SOURCES
james@computer:/tmp/rpmbuild$ mv vagrant-libvirt-noop.tar.bz2 SOURCES/
james@computer:/tmp/rpmbuild$ rpmbuild --define "_topdir $(pwd)/" -bs vagrant-libvirt.spec
Wrote: /tmp/rpmbuild/SRPMS/vagrant-libvirt-0.0.24-noop.src.rpm
james@computer:/tmp/rpmbuild$ rpmbuild --define "_topdir $(pwd)/" -bb vagrant-libvirt.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.dUivHv
+ umask 022
+ cd /tmp/rpmbuild//BUILD
+ cd /tmp/rpmbuild/BUILD
+ /usr/bin/mkdir -p vagrant-libvirt-0.0.24
+ cd vagrant-libvirt-0.0.24
+ /usr/bin/bzip2 -dc /tmp/rpmbuild/SOURCES/vagrant-libvirt-noop.tar.bz2
+ /usr/bin/tar -xf -
+ STATUS=0
+ '[' 0 -ne 0 ']'
+ /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.kLSHn2
+ umask 022
+ cd /tmp/rpmbuild//BUILD
+ cd vagrant-libvirt-0.0.24
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.xTiM4y
+ umask 022
+ cd /tmp/rpmbuild//BUILD
+ '[' /tmp/rpmbuild/BUILDROOT/vagrant-libvirt-0.0.24-noop.x86_64 '!=' / ']'
+ rm -rf /tmp/rpmbuild/BUILDROOT/vagrant-libvirt-0.0.24-noop.x86_64
++ dirname /tmp/rpmbuild/BUILDROOT/vagrant-libvirt-0.0.24-noop.x86_64
+ mkdir -p /tmp/rpmbuild/BUILDROOT
+ mkdir /tmp/rpmbuild/BUILDROOT/vagrant-libvirt-0.0.24-noop.x86_64
+ cd vagrant-libvirt-0.0.24
+ /usr/lib/rpm/find-debuginfo.sh --strict-build-id -m --run-dwz --dwz-low-mem-die-limit 10000000 --dwz-max-die-limit 110000000 /tmp/rpmbuild//BUILD/vagrant-libvirt-0.0.24
/usr/lib/rpm/sepdebugcrcfix: Updated 0 CRC32s, 0 CRC32s did match.
+ /usr/lib/rpm/check-rpaths /usr/lib/rpm/check-buildroot
+ /usr/lib/rpm/brp-compress
+ /usr/lib/rpm/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/brp-python-bytecompile /usr/bin/python 1
+ /usr/lib/rpm/brp-python-hardlink
+ /usr/lib/rpm/redhat/brp-java-repack-jars
Processing files: vagrant-libvirt-0.0.24-noop.noarch
Checking for unpackaged file(s): /usr/lib/rpm/check-files /tmp/rpmbuild/BUILDROOT/vagrant-libvirt-0.0.24-noop.x86_64
Wrote: /tmp/rpmbuild/RPMS/noarch/vagrant-libvirt-0.0.24-noop.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.0lR0a6
+ umask 022
+ cd /tmp/rpmbuild//BUILD
+ cd vagrant-libvirt-0.0.24
+ /usr/bin/rm -rf /tmp/rpmbuild/BUILDROOT/vagrant-libvirt-0.0.24-noop.x86_64
+ exit 0
james@computer:/tmp/rpmbuild$

This worked too! It has some interesting output though…

james@computer:/tmp/rpmbuild$ rpm -qlp RPMS/noarch/vagrant-libvirt-0.0.24-noop.noarch.rpm
(contains no files)
james@computer:/tmp/rpmbuild$ ls -lAh RPMS/noarch/vagrant-libvirt-0.0.24-noop.noarch.rpm
-rw-rw-r--. 1 james 5.5K Aug 11 11:53 RPMS/noarch/vagrant-libvirt-0.0.24-noop.noarch.rpm
james@computer:/tmp/rpmbuild$

As you can see this has created an empty RPM, but which is about 5k in size. While this worked, builds submitted in COPR don’t generate any output. I suppose this is a bug in COPR, but in the meantime, I still wanted something working. I added some nonsense to the spec file to continue.

Step 5: The final product

james@computer:/tmp/rpmbuild$ cat vagrant-libvirt.spec 
%global project_version 0.0.24

Name:       vagrant-libvirt
Version:    0.0.24
Release:    noop
Summary:    A fake vagrant-libvirt RPM
License:    AGPLv3+
Source0:    vagrant-libvirt-noop.tar.bz2
BuildArch:  noarch

Requires:   vagrant >= 1.6.5

%description
A fake vagrant-libvirt RPM

%prep
%setup -c -q -T -D -a 0

%build

%install
rm -rf %{buildroot}
# _datadir is typically /usr/share/
install -d -m 0755 %{buildroot}/%{_datadir}/vagrant-libvirt/
echo "This is a phony vagrant-libvirt package." > %{buildroot}/%{_datadir}/vagrant-libvirt/README

%files
%{_datadir}/vagrant-libvirt/README

%changelog

After running the usual build commands, and sticking an SRPM up in COPR, this builds and installs as expected! Phew! There might a manual way to do this with cpio, but I wanted to use the official tools, and avoid hacking the spec.

Perhaps there is a simpler way to workaround all of this, but until I find it, I hope you’ve enjoyed my story,

Happy Hacking!

James

UPDATE: Reader Jan pointed out, that you could use fpm to accomplish the same thing with a one-liner. The modified one-liner is:

fpm -s empty -t rpm -d 'vagrant >= 1.6.5' -n vagrant-libvirt -v 0.0.24 --iteration noop

This is a much shorter and more elegant solution, with the one exception that fpm doesn’t currently produce SRPMS, which are needed so that a trusted build service like COPR distributes them to the users.

Here’s the full output and comparison and anyways:

james@computer:/tmp/ftest$ fpm -s empty -t rpm -d 'vagrant >= 1.6.5' -n vagrant-libvirt -v 0.0.24 --iteration noop
no value for epoch is set, defaulting to nil {:level=>:warn}
no value for epoch is set, defaulting to nil {:level=>:warn}
Created package {:path=>"vagrant-libvirt-0.0.24-noop.x86_64.rpm"}
james@computer:/tmp/ftest$ sha1sum vagrant-libvirt-0.0.24-noop.x86_64.rpm 61b1c200d2efa87d790a2243ccbc4c4ebb7ef64d  vagrant-libvirt-0.0.24-noop.x86_64.rpm
james@computer:/tmp/ftest$ sha1sum ~/code/oh-my-vagrant/extras/.rpmbuild/RPMS/noarch/vagrant-libvirt-0.0.24-noop.noarch.rpm 
5f2abb15264de6c1c7f09039945cd7bbd3a96404  /home/james/code/oh-my-vagrant/extras/.rpmbuild/RPMS/noarch/vagrant-libvirt-0.0.24-noop.noarch.rpm

While the two sha1sums aren’t identical (probably due to timestamps or some other variant) the two RPM’s should be functionally identical.

Golang parallelism issues causing “too many open files” error

I’ve been hacking in golang for a while, but I’ll admit that I didn’t get too deep into some of the language nuances until more recently. Since some of them have started to bite me, here’s a little post-mortem of one of the problems I was having.

After hacking and testing code all day, I made a seemingly innocuous change, and when running my program, I saw the following error:

2015/07/10 14:34:12 too many open files

I didn’t know what I broke, but it was obviously my fault. I reverted my recent changes, but still the error persisted. Internet searches and many painful hours of debugging ensued.

I had definitely hit some sort of heisenbug.

I had definitely hit some sort of Heisenbug.

What had gone wrong? Digging around my system, I noticed something weird in my ps output:

$ ps auxww | grep go
james     3446  0.0  0.1 197392  9240 pts/4    Sl   11:48   0:00 go run ./main.go
james     3457  0.0  0.0   6268  1980 pts/4    Sl   11:48   0:00 /tmp/go-build030949627/command-line-arguments/_obj/exe/event
james     3487  0.0  0.1 197392  9184 pts/4    Sl   11:49   0:00 go run ./main.go
james     3501  0.0  0.0   6268  2040 pts/4    Sl   11:49   0:00 /tmp/go-build037131602/command-line-arguments/_obj/exe/event
james     3556  0.0  0.1 197392  9168 pts/4    Sl   11:49   0:00 go run ./main.go
james     3567  0.0  0.0   6268  1976 pts/4    Sl   11:49   0:00 /tmp/go-build957487534/command-line-arguments/_obj/exe/event
james     3788  0.0  0.0 197392  1636 pts/4    Sl   Jul04   0:07 go run ./main.go
james     3800  0.0  0.0   5180  1944 pts/4    Sl   Jul04   0:01 /tmp/go-build552106841/command-line-arguments/_obj/exe/event
[...]

Hoards and hoards of lingering go build artefacts, were still running. At one time I noticed over 42 of these! I quickly killed them all off:

# processes are named `event`, and I don't have any unrelated event processes running.
$ killall -9 event
kernel: ahh, much better! :)

Which brought my program back to life! Heisenbug gone… or was it? I soon noticed, that each time I ran my program, the left over process count would increment by one. What was causing this? After another session of debugging, I found that these leftovers were caused by a lack of clean up due to some buggy code. That buggy code is the interesting part. Let’s look at it:

for v := range obj.GetSomeChannel() {
    fmt.Printf("Processing: %v\n", v.Name)
    wg.Add(1)
    // BAD
    go func() {
        defer wg.Done()
        v.Start() // some method
        fmt.Printf("Finished: %v\n", v.Name)
    }()
}

I’m not sure how common this issue is, so if you’re not yet familiar with it, take a moment to try and figure it out.

Okay. The issue is that when you iterate through the loop, the v value which is passed in to the function (the closure) is actually referencing the memory space of v. As a result, whenever the v value changes (as it does in the loop) the v variable instantly contains the new value, and the go routine will see the value of whatever it happens to be when it uses it.

To get around this race (and it is a race) you need to copy in the value to the goroutine:

for v := range obj.GetSomeChannel() {
    fmt.Printf("Processing: %v\n", v.Name)
    wg.Add(1)
    // GOOD
    go func(v *Objtype) {
        defer wg.Done()
            v.Start() // some method
        fmt.Printf("Finished: %v\n", v.Name)

    }(v)
}

It so happens that v is a pointer, but that’s irrelevant. The value of the pointer still needs to be copied in to the goroutine that is being called to use it. In my case, v needs to be a pointer, because we want the same copy of our data to be used throughout the code.

Many thanks to bleidl for helping me with some of the analysis!

As a quick aside, I’m using this WaitGroup pattern, which replaced the much uglier version of this loop which I had previously written. For a language that claims to not be pattern and idiom heavy, there sure are a bunch that I’ve found so far, many of which come with gotchas.

Happy hacking!

James

A super privileged Puppet container

In this new crazy world of containers and immutable hosts, one might still want to run previous generation software such as Puppet on a current generation Atomic host. This article will explain how you can do that, and offer some proof of concept code.

The atomic host doesn’t provide a yum or dnf command, because the software is pre-baked into a read-only /usr/ partition. To “install” (to use) additional software, it usually needs to be distributed and run as a container.

The Dockerfile which describes the docker container that we will build, has two important sections:

ENV FACTER_fqdn=localhost
ENTRYPOINT ["puppet", "apply"]

The ENTRYPOINT verb causes docker to run the puppet command that we built into the container, when the container is run with externally provided arguments. The ENV verb causes some facter errors to be suppressed, since facter isn’t running on the host. The rest of the file contains boilerplate and other build/run dependencies.

You can build this container yourself manually, or if you are an Oh-My-Vagrant user, then you can use the supplied omv.yaml file to build the container automatically. A build with Oh-My-Vagrant looks like this:

$ time vup omv1
Bringing machine 'omv1' up with 'libvirt' provider...
==> omv1: Creating image (snapshot of base box volume).
==> omv1: Creating domain with the following settings...
==> omv1:  -- Name:              omv_omv1
==> omv1:  -- Domain type:       kvm
==> omv1:  -- Cpus:              1
==> omv1:  -- Memory:            512M
==> omv1:  -- Base box:          centos-7.1-docker
==> omv1:  -- Storage pool:      default
==> omv1:  -- Image:             /var/lib/libvirt/images/omv_omv1.img
==> omv1:  -- Volume Cache:      default
==> omv1:  -- Kernel:            
==> omv1:  -- Initrd:            
==> omv1:  -- Graphics Type:     vnc
==> omv1:  -- Graphics Port:     5900
==> omv1:  -- Graphics IP:       127.0.0.1
==> omv1:  -- Graphics Password: Not defined
==> omv1:  -- Video Type:        cirrus
==> omv1:  -- Video VRAM:        9216
==> omv1:  -- Keymap:            en-us
==> omv1:  -- Command line : 
==> omv1: Starting domain.
==> omv1: Waiting for domain to get an IP address...
==> omv1: Waiting for SSH to become available...
==> omv1: Starting domain.
==> omv1: Waiting for domain to get an IP address...
==> omv1: Waiting for SSH to become available...
==> omv1: Creating shared folders metadata...
==> omv1: Setting hostname...
==> omv1: Rsyncing folder: /home/james/code/oh-my-vagrant/vagrant/ => /vagrant
==> omv1: Configuring and enabling network interfaces...
==> omv1: Updating /etc/hosts file on active guest machines...
==> omv1: Running provisioner: shell...
    omv1: Running: inline script
==> omv1: Running provisioner: shell...
    omv1: Running: inline script
==> omv1: Running provisioner: docker...
    omv1: Configuring Docker to autostart containers...
==> omv1: Running provisioner: docker...
    omv1: Configuring Docker to autostart containers...
==> omv1: Building Docker images...
==> omv1: -- Path: /vagrant/docker/spc-puppet-apply
==> omv1: Sending build context to Docker daemon 122.9 kB
==> omv1: Sending build context to Docker daemon 
==> omv1: Step 0 : FROM centos:7
==> omv1:  ---> 0114405f9ff1
==> omv1: Step 1 : MAINTAINER James Shubin <james@shubin.ca>
==> omv1:  ---> Running in 2dbdc37494e9
==> omv1:  ---> e154b3cfed7f
==> omv1: Removing intermediate container 2dbdc37494e9
==> omv1: Step 2 : RUN echo Hello from purpleidea and aweiteka > README
==> omv1:  ---> Running in 97475154152f
==> omv1:  ---> e4728d71caac
==> omv1: Removing intermediate container 97475154152f
==> omv1: Step 3 : ADD el7-puppet.repo /etc/yum.repos.d/
==> omv1:  ---> c591e0a9a54d
==> omv1: Removing intermediate container 24e55893313c
==> omv1: Step 4 : ADD RPM-GPG-KEY-puppetlabs /etc/pki/rpm-gpg/
==> omv1:  ---> 223549fbf661
==> omv1: Removing intermediate container 2bceb998f1c3
==> omv1: Step 5 : RUN yum install -y puppet
==> omv1:  ---> Running in e44c9cf55dc5
==> omv1: Loaded plugins: fastestmirror
==> omv1: Determining fastest mirrors
==> omv1:  * base: centos.mirror.vexxhost.com
==> omv1:  * extras: mirror.gpmidi.net
==> omv1:  * updates: centos.mirror.vexxhost.com
==> omv1: Resolving Dependencies
==> omv1: --> Running transaction check
==> omv1: ---> Package puppet.noarch 0:3.7.5-1.el7 will be installed
==> omv1: --> Processing Dependency: ruby >= 1.8 for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: facter >= 1:1.7.0 for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: ruby >= 1.8.7 for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: hiera >= 1.0.0 for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: rubygem-json for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: libselinux-utils for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: ruby-augeas for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: ruby(selinux) for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: /usr/bin/ruby for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Processing Dependency: ruby-shadow for package: puppet-3.7.5-1.el7.noarch
==> omv1: --> Running transaction check
==> omv1: ---> Package facter.x86_64 1:2.4.3-1.el7 will be installed
==> omv1: --> Processing Dependency: net-tools for package: 1:facter-2.4.3-1.el7.x86_64
==> omv1: --> Processing Dependency: virt-what for package: 1:facter-2.4.3-1.el7.x86_64
==> omv1: --> Processing Dependency: pciutils for package: 1:facter-2.4.3-1.el7.x86_64
==> omv1: --> Processing Dependency: dmidecode for package: 1:facter-2.4.3-1.el7.x86_64
==> omv1: ---> Package hiera.noarch 0:1.3.4-1.el7 will be installed
==> omv1: ---> Package libselinux-ruby.x86_64 0:2.2.2-6.el7 will be installed
==> omv1: ---> Package libselinux-utils.x86_64 0:2.2.2-6.el7 will be installed
==> omv1: ---> Package ruby.x86_64 0:2.0.0.598-24.el7 will be installed
==> omv1: --> Processing Dependency: ruby-libs(x86-64) = 2.0.0.598-24.el7 for package: ruby-2.0.0.598-24.el7.x86_64
==> omv1: --> Processing Dependency: rubygem(bigdecimal) >= 1.2.0 for package: ruby-2.0.0.598-24.el7.x86_64
==> omv1: --> Processing Dependency: ruby(rubygems) >= 2.0.14 for package: ruby-2.0.0.598-24.el7.x86_64
==> omv1: --> Processing Dependency: libruby.so.2.0()(64bit) for package: ruby-2.0.0.598-24.el7.x86_64
==> omv1: ---> Package ruby-augeas.x86_64 0:0.4.1-3.el7 will be installed
==> omv1: --> Processing Dependency: augeas-libs >= 0.8.0 for package: ruby-augeas-0.4.1-3.el7.x86_64
==> omv1: --> Processing Dependency: libaugeas.so.0(AUGEAS_0.8.0)(64bit) for package: ruby-augeas-0.4.1-3.el7.x86_64
==> omv1: --> Processing Dependency: libaugeas.so.0(AUGEAS_0.1.0)(64bit) for package: ruby-augeas-0.4.1-3.el7.x86_64
==> omv1: --> Processing Dependency: libaugeas.so.0(AUGEAS_0.12.0)(64bit) for package: ruby-augeas-0.4.1-3.el7.x86_64
==> omv1: --> Processing Dependency: libaugeas.so.0(AUGEAS_0.10.0)(64bit) for package: ruby-augeas-0.4.1-3.el7.x86_64
==> omv1: --> Processing Dependency: libaugeas.so.0(AUGEAS_0.11.0)(64bit) for package: ruby-augeas-0.4.1-3.el7.x86_64
==> omv1: --> Processing Dependency: libaugeas.so.0()(64bit) for package: ruby-augeas-0.4.1-3.el7.x86_64
==> omv1: ---> Package ruby-shadow.x86_64 1:2.2.0-2.el7 will be installed
==> omv1: ---> Package rubygem-json.x86_64 0:1.7.7-24.el7 will be installed
==> omv1: --> Running transaction check
==> omv1: ---> Package augeas-libs.x86_64 0:1.1.0-17.el7 will be installed
==> omv1: ---> Package dmidecode.x86_64 1:2.12-5.el7 will be installed
==> omv1: ---> Package net-tools.x86_64 0:2.0-0.17.20131004git.el7 will be installed
==> omv1: ---> Package pciutils.x86_64 0:3.2.1-4.el7 will be installed
==> omv1: --> Processing Dependency: pciutils-libs = 3.2.1-4.el7 for package: pciutils-3.2.1-4.el7.x86_64
==> omv1: --> Processing Dependency: libpci.so.3(LIBPCI_3.2)(64bit) for package: pciutils-3.2.1-4.el7.x86_64
==> omv1: --> Processing Dependency: libpci.so.3(LIBPCI_3.1)(64bit) for package: pciutils-3.2.1-4.el7.x86_64
==> omv1: --> Processing Dependency: libpci.so.3(LIBPCI_3.0)(64bit) for package: pciutils-3.2.1-4.el7.x86_64
==> omv1: --> Processing Dependency: hwdata for package: pciutils-3.2.1-4.el7.x86_64
==> omv1: --> Processing Dependency: libpci.so.3()(64bit) for package: pciutils-3.2.1-4.el7.x86_64
==> omv1: ---> Package ruby-libs.x86_64 0:2.0.0.598-24.el7 will be installed
==> omv1: ---> Package rubygem-bigdecimal.x86_64 0:1.2.0-24.el7 will be installed
==> omv1: ---> Package rubygems.noarch 0:2.0.14-24.el7 will be installed
==> omv1: --> Processing Dependency: rubygem(rdoc) >= 4.0.0 for package: rubygems-2.0.14-24.el7.noarch
==> omv1: --> Processing Dependency: rubygem(psych) >= 2.0.0 for package: rubygems-2.0.14-24.el7.noarch
==> omv1: --> Processing Dependency: rubygem(io-console) >= 0.4.2 for package: rubygems-2.0.14-24.el7.noarch
==> omv1: ---> Package virt-what.x86_64 0:1.13-5.el7 will be installed
==> omv1: --> Running transaction check
==> omv1: ---> Package hwdata.noarch 0:0.252-7.5.el7 will be installed
==> omv1: ---> Package pciutils-libs.x86_64 0:3.2.1-4.el7 will be installed
==> omv1: ---> Package rubygem-io-console.x86_64 0:0.4.2-24.el7 will be installed
==> omv1: ---> Package rubygem-psych.x86_64 0:2.0.0-24.el7 will be installed
==> omv1: --> Processing Dependency: libyaml-0.so.2()(64bit) for package: rubygem-psych-2.0.0-24.el7.x86_64
==> omv1: ---> Package rubygem-rdoc.noarch 0:4.0.0-24.el7 will be installed
==> omv1: --> Processing Dependency: ruby(irb) = 2.0.0.598 for package: rubygem-rdoc-4.0.0-24.el7.noarch
==> omv1: --> Running transaction check
==> omv1: ---> Package libyaml.x86_64 0:0.1.4-11.el7_0 will be installed
==> omv1: ---> Package ruby-irb.noarch 0:2.0.0.598-24.el7 will be installed
==> omv1: --> Finished Dependency Resolution
==> omv1: 
==> omv1: Dependencies Resolved
==> omv1: 
==> omv1: ================================================================================
==> omv1:  Package            Arch   Version                    Repository           Size
==> omv1: ================================================================================
==> omv1: Installing:
==> omv1:  puppet             noarch 3.7.5-1.el7                puppetlabs-products 1.5 M
==> omv1: Installing for dependencies:
==> omv1:  augeas-libs        x86_64 1.1.0-17.el7               base                332 k
==> omv1:  dmidecode          x86_64 1:2.12-5.el7               base                 78 k
==> omv1:  facter             x86_64 1:2.4.3-1.el7              puppetlabs-products  98 k
==> omv1:  hiera              noarch 1.3.4-1.el7                puppetlabs-products  23 k
==> omv1:  hwdata             noarch 0.252-7.5.el7              base                2.0 M
==> omv1:  libselinux-ruby    x86_64 2.2.2-6.el7                base                127 k
==> omv1:  libselinux-utils   x86_64 2.2.2-6.el7                base                135 k
==> omv1:  libyaml            x86_64 0.1.4-11.el7_0             base                 55 k
==> omv1:  net-tools          x86_64 2.0-0.17.20131004git.el7   base                304 k
==> omv1:  pciutils           x86_64 3.2.1-4.el7                base                 90 k
==> omv1:  pciutils-libs      x86_64 3.2.1-4.el7                base                 45 k
==> omv1:  ruby               x86_64 2.0.0.598-24.el7           base                 67 k
==> omv1:  ruby-augeas        x86_64 0.4.1-3.el7                puppetlabs-deps      22 k
==> omv1:  ruby-irb           noarch 2.0.0.598-24.el7           base                 88 k
==> omv1:  ruby-libs          x86_64 2.0.0.598-24.el7           base                2.8 M
==> omv1:  ruby-shadow        x86_64 1:2.2.0-2.el7              puppetlabs-deps      14 k
==> omv1:  rubygem-bigdecimal x86_64 1.2.0-24.el7               base                 79 k
==> omv1:  rubygem-io-console x86_64 0.4.2-24.el7               base                 50 k
==> omv1:  rubygem-json       x86_64 1.7.7-24.el7               base                 75 k
==> omv1:  rubygem-psych      x86_64 2.0.0-24.el7               base                 77 k
==> omv1:  rubygem-rdoc       noarch 4.0.0-24.el7               base                318 k
==> omv1:  rubygems           noarch 2.0.14-24.el7              base                212 k
==> omv1:  virt-what          x86_64 1.13-5.el7                 base                 26 k
==> omv1: 
==> omv1: Transaction Summary
==> omv1: ================================================================================
==> omv1: Install  1 Package (+23 Dependent packages)
==> omv1: 
==> omv1: Total download size: 8.6 M
==> omv1: Installed size: 27 M
==> omv1: Downloading packages:
==> omv1: warning: /var/cache/yum/x86_64/7/puppetlabs-products/packages/hiera-1.3.4-1.el7.noarch.rpm: Header V4 RSA/SHA512 Signature, key ID 4bd6ec30: NOKEY
==> omv1: 
==> omv1: Public key for hiera-1.3.4-1.el7.noarch.rpm is not installed
==> omv1: warning: /var/cache/yum/x86_64/7/base/packages/dmidecode-2.12-5.el7.x86_64.rpm: Header V3 RSA/SHA256 Signature, key ID f4a80eb5: NOKEY
==> omv1: 
==> omv1: Public key for dmidecode-2.12-5.el7.x86_64.rpm is not installed
==> omv1: Public key for ruby-augeas-0.4.1-3.el7.x86_64.rpm is not installed
==> omv1: --------------------------------------------------------------------------------
==> omv1: Total                                              811 kB/s | 8.6 MB  00:10     
==> omv1: Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
==> omv1: Importing GPG key 0xF4A80EB5:
==> omv1:  Userid     : "CentOS-7 Key (CentOS 7 Official Signing Key) <security@centos.org>"
==> omv1:  Fingerprint: 6341 ab27 53d7 8a78 a7c2 7bb1 24c6 a8a7 f4a8 0eb5
==> omv1:  Package    : centos-release-7-1.1503.el7.centos.2.8.x86_64 (@CentOS/$releasever)
==> omv1:  From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
==> omv1: 
==> omv1: Retrieving key from file:///etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs
==> omv1: Importing GPG key 0x4BD6EC30:
==> omv1:  Userid     : "Puppet Labs Release Key (Puppet Labs Release Key) <info@puppetlabs.com>"
==> omv1:  Fingerprint: 47b3 20eb 4c7c 375a a9da e1a0 1054 b7a2 4bd6 ec30
==> omv1:  From       : /etc/pki/rpm-gpg/RPM-GPG-KEY-puppetlabs
==> omv1: 
==> omv1: Running transaction check
==> omv1: Running transaction test
==> omv1: Transaction test succeeded
==> omv1: Running transaction
==> omv1:   Installing : ruby-libs-2.0.0.598-24.el7.x86_64                           1/24
==> omv1:  
==> omv1:   Installing : 1:dmidecode-2.12-5.el7.x86_64                               2/24
==> omv1:  
==> omv1:   Installing : virt-what-1.13-5.el7.x86_64                                 3/24
==> omv1:  
==> omv1:   Installing : libyaml-0.1.4-11.el7_0.x86_64                               4/24
==> omv1:  
==> omv1:   Installing : rubygem-psych-2.0.0-24.el7.x86_64                           5/24
==> omv1:  
==> omv1:   Installing : rubygem-bigdecimal-1.2.0-24.el7.x86_64                      6/24
==> omv1:  
==> omv1:   Installing : rubygem-io-console-0.4.2-24.el7.x86_64                      7/24
==> omv1:  
==> omv1:   Installing : ruby-irb-2.0.0.598-24.el7.noarch                            8/24
==> omv1:  
==> omv1:   Installing : ruby-2.0.0.598-24.el7.x86_64                                9/24
==> omv1:  
==> omv1:   Installing : rubygems-2.0.14-24.el7.noarch                              10/24
==> omv1:  
==> omv1:   Installing : rubygem-json-1.7.7-24.el7.x86_64                           11/24
==> omv1:  
==> omv1:   Installing : rubygem-rdoc-4.0.0-24.el7.noarch                           12/24
==> omv1:  
==> omv1:   Installing : hiera-1.3.4-1.el7.noarch                                   13/24
==> omv1:  
==> omv1:   Installing : 1:ruby-shadow-2.2.0-2.el7.x86_64                           14/24
==> omv1:  
==> omv1:   Installing : hwdata-0.252-7.5.el7.noarch                                15/24
==> omv1:  
==> omv1:   Installing : libselinux-utils-2.2.2-6.el7.x86_64                        16/24
==> omv1:  
==> omv1:   Installing : pciutils-libs-3.2.1-4.el7.x86_64                           17/24
==> omv1:  
==> omv1:   Installing : pciutils-3.2.1-4.el7.x86_64                                18/24
==> omv1:  
==> omv1:   Installing : augeas-libs-1.1.0-17.el7.x86_64                            19/24
==> omv1:  
==> omv1:   Installing : ruby-augeas-0.4.1-3.el7.x86_64                             20/24
==> omv1:  
==> omv1:   Installing : net-tools-2.0-0.17.20131004git.el7.x86_64                  21/24
==> omv1:  
==> omv1:   Installing : 1:facter-2.4.3-1.el7.x86_64                                22/24
==> omv1:  
==> omv1:   Installing : libselinux-ruby-2.2.2-6.el7.x86_64                         23/24
==> omv1:  
==> omv1:   Installing : puppet-3.7.5-1.el7.noarch                                  24/24
==> omv1:  
==> omv1:   Verifying  : libselinux-ruby-2.2.2-6.el7.x86_64                          1/24
==> omv1:  
==> omv1:   Verifying  : rubygem-json-1.7.7-24.el7.x86_64                            2/24
==> omv1:  
==> omv1:   Verifying  : ruby-irb-2.0.0.598-24.el7.noarch                            3/24
==> omv1:  
==> omv1:   Verifying  : ruby-libs-2.0.0.598-24.el7.x86_64                           4/24
==> omv1:  
==> omv1:   Verifying  : net-tools-2.0-0.17.20131004git.el7.x86_64                   5/24
==> omv1:  
==> omv1:   Verifying  : augeas-libs-1.1.0-17.el7.x86_64                             6/24
==> omv1:  
==> omv1:   Verifying  : pciutils-libs-3.2.1-4.el7.x86_64                            7/24
==> omv1:  
==> omv1:   Verifying  : rubygem-psych-2.0.0-24.el7.x86_64                           8/24
==> omv1:  
==> omv1:   Verifying  : 1:facter-2.4.3-1.el7.x86_64                                 9/24
==> omv1:  
==> omv1:   Verifying  : pciutils-3.2.1-4.el7.x86_64                                10/24
==> omv1:  
==> omv1:   Verifying  : puppet-3.7.5-1.el7.noarch                                  11/24
==> omv1:  
==> omv1:   Verifying  : hiera-1.3.4-1.el7.noarch                                   12/24
==> omv1:  
==> omv1:   Verifying  : rubygem-rdoc-4.0.0-24.el7.noarch                           13/24
==> omv1:  
==> omv1:   Verifying  : virt-what-1.13-5.el7.x86_64                                14/24
==> omv1:  
==> omv1:   Verifying  : rubygems-2.0.14-24.el7.noarch                              15/24
==> omv1:  
==> omv1:   Verifying  : libselinux-utils-2.2.2-6.el7.x86_64                        16/24
==> omv1:  
==> omv1:   Verifying  : 1:ruby-shadow-2.2.0-2.el7.x86_64                           17/24
==> omv1:  
==> omv1:   Verifying  : rubygem-bigdecimal-1.2.0-24.el7.x86_64                     18/24
==> omv1:  
==> omv1:   Verifying  : 1:dmidecode-2.12-5.el7.x86_64                              19/24
==> omv1:  
==> omv1:   Verifying  : hwdata-0.252-7.5.el7.noarch                                20/24
==> omv1:  
==> omv1:   Verifying  : libyaml-0.1.4-11.el7_0.x86_64                              21/24
==> omv1:  
==> omv1:   Verifying  : rubygem-io-console-0.4.2-24.el7.x86_64                     22/24
==> omv1:  
==> omv1:   Verifying  : ruby-augeas-0.4.1-3.el7.x86_64                             23/24
==> omv1:  
==> omv1:   Verifying  : ruby-2.0.0.598-24.el7.x86_64                               24/24
==> omv1:  
==> omv1: 
==> omv1: Installed:
==> omv1:   puppet.noarch 0:3.7.5-1.el7                                                   
==> omv1: 
==> omv1: Dependency Installed:
==> omv1:   augeas-libs.x86_64 0:1.1.0-17.el7                                             
==> omv1:   dmidecode.x86_64 1:2.12-5.el7                                                 
==> omv1:   facter.x86_64 1:2.4.3-1.el7                                                   
==> omv1:   hiera.noarch 0:1.3.4-1.el7                                                    
==> omv1:   hwdata.noarch 0:0.252-7.5.el7                                                 
==> omv1:   libselinux-ruby.x86_64 0:2.2.2-6.el7                                          
==> omv1:   libselinux-utils.x86_64 0:2.2.2-6.el7                                         
==> omv1:   libyaml.x86_64 0:0.1.4-11.el7_0                                               
==> omv1:   net-tools.x86_64 0:2.0-0.17.20131004git.el7                                   
==> omv1:   pciutils.x86_64 0:3.2.1-4.el7                                                 
==> omv1:   pciutils-libs.x86_64 0:3.2.1-4.el7                                            
==> omv1:   ruby.x86_64 0:2.0.0.598-24.el7                                                
==> omv1:   ruby-augeas.x86_64 0:0.4.1-3.el7                                              
==> omv1:   ruby-irb.noarch 0:2.0.0.598-24.el7                                            
==> omv1:   ruby-libs.x86_64 0:2.0.0.598-24.el7                                           
==> omv1:   ruby-shadow.x86_64 1:2.2.0-2.el7                                              
==> omv1:   rubygem-bigdecimal.x86_64 0:1.2.0-24.el7                                      
==> omv1:   rubygem-io-console.x86_64 0:0.4.2-24.el7                                      
==> omv1:   rubygem-json.x86_64 0:1.7.7-24.el7                                            
==> omv1:   rubygem-psych.x86_64 0:2.0.0-24.el7                                           
==> omv1:   rubygem-rdoc.noarch 0:4.0.0-24.el7                                            
==> omv1:   rubygems.noarch 0:2.0.14-24.el7                                               
==> omv1:   virt-what.x86_64 0:1.13-5.el7                                                 
==> omv1: Complete!
==> omv1:  ---> bf169104271a
==> omv1: Removing intermediate container e44c9cf55dc5
==> omv1: Step 6 : RUN yum install -y hostname
==> omv1:  ---> Running in 6e5bd5a59223
==> omv1: Loaded plugins: fastestmirror
==> omv1: Loading mirror speeds from cached hostfile
==> omv1:  * base: centos.mirror.vexxhost.com
==> omv1:  * extras: mirror.gpmidi.net
==> omv1:  * updates: centos.mirror.vexxhost.com
==> omv1: Resolving Dependencies
==> omv1: --> Running transaction check
==> omv1: ---> Package hostname.x86_64 0:3.13-3.el7 will be installed
==> omv1: --> Finished Dependency Resolution
==> omv1: 
==> omv1: Dependencies Resolved
==> omv1: 
==> omv1: ================================================================================
==> omv1:  Package            Arch             Version               Repository      Size
==> omv1: ================================================================================
==> omv1: Installing:
==> omv1:  hostname           x86_64           3.13-3.el7            base            17 k
==> omv1: 
==> omv1: Transaction Summary
==> omv1: ================================================================================
==> omv1: Install  1 Package
==> omv1: 
==> omv1: Total download size: 17 k
==> omv1: Installed size: 19 k
==> omv1: Downloading packages:
==> omv1: Running transaction check
==> omv1: Running transaction test
==> omv1: Transaction test succeeded
==> omv1: Running transaction
==> omv1:   Installing : hostname-3.13-3.el7.x86_64                                   1/1
==> omv1:  
==> omv1:   Verifying  : hostname-3.13-3.el7.x86_64                                   1/1
==> omv1:  
==> omv1: 
==> omv1: Installed:
==> omv1:   hostname.x86_64 0:3.13-3.el7                                                  
==> omv1: 
==> omv1: Complete!
==> omv1:  ---> 2a20361f2d9d
==> omv1: Removing intermediate container 6e5bd5a59223
==> omv1: Step 7 : ADD run.sh /
==> omv1:  ---> 5493e62ac377
==> omv1: Removing intermediate container 84c8b7677f72
==> omv1: Step 8 : ADD test.pp /
==> omv1:  ---> 4f4d0023e612
==> omv1: Removing intermediate container 84cb691304a3
==> omv1: Step 9 : ENV FACTER_fqdn localhost
==> omv1:  ---> Running in 104fe3925dd6
==> omv1:  ---> 864c113d54b5
==> omv1: Removing intermediate container 104fe3925dd6
==> omv1: Step 10 : ENTRYPOINT puppet apply
==> omv1:  ---> Running in f6fe26141b19
==> omv1:  ---> cd19e4344bf1
==> omv1: Removing intermediate container f6fe26141b19
==> omv1: Step 11 : USER root
==> omv1:  ---> Running in 435752b9fb17
==> omv1:  ---> 86193e9815a8
==> omv1: Removing intermediate container 435752b9fb17
==> omv1: Step 12 : LABEL INSTALL docker run --rm -it --privileged -v /etc:/etc -v /var:/var -v /run:/run --net=host IMAGE
==> omv1:  ---> Running in d24f98a56936
==> omv1:  ---> 72f2dfbc4e07
==> omv1: Removing intermediate container d24f98a56936
==> omv1: Successfully built 72f2dfbc4e07

real    2m46.515s
user    0m10.036s
sys    0m1.678s

Once you’ve built the container, you should confirm its existence:

$ vscreen root@omv1
# docker images | grep spc
spc-puppet-apply    latest              72f2dfbc4e07        7 minutes ago       314.5 MB

The project repository tries to make your life easier, so it comes with a test.pp file that you can run to confirm puppet is working correctly. If you are using a regular host (not Atomic) you can run:

# mkdir /var/tmp/spc-puppet-apply/
# cp -a /vagrant/docker/spc-puppet-apply/test.pp /var/tmp/spc-puppet-apply/

or if you’re using an Atomic host, you can run:

# mkdir /var/tmp/spc-puppet-apply/
# cp -a /home/vagrant/sync/docker/spc-puppet-apply/test.pp /var/tmp/spc-puppet-apply/

since Oh-My-Vagrant can’t put files in /vagrant on an immutable Atomic host.

Finally, run the application with this monster docker command:

# docker run --rm -it --privileged -v /etc:/etc -v /var:/var -v /run:/run --net=host spc-puppet-apply /var/tmp/spc-puppet-apply/test.pp
Notice: Compiled catalog for omv1.example.com in environment production in 0.02 seconds
Notice: This is a puppet test!

Notice: /Stage[main]/Main/Notify[hello]/message: defined 'message' as 'This is a puppet test!'
Notice: Finished catalog run in 0.06 seconds

Since remembering that monster each time you want to do a simple puppet run is a bit of a monster, the Atomic project provides labels which make it so you can use the atomic run command instead.

Future work:

Interested parties are encouraged to test this, find any issues, and become comfortable with the idea of applications running from within containers. If running puppet with a puppet master is desirable, an spc-puppet-agent would be needed.

Happy Hacking,

James

Kubernetes clusters with Oh-My-Vagrant

I’ve added the ability to deploy a Kubernetes cluster with Oh-My-Vagrant (omv). I’ve also built an automated developer experience so that you can test your Kubernetes powered app in minutes. If you want to redeploy a new version, or see how your app behaves during a rolling update, you can use omv to test this out in minutes! I’ve recorded a screencast (~15 min), if you’d like to see some of this in action.

Background:

Kubernetes is a container cluster manager. It groups containers into pods, and those pods get scheduled to run on a certain machine in the cluster. We’ll be talking about Docker containers in this article, although there are lots of great new technologies such as nspawn.

An advantage of using Kubernetes is that it was created by a team at Google, who has a lot of experience running containers. A lot of smart folks (including many from Red Hat) are working on this project too! It is becoming the foundation for other software such as OpenShift v3. Google has released a paper on their earlier work (Borg), which is interesting, but lacks many details, and (unsurprisingly) no source code is present.

Some big disadvantages include the requirement of a CLA to contribute to the project, and the lack of good documentation and articles about it. Kubernetes itself, can’t yet be decentralized, but this might change in the future. It’s (currently) very difficult to construct the foo.json files required to build an application.

While the project is open source (ALv2) I’ve gotten the feeling that Google has a pretty strong hold on the project and has some changes to make before the community really trusts them. This can be a learning experience for any company where proprietary software is the culture. I wish them well on their journey!

Oh-My-Vagrant integration:

To deploy a Kubernetes cluster with omv, the kubernetes variable needs to be set to something meaningful. It’s also recommended that you boot up a minimum of three machines. Here is an except from an example omv.yaml config:

---
:domain: example.com
:network: 192.168.123.0/24
:image: centos-7.1-docker
:sync: rsync
:extern:
- type: git
  system: docker
  repository: https://github.com/purpleidea/docker-simple1
  directory: docker-simple1
- type: git
  system: kubernetes
  repository: https://github.com/purpleidea/kube-simple1
  directory: kube-simple1
:puppet: false
:docker: []
:kubernetes:
  applications:
  - kube-simple1/simple1.json
:vms: []
:namespace: omv
:count: 3

In the above example, you can see that we’ve only listed one Kubernetes application, but an arbitrary number can be included. The kubernetes variable can also accept a key named master if you’d like to choose which of your vm’s should be the primary. If you don’t specify this, the first vagrant machine will be chosen.

In the list of applications, instead of only specifying the .json file, you can instead specify a dictionary of key/value pairs. The .json key (file) is required, but you can also specify additional keys, such as the boolean key roll. When true, it will cause a rolling update to occur instead of a normal” update.

If you’re interested to see what needs to be done to set up Kubernetes, the bulk of the work was done by me in 1f26, but figuring out manual steps was only possible thanks to the work of my hacker friends: scollier and eparis.

Due to the power of the omv project, when you vagrant up, the necessary docker and Kubernetes projects listed in the extern variable will be automatically cloned and pulled into your omv environment.

Screencast:

You’re probably due for a screencast (~15 min). Have a watch, and then if you need review, go back and read what I’ve written above.

https://download.gluster.org/pub/gluster/purpleidea/screencasts/oh-my-vagrant-kubernetes-screencast.ogv

(Thanks to the Gluster community for generously hosting this video!)

Final thoughts:

I haven’t talked about networking, or actually building applications.

Container network might be a lot easier if you use an overlay network like flannel. Unfortunately, this isn’t yet built into Oh-My-Vagrant, but is in the list of feature requests if someone shows some interest.

Building useful applications is harder. In my screencast, you’ll see where to put the code, and how to iterate on it, but not what kind of code to write, or architecturally how your multi-container applications should work. Unfortunately this is out of scope for today’s article! My goal was to make it easy for you to focus on that topic, instead of having to figure out how to build the infrastructure. Hopefully Oh-My-Vagrant helps you accomplish that!

Happy hacking,

James

 

Docker containers in Oh-My-Vagrant

The Oh-My-Vagrant (omv) project is an easy way to bootstrap a development environment. It is particularly useful for spinning up an arbitrary number of virtual machines in Vagrant without writing ruby code. For multi-machine container development, omv can be used to help this happen more naturally.

Oh-My-Vagrant can be very useful as a docker application development environment. I’ve made a quick (<9min) screencast demoing this topic. Please have a look:

https://download.gluster.org/pub/gluster/purpleidea/screencasts/oh-my-vagrant-docker-screencast.ogv

If you watched the screencast, you should have a good overview of what’s possible. Let’s discuss some of these features in more detail.

Pull an arbitrary list of docker images:

If you use an image that was baked with vagrant-builder, you can make sure that an arbitrary list of docker images will be pre-cached into the base image so that you don’t have to wait for the slow docker registry every time you boot up a development vm.

This is easily seen in the CentOS-7.1 image definition file seen here. Here’s an excerpt:

VERSION='centos-7.1'
POSTFIX='docker'
SIZE='40'
DOCKER='centos fedora'		# list of docker images to include

The GlusterFS community gracefully hosts a copy of this image here.

If you’d like to add images to a vm you can add a list of things to pull in the docker omv.yaml variable:

---
:domain: example.com
:network: 192.168.123.0/24
:image: centos-7.1-docker
:docker:
- ubuntu
- busybox
:count: 1
: vms: []

This key is also available in the vms array.

Automatic docker builds:

If you have a Dockerfile in a vagrant/docker/*/ folder, then it will get automatically added to the running vagrant vm, and built every time you run a vagrant up. If the machine is already running, and you’d like to rebuild it from your local working directory, you can run: vagrant rsync && vagrant provision.

Automatic docker environments:

Building and defining docker applications can be a tricky process, particularly because the techniques are still quite new to developers. With Oh-My-Vagrant, this process is simplified for container developers because you can build an enhanced omv.yaml file which defines your app for you:

---
:domain: example.com
:network: 192.168.123.0/24
:image: centos-7.0-docker
:extern:
- type: git
  system: docker
  repository: https://github.com/purpleidea/docker-simple1
  directory: simple-app1
:docker: []
:vms: []
:count: 3

By listing multiple git repos in your omv.yaml file, they will be automatically pulled down and built for you. An example of the above running would look similar to this:

$ time vup omv1
Cloning into 'simple-app1'...
remote: Counting objects: 6, done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 6
Unpacking objects: 100% (6/6), done.
Checking connectivity... done.

Bringing machine 'omv1' up with 'libvirt' provider...
==> omv1: Creating image (snapshot of base box volume).
==> omv1: Creating domain with the following settings...
==> omv1:  -- Name:              omv_omv1
==> omv1:  -- Domain type:       kvm
==> omv1:  -- Cpus:              1
==> omv1:  -- Memory:            512M
==> omv1:  -- Base box:          centos-7.0-docker
==> omv1:  -- Storage pool:      default
==> omv1:  -- Image:             /var/lib/libvirt/images/omv_omv1.img
==> omv1:  -- Volume Cache:      default
==> omv1:  -- Kernel:            
==> omv1:  -- Initrd:            
==> omv1:  -- Graphics Type:     vnc
==> omv1:  -- Graphics Port:     5900
==> omv1:  -- Graphics IP:       127.0.0.1
==> omv1:  -- Graphics Password: Not defined
==> omv1:  -- Video Type:        cirrus
==> omv1:  -- Video VRAM:        9216
==> omv1:  -- Command line : 
==> omv1: Starting domain.
==> omv1: Waiting for domain to get an IP address...
==> omv1: Waiting for SSH to become available...
==> omv1: Starting domain.
==> omv1: Waiting for domain to get an IP address...
==> omv1: Waiting for SSH to become available...
==> omv1: Creating shared folders metadata...
==> omv1: Setting hostname...
==> omv1: Rsyncing folder: /home/james/code/oh-my-vagrant/vagrant/ => /vagrant
==> omv1: Configuring and enabling network interfaces...
==> omv1: Running provisioner: shell...
    omv1: Running: inline script
==> omv1: Running provisioner: docker...
    omv1: Configuring Docker to autostart containers...
==> omv1: Running provisioner: docker...
    omv1: Configuring Docker to autostart containers...
==> omv1: Building Docker images...
==> omv1: -- Path: /vagrant/docker/simple-app1
==> omv1: Sending build context to Docker daemon 54.27 kB
==> omv1: Sending build context to Docker daemon 
==> omv1: Step 0 : FROM fedora
==> omv1:  ---> 834629358fe2
==> omv1: Step 1 : MAINTAINER James Shubin <james@shubin.ca>
==> omv1:  ---> Running in 2afded16eec7
==> omv1:  ---> a7baf4784f57
==> omv1: Removing intermediate container 2afded16eec7
==> omv1: Step 2 : RUN echo Hello and welcome to the Technical Blog of James > README
==> omv1:  ---> Running in 709b9dc66e9b
==> omv1:  ---> b955154474f4
==> omv1: Removing intermediate container 709b9dc66e9b
==> omv1: Step 3 : ENTRYPOINT python -m SimpleHTTPServer
==> omv1:  ---> Running in 76840da9e963
==> omv1:  ---> b333c179dd56
==> omv1: Removing intermediate container 76840da9e963
==> omv1: Step 4 : EXPOSE 8000
==> omv1:  ---> Running in ebf83f08328e
==> omv1:  ---> f13049706668
==> omv1: Removing intermediate container ebf83f08328e
==> omv1: Successfully built f13049706668

real	1m12.221s
user	0m5.923s
sys	0m0.932s

All that happened in about a minute!

Conclusion:

I hope these tools help, if you’re following my git commits, you’ll notice that there are some new features I haven’t blogged about yet. Kubernetes integration exists, so please have a look, and hopefully I’ll have some screencasts and blog posts about this shortly.

Happy hacking,

James

Sharing dev environments with Oh-My-Vagrant

With Oh-My-Vagrant (omv) you can set up a dev environment in seconds. (Read the omv introduction if you’ve never used it before!) Since everything is defined in a single omv.yaml file, it is easy to share your cluster prototype with a friend! The one missing feature was associating code with this config file. This is now possible! Let me show you how it works…

In the omv.yaml file there is an extern variable. It is a list of each external repository which you’d like to include. Each element in this list is a hash of key value pairs. Currently four are supported: type, system, repository, directory.

An example will help you visualize this:

---
:domain: example.com
:network: 192.168.123.0/24
:image: fedora-21
:extern:
- type: git
  system: ansible
  repository: https://github.com/eparis/kubernetes-ansible
  directory: kubernetes
:reallyrm: true

In this example, we list one external repository. It is of type git, it is intended for use with the ansible integration provided by omv, the repository is hosted by eparis, and we’ll store this in a local directory called kubernetes.

We currently only support git repositories, but patches for other systems are welcome. A few different “systems” are supported, including puppet, docker and kubernetes. They each integrate with omv, and, as a result can pull code and modules into the appropriate places. Any repository path that is valid is acceptable (including local file paths) and lastly, the directory you choose is entirely up to you!

The most important part that I need to mention is the reallyrm variable. If this is set to true, and you remove a git repository from the list, omv will make sure that it removes it! Since some users might not expect this behaviour, it defaults to false, and shouldn’t bite you! If you’re not comfortable with it, don’t use it! I find it incredibly helpful.

Here’s a small screencast to show you some examples of this in action:

oh-my-vagrant-extern-screencast.ogv

I hope you enjoyed this. Please share and enjoy, and I’ll be back soon to explain some more of the features! Documentation patches are appreciated!

Happy hacking,

James