Puppet Development Workflow

Post on 09-Aug-2015

169 views 0 download

transcript

Puppet Development Workflow

Bio - Jeff Smith

• Manager, Site Reliability Engineering at Grubhub

• Puppet User for about 2 years

• Yes, we are also hiring.

• Yes, there is free food. Yes, it's totally awesome to work here.

Email: jsmith@grubhub.comTwitter: @DarkAndNerdyBlog: http://www.allthingsdork.com

Agenda• High level Environment Overview

• Local workstation setup

• How we solve problems

• Branching strategy

• Committing, Automated Testing and Publishing

Puppet Environment

Local Workstation SetupEditor Setup

• VIM

• tmux

• Nerd Tree

• Powerline

Local Testing ToolsListing tools can help you can syntax errors before you commit code!

Local Linting Tools

• jsonlint

• puppet parser validate

• erb syntax checker

VIM Linting Function Example function LintFile() let l:currentfile = expand('%:p') if &ft == 'puppet' let l:command = "puppet-lint " . l:currentfile elseif &ft == 'eruby.html' let l:command = "erb -P -x -T '-' " . l:currentfile . "| ruby -c" elseif &ft == 'json' let l:command = 'jsonlint -q ' . l:currentfile end silent !clear execute "!" . l:command . " " . bufname("%") endfunction map :call LintFile()

GuardGuard is a command line tool to easily handle events on file system modifications.

guard-puppet - A configuration of Guard geared towards Puppet manifests and syntax.

Find more at https://github.com/johnbintz/guard-puppet

Vagrant EnvironmentIt's easier to make changes locally then to a committed repository. Use Vagrant for test VMs

• Puppet Master

• 2 Agents (only 1 started by default)

• Shares the puppet repo on the Host machine and uses that as the module path in the VM

config.vm.define "master" do |master| master.vm.box = "centos65-base-small" if not ENV['PUPPETREPO'].nil? puppet_path = ENV['PUPPETREPO'] else puppet_path = ENV['HOME']+'/Development/puppet' end config.vm.synced_folder puppet_path, "/manifests", type: "nfs" master.vm.network "private_network", ip: "172.16.1.10" master.vm.network :forwarded_port, host: 10443, guest: 443 config.vm.hostname = "pe.local.vm" master.vm.provision :hosts config.pe_build.version = '3.3.2' config.pe_build.download_root = 'http://s3.amazonaws.com/specialbuildbucket/' master.vm.provision :hosts do |provisioner| provisioner.add_host '172.16.1.11', ['agent.local.vm', 'agent'] provisioner.add_host '172.16.1.12', ['agent-2.local.vm', 'agent-2'] end master.vm.provision :pe_bootstrap do |provisioner| provisioner.role = :master #provisioner.relocate_manifests = true end master.vm.provision :shell, inline: "service iptables stop;chkconfig --level 3 iptables off" master.vm.provision :shell, path: 'bootstrap.sh'

end

How We Approach DevelopmentGuiding Principles • YAGNI - You Ain't Gonna Need It

• DRY - Don't Repeat Yourself

• Modules, Profiles, Roles

• Have respect for what you're doing

• Write your manifests in Puppet

You Ain't Gonna Need ItIt's great to build your module to support CentOS, Ubuntu, Solaris, AIX. But if your shop only has CentOS, has only ever used CentOS and has plans to only use CentOS...........code for CentOS.

• Adds too much time to development

• Complicates a potentially simple solution

• Not being tested in those environments so probably won't work anyways

Don't Repeat YourselfWhen you see the same piece of code showing up over and over again, there's an opportunity.

• Limit the scope of where changes need to happen

• Compose your modules in a way that makes them reusable

• Hard-Coding is the devil

Modules, Profiles and RolesCompose your Puppet manifests into 3 separate categories

• Modules - Contains the implementation details of a module. Responsible for the plumbing.

• Profiles - Leverages modules to implement business logic. Takes your "Apache" module and turns it into "Payroll_Web"

• Roles - Takes all of the profiles necessary to make a complete system.

Only assign roles to a node and only 1 role per node

Have Respect for What You're Doing

YOU ARE PROGRAMMING

Write Your Manifests in Puppet file { '/opt/app/config.ini': ensure => file, content => template('config.erb') }

OR

payroll_config { 'database_connection': property => 'dbserver', value => 'dbserver1.example.com:3306' }

Time to Write Some Code!

Puppet Forge is Your Friend

• Someone has the same problem as you

• More eyeballs on the same problem

• More flexible

• Less Work

Branching StrategyLargely will depend on your environment. We have a single Puppet repository for everything. (Don't do this)

3 Phases of Development

Local => Preprod => Production

Local Development• Use Vagrant for test VMs

• Make all changes prior to commit

• Use linting

• Test using your local Puppet Master VM and Agent

• Set FACTS or variables via Custom Facts

Writing Test CasesBe highly selective about what you test in Puppet.

Puppet Code file { '/opt/app/config.ini': ensure => file, content => template('app/config.erb') }

RSpec Puppet Test it { should contain_file('/opt/app/config.ini) }

Puppet

Things To Test1. File Syntax

2. Conditional Branches

3. Interpreted Values (e.g. RegEx evaluations, Facts)

4. Catalog Compilation

Getting Code to the Puppet Master• Work off a branch. Branch name should match your ticket

• Push your branch to the remote origin server

• Push your branch to the Pre-prod Puppet Master Remote Repo (optional)

• Create a Pull Request from your branch, into the Develop Branch

Branching Workflow Diagram

Automated Test ExecutionCI Server watches master/develop branch. Executes tests on change

1. Script executes to determine which files have changed

2. Linting is performed on changed files (if applicable)

3. Catalog compilation of changed manifests

4. Execution of manifests specific tests (if applicable)

5. Deployment of code to the Puppet Masters

Auto Deployment

Building ConfidenceWith enough confidence in the process you should be able to

• Deploy your changes to production after a commit and successful automated testing

• Regular Puppet runs on production systems

• Iterate on changes faster

Move slowly. Go piece by piece. Every small step you take adds value immediately. Bite off small bits.

Questions?