Managing CoreOS With Puppet



To be perfectly honest, the only reason I wanted to do this is because I submitted a talk to PuppetConf on the subject and it got accepted. Woops. Way to go past Lucy.

There are a few cases where managing CoreOS with Puppet makes sense though.


Here's the basic steps we'll be taking to set up a puppet agent running on CoreOS, all running in Vagrant (because I'm too cheap to buy cloud space for this).

Stand Up Puppet Master and Agent

The first thing I wanted to do was get some basic Puppet infrastructure up that I could integrate my CoreOS nodes into. I used the following Vagrantfile and provisioning scripts to get my VMs running.

I also installed the vagrant-hosts plugin to make sure the VMs could talk to each other, and minimize the amount of networking voodoo I had to do: vagrant plugin install vagrant-hosts

$ tree
└── Vagrantfile


Configuration file for the virtual machines we'll install puppet master and agents on

Vagrant.configure("2") do |config|
  config.vm.define "puppetmaster" do |master| = "ubuntu/xenial64"
    master.vm.hostname = "puppet-master" :"private_network", ip: ""
    master.vm.provision :hosts, :sync_hosts => true
    master.vm.provision "shell", path: ""
    master.vm.provider "virtualbox" do |vb|
      vb.customize ["modifyvm", :id, "--memory", "4096"]

  config.vm.define "puppetagent" do |agent| = "centos/7"
    agent.vm.hostname = "puppet-agent" :"private_network", ip: ""
    agent.vm.provision :hosts, :sync_hosts => true
    agent.vm.provision "shell", path: ""

Provisioning script for the vagrant VM with puppet master on it


if ps aux | grep "puppet" | grep -v grep 2> /dev/null
  echo "Puppet Master is already installed. Exiting..."
  # Install puppet master
  sudo dpkg -i puppetlabs-release-pc1-xenial.deb
  sudo apt-get -yq update
  sudo apt-get -yq install puppetserver
  #sudo apt-get -yq install puppet
  sudo service puppetserver start

  # Configure /etc/hosts file
  echo "# Host config for Puppet Master and Agent Nodes   puppet-master puppet-master   puppet-agent puppet-agent" >> /etc/hosts

  # Add optional alternate DNS names to /etc/puppet/puppet.conf
  sudo echo "
  dns_alt_names = puppet,puppet-master" >> /etc/puppetlabs/puppet/puppet.conf

Provisioning script for the vagrant VM with puppet agent on it


if ps aux | grep "puppet" | grep -v grep 2> /dev/null
  echo "Puppet Agent is already installed. Moving on..."
  # Install puppet agent
  sudo rpm -Uvh
  sudo yum install -y puppet-agent
  sudo /opt/puppetlabs/bin/puppet resource service puppet ensure=running enable=true

  # Configure /etc/hosts file
  sudo echo "# Host config for Puppet Master and Agent Nodes   puppet-master puppet-master   puppet-agent puppet-agent" >> /etc/hosts

  # Add optional alternate DNS names to /etc/puppet/puppet.conf
  sudo echo "[agent]
  server=puppet-master" >> /etc/puppetlabs/puppet/puppet.conf

  sudo /opt/puppetlabs/bin/puppet agent --enable

Once everything is in place, we'll start up the VMs:

vagrant up

And run puppet on the agent and master

vagrant ssh puppetagent
sudo su -
puppet agent -t
exit && exit
vagrant ssh puppetmaster
sudo su -
puppet agent -t
puppet cert list --all
puppet cert sign --all

Create CoreOS Agent Node

The next step is to integrate a CoreOS node running puppet agent in a container into our puppet infrastructure. This involves a lot of changes, so let's break it down:

Add the following block to your Vagrantfile in the 'config' block

config.vm.define "coreosagent" do \|agent\|
  agent.ssh.insert_key = false
  agent.ssh.forward_agent = true = "coreos-beta"
  agent.vm.box_url = ""
  agent.vm.hostname = "coreos-agent"

  agent.vm.provider :virtualbox do \|v\|
    # On VirtualBox, we don't have guest additions or functional vboxsf
    # in CoreOS, so tell Vagrant that so it can be smarter.
    v.check_guest_additions = false
    v.functional_vboxsf = false
    v.memory = 2048
    v.cpus = 1
    v.customize ["modifyvm", :id, "--cpuexecutioncap", "100"]
  end :private_network, ip: ""
  agent.vm.provision :hosts, :sync_hosts => true

  if File.exist?(CLOUD_CONFIG_PATH)
    agent.vm.provision :file, :source => "#{CLOUD_CONFIG_PATH}", :destination => "/tmp/vagrantfile-user-data"
    agent.vm.provision :shell, :inline => "mv /tmp/vagrantfile-user-data /var/lib/coreos-vagrant/", :privileged => true

You'll also need the following in a file called config.rb

#Size of the CoreOS cluster created by Vagrant
# Used to fetch a new discovery token for a cluster of size $num_instances
# Official CoreOS channel from which updates should be downloaded

# Automatically replace the discovery token on 'vagrant up'

if File.exists?('user-data') && ARGV[0].eql?('up')
  require 'open-uri'
  require 'yaml'

  token = open($new_discovery_url).read

  data = YAML.load(IO.readlines('user-data')[1..-1].join)

  if data.key? 'coreos' and data['coreos'].key? 'etcd'
    data['coreos']['etcd']['discovery'] = token

  if data.key? 'coreos' and data['coreos'].key? 'etcd2'
    data['coreos']['etcd2']['discovery'] = token

  # Fix for YAML.load() converting reboot-strategy from 'off' to `false`
  if data.key? 'coreos' and data['coreos'].key? 'update' and data['coreos']['update'].key? 'reboot-strategy'
    if data['coreos']['update']['reboot-strategy'] == false
      data['coreos']['update']['reboot-strategy'] = 'off'

  yaml = YAML.dump(data)'user-data', 'w') { |file| file.write("#cloud-config\n\n#{yaml}") }

And lastly add a user-data file (your cloud-config file)


hostname: coreos-agent

  - name: puppet.service
    command: start
    content: |

      ExecStartPre=-/usr/bin/docker kill puppet1
      ExecStartPre=-/usr/bin/docker rm puppet1
      ExecStartPre=/usr/bin/docker pull puppet/puppet-agent


  - name:
    runtime: true
    content: |


Now we'll get that machine up and running:

vagrant up
vagrant ssh coreosagent

And wham, you're in a coreos machine!

Connecting Agent to Master

The only thing left to do is start our puppet agent container on the CoreOS VM and get it connected to the master.

Add puppet.conf to agent

Since our CoreOS machine doesn't know it's a puppet agent yet (or about puppet at all), we need to manually add /etc/puppetlabs/puppet/puppet.conf to configure the agent that will run in a docker container.

Test It Out

Ok, let's make sure our setup is actually working!