Showing posts with label configuration. Show all posts
Showing posts with label configuration. Show all posts

December 20, 2013

Day 20 - Distributed configuration data with etcd

Written by: Kelsey Hightower (@kelseyhightower)
Edited by: Ben Cotton (@funnelfiasco)

Intro

I’ve been managing applications for a long time, but I’ve never stopped to ponder why most application configurations are managed via files. Just about every application deployed today requires a configuration file stored in the correct location, with the proper permissions, and valid content on every host that runs the application.

If not, things break.

Sure configuration management tools provide everything you need to automate the process of constructing and syncing these files, but the whole process is starting to feel a bit outdated. Why are we still writing applications from scratch that rely on external tools (and even worse, people) to manage configuration files?

Think about that for a moment.

The state of application configuration seems a bit stagnant, especially when compared to the innovation happening in the world of application deployment. Thanks to virtualization we have the ability to deploy applications in minutes, and with advances in containerization, we get the same results in seconds.

However, all those application instances need to be configured. Is there a better way of doing this, or are we stuck with configuration files as the primary solution?

Introducing etcd

What is etcd? Straight from the docs:
A highly-available key value store for shared configuration and service discovery. etcd is inspired by zookeeper and doozer, with a focus on:
  • Simple: curl'able user facing API (HTTP+JSON)
  • Secure: optional SSL client cert authentication
  • Fast: benchmarked 1000s of writes/s per instance
  • Reliable: Robustly distributed using Raft
On the surface it appears that etcd could be swapped out with any key/value store, but if you did that you would be missing out on some key features such as:
  • Notification on key changes
  • TTLs on keys
But why would anyone choose to move configuration data from files to something like etcd? Well for the same reasons DNS moved away from zone files to a distributed database: speed and portability.

Speed

When using etcd all consumers have immediate access to configuration data. etcd makes it easy for applications to watch for changes, which reduces the time between a configuration change and propagation of that change throughout the infrastructure. In contrast, syncing files around takes time and in many cases you need to know the location of the consumer before files can be pushed. This becomes a pain point when you bring autoscaling into the picture.

Portability

Using a remote database of any kind can make data more portable. This holds true for configuration data and etcd -- access to configuration data stored in etcd is the same regardless of OS, device, or application platform in use.

Hands on with etcd

Lets run through a few quick examples to get a feel for how etcd works, then we’ll move on to a real world use case.

Adding values

curl -X PUT -L http://127.0.0.1:4001/v2/keys/url -d value="db.example.com"

Retrieving values

curl -L http://127.0.0.1:4001/v2/keys/url

{
   "action":"get",
   "node":{
   "key":"/url",
   "value":"db.example.com",
   "modifiedIndex":1,
   "createdIndex":1
  }
}

Deleting values

curl -L -XDELETE http://127.0.0.1:4001/v2/keys/url

That’s all there is to it. No need for a database library or specialized client, we can utilize all of etcd’s features using curl.

A real world use case

To really appreciate the full power of etcd we need to look at a real world example. I’ve put together an example weather-app that caches weather data in a redis database, which just so happens to utilize etcd for configuration.

First we need to populate etcd with the configuration data required by the weather app:
/weather_app/city
/weather_app/interval
/weather_app/redis_url
/weather_app/weather_url

We can do this using curl:
curl -XPUT -L http://127.0.0.1:4001/v2/keys/weather_app/city -d value="Portland"
curl -XPUT -L http://127.0.0.1:4001/v2/keys/weather_app/interval -d value="5"
curl -XPUT -L http://127.0.0.1:4001/v2/keys/weather_app/redis_url \
  -d value="127.0.0.1:6379"
curl -XPUT -L http://127.0.0.1:4001/v2/keys/weather_app/weather_url \
  -d value="http://api.openweathermap.org/data/2.5/weather"

Next we need to set the etcd host used by the weather app:
export WEATHER_APP_ETCD_URL="http://127.0.0.1:4001"

For this example I’m using an environment variable to bootstrap things. The prefered method would be to use a DNS service record instead, so we can avoid relying on local settings.

