其它工具 CFEngine Puppet and Chef

scmroad · 发布于 2012年4月09日 · 230 次阅读
96

[i=s] 本帖最后由 scmroad 于 2012-4-9 10:50 编辑

Introduction

Over the past few years, the topic of Infrastructure Automation has received a huge amount of attention. The three most commonly used tools for doing this (in order of appearance) are CFEngine, Puppet, and Chef. This article explores each of them by using one to set up another. If you have a chef-server or Hosted Chef account, you can follow along by following the instructions in the setup section. (Full disclosure: I work for Opscode, creators of Chef.)

Infrastructure

“Infrastructure” turns out to be the hardest thing to explain when discussing automation, yet is the most critical to understand. In this context, Infrastructure isn’t anything physical (or virtualized) like servers or networks. Instead, what we’re talking about is all the “stuff” that is configured across machines to enable an application or service.

In practice, “stuff” translates to operating system baselines, kernel settings, disk mounts, OS user accounts, directories, symlinks, software installations, configuration files, running processes, etc. People of the ITIL persuasion may think of these as Configuration Items. Units of management are composed into larger constructs, and complexity arises as these arrangements become more intricate.

Services running in an Infrastructure need to communicate with each other, and do so via networks. Even when running on a single node, things still communicate over a loopback address or a Unix domain socket. This means that Infrastructure has a topology, which is in itself yet another thing to manage.

Automation

Here is a picture of a duck.

This duck happens to be an automaton. An automaton is a self-operating machine. This one pretends to digest grain. It interacts with it’s environment by taking input and producing output. To continue operating, the duck requires maintenance. It needs to be wound, cleaned, and repaired. Automated services running on a computer are no different.

Once turned on, an automated service takes input, does something useful, then leaves logs and other data in it’s wake. It’s machinery is the arrangement of software installation, configuration, and the running state of a process. Maintenance is performed in a control loop, where an agent comes around at regular intervals inspecting it’s parts and fixing anything that’s broken.

In automated configuration management, the name of the game is hosting policy. The agents that build and maintain systems pull down blueprints and set to work building our automatons. When systems come back up from maintenance or new ones spring into existence, they configure themselves by downloading policy from the server.

Setup

If you’d like to follow along by configuring your own machines with knife, follow the setup instructions here. The setup will get your Chef workstation configured, code checked out from my blog git repo, and uploaded to chef-server for use. Otherwise, you can just browse the source here

CFEngine

CFEngine is a system based on promise theory. Promises are the basic atoms of the CFEngine universe. They have names, types, and intentions (among other things), and each acts as a convergent operator to move it’s subject toward an intended state. Like the parts in our duck, promises are assembled to create a larger whole.

Promises of various types are capable of different things. Promises of type “package” can interact with a package manager to make sure somthing is installed or removed, while a promise of type “file”, can copy, edit, and set permissions. Processes can be started or stopped, and commands can be ran if needed. Read all about them in the CFEngine reference manual.

Promises provide a declarative interface to resources under management, which has the remarkably handy attribute of being idempotent. An idempotent function gives the same result when applied multiple times. This allows our duck repairing maintence loop (in the form of cf-agent on a cron) to come around and safely execute instructions without having to worry about side effects. Consider “the line ‘foo’ should exist in the file” vs “append ‘foo’ to the end of the file”; the non-declarative ‘append’ would not be safe to repeat.

Convergent maintenance refers to the continuous repair of a system towards a desired state. At the individual promise level, convergence happens in a single run of the maintenance loop. If a package is supposed to be installed but isn’t, action will be taken to fix it. If a process is not running but should be, action will be taken again. Convergence in a larger system of promises can take multiple runs if things are processed in a non-optimal order. Consider the following:[code]Start the NTP service. Make sure the NTP configuration file is correct, restart the NTP service if repaired. Install the NTP package.[/code]Assuming a system with a base install, the first promise would fail to be kept. The NTP binary is not available, since we haven’t installed it’s package yet. The second promise would write the configuration file, but fail to restart the service. The third promise would succeed, assuming an appropriate package repo was available and functioning properly. After the first run is complete, the system has converged closer to where we want it to be, but isn’t quite there yet. Applying the functions again gets us closer to our goal.

