Slide 1

Slide 1 text

Ansible Radically simple* IT orchestration * If you believe the ads http://bit.ly/1hiL9Xl

Slide 2

Slide 2 text

24 years old Loves spicy food Developer @ TravelBird Python user since 2009 Linux user since around 2003 Big fan of Open Source Nick Groenen Twitter: @NickGroenen Web: https://zoni.nl Email: zoni@zoni.nl

Slide 3

Slide 3 text

Ansible Configuration management Remote execution Deployment

Slide 4

Slide 4 text

Python WSGI app on 2 application servers (web1 & web2) behind 1 loadbalancer (lb1) Explore through example

Slide 5

Slide 5 text

Vagrant Your own cloud on 127.0.0.1

Slide 6

Slide 6 text

Vagrantfile Vagrant.configure("2") do |config| config.vm.define :lb1 do |lb1| lb1.vm.box = "raring-server-cloudimg-amd64-vagrant" lb1.vm.network :private_network, ip: "192.168.99.10" end config.vm.define :web1 do |web1| web1.vm.box = "raring-server-cloudimg-amd64-vagrant" web1.vm.network :private_network, ip: "192.168.99.11" end config.vm.define :web2 do |web2| web2.vm.box = "raring-server-cloudimg-amd64-vagrant" web2.vm.network :private_network, ip: "192.168.99.12" end end

Slide 7

Slide 7 text

Vagrantfile Vagrant.configure("2") do |config| config.vm.define :lb1 do |lb1| lb1.vm.box = "raring-server-cloudimg-amd64-vagrant" lb1.vm.network :private_network, ip: "192.168.99.10" end config.vm.define :web1 do |web1| web1.vm.box = "raring-server-cloudimg-amd64-vagrant" web1.vm.network :private_network, ip: "192.168.99.11" end config.vm.define :web2 do |web2| web2.vm.box = "raring-server-cloudimg-amd64-vagrant" web2.vm.network :private_network, ip: "192.168.99.12" end end

Slide 8

Slide 8 text

$ vagrant up Bringing machine 'lb1' up with 'virtualbox' provider... Bringing machine 'web1' up with 'virtualbox' provider… Bringing machine 'web2' up with 'virtualbox' provider...

Slide 9

Slide 9 text

Ansible leverages SSH. No daemons to setup, no (extra) permissions to manage, we're ready to roll! pip install ansible or aptitude install ansible or yum install ansible or emerge ansible

Slide 10

Slide 10 text

/etc/ansible/hosts [loadbalancers] lb1 ansible_ssh_host=192.168.99.10 [webservers] web1 ansible_ssh_host=192.168.99.11 web2 ansible_ssh_host=192.168.99.12 ansible_ssh_host is only necessary because Vagrant VM's aren't in DNS

Slide 11

Slide 11 text

Nesting groups [loadbalancers] lb1 ansible_ssh_host=192.168.99.10 [webservers] web1 ansible_ssh_host=192.168.99.11 web2 ansible_ssh_host=192.168.99.12 [exampleapp:children] webservers

Slide 12

Slide 12 text

Needs nginx Needs to know webservers Loadbalancer

Slide 13

Slide 13 text

Playbooks describe series of states to be enforced ansible-playbook

Slide 14

Slide 14 text

playbook.yml - hosts: loadbalancers user: vagrant sudo: true tasks: - name: Install nginx apt: name=nginx state=present - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644

Slide 15

Slide 15 text

playbook.yml - hosts: loadbalancers user: vagrant sudo: true tasks: - name: Install nginx apt: name=nginx state=present - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644

Slide 16

Slide 16 text

playbook.yml - hosts: loadbalancers user: vagrant sudo: true tasks: - name: Install nginx apt: name=nginx state=present - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644

Slide 17

Slide 17 text

playbook.yml - hosts: loadbalancers user: vagrant sudo: true tasks: - name: Install nginx apt: name=nginx state=present - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644

Slide 18

Slide 18 text

What if nginx.conf changes?

Slide 19

Slide 19 text

playbook.yml # Some stuff omitted to save space tasks: - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644 notify: - restart nginx handlers: - name: restart nginx service: name=nginx state=restarted