Now with our configuration data in place, and the etcd host set, we are ready to start the weather-app:
 ./weather-app 
2013/12/18 21:17:36 weather app starting ...
2013/12/18 21:17:37 Setting current temp for Portland: 30.92
2013/12/18 21:17:42 Setting current temp for Portland: 30.92

Things seem to be working. From the output above I can tell I’m hitting the right URL to grab the current weather for the city of Portland every 5 seconds. If I check my the redis database, I see that the temperature is being cached:
redis-cli 
redis 127.0.0.1:6379> get Portland
"30.916418"

Nothing too exciting there. But watch what happens if I change the value of the /weather_app/city key in etcd:
curl -XPUT -L http://127.0.0.1:4001/v2/keys/weather_app/city -d value="Atlanta"

We end up with:
./weather-app 
2013/12/18 21:17:36 weather app starting ...
2013/12/18 21:17:37 Setting current temp for Portland: 30.92
2013/12/18 21:17:42 Setting current temp for Portland: 30.92
2013/12/18 21:17:48 Setting current temp for Portland: 30.92
2013/12/18 21:17:53 Setting current temp for Atlanta: 29.84
2013/12/18 21:17:59 Setting current temp for Atlanta: 29.84

Notice how we are now tracking the current temperature for Atlanta instead of Portland. The results are cached in Redis just as expected:
redis-cli 
redis 127.0.0.1:6379> get Atlanta
"29.836407"

etcd makes it really easy to update and watch for configuration changes; then apply the results at run-time. While this might seem a bit overkill for a single app instance, it’s incredibly useful when running large clusters or when autoscaling comes into the picture.

Everything we’ve done so far was pretty basic. We used curl to set some configuration, then had our application use those settings. But we can push this idea even further. There is no reason to limit our applications to read-only operations. We can also write configuration data to etcd directly. This unlocks a whole new world of possibilities. Web applications can expose their IP addresses and ports for use by load-balancers. Databases could expose connection details to entire clusters. This would mean making changes to existing applications, and perhaps more importantly would mean changing how we design new applications. But maybe it’s time for AppOps -- lets get out of the way and let the applications configure themselves.

Conclusion

Hopefully this post has highlighted how etcd can go beyond traditional configuration methods by exposing configuration data directly to applications. Does this mean we can get rid of configuration files? Nope. Today the file system provides a standard interface that works just about anywhere. However, it should be clear that files are not the only option for modern application configuration, and viable alternatives do exist.

December 24, 2009

Day 24 - Config Management with Cfengine 3

This article was written by Aleksey Tsalolikhin. If you are already using another automation tool, and even have no plans to change, this article may help you understand where much of today's config management and automation concepts came from.

Cfengine3 marks the third major version of the original configuration management software that started 16 years ago. Like Puppet, Chef, Bcfg2, and others, Cfengine helps you automate the configuration and maintenance of your systems.

I chose cfengine because of it's long track record, large user base, academic origins, wide platform support, and supportive community.

For the uninitiated, configuration management tools help you maintain a desired configuration state. If the system is not in the correct state, the config management tool will perform actions to move into the correct state. For example, if your state includes a cron job, and one of the systems doesn't have that cron job, the config management tool will install it. No action would be taken if the cron job already existed correctly.

Cfengine has its own configuration language. This language allows you to describe how things should be (state) and when necessary, describe how to do it or what to do. Using this language you create configuration "policy rules" or "promises" of how the system should be configured. Cfengine manages how to get to the promised state automatically.

In this way, Cfengine becomes your automated systems administrator, a kind of robot that maintains your system by your definitions. As exampled above: if a cron job is missing, and you said you wanted it, Cfengine will add it.

Your job then is promoted to one of configuring this automated system and monitoring its function. You can configure it to add cron jobs, upgrade software packages, or remove users, on thousands of hosts as easily as on one host. Use this tool with care ;)

Cfengine can be used standalone or in a client-server model. In the latter, if the server is unreachable, the client is smart and uses the last-seen cached set of policies and uses those until it can reach the server again. In either model, the client-side performs the checks and maintenance actions, so this should scale to thousands of hosts.