The second run of the loop would succeed in starting the service, but would be using the wrong configuration file. The package install from the previous loop clobbered the one written previously. Promise number two would fix the config and restart the service, and the third would do nothing because the package is already installed. Finally, we’ve converged to our desired system state. A third loop would take no actions at all.

Kicking things off

To set up a CFEngine server, invoke the following Chef command:[code] knife bootstrap centos6-1 -r 'role[cfengine]' -N "cfengine-1.example.com" -E development -d affs-omnibus-pre -x root[/code]When Chef is done doing it’s thing, you’ll end up with a functioning CFEngine policy host, happily promising to serve policy. Log into the freshly configured machine and check it out. Three things have happened. First, the cfengine package itself has been installed. Second, two directories have been created and populated: /var/cfengine/inputs, and /var/cfengine/masterfiles.

The inputs directory contains configuration for the CFEngine itself, which includes a promise to make the contents of masterfiles available for distribution. When a CFEngine client comes up, it will copy the contents of /var/cfengine/masterfiles from the server into it’s own inputs directory.

Examining policy

CFEngine’s main configuration file is promises.cf, from which everything else flows. Here’s a short snippet:

promises.cf snippet[code] body common control { bundlesequence => { "update", "garbage_collection", "cfengine", "puppet_server", };

inputs => { "update.cf", "cfengine_stdlib.cf", "cfengine.cf", "garbage_collection.cf", "puppet.cf", }; }[/code]The bundlesequence section tells cf-agent what promise bundles to execute, and in what order. The one we’re examining today is named puppet_server, found in puppet.cf

(puppet.cf.erb) download[code] ######################################################## ##

Installs me some puppet

## #########################################################

bundle agent puppet_server { vars: "foo" slist => { "Hi." }; "bar" slist => { "I'm like a Chef attribute." }; "redhat_packages" slist => { "puppet-server", "puppet", "facter" }; "debian_packages" slist => { "puppetmaster", "puppet", "vim-puppet" };

classes: "puppetmaster_enabled" expression => returnszero("/sbin/chkconfig puppetmaster", "noshell");

"iptables_enabled" expression => returnszero("/sbin/service iptables status", "noshell");

files: "/etc/puppet" comment => "default configuration file for puppet", copy_from => local_cp("/var/cfengine/masterfiles/puppet"), depth_search => recurse("inf"), perms => system("644"), classes => if_repaired("restart_puppetmaster");

packages: redhat|CentOS:: "$(redhat_packages)" comment => "install redhat based distro packages", package_policy => "add", package_method => yum;

debian|ubuntu:: "$(debian_packages)" comment => "install debian based distro packages", package_policy => "add", package_method => apt;

processes: "/usr/bin/ruby /usr/sbin/puppetmasterd" comment => "the puppetmaster service", restart_class => canonify("restart_puppetmaster");

commands: "/bin/echo" args => "$(foo) $(bar)", ifvarclass => canonify("restart_puppetmaster");

"/sbin/service puppetmaster restart" ifvarclass => canonify("restart_puppetmaster");

"/sbin/chkconfig puppetmaster on" ifvarclass => "!puppetmaster_enabled";

"/sbin/service iptables stop" ifvarclass => "iptables_enabled";

"/sbin/chkconfig iptables off" ifvarclass => "iptables_enabled"; }

########################################################

body perms system(p) { mode => "$(p)"; }[/code]A promise bundle is CFEngine’s basic unit of intent. It’s a place to logically group related promises. Within a bundle, CFEngine processes things with normal ordering. That is, variables are converged first, then classes, then files, then packages, and so on. I wrote the bundle sections in normal order to make it easier to read, but they could be rearranged and still have the same effect. Without going into too much detail about the language, I’ll give a couple hints to help with groking the example.

First, in CFEngine, the word ‘class’ does not mean what it normally does in other programming languages. Instead, classes are boolean flags that describe context. Classes can be ‘hard classes’, which are discovered attributes about the environment (hostname, operating system, time, etc), or ‘soft classes’, which are defined by the programmer. In the above example, puppetmaster_enabled and iptables_enabled are soft classes set based on the return status of a command. In the place of if or case statements, boolean checks on classes are used.

Second, there are no control statements like for or while. Instead, when lists are encountered they are automatically iterated. Check out the packages section for examples of both class decisions and list iteration. Given those two things, you should be able to work your way through the example. However, there’s really no getting around reading the reference manual if you want to learn CFEngine.

On to Puppet

Finally, let’s go ahead and use Chef to bring up a CFEngine client, which will be turned into a Puppet server.[code]knife bootstrap centos6-2 -r 'role[puppet]' -N "puppet-1.example.com" -E development -d affs-omnibus-pre -x root[/code]The first run will fail, since the host’s IP isn’t yet in the cfengine server’s allowed hosts lists. Complete the convergence by running these commands:[code]knife ssh "role:cfengine" -a ipaddress chef-client knife ssh "role:puppet" -a ipaddress chef-client knife ssh "role:puppet" -a ipaddress chef-client[/code]And viola! A working Puppet server, serving policy.

转自:http://blog.afistfulofservers.net/post/2011/12/30/cfengine-puppet-and-chef-part-1/

共收到 4 条回复
96
scmroad · #1 · 2012年4月09日

In the previous installment, we used Chef to configure CFEngine to serve policy that allowed us to create a Puppet service. In this one, we’ll have Chef use that Puppet service to create a Chef server. If you think this is a ridiculous thing to do, I would be inclined to agree with you. However, this is my blog so I make the rules.

Puppet

Puppet at it’s core works like CFEngine. Statements in Puppet are convergent operators, in that they are declarative (and therefore idempotent), and convergent in that they check a resource’s state before taking any action. Like the NTP example from the CFEngine installment, non-optimally ordered execution will usually work itself out after repeated Puppet runs.

Unlike CFEngine, where policy is copied and evaluated on the edges, Puppet clients connect to the Puppet server where configuration is determined based on a certificate CN. A catalog of serialized configuration data is shipped back to the client for execution. This catalog is computed based on the contents of the manifests stored on the server, as well as a collection of facts collected from the clients. Puppet facts, like CFEngine hard classes, are discoverable things about a node such as OS version, hostname, kernel version, network information, etc.

Puppet works a bit like the food replicators in Star Trek. Resources make up the basic atoms of a system, and the precise configuration of each must be defined. If a resource is defined twice in a manifest with conflicting states, Puppet refuses to run.

Ordering can be specified though require statements that set up relations between resources. These are used to build a directed graph, which Puppet sorts topologically and uses to determine the final ordering. If a resource in a chain fails for some reason, dependent resources down the graph will be skipped.

This allows for isolation of non-related resources collections. For example, if a package repository for some reason fails to deliver the ‘httpd’ package, it’s dependent configuration file and service resources will be skipped. This has nothing to do with an SSH resource collection, so the resources concerning that service will be executed even though the httpd collection had previously failed.

Just be careful not to create the coffee without the cup.

chef.pp

Let’s examine a Puppet manifest that creates a Chef server on Centos 6.

