Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ansible

 Ansible

Lightning talk on Ansible, given at the Amsterdam Python meetup group on Oct 2nd, 2013

18bdcd2f189a9b8fe2f836dde8db09fc?s=128

Nick Groenen

October 02, 2013
Tweet

More Decks by Nick Groenen

Other Decks in Programming

Transcript

  1. Ansible Radically simple* IT orchestration * If you believe the

    ads http://bit.ly/1hiL9Xl
  2. 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
  3. Ansible Configuration management Remote execution Deployment

  4. Python WSGI app on 2 application servers (web1 & web2)

    behind 1 loadbalancer (lb1) Explore through example
  5. Vagrant Your own cloud on 127.0.0.1

  6. 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
  7. 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
  8. $ vagrant up Bringing machine 'lb1' up with 'virtualbox' provider...

    Bringing machine 'web1' up with 'virtualbox' provider… Bringing machine 'web2' up with 'virtualbox' provider...
  9. 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
  10. /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
  11. 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
  12. Needs nginx Needs to know webservers Loadbalancer

  13. Playbooks describe series of states to be enforced ansible-playbook

  14. 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
  15. 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
  16. 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
  17. 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
  18. What if nginx.conf changes?

  19. 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
  20. 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
  21. Mitigating danger of syntax errors

  22. 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
  23. 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
  24. 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
  25. nginx.conf.j2 {{ ansible_managed }} user www-data; worker_processes {{ ansible_processor_cores }};

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

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

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

    pid /run/nginx.pid; events { worker_connections {{ nginx_worker_connections }}; }
  29. Interlude: Remote execution ansible --module-name setup --user vagrant lb1

  30. Which kernels am I running? $ ansible --module-name shell --args

    "uname -srv" --user vagrant all
  31. 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
  32. 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; } }
  33. 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; } }
  34. Take it for a spin ansible-playbook playbook.yml

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

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

  37. Needs nginx Needs to know webservers Loadbalancer

  38. Needs nginx Needs to know webservers Loadbalancer

  39. Needs nginx Needs to know webservers Loadbalancer

  40. Need uwsgi Need application code Application servers

  41. Need uwsgi no time for this Need application code Application

    servers
  42. playbook.yml - hosts: webservers user: vagrant sudo: true tasks: -

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

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

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

    name: Install dependencies apt: name={{ item }} state=present with_items: - python-virtualenv - python-pip - git
  46. - 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
  47. - 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
  48. Need uwsgi Need application code Application servers

  49. 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
  50. Python WSGI app on 2 application servers (web1 & web2)

    behind 1 loadbalancer (lb1) Deployed!
  51. Recap Configuration management Remote execution Deployment

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

  53. Rolling updates - hosts: webservers max_fail_percentage: 25 serial: 2 tasks:

    - do_stuff_here
  54. 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
  55. Tags tasks: - apt: name={{ item }} state=installed with_items: -

    nginx - memcached tags: - packages - template: src=templates/src.j2 dest=/etc/foo.conf tags: - configuration
  56. Tags Run with: $ ansible-playbook example.yml --tags "packages" $ ansible-playbook

    example.yml --tags "configuration" $ ansible-playbook example.yml \ --tags "configuration,packages"
  57. Prompting for input vars_prompt: - name: "some_password" prompt: "Enter password"

    private: yes - name: "release_version" prompt: "Product release version" private: no
  58. Fine-grained targeting Sequential: www[01:50].example.com db-[a:f].example.com Exclusion: webservers:!amsterdam Intersection: webservers:&staging Combinations

    possible!
  59. 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') }}"
  60. • External inventory scripts • Python API • Custom modules

    • Custom Jinja2 filters • Moar! And more...
  61. Closing words • Ignored all best practices for demonstration (use

    roles!) • Made many assumptions http://www.ansibleworks.com/quickstart-video/ http://www.ansibleworks.com/docs
  62. Questions?