50
Anatomy of a Reusable Module Alessandro Franceschi github.com/example42 PuppetConf 2013 Saturday, August 24, 13

Anatomy of a Reuseable Module

Embed Size (px)

DESCRIPTION

What is a reusable Puppet module? What are its requirements? Why does it matter and why it's not always the best choice? And, most of all, HOW do you make reusable modules? This session is all about this: How to make modules written in a way that you can use them in many different environments (also with different OS) without modifying them. Alessandro Franceschi Freelance Consultant, Lab42 Founded an Internet Service Provider in 1995. Founded a IT consulting firm in 1999. Decided that it was funnier to do technical stuff rather than to manage companies and became a freelance consultant in 2006. Discovered Puppet in 2007. Passed the last years pursuing an insane passion for fun and profit: Puppet consulting and modules development.

Citation preview

Page 1: Anatomy of a Reuseable Module

Anatomy of aReusable Module

Alessandro Franceschigithub.com/example42

PuppetConf 2013Saturday, August 24, 13

Page 2: Anatomy of a Reuseable Module

Be����������� ������������������  patientplease

Saturday, August 24, 13

Page 3: Anatomy of a Reuseable Module

How����������� ������������������  dowe����������� ������������������  usePuppettoday?

Include classes

Set Params & Variables

DefineBusiness &

Integration Logic

ProvideConfiguration Files

ManageResources

Saturday, August 24, 13

Page 4: Anatomy of a Reuseable Module

ENC

How do we use Puppet today

Include classesmanifests/

site.pp

Set Parameters / Variables

Integration logic

Resources

ENC HIERA

SITE MODULES

SHARED MODULES

BAD

EDG

EConfiguration files

manifests/site.pp ENC HIERA

manifests/site.pp

BAD

?

BAD

?

BAD

SITE MODULES

SITE MODULES

SHARED MODULES

SITE MODULES

SHARED MODULES

manifests/site.pp

BAD

?

ENC

BAD

?

Saturday, August 24, 13

Page 5: Anatomy of a Reuseable Module

A����������� ������������������  reusable����������� ������������������  module

is����������� ������������������  all����������� ������������������  aboutCHOICE

Operating Systems

Infrastructures

Scales

Node classifiers

Installation methods

Alternative setups

Saturday, August 24, 13

Page 6: Anatomy of a Reuseable Module

The����������� ������������������  consof����������� ������������������  a

reusablemodule

Harder & LongerDevelopment

Complexity

Verbosity

Not Optimized for performance

Saturday, August 24, 13

Page 7: Anatomy of a Reuseable Module

THE PARAMETERSDILEMMA

Managed resources attributesApplication specific config options

Application logic and behaviourIntegration with other modules

Saturday, August 24, 13

Page 8: Anatomy of a Reuseable Module

Parameters: Resources attributesEnough: $package = $redis::params::package, $service = $redis::params::service, $service_ensure = 'running', $service_enable = true, $file = $redis::params::file, $file_notify = "Service['redis']", $file_source = undef, $file_content = undef,

Too much? $package_provider = undef, $file_owner = $redis::params::file_owner, $file_group = $redis::params::file_group, $file_mode = $redis::params::file_mode, $file_replace = $redis::params::file_replace,

Benefits from: A standard naming convention

Saturday, August 24, 13

Page 9: Anatomy of a Reuseable Module

Parameters: Application optionsEnough: $puppet_server = “puppet.${::domain}”, $syslog_server = “syslog.${::domain}”, $munin_server = “munin.${::domain}”, $dns_servers = [ '8.8.8.8' , '8.8.4.4' ],

Too much! $anonymous_enable = true, $anon_mkdir_write_enable = true, $anon_upload_enable = false, $chroot_list_enable = true, $chroot_list_file = '/etc/vsftpd/chroot_list',

$resourcefile = $nagios::params::resourcefile, $statusfile = $nagios::params::statusfile, $commandfile = $nagios::params::commandfile, $resultpath = $nagios::params::resultpath, $retentionfile = $nagios::params::retentionfile, $p1file = $nagios::params::p1file,