Speaking of using Cfengine, the language syntax in the latest version (3) has been cleaned up from the previous version, which had grown to be varied and inconsistent.

When using Cfengine, it's important to know some terms:

Promise
A promise is a Cfengine policy statement - for example, that /etc/shadow is only readable by root - and it implies Cfengine will endeavor to keep that promise.
Pattern
I asked Mark to clarify for us what he means by "patterns" in Cfengine 3. Here is his answer:
A "configuration" is a design arrangement or a pattern you make with system resources. The cfengine language makes it easy to describe and implement patterns using tools like lists, bundles and regular expressions. While promises are things that are kept, the efficiencies of configuration come from how the promises form simple re-usable patterns.
Class
For you programmers, this has nothing to do with the Object-Oriented term. Classes are "if/then" tests but the test itself is hidden "under the hood" of Cfengine. There is no way to say "if/then" in Cfengine except with classes. Example - this shell script will only be executed on Linux systems:
shellcommands:
    linux:: "/var/cfengine/inputs/sh/my_script.sh"
There are a number of built-in classes, like the linux class above; they can also be explicitly defined.
Bundle
A bundle is a collection of promises
Body
The body of a promise explains what it is about. Think of the body of a contract, or the body of a document. Cfengine "body" declarations divide up these details into standardized, paramaterizable, library units. Like functions in programming, promise bodies are reusable and parameterized.
  cfengine-word => user-data-pattern

  body cfengine-word user-data-pattern
  {
      details
  }
The basic grammar of Cfengine 3 looks like this:
  promisetype:
      classes::
          "promiser" -> { "promisee1", "promisee2", ... }
              attribute_1 => value_1,
              attribute_2 => value_2,
              ...
              attribute_n => value_n;
Classes are optional. Here is the list of promise types:
  • commands - Run external commands
  • files - Handle files (permissions, copying, etc.)
  • edit_line - Handle files (content)
  • interfaces - Network configuration
  • methods - Methods are compound promises that refer to whole bundles of promises.
  • packages - Package management
  • processes - Process management
  • storage - Disk and filesystem management
Here's another example:
    files:
       "/tmp/test_plain" -> "John Smith",
            comment => "Make sure John's /tmp/test_plain exists",
            create  => "true";
Above, we have the promisee on the right side of the arrow. The promisee is "the abstract object to whom the promise is made". This is for documenation. The commercial version of cfengine uses promisees to generate automated knowledge maps. The object can be the handle of another promise with an interest in the outcome or an affected person who you might want to contact in case of emergency.

How about a more complete and practical example? Lets ensure some ntp and portmap services are running:

body common control
{
  # We can give this a version
  version => "1.0";
  # specify what bundles to apply
  bundlesequence  => { "check_service_running"  };
}

bundle agent check_service_running
{
    vars:
        # name    type  =>    value
        "service" slist => {"ntp", "portmap"};
        "daemon_path" string => "/etc/init.d";

    processes:
        "$(service)"
            comment => "Check processes running for '$(service)'",
            restart_class => "restart_$(service)";

    commands:
        "${daemon_path}/${service} start"
            comment => "Execute the start command for the service",
            ifvarclass => "restart_${service}";
}
Saving this as 'servicecheck.cf' we can test it in standalone mode with cf-agent:
% sudo /etc/init.d/portmap status
 * portmap is not running
% sudo /etc/init.d/ntp status    
 * NTP server is not running.

% sudo cf-agent -f ./servicecheck.cf
Q: "...init.d/ntp star":  * Starting NTP server ntpd
Q: "...init.d/ntp star":    ...done.
I: Last 2 QUOTEed lines were generated by promiser "/etc/init.d/ntp start"
I: Made in version '1.0' of './servicetest.cf' near line 20
I: Comment: Execute the start command for the service

Q: "....d/portmap star":  * Starting portmap daemon...
Q: "....d/portmap star":    ...done.
I: Last 2 QUOTEed lines were generated by promiser "/etc/init.d/portmap start"
I: Made in version '1.0' of './servicetest.cf' near line 20
I: Comment: Execute the start command for the service

