Tag Archives: python

Ansible Nginx Playbook

With the last post related to Ansible, I present a simple example of using templates and playbook.
The directory structure of Ansible Nginx playbook:

nginx-playbook/
  nginx-ubuntu.yml 
  - template/ 
      nginx.j2
  - tasks

Here’s the nginx-ubuntu.yml

---

- hosts: web-servers
  user: ubuntu 
  sudo: True

  vars:
     #workers: use at template ansible_processor_count 
     connections : "1024" 

     is_10_up: "'$ansible_distribution_version'  >= '10.04'"
     is_ubuntu: "'$ansible_distribution' == 'Ubuntu'"

  tasks:
     # Note: these tasks can be written to a file 
     #       and include that file here to make it cleaner.
     - name: install python-software-properties
       action: apt pkg='python-software-properties' ensure=installed

     - name: "add nginx ppa if it ubuntu 10.04 and up"
       action: command /usr/bin/add-apt-repository -y ppa:nginx/stable
       only_if: '$is_ubuntu and $is_10_up'

     - name: update apt repo
       action: command /usr/bin/apt-get update

     - name: install nginx 
       action: apt pkg=nginx ensure=installed

     - name: write nginx.conf 
       action: template src=templates/nginx.j2 dest=/etc/nginx/nginx.conf
       notify:
       - restart nginx

  handlers:
     - name: restart nginx
       action: service name=nginx state=restarted

Here, the template is using generated variable ansible_processor_count and a user-defined variable connections on playbook.
The template file: templates/nginx.j2

user www-data;
worker_processes {{ ansible_processor_count }};

pid /var/run/nginx.pid;

events {
	worker_connections {{ connections }} ;
	# multi_accept on;
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	server_tokens off;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	##
	# Gzip Settings
	##

	gzip on;
	gzip_disable "msie6";

	# gzip_vary on;
	# gzip_proxied any;
	# gzip_comp_level 6;
	# gzip_buffers 16 8k;
	# gzip_http_version 1.1;
	# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;

	##
	# If HTTPS, then set a variable so it can be passed along.
	##

	map $scheme $server_https {
		default off;
		https on;
	}

	##
	# Virtual Host Configs
	##

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}

I run this against Ubuntu Oneiric.

(ansi_env) ansible-playbook nginx-ubuntu.yml -T 30 
 
PLAY [web-servers] ****************************

SETUP PHASE ****************************

ok: [15.185.123.x]


TASK: [install python-software-properties] *********

ok: [15.185.123.x] => apt pkg='python-software-properties' ensure=installed


TASK: [add nginx ppa if it ubuntu 10.04 and up] *********

ok: [15.185.123.x] => command /usr/bin/add-apt-repository -y ppa:nginx/stable


TASK: [update apt repo] *********

ok: [15.185.123.x] => command /usr/bin/apt-get update


TASK: [install nginx] *********

ok: [15.185.123.x] => apt pkg=nginx ensure=installed


TASK: [write nginx.conf] *********

ok: [15.185.123.x] => template src=/home/ubuntu/.ansible/tmp/ansible.pVJ9lH/source dest=/etc/nginx/nginx.conf


NOTIFIED: [restart nginx] **********

ok: [15.185.123.x] => service name=nginx state=restarted



PLAY RECAP **********************


15.185.123.x                 : ok=   7 changed=   4 unreachable=   0 failed=   0 

Although this prompts for error on the current devel branch of Ansible, there’s a minor fix https://github.com/ansible/ansible/pull/282 for it.

That solves the Nginx restarts when the config file is updated. 🙂
Update: 05/04/2012 service path now fixed on devel branch.
Update: 05/07/2012 I’ve placed this playbook on my github.
Update: 06/14/2012 Added Fred’s Pedantically commented playbook example
Update: 07/10/2012 On version 0.5 service unable to get status http://bit.ly/PIGJ3I

Related examples:
https://github.com/sfromm/ansible-playbooks
https://github.com/mpdehaan/ansible-examples
Fred Alger: Pedantically commented playbook example

Ansible = “Infrastructure as Data, Not infrastructure as Code”

I like the idea of Michael DeeHan ‘infrastructure as data’, not ‘infrastructure as code’.
This is about this “a simple deployment, model-driven configuration management,
and command execution framework” tool he authored which is called Ansible.

Some of Ansible articles can be found also at:
http://www.coloandcloud.com/editorial/an-interview-with-ansible-author-michael-dehaan/
http://server.dzone.com/articles/ansible-cm-deployment-and-ad
http://highscalability.com/blog/2012/4/18/ansible-a-simple-model-driven-configuration-management-and-c.html

I will start with installing Ansible on MacOS. First, it’s best to setup virtualenv and virtualenvwrapper.
A good tutorial for this is Multiple Python Versions on OSX with Virtualenv and Homebrew

If configured correctly, start creating the Ansible environment and clone the code from github.

 
Cocoys-MacBook-Pro:~ cocoy$ mkvirtualenv ansi_env --no-site-packages
Cocoys-MacBook-Pro:~ cocoy$ workon ansi_env
(ansi_env)Cocoys-MacBook-Pro:~ cocoy$ pip install yolk paramiko jinja2 PyYAML
(ansi_env)Cocoys-MacBook-Pro:~$  yolk -l 
Jinja2          - 2.6          - active 
PyYAML          - 3.10         - active 
Python          - 2.6.6        - active development (/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/lib-dynload)
paramiko        - 1.7.7.1      - active 
pip             - 0.6.3        - active 
pycrypto        - 2.5          - active 
setuptools      - 0.6c11       - active 
wsgiref         - 0.1.2        - active development (/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6)
yolk            - 0.4.3        - active 

Now clone the github for Ansible:

(ansi_env)Cocoys-MacBook-Pro:~ $ git clone https://github.com/ansible/ansible.git 
(ansi_env)Cocoys-MacBook-Pro:~$ cd ansible 
(ansi_env)Cocoys-MacBook-Pro:~$ git checkout devel 
(ansi_env)Cocoys-MacBook-Pro:~$ source ./env-setup 
(ansi_env)Cocoys-MacBook-Pro:~$ echo "server.domain.com" >   myhost 
(ansi_env)Cocoys-MacBook-Pro:~$ export ANSIBLE_HOSTS=$(pwd)/myhost
(ansi_env)Cocoys-MacBook-Pro:~$ ssh-add /path/to/server-ssh-keypair 

Fire Ansible ping command:

(ansi_env)Cocoys-MacBook-Pro:~$ ansible  all -m ping -u server_user 
server.domain.com | success >> {
    "ping": "pong"
}

Now start adding more hostname to Ansible host file. 🙂

Oh, and btw, I’ve added some tiny patch here.
It support reading of .ssh/config file to get values for hostname,port, and ssh-keypairs.
Updated: Apr-30-2012, ssh config will not overrides Ansible’s user and hostname.
Updated: May-12-2012, ssh_config files is *NOT* read, considering playbook can possibly use different user for each play.

My Ansible hostfile:

[web-servers]
hpcloud-a 

[cassandra-servers]
ec2-xx-xx-xx.187.compute-1.amazonaws.com
ec2-xx-xx-xx-52.compute-1.amazonaws.com

My SSH ~/.ssh/config

Host hpcloud-a
  HostName 15.185.123.xx
  User ubuntu
  IdentityFile /Users/cocoy/hpcloud.pem 

Host *.compute-1.amazonaws.com
  User ubuntu
  IdentityFile /Users/cocoy/ec2-keypair

(Note: On Amazon EC2, my two test instances the same keypair. On production, EC2 instances may have different keypair for each server (ex. db , web).

Now I can try running nodetool with Cassandra cluster:

(ansi_env) ~$ ansible cassandra-servers -m shell -a  "nodetool -h localhost ring"

I notice that this can be close to Fabric on ad-hoc commands, but there are more features.

Next post?? Watch-out for templates and playbooks.

Update 07-03-2012: Now using source ./hacking/env-setup instead of source ./hacking-env as noted pointed by Scott.