(chef.pp)[code] class chef-server {

install FrameOS package repo

exec { 'rbel6-release': command => "/bin/rpm -Uvh http://rbel.co/rbel6", unless => "/bin/rpm -qa | grep rbel6-release" }

list of packages to install

$packages = [ "couchdb", "rabbitmq-server", "rubygem-chef", "rubygem-chef-server", "rubygem-chef-solr", "rubygem-chef-expander", "rubygem-chef-server-api", "rubygem-chef-server-webui" ]

install all the packages

package { $packages: ensure => installed, require => Exec[ 'rbel6-release' ] }

start couch

service { 'couchdb': ensure => running, enable => true, hasstatus => true, require => Package[ $packages ], }

start rabbitmq

service { 'rabbitmq-server': ensure => running, enable => true, hasstatus => true, status => "service rabbitmq-server status | grep -e 'Pid .* running'", require => Package[ $packages ] }

#FIXME - poke proper hole

turn off iptables

service { 'iptables': ensure => stopped, enable => false, status => "/sbin/service iptables status | grep 'Table: filter'"; }

rabbitmq vhost

exec { "add vhost chef to rabbitmq": command => "/usr/sbin/rabbitmqctl add_vhost /chef", unless => "/usr/sbin/rabbitmqctl list_vhosts | grep /chef", require => Service['rabbitmq-server'] }

rabbitmq user

exec { "add user chef to rabbitmq": command => "/usr/sbin/rabbitmqctl add_user chef testing", unless => "/usr/sbin/rabbitmqctl list_users | grep chef", require => [ Service['rabbitmq-server'], Exec['add vhost chef to rabbitmq'] ] }

rabbitmq permissions

exec { "add chef permissions to rabbitmq": command => "/usr/sbin/rabbitmqctl set_permissions -p /chef chef \".\" \".\" \".*\"", unless => "/usr/sbin/rabbitmqctl list_permissions -p /chef | grep chef", require => [ Service['rabbitmq-server'], Exec['add vhost chef to rabbitmq'], Exec['add user chef to rabbitmq'] ] }

log directory

file { '/var/log/chef': ensure => directory, owner => "root", mode => "755", require => Package[ $packages ], }

log files

$cheflogfiles = [ '/var/log/chef/solr.log', '/var/log/chef/server.log', '/var/log/chef/server-webui.log' ]

file { $cheflogfiles: ensure => present, owner => "root", mode => "644", require => [ File[ '/var/log/chef' ], Package[ $packages ] ] }

begin running services

service { 'chef-server': ensure => running, enable => true, require => [ Package[ $packages ], Exec[ 'add vhost chef to rabbitmq' ], Exec[ 'add user chef to rabbitmq' ], Exec[ 'add chef permissions to rabbitmq' ], File[ $cheflogfiles ] ] }

service { 'chef-solr': ensure => running, enable => true, hasstatus => true, require => [ Service['chef-server'] ] }

service { 'chef-expander': ensure => running, enable => true, hasstatus => true, require => [ Service['chef-solr'] ] }

service { 'chef-server-webui': ensure => running, enable => true, require => [ Service['chef-server'], Service[ 'iptables' ] ] } }[/code]Picking it apart

Line 1 is a Puppet class definition. This groups the resource statments between together, allowing us to assign chef-server to a node based on it’s hostname. This can be accomplished with an explicit nodes.pp definition, or with an external node classifier.

Line 3 is an exec resource, which we can later refer to with it’s name: rbel6-release. When using exec resources, it’s up to you to specify a convergence check. In this case, we used the unless keyword to check the return status of an rpm command. The same goes for command promise types in CFEngine, or an execute resources in Chef.

Line 9 is an example of an array variable, which is iterated over in line 21, much like a CFEngine slist.

Everything else is a standard Puppet resource declaration, each of which have a type, a name, and an argument list. Like CFEngine promises, each type has various intentions available under the hood. Packages can be installed. Services can be running or stopped, and files can be present with certain contents and permissions.

Refer to the Puppet documentation for more details.

96
scmroad · #2 · 2012年4月09日

At the end of the last installment, we used Puppet to create a Chef server. That brings us full circle, and the only thing we have left to do is examine how Chef works. We’ll do that by looking at the code that gave us our original CFEngine server.

Chef

Since they’re both written in Ruby, people tend to compare Puppet and Chef. This is natural since they have a lot in common. Both are convergence based configuration management tools inspired by CFEngine. Both have stand alone discovery agents (facter and ohai, respectively), as well as RESTful APIs for gleaning node information from the server. It turns out, however, that Chef actually has a lot more in common with CFEngine.

Like CFEngine, Chef copies policy from the server and evaluates it on the edges. This allows for high scalability, since the server isn’t doing very much. Think of web application that does most of it’s work in the browser instead of on the server.

A Chef recipe is a collection of convergent resource statements, and serves as the basic unit of intent. This is analogous to a CFEngine promise bundle. The Chef run list is how recipe ordering is defined, and is directly comparible to CFEngine’s bundlesqeuence. Using this approach makes it easy to reason about what’s going on when writing infrastructure as code.

Chef Specials

Imperative programming and declarative interface

While it’s true that Chef is just “pure ruby” and therefore imperative, to say that Chef is imperative without considering the declarative interface to resources is disingenuous at best. Using nothing but Chef resources, recipes look very much like their CFEngine and Puppet counterparts. The non-optimally ordered Chef version of NTP converges in the same number of runs as the CFEngine example from the first installment. This is because the underlying science of convergent operators is the same.[code]# service service "ntp" do action [ :enable, :start ] ignore_failure true end

file

template "/etc/ntp.conf" do source "ntp.conf.erb" owner "root" group "root" mode 0644 notifies :restart, "service[ntp]" ignore_failure true end

package

package "ntp" do action :install ignore_failure true end[/code]When and where order matters, imperative ordering isolated within a recipe is the most intuitive way for sysadmins to accomplish tasks within the convergent model. “Install a package, edit a config file, and start the service” is how most people think about the task. Imperative ordering of declarative statements give the best of both worlds. When order does NOT matter, it’s safe to re-arrange recipe ordering in the Chef run list.

Multiphase execution

The real trick to effective Chef cookbook development is to understand the Anatomy of a Chef Run. When a Chef recipe is evaluated in the compilation phase, encountered resources are added to the Resource Collection, which is an array of evaluated resources with deferred execution.

The compile phase of this recipe would add 99 uniquely named, 12 oz, convergent beer_bottles to the collection, and the configure phase would take them down and pass them around. Subsequent runs would do nothing.

thanks jtimberman![code]size = ((2 * 3) * 4) / 2

99.downto(1) do |i| beer_bottle "bottle-#{i}" do oz size action [ :take_down, :pass_around ] end end[/code]The idea is that you can take advantage of the full power of Ruby to make decisions about what to declare about your resources. Most people just use the built in Chef APIs to consult chef-server for topology information about their infrastructure. However, there’s nothing stopping you from importing random Ruby modules and accessing existing SQL databases instead.

Want to name name servers after your Facebook friends? Go for it. Want your MOTD to list all James Brown albums released between 1980 and 1990? Not a problem. The important part is that things are ultimately managed with a declarative, idempotent, and convergent resource interface.

cfengine.rb

Let’s take a look at the recipe that gave us our original CFEngine server.

(server.rb) download[code] ####################################################### ##

Installs me some cfengine

## #########################################################

variables

cfdir = "/var/cfengine" node.set[:cfengine][:server]=true cfengine_clients = search(:node, 'cfengine_client:true')

#######################################################

packages

#######################################################

cfengine

package "cfengine"

#######################################################

files, templates, and directories

#######################################################

masterfiles

directory "#{cfdir}/masterfiles" do action :create end

cfengine input files

%w{ inputs masterfiles }.each do |dir| %w{ failsafe cfengine_stdlib global garbage_collection cfengine }.each { |c| template "#{cfdir}/#{dir}/#{c}.cf" do source "inputs/#{c}.cf.erb" variables( :cfengine_server => node ) end }

# updates template "#{cfdir}/#{dir}/update.cf" do source "inputs/update.cf.erb" end end

promises.cf

template "#{cfdir}/inputs/promises.cf" do source "inputs/promises-server.cf.erb" variables( :cfengine_clients => cfengine_clients ) notifies :restart, "service[cf-serverd]" notifies :restart, "service[cf-execd]" end

#######################################################

Distribution only

#######################################################

promises.cf

template "#{cfdir}/masterfiles/promises.cf" do source "inputs/promises-client.cf.erb" variables( :cfengine_clients => cfengine_clients ) end

puppet.cf

template "#{cfdir}/masterfiles/puppet.cf" do source "inputs/puppet.cf.erb" variables( :cfengine_clients => cfengine_clients ) end

puppet server policy distribution

directory "#{cfdir}/masterfiles/puppet" do action :create end

puppet/site.pp

remote_directory "#{cfdir}/masterfiles/puppet" do source "server/puppet" end

#######################################################

services

#######################################################

poke a hole in the firewall

FIXME Do this properly once COOK-688 is done

service "iptables" do action [:disable,:stop] end

cfengine_services = %w{ cf-execd cf-serverd }

services

cfengine_services.each { |s| service s do action [:enable,:start] end }[/code]Topology management

When a node is bootstrapped with Chef, a run list of roles or recipes is requested by the node itself. After that, the host is found by recipes running elsewhere in the infrastructure by searching for roles or attributes. This is contrasted from the CFEngine and Puppet techniques of matching classes based on a hostname, FQDN, IP, or other found information.

This approach has the effect of decoupling a node’s name from it’s functionality. Line 10 in cfengine.rb above searches out node objects and later be passes them to the promises-server.cf.erb template for authorization.

Wrapping up

So there you have it folks. Chef making CFEngine making Puppet making Chef. These tools can be used to automate literally anything, and they’re pretty easy to use once you figure out how they work. I was going to throw some Bcfg2 and LCFG in there just for fun, but I only had some much free time =)

Configuration mangement is like a portal.

-s

Posted by Sean OMeara Dec 30th, 2011

96
scmroad · #3 · 2012年4月09日

Configuration Management Strategies

I just watched the “To Package or Not to Package” video from DevOps days Mountain View. The discussion was great, and there were some moments of hilarity. If you haven’t watched it yet, check it out here

Stephen Nelson Smith, I salute you, sir.

I’m quite firmly in the “Let your CM tool handle your config files” camp. To explain why, I think it’s worth briefly examining the evolution of configuration management strategies.

In order to keep this post as vague and heady as possible, no distinction between “system” and “application” configurations shall be made.

What is a configuration file?

Configuration files are text files that control the behavior of programs on a machine. That’s it. They are usually read once, when a program is started from a prompt or init script. A process restart or HUP is typically required for changes to take effect.

What is configuration management, really?

When thinking about configuration management, especially across multiple machines, it is easy to equate the task to file management. Configs do live in files, after all. Packages are remarkably good at file management, so it’s natural to want to use them.

However, the task goes well beyond that.

An important attribute of an effective management strategy, config or otherwise, is that it reduces the amount of complexity (aka work) that humans need to deal with. But what is the work that we’re trying to avoid?

Dependency Analysis and Runtime Configuration

Two tasks that systems administrators concern themselves with doing are dependency analysis and runtime configuration.

Within the context of a single machine, dependency analysis usually concerns software installation. Binaries depend on libraries and scripts depend on binaries. When building things from source, headers and compilers are needed. Keeping the details of all this straight is no small task. Packages capture these relationships in their metadata, the construction of which is painstaking and manual. Modern linux distributions can be described as collections of packages and the metadata that binds them. Go out and hug a package maintainer today.

Within the context of infrastructure architecture, dependency analysis involves stringing together layers of services and making individual software components act in concert. A typical web application might depend on database, caching, and email relay services being available on a network. A VPN or WiFi service might rely on PKI, Radius, LDAP and Kerberos services.

Runtime configuration is the process of taking all the details gathered from dependency analysis and encoding them into the system. Appropriate software needs to be installed, configuration files need to be populated, and kernels need to be tuned. Processes need to be started, and of course, it should all still work after a reboot.

Manual Configuration

Once upon a time, all systems were configured manually. This strategy is the easiest to understand, but the hardest one to execute. It typically happens in development and small production environments where configuration details are small enough to fit into a wiki or spreadsheet. As a network’s size and scope increases, management efforts became massive, time consuming, and prone to human error. Details end up in the heads of a few key people and reproducibility is abysmal. This is obviously unsustainable.

Scripting

The natural progression away from manual configuration was custom scripting. Scripting reduced management complexity by automating things using languages like Bash and Perl. Tutorials and documentation instruction like “add the following line to your /etc/sshd_config” were turned into automated scripts that grepped, sed’ed, appended, and clobbered. These scripts were typically very brittle and would only produce desired outcome after their first run.

File Distribution

File distribution was the next logical tactic. In this scheme, master copies of important configuration files are kept in a centralized location and distributed to machines. Distribution is handled in various ways. RDIST, NFS mounts, scp-on-a-for-loop, and rsync pulls are all popular methods.

This is nice for a lot of reasons. Centralization enables version control and reduces the time it takes to make changes across large groups of hosts. Like scripting, file distribution lowers the chance of human error by automating repetitive tasks.

However, these methods have their drawbacks. NFS mounts introduce single points of failure and brittleness. Push based methods miss hosts that happen to be down for maintenance. Pulling via rsync on a cron is better, but lacks the ability to notify services when files change.

Managing configs with packages falls into this category, and is attractive for a number of reasons. Packages can be written to take actions in their post-install sections, creating a way to restart services. It’s also pretty handy to be able to query package managers to see installed versions. However, you still need a way to manage config content, as well as initiate their installation in the first place.

Declarative Syntax

In this scheme, autonomous agents run on hosts under management. The word autonomous is important, because it stresses that the machines manage themselves by interpreting policy remotely set by administrators. The policy could state any number of things about installed software and configuration files.

Policy written as code is run through an agent, letting the manipulation of packages, configuration files, and services all be handled by the same process. Brittle scripts behaving badly are eliminated by exploiting the idempotent nature of a declarative interface.

When first encountered, this is often perceived as overly complex and confusing by some administrators. I believe this is because they have equated the task of configuration management to file management for such a long time. After the initial learning curve and picking up some tools, management is dramatically simplified by allowing administrators to spend time focusing on policy definition rather than implementation.

Configuration File Content Management

This is where things get interesting. We have programs under our command running on every node in an infrastructure, so what should we make them to do concerning configuration files?

“Copy this file from its distribution point” is very common, since it allows for versioning of configuration files. Packaging configs also accomplishes this, and lets you make declarations about dependency. But how are the contents of the files determined?

It’s actually possible to do this by hand. Information can be gathered from wikis, spreadsheets, grey matter, and stick-it notes. Configuration files can then be assembled by engineers, distributed, and manually modified as an infrastructure changes.

File generation is a much better idea. Information about the nodes in an infrastructure can be encoded into a database, then fed into templates by small utility programs that handle various aspects of dependency analysis. When a change is made, such as adding or removing a node from a cluster, configurations concerning themselves with that cluster can be updated with ease.

Local Configuration Generation

The logic that generates configuration files has to be executed somewhere. This is often done on the machine responsible for hosting the file distribution. A better place is directly on the nodes that need the configurations. This eliminates the need for distribution entirely.

Modifications to the node database now end up in all the correct places during the next agent run. Packaging the configs is completely unnecessary, since they don’t need to be moved from anywhere. Management complexity is reduced by eliminating the task entirely. Instead of worrying about file versioning, all that needs to be ensured is code correctness and the accuracy of the database.

Don’t edit config files. Instead, edit the truth.

-s

Posted by Sean OMeara Jul 27th, 2011

96
scmroad · #4 · 2012年4月09日

配置管理工具puppet与chef对比分析

作者:lixiaohong 转自:http://blog.sina.com.cn/s/blog_5374d6e30100sum0.html

puppet与chef对比

首先说说相同点:

1、都是基于ruby语言 2、对要配置的对象提供了跨平台的抽象,用户大部分时间只跟这些抽象的资源打交道,而不用关心实现,如只需关心要添加什么软件或用户,不需要关心这些用户或软件是怎么添加上去的 3、都有配置中心服务器,在每台要配置的客户端上都需要安装客户端,客户端跟服务器端用证书认证 4、配置应用过程都有两个阶段,第一个阶段在配置中心进行,由配置中收服务器针对客户端生成资源列表,第二个阶段在客户端运行,将应用收到的资源列表。 5、都提供了扩展的方式,puppet用的是模块的方式,而chef用的是cookbook的方式。虽然感觉(我没有真正用过chef)chef的cookbook方式更灵活和易于分享,但是这两者实质是一样的

再说说不同点: 1、puppet提供的配置语言更通用和高级一些,用户不需要懂ruby语言。而对于chef,没有专门的配置语言,用户需要了解比较多的ruby语言。 2、puppet资源之间有显式的依赖关系,按照这些关系去实现,而跟这些资源在配置文件的位置或前后没有关系。而看了一下chef的一些例子,更像是ruby脚本,从前到后按顺序执行 3、puppet安装简单,需要的支持软件也少,服务器端也是这样。而chef在配置中心服务器端需要依赖软件比较多,需要couchdb、RabbitMQ和Solr,这样连带需要安装java和erlang,这样配置服务器过程要复杂很多 4、puppet服务端的配置都是一个一个的文本文件,这样易于发布、备份和扩展。而chef的服务器端的配置放在couchdb和solr索引等二进制文件中,通过远程命令工具knife来操作这些配置。这样,puppet更符合unix管理员的使用习惯。 5、puppet的用户很多,象Google、Redhat等大公司都在用它。而chef的用户就少多了,而且没有什么大的公司

最后,我感觉chef从puppet身上学到或借用了很多有用的概念,但是没有什么超越的地方。而puppet比以前的cfengine工具多出了很多的亮点,这也是我愿意从一个cfengine用户转到puppet用户的原因。但是,如果让我从puppet往chef上转,确实缺少动力。chef可能更适合专业用户,用在云计算这种需要更多定制的场合,只是不知道有没有合适的生态环境让它长那么大。呵呵。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册