# Now check to make sure cfengine started our services:
% sudo /etc/init.d/portmap status
 * portmap is running
% sudo /etc/init.d/ntp status    
 * NTP server is running.
Configuration management is an essential tool for sane and happy sysadmins. They help you ensure your systems are correctly configured without repeatedly consuming your time fighting to maintain the status quo.

Further reading:

December 25, 2008

Day 25 - dotfiles and power users

Dotfiles are precious. They help you maintain your desired environment. My dotfiles have been built very slowly over time as I find features I like or change the way I use a tool. Additionally, I learn by reading other people's rc files. Ignoring your ability to make very useful changes in behavior and operation of your favorite tools will leave you at a very minimal level of productivity.

The more time you spend using a tool should mean putting more time into configuring it and learning about it. Using a program with its defaults alone, over time, is a massive productivity killer. For example, the speed at which I was able to do things in a unix shell skyrocketed when I learned that I could have vi keybindings in my shell (with 'set -o vi' or 'bindkey -v' in most shells).

As mentioned above, part of learning how to configure a tool is simply reading documentation, or searching online for how to do something. Another important part is by learning from others: reading their dotfiles. In order to read your dotfile, it must be available somewhere. I heavily encourage you to post your rc files online. Not only publish them, but make sure you put comments describing what each configuration decision does. Knowledge grows faster when there's a community contributing to it, so post documented snippets online!

Rather than covering another best practice or tool, today's article is an attempt to try and fill your mind with some useful things you may want to try in your own tools. Covered below are some of my configurations for various tools. Each configuration has a link to respective documentation (if available online) about that option. Futher, it is not my hope that you agree with my configurations, but that you find options here that you didn't know about that might help you.

In the process of reading peer rc files and gathering data for this article, I found a neat website where people can publish their own dotfiles, dotfiles.org. This site lets you view everyone's uploaded dotfiles.

There are far too many tools and options to cover, so I'll cover the three tools closest to my heart: zsh, vim, and screen. However, before I get into it, I want to make a few, important points.

  1. Vi mode in your shell is one of the best features available if you are familiar with vi. Bash, ksh, zsh, and tcsh all support 'vi mode' in varying degrees of compatibility. In your shell, type 'set -o vi' (bash, ksh) or 'bindkey -v' (zsh, tcsh), and be happy with your increase in productivity.
  2. Set your terminal (screen or terminal) title! There are lots of existing rc files that show you how to do this. For zsh, try searching zsh screen title.
  3. Don't ignore configuration of tools you use every day. Being a power user doesn't mean you automatically do lame things like recompiling vim with -O9999, it means understanding the tools you use and how to configure them to best fit your pattern of work and your style preferences.
To repeat one more time, publish and document your dotfile configurations. Ok, on to some options for zsh, vim, and screen.

December 13, 2008

Day 13 - Accessible Automation

Modern systems administration often involves saving yourself (and your company) time and money. If I had to list skills (of people) and features (of software) by priority, automation would be near the top.

This is usually why I get so mad at software that doesn't lend itself easily to automation. Day 6 pointed out some potential difficulty in automating Tripwire. I'm willing to forgive Tripwire since it's security software. I don't expect security guys to think about systems administration problems.

But what about something like Cacti? Cacti is a monitoring tool aimed at helping you track data and data trends on your systems. You can configure it to graph many things on many hosts; sounds like a systems administration tool, right? Monitoring sounds sysadmin-ey. Transitivity from sysadmin goes to automation. Therefore, I expect that Cacti will fall happily into my family of other automated configurations. If I have a few hundred machines in various, known configurations, can I easily put this data into Cacti and keep it up to date?

Cacti's main interface is a web interface, and such things do not easily lend themselves to automation.

Searching for 'cacti automation' will point you at Cacti's command-line scripts (add_device.php, for example). These scripts only support adding things, not modifying or deleting them. If you want to do that, you're almost on your own. Automation features started showing up in Cacti 0.8.6 from a feature request that there was no way to mass add devices. This request lead a few new functions and the scripts mentioned previously.