Benefits from: Template + Options Hash patternSaturday, August 24, 13

Page 10: Anatomy of a Reuseable Module

Parameters: Application logicExamples: $install_client = true, $install_stomp_server = true, $install_plugins = true, $use_ssl = false, $munin_autoconfigure = true, $service_autorestart = true, $manage_package_repo = true, $run_initdb = undef,

Benefits from: A standard naming convention

Saturday, August 24, 13

Page 11: Anatomy of a Reuseable Module

Parameters: Modules IntegrationsExamples: $mongo_db_host = $graylog2::params::mongo_db_host, $mongo_db_port = $graylog2::params::mongo_db_port, $mongo_db_name = $graylog2::params::mongo_db_name, $mongo_user = $graylog2::params::mongo_user, $mongo_password = $graylog2::params::mongo_password, $elasticsearch_template = $graylog2::params::elasticsearch_template, $elasticsearch_path = $graylog2::params::elasticsearch_path,

$database = $puppetdb::params::database, $manage_redhat_firewall = $puppetdb::params::manage_redhat_firewall, $db_type = 'mysql',

Benefits from: Shared Stacks

Saturday, August 24, 13

Page 12: Anatomy of a Reuseable Module

PATTERNSREUSABILITY

Saturday, August 24, 13

Page 13: Anatomy of a Reuseable Module

Managing����������� ������������������  files

Let user decide how to manage

configuration files.

Alternatives:sourcecontentconcataugeas

custom types

Saturday, August 24, 13

Page 14: Anatomy of a Reuseable Module

Managing files: source & contentredis/manifests/init.ppclass redis ( $file = $redis::params::file, $file_source = undef, $file_template = undef, $file_content = undef, ) {

[...]

$managed_file_content = $file_content ? { undef => $file_template ? { undef => undef, default => template($file_template), }, default => $file_content, }

[...]

if $redis::file { file { 'redis.conf': path => $redis::file, source => $redis::file_source, content => $redis::managed_file_content, } }}

Provide the Puppet path of an erb templateclass { ‘redis’: file_template => ‘site/redis/redis.conf.erb’,}

Provide directly the content attributeclass { ‘redis’: file_content => “template(‘site/redis/redis.conf.erb’)”,}

