more tips n tricks

33
More Tips and Tricks Some “Non Bad” Practices

Upload: bcoca

Post on 16-Apr-2017

328 views

Category:

Technology


1 download

TRANSCRIPT

More Tips and TricksSome “Non Bad” Practices

#>whoami?- Ansible core maintainer

- bcoca @ IRC and github and mailing lists

- worn hats as SA/QA/Programmer/DBA/etc

- tech janitor

2

Why not ‘best practice’?

- No such thing- Many ‘good practices’- Environments are different: Florist e-commerce != VR mobile app- Companies and compliance are different- Rules and standards are great … until they get in the way

- Context is important- Similarities exist but things are rarely identical (siblings not twins)- “One size fits all” solutions are limited and limiting- Adaptability is key, provides choice- Decide on a workflow, use tools to achieve it- Pain appears when systems and workflows are in opposition

TIP: non bad practices

3

PEP8A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is the most important.

However, know when to be inconsistent -- sometimes style guide recommendations just aren't applicable. When in doubt, use your best judgment.

Look at other examples and decide what looks best. And don't hesitate to ask!

4

What is wrong?

- hosts: webservers handlers: - service: name: nginx state: restarted name: restart_nginx tasks: - template: src: templates/nginx.conf dest: /etc/nginx.conf notify: restart_nginx name: Set nginx config pre_tasks: - stat: path=/etc/nginx.conf

Bad Practice

5

- hosts: wx01 tasks: - template: src: templates/xl183.conf dest: /etc/apache2.conf notify: service handlers: - service: name=httpd state=restarted

How can it be better?

TIPS: Better Practices

6

- hosts: webserver01 pre_tasks: - stat: path=/etc/nginx.conf tasks: - name: Configure app02 webserver template: src: templates/webapp02.conf dest: /etc/apache2.conf notify: restart_httpd handlers: - name: restart_httpd service: name=httpd state=restarted

- Nothing syntactically wrong

- Dictionary order does not matter to

Ansible, but it matters to humans

- List order matters to all (- stuff)

- name: is your documentation

- Good host, group and task names

make things evident.

- Relying on explicit behaviour over

implicit keeps things clear

- Nothing I say is mandatory

Basic Layout

TIPS: Inventory

7

ansible/inventorygroup_vars/

all.yml appservers.ymldatabases.ymlloadbalancers.ymlnetwork.ymlwebservers/

secrets.ymlservice_vars.yml

host_vars/webserver01.ymlwebserver02.yml

PROS

- Simple setup

- Quick to get started

- Works for your average app

- Naming by function (not actual names)

CONS

- All or nothing access

- Grouped only on one criteria

Complicating layout

TIPS: Inventory

8

ansible/inventory/production (groups: dc1,dc2)development(idf)staging (dc1)group_vars/

all.yml appservers.ymldatabases.ymlloadbalancers.ymlnetwork.ymlwebservers/

secrets.ymlservice_vars.yml

dc1.ymldc2.yml

idf.yml host_vars/

PROS

- I can target environments by file

- File permissions for access (also ACLs)

- inventory_file or groups for ‘location’ and

‘environment’ variables, avoids overlap.

- Avoids relying on --limit

CONS

- More things to keep track of

- Requires more knowledge to setup

- Geared towards central mgmt host

- Overlaps with --limit

Unix tools use Unix permissions

TIPS: Inventory

9

jumphost#>ls -l /etc/ansible/inventory

drwxr-xr-x 2 root ops 4096 Aug 19 00:55 group_varsdrwxr-xr-x 2 root ops 4096 Aug 12 11:10 host_vars-rw-r----- 1 root ops 1825 Aug 18 03:07 production

-rw-r----- 1 root qa 251 Aug 18 03:07 staging

-rw-r----- 1 root devel 789 Aug 18 03:07 devel

jumphost#> getfacl production

# file: production

# owner: root

# group: ops

user:bcoca:rw-

user::rw-

group::r--

other::---

visible permissionsmy ‘back door’

Designing your inventory

TIPS: Inventory

10

- Inventory is the expression of your environment

- Hostnames, groups, vars are for YOUR use, they have to make sense to YOU

- Ansible cares about hosts and tasks, everything else is in support of that

- Select a single source of truth .. or try to minimize duplication of data

- Normally, there is a simpler way to do it

- Ansible makes it easy to switch approaches, don’t be afraid to test and try

- Mistakes are not failures

Generate YAML Inventory

Tricks

11

- hosts: localhost gather_facts: false tasks: - template: src: dump_hosts_yaml.j2 dest: /tmp/hosts.yml

{% set builtins = ['hostvars', 'vars', … ]%}

{% set dumped_vars = [] %}

{% for group in groups if group != 'all'%}

{{group}}:

{% for host in groups[group] %}

{{ host }}:

{% for myhostvar in hostvars[host] if myhostvar not in builtins %}

{{ myhostvar}}: {{hostvars[host][myhostvar]|to_json}}

{% if loop.last %}{% do dumped_vars.append(host) %}{% endif %}

{% endfor %}

{% endfor %}

{% endfor %}

migrate_yaml_inventory.yml dump_hosts_yaml.j2

12

ungrouped: ubuntu15test: testvar: default from all test2: {"key3": "valueall3", "key1": "valueall1"} ansible_python_interpreter: /usr/bin/python2.7 ansible_ssh_host: 192.168.1.7ubuntu: ubuntu12test: ubuntu14test: testvar: default from all test2: {"key3": "valueall3", "key1": "valueall1"}tests: ubuntu12test: ubuntu15test: centos7test: testvar: inv groupvar test2: {"key3": "valueall3", "key1": "valueall1"} cron_service: crond ansible_ssh_host: 192.168.0.24 test_prio: testsaptdistros: ubuntu12test: ubuntu14test:

/tmp/hosts.yml

Generate INI Inventory

Tricks

13

{% set builtins = ['hostvars', 'vars', 'groups', 'group_names', … '] %}{% set

dumped_vars = [] %}

{% for group in groups if group != 'all'%}

[{{group}}]

{% for host in groups[group] %}

{{ host }} {% for myhostvar in hostvars[host] if myhostvar not in builtins and host

not in dumped_vars %} {{ myhostvar}}={{hostvars[host][myhostvar]|to_json}} {% if

loop.last %}{% do dum

ped_vars.append(host) %}{% endif %} {% endfor %}

{% endfor %}

{% endfor %}

dump_hosts_ini.j2

14

[ungrouped]

ubuntu15test testvar=”default from all” test= {"key3": "valueall3", "key1": "valueall1"}

[ubuntu]

ubuntu12test

ubuntu14test testvar=”default from all” test2= {"key3": "valueall3", "key1": "valueall1"}

[tests]

ubuntu12test

ubuntu15test

centos7test testvar=”inv groupvar” ansible_ssh_host="192.168.0.24" test2={"key3": ...

[aptdistros]

ubuntu12test

/tmp/hosts.ini

Variable Sources

TIPS: vars can go in many places

15

PRECEDENCE (low to high):

role defaultsinventory file varsinventory group_vars, host_varsplaybook group_vars, host_varshost factsplay vars, vars_prompt, vars_filesregistered varsset_factsrole parameters and include varsblock(only for tasks in block), task varsextra vars (CLI, global, precedence)

● Many choices, flexible!

● Many choices, confusing!

● Where do I define my var?

○ Locality (host, group, play, role, task)

○ Use only what you need, ignore rest

○ Inheritance and scope (play, host)

● group/host_vars are always loaded

● variables are flattened per host

Variable Sources

TIPS: vars can go in many places

16

inventory/

...

playbooks/

webconfig/

...

certificates/

load_balancer_main.yml

load_balancer_internal.yml

ldap.yml

vars/

vaulted_certificates.yml

- Avoid vault errors

- Allows ‘need to know’

- group/host_vars are still autoloaded

- explicit: vars_files, include_vars from vars/

- vault files require passwords

- ansible cannot know if vault is needed, must

run play and open vault to find out.

- no tardis plugin (yet)

vars_prompt

TIPS: vars can go in many places

17

- Interactive vars

- Per play

- Available only at start

- Smart prompting:

- only if tty is present

- skip if already provided (extra vars)

- Unconditional

vars_prompt:

- name: "myvar"

prompt: "Question?"

Pause

TIPS: vars can go in many places

18

- Interactive vars

- It is a task:

- Can appear at any point a task can

- Tasks are conditional (when, tags)

- host scope, persists across plays

- No validation

- Only if tty is present

- result var structure (myvar.user_input)

- pause: prompt=”Question?” register: myvar

Single deployment script

Tricks

19

#!/usr/bin/ansible-playbook- hosts: localhost vars_prompt: - name: app_name prompt: “Which app do you want to deploy?” default: mainapp - name: app_version prompt: “Choose version/tag (default HEAD)” default: ‘HEAD’ tasks: - git: repo=git@myreposerver/{{app_version}} version={{app_version}} ...

bin/deploy Normal Interactive commandWhen using cron:

- pass in extra vars- let it use defaults

3 plays that can run in parallel

Tricks: parallel playbook execution

20

- name: play1 hosts: all gather_facts: false tasks: - debug: msg=starting1 - wait_for: timeout=10 - debug: msg=ending1

- name: play2 hosts: all gather_facts: false tasks: - debug: msg=starting2 - wait_for: timeout=14 - debug: msg=ending2

- name: play3 hosts: all gather_facts: false tasks: - debug: msg=starting3 - wait_for: timeout=16 - debug: msg=ending3

play1.yml play3.ymlplay2.yml

Sometimes you don’t want to run stuff serially .. if only ansible-playbook ran in parallel!

From the command line

Tricks: parallel playbook execution

21

#> time ansible-playbook play?.ymlreal 0m40.691suser 0m1.371ssys 0m0.353s

#>time parallel ansible-playbook {} ::: play?.ymlreal 0m16.816suser 0m3.928ssys 0m0.848s

#> time $(ls play?.yml|xargs -n1 -P3 ansible-playbook)real 0m16.788suser 0m3.963ssys 0m0.842s

- Ansible is unix tool, many other tools do parallel

- Several options to run in parallel

- Easy to redirect output

- manage resources with nice and ionice

- use with inotify, tcpserver, cron, at

… even with procmail filters

ansible-meta?

Tricks: parallel playbook execution

22

- name: really trying hard to avoid shell scripts hosts: localhost gather_facts: False tasks: - shell: ansible-playbook play1.yml async: 10000 poll: 0 - shell: ansible-playbook play2.yml async: 10000 poll: 0 - shell: ansible-playbook play3.yml async: 10000 poll: 0

#>time ansible-playbook async.ymlreal 0m3.572suser 0m0.497ssys 0m0.105s

Less typing!

Tricks: parallel playbook execution

23

- name: really trying hard to avoid shell scripts v2 hosts: localhost gather_facts: False tasks: - shell: ansible-playbook play{{item}}.yml async: 10000 poll: 0 with_items: [1,2,3]

#>time ansible-playbook async2.ymlreal 0m3.531suser 0m0.463ssys 0m0.107s

ansible-meta … with results!

Tricks: parallel playbook execution

24

- name: really trying hard to avoid shell scripts v3 hosts: localhost gather_facts: False tasks: - shell: ansible-playbook play{{item}}.yml async: 10000 poll: 0 with_items: [1,2,3] register: runplays

- async_status: jid={{runplays.results[item.index].ansible_job_id}} resgister: jobs

until: jobs.finished with_indexed_items: [1,2,3] retries: 100

#>time ansible-playbook async3.ymlreal 0m24.140suser 0m1.239ssys 0m0.265s

Static vs Dynamic

TIPS: includes

25

- Beginning in 2.0 includes can be dynamic, loops, conditionals!

- Static includes are not real tasks, dynamic includes … almost are

- Only task includes can be dynamic, play includes are always static

- Dynamic includes do not show included tasks in --list-tasks/tags

- Handler includes are static by default (both types have config setting)

- Task includes have dynamic include detection, use static: yes|no (>=2.1) directive to control

- Task behaviour can change in each case

dynamic include loops

TIPS: includes

26

- hosts: localhost gather_facts: False tasks: - include: test.yml with_items: [1,2] static: yes

- set_fact: outer={{item}}- debug: msg="{{outer}} and {{item}}" with_items: ['a','b']

TASK [debug] ********************************************************ok: [localhost] => (item=a) => { "item": "a", "msg": "1 and a"}ok: [localhost] => (item=b) => { "item": "b", "msg": "1 and b"}

TASK [debug] ********************************************************ok: [localhost] => (item=a) => { "item": "a", "msg": "2 and a"}ok: [localhost] => (item=b) => { "item": "b", "msg": "2 and b"}

play.yml

test.yml

Since 2.1 you can use loop_control instead of set_fact

New in 2.2

Tricks: include_role

27

- hosts: localhost tasks: - package: name={{httpd}} state=latest

- include_role:name: webapptasks_from: install.yml

- service: name={{httpd}} state=started

- include_role:name: webapptasks_from: configure.yml

vars_from: “{{ansible_os}}.yml” with_items: “{{ applications }}”

Multiple notification

TIPS: fun with handlers

28

- hosts: all tasks: - name: configure nginx template: src=nginx.j2 dest=/etc/nginx.conf notify: - restart_uwcgi - restart_nginx

handlers: - name: restart_uwcgi service: name=uwcgi state=restarted

- name: restart_nginx service: name=nginx state=restarted

- Most flexible

- Can get repetitive

- List can be a variable

Chaining handlers

TIPS: fun with handlers

29

- hosts: all tasks: - name: configure nginx template: src=nginx.j2 dest=/etc/nginx.conf notify: restart_nginx_cluster

handlers:

- name: restart_nginx_cluster service: name=uwcgi state=restarted notify: restart_nginx

- name: restart_nginx service: name=nginx state=restarted

- Call observes definition order

- Cannot call previous

- Less flexible

- Can call middle of chain

Grouping handlers

TIPS: fun with handlers

30

- hosts: all tasks: - name: configure nginx template: src=nginx.j2 dest=/etc/nginx.conf notify: restart_nginx_cluster

handlers: - name: restart_nginx_cluster wait_for: seconds=1 changed_when: true notify: [‘restart_uwcgi’, ‘nginx’] - name: restart_uwcgi service: name=uwcgi state=restarted - name: restart_nginx service: name=nginx state=restarted

- Still flexible

- Less repetition

- Needs dummy task

- Also relies on chain

- List can be a variable

listen! new in 2.2

TIPS: fun with handlers

31

- hosts: all tasks: - name: configure nginx template: src=nginx.j2 dest=/etc/nginx.conf notify: restart_nginx_cluster

handlers: - name: restart_uwcgi service: name=uwcgi state=restarted listen: restart_nginx_cluster

- name: restart_nginx service: name=nginx state=restarted listen: restart_nginx_cluster

- Still flexible

- Less repetition

- listen is not unique

- Does not rely on chain

- List can be a variable

include as handler

TIPS: fun with handlers

32

- hosts: all tasks: - name: configure nginx template: src=nginx.j2 dest=/etc/nginx.conf notify: restart_nginx_cluster

handlers:

- name: restart_nginx_cluster include: nginx_cluster_restart.yml static: no

- Only dynamic includes

- Include itself is handler

- File can be variable

- Also toggleable from config

#>exit- Ansible is flexible, many ways to do things

- Choice can be daunting

- Easy to make mistakes, easy to correct them

- “Best” is relative, depends on context, test it!

33