Slide 20

Slide 20 text

playbook.yml # Some stuff omitted to save space tasks: - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644 notify: - restart nginx handlers: - name: restart nginx service: name=nginx state=restarted

Slide 21

Slide 21 text

Mitigating danger of syntax errors

Slide 22

Slide 22 text

playbook.yml # Some stuff omitted to save space tasks: - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644 validate="nginx -t -c %s" notify: - restart nginx handlers: - name: restart nginx service: name=nginx state=restarted

Slide 23

Slide 23 text

playbook.yml # Some stuff omitted to save space tasks: - name: Install nginx config template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf owner=root group=root mode=644 validate="nginx -t -c %s" notify: - restart nginx handlers: - name: restart nginx service: name=nginx state=restarted

Slide 24

Slide 24 text

failed: [lb1] => {"failed": true} msg: failed to validate: rc:1 error:nginx: [emerg] directive "pid" is not terminated by ";" in /home/vagrant/. ansible/tmp/ansible-1380702362.41-73682544006063/source:5 nginx: configuration file /home/vagrant/.ansible/tmp/ansible- 1380702362.41-73682544006063/source test failed FATAL: all hosts have already failed -- aborting If you screwed up

Slide 25

Slide 25 text

nginx.conf.j2 {{ ansible_managed }} user www-data; worker_processes {{ ansible_processor_cores }}; pid /run/nginx.pid; events { worker_connections {{ nginx_worker_connections }}; }

Slide 26

Slide 26 text

nginx.conf.j2 {{ ansible_managed }} user www-data; worker_processes {{ ansible_processor_cores }}; pid /run/nginx.pid; events { worker_connections {{ nginx_worker_connections }}; }

Slide 27

Slide 27 text

nginx.conf.j2 {{ ansible_managed }} user www-data; worker_processes {{ ansible_processor_cores }}; pid /run/nginx.pid; events { worker_connections {{ nginx_worker_connections }}; }

Slide 28

Slide 28 text

nginx.conf.j2 {{ ansible_managed }} user www-data; worker_processes {{ ansible_processor_cores }}; pid /run/nginx.pid; events { worker_connections {{ nginx_worker_connections }}; }

Slide 29

Slide 29 text

Interlude: Remote execution ansible --module-name setup --user vagrant lb1

Slide 30

Slide 30 text

Which kernels am I running? $ ansible --module-name shell --args "uname -srv" --user vagrant all

Slide 31

Slide 31 text

lb1 | success | rc=0 >> Linux 3.8.0-30-generic #44-Ubuntu SMP Thu Aug 22 20:52:24 UTC 2013 web1 | success | rc=0 >> Linux 3.8.0-30-generic #44-Ubuntu SMP Thu Aug 22 20:52:24 UTC 2013 web2 | FAILED => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue

Slide 32

Slide 32 text

nginx.conf.j2 (continued) upstream exampleapp { {% for host in groups['webservers'] %} server {{ hostvars[host]['ansible_ssh_host'] }}:9001; {% endfor %} } server { listen 80; server_name example.vagrant; location / { uwsgi_pass exampleapp; } }

Slide 33

Slide 33 text

nginx.conf.j2 (continued) upstream exampleapp { {% for host in groups['webservers'] %} server {{ hostvars[host]['ansible_ssh_host'] }}:9001; {% endfor %} } server { listen 80; server_name example.vagrant; location / { uwsgi_pass exampleapp; } }

Slide 34

Slide 34 text

Take it for a spin ansible-playbook playbook.yml

Slide 35

Slide 35 text

If all went well lb1 : ok=3 changed=2 unreachable=0 failed=0

Slide 36

Slide 36 text

Run it again! lb1 : ok=3 changed=0 unreachable=0 failed=0

Slide 37

Slide 37 text

Needs nginx Needs to know webservers Loadbalancer

Slide 38

Slide 38 text

Needs nginx Needs to know webservers Loadbalancer

Slide 39

Slide 39 text

Needs nginx Needs to know webservers Loadbalancer

Slide 40

Slide 40 text

Need uwsgi Need application code Application servers

Slide 41

Slide 41 text

Need uwsgi no time for this Need application code Application servers

Slide 42

Slide 42 text

playbook.yml - hosts: webservers user: vagrant sudo: true tasks: - name: Install dependencies apt: name={{ item }} state=present with_items: - python-virtualenv - python-pip - git

Slide 43

Slide 43 text

playbook.yml - hosts: webservers user: vagrant sudo: true tasks: - name: Install dependencies apt: name={{ item }} state=present with_items: - python-virtualenv - python-pip - git

Slide 44

Slide 44 text

playbook.yml - hosts: webservers user: vagrant sudo: true tasks: - name: Install dependencies apt: name={{ item }} state=present with_items: - python-virtualenv - python-pip - git

Slide 45

Slide 45 text

playbook.yml - hosts: webservers user: vagrant sudo: true tasks: - name: Install dependencies apt: name={{ item }} state=present with_items: - python-virtualenv - python-pip - git

Slide 46

Slide 46 text

- name: Checkout application code git: repo=https://gist.github.com/6164386.git dest=/srv/exampleapp version=HEAD update=yes notify: - reload uwsgi instance - name: Install requirements pip: state=present virtualenv=/srv/exampleapp requirements=/srv/exampleapp/requirements.txt playbook.yml

Slide 47

Slide 47 text

- name: Checkout application code git: repo=https://gist.github.com/6164386.git dest=/srv/exampleapp version=HEAD update=yes notify: - reload uwsgi instance - name: Install requirements pip: state=present virtualenv=/srv/exampleapp requirements=/srv/exampleapp/requirements.txt playbook.yml

Slide 48

Slide 48 text

Need uwsgi Need application code Application servers

Slide 49

Slide 49 text

ansible-playbook playbook.yml lb1 : ok=3 changed=0 unreachable=0 failed=0 web1 : ok=4 changed=3 unreachable=0 failed=0 web2 : ok=4 changed=3 unreachable=0 failed=0

Slide 50

Slide 50 text

Python WSGI app on 2 application servers (web1 & web2) behind 1 loadbalancer (lb1) Deployed!

Slide 51

Slide 51 text

Recap Configuration management Remote execution Deployment

Slide 52

Slide 52 text

But wait! There's more! (If there's time)

Slide 53

Slide 53 text

Rolling updates - hosts: webservers max_fail_percentage: 25 serial: 2 tasks: - do_stuff_here

Slide 54

Slide 54 text

Delegating actions - hosts: webservers serial: 2 tasks: - name: Remove from loadbalancer shell: remove_from_lb_command delegate_to: lb1 - do_stuff_here - name: Add to loadbalancer shell: add_to_lb_command delegate_to: lb1

Slide 55

Slide 55 text

Tags tasks: - apt: name={{ item }} state=installed with_items: - nginx - memcached tags: - packages - template: src=templates/src.j2 dest=/etc/foo.conf tags: - configuration

Slide 56

Slide 56 text

Tags Run with: $ ansible-playbook example.yml --tags "packages" $ ansible-playbook example.yml --tags "configuration" $ ansible-playbook example.yml \ --tags "configuration,packages"

Slide 57

Slide 57 text

Prompting for input vars_prompt: - name: "some_password" prompt: "Enter password" private: yes - name: "release_version" prompt: "Product release version" private: no

Slide 58

Slide 58 text

Fine-grained targeting Sequential: www[01:50].example.com db-[a:f].example.com Exclusion: webservers:!amsterdam Intersection: webservers:&staging Combinations possible!

Slide 59

Slide 59 text

Looking up data - hosts: localhost tasks: - name: Print result of DNS lookup shell: echo {{ lookup('dnstxt', 'example.com') }} - name: Print file contents debug: msg="Motd content: {{ lookup('file', '/etc/motd') }}"

Slide 60

Slide 60 text

● External inventory scripts ● Python API ● Custom modules ● Custom Jinja2 filters ● Moar! And more...

Slide 61

Slide 61 text

Closing words ● Ignored all best practices for demonstration (use roles!) ● Made many assumptions http://www.ansibleworks.com/quickstart-video/ http://www.ansibleworks.com/docs

Slide 62

Slide 62 text

Questions?