Provide a fileserver source pathclass { ‘redis’: file_source => ‘puppet:///modules/site/redis/redis.conf’,}

Manage the configuration file with other methods(augeas, concat...)class { ‘redis’: }

Saturday, August 24, 13

Page 15: Anatomy of a Reuseable Module

Multiple����������� ������������������  config����������� ������������������  files����������� ������������������  

Add parametersto main class

Use a genericconf define

Manage the whole configuration dir

Saturday, August 24, 13

Page 16: Anatomy of a Reuseable Module

Multiple files: Add parameterselasticsearch/manifests/init.ppclass elasticsearch (

$file = $elasticsearch::params::file, $file_source = undef, $file_template = undef, $file_content = undef,[...]

$init_script_file = '/etc/init.d/elasticsearch', $init_script_file_template = 'elasticsearch/init.erb',

$init_options_file = $elasticsearch::params::init_options_file, $init_options_file_template = 'elasticsearch/init_options.erb',

Provide custom templates for the main file and the init scriptclass { ‘elasticsearch’: file_template => ‘site/elasticsearch/elasticsearch.yml.erb’, init_script_file_template => ‘site/elasticsearch/elasticsearch.init.erb’,}

Saturday, August 24, 13

Page 17: Anatomy of a Reuseable Module

Multiple files: Generic conf definenova/manifests/conf.ppdefine nova::conf ( $source = undef, $template = undef, $content = undef, $path = undef,[...] $options_hash = undef, $ensure = present ) {

include nova $managed_path = $path ? { undef => "${nova::config_dir}/${name}", default => $path, }[...] file { "nova_conf_${name}": ensure => $ensure, source => $source, content => $managed_content, path => $managed_path, mode => $managed_mode, owner => $managed_owner, group => $managed_group, require => $managed_require, notify => $managed_notify, replace => $managed_replace, }}

Provide a custom template for an alternative config file in config_dirnova::conf { ‘rootwrap.conf’: template => ‘site/nova/rootwrap.conf.erb’,}

Saturday, August 24, 13

Page 18: Anatomy of a Reuseable Module

Multiple files: Whole config dirredis/manifests/init.ppclass redis ( $dir = $redis::params::dir, $dir_source = undef, $dir_purge = false, $dir_recurse = true, ) {[...]

$dir_ensure = $ensure ? { 'absent' => 'absent', 'present' => 'directory', }

if $redis::dir_source { file { 'redis.dir': ensure => $redis::dir_ensure, path => $redis::dir, source => $redis::dir_source, recurse => $redis::dir_recurse, purge => $redis::dir_purge, force => $redis::dir_purge, notify => $redis::file_notify, require => $redis::file_require, } }

}

Provide a custom source for the whole config_dirclass { ‘redis’: dir_source => ‘puppet:///modules/site/redis/conf/’,}

Provide a custom source for the whole config_dir and purge any not managed config fileclass { ‘redis’: dir_source => ‘puppet:///modules/site/redis/conf/’, dir_purge => true,}

Saturday, August 24, 13

Page 19: Anatomy of a Reuseable Module

Managing����������� ������������������  Users

Everyone has his own users...

Leave options to decide

if, how and where to manage the ones

the module requires.

Saturday, August 24, 13

Page 20: Anatomy of a Reuseable Module

Managing Userselasticsearch/manifests/init.ppclass elasticsearch { $ensure = 'present',[...] $user = 'elasticsearch', $user_uid = undef, $user_gid = undef, $user_groups = undef, $user_class = 'elasticsearch::user',[...] if $elasticsearch::user_class { require $elasticsearch::user_class }

elasticsearch/manifests/user.ppclass elasticsearch::user { @user { $elasticsearch::user : ensure => $elasticsearch::ensure, comment => "${elasticsearch::user} user", password => '!', managehome => false, uid => $elasticsearch::user_uid, gid => $elasticsearch::user_gid, groups => $elasticsearch::user_groups, shell => '/bin/bash', } User <| title == $elasticsearch::user |>}

Do not create the requested userclass { ‘elasticsearch’: user_class => undef,}

Provide the user in a different custom classclass { ‘elasticsearch’: user_class => 'site::users',}

Run elasticsearch with a different userclass { ‘elasticsearch’: user => 'apache',}

Saturday, August 24, 13

Page 21: Anatomy of a Reuseable Module

Managingextra����������� ������������������  

resources

Options to specify custom classes

Options to passan hash to

create_resources

Saturday, August 24, 13

Page 22: Anatomy of a Reuseable Module

Extra Resources: Custom classeselasticsearch/manifests/init.ppclass elasticsearch (

$dependency_class = 'elasticsearch::dependency', $monitor_class = 'elasticsearch::monitor', $firewall_class = 'elasticsearch::firewall', $my_class = undef,

) {

[...]

if $elasticsearch::dependency_class { include $elasticsearch::dependency_class }

if $elasticsearch::monitor and $elasticsearch::monitor_class { include $elasticsearch::monitor_class }

if $elasticsearch::firewall and $elasticsearch::firewall_class { include $elasticsearch::firewall_class }

if $elasticsearch::my_class { include $elasticsearch::my_class }[...]

Provide the modules dependencies with a custom classclass { ‘elasticsearch’: dependency_class => 'site::dep_elasticsearch',}

Saturday, August 24, 13

Page 23: Anatomy of a Reuseable Module

Extra Resources: Resources Hashelasticsearch/manifests/init.ppclass elasticsearch ( $create_resource = undef, $resources_hash = undef,

) {

[...]

if $create_resource { create_resources( $create_resource , $resources_hash ) }

Alternative: A single hash that includes resources and resources_hash

Provide the modules dependencies with a custom classclass { ‘elasticsearch’: create_resource => 'file', resources_hash => { path => '/etc/elasticsearch/my_file', content => template('site/elasticsearch/my_file.erb), mode => '0600', },}

Saturday, August 24, 13

Page 24: Anatomy of a Reuseable Module

ManagingPackages

andServices

Names change

Custom packagesare common

Leave choice,optionally

Saturday, August 24, 13

Page 25: Anatomy of a Reuseable Module

Managing packagesopenssh/manifests/init.ppclass openssh (

$ensure = 'present', $version = undef, $package = $openssh::params::package,[...] ) {

if $version and $ensure == 'present' { $managed_package_ensure = $version } else { $managed_package_ensure = $ensure }

if $openssh::package { package { $openssh::package: ensure => $openssh::managed_package_ensure, } }

openssh/manifests/params.ppclass openssh::params {

$package = $::osfamily ? { Suse => 'openssh', OpenBSD => '', default => 'openssh-server', }

Install a custom company-openssh packageclass { ‘openssh’: package => 'company-openssh',}

Saturday, August 24, 13

Page 26: Anatomy of a Reuseable Module

Managing servicesopenssh/manifests/init.ppclass openssh ( $service = $openssh::params::service, $service_ensure = 'running', $service_enable = true,[...] ) { if $ensure == 'absent' { $managed_service_enable = undef $managed_service_ensure = stopped } else { $managed_service_enable = $service_enable $managed_service_ensure = $service_ensure }

if $openssh::service { service { $openssh::service: ensure => $openssh::managed_service_ensure, enable => $openssh::managed_service_enable, } }

openssh/manifests/params.ppclass openssh::params {

$service = $::osfamily ? { Debian => 'ssh', default => 'sshd', }[...]

Manage a custom company-openssh serviceclass { ‘openssh’: service => 'company-openssh',}

Saturday, August 24, 13

Page 27: Anatomy of a Reuseable Module

ManagingInstallation����������� ������������������  options

Let users decide:

OS Packages

Upstream tarballs

Provider

Saturday, August 24, 13

Page 28: Anatomy of a Reuseable Module

Installation optionselasticsearch/manifests/init.ppclass elasticsearch ( $package_provider = undef,

$install = 'package', $install_base_url = $elasticsearch::params::install_base_url, $install_source = undef, $install_destination = '/opt', ) {[...] $managed_file = $elasticsearch::install ? { package => $elasticsearch::file, default => "${elasticsearch::home_dir}/config/elasticsearch.yml", }[...]case $elasticsearch::install { package: { package { $elasticsearch::package: ensure => $elasticsearch::managed_package_ensure, provider => $elasticsearch::package_provider, } } upstream: { puppi::netinstall { 'netinstall_elasticsearch': url => $elasticsearch::managed_install_source, destination_dir => $elasticsearch::install_destination, owner => $elasticsearch::user, group => $elasticsearch::user, }[...]

Install elasticsearch from upstream sourceclass { ‘elasticsearch’: install => 'upstream', install_source => 'https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-0.90.3.zip',}

Saturday, August 24, 13

Page 29: Anatomy of a Reuseable Module

Templatesand����������� ������������������  hashes

Managing specific application configs

parameters may get out of control

A single config hashto show them all

A custom templateto use them

Application specific configs

THE PARAMETERSDILEMMA

Saturday, August 24, 13

Page 30: Anatomy of a Reuseable Module

Options Hash: Setupopenssh/manifests/init.ppclass openssh ([...] $file_template = undef, $options_hash = undef,

site/templates/openssh/sshd_config.erb# File Managed by Puppet[...] Port <%= scope.function_options_lookup(['Port','22']) %> PermitRootLogin <%= scope.function_options_lookup(['PermitRootLogin','yes']) %> UsePAM <%= scope.function_options_lookup(['UsePAM','yes']) %>[...]

* Function options_lookup currently in Example42's Puppi module

Alternative site/templates/openssh/sshd_config.erb Port <%= scope.lookupvar('openssh::options_hash')['Port'] ||='22' %> PermitRootLogin <%= scope.lookupvar('openssh::options_hash')['PermitRootLogin'] ||='yes' %> UsePAM <%= scope.lookupvar('openssh::options_hash')['UsePAM'] ||='yes' %>[...]

Saturday, August 24, 13

Page 31: Anatomy of a Reuseable Module

Options Hash: UsageUsage (with Hiera):include openssh

/etc/puppet/hieradata/global.yml:---openssh::file_template: 'site/openssh/sshd_config.erb'openssh::file_options_hash: Port: '22222' PermitRootLogin: 'no'

Usage (with parametrized class):class { 'openssh': file_template => 'site/openssh/sshd_config.erb' file_options_hash => { Port => '22222', PermitRootLogin => 'no', }

Saturday, August 24, 13

Page 32: Anatomy of a Reuseable Module

STANDARDSNAMING

Managed resources attributes

THE PARAMETERSDILEMMA

Saturday, August 24, 13

Page 33: Anatomy of a Reuseable Module

TheHandy����������� ������������������  Grailof����������� ������������������  Modules����������� ������������������  Standards

A blog post*

Some discussions on Puppet-Users

github.com/stdmod

Naming standardsfor modules parameters

Community driven

(draft 0.0.2)

* http://www.example42.com/?q=The_handy_Grail_of_Modules_StandardsSaturday, August 24, 13

Page 34: Anatomy of a Reuseable Module

Benefits����������� ������������������  of����������� ������������������  suggested

(and����������� ������������������  shared)naming����������� ������������������  

conventions

Saner User Experience

Better modules Interoperability

Reusability Patterns

Predictability in usage and

development

Saturday, August 24, 13

Page 35: Anatomy of a Reuseable Module

Stdmod Params: Main resources### General parametersensure (enable?) version (package_version?)

### Package - Service - Main configuration filepackage (package_name?)package_ensurepackage_providerpackage_* [any relevant package type attribute]

service (service_name?)service_ensureservice_enableservice_subscribeservice_*

file (file_path? config_file? config?)file_source (source? config_file_source? config_source?)file_template (template? config_file_template? config_template?)file_content (content? config_file_content? config_content?)file_* (config_file_*? config_*?)

file_options_hash (options? options_hash? file_options?)Saturday, August 24, 13

Page 36: Anatomy of a Reuseable Module

Stdmod Params: Extra resourcesother_packageother_package_*

client_packageclient_package_*

server_packageserver_package_*

other_serviceother_service_*

log_filelog_file_*

pid_filepid_file_*

init_script_fileinit_script_file_*init_config_fileinit_config_file_*

Saturday, August 24, 13

Page 37: Anatomy of a Reuseable Module

Stdmod Params: Installation ### Parameter related parameters

installinstall_urlinstall_base_urlinstall_sourceinstall_destinationinstall_pre_execinstall_pre_exec_*install_post_execinstall_post_exec_*

install_script_fileinstall_script_file_*install_response_fileinstall_response_file_*

Saturday, August 24, 13

Page 38: Anatomy of a Reuseable Module

Stdmod Params: Monitormonitormonitor_toolmonitor_hostmonitor_portmonitor_protocolmonitor_urlmonitor_processmonitor_servicemonitor_config_hash

Saturday, August 24, 13

Page 39: Anatomy of a Reuseable Module

Stdmod Params: Firewallfirewallfirewall_srcfirewall_dstfirewall_portfirewall_protocol

Saturday, August 24, 13

Page 40: Anatomy of a Reuseable Module

STACKSMODULES

Integrations of modules

THE PARAMETERSDILEMMA

Saturday, August 24, 13

Page 41: Anatomy of a Reuseable Module

Why����������� ������������������  reinventing����������� ������������������  the����������� ������������������  stack?

We always use stacks.

We need them to make something

useful with modules.

What about:Sharing?

Best practices?Standardization?

Saturday, August 24, 13

Page 42: Anatomy of a Reuseable Module

Stacks - A Simple Sample class stack::logs (

$ensure = 'present',

$syslog_server = false, $syslog_server_port = '5544',

$elasticsearch_server = false, $elasticsearch_server_port = '9200', $elasticsearch_cluster = 'logs', $elasticsearch_java_opts = '-Xmx2g -Xms1g',

$install_logstash = false, $install_elasticsearch = false, $install_kibana = false,

$install_graylog2 = false, $install_graylog2_webinterface = false,

$syslog_config_template = 'stack/logs/syslog.conf.erb', $logstash_config_template = 'stack/logs/logstash.conf.erb', $elasticsearch_config_template = 'stack/logs/elasticsearch.yml.erb', $kibana_config_template = 'stack/logs/config.js.erb', $graylog2_config_template = 'stack/logs/graylog2.conf.erb',

) {

[... TO BE CONTINUED ...]

Saturday, August 24, 13

Page 43: Anatomy of a Reuseable Module

Stacks - A Simple Sample[...] if $syslog_server { rsyslog::config { 'logstash_stack': content => template($syslog_config_template), } }

if $install_logstash { class { 'logstash': template => $logstash_config_template, } }

if $install_elasticsearch { class { 'elasticsearch': java_opts => $elasticsearch_java_opts, template => $elasticsearch_config_template, } }

[...]

Saturday, August 24, 13

Page 44: Anatomy of a Reuseable Module

Stacks - UsageOn any host:stack::logs { 'central': syslog_server => 'syslog.example42.com',}

On the Logstash (syslog) server:stack::logs { 'central': syslog_server => 'syslog.example42.com', install_logstash => true, elasticsearch_server => 'el.example42.com',}

On the Elasticsearch server(s), with a custom configuration file:stack::logs { 'central': syslog_server => 'syslog.example42.com', install_elasticsearch => true, elasticsearch_server => 'el.example42.com', elasticsearch_config_template => 'site/logs/elasticsearch.yml.erb',}

On the Kibana server:stack::logs { 'central': syslog_server => 'syslog.example42.com', install_kibana => true, elasticsearch_server => 'el.example42.com',}

Saturday, August 24, 13

Page 45: Anatomy of a Reuseable Module

TheStacksLogic

Stacks are localModules are shared

Higher level interface

Integrate different set of modules

Preserve moduleslocal change

Saturday, August 24, 13

Page 46: Anatomy of a Reuseable Module

ENC

How do we use Puppet today

Include classesmanifests/

site.pp

Set Parameters / Variables

Integration logic

Resources

ENC HIERA

SITE MODULES

SHARED MODULES

BAD

EDG

EConfiguration files

manifests/site.pp ENC HIERA

manifests/site.pp

BAD

?

BAD

?

BAD

SITE MODULES

SITE MODULES

SHARED MODULES

SITE MODULES

SHARED MODULES

manifests/site.pp

BAD

?

ENC

BAD

?

STACKS

STACKS

STACKS

Saturday, August 24, 13

Page 47: Anatomy of a Reuseable Module

Standardsfor

Stacks?

Usual benefits:

User Experience

Interoperability

Higher level API exposure

Possible GUI Integrations

Saturday, August 24, 13

Page 48: Anatomy of a Reuseable Module

Steps

Define stdmod naming conventions

Explore Stacks design and approach

Create templatesfor stacks and

modules

ExploreGUI integrations

Saturday, August 24, 13

Page 49: Anatomy of a Reuseable Module

SO����������� ������������������  Long and thanksfor all the fish!

Graphics: www.tatlin.net

@alvagante

Saturday, August 24, 13

Page 50: Anatomy of a Reuseable Module

SO����������� ������������������  Long and thanksfor all the fish!

Graphics: www.tatlin.net

@alvagante

Saturday, August 24, 13