With that version and beyond, you can add devices, graphs, and other things, in an easily automated way. For all other interactions, you'll need to click your way through the web interface. Removing devices, etc... click click click. If you want to import or export graph, data, or host templates, you can do that using the web interface, but only one template at a time.

There is a file called "api_automation_tools.php" (or the other api_xxxx.php scripts) in Cacti that sounds promising, but has no documentation. Many of the functions are self documenting by name or simplicity, but others are in great need of documenting and simplicity. Reading over the code, it's not obvious to me how I can do automated maintenance of cacti's devices, graphs, etc. Looking at Cacti's roadmap, I don't see 'improvements in automation' on it. Plugins are on the roadmap, but it's unclear if plugins will help with automation. There is an existing plugin effort for Cacti, but none of the plugins appear to aid with automation.

My guess is that the reason that the available automation is poorly documented or doesn't exist is because of two reasons: first, that the developers didn't develop cacti with systems administrator priorities in mind, and second, that no one has made the feature request. The second point makes me wonder, is cacti only used by people with a handful of systems to monitor? As I research Cacti, it's starting to feel more and more like it isn't for people who want automation or is mainly for those who have plenty of time to click few zillion times keeping Cacti's information aligned with reality.

Automation should be accessible. It should be documented. If I can't find Cacti's automation documentation, if it exists, then it's not accessible automation. Yes, you could read the code and figure out exactly functions you needed to call to modify or remove a device, graph, or whatever. Or skip the code and look at the storage system to modify the configuration. Hacks produced with this method are troublesome and will not scale. You will be lucky if the hacks survive the upgrade to the next version. Further, reading the code so you can implement your own hacks is not accessible automation.

If an open source tool fits your needs but lacks automation, get involved in the community, if there is one. File bug reports and feature requests, or send patches if possible. If a commercial tool fits your needs but lacks automation, contact the vendor. Find out if they can implement what you need, and how long it might take. Don't get stuck with a tool that sucks away resources because the best maintenance interface is with the mouse and keyboard.

I'm not trying to pick on Cacti, specifically. Many software tools are simply not written with accessible automation or other important sysadmin features in mind. This is a very important feature to consider when looking for tools to solve a problem, because, as I've repeated previously, automation saves you time, effort, and errors.

Further reading:

December 12, 2008

Day 12: Capistrano or Puppet?

I'm compelled to write about this subject today because of having received this question multiple times since sysadvent began.

Capistrano or Puppet? Both.

Puppet provides you with a way to specify a state your system should be in. Puppet's features will help you keep a machine in the same state. If someone hand-edits an apache config, you can have puppet automatically replace it with the correct one and reload apache, for example. Puppet runs on each of your servers.

Capistrano lets you describe what to do to a system or set of systems: Upload a file, run a program, restart a service, etc. Capistrano runs from your workstation and does work for you on remote systems.

So, why both?

Puppet needs a source of state information, and that source has to come from somewhere. If you always run on the bleeding edge of your configurations, you can feed the puppet master with your revision control system and use the state described in the head revision. Bleeding edges tend to be bloody for a reason. You could feed puppet with data from a branch of your revision control, too, and both not need capistrano for the feeding and not run on the head revision. You could deploy new state to puppet with Capistrano on a planned release schedule.

Puppet lets you specify that the 'httpd' package should be installed, and even what version. If you maintain your own package repository, you can control what version is installed (which you should). To upgrade the 'httpd' package, you could use Capistrano to upload new packages to your package repository and to deploy puppet manifests to keep 'httpd' automatically updated to whatever version you decide.

As an example, here's how the state management with puppet and capistrano might look for your apache configuration:

  1. Modify httpd.conf in revision control, check it in.
  2. Use capistrano to push the new state to your puppet masters.
  3. Puppet will see the new state and apply necessary changes. [*]
[*] This will only occur automatically if you run puppet periodically (like through cron) rather than manually.

If the change was bad, you can revert the change in revision control and again use capistrano to push the new files.

You can use puppet and capistrano to do similar tasks, if you wish, but I find they are best suited to compliment each other. Let puppet focus on automated state maintenance and let capistrano help you do deployments of new packages and new configurations.

Further reading: